@clankmates/cli 0.2.0 → 0.3.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 +72 -216
- package/package.json +1 -1
- package/skills/codex/clankmates/SKILL.md +40 -11
- package/src/cli.ts +26 -3
- package/src/commands/auth.ts +241 -26
- package/src/commands/channel.ts +302 -52
- package/src/commands/post.ts +124 -17
- package/src/commands/user.ts +52 -0
- package/src/lib/args.ts +1 -0
- package/src/lib/client.ts +308 -37
- package/src/types/api.ts +73 -3
package/src/commands/channel.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
booleanFlag,
|
|
3
|
+
integerFlag,
|
|
3
4
|
requiredPositional,
|
|
4
5
|
requiredStringFlag,
|
|
5
6
|
stringFlag,
|
|
6
7
|
type ParsedArgs,
|
|
7
8
|
} from "../lib/args";
|
|
8
|
-
import { storeChannelToken } from "../lib/config";
|
|
9
|
+
import { storeChannelToken, updateProfile } from "../lib/config";
|
|
9
10
|
import { createCommandContext } from "../lib/context";
|
|
10
11
|
import { CliError } from "../lib/errors";
|
|
11
12
|
import { printJson, printValue, type Io } from "../lib/output";
|
|
13
|
+
import type {
|
|
14
|
+
ChannelAttributes,
|
|
15
|
+
ChannelKeyAttributes,
|
|
16
|
+
ChannelKeyIssueResponse,
|
|
17
|
+
} from "../types/api";
|
|
12
18
|
|
|
13
19
|
const CHANNEL_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]*$/;
|
|
14
20
|
const CHANNEL_NAME_ERROR =
|
|
@@ -23,23 +29,12 @@ export async function runChannelCommand(
|
|
|
23
29
|
|
|
24
30
|
switch (subcommand) {
|
|
25
31
|
case "list": {
|
|
26
|
-
const response = await context.client.listChannels(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
visibility: item.attributes.visibility,
|
|
31
|
-
description: item.attributes.description ?? "",
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
|
-
if (context.outputMode === "json") {
|
|
35
|
-
printJson(io, {
|
|
36
|
-
items: response.items,
|
|
37
|
-
nextCursor: response.nextCursor,
|
|
38
|
-
});
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
32
|
+
const response = await context.client.listChannels({
|
|
33
|
+
limit: integerFlag(args.flags, "limit", { label: "--limit" }),
|
|
34
|
+
cursor: stringFlag(args.flags, "cursor"),
|
|
35
|
+
});
|
|
41
36
|
|
|
42
|
-
|
|
37
|
+
printChannelCollection(context.outputMode, io, response);
|
|
43
38
|
return;
|
|
44
39
|
}
|
|
45
40
|
|
|
@@ -51,15 +46,45 @@ export async function runChannelCommand(
|
|
|
51
46
|
printValue(
|
|
52
47
|
io,
|
|
53
48
|
context.outputMode,
|
|
54
|
-
context.outputMode === "json"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
49
|
+
context.outputMode === "json" ? channel : formatChannelRecord(channel),
|
|
50
|
+
);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case "public-list": {
|
|
55
|
+
const response = await context.client.listPublicChannelsForHandle({
|
|
56
|
+
handle: requiredPositional(args.positionals, 1, "Missing public handle"),
|
|
57
|
+
limit: integerFlag(args.flags, "limit", { label: "--limit" }),
|
|
58
|
+
cursor: stringFlag(args.flags, "cursor"),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
printChannelCollection(context.outputMode, io, response);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case "public-get": {
|
|
66
|
+
const channel = await context.client.getPublicChannelByHandle(
|
|
67
|
+
requiredPositional(args.positionals, 1, "Missing public handle"),
|
|
68
|
+
requiredPositional(args.positionals, 2, "Missing public channel name"),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
printValue(
|
|
72
|
+
io,
|
|
73
|
+
context.outputMode,
|
|
74
|
+
context.outputMode === "json" ? channel : formatChannelRecord(channel),
|
|
75
|
+
);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
case "shared-get": {
|
|
80
|
+
const channel = await context.client.getSharedChannel(
|
|
81
|
+
requiredPositional(args.positionals, 1, "Missing share token"),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
printValue(
|
|
85
|
+
io,
|
|
86
|
+
context.outputMode,
|
|
87
|
+
context.outputMode === "json" ? channel : formatChannelRecord(channel),
|
|
63
88
|
);
|
|
64
89
|
return;
|
|
65
90
|
}
|
|
@@ -76,14 +101,7 @@ export async function runChannelCommand(
|
|
|
76
101
|
printValue(
|
|
77
102
|
io,
|
|
78
103
|
context.outputMode,
|
|
79
|
-
context.outputMode === "json"
|
|
80
|
-
? channel
|
|
81
|
-
: {
|
|
82
|
-
id: channel.id,
|
|
83
|
-
name: channel.attributes.name,
|
|
84
|
-
visibility: channel.attributes.visibility,
|
|
85
|
-
description: channel.attributes.description ?? "",
|
|
86
|
-
},
|
|
104
|
+
context.outputMode === "json" ? channel : formatChannelRecord(channel),
|
|
87
105
|
);
|
|
88
106
|
return;
|
|
89
107
|
}
|
|
@@ -118,15 +136,57 @@ export async function runChannelCommand(
|
|
|
118
136
|
printValue(
|
|
119
137
|
io,
|
|
120
138
|
context.outputMode,
|
|
121
|
-
context.outputMode === "json"
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
context.outputMode === "json" ? channel : formatChannelRecord(channel),
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
case "publish-public": {
|
|
145
|
+
const channelId = await context.client.resolveChannelId(
|
|
146
|
+
requiredPositional(args.positionals, 1, "Missing channel"),
|
|
147
|
+
);
|
|
148
|
+
const channel = await context.client.publishChannelPublicly(channelId);
|
|
149
|
+
|
|
150
|
+
printValue(
|
|
151
|
+
io,
|
|
152
|
+
context.outputMode,
|
|
153
|
+
context.outputMode === "json" ? channel : formatChannelRecord(channel),
|
|
154
|
+
);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
case "unpublish-public": {
|
|
159
|
+
const channelId = await context.client.resolveChannelId(
|
|
160
|
+
requiredPositional(args.positionals, 1, "Missing channel"),
|
|
161
|
+
);
|
|
162
|
+
const response = await context.client.unpublishChannelPublicly(channelId);
|
|
163
|
+
|
|
164
|
+
printValue(io, context.outputMode, response);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case "share": {
|
|
169
|
+
const channelId = await context.client.resolveChannelId(
|
|
170
|
+
requiredPositional(args.positionals, 1, "Missing channel"),
|
|
171
|
+
);
|
|
172
|
+
const response = await context.client.shareChannel(channelId);
|
|
173
|
+
|
|
174
|
+
if (booleanFlag(args.flags, "tokenOnly")) {
|
|
175
|
+
io.stdout(response.token);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
printValue(io, context.outputMode, response);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
case "revoke-share": {
|
|
184
|
+
const channelId = await context.client.resolveChannelId(
|
|
185
|
+
requiredPositional(args.positionals, 1, "Missing channel"),
|
|
129
186
|
);
|
|
187
|
+
const response = await context.client.revokeChannelShare(channelId);
|
|
188
|
+
|
|
189
|
+
printValue(io, context.outputMode, response);
|
|
130
190
|
return;
|
|
131
191
|
}
|
|
132
192
|
|
|
@@ -146,21 +206,93 @@ export async function runChannelCommand(
|
|
|
146
206
|
return;
|
|
147
207
|
}
|
|
148
208
|
|
|
209
|
+
case "token": {
|
|
210
|
+
await runChannelTokenCommand(args, io);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
149
214
|
case "rotate-token": {
|
|
215
|
+
const channelRef = requiredPositional(args.positionals, 1, "Missing channel");
|
|
216
|
+
const channelId = await context.client.resolveChannelId(channelRef);
|
|
217
|
+
const response = await context.client.issueChannelKey({
|
|
218
|
+
channelId,
|
|
219
|
+
name: stringFlag(args.flags, "name") ?? legacyKeyName(),
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
await maybeStoreChannelToken(
|
|
223
|
+
args,
|
|
224
|
+
response,
|
|
225
|
+
channelId,
|
|
226
|
+
context.profileName,
|
|
227
|
+
context.configPath,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
if (booleanFlag(args.flags, "tokenOnly")) {
|
|
231
|
+
io.stdout(response.token);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
printValue(io, context.outputMode, response);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
default:
|
|
240
|
+
throw new CliError("Unknown channel subcommand", 2);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function runChannelTokenCommand(
|
|
245
|
+
args: ParsedArgs,
|
|
246
|
+
io: Io,
|
|
247
|
+
): Promise<void> {
|
|
248
|
+
const context = await createCommandContext(args, io);
|
|
249
|
+
const tokenCommand = requiredPositional(
|
|
250
|
+
args.positionals,
|
|
251
|
+
1,
|
|
252
|
+
"Missing channel token subcommand",
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
switch (tokenCommand) {
|
|
256
|
+
case "list": {
|
|
150
257
|
const channelId = await context.client.resolveChannelId(
|
|
151
|
-
requiredPositional(args.positionals,
|
|
258
|
+
requiredPositional(args.positionals, 2, "Missing channel"),
|
|
152
259
|
);
|
|
153
|
-
const response = await context.client.
|
|
260
|
+
const response = await context.client.listChannelKeys({
|
|
261
|
+
channelId,
|
|
262
|
+
limit: integerFlag(args.flags, "limit", { label: "--limit" }),
|
|
263
|
+
cursor: stringFlag(args.flags, "cursor"),
|
|
264
|
+
});
|
|
154
265
|
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
channelId,
|
|
159
|
-
response.token,
|
|
160
|
-
context.configPath,
|
|
161
|
-
);
|
|
266
|
+
if (context.outputMode === "json") {
|
|
267
|
+
printJson(io, { items: response.items, nextCursor: response.nextCursor });
|
|
268
|
+
return;
|
|
162
269
|
}
|
|
163
270
|
|
|
271
|
+
printValue(
|
|
272
|
+
io,
|
|
273
|
+
context.outputMode,
|
|
274
|
+
response.items.map((item) => formatChannelKeyRow(item)),
|
|
275
|
+
);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
case "issue": {
|
|
280
|
+
const channelId = await context.client.resolveChannelId(
|
|
281
|
+
requiredPositional(args.positionals, 2, "Missing channel"),
|
|
282
|
+
);
|
|
283
|
+
const response = await context.client.issueChannelKey({
|
|
284
|
+
channelId,
|
|
285
|
+
name: requiredStringFlag(args.flags, "name"),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
await maybeStoreChannelToken(
|
|
289
|
+
args,
|
|
290
|
+
response,
|
|
291
|
+
channelId,
|
|
292
|
+
context.profileName,
|
|
293
|
+
context.configPath,
|
|
294
|
+
);
|
|
295
|
+
|
|
164
296
|
if (booleanFlag(args.flags, "tokenOnly")) {
|
|
165
297
|
io.stdout(response.token);
|
|
166
298
|
return;
|
|
@@ -170,9 +302,127 @@ export async function runChannelCommand(
|
|
|
170
302
|
return;
|
|
171
303
|
}
|
|
172
304
|
|
|
305
|
+
case "revoke": {
|
|
306
|
+
const response = await context.client.revokeChannelKey(
|
|
307
|
+
requiredPositional(args.positionals, 2, "Missing channel key id"),
|
|
308
|
+
);
|
|
309
|
+
await pruneInvalidStoredChannelTokens(
|
|
310
|
+
context.profileName,
|
|
311
|
+
context.profile.channelTokens,
|
|
312
|
+
context.client,
|
|
313
|
+
context.configPath,
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
printValue(io, context.outputMode, response);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
173
320
|
default:
|
|
174
|
-
throw new CliError("Unknown channel subcommand", 2);
|
|
321
|
+
throw new CliError("Unknown channel token subcommand", 2);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function maybeStoreChannelToken(
|
|
326
|
+
args: ParsedArgs,
|
|
327
|
+
response: ChannelKeyIssueResponse,
|
|
328
|
+
channelId: string,
|
|
329
|
+
profileName: string,
|
|
330
|
+
configPath: string,
|
|
331
|
+
): Promise<void> {
|
|
332
|
+
if (!booleanFlag(args.flags, "save")) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
await storeChannelToken(profileName, channelId, response.token, configPath);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function printChannelCollection(
|
|
340
|
+
outputMode: "json" | "table",
|
|
341
|
+
io: Io,
|
|
342
|
+
response: {
|
|
343
|
+
items: Array<{ id: string; attributes: ChannelAttributes }>;
|
|
344
|
+
nextCursor?: string;
|
|
345
|
+
},
|
|
346
|
+
): void {
|
|
347
|
+
if (outputMode === "json") {
|
|
348
|
+
printJson(io, {
|
|
349
|
+
items: response.items,
|
|
350
|
+
nextCursor: response.nextCursor,
|
|
351
|
+
});
|
|
352
|
+
return;
|
|
175
353
|
}
|
|
354
|
+
|
|
355
|
+
printValue(
|
|
356
|
+
io,
|
|
357
|
+
outputMode,
|
|
358
|
+
response.items.map((item) => formatChannelRow(item)),
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function formatChannelRecord(channel: { id: string; attributes: ChannelAttributes }) {
|
|
363
|
+
return {
|
|
364
|
+
id: channel.id,
|
|
365
|
+
name: channel.attributes.name,
|
|
366
|
+
visibility: channel.attributes.visibility,
|
|
367
|
+
publiclyListed: channel.attributes.publicly_listed ?? false,
|
|
368
|
+
description: channel.attributes.description ?? "",
|
|
369
|
+
postingPausedUntil: channel.attributes.posting_paused_until ?? "",
|
|
370
|
+
insertedAt: channel.attributes.inserted_at ?? "",
|
|
371
|
+
updatedAt: channel.attributes.updated_at ?? "",
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function formatChannelRow(channel: { id: string; attributes: ChannelAttributes }) {
|
|
376
|
+
return {
|
|
377
|
+
id: channel.id,
|
|
378
|
+
name: channel.attributes.name,
|
|
379
|
+
visibility: channel.attributes.visibility,
|
|
380
|
+
publiclyListed: channel.attributes.publicly_listed ?? false,
|
|
381
|
+
description: channel.attributes.description ?? "",
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function formatChannelKeyRow(item: { id: string; attributes: ChannelKeyAttributes }) {
|
|
386
|
+
return {
|
|
387
|
+
id: item.id,
|
|
388
|
+
name: item.attributes.name ?? "",
|
|
389
|
+
expiresAt: item.attributes.expires_at ?? "",
|
|
390
|
+
revokedAt: item.attributes.revoked_at ?? "",
|
|
391
|
+
issuedAt: item.attributes.inserted_at ?? "",
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function legacyKeyName(): string {
|
|
396
|
+
return `legacy-rotate-${new Date().toISOString()}`;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async function pruneInvalidStoredChannelTokens(
|
|
400
|
+
profileName: string,
|
|
401
|
+
storedTokens: Record<string, { token: string }>,
|
|
402
|
+
client: Awaited<ReturnType<typeof createCommandContext>>["client"],
|
|
403
|
+
configPath: string,
|
|
404
|
+
): Promise<void> {
|
|
405
|
+
const invalidChannelIds: string[] = [];
|
|
406
|
+
|
|
407
|
+
for (const [channelId, storedToken] of Object.entries(storedTokens)) {
|
|
408
|
+
if (!(await client.canAuthenticate(storedToken.token))) {
|
|
409
|
+
invalidChannelIds.push(channelId);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (invalidChannelIds.length === 0) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
await updateProfile(
|
|
418
|
+
profileName,
|
|
419
|
+
(profile) => {
|
|
420
|
+
for (const channelId of invalidChannelIds) {
|
|
421
|
+
delete profile.channelTokens[channelId];
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
configPath,
|
|
425
|
+
);
|
|
176
426
|
}
|
|
177
427
|
|
|
178
428
|
function assertValidChannelName(name: string): void {
|
package/src/commands/post.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
booleanFlag,
|
|
2
3
|
integerFlag,
|
|
3
4
|
requiredChannelFlag,
|
|
4
5
|
requiredPositional,
|
|
@@ -9,6 +10,7 @@ import { resolveBodyInput } from "../lib/body-input";
|
|
|
9
10
|
import { createCommandContext } from "../lib/context";
|
|
10
11
|
import { CliError } from "../lib/errors";
|
|
11
12
|
import { printJson, printValue, type Io } from "../lib/output";
|
|
13
|
+
import type { PostAttributes } from "../types/api";
|
|
12
14
|
|
|
13
15
|
export async function runPostCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
14
16
|
const subcommand = args.positionals[0];
|
|
@@ -53,24 +55,34 @@ export async function runPostCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
53
55
|
cursor: stringFlag(args.flags, "cursor"),
|
|
54
56
|
});
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
nextCursor: response.nextCursor,
|
|
60
|
-
});
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
58
|
+
printPostCollection(context.outputMode, io, response);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
})
|
|
73
|
-
|
|
62
|
+
case "public-list": {
|
|
63
|
+
const response = await context.client.listPublicChannelPosts({
|
|
64
|
+
handle: requiredPositional(args.positionals, 1, "Missing public handle"),
|
|
65
|
+
channelName: requiredPositional(
|
|
66
|
+
args.positionals,
|
|
67
|
+
2,
|
|
68
|
+
"Missing public channel name",
|
|
69
|
+
),
|
|
70
|
+
limit: integerFlag(args.flags, "limit", { label: "--limit" }),
|
|
71
|
+
cursor: stringFlag(args.flags, "cursor"),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
printPostCollection(context.outputMode, io, response);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
case "shared-list": {
|
|
79
|
+
const response = await context.client.listSharedChannelPosts({
|
|
80
|
+
token: requiredPositional(args.positionals, 1, "Missing share token"),
|
|
81
|
+
limit: integerFlag(args.flags, "limit", { label: "--limit" }),
|
|
82
|
+
cursor: stringFlag(args.flags, "cursor"),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
printPostCollection(context.outputMode, io, response);
|
|
74
86
|
return;
|
|
75
87
|
}
|
|
76
88
|
|
|
@@ -134,7 +146,102 @@ export async function runPostCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
134
146
|
return;
|
|
135
147
|
}
|
|
136
148
|
|
|
149
|
+
case "public-get": {
|
|
150
|
+
const post = await context.client.getPublicPostByHandle({
|
|
151
|
+
handle: requiredPositional(args.positionals, 1, "Missing public handle"),
|
|
152
|
+
channelName: requiredPositional(
|
|
153
|
+
args.positionals,
|
|
154
|
+
2,
|
|
155
|
+
"Missing public channel name",
|
|
156
|
+
),
|
|
157
|
+
postId: requiredPositional(args.positionals, 3, "Missing post id"),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
printValue(
|
|
161
|
+
io,
|
|
162
|
+
context.outputMode,
|
|
163
|
+
context.outputMode === "json"
|
|
164
|
+
? post
|
|
165
|
+
: {
|
|
166
|
+
id: post.id,
|
|
167
|
+
source: post.attributes.source,
|
|
168
|
+
body: post.attributes.body,
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case "shared-get": {
|
|
175
|
+
const post = await context.client.getSharedPost(
|
|
176
|
+
requiredPositional(args.positionals, 1, "Missing share token"),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
printValue(
|
|
180
|
+
io,
|
|
181
|
+
context.outputMode,
|
|
182
|
+
context.outputMode === "json"
|
|
183
|
+
? post
|
|
184
|
+
: {
|
|
185
|
+
id: post.id,
|
|
186
|
+
source: post.attributes.source,
|
|
187
|
+
body: post.attributes.body,
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case "share": {
|
|
194
|
+
const response = await context.client.sharePost(
|
|
195
|
+
requiredPositional(args.positionals, 1, "Missing post id"),
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
if (booleanFlag(args.flags, "tokenOnly")) {
|
|
199
|
+
io.stdout(response.token);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
printValue(io, context.outputMode, response);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case "revoke-share": {
|
|
208
|
+
const response = await context.client.revokePostShare(
|
|
209
|
+
requiredPositional(args.positionals, 1, "Missing post id"),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
printValue(io, context.outputMode, response);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
137
216
|
default:
|
|
138
217
|
throw new CliError("Unknown post subcommand", 2);
|
|
139
218
|
}
|
|
140
219
|
}
|
|
220
|
+
|
|
221
|
+
function printPostCollection(
|
|
222
|
+
outputMode: "json" | "table",
|
|
223
|
+
io: Io,
|
|
224
|
+
response: {
|
|
225
|
+
items: Array<{ id: string; attributes: PostAttributes }>;
|
|
226
|
+
nextCursor?: string;
|
|
227
|
+
},
|
|
228
|
+
): void {
|
|
229
|
+
if (outputMode === "json") {
|
|
230
|
+
printJson(io, {
|
|
231
|
+
items: response.items,
|
|
232
|
+
nextCursor: response.nextCursor,
|
|
233
|
+
});
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
printValue(
|
|
238
|
+
io,
|
|
239
|
+
outputMode,
|
|
240
|
+
response.items.map((item) => ({
|
|
241
|
+
id: item.id,
|
|
242
|
+
source: item.attributes.source,
|
|
243
|
+
date: item.attributes.updated_at ?? item.attributes.inserted_at ?? "",
|
|
244
|
+
body: item.attributes.body,
|
|
245
|
+
})),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { requiredPositional, type ParsedArgs } from "../lib/args";
|
|
2
|
+
import { createCommandContext } from "../lib/context";
|
|
3
|
+
import { CliError } from "../lib/errors";
|
|
4
|
+
import { printValue, type Io } from "../lib/output";
|
|
5
|
+
|
|
6
|
+
export async function runUserCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
7
|
+
const subcommand = args.positionals[0];
|
|
8
|
+
const context = await createCommandContext(args, io);
|
|
9
|
+
|
|
10
|
+
switch (subcommand) {
|
|
11
|
+
case "get": {
|
|
12
|
+
const user = await context.client.getUserByPublicHandle(
|
|
13
|
+
requiredPositional(args.positionals, 1, "Missing public handle"),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
printValue(
|
|
17
|
+
io,
|
|
18
|
+
context.outputMode,
|
|
19
|
+
context.outputMode === "json"
|
|
20
|
+
? user
|
|
21
|
+
: {
|
|
22
|
+
id: user.id,
|
|
23
|
+
email: user.attributes.email,
|
|
24
|
+
publicHandle: user.attributes.public_handle ?? "",
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
case "claim-handle": {
|
|
31
|
+
const user = await context.client.claimPublicHandle(
|
|
32
|
+
requiredPositional(args.positionals, 1, "Missing public handle"),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
printValue(
|
|
36
|
+
io,
|
|
37
|
+
context.outputMode,
|
|
38
|
+
context.outputMode === "json"
|
|
39
|
+
? user
|
|
40
|
+
: {
|
|
41
|
+
id: user.id,
|
|
42
|
+
email: user.attributes.email,
|
|
43
|
+
publicHandle: user.attributes.public_handle ?? "",
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
default:
|
|
50
|
+
throw new CliError("Unknown user subcommand", 2);
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/lib/args.ts
CHANGED
|
@@ -15,6 +15,7 @@ const CLI_OPTIONS = {
|
|
|
15
15
|
"master-token": { type: "string" },
|
|
16
16
|
readOnlyToken: { type: "string" },
|
|
17
17
|
"read-only-token": { type: "string" },
|
|
18
|
+
scope: { type: "string" },
|
|
18
19
|
name: { type: "string" },
|
|
19
20
|
description: { type: "string" },
|
|
20
21
|
save: { type: "boolean" },
|