tenbrains 2.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 (183) hide show
  1. package/AGENTS.md +112 -0
  2. package/README.md +218 -0
  3. package/dist/ai/analyzer.d.ts +28 -0
  4. package/dist/ai/analyzer.d.ts.map +1 -0
  5. package/dist/ai/analyzer.js +35 -0
  6. package/dist/ai/analyzer.js.map +1 -0
  7. package/dist/ai/client.d.ts +12 -0
  8. package/dist/ai/client.d.ts.map +1 -0
  9. package/dist/ai/client.js +152 -0
  10. package/dist/ai/client.js.map +1 -0
  11. package/dist/ai/json.d.ts +7 -0
  12. package/dist/ai/json.d.ts.map +1 -0
  13. package/dist/ai/json.js +57 -0
  14. package/dist/ai/json.js.map +1 -0
  15. package/dist/ai/mock.d.ts +6 -0
  16. package/dist/ai/mock.d.ts.map +1 -0
  17. package/dist/ai/mock.js +66 -0
  18. package/dist/ai/mock.js.map +1 -0
  19. package/dist/ai/prompts.d.ts +19 -0
  20. package/dist/ai/prompts.d.ts.map +1 -0
  21. package/dist/ai/prompts.js +40 -0
  22. package/dist/ai/prompts.js.map +1 -0
  23. package/dist/ai/providers.d.ts +22 -0
  24. package/dist/ai/providers.d.ts.map +1 -0
  25. package/dist/ai/providers.js +56 -0
  26. package/dist/ai/providers.js.map +1 -0
  27. package/dist/ai/resolve.d.ts +24 -0
  28. package/dist/ai/resolve.d.ts.map +1 -0
  29. package/dist/ai/resolve.js +33 -0
  30. package/dist/ai/resolve.js.map +1 -0
  31. package/dist/bin/preflight.d.ts +2 -0
  32. package/dist/bin/preflight.d.ts.map +1 -0
  33. package/dist/bin/preflight.js +11 -0
  34. package/dist/bin/preflight.js.map +1 -0
  35. package/dist/bin/tenbrains.d.ts +3 -0
  36. package/dist/bin/tenbrains.d.ts.map +1 -0
  37. package/dist/bin/tenbrains.js +9 -0
  38. package/dist/bin/tenbrains.js.map +1 -0
  39. package/dist/cli.d.ts +4 -0
  40. package/dist/cli.d.ts.map +1 -0
  41. package/dist/cli.js +554 -0
  42. package/dist/cli.js.map +1 -0
  43. package/dist/commands/analyze.d.ts +7 -0
  44. package/dist/commands/analyze.d.ts.map +1 -0
  45. package/dist/commands/analyze.js +204 -0
  46. package/dist/commands/analyze.js.map +1 -0
  47. package/dist/commands/bookmark.d.ts +9 -0
  48. package/dist/commands/bookmark.d.ts.map +1 -0
  49. package/dist/commands/bookmark.js +120 -0
  50. package/dist/commands/bookmark.js.map +1 -0
  51. package/dist/commands/config.d.ts +15 -0
  52. package/dist/commands/config.d.ts.map +1 -0
  53. package/dist/commands/config.js +142 -0
  54. package/dist/commands/config.js.map +1 -0
  55. package/dist/commands/db.d.ts +9 -0
  56. package/dist/commands/db.d.ts.map +1 -0
  57. package/dist/commands/db.js +64 -0
  58. package/dist/commands/db.js.map +1 -0
  59. package/dist/commands/digest.d.ts +19 -0
  60. package/dist/commands/digest.d.ts.map +1 -0
  61. package/dist/commands/digest.js +71 -0
  62. package/dist/commands/digest.js.map +1 -0
  63. package/dist/commands/import.d.ts +11 -0
  64. package/dist/commands/import.d.ts.map +1 -0
  65. package/dist/commands/import.js +78 -0
  66. package/dist/commands/import.js.map +1 -0
  67. package/dist/commands/learn.d.ts +9 -0
  68. package/dist/commands/learn.d.ts.map +1 -0
  69. package/dist/commands/learn.js +149 -0
  70. package/dist/commands/learn.js.map +1 -0
  71. package/dist/commands/manifest.d.ts +13 -0
  72. package/dist/commands/manifest.d.ts.map +1 -0
  73. package/dist/commands/manifest.js +62 -0
  74. package/dist/commands/manifest.js.map +1 -0
  75. package/dist/commands/record.d.ts +10 -0
  76. package/dist/commands/record.d.ts.map +1 -0
  77. package/dist/commands/record.js +37 -0
  78. package/dist/commands/record.js.map +1 -0
  79. package/dist/commands/search.d.ts +5 -0
  80. package/dist/commands/search.d.ts.map +1 -0
  81. package/dist/commands/search.js +33 -0
  82. package/dist/commands/search.js.map +1 -0
  83. package/dist/commands/shared.d.ts +9 -0
  84. package/dist/commands/shared.d.ts.map +1 -0
  85. package/dist/commands/shared.js +13 -0
  86. package/dist/commands/shared.js.map +1 -0
  87. package/dist/commands/suggest.d.ts +9 -0
  88. package/dist/commands/suggest.d.ts.map +1 -0
  89. package/dist/commands/suggest.js +102 -0
  90. package/dist/commands/suggest.js.map +1 -0
  91. package/dist/commands/takeaway.d.ts +9 -0
  92. package/dist/commands/takeaway.d.ts.map +1 -0
  93. package/dist/commands/takeaway.js +167 -0
  94. package/dist/commands/takeaway.js.map +1 -0
  95. package/dist/core/config.d.ts +52 -0
  96. package/dist/core/config.d.ts.map +1 -0
  97. package/dist/core/config.js +180 -0
  98. package/dist/core/config.js.map +1 -0
  99. package/dist/core/context.d.ts +28 -0
  100. package/dist/core/context.d.ts.map +1 -0
  101. package/dist/core/context.js +40 -0
  102. package/dist/core/context.js.map +1 -0
  103. package/dist/core/errors.d.ts +33 -0
  104. package/dist/core/errors.d.ts.map +1 -0
  105. package/dist/core/errors.js +82 -0
  106. package/dist/core/errors.js.map +1 -0
  107. package/dist/core/input.d.ts +9 -0
  108. package/dist/core/input.d.ts.map +1 -0
  109. package/dist/core/input.js +47 -0
  110. package/dist/core/input.js.map +1 -0
  111. package/dist/core/opts.d.ts +8 -0
  112. package/dist/core/opts.d.ts.map +1 -0
  113. package/dist/core/opts.js +38 -0
  114. package/dist/core/opts.js.map +1 -0
  115. package/dist/core/output.d.ts +58 -0
  116. package/dist/core/output.d.ts.map +1 -0
  117. package/dist/core/output.js +76 -0
  118. package/dist/core/output.js.map +1 -0
  119. package/dist/core/paths.d.ts +20 -0
  120. package/dist/core/paths.d.ts.map +1 -0
  121. package/dist/core/paths.js +31 -0
  122. package/dist/core/paths.js.map +1 -0
  123. package/dist/core/text.d.ts +10 -0
  124. package/dist/core/text.d.ts.map +1 -0
  125. package/dist/core/text.js +132 -0
  126. package/dist/core/text.js.map +1 -0
  127. package/dist/core/validate.d.ts +5 -0
  128. package/dist/core/validate.d.ts.map +1 -0
  129. package/dist/core/validate.js +17 -0
  130. package/dist/core/validate.js.map +1 -0
  131. package/dist/db/database.d.ts +27 -0
  132. package/dist/db/database.d.ts.map +1 -0
  133. package/dist/db/database.js +90 -0
  134. package/dist/db/database.js.map +1 -0
  135. package/dist/db/fts.d.ts +12 -0
  136. package/dist/db/fts.d.ts.map +1 -0
  137. package/dist/db/fts.js +42 -0
  138. package/dist/db/fts.js.map +1 -0
  139. package/dist/db/migrations.d.ts +20 -0
  140. package/dist/db/migrations.d.ts.map +1 -0
  141. package/dist/db/migrations.js +243 -0
  142. package/dist/db/migrations.js.map +1 -0
  143. package/dist/db/repositories.d.ts +130 -0
  144. package/dist/db/repositories.d.ts.map +1 -0
  145. package/dist/db/repositories.js +418 -0
  146. package/dist/db/repositories.js.map +1 -0
  147. package/dist/domain/learn.d.ts +25 -0
  148. package/dist/domain/learn.d.ts.map +1 -0
  149. package/dist/domain/learn.js +85 -0
  150. package/dist/domain/learn.js.map +1 -0
  151. package/dist/domain/schemas.d.ts +169 -0
  152. package/dist/domain/schemas.d.ts.map +1 -0
  153. package/dist/domain/schemas.js +54 -0
  154. package/dist/domain/schemas.js.map +1 -0
  155. package/dist/domain/search.d.ts +32 -0
  156. package/dist/domain/search.d.ts.map +1 -0
  157. package/dist/domain/search.js +77 -0
  158. package/dist/domain/search.js.map +1 -0
  159. package/dist/domain/suggest.d.ts +17 -0
  160. package/dist/domain/suggest.d.ts.map +1 -0
  161. package/dist/domain/suggest.js +114 -0
  162. package/dist/domain/suggest.js.map +1 -0
  163. package/dist/domain/tags.d.ts +9 -0
  164. package/dist/domain/tags.d.ts.map +1 -0
  165. package/dist/domain/tags.js +40 -0
  166. package/dist/domain/tags.js.map +1 -0
  167. package/dist/domain/types.d.ts +84 -0
  168. package/dist/domain/types.d.ts.map +1 -0
  169. package/dist/domain/types.js +2 -0
  170. package/dist/domain/types.js.map +1 -0
  171. package/dist/index.d.ts +19 -0
  172. package/dist/index.d.ts.map +1 -0
  173. package/dist/index.js +18 -0
  174. package/dist/index.js.map +1 -0
  175. package/dist/x/archive.d.ts +32 -0
  176. package/dist/x/archive.d.ts.map +1 -0
  177. package/dist/x/archive.js +108 -0
  178. package/dist/x/archive.js.map +1 -0
  179. package/dist/x/client.d.ts +61 -0
  180. package/dist/x/client.d.ts.map +1 -0
  181. package/dist/x/client.js +321 -0
  182. package/dist/x/client.js.map +1 -0
  183. package/package.json +58 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Programmatic API. The CLI is the primary surface, but the same building
