Commit Graph

380 Commits

Author SHA1 Message Date
jakevin 14672ddf9b refactor: simplify codebase by removing dead code, deduplicating types, and extracting shared desktop adapter commands (#360)
- Remove dead code: unused `promises` array in discovery.ts, unused `DEFAULT_BROWSER_SMOKE_TIMEOUT`, unused `checkFfmpeg()`, deprecated `PlaywrightMCP` alias
- Extract shared `YamlArgDefinition`/`YamlCliDefinition` into `yaml-schema.ts` (was duplicated in discovery.ts and build-manifest.ts)
- Unify `BrowserCookie` type: remove duplicate from download/index.ts, re-export from types.ts
- Create `_shared/desktop-commands.ts` with factory functions (makeScreenshotCommand, makeStatusCommand, makeNewCommand, makeDumpCommand), simplifying 11 adapter files from ~20-30 lines each to 3 lines
- Fix unnecessary dynamic imports in utils.ts

Net: +30 / -361 lines
2026-03-24 20:20:32 +08:00
jakevin 77814553cf Revert "feat(browser): human-like delay system for anti-detection (#297)" (#359)
This reverts commit 376c63c7db.
2026-03-24 19:49:53 +08:00
jakevin 9018713749 fix: add fallback guards to dictionary search for better error handling (#358) 2026-03-24 19:43:26 +08:00
VK 3d39574501 feat(dictionary): add dictionary search, synonyms, and examples adapters (#241)
* feat(dictionary): add dictionary search, synonyms, and examples adapters

* feat: Improve dictionary commands with positional word arguments, URL encoding, enhanced phonetic parsing, and new E2E tests.
2026-03-24 19:26:00 +08:00
工具人研究所 376c63c7db feat(browser): human-like delay system for anti-detection (#297)
* feat(browser): human-like delay system for anti-detection

Adds a framework-level delay/jitter system using log-normal distribution
to simulate natural browsing patterns, addressing issue #59 (P0).

- New `HumanDelay` class with configurable profiles (none/fast/moderate/cautious/stealth)
- Log-normal distribution for realistic delay variance (not uniform)
- Periodic "breaks" that simulate reading/thinking pauses
- Auto-injected between page.goto() navigations
- Configurable via OPENCLI_DELAY_PROFILE env var
- Boss search adapter migrated from hardcoded jitter to framework delay
- 10 unit tests covering all profiles and edge cases

Real-world validation against a major job board (cookie-authenticated,
aggressive bot detection):

| Scenario              | Without jitter     | With jitter        |
|-----------------------|--------------------|--------------------|
| 50 detail pages       | ✅ OK              | ✅ OK              |
| 200 detail pages      | ❌ Banned (code 32) | ✅ OK              |
| 850 requests over 5h  | N/A (banned early) | ✅ Zero detection   |
| 4-day sustained crawl | N/A                | ✅ 1800+ records    |

Closes #59

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: disable human delay in CI environment to prevent E2E timeouts

In CI environments (CI=true), resolveProfile() now defaults to the
'none' profile instead of 'moderate'. This prevents the 1-8s per-
navigation delay from causing E2E test timeouts (30s limit).

Users can override this by setting OPENCLI_DELAY_PROFILE explicitly.

---------

Co-authored-by: toolmanlab <toolmanlab@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 19:22:13 +08:00
sline ccfc0ed0de fix: remove stale SQLite file, add chatgpt platform guard, fix daemon exit code (#308)
- Add *.db to .gitignore to prevent accidental database commits
- Add macOS platform check before osascript calls in chatgpt commands
- Change daemon EADDRINUSE exit code from 0 to 1
2026-03-24 19:20:42 +08:00
AstroHan 3a7a5e135b fix(grok): preserve conversation across repeated ask calls (#332)
* fix(grok): preserve conversation across repeated ask calls (#330)

The adapter unconditionally navigated to grok.com/ on every invocation,
destroying the existing conversation URL even when --new was not passed.
Since the browser daemon already reuses the same Chrome tab, skipping
navigation lets the tab stay on the current chat thread.

- Only navigate to grok.com/ when --new is true or tab is not on grok.com
- Add tryStartFreshChat to the default path's --new branch (was dead code)
- Add isOnGrok helper with hostname-based domain matching
- Add unit tests for isOnGrok

* test(grok): add adapter to vitest project config

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 19:19:40 +08:00
jakevin 618dae9148 fix(stealth): harden anti-detection against advanced fingerprinting (#357)
- navigator.webdriver returns false instead of undefined (matches real Chrome)
- Stealth guard uses non-enumerable prototype property instead of discoverable window prop
- Interceptor globals are non-enumerable via Object.defineProperty
- Monkey-patched fetch/XHR disguised with native toString() signatures
- XHR instance properties use non-enumerable descriptors
- Remove overly broad chrome-extension:// stack filter
- Remove __opencli from stack patterns to avoid self-exposure
- Update download User-Agent from Chrome/120 to Chrome/134
- Clean up dead STEALTH_GUARD export

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:19:27 +08:00
AstroHan 11df7181b3 fix(twitter): retry transient search spa navigation (#355)
* fix(twitter): retry transient search spa navigation

* test(twitter): cover search navigation failure path
2026-03-24 19:18:23 +08:00
MatrixA 2b24f517fe fix(arxiv): use correct query arg instead of keyword in search (#356)
The search command defined its argument as `query` but referenced
`args.keyword`, causing the search term to be undefined.

Closes #334

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:16:16 +08:00
zhanghui67 9f2aa3b711 fix(bilibili): use actual user UID instead of 0 for favorite command (#333)
The favorite command was using up_mid: 0 which returns empty results. Now it correctly fetches the current user UID using getSelfUid().

Co-authored-by: 章晖 <zhanghui@MacBook-Pro.local>
2026-03-24 16:23:41 +08:00
Piotr Yordanov 8869d3b457 feat(linkedin): add timeline feed command (#342)
* feat(linkedin): add timeline feed command

* test(linkedin): add timeline adapter unit tests

Add shape tests and utility function tests for the new timeline command.
Include linkedin in the vitest adapter project config.

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 15:54:51 +08:00
jakevin 7c9caa81f8 refactor: extract shared utilities and simplify codebase (#348)
- R1: Extract isRecord() type guard to shared src/utils.ts (8 files)
- R2: Merge duplicate mapConcurrent() to shared utils (fetch.ts + download.ts)
- R3: Extract saveBase64ToFile() helper (cdp.ts + page.ts)
- R4: Merge _tabOpt() + _workspaceOpt() into _cmdOpts()/_wsOpt() (page.ts)
- R5: Extract normalizeRows() + resolveColumns() in output.ts
- R6: Remove dead register stub from generate.ts
2026-03-24 15:47:21 +08:00
Xeron 7c808fd339 feat(jd): add JD.com product details adapter (#344)
* feat(jd): add item adapter for JD.com product details

Support fetching:
- Product title, price, shop
- Product specifications (品牌, 型号, 规格参数 etc.)
- Main product images
- Detail images from product page

Usage: opencli jd item <sku>

* fix: use images arg instead of hardcoded limit, add command shape test

- Wire the `images` arg to control mainImages/detailImages slice count
  (was hardcoded to 10, ignoring the arg entirely)
- Add item.test.ts verifying command registration shape

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 15:37:22 +08:00
jakevin fb562fa1e9 fix: allow browser:false commands to run without page after lazy-load (#347)
The C2 fix in PR #337 added a null-page guard after lazy-loading TS
modules, but it threw unconditionally — breaking all browser:false
commands (bloomberg, apple-podcasts, google, yollomi, etc.) that
use func() with a null page. Guard now checks updated.browser !== false.

Also fixes apple-podcasts top E2E flake: when the command times out on
CI, stderr is empty and the guard didn't catch it.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 15:08:08 +08:00
haijun huang f6466db39a feat: add generic web read command for any URL → Markdown (#343)
* feat: add generic `web read` command for any URL → Markdown

Adds a new `opencli web read --url <any-url>` command that fetches any
web page and exports it as clean Markdown with optional image download.

Uses browser-side DOM heuristics for content extraction:
  1. <article> element
  2. [role="main"] element
  3. <main> element
  4. Largest text-dense block fallback

Pipes through the existing article-download pipeline (Turndown + image
localization), so it inherits code block handling, frontmatter generation,
and concurrent image downloading for free.

Tested on: Anthropic blog, OpenAI blog, general news sites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: improve web read dedup for sites with duplicated DOM paragraphs

Anthropic's blog renders each paragraph twice (a normal version + a
line-broken animation version). The previous substring-based dedup
missed these because whitespace differences changed string lengths.

Fix: compare texts after stripping ALL whitespace, and keep the
version with more proper spacing (more spaces = better formatted).

Result on Anthropic blog: 98.4KB → 53.7KB (45% reduction).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Harrison <harrison@HarrisondeMacBook-Pro.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:57:34 +08:00
jakevin d58e1a74cd fix: resolve 11 important bugs from deep code review (#340)
- I1: Log pre-navigation failures in debug mode instead of silently swallowing
- I2: Validate env var timeout values, fallback on NaN/negative
- I4: Guard against indexOf returning -1 for unknown strategies in cascade
- I5: Fix shouldReplaceManifestEntry returning true for same-type entries
- I6: Prevent infinite loop in parseTsArgsBlock cursor advancement
- I7: Skip redundant Page.enable calls in CDP goto
- I8: Fix wait({time:0}) being treated as falsy
- I10: Warn when cookiesFile path doesn't exist before fallback
- I11: Sanitize tab/newline chars in cookie name/value for Netscape format
- I12: Use DEFAULT_DAEMON_PORT constant instead of hardcoded port in error
- I15: Log npm install failures in plugin lifecycle instead of swallowing

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 12:10:13 +08:00
jakevin 1b3f74cd6a test: focus adapter coverage on four priority sites (#339) 2026-03-24 12:03:26 +08:00
jakevin bdcffd147f fix: resolve 6 critical bugs from deep code review (#337)
1. execution.ts: Guard lazy-loaded func commands against null page — if a
   lazy module incorrectly requires browser context, throw a clear error
   instead of a cryptic TypeError on page.goto().

2. daemon.ts: Fix readBody race condition — add aborted flag to prevent
   req.destroy() from triggering both reject (via error) and resolve
   (via end event) on the same Promise, which could process truncated data.

3. browser/cdp.ts: Prevent CDPBridge.connect() reentry — throw if already
   connected instead of silently leaking the previous WebSocket and its
   message handlers.

4. interceptor.ts: Store intercept pattern in a separate global variable
   so subsequent installInterceptor calls with different patterns update
   the match condition without being blocked by the patchGuard.

5. record.ts: Always call cleanupEnter() after Promise.race — previously
   only called in the timeout path, leaving readline open when user pressed
   Enter, potentially blocking process exit. Also removed unused enterRace.

6. generate.ts: Fix undefined entering String.includes() — when c.name is
   undefined, toLowerCase() returns undefined which gets coerced to the
   string "undefined" by includes(), causing false positive matches.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 11:37:03 +08:00
jakevin 53699eb807 fix: harden security-sensitive execution paths (#335)
* fix(security): harden against command injection and sandbox escape

1. cli.ts: Remove auto-discover of arbitrary system binaries via denylist.
   Unknown commands now require explicit registration via `opencli register`.
   The previous denylist approach was trivially bypassable (bash, curl, etc.).

2. template.ts: Protect evalJsExpr against prototype chain escape.
   Block expressions containing constructor/prototype/__proto__/process/etc.
   Deep-copy context objects to sever prototype chains before passing to
   new Function().

3. external.ts: Expand shell operator detection in parseCommand to cover
   $(), $, #, \n, \r — preventing command substitution and comment injection.

4. fetch.ts: Use JSON.stringify for HTTP method in browser evaluate() instead
   of raw string interpolation, preventing JS injection via crafted method values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden security-sensitive execution paths

* chore: tighten template sandbox guard

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 11:28:22 +08:00
jakevin f852da3af4 fix(stealth): review fixes — guard plugins, rewrite stack trace cleanup (#320)
- Only override navigator.plugins when empty (don't replace real user
  browser plugins with fakes)
- Replace Error.prepareStackTrace (V8/Node-only) with
  Error.prototype.stack getter override that works in browser context
- Fix \\n escaping in template literal for stack trace split/join
- Dynamic cdc_ variable scan via getOwnPropertyNames instead of
  hardcoded names
- Update tests to cover 7 patches
2026-03-24 01:41:22 +08:00
jakevin 15d9ef814f feat(browser): add stealth anti-detection for CDP and daemon modes (#319)
Add stealth.ts module that patches browser globals to hide automation
fingerprints when opencli controls a browser via CDP or daemon extension.

Patches applied:
- navigator.webdriver → undefined (CDP sets it to true)
- window.chrome stub (only if missing)
- navigator.plugins fake list (only if empty)
- navigator.languages guarantee (only if empty)
- Permissions.query normalization for notifications
- Cleanup __playwright/__puppeteer/cdc_* artifacts

CDP mode: stealth registered via Page.addScriptToEvaluateOnNewDocument
(runs before any page JS on every navigation).

Daemon mode: stealth injected via exec after navigation, with guard
flag to prevent double-injection.
2026-03-24 01:32:06 +08:00
calm b6a02f82ed refactor: extract getErrorMessage and DEFAULT_DAEMON_PORT to shared modules (#313)
- Add getErrorMessage() to errors.ts (used in 5 files)
- Add DEFAULT_DAEMON_PORT to constants.ts (used in 5 files)
- Reduces code duplication and improves maintainability
2026-03-24 00:56:15 +08:00
jakevin 75f42371ca fix: remove duplicate getErrorMessage import in discovery.ts (#315)
Squash merge left a duplicate import line causing TS2300 and oxc parse
errors in CI. Also clean up stale blank lines in discovery.ts and
execution.ts.
2026-03-24 00:42:29 +08:00
sline 41aedf68cd fix(external): replace execSync with execFileSync to prevent command injection (#309)
* fix(external): replace execSync with execFileSync to prevent command injection

* fix(review): preserve Windows external installs and restore docs build

* fix(review): preserve Windows external installs after rebase

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 00:40:17 +08:00
jakevin c9b3568594 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)
2026-03-24 00:33:40 +08:00
jakevin b4d64cad6e feat: refine error handling with semantic error types (#312)
- Add 5 new CliError subclasses: AuthRequiredError, TimeoutError,
  ArgumentError, EmptyResultError, SelectorError
- Centralize getErrorMessage() and ERROR_ICONS in errors.ts
- Data-driven error rendering in commanderAdapter.ts (replaces 5 if/else)
- withTimeoutMs accepts factory function for backward compatibility
- browser/errors.ts returns BrowserConnectError instead of bare Error
- Migrate 5 benchmark adapters to AuthRequiredError
- 318 unit tests passing, 0 regressions
2026-03-24 00:20:38 +08:00
jakevin 966f6e5019 feat(plugin): add update command, hot reload after install, README section (#307)
- Add `opencli plugin update <name>` command (git pull + post-install lifecycle)
- Extract shared postInstallLifecycle() helper to deduplicate install/update code
- Hot reload: call discoverPlugins() after install/update (no restart needed)
- Add Plugins section to README.md and README.zh-CN.md
- Add updatePlugin test coverage
2026-03-23 23:21:02 +08:00
AniChikage ea8324257d feat(yollomi): add new commands and update documentation in README files (#235)
* feat(yollomi): add new commands and update documentation in README files

- Added yollomi commands for generating images, videos, and editing capabilities.
- Updated README.md and README.zh-CN.md to include yollomi in the command list.
- Enhanced SKILL.md with yollomi-related tags and usage examples.

* feat(yollomi): add yollomi adapter to documentation

- Included yollomi in the VitePress configuration for browser adapters.
- Updated adapters index documentation to reflect yollomi's capabilities and commands.

* fix(yollomi): bug fixes, tests & improvements

- models.ts: add browser: false (no browser connection needed for hardcoded data)
- edit.ts: remove unused resolveImageInput import
- upload.ts: lower video upload limit from 100MB to 20MB (base64 OOM risk)
- generate.ts: improve file extension detection using URL.pathname
- upscale.ts: use choices for scale arg, improve extension detection
- object-remover.ts: make image/mask args positional
- Add yollomi models tests to public-commands.test.ts
- Add yollomi generate/video graceful-failure tests to browser-auth.test.ts

---------

Co-authored-by: anichikage <hanzhishuai@bytedance.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 23:04:01 +08:00
Yee dff0fe510c feat(record): add live recording command for API capture (#300)
* feat(record): add live recording command for API capture

- Add `opencli record <url>` command that injects fetch/XHR interceptors
  into all tabs in the automation window, polls captured requests, and
  auto-generates YAML candidate adapters
- Support multi-tab recording: new tabs discovered during polling are
  automatically injected
- Add --timeout (default 60s) for agent-friendly non-blocking operation;
  stops on Enter, timeout, or SIGINT — whichever comes first
- Fix idempotent re-injection: restores original fetch/XHR before
  re-patching so guard flag no longer blocks subsequent record runs
- Add --poll interval option (default 2000ms)
- Expand SKILL.md with full Record Workflow section: interceptor
  internals, page-type capture expectations, YAML→TS conversion guide,
  and troubleshooting table

* fix(record): fix XHR listener leak, pathChain syntax error, readline hang & args interpolation

- XHR send(): add __rec_listener_added guard to prevent duplicate event
  listeners when XHR is reused (abort → open → send)
- pathChain: when findArrayPath returns '' (root-level array), data access
  is just 'data' not 'data?.' which was invalid JS syntax
- waitForEnter(): return cleanup fn so timeout path can close readline.Interface
  preventing the process from hanging on stdin after auto-timeout
- buildRecordedYaml: replace search/page query param values with template
  vars ({{args.keyword}}, {{args.page}}) so generated YAML actually uses
  the declared args instead of hardcoding the recorded URL

---------

Co-authored-by: yee.wang <yee.wang@lazada.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 19:19:54 +08:00
caokaizz c7895eaf8e Add weibo search command (#299)
* Add weibo search command

* fix(weibo/search): correct domain to weibo.com, add browser: true, fill doc description

- Change domain from s.weibo.com to weibo.com so browser cookies are picked
  up correctly (matches hot.ts which also uses weibo.com)
- Add browser: true for consistency with other browser-based adapters
- Add description for weibo search in adapter docs table

---------

Co-authored-by: 小小机器人 <14351708+little-little-robot@user.noreply.gitee.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 17:48:16 +08:00
AstroHan a83027d19c feat(v2ex): add node, user, member, replies, nodes commands (#282)
* feat(v2ex): add node, user, member, replies, nodes commands

Add 5 new public API commands to the v2ex adapter:
- node: browse topics by node name
- user: list topics by username
- member: show user profile
- replies: list topic replies
- nodes: list all nodes sorted by topic count

All commands use strategy: public, browser: false.

* test(v2ex): add E2E tests for node, user, member, replies, nodes commands

* docs(v2ex): update adapter docs with new commands

* fix(v2ex): address review findings - rate-limit guards, sort verification, docs

* docs(v2ex): update README command tables and add user example

* test(v2ex): improve test quality - soft guards, value assertions, smoke tests

- Replace isExpectedChineseSiteRestriction with if(code===0) soft guard
  (V2EX is globally accessible; YAML fetch doesn't throw FETCH_ERROR)
- Add value assertions: member username===Livid, limit effectiveness
- Add smoke tests for node, member, replies, nodes commands

* fix(v2ex): add url field to node/user commands, add missing user smoke test

- Add url to node.yaml and user.yaml pipeline map steps and columns
  (V2EX API provides item.url; improves usability for follow-up lookups)
- Add v2ex user smoke test (other 4 new commands all had smoke tests; user was missing)
- Update E2E assertions to verify url field in node/user results

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 17:45:02 +08:00
yin1991 f8bf66390d fix(xiaohongshu): improve search login-wall handling and detail output (#298)
* fix(xiaohongshu): improve search login-wall handling and detail output

* fix(xiaohongshu/search): keep login-wall detection & URL improvements, remove serial per-note enrichment

- Detect login wall and throw a clear error message (from original PR)
- Preserve search_result/ URL with xsec_token instead of degrading to /explore/<id>
- Add author_url to results
- Remove readNoteDetail() + sequential page.goto() per note (caused 60s+ delays
  for default limit=20 with 3s wait each)
- Simplify and unify DOM extraction logic (remove unused fallback anchor scan)
- Update tests: cover login-wall, URL preservation (assert single goto), and limit/filter

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 17:34:23 +08:00
jicaiji1-max 22f5c7ade0 fix: ensure standard PATH is available for external CLIs (#285)
Some environments (GUI apps, cron, IDE terminals) launch with a minimal
PATH that excludes standard directories like /usr/local/bin and /usr/sbin.
This causes external CLIs to fail when they try to run system commands
(e.g. sysctl).

Fix by ensuring standard system paths exist in process.env.PATH at
startup. This is a one-time fix that benefits ALL child processes —
isBinaryInstalled(), installExternalCli(), daemon spawn, etc. — without
needing per-call env patching.

Fixes #284

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 15:59:57 +08:00
jakevin 8ab0cd2a50 Revert "feat: add xianyu-cli as external CLI (#292)" (#295)
This reverts commit d4b06be049.
2026-03-23 15:45:09 +08:00
donquijote2557-web d4b06be049 feat: add xianyu-cli as external CLI (#292)
Add xianyu (闲鱼/Goofish) CLI tool as an external CLI integration.

Features: search, messaging, agent-flow auto-pricing pipeline,
QR code login, WebSocket real-time messaging with auto-reconnect.

Repo: https://github.com/Donquijote-coder/xianyu-cli

Co-authored-by: donquijote2557-web <donquijote2557-web@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 15:40:39 +08:00
jakevin 3a21be624e fix(xiaohongshu): scope image selector to avoid downloading avatars (#293)
Narrow '#noteContainer img[src*="xhscdn"]' to
'#noteContainer .media-container img[src*="xhscdn"]'
to exclude user avatars and sidebar icons from downloads.

Closes #281
2026-03-23 15:29:43 +08:00
AstroHan 127a974ecd feat(hackernews): add new, best, ask, show, jobs, search, user commands (#290)
Expand HackerNews from 1 command to 8, covering all major HN use cases.
All YAML adapters, strategy: public, browser: false.

- new/best/ask/show/jobs: Firebase API list endpoints with deleted/dead filtering
- search: Algolia API with query + sort (relevance/date)
- user: Firebase user profile with date formatting
- top.yaml: add filter for deleted/dead items + dynamic pre-fetch limit
- E2E tests for all 7 new commands
- Update README, README.zh-CN, adapter docs

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 15:13:47 +08:00
jakevin 66c4b841f2 feat(doubao-app): add Doubao AI desktop app CLI adapter (#289)
- Commands: status, send, read, new, ask, screenshot, dump
- Uses Strategy.UI for desktop CDP connection
- Shared common.ts with selectors and evaluate script builders
- Requires Doubao launched with --remote-debugging-port=9226
2026-03-23 14:52:58 +08:00
helloimcx 9cdc1274b2 feat: add doubao browser adapter (#277) 2026-03-23 12:34:27 +08:00
stometaverse a6d993f37f feat(xiaohongshu): add publish command for 图文 note automation (#276)
Adds `opencli xiaohongshu publish` which automates posting a 图文 (image+text)
note via the creator center UI (creator.xiaohongshu.com/publish/publish).

Features:
- --title (required, max 20 chars)
- positional content argument
- --images comma-separated local file paths (jpg/png/gif/webp, max 9)
- --topics comma-separated hashtag names (without #)
- --draft flag to save as draft instead of publishing

Image upload uses DataTransfer injection into the file input element, converting
local files to base64 in Node.js and creating File blobs in the browser context.
Text fields use document.execCommand('insertText') for contenteditable editors.
Graceful debug screenshots on failure (/tmp/xhs_publish_*_debug.png).

Requires: opencli browser session logged into creator.xiaohongshu.com.
2026-03-23 12:26:02 +08:00
jakevin b7c6c02370 feat: add weixin article download adapter & abstract download helpers (#280)
- New: src/clis/weixin/download.ts — WeChat article to Markdown adapter
- New: src/download/article-download.ts — shared article download helper
  (TurndownService, image localization, frontmatter, customizable labels)
- New: src/download/media-download.ts — shared media download helper
  (batch download, ProgressTracker, yt-dlp routing, auto cookie export)
- Refactor: migrate zhihu/download to use downloadArticle()
- Refactor: migrate xiaohongshu/download to use downloadMedia()
- Refactor: migrate twitter/download to use downloadMedia()
- Refactor: migrate bilibili/download to use downloadMedia()
- Docs: add weixin to README, README.zh-CN, download docs, adapter docs
2026-03-23 12:11:43 +08:00
jakevin 4b976da04f perf: smart page settle via DOM stability detection (#271)
Replace fixed settleMs sleep in goto() with MutationObserver-based DOM
stability detection. The page is considered settled when no DOM mutations
occur for quietMs (default 500ms), with settleMs as a hard timeout cap.

Changes:
- Add waitForDomStableJs() shared helper to dom-helpers.ts
- Update Page.goto() and CDPPage.goto() to use smart settle
- No IPage interface changes (implementation detail only)

Key improvements over naive approach:
- Timer starts AFTER MutationObserver.observe() to avoid race condition
- Falls back to sleep(maxMs) if document.body is not available
- Monitors attributes in addition to childList/subtree
- quietMs defaults to 500ms (conservative) for async request buffering
2026-03-23 00:24:09 +08:00
jakevin 40bd11dfbe fix(daemon): harden security against browser CSRF attacks (#268) (#270)
- Add Origin header check: reject HTTP/WS from non chrome-extension:// origins
- Require X-OpenCLI custom header on all HTTP requests
- Remove Access-Control-Allow-Origin: * from all responses
- Add WebSocket verifyClient to reject malicious connections at upgrade
- Add 1MB body size limit to prevent OOM
- Update file header with security model documentation

Closes #268
2026-03-22 23:56:39 +08:00
jakevin 637161f0ab fix: update doctor tests for auto-start daemon and --no-live default (#265)
- Fix skip message assertion: 'skipped (--no-live)' instead of old text
- Fix auto-start test: mock checkDaemonStatus for both initial and final calls
2026-03-22 22:57:33 +08:00
jakevin b4a8089224 refactor: doctor defaults to live mode, remove setup command entirely (#263)
- Remove setup command completely (no backward compat needed)
- Doctor now runs live connectivity test by default
- Add --no-live flag to skip if needed
- Update SKILL.md docs
2026-03-22 22:51:28 +08:00
jakevin 428b831f85 refactor: deprecate opencli setup, enhance doctor with daemon auto-start (#262)
- Delete setup.ts (fully redundant with doctor)
- opencli setup now prints deprecation warning and delegates to doctor
- doctor auto-starts daemon if not running (no more false 'not connected')
- Update all doc references (README, SKILL.md, docs/)
2026-03-22 22:49:14 +08:00
jakevin 71b2c3961b fix: harden browser automation pipeline (resolves #249) (#251)
- resolveTabId: validate URL even for explicit tabId, fall through to
  auto-resolve when tab is not debuggable or has been closed
- handleNavigate: wait for URL change before checking 'complete' status
  to avoid race condition with stale about:blank
- ensureAttached: pre-check tab URL, verify cached attach with probe,
  invalidate cache on URL change via onUpdated listener
- daemon-client: recognize transient extension errors (disconnected,
  attach failed) as retryable with 1500ms delay; fresh command ID per attempt
- pipeline executor: add per-step retry for browser steps (up to 2 retries
  on transient errors); cleanup automation window on pipeline failure
- page.ts: selectTab/newTab/closeTab properly update/invalidate _tabId
- daemon.ts: add WebSocket ping/pong heartbeat (15s interval, 2-miss disconnect)
- Increase automation window idle timeout from 30s to 120s
- Fix timeout param edge cases in BrowserBridge._ensureDaemon
- Remove unused chalk import; fix trailing import placement

Closes #249
2026-03-22 21:23:21 +08:00
jakevin 2d1b8c1e76 chore: prepare v1.2 release (#244) 2026-03-22 18:40:32 +08:00
ykfnxx 70651d3ba8 feat(douban): add movie adapter with search, top250, subject, marks, reviews commands (#239)
* feat(douban): add movie adapter with search, top250, subject, marks, reviews commands

- search: search movies by keyword
- top250: get top 250 movies
- subject: get movie details by id
- marks: export personal viewing marks
- reviews: export personal movie reviews

* review: resolve douban adapter blockers

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-22 17:57:34 +08:00