@dotta/xc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +348 -0
  2. package/dist/__tests__/bookmarks.test.d.ts +4 -0
  3. package/dist/__tests__/bookmarks.test.js +104 -0
  4. package/dist/__tests__/bookmarks.test.js.map +1 -0
  5. package/dist/__tests__/budget.test.d.ts +6 -0
  6. package/dist/__tests__/budget.test.js +105 -0
  7. package/dist/__tests__/budget.test.js.map +1 -0
  8. package/dist/__tests__/dm.test.d.ts +4 -0
  9. package/dist/__tests__/dm.test.js +115 -0
  10. package/dist/__tests__/dm.test.js.map +1 -0
  11. package/dist/__tests__/followers.test.d.ts +4 -0
  12. package/dist/__tests__/followers.test.js +129 -0
  13. package/dist/__tests__/followers.test.js.map +1 -0
  14. package/dist/__tests__/lib/api.test.d.ts +5 -0
  15. package/dist/__tests__/lib/api.test.js +202 -0
  16. package/dist/__tests__/lib/api.test.js.map +1 -0
  17. package/dist/__tests__/lib/budget.test.d.ts +5 -0
  18. package/dist/__tests__/lib/budget.test.js +194 -0
  19. package/dist/__tests__/lib/budget.test.js.map +1 -0
  20. package/dist/__tests__/lib/config.test.d.ts +6 -0
  21. package/dist/__tests__/lib/config.test.js +228 -0
  22. package/dist/__tests__/lib/config.test.js.map +1 -0
  23. package/dist/__tests__/lib/cost.test.d.ts +6 -0
  24. package/dist/__tests__/lib/cost.test.js +177 -0
  25. package/dist/__tests__/lib/cost.test.js.map +1 -0
  26. package/dist/__tests__/lib/format.test.d.ts +4 -0
  27. package/dist/__tests__/lib/format.test.js +139 -0
  28. package/dist/__tests__/lib/format.test.js.map +1 -0
  29. package/dist/__tests__/lib/oauth.test.d.ts +5 -0
  30. package/dist/__tests__/lib/oauth.test.js +123 -0
  31. package/dist/__tests__/lib/oauth.test.js.map +1 -0
  32. package/dist/__tests__/lib/resolve.test.d.ts +4 -0
  33. package/dist/__tests__/lib/resolve.test.js +154 -0
  34. package/dist/__tests__/lib/resolve.test.js.map +1 -0
  35. package/dist/__tests__/lists.test.d.ts +4 -0
  36. package/dist/__tests__/lists.test.js +96 -0
  37. package/dist/__tests__/lists.test.js.map +1 -0
  38. package/dist/__tests__/media.test.d.ts +4 -0
  39. package/dist/__tests__/media.test.js +132 -0
  40. package/dist/__tests__/media.test.js.map +1 -0
  41. package/dist/cli.d.ts +2 -0
  42. package/dist/cli.js +93 -0
  43. package/dist/cli.js.map +1 -0
  44. package/dist/commands/auth.d.ts +2 -0
  45. package/dist/commands/auth.js +191 -0
  46. package/dist/commands/auth.js.map +1 -0
  47. package/dist/commands/block.d.ts +15 -0
  48. package/dist/commands/block.js +117 -0
  49. package/dist/commands/block.js.map +1 -0
  50. package/dist/commands/bookmarks.d.ts +12 -0
  51. package/dist/commands/bookmarks.js +100 -0
  52. package/dist/commands/bookmarks.js.map +1 -0
  53. package/dist/commands/budget.d.ts +9 -0
  54. package/dist/commands/budget.js +124 -0
  55. package/dist/commands/budget.js.map +1 -0
  56. package/dist/commands/cost.d.ts +5 -0
  57. package/dist/commands/cost.js +75 -0
  58. package/dist/commands/cost.js.map +1 -0
  59. package/dist/commands/delete.d.ts +8 -0
  60. package/dist/commands/delete.js +31 -0
  61. package/dist/commands/delete.js.map +1 -0
  62. package/dist/commands/dm.d.ts +10 -0
  63. package/dist/commands/dm.js +179 -0
  64. package/dist/commands/dm.js.map +1 -0
  65. package/dist/commands/engagement.d.ts +14 -0
  66. package/dist/commands/engagement.js +167 -0
  67. package/dist/commands/engagement.js.map +1 -0
  68. package/dist/commands/followers.d.ts +14 -0
  69. package/dist/commands/followers.js +138 -0
  70. package/dist/commands/followers.js.map +1 -0
  71. package/dist/commands/get.d.ts +2 -0
  72. package/dist/commands/get.js +63 -0
  73. package/dist/commands/get.js.map +1 -0
  74. package/dist/commands/hide.d.ts +10 -0
  75. package/dist/commands/hide.js +58 -0
  76. package/dist/commands/hide.js.map +1 -0
  77. package/dist/commands/like.d.ts +3 -0
  78. package/dist/commands/like.js +52 -0
  79. package/dist/commands/like.js.map +1 -0
  80. package/dist/commands/lists.d.ts +20 -0
  81. package/dist/commands/lists.js +384 -0
  82. package/dist/commands/lists.js.map +1 -0
  83. package/dist/commands/media.d.ts +19 -0
  84. package/dist/commands/media.js +205 -0
  85. package/dist/commands/media.js.map +1 -0
  86. package/dist/commands/mentions.d.ts +8 -0
  87. package/dist/commands/mentions.js +59 -0
  88. package/dist/commands/mentions.js.map +1 -0
  89. package/dist/commands/mute.d.ts +12 -0
  90. package/dist/commands/mute.js +99 -0
  91. package/dist/commands/mute.js.map +1 -0
  92. package/dist/commands/post.d.ts +11 -0
  93. package/dist/commands/post.js +87 -0
  94. package/dist/commands/post.js.map +1 -0
  95. package/dist/commands/repost.d.ts +10 -0
  96. package/dist/commands/repost.js +59 -0
  97. package/dist/commands/repost.js.map +1 -0
  98. package/dist/commands/search.d.ts +2 -0
  99. package/dist/commands/search.js +49 -0
  100. package/dist/commands/search.js.map +1 -0
  101. package/dist/commands/stream.d.ts +13 -0
  102. package/dist/commands/stream.js +251 -0
  103. package/dist/commands/stream.js.map +1 -0
  104. package/dist/commands/timeline.d.ts +2 -0
  105. package/dist/commands/timeline.js +61 -0
  106. package/dist/commands/timeline.js.map +1 -0
  107. package/dist/commands/trends.d.ts +10 -0
  108. package/dist/commands/trends.js +59 -0
  109. package/dist/commands/trends.js.map +1 -0
  110. package/dist/commands/usage.d.ts +2 -0
  111. package/dist/commands/usage.js +52 -0
  112. package/dist/commands/usage.js.map +1 -0
  113. package/dist/commands/user.d.ts +2 -0
  114. package/dist/commands/user.js +43 -0
  115. package/dist/commands/user.js.map +1 -0
  116. package/dist/commands/usersearch.d.ts +8 -0
  117. package/dist/commands/usersearch.js +48 -0
  118. package/dist/commands/usersearch.js.map +1 -0
  119. package/dist/commands/whoami.d.ts +2 -0
  120. package/dist/commands/whoami.js +54 -0
  121. package/dist/commands/whoami.js.map +1 -0
  122. package/dist/lib/api.d.ts +12 -0
  123. package/dist/lib/api.js +91 -0
  124. package/dist/lib/api.js.map +1 -0
  125. package/dist/lib/budget.d.ts +44 -0
  126. package/dist/lib/budget.js +119 -0
  127. package/dist/lib/budget.js.map +1 -0
  128. package/dist/lib/config.d.ts +39 -0
  129. package/dist/lib/config.js +63 -0
  130. package/dist/lib/config.js.map +1 -0
  131. package/dist/lib/cost.d.ts +43 -0
  132. package/dist/lib/cost.js +224 -0
  133. package/dist/lib/cost.js.map +1 -0
  134. package/dist/lib/format.d.ts +24 -0
  135. package/dist/lib/format.js +72 -0
  136. package/dist/lib/format.js.map +1 -0
  137. package/dist/lib/oauth.d.ts +32 -0
  138. package/dist/lib/oauth.js +132 -0
  139. package/dist/lib/oauth.js.map +1 -0
  140. package/dist/lib/resolve.d.ts +12 -0
  141. package/dist/lib/resolve.js +48 -0
  142. package/dist/lib/resolve.js.map +1 -0
  143. package/package.json +46 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Bookmarks commands: list, add, and remove bookmarks.
