@spectratools/xapi-cli 0.1.1 → 0.1.2
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 +55 -50
- package/dist/cli.js +381 -293
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,74 +1,79 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @spectratools/xapi-cli
|
|
2
2
|
|
|
3
|
-
X (Twitter) API v2 CLI for
|
|
3
|
+
X (Twitter) API v2 CLI for post, user, list, trend, timeline, and DM workflows.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
npx @spectra-the-bot/xapi-cli --help
|
|
8
|
+
pnpm add -g @spectratools/xapi-cli
|
|
10
9
|
```
|
|
11
10
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
### Posts
|
|
11
|
+
## LLM / Agent Discovery
|
|
15
12
|
|
|
16
13
|
```bash
|
|
17
|
-
|
|
18
|
-
xapi
|
|
19
|
-
xapi posts create --text "Hello world!" [--reply-to <id>] [--quote <id>]
|
|
20
|
-
xapi posts delete <id>
|
|
21
|
-
xapi posts likes <id>
|
|
22
|
-
xapi posts retweets <id>
|
|
23
|
-
```
|
|
14
|
+
# Emit machine-readable command metadata
|
|
15
|
+
xapi-cli --llms
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
# Register as a reusable local skill for agent runtimes
|
|
18
|
+
xapi-cli skills add
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
xapi
|
|
29
|
-
xapi users followers <username> [-n 100]
|
|
30
|
-
xapi users following <username>
|
|
31
|
-
xapi users posts <username> [-n 10]
|
|
32
|
-
xapi users mentions <username>
|
|
33
|
-
xapi users search <query>
|
|
20
|
+
# Register as an MCP server entry
|
|
21
|
+
xapi-cli mcp add
|
|
34
22
|
```
|
|
35
23
|
|
|
36
|
-
|
|
24
|
+
## Configuration
|
|
37
25
|
|
|
38
26
|
```bash
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### Lists
|
|
27
|
+
# Read-only endpoints (search, profiles, trends, list reads)
|
|
28
|
+
export X_BEARER_TOKEN=your_app_bearer_token
|
|
44
29
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
xapi lists posts <id> [-n 25]
|
|
30
|
+
# Write endpoints (create/delete post, send DM)
|
|
31
|
+
# OAuth 2.0 user context token with the required write scopes
|
|
32
|
+
export X_ACCESS_TOKEN=your_oauth2_user_access_token
|
|
49
33
|
```
|
|
50
34
|
|
|
51
|
-
|
|
35
|
+
Auth behavior:
|
|
36
|
+
- Reads use `X_ACCESS_TOKEN` when present, otherwise fall back to `X_BEARER_TOKEN`.
|
|
37
|
+
- Writes require `X_ACCESS_TOKEN` and will return a structured auth error if missing/insufficient.
|
|
52
38
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
39
|
+
## Command Group Intent Summary
|
|
40
|
+
|
|
41
|
+
- `posts` — Read/search/create/delete posts and inspect social engagement
|
|
42
|
+
- `users` — Profile lookup, social graph traversal, and user timelines
|
|
43
|
+
- `timeline` — Home timeline and mention stream monitoring
|
|
44
|
+
- `lists` — List discovery, member inspection, and list feed reads
|
|
45
|
+
- `trends` — Trend place discovery and per-location trend fetch
|
|
46
|
+
- `dm` — Conversation listing and outbound direct messages
|
|
57
47
|
|
|
58
|
-
|
|
48
|
+
## Agent-Oriented Examples
|
|
59
49
|
|
|
60
50
|
```bash
|
|
61
|
-
|
|
62
|
-
xapi
|
|
51
|
+
# 1) Trend-to-content pipeline
|
|
52
|
+
xapi-cli trends places --format json
|
|
53
|
+
xapi-cli trends location 1 --format json
|
|
54
|
+
xapi-cli posts search "AI agents" --sort relevancy --max-results 20 --format json
|
|
55
|
+
|
|
56
|
+
# 2) User intelligence pass
|
|
57
|
+
xapi-cli users get jack --format json
|
|
58
|
+
xapi-cli users posts jack --max-results 20 --format json
|
|
59
|
+
xapi-cli users followers jack --max-results 100 --format json
|
|
60
|
+
|
|
61
|
+
# 3) Moderation helper flow
|
|
62
|
+
xapi-cli posts get 1234567890 --format json
|
|
63
|
+
xapi-cli posts likes 1234567890 --max-results 100 --format json
|
|
64
|
+
xapi-cli posts retweets 1234567890 --max-results 100 --format json
|
|
65
|
+
|
|
66
|
+
# 4) Timeline monitor
|
|
67
|
+
xapi-cli timeline home --max-results 50 --format json
|
|
68
|
+
xapi-cli timeline mentions --max-results 50 --format json
|
|
69
|
+
|
|
70
|
+
# 5) DM assistant loop
|
|
71
|
+
xapi-cli dm conversations --max-results 20 --format json
|
|
72
|
+
xapi-cli dm send 12345 --text "hello from agent" --format json
|
|
63
73
|
```
|
|
64
74
|
|
|
65
|
-
##
|
|
66
|
-
|
|
67
|
-
- `--verbose` — Show full text without truncation
|
|
68
|
-
- `-n, --max-results` — Control result count
|
|
69
|
-
- `--format json` — JSON output
|
|
70
|
-
- `--help` — Show help
|
|
71
|
-
|
|
72
|
-
## Auth
|
|
75
|
+
## Notes
|
|
73
76
|
|
|
74
|
-
All
|
|
77
|
+
- All commands support JSON output with `--format json`.
|
|
78
|
+
- `X_BEARER_TOKEN` is for read-only app auth.
|
|
79
|
+
- `X_ACCESS_TOKEN` is required for write actions (`posts create`, `posts delete`, `dm send`).
|
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { realpathSync } from "fs";
|
|
4
5
|
import { fileURLToPath } from "url";
|
|
5
6
|
import { Cli as Cli7 } from "incur";
|
|
6
7
|
|
|
7
8
|
// src/commands/dm.ts
|
|
8
|
-
import {
|
|
9
|
-
import { Cli, z } from "incur";
|
|
9
|
+
import { Cli, z as z2 } from "incur";
|
|
10
10
|
|
|
11
11
|
// src/api.ts
|
|
12
12
|
import { createHttpClient, withRetry } from "@spectratools/cli-shared";
|
|
@@ -214,6 +214,66 @@ function truncateText(text, max = 100) {
|
|
|
214
214
|
return `${text.slice(0, max - 3)}...`;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
// src/auth.ts
|
|
218
|
+
import { HttpError } from "@spectratools/cli-shared";
|
|
219
|
+
import { z } from "incur";
|
|
220
|
+
var bearerTokenSchema = z.string().describe("X app-only bearer token (read-only endpoints)");
|
|
221
|
+
var accessTokenSchema = z.string().describe("X OAuth 2.0 user access token (required for write endpoints)");
|
|
222
|
+
var xApiReadEnv = z.object({
|
|
223
|
+
X_BEARER_TOKEN: bearerTokenSchema.optional(),
|
|
224
|
+
X_ACCESS_TOKEN: accessTokenSchema.optional()
|
|
225
|
+
}).refine((env) => Boolean(env.X_ACCESS_TOKEN || env.X_BEARER_TOKEN), {
|
|
226
|
+
message: "Set X_ACCESS_TOKEN or X_BEARER_TOKEN to authenticate X API requests."
|
|
227
|
+
});
|
|
228
|
+
var xApiWriteEnv = z.object({
|
|
229
|
+
X_ACCESS_TOKEN: accessTokenSchema,
|
|
230
|
+
X_BEARER_TOKEN: bearerTokenSchema.optional()
|
|
231
|
+
});
|
|
232
|
+
function readAuthToken(env) {
|
|
233
|
+
if (env.X_ACCESS_TOKEN) {
|
|
234
|
+
return env.X_ACCESS_TOKEN;
|
|
235
|
+
}
|
|
236
|
+
return env.X_BEARER_TOKEN;
|
|
237
|
+
}
|
|
238
|
+
function writeAuthToken(env) {
|
|
239
|
+
return env.X_ACCESS_TOKEN;
|
|
240
|
+
}
|
|
241
|
+
function parseXApiErrorDetail(body) {
|
|
242
|
+
try {
|
|
243
|
+
const parsed = JSON.parse(body);
|
|
244
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
245
|
+
return void 0;
|
|
246
|
+
}
|
|
247
|
+
const candidate = parsed;
|
|
248
|
+
if (typeof candidate.detail === "string" && candidate.detail.trim()) return candidate.detail;
|
|
249
|
+
if (typeof candidate.title === "string" && candidate.title.trim()) return candidate.title;
|
|
250
|
+
const firstError = candidate.errors?.[0];
|
|
251
|
+
if (typeof firstError?.message === "string" && firstError.message.trim())
|
|
252
|
+
return firstError.message;
|
|
253
|
+
if (typeof firstError?.detail === "string" && firstError.detail.trim())
|
|
254
|
+
return firstError.detail;
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
return void 0;
|
|
258
|
+
}
|
|
259
|
+
function toWriteAuthError(operation, error) {
|
|
260
|
+
if (error instanceof HttpError && (error.status === 401 || error.status === 403)) {
|
|
261
|
+
const detail = parseXApiErrorDetail(error.body);
|
|
262
|
+
return {
|
|
263
|
+
code: "INSUFFICIENT_WRITE_AUTH",
|
|
264
|
+
message: [
|
|
265
|
+
"Insufficient auth for write endpoint:",
|
|
266
|
+
`- operation: ${operation}`,
|
|
267
|
+
`- status: ${error.status} ${error.statusText}`,
|
|
268
|
+
"- required auth: X_ACCESS_TOKEN (OAuth 2.0 user token with write scopes)",
|
|
269
|
+
"- note: app-only X_BEARER_TOKEN cannot perform write actions",
|
|
270
|
+
...detail ? [`- x_api_detail: ${detail}`] : []
|
|
271
|
+
].join("\n")
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return void 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
217
277
|
// src/collect-paged.ts
|
|
218
278
|
import { paginateCursor } from "@spectratools/cli-shared";
|
|
219
279
|
async function collectPaged(fetchFn, mapFn, maxResults, pageSize = 100) {
|
|
@@ -242,23 +302,23 @@ var dm = Cli.create("dm", {
|
|
|
242
302
|
});
|
|
243
303
|
dm.command("conversations", {
|
|
244
304
|
description: "List your DM conversations.",
|
|
245
|
-
options:
|
|
246
|
-
maxResults:
|
|
305
|
+
options: z2.object({
|
|
306
|
+
maxResults: z2.number().default(20).describe("Maximum conversations to return")
|
|
247
307
|
}),
|
|
248
308
|
alias: { maxResults: "n" },
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
309
|
+
env: xApiReadEnv,
|
|
310
|
+
output: z2.object({
|
|
311
|
+
conversations: z2.array(
|
|
312
|
+
z2.object({
|
|
313
|
+
dm_conversation_id: z2.string(),
|
|
314
|
+
participant_ids: z2.array(z2.string())
|
|
254
315
|
})
|
|
255
316
|
),
|
|
256
|
-
count:
|
|
317
|
+
count: z2.number()
|
|
257
318
|
}),
|
|
258
319
|
examples: [{ description: "List your DM conversations" }],
|
|
259
320
|
async run(c) {
|
|
260
|
-
const
|
|
261
|
-
const client = createXApiClient(apiKey);
|
|
321
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
262
322
|
const meRes = await client.getMe();
|
|
263
323
|
const userId = meRes.data.id;
|
|
264
324
|
const allConvos = await collectPaged(
|
|
@@ -290,15 +350,16 @@ dm.command("conversations", {
|
|
|
290
350
|
});
|
|
291
351
|
dm.command("send", {
|
|
292
352
|
description: "Send a direct message to a user.",
|
|
293
|
-
args:
|
|
294
|
-
participantId:
|
|
353
|
+
args: z2.object({
|
|
354
|
+
participantId: z2.string().describe("User ID to send message to")
|
|
295
355
|
}),
|
|
296
|
-
options:
|
|
297
|
-
text:
|
|
356
|
+
options: z2.object({
|
|
357
|
+
text: z2.string().describe("Message text")
|
|
298
358
|
}),
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
359
|
+
env: xApiWriteEnv,
|
|
360
|
+
output: z2.object({
|
|
361
|
+
dm_conversation_id: z2.string(),
|
|
362
|
+
dm_event_id: z2.string()
|
|
302
363
|
}),
|
|
303
364
|
examples: [
|
|
304
365
|
{
|
|
@@ -308,35 +369,39 @@ dm.command("send", {
|
|
|
308
369
|
}
|
|
309
370
|
],
|
|
310
371
|
async run(c) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
372
|
+
try {
|
|
373
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
374
|
+
const res = await client.sendDm(c.args.participantId, c.options.text);
|
|
375
|
+
return c.ok(res.data);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
const authError = toWriteAuthError("dm send", error);
|
|
378
|
+
if (authError) return c.error(authError);
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
315
381
|
}
|
|
316
382
|
});
|
|
317
383
|
|
|
318
384
|
// src/commands/lists.ts
|
|
319
|
-
import {
|
|
320
|
-
import { Cli as Cli2, z as z2 } from "incur";
|
|
385
|
+
import { Cli as Cli2, z as z3 } from "incur";
|
|
321
386
|
var lists = Cli2.create("lists", {
|
|
322
387
|
description: "Manage and browse X lists."
|
|
323
388
|
});
|
|
324
389
|
lists.command("get", {
|
|
325
390
|
description: "Get a list by ID.",
|
|
326
|
-
args:
|
|
327
|
-
id:
|
|
391
|
+
args: z3.object({
|
|
392
|
+
id: z3.string().describe("List ID")
|
|
328
393
|
}),
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
394
|
+
env: xApiReadEnv,
|
|
395
|
+
output: z3.object({
|
|
396
|
+
id: z3.string(),
|
|
397
|
+
name: z3.string(),
|
|
398
|
+
description: z3.string().optional(),
|
|
399
|
+
owner_id: z3.string().optional(),
|
|
400
|
+
member_count: z3.number().optional()
|
|
335
401
|
}),
|
|
336
402
|
examples: [{ args: { id: "1234567890" }, description: "Get list details" }],
|
|
337
403
|
async run(c) {
|
|
338
|
-
const
|
|
339
|
-
const client = createXApiClient(apiKey);
|
|
404
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
340
405
|
const res = await client.getList(c.args.id);
|
|
341
406
|
const list = res.data;
|
|
342
407
|
return c.ok(
|
|
@@ -361,28 +426,28 @@ lists.command("get", {
|
|
|
361
426
|
});
|
|
362
427
|
lists.command("members", {
|
|
363
428
|
description: "List members of an X list.",
|
|
364
|
-
args:
|
|
365
|
-
id:
|
|
429
|
+
args: z3.object({
|
|
430
|
+
id: z3.string().describe("List ID")
|
|
366
431
|
}),
|
|
367
|
-
options:
|
|
368
|
-
maxResults:
|
|
432
|
+
options: z3.object({
|
|
433
|
+
maxResults: z3.number().default(100).describe("Maximum members to return")
|
|
369
434
|
}),
|
|
370
435
|
alias: { maxResults: "n" },
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
436
|
+
env: xApiReadEnv,
|
|
437
|
+
output: z3.object({
|
|
438
|
+
users: z3.array(
|
|
439
|
+
z3.object({
|
|
440
|
+
id: z3.string(),
|
|
441
|
+
name: z3.string(),
|
|
442
|
+
username: z3.string(),
|
|
443
|
+
followers: z3.number().optional()
|
|
378
444
|
})
|
|
379
445
|
),
|
|
380
|
-
count:
|
|
446
|
+
count: z3.number()
|
|
381
447
|
}),
|
|
382
448
|
examples: [{ args: { id: "1234567890" }, description: "List all members" }],
|
|
383
449
|
async run(c) {
|
|
384
|
-
const
|
|
385
|
-
const client = createXApiClient(apiKey);
|
|
450
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
386
451
|
const allUsers = await collectPaged(
|
|
387
452
|
(limit, cursor) => client.getListMembers(c.args.id, limit, cursor),
|
|
388
453
|
(user) => ({
|
|
@@ -398,30 +463,30 @@ lists.command("members", {
|
|
|
398
463
|
});
|
|
399
464
|
lists.command("posts", {
|
|
400
465
|
description: "Get posts from an X list.",
|
|
401
|
-
args:
|
|
402
|
-
id:
|
|
466
|
+
args: z3.object({
|
|
467
|
+
id: z3.string().describe("List ID")
|
|
403
468
|
}),
|
|
404
|
-
options:
|
|
405
|
-
maxResults:
|
|
406
|
-
verbose:
|
|
469
|
+
options: z3.object({
|
|
470
|
+
maxResults: z3.number().default(25).describe("Maximum posts to return"),
|
|
471
|
+
verbose: z3.boolean().optional().describe("Show full text")
|
|
407
472
|
}),
|
|
408
473
|
alias: { maxResults: "n" },
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
474
|
+
env: xApiReadEnv,
|
|
475
|
+
output: z3.object({
|
|
476
|
+
posts: z3.array(
|
|
477
|
+
z3.object({
|
|
478
|
+
id: z3.string(),
|
|
479
|
+
text: z3.string(),
|
|
480
|
+
author_id: z3.string().optional(),
|
|
481
|
+
created_at: z3.string().optional(),
|
|
482
|
+
likes: z3.number().optional()
|
|
417
483
|
})
|
|
418
484
|
),
|
|
419
|
-
count:
|
|
485
|
+
count: z3.number()
|
|
420
486
|
}),
|
|
421
487
|
examples: [{ args: { id: "1234567890" }, description: "Get posts from a list" }],
|
|
422
488
|
async run(c) {
|
|
423
|
-
const
|
|
424
|
-
const client = createXApiClient(apiKey);
|
|
489
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
425
490
|
const allPosts = await collectPaged(
|
|
426
491
|
(limit, cursor) => client.getListPosts(c.args.id, limit, cursor),
|
|
427
492
|
(post) => ({
|
|
@@ -453,32 +518,31 @@ lists.command("posts", {
|
|
|
453
518
|
});
|
|
454
519
|
|
|
455
520
|
// src/commands/posts.ts
|
|
456
|
-
import {
|
|
457
|
-
import { Cli as Cli3, z as z3 } from "incur";
|
|
521
|
+
import { Cli as Cli3, z as z4 } from "incur";
|
|
458
522
|
var posts = Cli3.create("posts", {
|
|
459
523
|
description: "Manage and search X posts."
|
|
460
524
|
});
|
|
461
525
|
posts.command("get", {
|
|
462
526
|
description: "Get a post by ID.",
|
|
463
|
-
args:
|
|
464
|
-
id:
|
|
527
|
+
args: z4.object({
|
|
528
|
+
id: z4.string().describe("Post ID")
|
|
465
529
|
}),
|
|
466
|
-
options:
|
|
467
|
-
verbose:
|
|
530
|
+
options: z4.object({
|
|
531
|
+
verbose: z4.boolean().optional().describe("Show full text without truncation")
|
|
468
532
|
}),
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
533
|
+
env: xApiReadEnv,
|
|
534
|
+
output: z4.object({
|
|
535
|
+
id: z4.string(),
|
|
536
|
+
text: z4.string(),
|
|
537
|
+
author_id: z4.string().optional(),
|
|
538
|
+
created_at: z4.string().optional(),
|
|
539
|
+
likes: z4.number().optional(),
|
|
540
|
+
retweets: z4.number().optional(),
|
|
541
|
+
replies: z4.number().optional()
|
|
477
542
|
}),
|
|
478
543
|
examples: [{ args: { id: "1234567890" }, description: "Get a post by ID" }],
|
|
479
544
|
async run(c) {
|
|
480
|
-
const
|
|
481
|
-
const client = createXApiClient(apiKey);
|
|
545
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
482
546
|
const res = await client.getPost(c.args.id);
|
|
483
547
|
const post = res.data;
|
|
484
548
|
const text = c.options.verbose ? post.text : truncateText(post.text);
|
|
@@ -514,26 +578,27 @@ posts.command("get", {
|
|
|
514
578
|
});
|
|
515
579
|
posts.command("search", {
|
|
516
580
|
description: "Search recent posts.",
|
|
517
|
-
args:
|
|
518
|
-
query:
|
|
581
|
+
args: z4.object({
|
|
582
|
+
query: z4.string().describe("Search query")
|
|
519
583
|
}),
|
|
520
|
-
options:
|
|
521
|
-
maxResults:
|
|
522
|
-
sort:
|
|
523
|
-
verbose:
|
|
584
|
+
options: z4.object({
|
|
585
|
+
maxResults: z4.number().default(10).describe("Maximum results to return (10\u2013100)"),
|
|
586
|
+
sort: z4.enum(["recency", "relevancy"]).default("recency").describe("Sort order"),
|
|
587
|
+
verbose: z4.boolean().optional().describe("Show full text without truncation")
|
|
524
588
|
}),
|
|
525
589
|
alias: { maxResults: "n" },
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
590
|
+
env: xApiReadEnv,
|
|
591
|
+
output: z4.object({
|
|
592
|
+
posts: z4.array(
|
|
593
|
+
z4.object({
|
|
594
|
+
id: z4.string(),
|
|
595
|
+
text: z4.string(),
|
|
596
|
+
created_at: z4.string().optional(),
|
|
597
|
+
likes: z4.number().optional(),
|
|
598
|
+
retweets: z4.number().optional()
|
|
534
599
|
})
|
|
535
600
|
),
|
|
536
|
-
count:
|
|
601
|
+
count: z4.number()
|
|
537
602
|
}),
|
|
538
603
|
examples: [
|
|
539
604
|
{ args: { query: "TypeScript" }, description: "Search for TypeScript posts" },
|
|
@@ -544,8 +609,7 @@ posts.command("search", {
|
|
|
544
609
|
}
|
|
545
610
|
],
|
|
546
611
|
async run(c) {
|
|
547
|
-
const
|
|
548
|
-
const client = createXApiClient(apiKey);
|
|
612
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
549
613
|
const res = await client.searchPosts(c.args.query, c.options.maxResults, c.options.sort);
|
|
550
614
|
const items = (res.data ?? []).map((p) => ({
|
|
551
615
|
id: p.id,
|
|
@@ -574,74 +638,90 @@ posts.command("search", {
|
|
|
574
638
|
});
|
|
575
639
|
posts.command("create", {
|
|
576
640
|
description: "Create a new post.",
|
|
577
|
-
options:
|
|
578
|
-
text:
|
|
579
|
-
replyTo:
|
|
580
|
-
quote:
|
|
641
|
+
options: z4.object({
|
|
642
|
+
text: z4.string().describe("Post text"),
|
|
643
|
+
replyTo: z4.string().optional().describe("Reply to post ID"),
|
|
644
|
+
quote: z4.string().optional().describe("Quote post ID")
|
|
581
645
|
}),
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
646
|
+
env: xApiWriteEnv,
|
|
647
|
+
output: z4.object({
|
|
648
|
+
id: z4.string(),
|
|
649
|
+
text: z4.string()
|
|
585
650
|
}),
|
|
586
651
|
examples: [
|
|
587
652
|
{ options: { text: "Hello world!" }, description: "Post a simple message" },
|
|
588
653
|
{ options: { text: "Great point!", replyTo: "1234567890" }, description: "Reply to a post" }
|
|
589
654
|
],
|
|
590
655
|
async run(c) {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
656
|
+
try {
|
|
657
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
658
|
+
const res = await client.createPost(c.options.text, c.options.replyTo, c.options.quote);
|
|
659
|
+
return c.ok(res.data, {
|
|
660
|
+
cta: {
|
|
661
|
+
description: "View your post:",
|
|
662
|
+
commands: [
|
|
663
|
+
{
|
|
664
|
+
command: "posts get",
|
|
665
|
+
args: { id: res.data.id },
|
|
666
|
+
description: "See the created post"
|
|
667
|
+
}
|
|
668
|
+
]
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
} catch (error) {
|
|
672
|
+
const authError = toWriteAuthError("posts create", error);
|
|
673
|
+
if (authError) return c.error(authError);
|
|
674
|
+
throw error;
|
|
675
|
+
}
|
|
602
676
|
}
|
|
603
677
|
});
|
|
604
678
|
posts.command("delete", {
|
|
605
679
|
description: "Delete a post by ID.",
|
|
606
|
-
args:
|
|
607
|
-
id:
|
|
680
|
+
args: z4.object({
|
|
681
|
+
id: z4.string().describe("Post ID to delete")
|
|
608
682
|
}),
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
683
|
+
env: xApiWriteEnv,
|
|
684
|
+
output: z4.object({
|
|
685
|
+
deleted: z4.boolean(),
|
|
686
|
+
id: z4.string()
|
|
612
687
|
}),
|
|
613
688
|
examples: [{ args: { id: "1234567890" }, description: "Delete a post" }],
|
|
614
689
|
async run(c) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
690
|
+
try {
|
|
691
|
+
const client = createXApiClient(writeAuthToken(c.env));
|
|
692
|
+
const res = await client.deletePost(c.args.id);
|
|
693
|
+
return c.ok({ deleted: res.data.deleted, id: c.args.id });
|
|
694
|
+
} catch (error) {
|
|
695
|
+
const authError = toWriteAuthError("posts delete", error);
|
|
696
|
+
if (authError) return c.error(authError);
|
|
697
|
+
throw error;
|
|
698
|
+
}
|
|
619
699
|
}
|
|
620
700
|
});
|
|
621
701
|
posts.command("likes", {
|
|
622
702
|
description: "List users who liked a post.",
|
|
623
|
-
args:
|
|
624
|
-
id:
|
|
703
|
+
args: z4.object({
|
|
704
|
+
id: z4.string().describe("Post ID")
|
|
625
705
|
}),
|
|
626
|
-
options:
|
|
627
|
-
maxResults:
|
|
706
|
+
options: z4.object({
|
|
707
|
+
maxResults: z4.number().default(100).describe("Maximum users to return")
|
|
628
708
|
}),
|
|
629
709
|
alias: { maxResults: "n" },
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
710
|
+
env: xApiReadEnv,
|
|
711
|
+
output: z4.object({
|
|
712
|
+
users: z4.array(
|
|
713
|
+
z4.object({
|
|
714
|
+
id: z4.string(),
|
|
715
|
+
name: z4.string(),
|
|
716
|
+
username: z4.string(),
|
|
717
|
+
followers: z4.number().optional()
|
|
637
718
|
})
|
|
638
719
|
),
|
|
639
|
-
count:
|
|
720
|
+
count: z4.number()
|
|
640
721
|
}),
|
|
641
722
|
examples: [{ args: { id: "1234567890" }, description: "See who liked a post" }],
|
|
642
723
|
async run(c) {
|
|
643
|
-
const
|
|
644
|
-
const client = createXApiClient(apiKey);
|
|
724
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
645
725
|
const allUsers = await collectPaged(
|
|
646
726
|
(limit, cursor) => client.getPostLikes(c.args.id, limit, cursor),
|
|
647
727
|
(user) => ({
|
|
@@ -669,28 +749,28 @@ posts.command("likes", {
|
|
|
669
749
|
});
|
|
670
750
|
posts.command("retweets", {
|
|
671
751
|
description: "List users who retweeted a post.",
|
|
672
|
-
args:
|
|
673
|
-
id:
|
|
752
|
+
args: z4.object({
|
|
753
|
+
id: z4.string().describe("Post ID")
|
|
674
754
|
}),
|
|
675
|
-
options:
|
|
676
|
-
maxResults:
|
|
755
|
+
options: z4.object({
|
|
756
|
+
maxResults: z4.number().default(100).describe("Maximum users to return")
|
|
677
757
|
}),
|
|
678
758
|
alias: { maxResults: "n" },
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
759
|
+
env: xApiReadEnv,
|
|
760
|
+
output: z4.object({
|
|
761
|
+
users: z4.array(
|
|
762
|
+
z4.object({
|
|
763
|
+
id: z4.string(),
|
|
764
|
+
name: z4.string(),
|
|
765
|
+
username: z4.string(),
|
|
766
|
+
followers: z4.number().optional()
|
|
686
767
|
})
|
|
687
768
|
),
|
|
688
|
-
count:
|
|
769
|
+
count: z4.number()
|
|
689
770
|
}),
|
|
690
771
|
examples: [{ args: { id: "1234567890" }, description: "See who retweeted a post" }],
|
|
691
772
|
async run(c) {
|
|
692
|
-
const
|
|
693
|
-
const client = createXApiClient(apiKey);
|
|
773
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
694
774
|
const allUsers = await collectPaged(
|
|
695
775
|
(limit, cursor) => client.getPostRetweets(c.args.id, limit, cursor),
|
|
696
776
|
(user) => ({
|
|
@@ -706,38 +786,37 @@ posts.command("retweets", {
|
|
|
706
786
|
});
|
|
707
787
|
|
|
708
788
|
// src/commands/timeline.ts
|
|
709
|
-
import {
|
|
710
|
-
import { Cli as Cli4, z as z4 } from "incur";
|
|
789
|
+
import { Cli as Cli4, z as z5 } from "incur";
|
|
711
790
|
var timeline = Cli4.create("timeline", {
|
|
712
791
|
description: "View your X timeline."
|
|
713
792
|
});
|
|
714
793
|
timeline.command("home", {
|
|
715
794
|
description: "View your home timeline.",
|
|
716
|
-
options:
|
|
717
|
-
maxResults:
|
|
718
|
-
verbose:
|
|
795
|
+
options: z5.object({
|
|
796
|
+
maxResults: z5.number().default(25).describe("Maximum posts to return (5\u2013100)"),
|
|
797
|
+
verbose: z5.boolean().optional().describe("Show full text without truncation")
|
|
719
798
|
}),
|
|
720
799
|
alias: { maxResults: "n" },
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
800
|
+
env: xApiReadEnv,
|
|
801
|
+
output: z5.object({
|
|
802
|
+
posts: z5.array(
|
|
803
|
+
z5.object({
|
|
804
|
+
id: z5.string(),
|
|
805
|
+
text: z5.string(),
|
|
806
|
+
author_id: z5.string().optional(),
|
|
807
|
+
created_at: z5.string().optional(),
|
|
808
|
+
likes: z5.number().optional(),
|
|
809
|
+
retweets: z5.number().optional()
|
|
730
810
|
})
|
|
731
811
|
),
|
|
732
|
-
count:
|
|
812
|
+
count: z5.number()
|
|
733
813
|
}),
|
|
734
814
|
examples: [
|
|
735
815
|
{ description: "View your home timeline" },
|
|
736
816
|
{ options: { maxResults: 50 }, description: "View 50 posts" }
|
|
737
817
|
],
|
|
738
818
|
async run(c) {
|
|
739
|
-
const
|
|
740
|
-
const client = createXApiClient(apiKey);
|
|
819
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
741
820
|
const meRes = await client.getMe();
|
|
742
821
|
const userId = meRes.data.id;
|
|
743
822
|
const allPosts = await collectPaged(
|
|
@@ -772,26 +851,26 @@ timeline.command("home", {
|
|
|
772
851
|
});
|
|
773
852
|
timeline.command("mentions", {
|
|
774
853
|
description: "View your recent mentions.",
|
|
775
|
-
options:
|
|
776
|
-
maxResults:
|
|
777
|
-
verbose:
|
|
854
|
+
options: z5.object({
|
|
855
|
+
maxResults: z5.number().default(25).describe("Maximum mentions to return"),
|
|
856
|
+
verbose: z5.boolean().optional().describe("Show full text without truncation")
|
|
778
857
|
}),
|
|
779
858
|
alias: { maxResults: "n" },
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
859
|
+
env: xApiReadEnv,
|
|
860
|
+
output: z5.object({
|
|
861
|
+
posts: z5.array(
|
|
862
|
+
z5.object({
|
|
863
|
+
id: z5.string(),
|
|
864
|
+
text: z5.string(),
|
|
865
|
+
author_id: z5.string().optional(),
|
|
866
|
+
created_at: z5.string().optional()
|
|
787
867
|
})
|
|
788
868
|
),
|
|
789
|
-
count:
|
|
869
|
+
count: z5.number()
|
|
790
870
|
}),
|
|
791
871
|
examples: [{ description: "View your recent mentions" }],
|
|
792
872
|
async run(c) {
|
|
793
|
-
const
|
|
794
|
-
const client = createXApiClient(apiKey);
|
|
873
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
795
874
|
const meRes = await client.getMe();
|
|
796
875
|
const userId = meRes.data.id;
|
|
797
876
|
const allPosts = await collectPaged(
|
|
@@ -809,27 +888,26 @@ timeline.command("mentions", {
|
|
|
809
888
|
});
|
|
810
889
|
|
|
811
890
|
// src/commands/trends.ts
|
|
812
|
-
import {
|
|
813
|
-
import { Cli as Cli5, z as z5 } from "incur";
|
|
891
|
+
import { Cli as Cli5, z as z6 } from "incur";
|
|
814
892
|
var trends = Cli5.create("trends", {
|
|
815
893
|
description: "Explore trending topics on X."
|
|
816
894
|
});
|
|
817
895
|
trends.command("places", {
|
|
818
896
|
description: "List places where trending topics are available.",
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
897
|
+
env: xApiReadEnv,
|
|
898
|
+
output: z6.object({
|
|
899
|
+
places: z6.array(
|
|
900
|
+
z6.object({
|
|
901
|
+
woeid: z6.number(),
|
|
902
|
+
name: z6.string(),
|
|
903
|
+
country: z6.string()
|
|
825
904
|
})
|
|
826
905
|
),
|
|
827
|
-
count:
|
|
906
|
+
count: z6.number()
|
|
828
907
|
}),
|
|
829
908
|
examples: [{ description: "List all trending places" }],
|
|
830
909
|
async run(c) {
|
|
831
|
-
const
|
|
832
|
-
const client = createXApiClient(apiKey);
|
|
910
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
833
911
|
const res = await client.getTrendingPlaces();
|
|
834
912
|
const places = res.data ?? [];
|
|
835
913
|
const first = places[0];
|
|
@@ -852,26 +930,26 @@ trends.command("places", {
|
|
|
852
930
|
});
|
|
853
931
|
trends.command("location", {
|
|
854
932
|
description: "Get trending topics for a specific location (WOEID).",
|
|
855
|
-
args:
|
|
856
|
-
woeid:
|
|
933
|
+
args: z6.object({
|
|
934
|
+
woeid: z6.string().describe("Where On Earth ID (from trends places)")
|
|
857
935
|
}),
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
936
|
+
env: xApiReadEnv,
|
|
937
|
+
output: z6.object({
|
|
938
|
+
trends: z6.array(
|
|
939
|
+
z6.object({
|
|
940
|
+
name: z6.string(),
|
|
941
|
+
query: z6.string(),
|
|
942
|
+
tweet_volume: z6.number().optional()
|
|
864
943
|
})
|
|
865
944
|
),
|
|
866
|
-
count:
|
|
945
|
+
count: z6.number()
|
|
867
946
|
}),
|
|
868
947
|
examples: [
|
|
869
948
|
{ args: { woeid: "1" }, description: "Get worldwide trends" },
|
|
870
949
|
{ args: { woeid: "2459115" }, description: "Get trends for New York" }
|
|
871
950
|
],
|
|
872
951
|
async run(c) {
|
|
873
|
-
const
|
|
874
|
-
const client = createXApiClient(apiKey);
|
|
952
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
875
953
|
const res = await client.getTrendsByLocation(Number(c.args.woeid));
|
|
876
954
|
const trendItems = res.data ?? [];
|
|
877
955
|
const firstTrend = trendItems[0];
|
|
@@ -894,8 +972,7 @@ trends.command("location", {
|
|
|
894
972
|
});
|
|
895
973
|
|
|
896
974
|
// src/commands/users.ts
|
|
897
|
-
import {
|
|
898
|
-
import { Cli as Cli6, z as z6 } from "incur";
|
|
975
|
+
import { Cli as Cli6, z as z7 } from "incur";
|
|
899
976
|
var users = Cli6.create("users", {
|
|
900
977
|
description: "Look up X users."
|
|
901
978
|
});
|
|
@@ -907,29 +984,29 @@ async function resolveUser(client, usernameOrId) {
|
|
|
907
984
|
}
|
|
908
985
|
users.command("get", {
|
|
909
986
|
description: "Get a user by username or ID.",
|
|
910
|
-
args:
|
|
911
|
-
username:
|
|
987
|
+
args: z7.object({
|
|
988
|
+
username: z7.string().describe("Username (with or without @) or user ID")
|
|
912
989
|
}),
|
|
913
|
-
options:
|
|
914
|
-
verbose:
|
|
990
|
+
options: z7.object({
|
|
991
|
+
verbose: z7.boolean().optional().describe("Show full bio without truncation")
|
|
915
992
|
}),
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
993
|
+
env: xApiReadEnv,
|
|
994
|
+
output: z7.object({
|
|
995
|
+
id: z7.string(),
|
|
996
|
+
name: z7.string(),
|
|
997
|
+
username: z7.string(),
|
|
998
|
+
description: z7.string().optional(),
|
|
999
|
+
followers: z7.number().optional(),
|
|
1000
|
+
following: z7.number().optional(),
|
|
1001
|
+
tweets: z7.number().optional(),
|
|
1002
|
+
joined: z7.string().optional()
|
|
925
1003
|
}),
|
|
926
1004
|
examples: [
|
|
927
1005
|
{ args: { username: "jack" }, description: "Get a user by username" },
|
|
928
1006
|
{ args: { username: "12345" }, description: "Get a user by ID" }
|
|
929
1007
|
],
|
|
930
1008
|
async run(c) {
|
|
931
|
-
const
|
|
932
|
-
const client = createXApiClient(apiKey);
|
|
1009
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
933
1010
|
const res = await resolveUser(client, c.args.username);
|
|
934
1011
|
const user = res.data;
|
|
935
1012
|
return c.ok(
|
|
@@ -965,28 +1042,28 @@ users.command("get", {
|
|
|
965
1042
|
});
|
|
966
1043
|
users.command("followers", {
|
|
967
1044
|
description: "List followers of a user.",
|
|
968
|
-
args:
|
|
969
|
-
username:
|
|
1045
|
+
args: z7.object({
|
|
1046
|
+
username: z7.string().describe("Username or user ID")
|
|
970
1047
|
}),
|
|
971
|
-
options:
|
|
972
|
-
maxResults:
|
|
1048
|
+
options: z7.object({
|
|
1049
|
+
maxResults: z7.number().default(100).describe("Maximum followers to return")
|
|
973
1050
|
}),
|
|
974
1051
|
alias: { maxResults: "n" },
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1052
|
+
env: xApiReadEnv,
|
|
1053
|
+
output: z7.object({
|
|
1054
|
+
users: z7.array(
|
|
1055
|
+
z7.object({
|
|
1056
|
+
id: z7.string(),
|
|
1057
|
+
name: z7.string(),
|
|
1058
|
+
username: z7.string(),
|
|
1059
|
+
followers: z7.number().optional()
|
|
982
1060
|
})
|
|
983
1061
|
),
|
|
984
|
-
count:
|
|
1062
|
+
count: z7.number()
|
|
985
1063
|
}),
|
|
986
1064
|
examples: [{ args: { username: "jack" }, description: "List followers of jack" }],
|
|
987
1065
|
async run(c) {
|
|
988
|
-
const
|
|
989
|
-
const client = createXApiClient(apiKey);
|
|
1066
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
990
1067
|
const userRes = await resolveUser(client, c.args.username);
|
|
991
1068
|
const userId = userRes.data.id;
|
|
992
1069
|
const allUsers = await collectPaged(
|
|
@@ -1005,28 +1082,28 @@ users.command("followers", {
|
|
|
1005
1082
|
});
|
|
1006
1083
|
users.command("following", {
|
|
1007
1084
|
description: "List accounts a user is following.",
|
|
1008
|
-
args:
|
|
1009
|
-
username:
|
|
1085
|
+
args: z7.object({
|
|
1086
|
+
username: z7.string().describe("Username or user ID")
|
|
1010
1087
|
}),
|
|
1011
|
-
options:
|
|
1012
|
-
maxResults:
|
|
1088
|
+
options: z7.object({
|
|
1089
|
+
maxResults: z7.number().default(100).describe("Maximum accounts to return")
|
|
1013
1090
|
}),
|
|
1014
1091
|
alias: { maxResults: "n" },
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1092
|
+
env: xApiReadEnv,
|
|
1093
|
+
output: z7.object({
|
|
1094
|
+
users: z7.array(
|
|
1095
|
+
z7.object({
|
|
1096
|
+
id: z7.string(),
|
|
1097
|
+
name: z7.string(),
|
|
1098
|
+
username: z7.string(),
|
|
1099
|
+
followers: z7.number().optional()
|
|
1022
1100
|
})
|
|
1023
1101
|
),
|
|
1024
|
-
count:
|
|
1102
|
+
count: z7.number()
|
|
1025
1103
|
}),
|
|
1026
1104
|
examples: [{ args: { username: "jack" }, description: "List accounts jack follows" }],
|
|
1027
1105
|
async run(c) {
|
|
1028
|
-
const
|
|
1029
|
-
const client = createXApiClient(apiKey);
|
|
1106
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
1030
1107
|
const userRes = await resolveUser(client, c.args.username);
|
|
1031
1108
|
const userId = userRes.data.id;
|
|
1032
1109
|
const allUsers = await collectPaged(
|
|
@@ -1045,30 +1122,30 @@ users.command("following", {
|
|
|
1045
1122
|
});
|
|
1046
1123
|
users.command("posts", {
|
|
1047
1124
|
description: "List a user's posts.",
|
|
1048
|
-
args:
|
|
1049
|
-
username:
|
|
1125
|
+
args: z7.object({
|
|
1126
|
+
username: z7.string().describe("Username or user ID")
|
|
1050
1127
|
}),
|
|
1051
|
-
options:
|
|
1052
|
-
maxResults:
|
|
1053
|
-
verbose:
|
|
1128
|
+
options: z7.object({
|
|
1129
|
+
maxResults: z7.number().default(10).describe("Maximum posts to return"),
|
|
1130
|
+
verbose: z7.boolean().optional().describe("Show full text without truncation")
|
|
1054
1131
|
}),
|
|
1055
1132
|
alias: { maxResults: "n" },
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1133
|
+
env: xApiReadEnv,
|
|
1134
|
+
output: z7.object({
|
|
1135
|
+
posts: z7.array(
|
|
1136
|
+
z7.object({
|
|
1137
|
+
id: z7.string(),
|
|
1138
|
+
text: z7.string(),
|
|
1139
|
+
created_at: z7.string().optional(),
|
|
1140
|
+
likes: z7.number().optional(),
|
|
1141
|
+
retweets: z7.number().optional()
|
|
1064
1142
|
})
|
|
1065
1143
|
),
|
|
1066
|
-
count:
|
|
1144
|
+
count: z7.number()
|
|
1067
1145
|
}),
|
|
1068
1146
|
examples: [{ args: { username: "jack" }, description: "Get jack's recent posts" }],
|
|
1069
1147
|
async run(c) {
|
|
1070
|
-
const
|
|
1071
|
-
const client = createXApiClient(apiKey);
|
|
1148
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
1072
1149
|
const userRes = await resolveUser(client, c.args.username);
|
|
1073
1150
|
const userId = userRes.data.id;
|
|
1074
1151
|
const allPosts = await collectPaged(
|
|
@@ -1102,28 +1179,28 @@ users.command("posts", {
|
|
|
1102
1179
|
});
|
|
1103
1180
|
users.command("mentions", {
|
|
1104
1181
|
description: "List recent mentions of a user.",
|
|
1105
|
-
args:
|
|
1106
|
-
username:
|
|
1182
|
+
args: z7.object({
|
|
1183
|
+
username: z7.string().describe("Username or user ID")
|
|
1107
1184
|
}),
|
|
1108
|
-
options:
|
|
1109
|
-
maxResults:
|
|
1110
|
-
verbose:
|
|
1185
|
+
options: z7.object({
|
|
1186
|
+
maxResults: z7.number().default(10).describe("Maximum mentions to return"),
|
|
1187
|
+
verbose: z7.boolean().optional().describe("Show full text")
|
|
1111
1188
|
}),
|
|
1112
1189
|
alias: { maxResults: "n" },
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1190
|
+
env: xApiReadEnv,
|
|
1191
|
+
output: z7.object({
|
|
1192
|
+
posts: z7.array(
|
|
1193
|
+
z7.object({
|
|
1194
|
+
id: z7.string(),
|
|
1195
|
+
text: z7.string(),
|
|
1196
|
+
created_at: z7.string().optional()
|
|
1119
1197
|
})
|
|
1120
1198
|
),
|
|
1121
|
-
count:
|
|
1199
|
+
count: z7.number()
|
|
1122
1200
|
}),
|
|
1123
1201
|
examples: [{ args: { username: "jack" }, description: "Get mentions of jack" }],
|
|
1124
1202
|
async run(c) {
|
|
1125
|
-
const
|
|
1126
|
-
const client = createXApiClient(apiKey);
|
|
1203
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
1127
1204
|
const userRes = await resolveUser(client, c.args.username);
|
|
1128
1205
|
const userId = userRes.data.id;
|
|
1129
1206
|
const allPosts = await collectPaged(
|
|
@@ -1140,24 +1217,24 @@ users.command("mentions", {
|
|
|
1140
1217
|
});
|
|
1141
1218
|
users.command("search", {
|
|
1142
1219
|
description: "Search for users by keyword.",
|
|
1143
|
-
args:
|
|
1144
|
-
query:
|
|
1220
|
+
args: z7.object({
|
|
1221
|
+
query: z7.string().describe("Search query")
|
|
1145
1222
|
}),
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1223
|
+
env: xApiReadEnv,
|
|
1224
|
+
output: z7.object({
|
|
1225
|
+
users: z7.array(
|
|
1226
|
+
z7.object({
|
|
1227
|
+
id: z7.string(),
|
|
1228
|
+
name: z7.string(),
|
|
1229
|
+
username: z7.string(),
|
|
1230
|
+
followers: z7.number().optional()
|
|
1153
1231
|
})
|
|
1154
1232
|
),
|
|
1155
|
-
count:
|
|
1233
|
+
count: z7.number()
|
|
1156
1234
|
}),
|
|
1157
1235
|
examples: [{ args: { query: "TypeScript" }, description: "Search for users about TypeScript" }],
|
|
1158
1236
|
async run(c) {
|
|
1159
|
-
const
|
|
1160
|
-
const client = createXApiClient(apiKey);
|
|
1237
|
+
const client = createXApiClient(readAuthToken(c.env));
|
|
1161
1238
|
const res = await client.searchUsers(c.args.query);
|
|
1162
1239
|
const items = (res.data ?? []).map((u) => ({
|
|
1163
1240
|
id: u.id,
|
|
@@ -1194,7 +1271,18 @@ cli.command(timeline);
|
|
|
1194
1271
|
cli.command(lists);
|
|
1195
1272
|
cli.command(trends);
|
|
1196
1273
|
cli.command(dm);
|
|
1197
|
-
|
|
1274
|
+
var isMain = (() => {
|
|
1275
|
+
const entrypoint = process.argv[1];
|
|
1276
|
+
if (!entrypoint) {
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
try {
|
|
1280
|
+
return realpathSync(entrypoint) === realpathSync(fileURLToPath(import.meta.url));
|
|
1281
|
+
} catch {
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
})();
|
|
1285
|
+
if (isMain) {
|
|
1198
1286
|
cli.serve();
|
|
1199
1287
|
}
|
|
1200
1288
|
export {
|