ants-move 0.0.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stephen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # ants-move
2
+
3
+ `ants-move` is an open-source TypeScript CLI for moving data between systems. Its commands are small workers: collectors bring data in, and future automation commands can move that data into forms and other systems. The first release collects data from 36Kr, Toutiao, and Hacker News.
4
+
5
+ ## Installation and requirements
6
+
7
+ Requirements:
8
+
9
+ - Node.js 22 or later
10
+ - npm
11
+ - `curl` on `PATH` for 36Kr collection
12
+ - Chromium installed through Playwright for Toutiao collection
13
+
14
+ Install the package globally, then install the browser used by Toutiao:
15
+
16
+ ```bash
17
+ npm install -g ants-move
18
+ npx playwright@1.59.1 install chromium
19
+ ```
20
+
21
+ The package installs two equivalent executables: `ants` and the short alias `amv`.
22
+
23
+ ## Output conventions
24
+
25
+ Commands write successful results to stdout and structured errors to stderr. JSON is the default format. List and search commands accept `--format table` or the `-t` shortcut for table output. Article detail commands support JSON only.
26
+
27
+ Successful JSON uses an `{ "ok": true, "data": ... }` envelope. Errors use an `{ "ok": false, "error": ... }` envelope and return a nonzero exit code.
28
+
29
+ ## 36Kr commands
30
+
31
+ Fetch an article by numeric ID:
32
+
33
+ ```bash
34
+ ants 36kr article <article-id> [--format json]
35
+ ```
36
+
37
+ Fetch one to 20 information pages from the `AI` or `technology` channel:
38
+
39
+ ```bash
40
+ ants 36kr list <AI|technology> [--pages 1..20] [--format json|table] [-t]
41
+ ```
42
+
43
+ Pagination is serial and bounded at 20 pages per invocation. Each curl response and mapped article result is limited to 20 MB, each list page to 30 items, and the mapped list to 5 MB, so a list retains at most 600 items without accumulating oversized fields across pages.
44
+
45
+ ## Toutiao commands and verification limitations
46
+
47
+ Fetch an article by numeric ID or a Toutiao article, group, or legacy URL:
48
+
49
+ ```bash
50
+ ants toutiao article <article-id-or-url> [--format json]
51
+ ```
52
+
53
+ Fetch one to five pages from the technology channel or a supported search keyword:
54
+
55
+ ```bash
56
+ ants toutiao list <tech|AI|光刻机|芯片|半导体> [--pages 1..5] [--format json|table] [-t]
57
+ ```
58
+
59
+ Fetch an author feed by token or `/c/user/token/<token>/` profile URL:
60
+
61
+ ```bash
62
+ ants toutiao author <token-or-url> [--pages 1..5] [--with-content] [--format json|table] [-t]
63
+ ```
64
+
65
+ Feed collection keeps at most the requested number of response parses active, starts at most twice that many bounded parse attempts to replace malformed responses, and accepts at most five successful responses. Each feed body is limited to 5 MB before JSON parsing and 100 raw items after parsing.
66
+
67
+ Article extraction limits title, body, and paragraph text to 1 MB each. Search fallback inspection serializes at most 300 bounded links and rejects body text larger than 1 MB. `--with-content` fetches article details serially in the same browser session, accepts at most 100 unique articles, and retains at most 10 MB of article results per invocation. Toutiao can require interactive browser verification; when verification prevents usable search results, the command returns `TOUTIAO_VERIFICATION_REQUIRED`. The CLI does not attempt to bypass site verification.
68
+
69
+ ## Hacker News commands and aliases
70
+
71
+ Fetch one to 100 stories from each Firebase list:
72
+
73
+ ```bash
74
+ ants hn top [--limit 1..100] [--format json|table] [-t]
75
+ ants hn new [--limit 1..100] [--format json|table] [-t]
76
+ ants hn best [--limit 1..100] [--format json|table] [-t]
77
+ ```
78
+
79
+ Search through Algolia, ordered by relevance or date:
80
+
81
+ ```bash
82
+ ants hn search <query> [--limit 1..100] [--sort relevance|date] [--format json|table] [-t]
83
+ ```
84
+
85
+ `hackernews` is an alias for `hn`, so `ants hackernews top` is equivalent to `ants hn top`. The `amv` executable accepts every command shown above, for example `amv hackernews search typescript`.
86
+
87
+ Hacker News uses at most eight concurrent detail requests. ID-list and search responses are limited to 5 MB, while each Firebase story detail is limited to 256 KB.
88
+
89
+ ## High-concurrency usage warning
90
+
91
+ Resource use is bounded within one process, but identical commands running in separate processes are not globally deduplicated or rate limited. At `Q` concurrent invocations, upstream work can approach `20Q` requests for a 36Kr list, `201Q` requests for a Hacker News list, or `105Q` browser navigations plus page subresources for a Toutiao author collection.
92
+
93
+ Retained collector data is also bounded per invocation: 5 MB for a 36Kr list, approximately 50 MB across 200 Hacker News candidate details before result filtering, and 10 MB for Toutiao author article results. Toutiao may additionally hold up to five active 5 MB feed buffers inside one browser session; browser process overhead and required page subresources are separate from these payload limits.
94
+
95
+ Do not place the CLI directly on a high-QPS request path. Online services must use an external bounded queue and a shared rate limiter to apply backpressure across processes and instances. This project has no distributed lock, shared cache, retry queue, or multi-instance single-flight mechanism.
96
+
97
+ ## Development and verification
98
+
99
+ Install dependencies and run the deterministic checks:
100
+
101
+ ```bash
102
+ npm install
103
+ npm run check
104
+ npm test
105
+ npm run coverage
106
+ npm run build
107
+ npm pack --json
108
+ ```
109
+
110
+ Tests use injected network and browser boundaries and do not contact live websites.
111
+
112
+ ## License
113
+
114
+ Licensed under the [MIT License](LICENSE).
@@ -0,0 +1,143 @@
1
+ type HackerNewsStorySource = 'top' | 'new' | 'best';
2
+ type HackerNewsSearchSort = 'relevance' | 'date';
3
+ interface HackerNewsItem {
4
+ author?: string;
5
+ commentCount?: number;
6
+ id: number;
7
+ score?: number;
8
+ text?: string;
9
+ time?: {
10
+ iso: string;
11
+ seconds: number;
12
+ };
13
+ title: string;
14
+ type: 'story';
15
+ url: string;
16
+ }
17
+ interface HackerNewsStoriesRuntimeResult {
18
+ items: HackerNewsItem[];
19
+ source: HackerNewsStorySource;
20
+ }
21
+ interface HackerNewsSearchRuntimeResult {
22
+ items: HackerNewsItem[];
23
+ query: string;
24
+ }
25
+
26
+ interface HackerNewsRuntime {
27
+ fetchSearch: (options: {
28
+ limit: number;
29
+ query: string;
30
+ sort: HackerNewsSearchSort;
31
+ }) => Promise<HackerNewsSearchRuntimeResult>;
32
+ fetchStories: (options: {
33
+ limit: number;
34
+ source: HackerNewsStorySource;
35
+ }) => Promise<HackerNewsStoriesRuntimeResult>;
36
+ }
37
+
38
+ interface Kr36Request {
39
+ headers: Record<string, string>;
40
+ url: string;
41
+ }
42
+ interface Kr36JsonRequest {
43
+ body: unknown;
44
+ headers: Record<string, string>;
45
+ url: string;
46
+ }
47
+
48
+ interface Kr36Runtime {
49
+ fetchArticleHtml: (request: Kr36Request) => Promise<string>;
50
+ fetchJson: (request: Kr36JsonRequest) => Promise<string>;
51
+ }
52
+
53
+ type ToutiaoSource = 'tech' | 'AI' | '光刻机' | '芯片' | '半导体';
54
+ interface ToutiaoItem {
55
+ abstract?: string;
56
+ authorName?: string;
57
+ commentCount?: number;
58
+ id: string;
59
+ image?: string;
60
+ publishTime?: {
61
+ iso: string;
62
+ local: string;
63
+ seconds: number;
64
+ };
65
+ sourceUrl?: string;
66
+ title: string;
67
+ url: string;
68
+ }
69
+ interface ToutiaoAuthorRuntimeResult {
70
+ authorToken: string;
71
+ hasMore: boolean;
72
+ items: ToutiaoItem[];
73
+ next?: {
74
+ maxBehotTime?: number;
75
+ offset?: number;
76
+ };
77
+ }
78
+ interface ToutiaoArticle {
79
+ authorName?: string;
80
+ content: {
81
+ paragraphs: string[];
82
+ text: string;
83
+ };
84
+ id: string;
85
+ publishTimeText?: string;
86
+ request: {
87
+ input: string;
88
+ url: string;
89
+ };
90
+ title: string;
91
+ url: string;
92
+ }
93
+ interface ToutiaoRuntimeListResult {
94
+ hasMore: boolean;
95
+ items: ToutiaoItem[];
96
+ keyword?: string;
97
+ next?: {
98
+ maxBehotTime?: number;
99
+ offset?: number;
100
+ };
101
+ source: ToutiaoSource;
102
+ }
103
+
104
+ interface ToutiaoSession {
105
+ fetchArticle: (request: {
106
+ input: string;
107
+ url: string;
108
+ }) => Promise<ToutiaoArticle>;
109
+ fetchAuthorArticles: (options: {
110
+ authorToken: string;
111
+ pages: number;
112
+ url: string;
113
+ }) => Promise<ToutiaoAuthorRuntimeResult>;
114
+ fetchKeywordInformation: (options: {
115
+ keyword: ToutiaoSource;
116
+ pages: number;
117
+ source: ToutiaoSource;
118
+ }) => Promise<ToutiaoRuntimeListResult>;
119
+ fetchTechnologyChannel: (options: {
120
+ pages: number;
121
+ }) => Promise<ToutiaoRuntimeListResult>;
122
+ }
123
+ interface ToutiaoRuntime {
124
+ withSession: <T>(operation: (session: ToutiaoSession) => Promise<T>) => Promise<T>;
125
+ }
126
+
127
+ interface CliIo {
128
+ stderr: (value: string) => void;
129
+ stdout: (value: string) => void;
130
+ }
131
+ interface CliRunner {
132
+ run: (args: string[]) => Promise<number>;
133
+ }
134
+ interface CreateCliOptions extends Partial<CliIo> {
135
+ hackerNewsRuntime?: HackerNewsRuntime;
136
+ kr36Runtime?: Kr36Runtime;
137
+ toutiaoRuntime?: ToutiaoRuntime;
138
+ }
139
+ declare function createCli(options?: CreateCliOptions): CliRunner;
140
+
141
+ declare function isMainEntrypoint(moduleUrl: string, argv: string[]): boolean;
142
+
143
+ export { type CliIo, type CliRunner, type CreateCliOptions, createCli, isMainEntrypoint };