3
+ *
4
+ * Usage:
5
+ * xc bookmarks — list bookmarked posts
6
+ * xc bookmark <post-id> — add a post to bookmarks
7
+ * xc unbookmark <post-id> — remove from bookmarks
8
+ */
9
+ import { getClient } from "../lib/api.js";
10
+ import { outputJson } from "../lib/cost.js";
11
+ import { resolveAuthenticatedUserId } from "../lib/resolve.js";
12
+ import { buildUserMap, formatTweetList } from "../lib/format.js";
13
+ const TWEET_FIELDS = ["created_at", "public_metrics", "author_id"];
14
+ const EXPANSIONS = ["author_id"];
15
+ const USER_FIELDS = ["name", "username"];
16
+ export function registerBookmarksCommand(program) {
17
+ program
18
+ .command("bookmarks")
19
+ .description("List your bookmarked posts")
20
+ .option("-n, --limit <n>", "Max results (1-100)", "20")
21
+ .option("--json", "Output raw JSON")
22
+ .option("--account <name>", "Account to use")
23
+ .action(async (opts) => {
24
+ try {
25
+ const userId = await resolveAuthenticatedUserId(opts.account);
26
+ const client = await getClient(opts.account);
27
+ const result = await client.users.getBookmarks(userId, {
28
+ tweetFields: TWEET_FIELDS,
29
+ expansions: EXPANSIONS,
30
+ userFields: USER_FIELDS,
31
+ maxResults: parseInt(opts.limit, 10),
32
+ });
33
+ if (opts.json) {
34
+ outputJson(result);
35
+ return;
36
+ }
37
+ const tweets = result.data ?? [];
38
+ if (tweets.length === 0) {
39
+ console.log("No bookmarks.");
40
+ return;
41
+ }
42
+ const usersById = buildUserMap(result.includes?.users);
43
+ console.log("Bookmarks:\n");
44
+ console.log(formatTweetList(tweets, usersById));
45
+ }
46
+ catch (err) {
47
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
48
+ process.exit(1);
49
+ }
50
+ });
51
+ }
52
+ export function registerBookmarkCommand(program) {
53
+ program
54
+ .command("bookmark <post-id>")
55
+ .description("Bookmark a post")
56
+ .option("--json", "Output raw JSON")
57
+ .option("--account <name>", "Account to use")
58
+ .action(async (postId, opts) => {
59
+ try {
60
+ const userId = await resolveAuthenticatedUserId(opts.account);
61
+ const client = await getClient(opts.account);
62
+ const result = await client.users.createBookmark(userId, {
63
+ tweetId: postId,
64
+ });
65
+ if (opts.json) {
66
+ outputJson(result);
67
+ return;
68
+ }
69
+ console.log(`Bookmarked post ${postId}`);
70
+ }
71
+ catch (err) {
72
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
73
+ process.exit(1);
74
+ }
75
+ });
76
+ }
77
+ export function registerUnbookmarkCommand(program) {
78
+ program
79
+ .command("unbookmark <post-id>")
80
+ .description("Remove a post from bookmarks")
81
+ .option("--json", "Output raw JSON")
82
+ .option("--account <name>", "Account to use")
83
+ .action(async (postId, opts) => {
84
+ try {
85
+ const userId = await resolveAuthenticatedUserId(opts.account);
86
+ const client = await getClient(opts.account);
87
+ const result = await client.users.deleteBookmark(userId, postId);
88
+ if (opts.json) {
89
+ outputJson(result);
90
+ return;
91
+ }
92
+ console.log(`Unbookmarked post ${postId}`);
93
+ }
94
+ catch (err) {
95
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
96
+ process.exit(1);
97
+ }
98
+ });
99
+ }
100
+ //# sourceMappingURL=bookmarks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bookmarks.js","sourceRoot":"","sources":["../../src/commands/bookmarks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `xc budget` — manage daily API cost budget.
3
+ * Subcommands: set, show, reset, lock, unlock.
4
+ *
5
+ * When locked with a password, set/reset require --password.
6
+ * show and cost do NOT require a password.
7
+ */
8
+ import { Command } from "commander";
9
+ export declare function registerBudgetCommand(program: Command): void;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * `xc budget` — manage daily API cost budget.
3
+ * Subcommands: set, show, reset, lock, unlock.
4
+ *
5
+ * When locked with a password, set/reset require --password.
6
+ * show and cost do NOT require a password.
7
+ */
8
+ import { loadBudget, saveBudget, resetBudget, isLocked, verifyPassword, lockBudget, unlockBudget, } from "../lib/budget.js";
9
+ import { loadUsageLog, computeTodaySpend } from "../lib/cost.js";
10
+ const VALID_ACTIONS = ["block", "warn", "confirm"];
11
+ /**
12
+ * If the budget is locked, verify the provided password.
13
+ * Exits with error if locked and password is missing or incorrect.
14
+ */
15
+ function requirePasswordIfLocked(password) {
16
+ if (!isLocked())
17
+ return;
18
+ if (!password) {
19
+ console.error("Error: budget is locked. Provide --password to continue.");
20
+ process.exit(1);
21
+ }
22
+ if (!verifyPassword(password)) {
23
+ console.error("Error: incorrect password.");
24
+ process.exit(1);
25
+ }
26
+ }
27
+ export function registerBudgetCommand(program) {
28
+ const budget = program
29
+ .command("budget")
30
+ .description("Manage API cost budget");
31
+ // xc budget set --daily 2.00 --action warn [--password <pass>]
32
+ budget
33
+ .command("set")
34
+ .description("Set daily budget limit")
35
+ .requiredOption("--daily <amount>", "Daily budget in dollars")
36
+ .option("--action <action>", "Action when exceeded: block, warn, confirm", "warn")
37
+ .option("--password <pass>", "Password (required if budget is locked)")
38
+ .action((opts) => {
39
+ requirePasswordIfLocked(opts.password);
40
+ const daily = parseFloat(opts.daily);
41
+ if (isNaN(daily) || daily <= 0) {
42
+ console.error("Error: --daily must be a positive number.");
43
+ process.exit(1);
44
+ }
45
+ const action = opts.action;
46
+ if (!VALID_ACTIONS.includes(action)) {
47
+ console.error("Error: --action must be block, warn, or confirm.");
48
+ process.exit(1);
49
+ }
50
+ // Preserve existing password lock when updating budget
51
+ const existing = loadBudget();
52
+ saveBudget({
53
+ daily,
54
+ action,
55
+ passwordHash: existing.passwordHash,
56
+ passwordSalt: existing.passwordSalt,
57
+ });
58
+ console.log(`Budget set: $${daily.toFixed(2)}/day (action: ${action})`);
59
+ });
60
+ // xc budget show — does NOT require password
61
+ budget
62
+ .command("show")
63
+ .description("Show current budget and today's spend")
64
+ .action(() => {
65
+ const config = loadBudget();
66
+ const entries = loadUsageLog();
67
+ const todaySpend = computeTodaySpend(entries);
68
+ if (!config.daily) {
69
+ console.log("No budget configured.\n");
70
+ console.log("Set one with: xc budget set --daily 2.00");
71
+ return;
72
+ }
73
+ const remaining = Math.max(0, config.daily - todaySpend);
74
+ const pct = ((todaySpend / config.daily) * 100).toFixed(0);
75
+ const locked = isLocked();
76
+ console.log("Budget:\n");
77
+ console.log(` Daily limit: $${config.daily.toFixed(2)}`);
78
+ console.log(` Today spent: $${todaySpend.toFixed(2)} (${pct}%)`);
79
+ console.log(` Remaining: $${remaining.toFixed(2)}`);
80
+ console.log(` Action: ${config.action}`);
81
+ console.log(` Locked: ${locked ? "yes" : "no"}`);
82
+ });
83
+ // xc budget reset [--password <pass>]
84
+ budget
85
+ .command("reset")
86
+ .description("Remove budget configuration")
87
+ .option("--password <pass>", "Password (required if budget is locked)")
88
+ .action((opts) => {
89
+ requirePasswordIfLocked(opts.password);
90
+ resetBudget();
91
+ console.log("Budget configuration removed.");
92
+ });
93
+ // xc budget lock --password <pass>
94
+ budget
95
+ .command("lock")
96
+ .description("Lock budget with a password")
97
+ .requiredOption("--password <pass>", "Password to lock with")
98
+ .action((opts) => {
99
+ if (isLocked()) {
100
+ console.error("Error: budget is already locked. Unlock first.");
101
+ process.exit(1);
102
+ }
103
+ lockBudget(opts.password);
104
+ console.log("Budget locked. Use --password for set/reset commands.");
105
+ });
106
+ // xc budget unlock --password <pass>
107
+ budget
108
+ .command("unlock")
109
+ .description("Remove password lock from budget")
110
+ .requiredOption("--password <pass>", "Current password")
111
+ .action((opts) => {
112
+ if (!isLocked()) {
113
+ console.log("Budget is not locked.");
114
+ return;
115
+ }
116
+ if (!verifyPassword(opts.password)) {
117
+ console.error("Error: incorrect password.");
118
+ process.exit(1);
119
+ }
120
+ unlockBudget();
121
+ console.log("Budget unlocked.");
122
+ });
123
+ }
124
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/commands/budget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * `xc cost` — view API cost summary, daily breakdown, or raw request log.
3
+ */
4
+ import { Command } from "commander";
5
+ export declare function registerCostCommand(program: Command): void;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * `xc cost` — view API cost summary, daily breakdown, or raw request log.
3
+ */
4
+ import { loadUsageLog, computeSpend, HOUR, DAY, WEEK, MONTH, } from "../lib/cost.js";
5
+ export function registerCostCommand(program) {
6
+ const cost = program
7
+ .command("cost")
8
+ .description("Show API cost summary")
9
+ .option("--daily", "Show daily breakdown")
10
+ .option("--json", "Output raw JSON")
11
+ .action((opts) => {
12
+ const entries = loadUsageLog();
13
+ if (entries.length === 0) {
14
+ console.log("No API usage recorded yet.");
15
+ return;
16
+ }
17
+ if (opts.json) {
18
+ const summary = {
19
+ "1h": computeSpend(entries, HOUR),
20
+ "24h": computeSpend(entries, DAY),
21
+ "7d": computeSpend(entries, WEEK),
22
+ "30d": computeSpend(entries, MONTH),
23
+ totalRequests: entries.length,
24
+ };
25
+ console.log(JSON.stringify(summary, null, 2));
26
+ return;
27
+ }
28
+ // Daily breakdown: group entries by YYYY-MM-DD
29
+ if (opts.daily) {
30
+ const byDay = new Map();
31
+ for (const entry of entries) {
32
+ const date = entry.timestamp.slice(0, 10);
33
+ byDay.set(date, (byDay.get(date) ?? 0) + entry.estimatedCost);
34
+ }
35
+ console.log("Daily cost breakdown:\n");
36
+ const sorted = [...byDay.entries()].sort((a, b) => b[0].localeCompare(a[0]));
37
+ for (const [date, total] of sorted) {
38
+ console.log(` ${date} $${total.toFixed(2)}`);
39
+ }
40
+ return;
41
+ }
42
+ // Default: time-window summary
43
+ console.log("API cost summary:\n");
44
+ console.log(` Last hour: $${computeSpend(entries, HOUR).toFixed(2)}`);
45
+ console.log(` Last 24h: $${computeSpend(entries, DAY).toFixed(2)}`);
46
+ console.log(` Last 7 days: $${computeSpend(entries, WEEK).toFixed(2)}`);
47
+ console.log(` Last 30 days: $${computeSpend(entries, MONTH).toFixed(2)}`);
48
+ console.log(`\n Total requests: ${entries.length}`);
49
+ });
50
+ // xc cost log — raw request log
51
+ cost
52
+ .command("log")
53
+ .description("Show raw request log")
54
+ .option("-n, --limit <n>", "Show last N entries", "20")
55
+ .option("--json", "Output raw JSON")
56
+ .action((opts) => {
57
+ const entries = loadUsageLog();
58
+ const limit = parseInt(opts.limit, 10);
59
+ const recent = entries.slice(-limit);
60
+ if (opts.json) {
61
+ console.log(JSON.stringify(recent, null, 2));
62
+ return;
63
+ }
64
+ if (recent.length === 0) {
65
+ console.log("No API requests logged yet.");
66
+ return;
67
+ }
68
+ console.log(`Recent API requests (last ${recent.length}):\n`);
69
+ for (const entry of recent) {
70
+ const ts = new Date(entry.timestamp).toLocaleString();
71
+ console.log(` ${ts} ${entry.method.padEnd(6)} ${entry.endpoint} $${entry.estimatedCost.toFixed(3)}`);
72
+ }
73
+ });
74
+ }
75
+ //# sourceMappingURL=cost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost.js","sourceRoot":"","sources":["../../src/commands/cost.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Delete command: remove a post by ID.
3
+ *
4
+ * Usage:
5
+ * xc delete <post-id>
6
+ */
7
+ import { Command } from "commander";
8
+ export declare function registerDeleteCommand(program: Command): void;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Delete command: remove a post by ID.
3
+ *
4
+ * Usage:
5
+ * xc delete <post-id>
6
+ */
7
+ import { getClient } from "../lib/api.js";
8
+ import { outputJson } from "../lib/cost.js";
9
+ export function registerDeleteCommand(program) {
10
+ program
11
+ .command("delete <post-id>")
12
+ .description("Delete a post by ID")
13
+ .option("--json", "Output raw JSON")
14
+ .option("--account <name>", "Account to use")
15
+ .action(async (postId, opts) => {
16
+ try {
17
+ const client = await getClient(opts.account);
18
+ const result = await client.posts.delete(postId);
19
+ if (opts.json) {
20
+ outputJson(result);
21
+ return;
22
+ }
23
+ console.log(`Deleted post ${postId}`);
24
+ }
25
+ catch (err) {
26
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
27
+ process.exit(1);
28
+ }
29
+ });
30
+ }
31
+ //# sourceMappingURL=delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,kBAAkB,CAAC;SAC3B,WAAW,CAAC,qBAAqB,CAAC;SAClC,MAAM,CAAC,QAAQ,EAAE,iBAAiB,CAAC;SACnC,MAAM,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;SAC5C,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,IAAI,EAAE,EAAE;QACrC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAEjD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,UAAU,CAAC,MAAM,CAAC,CAAC;gBACnB,OAAO;YACT,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,EAAE,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * DM commands: send, list, and view message history.
3
+ *
4
+ * Usage:
5
+ * xc dm send <@username> <message>
6
+ * xc dm list
7
+ * xc dm history <@username>
8
+ */
9
+ import { Command } from "commander";
10
+ export declare function registerDmCommand(program: Command): void;
@@ -0,0 +1,179 @@
1
+ /**
2
+ * DM commands: send, list, and view message history.
3
+ *
4
+ * Usage:
5
+ * xc dm send <@username> <message>
6
+ * xc dm list
7
+ * xc dm history <@username>
8
+ */
9
+ import { getClient } from "../lib/api.js";
10
+ import { outputJson } from "../lib/cost.js";
11
+ import { resolveUserId, resolveAuthenticatedUserId } from "../lib/resolve.js";
12
+ /** Format a single DM event for terminal display. */
13
+ function formatDmEvent(event, usersById) {
14
+ const lines = [];
15
+ const sender = event.senderId ? usersById.get(event.senderId) : undefined;
16
+ const who = sender ? `@${sender.username}` : (event.senderId ?? "unknown");
17
+ const time = event.createdAt
18
+ ? new Date(event.createdAt).toLocaleString()
19
+ : "";
20
+ lines.push(`${who} ${time}`);
21
+ if (event.text) {
22
+ lines.push(` ${event.text.replace(/\n/g, "\n ")}`);
23
+ }
24
+ return lines.join("\n");
25
+ }
26
+ export function registerDmCommand(program) {
27
+ const dm = program
28
+ .command("dm")
29
+ .description("Direct message operations");
30
+ // xc dm send <@username> <message>
31
+ dm.command("send <username> <message>")
32
+ .description("Send a DM to a user")
33
+ .option("--json", "Output raw JSON")
34
+ .option("--account <name>", "Account to use")
35
+ .action(async (username, message, opts) => {
36
+ try {
37
+ const participantId = await resolveUserId(username, opts.account);
38
+ const client = await getClient(opts.account);
39
+ const result = await client.directMessages.createByParticipantId(participantId, { body: { text: message } });
40
+ if (opts.json) {
41
+ outputJson(result);
42
+ return;
43
+ }
44
+ const eventId = result.data
45
+ ?.dmEventId ?? "unknown";
46
+ console.log(`DM sent to @${username.replace(/^@/, "")} (event: ${eventId})`);
47
+ }
48
+ catch (err) {
49
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
50
+ process.exit(1);
51
+ }
52
+ });
53
+ // xc dm list — list recent DM conversations
54
+ dm.command("list")
55
+ .description("List recent DM conversations")
56
+ .option("-n, --limit <n>", "Max results", "20")
57
+ .option("--json", "Output raw JSON")
58
+ .option("--account <name>", "Account to use")
59
+ .action(async (opts) => {
60
+ try {
61
+ const client = await getClient(opts.account);
62
+ const result = await client.directMessages.getEvents({
63
+ maxResults: Math.min(parseInt(opts.limit, 10), 100),
64
+ dmEventFields: [
65
+ "id",
66
+ "text",
67
+ "event_type",
68
+ "sender_id",
69
+ "created_at",
70
+ "dm_conversation_id",
71
+ ],
72
+ expansions: ["sender_id"],
73
+ userFields: ["username", "name"],
74
+ });
75
+ if (opts.json) {
76
+ outputJson(result);
77
+ return;
78
+ }
79
+ const events = (result.data ?? []);
80
+ if (events.length === 0) {
81
+ console.log("No recent DM conversations.");
82
+ return;
83
+ }
84
+ // Build user lookup from includes
85
+ const usersById = new Map();
86
+ const includeUsers = result.includes?.users;
87
+ if (includeUsers) {
88
+ for (const u of includeUsers) {
89
+ if (u.id)
90
+ usersById.set(u.id, u);
91
+ }
92
+ }
93
+ // Group by conversation, show latest per conversation
94
+ const seen = new Set();
95
+ const unique = [];
96
+ for (const ev of events) {
97
+ const cid = ev.dmConversationId ?? "";
98
+ if (!seen.has(cid)) {
99
+ seen.add(cid);
100
+ unique.push(ev);
101
+ }
102
+ }
103
+ console.log("Recent DM conversations:\n");
104
+ for (const ev of unique) {
105
+ console.log(formatDmEvent(ev, usersById));
106
+ console.log();
107
+ }
108
+ }
109
+ catch (err) {
110
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
111
+ process.exit(1);
112
+ }
113
+ });
114
+ // xc dm history <@username> — message history with a user
115
+ dm.command("history <username>")
116
+ .description("View DM history with a user")
117
+ .option("-n, --limit <n>", "Max results", "20")
118
+ .option("--json", "Output raw JSON")
119
+ .option("--account <name>", "Account to use")
120
+ .action(async (username, opts) => {
121
+ try {
122
+ const participantId = await resolveUserId(username, opts.account);
123
+ const client = await getClient(opts.account);
124
+ const result = await client.directMessages.getEventsByParticipantId(participantId, {
125
+ maxResults: Math.min(parseInt(opts.limit, 10), 100),
126
+ dmEventFields: [
127
+ "id",
128
+ "text",
129
+ "event_type",
130
+ "sender_id",
131
+ "created_at",
132
+ ],
133
+ expansions: ["sender_id"],
134
+ userFields: ["username", "name"],
135
+ });
136
+ if (opts.json) {
137
+ outputJson(result);
138
+ return;
139
+ }
140
+ const events = (result.data ?? []);
141
+ if (events.length === 0) {
142
+ console.log(`No messages with @${username.replace(/^@/, "")}.`);
143
+ return;
144
+ }
145
+ // Build user lookup from includes
146
+ const usersById = new Map();
147
+ const includeUsers = result.includes?.users;
148
+ if (includeUsers) {
149
+ for (const u of includeUsers) {
150
+ if (u.id)
151
+ usersById.set(u.id, u);
152
+ }
153
+ }
154
+ // Get our own user ID for labeling
155
+ const myId = await resolveAuthenticatedUserId(opts.account);
156
+ const clean = username.replace(/^@/, "");
157
+ console.log(`DM history with @${clean}:\n`);
158
+ // Events come newest-first; reverse for chronological display
159
+ const chronological = [...events].reverse();
160
+ for (const ev of chronological) {
161
+ const isMine = ev.senderId === myId;
162
+ const label = isMine ? "You" : `@${clean}`;
163
+ const time = ev.createdAt
164
+ ? new Date(ev.createdAt).toLocaleString()
165
+ : "";
166
+ console.log(`${label} ${time}`);
167
+ if (ev.text) {
168
+ console.log(` ${ev.text.replace(/\n/g, "\n ")}`);
169
+ }
170
+ console.log();
171
+ }
172
+ }
173
+ catch (err) {
174
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
175
+ process.exit(1);
176
+ }
177
+ });
178
+ }
179
+ //# sourceMappingURL=dm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dm.js","sourceRoot":"","sources":["../../src/commands/dm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Engagement lookup commands: quote tweets, liking users, reposted-by, liked posts.
3
+ *
4
+ * Usage:
5
+ * xc quotes <post-id> — list quote tweets of a post
6
+ * xc likes <post-id> — list users who liked a post
7
+ * xc reposts <post-id> — list users who reposted a post
8
+ * xc liked [username] — list posts liked by a user
9
+ */
10
+ import { Command } from "commander";
11
+ export declare function registerQuotesCommand(program: Command): void;
12
+ export declare function registerLikesCommand(program: Command): void;
13
+ export declare function registerRepostsCommand(program: Command): void;
14
+ export declare function registerLikedCommand(program: Command): void;