3
+ * blocks are exported so the analysis/persistence pipeline can be embedded.
4
+ */
5
+ export { buildProgram, run } from "./cli.js";
6
+ export { Database, newId, nowIso } from "./db/database.js";
7
+ export { Store } from "./db/repositories.js";
8
+ export { ConfigStore } from "./core/config.js";
9
+ export { CliError, type ErrorCode } from "./core/errors.js";
10
+ export { searchCorpus } from "./domain/search.js";
11
+ export { generateSuggestions } from "./domain/suggest.js";
12
+ export { buildFeynmanTrack, prioritizeConcepts } from "./domain/learn.js";
13
+ export { suggestTags } from "./domain/tags.js";
14
+ export { analyzePost, summarizeAccount } from "./ai/analyzer.js";
15
+ export { DEFAULT_PROVIDER, PROVIDER_CATALOG, PROVIDER_IDS, type ProviderId, } from "./ai/providers.js";
16
+ export * from "./domain/types.js";
17
+ export { AnalysisResultSchema, ConceptRatingSchema, ConceptSchema, PostInputSchema, RatingsInputSchema, TakeawayPostInputSchema, TakeawayPostsInputSchema, TakeawayResultSchema, } from "./domain/schemas.js";
18
+ export type { AnalysisResult, PostInput, TakeawayPostInput, TakeawayResult, } from "./domain/schemas.js";
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,KAAK,UAAU,GAChB,MAAM,mBAAmB,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,cAAc,GACf,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Programmatic API. The CLI is the primary surface, but the same building
3
+ * blocks are exported so the analysis/persistence pipeline can be embedded.
4
+ */
5
+ export { buildProgram, run } from "./cli.js";
6
+ export { Database, newId, nowIso } from "./db/database.js";
7
+ export { Store } from "./db/repositories.js";
8
+ export { ConfigStore } from "./core/config.js";
9
+ export { CliError } from "./core/errors.js";
10
+ export { searchCorpus } from "./domain/search.js";
11
+ export { generateSuggestions } from "./domain/suggest.js";
12
+ export { buildFeynmanTrack, prioritizeConcepts } from "./domain/learn.js";
13
+ export { suggestTags } from "./domain/tags.js";
14
+ export { analyzePost, summarizeAccount } from "./ai/analyzer.js";
15
+ export { DEFAULT_PROVIDER, PROVIDER_CATALOG, PROVIDER_IDS, } from "./ai/providers.js";
16
+ export * from "./domain/types.js";
17
+ export { AnalysisResultSchema, ConceptRatingSchema, ConceptSchema, PostInputSchema, RatingsInputSchema, TakeawayPostInputSchema, TakeawayPostsInputSchema, TakeawayResultSchema, } from "./domain/schemas.js";
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAkB,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,GAEb,MAAM,mBAAmB,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Parsers for the official X (Twitter) account archive — the free bulk path
3
+ * to your own data. Users request it at Settings → "Download an archive of
4
+ * your data" and extract the zip; this module reads the extracted directory.
5
+ * Each data file is JavaScript of the form `window.YTD.<kind>.partN = [...]`.
6
+ */
7
+ export interface ArchiveLike {
8
+ text: string;
9
+ externalId: string;
10
+ url?: string | undefined;
11
+ }
12
+ export interface ArchiveTweet {
13
+ text: string;
14
+ externalId: string;
15
+ postedAt?: string | undefined;
16
+ }
17
+ /** Strip the `window.YTD.<kind>.partN = ` prefix and parse the JSON array. */
18
+ export declare function parseArchiveJs(content: string): unknown[];
19
+ export declare function extractLikes(entries: unknown[]): ArchiveLike[];
20
+ export declare function extractTweets(entries: unknown[]): ArchiveTweet[];
21
+ export declare function extractUsername(entries: unknown[]): string | undefined;
22
+ export interface ArchiveContents {
23
+ username: string | undefined;
24
+ likes: ArchiveLike[];
25
+ tweets: ArchiveTweet[];
26
+ }
27
+ /**
28
+ * Read an extracted X archive. `root` is the archive directory (containing
29
+ * `data/`) or the `data/` directory itself.
30
+ */
31
+ export declare function readArchive(root: string): ArchiveContents;
32
+ //# sourceMappingURL=archive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/x/archive.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAQD,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAmBzD;AAWD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,CAa9D;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,YAAY,EAAE,CAchE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAUD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CA+BzD"}
@@ -0,0 +1,108 @@
1
+ import { readFileSync, readdirSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { CliError } from "../core/errors.js";
4
+ function asRecord(value) {
5
+ return typeof value === "object" && value !== null ? value : {};
6
+ }
7
+ /** Strip the `window.YTD.<kind>.partN = ` prefix and parse the JSON array. */
8
+ export function parseArchiveJs(content) {
9
+ let body = content.trim();
10
+ if (!body.startsWith("[")) {
11
+ // Plain JSON arrays are tolerated; everything else must carry the prefix.
12
+ const match = body.match(/^window\.YTD\.[\w.]+\.part\d+\s*=\s*/);
13
+ if (!match) {
14
+ throw new CliError("VALIDATION", "Not an X archive data file (missing window.YTD prefix).");
15
+ }
16
+ body = body.slice(match[0].length);
17
+ }
18
+ try {
19
+ const parsed = JSON.parse(body);
20
+ if (!Array.isArray(parsed)) {
21
+ throw new Error("expected an array");
22
+ }
23
+ return parsed;
24
+ }
25
+ catch (error) {
26
+ throw new CliError("VALIDATION", "Could not parse X archive data file.", { cause: error });
27
+ }
28
+ }
29
+ /** Twitter's archive date format ("Wed Oct 10 20:19:24 +0000 2018") to ISO. */
30
+ function toIso(value) {
31
+ if (typeof value !== "string" || value.length === 0) {
32
+ return undefined;
33
+ }
34
+ const date = new Date(value);
35
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
36
+ }
37
+ export function extractLikes(entries) {
38
+ return entries
39
+ .map((entry) => {
40
+ const like = asRecord(asRecord(entry).like);
41
+ const text = typeof like.fullText === "string" ? like.fullText : "";
42
+ const externalId = typeof like.tweetId === "string" ? like.tweetId : "";
43
+ return {
44
+ text,
45
+ externalId,
46
+ url: typeof like.expandedUrl === "string" ? like.expandedUrl : undefined,
47
+ };
48
+ })
49
+ .filter((like) => like.text.length > 0 && like.externalId.length > 0);
50
+ }
51
+ export function extractTweets(entries) {
52
+ return entries
53
+ .map((entry) => {
54
+ const tweet = asRecord(asRecord(entry).tweet);
55
+ const text = typeof tweet.full_text === "string"
56
+ ? tweet.full_text
57
+ : typeof tweet.text === "string"
58
+ ? tweet.text
59
+ : "";
60
+ const externalId = typeof tweet.id_str === "string" ? tweet.id_str : "";
61
+ return { text, externalId, postedAt: toIso(tweet.created_at) };
62
+ })
63
+ .filter((tweet) => tweet.text.length > 0 && tweet.externalId.length > 0);
64
+ }
65
+ export function extractUsername(entries) {
66
+ const account = asRecord(asRecord(entries[0]).account);
67
+ return typeof account.username === "string" ? account.username : undefined;
68
+ }
69
+ /** Files for one archive kind, including multi-part exports (tweets-part1.js). */
70
+ function kindFiles(dir, kind) {
71
+ return readdirSync(dir)
72
+ .filter((name) => new RegExp(`^${kind}(-part\\d+)?\\.js$`).test(name))
73
+ .sort()
74
+ .map((name) => path.join(dir, name));
75
+ }
76
+ /**
77
+ * Read an extracted X archive. `root` is the archive directory (containing
78
+ * `data/`) or the `data/` directory itself.
79
+ */
80
+ export function readArchive(root) {
81
+ let dir;
82
+ try {
83
+ dir = statSync(path.join(root, "data")).isDirectory() ? path.join(root, "data") : root;
84
+ }
85
+ catch {
86
+ dir = root;
87
+ }
88
+ let names;
89
+ try {
90
+ names = readdirSync(dir);
91
+ }
92
+ catch (error) {
93
+ throw new CliError("NOT_FOUND", `Cannot read archive directory ${root}.`, { cause: error });
94
+ }
95
+ if (!names.some((n) => /^(like|tweets?|account)(-part\d+)?\.js$/.test(n))) {
96
+ throw new CliError("VALIDATION", `${dir} does not look like an extracted X archive (no like.js / tweets.js / account.js). Extract the archive zip first.`);
97
+ }
98
+ const parseAll = (files) => files.flatMap((file) => parseArchiveJs(readFileSync(file, "utf8")));
99
+ const accountEntries = parseAll(kindFiles(dir, "account"));
100
+ // Older archives name the file `tweet.js`, newer ones `tweets.js`.
101
+ const tweetFiles = [...kindFiles(dir, "tweets"), ...kindFiles(dir, "tweet")];
102
+ return {
103
+ username: extractUsername(accountEntries),
104
+ likes: extractLikes(parseAll(kindFiles(dir, "like"))),
105
+ tweets: extractTweets(parseAll(tweetFiles)),
106
+ };
107
+ }
108
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/x/archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAuB7C,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAE,KAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;AAClF,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,0EAA0E;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACjE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,QAAQ,CAAC,YAAY,EAAE,yDAAyD,CAAC,CAAC;QAC9F,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,QAAQ,CAAC,YAAY,EAAE,sCAAsC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7F,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,KAAK,CAAC,KAAc;IAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAkB;IAC7C,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO;YACL,IAAI;YACJ,UAAU;YACV,GAAG,EAAE,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;SACzE,CAAC;IACJ,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAkB;IAC9C,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,IAAI,GACR,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;YACjC,CAAC,CAAC,KAAK,CAAC,SAAS;YACjB,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAC9B,CAAC,CAAC,KAAK,CAAC,IAAI;gBACZ,CAAC,CAAC,EAAE,CAAC;QACX,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;IACjE,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAkB;IAChD,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACvD,OAAO,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7E,CAAC;AAQD,kFAAkF;AAClF,SAAS,SAAS,CAAC,GAAW,EAAE,IAAY;IAC1C,OAAO,WAAW,CAAC,GAAG,CAAC;SACpB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,IAAI,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACrE,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,GAAG,IAAI,CAAC;IACb,CAAC;IACD,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,QAAQ,CAAC,WAAW,EAAE,iCAAiC,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,yCAAyC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,QAAQ,CAChB,YAAY,EACZ,GAAG,GAAG,kHAAkH,CACzH,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,KAAe,EAAa,EAAE,CAC9C,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAEtE,MAAM,cAAc,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;IAC3D,mEAAmE;IACnE,MAAM,UAAU,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7E,OAAO;QACL,QAAQ,EAAE,eAAe,CAAC,cAAc,CAAC;QACzC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;QACrD,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;KAC5C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * X (Twitter) fetching, designed free-first.
3
+ *
4
+ * - Single tweets default to the public **oEmbed** endpoint
5
+ * (`publish.twitter.com/oembed`), which needs no API key and no paid tier —
6
+ * it returns a public tweet's text and author for embedding.
7
+ * - Account **timelines** have no free path, so they use the official X API v2
8
+ * with a Bearer token. Most accounts need a paid tier (Basic+) to read
9
+ * timelines; when the tier doesn't permit it the call surfaces a structured
10
+ * PROVIDER_* error and the caller can fall back to supplying `--posts`.
11
+ */
12
+ export interface FetchedTweet {
13
+ text: string;
14
+ externalId?: string | undefined;
15
+ url?: string | undefined;
16
+ authorUsername?: string | undefined;
17
+ authorName?: string | undefined;
18
+ postedAt?: string | undefined;
19
+ conversationId?: string | undefined;
20
+ }
21
+ export type FetchMode = "auto" | "oembed" | "api";
22
+ export declare function isFetchMode(value: string): value is FetchMode;
23
+ /** Parse a tweet reference: a bare numeric id or any twitter.com / x.com URL. */
24
+ export declare function parseTweetRef(input: string): {
25
+ id: string | undefined;
26
+ url: string;
27
+ };
28
+ /** Pure parser for an oEmbed payload — unit-testable without network. */
29
+ export declare function parseOembedPayload(payload: unknown, fallbackUrl: string): FetchedTweet;
30
+ /**
31
+ * Fetch an account's recent original posts (retweets/replies excluded) via the
32
+ * official API. Requires a Bearer token whose access tier permits timeline reads.
33
+ */
34
+ export declare function fetchAccountTimeline(username: string, bearer: string, count: number): Promise<FetchedTweet[]>;
35
+ /** Pure parser for a recent-search payload: the author's replies in a conversation. */
36
+ export declare function parseThreadSearchPayload(payload: unknown, username: string | undefined): FetchedTweet[];
37
+ /** Merge the root tweet with its replies, deduped by id and in posting order. */
38
+ export declare function assembleThread(root: FetchedTweet, replies: FetchedTweet[]): FetchedTweet[];
39
+ export interface ThreadFetchResult {
40
+ root: FetchedTweet;
41
+ parts: FetchedTweet[];
42
+ /** False when the reply search was unavailable and only the root was fetched. */
43
+ complete: boolean;
44
+ }
45
+ /**
46
+ * Fetch a self-thread via the official API: the tweet itself plus the author's
47
+ * own replies in the same conversation. Recent search only covers ~7 days and
48
+ * some access tiers exclude it — in those cases the result degrades to the
49
+ * root tweet with `complete: false` instead of failing.
50
+ */
51
+ export declare function fetchThread(refInput: string, bearer: string): Promise<ThreadFetchResult>;
52
+ export interface TweetFetchResult {
53
+ tweet: FetchedTweet;
54
+ source: "x:oembed" | "x:api";
55
+ }
56
+ /**
57
+ * Fetch one tweet, free-first. `auto` tries oEmbed (free) and only falls back to
58
+ * the API when oEmbed fails and a Bearer token is available.
59
+ */
60
+ export declare function fetchTweet(refInput: string, mode: FetchMode, bearer: string | null): Promise<TweetFetchResult>;
61
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/x/client.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAElD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,SAAS,CAE7D;AAoBD,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAepF;AAoDD,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAmBtF;AA8DD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,YAAY,EAAE,CAAC,CAqBzB;AAYD,uFAAuF;AACvF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,YAAY,EAAE,CAkBhB;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAW1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA2B9F;AAID,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,gBAAgB,CAAC,CA6B3B"}
@@ -0,0 +1,321 @@
1
+ import { CliError } from "../core/errors.js";
2
+ export function isFetchMode(value) {
3
+ return value === "auto" || value === "oembed" || value === "api";
4
+ }
5
+ const REQUEST_TIMEOUT_MS = 15_000;
6
+ const OEMBED_ENDPOINT = "https://publish.twitter.com/oembed";
7
+ const API_BASE = "https://api.twitter.com/2";
8
+ function asRecord(value) {
9
+ return typeof value === "object" && value !== null ? value : {};
10
+ }
11
+ function idFromUrl(url) {
12
+ if (!url) {
13
+ return undefined;
14
+ }
15
+ const match = url.match(/status(?:es)?\/(\d+)/i);
16
+ return match ? match[1] : undefined;
17
+ }
18
+ /** Parse a tweet reference: a bare numeric id or any twitter.com / x.com URL. */
19
+ export function parseTweetRef(input) {
20
+ const trimmed = input.trim();
21
+ if (/^\d+$/.test(trimmed)) {
22
+ return { id: trimmed, url: `https://twitter.com/i/status/${trimmed}` };
23
+ }
24
+ const match = trimmed.match(/(?:twitter\.com|x\.com)\/[^/]+\/status(?:es)?\/(\d+)/i);
25
+ if (match) {
26
+ return { id: match[1], url: trimmed.replace(/^http:/i, "https:") };
27
+ }
28
+ if (/^https?:\/\//i.test(trimmed)) {
29
+ return { id: undefined, url: trimmed };
30
+ }
31
+ throw new CliError("USAGE", `Could not parse a tweet id or URL from "${input}".`, {
32
+ details: { input },
33
+ });
34
+ }
35
+ // --- oEmbed (free) -----------------------------------------------------------
36
+ const NAMED_ENTITIES = {
37
+ amp: "&",
38
+ lt: "<",
39
+ gt: ">",
40
+ quot: '"',
41
+ apos: "'",
42
+ nbsp: " ",
43
+ };
44
+ function decodeEntities(text) {
45
+ return text.replace(/&(#x?[0-9a-f]+|[a-z]+);/gi, (match, code) => {
46
+ if (code.startsWith("#")) {
47
+ const num = code[1]?.toLowerCase() === "x"
48
+ ? Number.parseInt(code.slice(2), 16)
49
+ : Number.parseInt(code.slice(1), 10);
50
+ return Number.isFinite(num) ? String.fromCodePoint(num) : match;
51
+ }
52
+ return NAMED_ENTITIES[code.toLowerCase()] ?? match;
53
+ });
54
+ }
55
+ /** Extract readable tweet text from the oEmbed HTML blockquote. */
56
+ function extractTweetText(html) {
57
+ const paragraph = html.match(/<p[^>]*>([\s\S]*?)<\/p>/i);
58
+ const inner = paragraph ? (paragraph[1] ?? "") : html;
59
+ const withBreaks = inner.replace(/<br\s*\/?>/gi, "\n");
60
+ const stripped = withBreaks.replace(/<[^>]+>/g, "");
61
+ return decodeEntities(stripped)
62
+ .replace(/[ \t]+\n/g, "\n")
63
+ .replace(/\n{3,}/g, "\n\n")
64
+ .trim();
65
+ }
66
+ /**
67
+ * The blockquote's trailing link text is the post date ("June 20, 2026").
68
+ * Day-level precision only, but the free path otherwise has no date at all.
69
+ */
70
+ function extractTweetDate(html) {
71
+ const links = [...html.matchAll(/<a[^>]*>([^<]+)<\/a>/gi)];
72
+ const last = links[links.length - 1]?.[1];
73
+ if (!last) {
74
+ return undefined;
75
+ }
76
+ const date = new Date(`${decodeEntities(last).trim()} UTC`);
77
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
78
+ }
79
+ /** Pure parser for an oEmbed payload — unit-testable without network. */
80
+ export function parseOembedPayload(payload, fallbackUrl) {
81
+ const record = asRecord(payload);
82
+ const html = typeof record.html === "string" ? record.html : "";
83
+ const text = extractTweetText(html);
84
+ if (!text) {
85
+ throw new CliError("PROVIDER_BAD_OUTPUT", "Could not extract tweet text from oEmbed response.");
86
+ }
87
+ const url = typeof record.url === "string" ? record.url : fallbackUrl;
88
+ const authorName = typeof record.author_name === "string" ? record.author_name : undefined;
89
+ const authorUrl = typeof record.author_url === "string" ? record.author_url : undefined;
90
+ const authorUsername = authorUrl ? authorUrl.split("/").filter(Boolean).pop() : undefined;
91
+ return {
92
+ text,
93
+ url,
94
+ authorName,
95
+ authorUsername,
96
+ postedAt: extractTweetDate(html),
97
+ externalId: idFromUrl(url) ?? idFromUrl(fallbackUrl),
98
+ };
99
+ }
100
+ async function fetchTweetOembed(url) {
101
+ const endpoint = `${OEMBED_ENDPOINT}?omit_script=true&dnt=true&url=${encodeURIComponent(url)}`;
102
+ const response = await safeFetch(endpoint, {}, "oembed");
103
+ if (response.status === 404) {
104
+ throw new CliError("NOT_FOUND", "Tweet not found, deleted, or not public (oEmbed).", {
105
+ details: { url },
106
+ });
107
+ }
108
+ if (!response.ok) {
109
+ throw mapXStatus(response.status, "oembed", `request failed (${response.status})`);
110
+ }
111
+ return parseOembedPayload(await response.json(), url);
112
+ }
113
+ // --- official API (Bearer token) ---------------------------------------------
114
+ async function fetchTweetApi(id, bearer) {
115
+ const url = `${API_BASE}/tweets/${id}?expansions=author_id&tweet.fields=created_at,conversation_id&user.fields=username,name`;
116
+ const response = await apiGet(url, bearer);
117
+ const payload = asRecord(await response.json());
118
+ const data = asRecord(payload.data);
119
+ const users = asRecord(payload.includes).users;
120
+ const author = Array.isArray(users) ? asRecord(users[0]) : {};
121
+ const text = typeof data.text === "string" ? data.text : "";
122
+ if (!text) {
123
+ throw new CliError("PROVIDER_BAD_OUTPUT", "X API returned a tweet without text.");
124
+ }
125
+ const username = typeof author.username === "string" ? author.username : undefined;
126
+ const tweetId = typeof data.id === "string" ? data.id : id;
127
+ return {
128
+ text,
129
+ externalId: tweetId,
130
+ url: `https://x.com/${username ?? "i"}/status/${tweetId}`,
131
+ authorUsername: username,
132
+ authorName: typeof author.name === "string" ? author.name : undefined,
133
+ postedAt: typeof data.created_at === "string" ? data.created_at : undefined,
134
+ conversationId: typeof data.conversation_id === "string" ? data.conversation_id : undefined,
135
+ };
136
+ }
137
+ async function fetchUserByUsername(username, bearer) {
138
+ const url = `${API_BASE}/users/by/username/${encodeURIComponent(username)}?user.fields=name`;
139
+ const response = await apiGet(url, bearer);
140
+ const data = asRecord(asRecord(await response.json()).data);
141
+ if (typeof data.id !== "string") {
142
+ throw new CliError("NOT_FOUND", `X account @${username} not found.`, { details: { username } });
143
+ }
144
+ return {
145
+ id: data.id,
146
+ username: typeof data.username === "string" ? data.username : username,
147
+ name: typeof data.name === "string" ? data.name : undefined,
148
+ };
149
+ }
150
+ /**
151
+ * Fetch an account's recent original posts (retweets/replies excluded) via the
152
+ * official API. Requires a Bearer token whose access tier permits timeline reads.
153
+ */
154
+ export async function fetchAccountTimeline(username, bearer, count) {
155
+ const user = await fetchUserByUsername(username, bearer);
156
+ const max = Math.min(100, Math.max(5, count));
157
+ const url = `${API_BASE}/users/${user.id}/tweets?max_results=${max}&exclude=retweets,replies&tweet.fields=created_at`;
158
+ const response = await apiGet(url, bearer);
159
+ const data = asRecord(await response.json()).data;
160
+ if (!Array.isArray(data)) {
161
+ return [];
162
+ }
163
+ return data
164
+ .map((entry) => {
165
+ const record = asRecord(entry);
166
+ const id = typeof record.id === "string" ? record.id : "";
167
+ return {
168
+ text: typeof record.text === "string" ? record.text : "",
169
+ externalId: id,
170
+ url: `https://x.com/${user.username}/status/${id}`,
171
+ postedAt: typeof record.created_at === "string" ? record.created_at : undefined,
172
+ };
173
+ })
174
+ .filter((tweet) => tweet.text.length > 0);
175
+ }
176
+ // --- threads (official API) ---------------------------------------------------
177
+ /** Numeric-string tweet ids (snowflakes) compare by length, then lexically. */
178
+ function compareTweetIds(a, b) {
179
+ if (a.length !== b.length) {
180
+ return a.length - b.length;
181
+ }
182
+ return a < b ? -1 : a > b ? 1 : 0;
183
+ }
184
+ /** Pure parser for a recent-search payload: the author's replies in a conversation. */
185
+ export function parseThreadSearchPayload(payload, username) {
186
+ const data = asRecord(payload).data;
187
+ if (!Array.isArray(data)) {
188
+ return [];
189
+ }
190
+ return data
191
+ .map((entry) => {
192
+ const record = asRecord(entry);
193
+ const id = typeof record.id === "string" ? record.id : "";
194
+ return {
195
+ text: typeof record.text === "string" ? record.text : "",
196
+ externalId: id,
197
+ url: `https://x.com/${username ?? "i"}/status/${id}`,
198
+ authorUsername: username,
199
+ postedAt: typeof record.created_at === "string" ? record.created_at : undefined,
200
+ };
201
+ })
202
+ .filter((tweet) => tweet.text.length > 0 && tweet.externalId.length > 0);
203
+ }
204
+ /** Merge the root tweet with its replies, deduped by id and in posting order. */
205
+ export function assembleThread(root, replies) {
206
+ const byId = new Map();
207
+ for (const tweet of [root, ...replies]) {
208
+ if (tweet.externalId) {
209
+ byId.set(tweet.externalId, tweet);
210
+ }
211
+ }
212
+ if (byId.size === 0) {
213
+ return [root];
214
+ }
215
+ return [...byId.values()].sort((a, b) => compareTweetIds(a.externalId ?? "", b.externalId ?? ""));
216
+ }
217
+ /**
218
+ * Fetch a self-thread via the official API: the tweet itself plus the author's
219
+ * own replies in the same conversation. Recent search only covers ~7 days and
220
+ * some access tiers exclude it — in those cases the result degrades to the
221
+ * root tweet with `complete: false` instead of failing.
222
+ */
223
+ export async function fetchThread(refInput, bearer) {
224
+ const ref = parseTweetRef(refInput);
225
+ if (!ref.id) {
226
+ throw new CliError("USAGE", "Fetching a thread needs a numeric tweet id or a status URL.");
227
+ }
228
+ const root = await fetchTweetApi(ref.id, bearer);
229
+ const conversationId = root.conversationId ?? ref.id;
230
+ const username = root.authorUsername;
231
+ let replies = [];
232
+ let complete = false;
233
+ if (username) {
234
+ const query = `conversation_id:${conversationId} from:${username} to:${username}`;
235
+ const url = `${API_BASE}/tweets/search/recent?query=${encodeURIComponent(query)}&max_results=100&tweet.fields=created_at`;
236
+ try {
237
+ const response = await apiGet(url, bearer);
238
+ replies = parseThreadSearchPayload(await response.json(), username);
239
+ complete = true;
240
+ }
241
+ catch (error) {
242
+ // A tier without recent search shouldn't sink the whole command.
243
+ if (!(error instanceof CliError) || error.code !== "PROVIDER_UNAUTHORIZED") {
244
+ throw error;
245
+ }
246
+ }
247
+ }
248
+ return { root, parts: assembleThread(root, replies), complete };
249
+ }
250
+ /**
251
+ * Fetch one tweet, free-first. `auto` tries oEmbed (free) and only falls back to
252
+ * the API when oEmbed fails and a Bearer token is available.
253
+ */
254
+ export async function fetchTweet(refInput, mode, bearer) {
255
+ const ref = parseTweetRef(refInput);
256
+ if (mode === "api") {
257
+ if (!bearer) {
258
+ throw new CliError("MISSING_CREDENTIALS", "--fetch api needs an X Bearer token. Configure it with `tenbrains setup --x-bearer <token>`.");
259
+ }
260
+ if (!ref.id) {
261
+ throw new CliError("USAGE", "--fetch api needs a numeric tweet id or a status URL.");
262
+ }
263
+ return { tweet: await fetchTweetApi(ref.id, bearer), source: "x:api" };
264
+ }
265
+ if (mode === "oembed") {
266
+ return { tweet: await fetchTweetOembed(ref.url), source: "x:oembed" };
267
+ }
268
+ // auto: free first, fall back to the API only if a token is present.
269
+ try {
270
+ return { tweet: await fetchTweetOembed(ref.url), source: "x:oembed" };
271
+ }
272
+ catch (error) {
273
+ if (bearer && ref.id) {
274
+ return { tweet: await fetchTweetApi(ref.id, bearer), source: "x:api" };
275
+ }
276
+ throw error;
277
+ }
278
+ }
279
+ // --- HTTP helpers ------------------------------------------------------------
280
+ async function safeFetch(url, init, label) {
281
+ const controller = new AbortController();
282
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
283
+ try {
284
+ return await fetch(url, { ...init, signal: controller.signal });
285
+ }
286
+ catch (error) {
287
+ throw new CliError("PROVIDER_NETWORK", controller.signal.aborted
288
+ ? `x ${label}: request timed out after ${REQUEST_TIMEOUT_MS}ms`
289
+ : `x ${label}: ${error instanceof Error ? error.message : "network error"}`, { retryable: true, cause: error });
290
+ }
291
+ finally {
292
+ clearTimeout(timeout);
293
+ }
294
+ }
295
+ async function apiGet(url, bearer) {
296
+ const response = await safeFetch(url, { headers: { authorization: `Bearer ${bearer}` } }, "api");
297
+ if (response.ok) {
298
+ return response;
299
+ }
300
+ const body = await response.text();
301
+ throw mapXStatus(response.status, "api", body.slice(0, 200));
302
+ }
303
+ function mapXStatus(status, label, detail) {
304
+ if (status === 401 || status === 403) {
305
+ return new CliError("PROVIDER_UNAUTHORIZED", `x ${label}: unauthorized (${status}). The token is invalid or your X API access tier does not permit this read — timeline/tweet reads usually require a paid tier (Basic+).`, { details: { status, detail } });
306
+ }
307
+ if (status === 429) {
308
+ return new CliError("PROVIDER_RATE_LIMITED", `x ${label}: rate limited (429).`, {
309
+ details: { status },
310
+ retryable: true,
311
+ });
312
+ }
313
+ if (status === 404) {
314
+ return new CliError("NOT_FOUND", `x ${label}: not found (404).`, { details: { detail } });
315
+ }
316
+ return new CliError("PROVIDER_UPSTREAM", `x ${label}: request failed (${status}).`, {
317
+ details: { status, detail },
318
+ retryable: status >= 500,
319
+ });
320
+ }
321
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/x/client.ts"],"names":[],"mappings":"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"}