x-reader 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.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/dist/api/client.d.ts +65 -0
- package/dist/api/client.js +527 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/constants.d.ts +23 -0
- package/dist/api/constants.js +60 -0
- package/dist/api/constants.js.map +1 -0
- package/dist/api/features.d.ts +14 -0
- package/dist/api/features.js +158 -0
- package/dist/api/features.js.map +1 -0
- package/dist/api/parser.d.ts +23 -0
- package/dist/api/parser.js +236 -0
- package/dist/api/parser.js.map +1 -0
- package/dist/api/query-ids.d.ts +31 -0
- package/dist/api/query-ids.js +201 -0
- package/dist/api/query-ids.js.map +1 -0
- package/dist/api/types.d.ts +91 -0
- package/dist/api/types.js +3 -0
- package/dist/api/types.js.map +1 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.js +258 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/auth.d.ts +21 -0
- package/dist/utils/auth.js +128 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/format.d.ts +15 -0
- package/dist/utils/format.js +62 -0
- package/dist/utils/format.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication utilities — resolve cookies from Chrome, env, or config file.
|
|
3
|
+
*/
|
|
4
|
+
import { readFile } from 'fs/promises';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
const CONFIG_DIR = join(homedir(), '.config', 'x-reader');
|
|
9
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
10
|
+
/**
|
|
11
|
+
* Try to get cookies from Chrome's cookie store on macOS.
|
|
12
|
+
* This uses `security` CLI to access the keychain and `sqlite3` to read the cookie DB.
|
|
13
|
+
*/
|
|
14
|
+
async function getCookiesFromChrome() {
|
|
15
|
+
if (process.platform !== 'darwin')
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
// Chrome cookie DB path on macOS
|
|
19
|
+
const cookieDb = join(homedir(), 'Library/Application Support/Google/Chrome/Default/Cookies');
|
|
20
|
+
if (!existsSync(cookieDb))
|
|
21
|
+
return null;
|
|
22
|
+
// Note: Chrome encrypts cookies on macOS. We'd need the encryption key from Keychain.
|
|
23
|
+
// For now, this is a placeholder — users should use env vars or config file.
|
|
24
|
+
// TODO: Implement Chrome cookie decryption if needed.
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read cookies from config file (~/.config/x-reader/config.json).
|
|
33
|
+
*/
|
|
34
|
+
async function getCookiesFromConfig() {
|
|
35
|
+
try {
|
|
36
|
+
if (!existsSync(CONFIG_FILE))
|
|
37
|
+
return null;
|
|
38
|
+
const raw = await readFile(CONFIG_FILE, 'utf8');
|
|
39
|
+
const config = JSON.parse(raw);
|
|
40
|
+
if (config.auth_token && config.ct0) {
|
|
41
|
+
return {
|
|
42
|
+
authToken: config.auth_token,
|
|
43
|
+
ct0: config.ct0,
|
|
44
|
+
source: 'config file',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Read cookies from Bird CLI's config (for seamless migration).
|
|
55
|
+
*/
|
|
56
|
+
async function getCookiesFromBirdConfig() {
|
|
57
|
+
try {
|
|
58
|
+
const birdConfig = join(homedir(), '.config', 'bird', 'config.json');
|
|
59
|
+
if (!existsSync(birdConfig))
|
|
60
|
+
return null;
|
|
61
|
+
const raw = await readFile(birdConfig, 'utf8');
|
|
62
|
+
const config = JSON.parse(raw);
|
|
63
|
+
if (config.auth_token && config.ct0) {
|
|
64
|
+
return {
|
|
65
|
+
authToken: config.auth_token,
|
|
66
|
+
ct0: config.ct0,
|
|
67
|
+
source: 'bird config',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Read cookies from environment variables.
|
|
78
|
+
*/
|
|
79
|
+
function getCookiesFromEnv() {
|
|
80
|
+
const authToken = process.env.AUTH_TOKEN || process.env.TWITTER_AUTH_TOKEN;
|
|
81
|
+
const ct0 = process.env.CT0 || process.env.TWITTER_CT0;
|
|
82
|
+
if (authToken && ct0) {
|
|
83
|
+
return { authToken, ct0, source: 'environment variables' };
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve cookies from all sources. Priority:
|
|
89
|
+
* 1. CLI flags (--auth-token, --ct0)
|
|
90
|
+
* 2. Environment variables (AUTH_TOKEN, CT0)
|
|
91
|
+
* 3. x-reader config file
|
|
92
|
+
* 4. Bird CLI config file (migration)
|
|
93
|
+
* 5. Chrome cookies (macOS only)
|
|
94
|
+
*/
|
|
95
|
+
export async function resolveCookies(flags = {}) {
|
|
96
|
+
const warnings = [];
|
|
97
|
+
// 1. CLI flags
|
|
98
|
+
if (flags.authToken && flags.ct0) {
|
|
99
|
+
return {
|
|
100
|
+
cookies: { authToken: flags.authToken, ct0: flags.ct0, source: 'CLI flags' },
|
|
101
|
+
warnings,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// 2. Environment
|
|
105
|
+
const envCookies = getCookiesFromEnv();
|
|
106
|
+
if (envCookies)
|
|
107
|
+
return { cookies: envCookies, warnings };
|
|
108
|
+
// 3. x-reader config
|
|
109
|
+
const configCookies = await getCookiesFromConfig();
|
|
110
|
+
if (configCookies)
|
|
111
|
+
return { cookies: configCookies, warnings };
|
|
112
|
+
// 4. Bird config (migration)
|
|
113
|
+
const birdCookies = await getCookiesFromBirdConfig();
|
|
114
|
+
if (birdCookies) {
|
|
115
|
+
warnings.push('Using Bird CLI config. Run `x-reader setup` to create x-reader config.');
|
|
116
|
+
return { cookies: birdCookies, warnings };
|
|
117
|
+
}
|
|
118
|
+
// 5. Chrome (placeholder)
|
|
119
|
+
const chromeCookies = await getCookiesFromChrome();
|
|
120
|
+
if (chromeCookies)
|
|
121
|
+
return { cookies: chromeCookies, warnings };
|
|
122
|
+
// Nothing found
|
|
123
|
+
return {
|
|
124
|
+
cookies: { authToken: '', ct0: '', source: '' },
|
|
125
|
+
warnings: ['No credentials found. Set AUTH_TOKEN and CT0 env vars, or run `x-reader setup`.'],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/utils/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatting — JSON and human-readable text.
|
|
3
|
+
*/
|
|
4
|
+
import type { Tweet } from '../api/types.js';
|
|
5
|
+
/** Format a tweet for human-readable output */
|
|
6
|
+
export declare function formatTweet(tweet: Tweet, separator?: boolean): string;
|
|
7
|
+
/** Format tweets array for output */
|
|
8
|
+
export declare function formatTweets(tweets: Tweet[], options?: {
|
|
9
|
+
json?: boolean;
|
|
10
|
+
emptyMessage?: string;
|
|
11
|
+
}): string;
|
|
12
|
+
/** Extract tweet ID from URL or return as-is */
|
|
13
|
+
export declare function extractTweetId(input: string): string;
|
|
14
|
+
/** Normalize username — strip @ if present */
|
|
15
|
+
export declare function normalizeUsername(input: string): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatting — JSON and human-readable text.
|
|
3
|
+
*/
|
|
4
|
+
/** Format a tweet for human-readable output */
|
|
5
|
+
export function formatTweet(tweet, separator = true) {
|
|
6
|
+
const lines = [];
|
|
7
|
+
const header = `@${tweet.author.username} (${tweet.author.name})`;
|
|
8
|
+
const date = tweet.createdAt ? ` ${tweet.createdAt}` : '';
|
|
9
|
+
lines.push(`${header}${date}`);
|
|
10
|
+
lines.push(tweet.text);
|
|
11
|
+
const stats = [];
|
|
12
|
+
if (tweet.likeCount !== undefined)
|
|
13
|
+
stats.push(`❤️ ${tweet.likeCount}`);
|
|
14
|
+
if (tweet.retweetCount !== undefined)
|
|
15
|
+
stats.push(`🔁 ${tweet.retweetCount}`);
|
|
16
|
+
if (tweet.replyCount !== undefined)
|
|
17
|
+
stats.push(`💬 ${tweet.replyCount}`);
|
|
18
|
+
if (stats.length > 0)
|
|
19
|
+
lines.push(stats.join(' '));
|
|
20
|
+
lines.push(`🔗 https://x.com/i/status/${tweet.id}`);
|
|
21
|
+
if (tweet.quotedTweet) {
|
|
22
|
+
lines.push(` ↪ Quoting @${tweet.quotedTweet.author.username}: ${tweet.quotedTweet.text.slice(0, 100)}…`);
|
|
23
|
+
}
|
|
24
|
+
if (tweet.media?.length) {
|
|
25
|
+
for (const m of tweet.media) {
|
|
26
|
+
if (m.videoUrl) {
|
|
27
|
+
lines.push(` 🎬 ${m.videoUrl}`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
lines.push(` 🖼️ ${m.url}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (separator)
|
|
35
|
+
lines.push('─'.repeat(50));
|
|
36
|
+
return lines.join('\n');
|
|
37
|
+
}
|
|
38
|
+
/** Format tweets array for output */
|
|
39
|
+
export function formatTweets(tweets, options = {}) {
|
|
40
|
+
if (options.json) {
|
|
41
|
+
return JSON.stringify(tweets, null, 2);
|
|
42
|
+
}
|
|
43
|
+
if (tweets.length === 0) {
|
|
44
|
+
return options.emptyMessage ?? 'No tweets found.';
|
|
45
|
+
}
|
|
46
|
+
return tweets.map((t) => formatTweet(t)).join('\n');
|
|
47
|
+
}
|
|
48
|
+
/** Extract tweet ID from URL or return as-is */
|
|
49
|
+
export function extractTweetId(input) {
|
|
50
|
+
const urlMatch = input.match(/(?:twitter\.com|x\.com)\/\w+\/status\/(\d+)/);
|
|
51
|
+
if (urlMatch)
|
|
52
|
+
return urlMatch[1];
|
|
53
|
+
if (/^\d+$/.test(input.trim()))
|
|
54
|
+
return input.trim();
|
|
55
|
+
throw new Error(`Invalid tweet ID or URL: ${input}`);
|
|
56
|
+
}
|
|
57
|
+
/** Normalize username — strip @ if present */
|
|
58
|
+
export function normalizeUsername(input) {
|
|
59
|
+
const trimmed = input.trim();
|
|
60
|
+
return trimmed.startsWith('@') ? trimmed.slice(1) : trimmed;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "x-reader",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Read-only Twitter/X CLI. Search tweets, read timelines, bookmarks, and replies from the terminal.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"x-reader": "./dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"start": "node dist/cli/index.js",
|
|
20
|
+
"test": "node --test tests/*.test.js",
|
|
21
|
+
"lint": "tsc --noEmit",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"twitter",
|
|
26
|
+
"x",
|
|
27
|
+
"cli",
|
|
28
|
+
"tweet",
|
|
29
|
+
"reader",
|
|
30
|
+
"twitter-api",
|
|
31
|
+
"twitter-cli",
|
|
32
|
+
"x-api",
|
|
33
|
+
"tweet-reader",
|
|
34
|
+
"twitter-scraper",
|
|
35
|
+
"bookmarks",
|
|
36
|
+
"timeline",
|
|
37
|
+
"search-tweets",
|
|
38
|
+
"read-only",
|
|
39
|
+
"terminal",
|
|
40
|
+
"command-line",
|
|
41
|
+
"social-media"
|
|
42
|
+
],
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/DjinnFoundry/x-reader.git"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/DjinnFoundry/x-reader#readme",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/DjinnFoundry/x-reader/issues"
|
|
51
|
+
},
|
|
52
|
+
"author": "DjinnFoundry",
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"registry": "https://registry.npmjs.org/"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=20"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"commander": "^12.1.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^22.0.0",
|
|
64
|
+
"typescript": "^5.7.0"
|
|
65
|
+
}
|
|
66
|
+
}
|