Skip to content

Commit c9b3568

Browse files
authored
chore: pre-release cleanup — fix dependencies, sync docs, reduce code duplication (#311)
* chore: pre-release cleanup — fix dependencies, sync docs, reduce code duplication - fix: move @types/turndown from dependencies to devDependencies - docs: backfill CHANGELOG for v1.2.0 through v1.3.1 - docs: remove internal release reminder from READMEs - docs: update SKILL.md version to 1.3.1 - refactor: extract getErrorMessage() to errors.ts (was duplicated 5x) - refactor: introduce CommandArgs type alias in registry.ts - refactor: eliminate as-any in runtime.ts and cli.ts - refactor: parallelize site directory scanning in discovery.ts - refactor: add declare global for registry globalThis access - fix: strengthen isBooleanRecord type guard in explore.ts - fix: replace empty catch with log.debug in explore.ts - fix: daemon now accepts timeout from request body - chore: remove unused REGISTRY_KEY constant - chore: update generate.ts TODO to stub annotation * docs: add doubao-app desktop adapter doc (fixes docs-build dead link)
1 parent b4d64ca commit c9b3568

File tree

13 files changed

+190
-38
lines changed

13 files changed

+190
-38
lines changed

CHANGELOG.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,108 @@
11
# Changelog
22

3+
## [1.3.1](https://github.com/jackwener/opencli/compare/v1.3.0...v1.3.1) (2026-03-22)
4+
5+
6+
### Features
7+
8+
* **plugin:** add update command, hot reload after install, README section ([#307](https://github.com/jackwener/opencli/issues/307)) ([966f6e5](https://github.com/jackwener/opencli/commit/966f6e5))
9+
* **yollomi:** add new commands and update documentation ([#235](https://github.com/jackwener/opencli/issues/235)) ([ea83242](https://github.com/jackwener/opencli/commit/ea83242))
10+
* **record:** add live recording command for API capture ([#300](https://github.com/jackwener/opencli/issues/300)) ([dff0fe5](https://github.com/jackwener/opencli/commit/dff0fe5))
11+
* **weibo:** add weibo search command ([#299](https://github.com/jackwener/opencli/issues/299)) ([c7895ea](https://github.com/jackwener/opencli/commit/c7895ea))
12+
* **v2ex:** add node, user, member, replies, nodes commands ([#282](https://github.com/jackwener/opencli/issues/282)) ([a83027d](https://github.com/jackwener/opencli/commit/a83027d))
13+
* **hackernews:** add new, best, ask, show, jobs, search, user commands ([#290](https://github.com/jackwener/opencli/issues/290)) ([127a974](https://github.com/jackwener/opencli/commit/127a974))
14+
* **doubao-app:** add Doubao AI desktop app CLI adapter ([#289](https://github.com/jackwener/opencli/issues/289)) ([66c4b84](https://github.com/jackwener/opencli/commit/66c4b84))
15+
* **doubao:** add doubao browser adapter ([#277](https://github.com/jackwener/opencli/issues/277)) ([9cdc127](https://github.com/jackwener/opencli/commit/9cdc127))
16+
* **xiaohongshu:** add publish command for 图文 note automation ([#276](https://github.com/jackwener/opencli/issues/276)) ([a6d993f](https://github.com/jackwener/opencli/commit/a6d993f))
17+
* **weixin:** add weixin article download adapter & abstract download helpers ([#280](https://github.com/jackwener/opencli/issues/280)) ([b7c6c02](https://github.com/jackwener/opencli/commit/b7c6c02))
18+
19+
20+
### Bug Fixes
21+
22+
* **tests:** use positional arg syntax in browser search tests ([#302](https://github.com/jackwener/opencli/issues/302)) ([4343ec0](https://github.com/jackwener/opencli/commit/4343ec0))
23+
* **xiaohongshu:** improve search login-wall handling and detail output ([#298](https://github.com/jackwener/opencli/issues/298)) ([f8bf663](https://github.com/jackwener/opencli/commit/f8bf663))
24+
* ensure standard PATH is available for external CLIs ([#285](https://github.com/jackwener/opencli/issues/285)) ([22f5c7a](https://github.com/jackwener/opencli/commit/22f5c7a))
25+
* **xiaohongshu:** scope image selector to avoid downloading avatars ([#293](https://github.com/jackwener/opencli/issues/293)) ([3a21be6](https://github.com/jackwener/opencli/commit/3a21be6))
26+
* add turndown dependency to package.json ([#288](https://github.com/jackwener/opencli/issues/288)) ([2a52906](https://github.com/jackwener/opencli/commit/2a52906))
27+
28+
29+
## [1.3.0](https://github.com/jackwener/opencli/compare/v1.2.3...v1.3.0) (2026-03-21)
30+
31+
32+
### Features
33+
34+
* **daemon:** harden security against browser CSRF attacks ([#268](https://github.com/jackwener/opencli/issues/268)) ([40bd11d](https://github.com/jackwener/opencli/commit/40bd11d))
35+
36+
37+
### Performance
38+
39+
* smart page settle via DOM stability detection ([#271](https://github.com/jackwener/opencli/issues/271)) ([4b976da](https://github.com/jackwener/opencli/commit/4b976da))
40+
41+
42+
### Refactoring
43+
44+
* doctor defaults to live mode, remove setup command entirely ([#263](https://github.com/jackwener/opencli/issues/263)) ([b4a8089](https://github.com/jackwener/opencli/commit/b4a8089))
45+
46+
47+
## [1.2.3](https://github.com/jackwener/opencli/compare/v1.2.2...v1.2.3) (2026-03-21)
48+
49+
50+
### Bug Fixes
51+
52+
* replace all about:blank with data: URI to prevent New Tab Override interception ([#257](https://github.com/jackwener/opencli/issues/257)) ([3e91876](https://github.com/jackwener/opencli/commit/3e91876))
53+
* harden resolveTabId against New Tab Override extension interception ([#255](https://github.com/jackwener/opencli/issues/255)) ([112fdef](https://github.com/jackwener/opencli/commit/112fdef))
54+
55+
56+
## [1.2.2](https://github.com/jackwener/opencli/compare/v1.2.1...v1.2.2) (2026-03-21)
57+
58+
59+
### Bug Fixes
60+
61+
* harden browser automation pipeline (resolves [#249](https://github.com/jackwener/opencli/issues/249)) ([#251](https://github.com/jackwener/opencli/issues/251)) ([71b2c39](https://github.com/jackwener/opencli/commit/71b2c39))
62+
63+
64+
## [1.2.1](https://github.com/jackwener/opencli/compare/v1.2.0...v1.2.1) (2026-03-21)
65+
66+
67+
### Bug Fixes
68+
69+
* **twitter:** harden timeline review findings ([#236](https://github.com/jackwener/opencli/issues/236)) ([4cd0409](https://github.com/jackwener/opencli/commit/4cd0409))
70+
* **wikipedia:** fix search arg name + add random and trending commands ([#231](https://github.com/jackwener/opencli/issues/231)) ([1d56dd7](https://github.com/jackwener/opencli/commit/1d56dd7))
71+
* resolve inconsistent doctor --live report (fix [#121](https://github.com/jackwener/opencli/issues/121)) ([#224](https://github.com/jackwener/opencli/issues/224)) ([387aa0d](https://github.com/jackwener/opencli/commit/387aa0d))
72+
73+
74+
## [1.2.0](https://github.com/jackwener/opencli/compare/v1.1.0...v1.2.0) (2026-03-21)
75+
76+
77+
### Features
78+
79+
* **douban:** add movie adapter with search, top250, subject, marks, reviews commands ([#239](https://github.com/jackwener/opencli/issues/239)) ([70651d3](https://github.com/jackwener/opencli/commit/70651d3))
80+
* **devto:** add devto adapter ([#234](https://github.com/jackwener/opencli/issues/234)) ([ea113a6](https://github.com/jackwener/opencli/commit/ea113a6))
81+
* **twitter:** add --type flag to timeline command ([#83](https://github.com/jackwener/opencli/issues/83)) ([e98cf75](https://github.com/jackwener/opencli/commit/e98cf75))
82+
* **google:** add search, suggest, news, and trends adapters ([#184](https://github.com/jackwener/opencli/issues/184)) ([4e32599](https://github.com/jackwener/opencli/commit/4e32599))
83+
* add douban, sinablog, substack adapters; upgrade medium to TS ([#185](https://github.com/jackwener/opencli/issues/185)) ([bdf5967](https://github.com/jackwener/opencli/commit/bdf5967))
84+
* **xueqiu:** add earnings-date command ([#211](https://github.com/jackwener/opencli/issues/211)) ([fae1dce](https://github.com/jackwener/opencli/commit/fae1dce))
85+
* **browser:** advanced DOM snapshot engine with 13-layer pruning pipeline ([#210](https://github.com/jackwener/opencli/issues/210)) ([d831b04](https://github.com/jackwener/opencli/commit/d831b04))
86+
* **instagram,facebook:** add write actions and extended commands ([#201](https://github.com/jackwener/opencli/issues/201)) ([eb0ccaf](https://github.com/jackwener/opencli/commit/eb0ccaf))
87+
* **grok:** add opt-in --web flow for grok ask ([#193](https://github.com/jackwener/opencli/issues/193)) ([fcff2e4](https://github.com/jackwener/opencli/commit/fcff2e4))
88+
* **tiktok:** add TikTok adapter with 15 commands ([#202](https://github.com/jackwener/opencli/issues/202)) ([4391ccf](https://github.com/jackwener/opencli/commit/4391ccf))
89+
* add Lobste.rs, Instagram, and Facebook adapters ([#199](https://github.com/jackwener/opencli/issues/199)) ([ce484c2](https://github.com/jackwener/opencli/commit/ce484c2))
90+
* **medium:** add medium adapter ([#190](https://github.com/jackwener/opencli/issues/190)) ([06c902a](https://github.com/jackwener/opencli/commit/06c902a))
91+
* plugin system (Stage 0-2) ([1d39295](https://github.com/jackwener/opencli/commit/1d39295))
92+
* make primary args positional across all CLIs ([#242](https://github.com/jackwener/opencli/issues/242)) ([9696db9](https://github.com/jackwener/opencli/commit/9696db9))
93+
* **xueqiu:** make primary args positional ([#213](https://github.com/jackwener/opencli/issues/213)) ([fb2a145](https://github.com/jackwener/opencli/commit/fb2a145))
94+
95+
96+
### Refactoring
97+
98+
* replace hardcoded skipPreNav with declarative navigateBefore field ([#208](https://github.com/jackwener/opencli/issues/208)) ([a228758](https://github.com/jackwener/opencli/commit/a228758))
99+
* **boss:** extract common.ts utilities, fix missing login detection ([#200](https://github.com/jackwener/opencli/issues/200)) ([ae30763](https://github.com/jackwener/opencli/commit/ae30763))
100+
* type discovery core ([#219](https://github.com/jackwener/opencli/issues/219)) ([bd274ce](https://github.com/jackwener/opencli/commit/bd274ce))
101+
* type browser core ([#218](https://github.com/jackwener/opencli/issues/218)) ([28c393e](https://github.com/jackwener/opencli/commit/28c393e))
102+
* type pipeline core ([#217](https://github.com/jackwener/opencli/issues/217)) ([8a4ea41](https://github.com/jackwener/opencli/commit/8a4ea41))
103+
* reduce core any usage ([#216](https://github.com/jackwener/opencli/issues/216)) ([45cee57](https://github.com/jackwener/opencli/commit/45cee57))
104+
* fail fast on invalid pipeline steps ([#237](https://github.com/jackwener/opencli/issues/237)) ([c76f86c](https://github.com/jackwener/opencli/commit/c76f86c))
105+
3106
## [1.1.0](https://github.com/jackwener/opencli/compare/v1.0.6...v1.1.0) (2026-03-20)
4107

5108

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ See **[TESTING.md](./TESTING.md)** for how to run and write tests.
328328

329329
[![Star History Chart](https://api.star-history.com/svg?repos=jackwener/opencli&type=Date)](https://star-history.com/#jackwener/opencli&Date)
330330

331-
After publishing the new version, remember to update the browser extension in the Chrome Web Store as well, so the extension release stays in sync with the CLI release.
331+
332332

333333
## License
334334

README.zh-CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ opencli cascade https://api.example.com/data
326326

327327
[![Star History Chart](https://api.star-history.com/svg?repos=jackwener/opencli&type=Date)](https://star-history.com/#jackwener/opencli&Date)
328328

329-
发版完成后,记得也要去 Chrome Web Store 更新浏览器插件,保持插件版本和 CLI 版本同步。
329+
330330

331331
## License
332332

SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
name: opencli
33
description: "OpenCLI — Make any website or Electron App your CLI. Zero risk, AI-powered, reuse Chrome login. 150+ commands across 30+ sites."
4-
version: 1.1.0
4+
version: 1.3.1
55
author: jackwener
66
tags: [cli, browser, web, chrome-extension, cdp, bilibili, zhihu, twitter, github, v2ex, hackernews, reddit, xiaohongshu, xueqiu, youtube, boss, coupang, yollomi, AI, agent]
77
---
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Doubao App (豆包桌面版)
2+
3+
Control the **Doubao AI Desktop App** via Chrome DevTools Protocol (CDP).
4+
5+
## Prerequisites
6+
7+
1. Launch Doubao Desktop with remote debugging enabled:
8+
```bash
9+
/Applications/Doubao.app/Contents/MacOS/Doubao --remote-debugging-port=9225
10+
```
11+
2. Set the CDP endpoint:
12+
```bash
13+
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9225"
14+
```
15+
16+
## Commands
17+
18+
| Command | Description |
19+
|---------|-------------|
20+
| `opencli doubao-app status` | Check CDP connection status |
21+
| `opencli doubao-app new` | Start a new conversation |
22+
| `opencli doubao-app send "message"` | Send a message to the current chat |
23+
| `opencli doubao-app read` | Read the latest assistant reply |
24+
| `opencli doubao-app ask "message"` | Send a prompt and wait for the reply |
25+
| `opencli doubao-app screenshot` | Capture a screenshot of the app window |
26+
| `opencli doubao-app dump` | Export DOM and snapshot debug info |
27+
28+
## How It Works
29+
30+
Connects to the Doubao Electron app via CDP, injecting JavaScript into the renderer process to control the chat UI — sending messages, reading replies, and capturing screenshots.
31+
32+
## Limitations
33+
34+
- Requires Doubao Desktop to be launched with `--remote-debugging-port`
35+
- macOS / Linux / Windows (Electron-based, platform independent)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
"url": "git+https://github.com/jackwener/opencli.git"
5050
},
5151
"dependencies": {
52-
"@types/turndown": "^5.0.6",
5352
"chalk": "^5.3.0",
5453
"cli-table3": "^0.6.5",
5554
"commander": "^14.0.3",
@@ -60,6 +59,7 @@
6059
"devDependencies": {
6160
"@types/js-yaml": "^4.0.9",
6261
"@types/node": "^22.13.10",
62+
"@types/turndown": "^5.0.6",
6363
"@types/ws": "^8.5.13",
6464
"tsx": "^4.19.3",
6565
"typescript": "^5.8.2",

src/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
196196
.action(async (url, opts) => {
197197
const { recordSession, renderRecordSummary } = await import('./record.js');
198198
const result = await recordSession({
199-
BrowserFactory: getBrowserFactory() as any,
199+
BrowserFactory: getBrowserFactory(),
200200
url,
201201
site: opts.site,
202202
outDir: opts.out,

src/daemon.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,14 @@ async function handleRequest(req: IncomingMessage, res: ServerResponse): Promise
150150
return;
151151
}
152152

153+
const timeoutMs = typeof body.timeout === 'number' && body.timeout > 0
154+
? body.timeout * 1000
155+
: 120000;
153156
const result = await new Promise<unknown>((resolve, reject) => {
154157
const timer = setTimeout(() => {
155158
pending.delete(body.id);
156-
reject(new Error('Command timeout (120s)'));
157-
}, 120000);
159+
reject(new Error(`Command timeout (${timeoutMs / 1000}s)`));
160+
}, timeoutMs);
158161
pending.set(body.id, { resolve, reject, timer });
159162
extensionWs!.send(JSON.stringify(body));
160163
});

src/discovery.ts

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as path from 'node:path';
1414
import { pathToFileURL } from 'node:url';
1515
import yaml from 'js-yaml';
1616
import { type CliCommand, type InternalCliCommand, type Arg, Strategy, registerCommand } from './registry.js';
17+
import { getErrorMessage } from './errors.js';
1718
import { log } from './logger.js';
1819
import { getErrorMessage } from './errors.js';
1920
import type { ManifestEntry } from './build-manifest.js';
@@ -141,29 +142,32 @@ async function discoverClisFromFs(dir: string): Promise<void> {
141142
const promises: Promise<unknown>[] = [];
142143
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
143144

144-
for (const entry of entries) {
145-
if (!entry.isDirectory()) continue;
146-
const site = entry.name;
147-
const siteDir = path.join(dir, site);
148-
const files = await fs.promises.readdir(siteDir);
149-
for (const file of files) {
150-
const filePath = path.join(siteDir, file);
151-
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
152-
promises.push(registerYamlCli(filePath, site));
153-
} else if (
154-
(file.endsWith('.js') && !file.endsWith('.d.js')) ||
155-
(file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts'))
156-
) {
157-
if (!(await isCliModule(filePath))) continue;
158-
promises.push(
159-
import(pathToFileURL(filePath).href).catch((err) => {
160-
log.warn(`Failed to load module ${filePath}: ${getErrorMessage(err)}`);
161-
})
162-
);
145+
const sitePromises = entries
146+
.filter(entry => entry.isDirectory())
147+
.map(async (entry) => {
148+
const site = entry.name;
149+
const siteDir = path.join(dir, site);
150+
const files = await fs.promises.readdir(siteDir);
151+
const filePromises: Promise<unknown>[] = [];
152+
for (const file of files) {
153+
const filePath = path.join(siteDir, file);
154+
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
155+
filePromises.push(registerYamlCli(filePath, site));
156+
} else if (
157+
(file.endsWith('.js') && !file.endsWith('.d.js')) ||
158+
(file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts'))
159+
) {
160+
if (!(await isCliModule(filePath))) continue;
161+
filePromises.push(
162+
import(pathToFileURL(filePath).href).catch((err) => {
163+
log.warn(`Failed to load module ${filePath}: ${getErrorMessage(err)}`);
164+
})
165+
);
166+
}
163167
}
164-
}
165-
}
166-
await Promise.all(promises);
168+
await Promise.all(filePromises);
169+
});
170+
await Promise.all(sitePromises);
167171
}
168172

169173
async function registerYamlCli(filePath: string, defaultSite: string): Promise<void> {

src/explore.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { detectFramework } from './scripts/framework.js';
1515
import { discoverStores } from './scripts/store.js';
1616
import { interactFuzz } from './scripts/interact.js';
1717
import type { IPage } from './types.js';
18+
import { log } from './logger.js';
1819

1920
// ── Site name detection ────────────────────────────────────────────────────
2021

@@ -224,7 +225,8 @@ function flattenFields(obj: unknown, prefix: string, maxDepth: number): string[]
224225
}
225226

226227
function isBooleanRecord(value: unknown): value is Record<string, boolean> {
227-
return typeof value === 'object' && value !== null && !Array.isArray(value);
228+
return typeof value === 'object' && value !== null && !Array.isArray(value)
229+
&& Object.values(value as Record<string, unknown>).every(v => typeof v === 'boolean');
228230
}
229231

230232
function scoreEndpoint(ep: { contentType: string; responseAnalysis: AnalyzedEndpoint['responseAnalysis']; pattern: string; status: number | null; hasSearchParam: boolean; hasPaginationParam: boolean; hasLimitParam: boolean }): number {
@@ -453,7 +455,7 @@ export async function exploreUrl(
453455
const clicks = await page.evaluate(INTERACT_FUZZ_JS);
454456
await page.wait(2); // wait for XHRs to settle
455457
} catch (e) {
456-
// fuzzing is best-effort, don't fail the whole explore
458+
log.debug(`Interactive fuzzing skipped: ${e instanceof Error ? e.message : String(e)}`);
457459
}
458460
}
459461

0 commit comments

Comments
 (0)