Compare commits

...

450 Commits

Author SHA1 Message Date
pi-dal 85f81c87f2 test(e2e): accept current apple podcasts fetch errors 2026-03-28 00:13:45 +08:00
pi-dal 6b2ccbd5b8 fix(ci): stabilize plugin and public command checks 2026-03-28 00:13:45 +08:00
jakevin 6e90356649 chore(extension): bump version to 1.5.1 (#519) 2026-03-28 00:09:30 +08:00
AstroHan 0085d63fb8 fix(weread): resolve shelf auth fallback (#518)
* fix(weread): resolve shelf auth fallback

* chore(docs): move local issue notes out of pr

* fix(weread): classify session expiry as auth required

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-28 00:09:28 +08:00
jakevin 0f3021a086 fix: relax extension version check, enable all adapter tests (#520)
* feat: zero onboarding, extension version check, and update notifier

- Fail-fast guard in execution.ts: when daemon is running but extension
  is not connected, immediately surface a setup guide instead of waiting
  for the 30s connect timeout

- Extension version handshake: extension sends `hello` with its version
  on WebSocket connect; daemon stores it and exposes via /status; CLI
  warns on mismatch in both execution path and `opencli doctor`

- `opencli doctor` now shows extension version inline and reports
  version mismatch as an actionable issue

- Non-blocking npm update checker: registers a process exit hook so the
  update notice appears after command output (same pattern as npm/gh/yarn);
  background fetch writes to ~/.opencli/update-check.json for next run

- postinstall: print Browser Bridge setup instructions after shell
  completion install for first-time global install users

Bug fixes caught in review:
- discover.ts: add AbortController timeout to checkDaemonStatus() fetch,
  move clearTimeout after res.json() to cover body streaming
- daemon.ts: clear extensionVersion and reject pending requests in
  ws.on('error') handler, not just ws.on('close')
- update-check.ts: skip update notice when process exits with non-zero
  code; read cache once at module load to avoid double disk I/O;
  guard isNewer() against NaN from pre-release version strings

* fix: relax extension version check to major-only in doctor, remove from hot path

* test: enable all adapter tests via wildcard glob, fix apple-podcasts url field

* fix: clearTimeout in finally block, reset extensionVersion on reconnect, fix e2e regex
2026-03-28 00:08:10 +08:00
jakevin a1561e5361 fix(extension): minimize automation window + reduce idle timeout to 30s (#521)
- Create automation window with `state: 'minimized'` so it never
  appears in the user's taskbar or steals visual attention
- Reduce idle timeout from 120s to 30s — window closes quickly after
  the last command finishes, instead of lingering for 2 minutes
- CDP debugger works fine on minimized windows, no functional impact

Fixes the user-visible issue of a blank data:text/html tab appearing
during command execution.
2026-03-28 00:03:44 +08:00
jakevin f9f11e4b17 chore(release): 1.5.1 (#513)
Release / release (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
2026-03-27 19:25:37 +08:00
jakevin 5bd0497244 refactor(plugin): make plugin installs transactional (#509)
* feat(plugin): stage installs before promote

* feat(plugin): make remote updates transactional

* fix(plugin): rollback relink failures

* refactor(plugin): unify transactional publish flow

* refactor(plugin): extract publish pipeline helpers

* refactor(plugin): add structured source model

* refactor(plugin): promote lockfile to structured sources

* fix(plugin): preserve lock reads when migration rewrite fails

* fix(plugin): write lockfiles atomically

* test(plugin): make source helper assertions cross-platform
2026-03-27 18:25:56 +08:00
Guyue a2d1199b50 fix(v2ex): fetch hot topics through browser context (#493)
* Update hot.yaml

* review: fetch v2ex hot data within browser context

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-27 17:50:44 +08:00
jakevin cf99c61df5 perf: smart pre-navigation — skip redundant nav + remove 2s wait (#507)
* perf: smart pre-navigation — skip redundant domain nav + remove hardcoded 2s wait

- Add `getCurrentUrl()` to IPage, Page, and CDPPage to check current browser URL
- Skip pre-navigation entirely if the browser is already on the target domain
- Remove the hardcoded `page.wait(2)` after pre-navigation — `page.goto()` already
  includes smart DOM-settle detection via `waitForDomStable`, making the fixed
  2-second sleep redundant
- Saves ~2s per browser command in the common case (consecutive commands on the
  same site), and ~1-2s even on cold navigation

* perf: smart page.wait() — DOM-stable early return for waits >= 1s

For page.wait(N) where N >= 1 second, use DOM MutationObserver-based
stability detection instead of a fixed sleep. The original wait time
becomes a hard cap, but the call returns as soon as the DOM stops
mutating (500ms quiet period).

This benefits ~200 hardcoded sleep calls across ~40 adapters without
changing any adapter code. A typical page.wait(5) now completes in
<1s when the page is already stable, instead of always waiting 5s.

Short waits (< 1s) are kept as fixed sleeps — these are typically
UI animation delays or anti-bot throttling where DOM-ready is irrelevant.

* refactor: getCurrentUrl() uses in-memory tracking instead of round-trip

Replace the sendCommand('exec', 'window.location.href') call with a
local _lastUrl field set during goto(). This eliminates a daemon HTTP
round-trip for the domain check, making isAlreadyOnDomain() zero-cost.

On fresh tabs (about:blank), _lastUrl is null so we correctly fall
through to navigation — no special-casing needed.
2026-03-27 17:42:48 +08:00
AlexYue 70ad5700c9 feat(plugin): support multi-source plugin install (ssh, git@, generic https) (#504)
Extends parseSource() to accept any git-cloneable URL, not just GitHub:
- ssh://git@host/path/repo.git
- git@host:user/repo.git (SCP-style)
- https://any-host.com/path/repo.git

GitHub shorthand (github:user/repo) and local paths continue to work.
Updated error messages, CLI description, docs, and added 7 new unit tests.

Closes #492
2026-03-27 17:04:57 +08:00
AlexYue 2ad1215ac2 fix(plugin): prevent raw .ts import crash when esbuild transpilation fails (#500) (#503)
When a TS plugin is installed but esbuild is unavailable or transpilation
fails silently, the plugin discovery would attempt to import() the raw
.ts file, causing 'Unknown file extension .ts' on production Node.js.

Changes:
- discovery.ts: Skip raw .ts import when no compiled .js exists; show
  an actionable warning guiding the user to re-transpile or install esbuild
- plugin.ts: Upgrade esbuild-not-found from debug to warn level; log
  the outer catch error instead of silently swallowing it

Closes #500
2026-03-27 16:57:59 +08:00
AstroHan ee59750ddb fix(execution): apply timeout to non-browser commands (#383)
Non-browser commands (`browser: false`) ran without any timeout
protection, even when `timeoutSeconds` was explicitly set. This wraps
the non-browser execution path with `runWithTimeout()` when the
adapter defines a positive `timeoutSeconds`.

Also adds an optional `hint` parameter to `TimeoutError` so the
non-browser path shows a relevant suggestion instead of the
browser-specific `OPENCLI_BROWSER_COMMAND_TIMEOUT` env var hint.
2026-03-27 14:54:28 +08:00
sline 9a9e078462 feat(bluesky): add Bluesky adapter with 9 commands (#215)
Bluesky (9 commands, public AT Protocol API, no auth needed):
- profile: user profile info (followers, following, posts)
- user: recent posts from a user with engagement stats
- trending: trending topics on Bluesky
- search: search users
- feeds: popular feed generators
- followers: list user's followers
- following: list accounts a user follows
- thread: post thread with replies
- starter-packs: user's starter packs

All commands use the public Bluesky API, no browser or login required.
2026-03-27 14:39:43 +08:00
AlexYue 55d0473bcf fix(plugin): handle EXDEV cross-filesystem rename during install (#488)
* fix(plugin): handle EXDEV cross-filesystem rename during install

fs.renameSync() fails with EXDEV when source and destination are on
different filesystem mount points. This commonly happens because plugin
clones land in os.tmpdir() (often /tmp on a tmpfs) while plugins are
installed to ~/.opencli/plugins/ (on the root filesystem).

Add a moveDir() helper that catches EXDEV and falls back to
fs.cpSync() + fs.rmSync(). Applied to both single-plugin and monorepo
install paths.

* review: clean up failed EXDEV fallback installs

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-27 14:22:03 +08:00
AstroHan 1a44d8ccff feat(xiaohongshu): add published_at to search results (#484) (#485)
Derive approximate publish date from note IDs, which follow MongoDB
ObjectID format (first 8 hex chars = Unix timestamp). Exported as a
pure function with UTC+8 offset for China timezone.

Closes #484
2026-03-27 14:20:22 +08:00
jakevin 31cb2291c5 perf: parallel file discovery, plugin scanning, and external CLI caching (#501)
- Parallelize file scanning in discoverClisFromFs and discoverPluginDir
  using Promise.all(files.map(async ...)) instead of serial for-of with
  await, so isCliModule checks run concurrently
- Parallelize plugin directory scanning in discoverPlugins
- Cache loadExternalClis() result to avoid re-parsing YAML on every call
- Invalidate cache in registerExternalCli after writing to disk
- Cache strategyLabel() call in list command to avoid redundant computation
- Add comment explaining why discovery must remain sequential (plugin override semantics)
2026-03-27 14:19:55 +08:00
AstroHan fb5b608607 fix(twitter): use DOM-only scraping for trending to match page results (#486)
Remove guide.json API path that returned data inconsistent with what
users see on the page (#463). Use semantic caret button detection
via data-testid instead of position-based heuristics, and validate
post count text contains digits before displaying.
2026-03-27 14:15:36 +08:00
AlexYue 5e2e1dfe60 fix(plugin): detect symlinked monorepo sub-plugins in discoverPlugins (#487)
* fix(plugin): detect symlinked monorepo sub-plugins in discoverPlugins

discoverPlugins() used entry.isDirectory() to filter plugin directories,
but monorepo sub-plugins are installed as symlinks pointing into
~/.opencli/monorepos/. On most Node.js versions, isDirectory() returns
false for symlinks, causing monorepo plugin commands to be silently
skipped during discovery.

Add entry.isSymbolicLink() check so symlinked plugin directories are
properly discovered and their commands registered.

* fix(plugin): skip broken symlink discovery

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-27 14:12:54 +08:00
AstroHan 39eec0da82 fix(xiaohongshu): adapt publish to new two-step creator center UI (#490)
* fix(xiaohongshu): adapt publish to new two-step creator center UI (#460)

The creator center now requires image upload before showing the
title/content editor form. This caused the publish command to fail
with "Could not find title input".

- Add waitForEditForm() to poll for editor after image upload
- Extract TITLE_SELECTORS constant shared by waitForEditForm and fillField
- Add contenteditable title selectors for new UI
- Make images required (new UI mandates images before editor)
- Update draft button to match both '暂存离开' and '存草稿'
- Exclude title placeholder from content fallback selector
- Update tests to match new flow

* refactor(xiaohongshu): clarify publish surface states

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-27 14:03:10 +08:00
AlexYue 384419f4f5 feat(plugin): support local path install via file:// and absolute path (#491)
Add support for installing plugins from local directories:
  opencli plugin install file:///path/to/my-plugin
  opencli plugin install /path/to/my-plugin

Local plugins are symlinked (not copied) into ~/.opencli/plugins/
so code changes are reflected immediately without reinstall — ideal
for plugin development workflows.

Changes:
- parseSource() now handles file:// URLs and bare absolute paths
- New installLocalPlugin() creates symlink + installs deps + transpiles
- Lock file records 'local:<path>' as source for local plugins
- 6 new test cases for local path parsing and install behavior
2026-03-27 13:45:42 +08:00
AlexYue fa4c44a0c4 feat(plugin): add 'plugin create <name>' scaffold command (#494)
* feat(plugin): add 'plugin create <name>' scaffold command

Generate a ready-to-develop plugin directory with all required files:
- opencli-plugin.json (manifest with name, version, compatibility)
- package.json (ESM, peer dependency on @jackwener/opencli)
- hello.yaml (sample YAML command using httpbin)
- greet.ts (sample TS command using cli() API)
- README.md (install, usage, and development instructions)

Usage:
  opencli plugin create my-plugin
  opencli plugin create my-plugin --dir /path/to/dir
  opencli plugin create my-plugin --description 'My awesome plugin'

Includes 5 test cases for scaffold generation and error handling.

* fix(plugin): align scaffold with local install flow

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-27 13:34:36 +08:00
AlexYue d74263523f fix(plugin): remove legacy LOCK_FILE/MONOREPOS_DIR constants (#495)
Remove the module-level LOCK_FILE and MONOREPOS_DIR constants that were
computed at load time using os.homedir(). These ignored the HOME
environment variable, causing path mismatches when tests use HOME for
isolation.

All usages now go through getLockFilePath() and getMonoreposDir() which
respect process.env.HOME. Updated plugin.test.ts accordingly.
2026-03-27 13:27:47 +08:00
jakevin f9f018d7f4 fix(doctor): remove unused fix option and add release URL to extension install hint (#498)
* feat: zero onboarding, extension version check, and update notifier

- Fail-fast guard in execution.ts: when daemon is running but extension
  is not connected, immediately surface a setup guide instead of waiting
  for the 30s connect timeout

- Extension version handshake: extension sends `hello` with its version
  on WebSocket connect; daemon stores it and exposes via /status; CLI
  warns on mismatch in both execution path and `opencli doctor`

- `opencli doctor` now shows extension version inline and reports
  version mismatch as an actionable issue

- Non-blocking npm update checker: registers a process exit hook so the
  update notice appears after command output (same pattern as npm/gh/yarn);
  background fetch writes to ~/.opencli/update-check.json for next run

- postinstall: print Browser Bridge setup instructions after shell
  completion install for first-time global install users

Bug fixes caught in review:
- discover.ts: add AbortController timeout to checkDaemonStatus() fetch,
  move clearTimeout after res.json() to cover body streaming
- daemon.ts: clear extensionVersion and reject pending requests in
  ws.on('error') handler, not just ws.on('close')
- update-check.ts: skip update notice when process exits with non-zero
  code; read cache once at module load to avoid double disk I/O;
  guard isNewer() against NaN from pre-release version strings

* fix: reduce fail-fast timeout to 300ms and guard stderr.write in exit hook

* fix(doctor): remove unused fix option and add release URL to extension install hint

* fix(e2e): update BrowserBridge unavailable detection regex to match current error format
2026-03-27 13:26:27 +08:00
jakevin 218ba918d9 chore: bump version to 1.5.0 (#482)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-27 03:57:05 +08:00
jakevin 40b923778f feat: smart error dispatch with inline Browser Bridge diagnosis (#481)
* feat: smart error dispatch with inline Browser Bridge diagnosis

- BrowserConnectError: runs checkDaemonStatus() on failure, shows real-time
  daemon/extension status and specific fix steps instead of a static hint
- AuthRequiredError: domain-specific login guidance
- TimeoutError: shows exact env var override command
- SelectorError/EmptyResultError: flags adapter as potentially outdated,
  links to debug command and issue tracker
- Generic untyped errors (164 in adapters): pattern-classified into
  auth/http/not-found/other with tailored guidance per category
- BrowserConnectError gains a `kind` field for future dispatch
- Added 6 new error icons (COMMAND_EXEC, ADAPTER_LOAD, NETWORK, etc.)
- Updated test: invalid bool now rejected eagerly in commanderAdapter

* fix: review fixes for smart error dispatch

- checkDaemonStatus: add { timeout: 300 } to match execution.ts behavior,
  avoids 2s wait on an already-failed path
- catch block: use named _statusErr variable; fall back to kind-derived
  state (running/extensionConnected inferred from BrowserConnectError.kind)
  instead of re-accessing outer err.hint ambiguously
- Extract renderBridgeStatus() helper to share logic between real-time
  and kind-derived fallback paths
- AuthRequiredError: use err.hint when set, respecting adapter-supplied
  hints; fall back to generic domain-based guidance
- HTTP regex: broaden from 'http [45]xx' to also match 'status: 404',
  bare '404', 'status 500', etc. — avoids false negatives
2026-03-27 03:04:09 +08:00
jakevin 15c6d0d508 refactor: deduplicate code, improve type safety, simplify error classes (#480)
- Extract shared parseYamlArgs() to yaml-schema.ts, eliminating duplicate
  YAML args parsing in discovery.ts and build-manifest.ts
- Unify BROWSER_ONLY_STEPS: export from capabilityRouting.ts, reuse in
  pipeline executor (fixes missing intercept/tap in retry set)
- Remove dead normalizeArgValue from commanderAdapter; bool coercion now
  handled solely by coerceAndValidateArgs in execution.ts
- Add closeWindow?() to IPage interface, replacing unsafe casts in executor
- BrowserBridge/CDPBridge implement IBrowserFactory, removing double cast
  in getBrowserFactory()
- Simplify CliError subclasses with new.target.name (9 redundant this.name
  assignments removed)
- Add hook dedup in addHook() to prevent duplicate registrations
- Fix normalizeRows to safely handle primitive values
- Unify CommandArgs type: execution.ts now imports from registry.ts
- Cache strategyLabel() call in cli.ts list command
2026-03-27 02:45:42 +08:00
jakevin 7617dff262 feat: zero onboarding, extension version check, and update notifier (#479)
* feat: zero onboarding, extension version check, and update notifier

- Fail-fast guard in execution.ts: when daemon is running but extension
  is not connected, immediately surface a setup guide instead of waiting
  for the 30s connect timeout

- Extension version handshake: extension sends `hello` with its version
  on WebSocket connect; daemon stores it and exposes via /status; CLI
  warns on mismatch in both execution path and `opencli doctor`

- `opencli doctor` now shows extension version inline and reports
  version mismatch as an actionable issue

- Non-blocking npm update checker: registers a process exit hook so the
  update notice appears after command output (same pattern as npm/gh/yarn);
  background fetch writes to ~/.opencli/update-check.json for next run

- postinstall: print Browser Bridge setup instructions after shell
  completion install for first-time global install users

Bug fixes caught in review:
- discover.ts: add AbortController timeout to checkDaemonStatus() fetch,
  move clearTimeout after res.json() to cover body streaming
- daemon.ts: clear extensionVersion and reject pending requests in
  ws.on('error') handler, not just ws.on('close')
- update-check.ts: skip update notice when process exits with non-zero
  code; read cache once at module load to avoid double disk I/O;
  guard isNewer() against NaN from pre-release version strings

* fix: reduce fail-fast timeout to 300ms and guard stderr.write in exit hook
2026-03-27 02:14:37 +08:00
AlexYue 31d3988398 feat(plugin): add opencli-plugin.json manifest and monorepo plugin support (#475)
* feat(plugin): add opencli-plugin.json manifest and monorepo plugin support

- New : types, read/validate, semver compatibility
- Monorepo install: clone → symlink sub-plugins → postInstall per sub-plugin
- Monorepo uninstall: symlink cleanup with ref counting
- Monorepo update: git pull on repo root, refresh all sub-plugins
-  supports  syntax
-  reads manifest metadata, groups monorepo plugins
-  install/list handlers updated for monorepo output
- 60 unit tests (25 manifest + 35 plugin including 11 new monorepo tests)
- Docs updated (EN + ZH) with monorepo section

* fix(plugin): install monorepo dependencies at repo root

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-27 01:31:38 +08:00
Lr_2002 310e136a6c feat(paperreview): add paperreview.ai adapter (#464)
* feat(paperreview): add paperreview.ai adapter

* fix(cli): normalize boolean command options

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 22:25:30 +08:00
d 🔹 776674c8dc feat(twitter): add time column to search output (#473)
* feat(twitter): add time column to search output

Extract created_at from tweet data and format as ISO datetime.
This helps users filter tweets by recency during monitoring.

Closes #465

* refactor(twitter): align search timestamp field with created_at

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 22:25:22 +08:00
AstroHan 0773616c1e feat(imdb): add IMDb adapter with 6 commands (#472)
* feat(imdb): add IMDb adapter with 6 commands

Add a public IMDb adapter using browser-based JSON-LD and __NEXT_DATA__
extraction. All commands use Strategy.PUBLIC with browser: true.

Commands:
- imdb search <query> — search movies, TV shows, and people
- imdb title <id> — get movie/show details (Movie, TVSeries, TVEpisode, TVMiniseries, TVMovie, etc.)
- imdb top — IMDb Top 250 chart
- imdb trending — Most Popular Movies
- imdb person <id> — actor/director info with filmography
- imdb reviews <id> — user reviews (first page, max 25)

Shared utils: ID normalization, ISO 8601 duration formatting, locale
forcing, JSON-LD extraction (supports type array filtering), and
anti-bot challenge detection.

* review: harden imdb adapter loading and tests

* test: unblock PR CI on merge head

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 22:21:52 +08:00
Mikey Zhou 5731881d59 feat: add bilibili/comments, xiaohongshu/comments commands + rate-limiter plugin docs (#457)
* feat: add bilibili/comments, xiaohongshu/comments, and rate-limiter plugin docs

- bilibili/comments: fetch top-level replies via /x/v2/reply/main with WBI signing
  (bvid → aid resolution + signed params, no DOM dependency)
- xiaohongshu/comments: DOM extraction from note detail page with login-wall detection
  and correct handling of 0-like counts (XHS shows "赞" text instead of "0")
- docs/advanced/rate-limiter-plugin.md: documents the onAfterExecute hook pattern
  and shows a plug-and-play rate limiter that adds random sleep between platform
  commands to reduce bot-detection risk

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

* fix(xiaohongshu): allow empty comments results

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 22:10:12 +08:00
槑囿脑袋 c75fea90ad feat(douban): add photo listing and download commands (#474)
* feat(douban): add photo listing and download commands

* refactor(douban): remove unreachable empty download branch

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 21:59:20 +08:00
AlexYue 6d1fb6d219 feat(runtime): add Bun runtime compatibility (#459)
* feat(runtime): add runtime detection utility for Bun/Node.js

Add runtime-detect.ts module that detects whether opencli is running
under Bun or Node.js via globalThis.Bun check. Includes helper
functions for version string and label formatting.

Add corresponding unit tests that work correctly under both runtimes.

* feat(runtime): integrate Bun runtime support into CLI tooling

- doctor: show runtime label (e.g. 'node v22.13.0') in diagnostic output
- package.json: add dev:bun, start:bun, test:bun convenience scripts
- E2E helpers: support OPENCLI_TEST_RUNTIME env var for runtime selection

* ci: add Bun compatibility test job and document runtime support

- ci.yml: add bun-test job using oven-sh/setup-bun@v2
- README.md: update Prerequisites to mention Bun, add Runtime Support
  section with usage examples for dev:bun, start:bun, test:bun

* ci: pin Bun version in compatibility job

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 19:10:31 +08:00
zzf fe87fc7b87 fix(extension): fail release packaging when manifest entry files are missing (#470) 2026-03-26 19:08:47 +08:00
jakevin b4cdc922c9 fix(36kr): avoid slow Intl timezone formatting in tests (#466) 2026-03-26 16:25:13 +08:00
Conn Ho 15b9bc8e0c feat(producthunt): add Product Hunt CLI adapter (#462)
* feat(producthunt): add Product Hunt CLI adapter

Add three commands:
- posts: RSS feed with optional category filter
- today: latest day's posts from feed
- hot: today's top posts with vote counts (browser INTERCEPT strategy)

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

* feat(producthunt): add browse command for category best products

Browse top-rated products in any Product Hunt category (e.g. vibe-coding,
ai-agents, developer-tools) with name, tagline, and review count.

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

* docs(producthunt): add adapter documentation

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

* fix(producthunt): rebase on main and stabilize selectors

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 16:02:33 +08:00
Conn Ho 22399cee1a feat(36kr): add 36氪 CLI adapter (#461)
* feat(36kr): add 36氪 CLI adapter with 4 commands

- news: latest articles via public RSS feed (no browser needed), includes title/summary/date/url
- hot: trending articles via INTERCEPT strategy, supports --type renqi/zonghe/shoucang/catalog
- search: keyword search via INTERCEPT + DOM scraping
- article: fetch article detail (title/author/date/body) by ID or URL

Also adds vitest adapter project entry for 36kr tests.

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

* docs(36kr): add adapter documentation

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

* fix(36kr): use Shanghai hot-list dates and complete docs

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 15:34:46 +08:00
Xeron ed89157804 fix(jd): filter avif images only from pcpubliccms CDN (#453)
* 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(jd): update test to expect avifImages column

* review: tighten jd item image contract

* fix: stabilize extension packaging and Chinese-site e2e

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 15:12:41 +08:00
jakevin 53122cd028 docs: align language badges with status badges (#455) 2026-03-26 12:51:47 +08:00
jakevin 6c6b3c0a39 docs: turn language links into badges (#454) 2026-03-26 12:50:07 +08:00
Xiao Han 7348231b08 feat(twitter): add likes command (#448)
* feat(twitter): add likes command

* review: harden twitter likes query resolution

* refactor(twitter): share query id resolution

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 12:38:35 +08:00
HzTTT 0705d38b40 fix(ci): include popup assets in extension release zip (#444)
* fix(ci): include popup assets in extension release

Copy popup assets into the packaged Chrome extension zip and validate that manifest-referenced files exist before publishing the artifact.

Co-authored-by: Codex <noreply@openai.com>

* fix: restore executable permission on bin entries after tsc build (#446) (#452)

tsc does not preserve the +x bit when compiling, so after clean-dist
removes dist/ and tsc regenerates it, dist/main.js loses its executable
permission. This causes 'Permission denied' when users run 'npm run build'
in the installed directory.

Fix: read bin entries from package.json at the end of build-manifest and
chmod 0o755 them (skipped on Windows). Wrapped in try/catch so it never
breaks the build.

Closes #446

* fix: correct positional arg usage in tests (#449)

* fix yahoo-finance quote e2e invocation

* fix positional args in v2ex topic tests

* fix(ci): script extension release packaging

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: jakevin <jakevingoo@gmail.com>
Co-authored-by: pi-dal <hi@pi-dal.com>
2026-03-26 12:27:57 +08:00
glwlg 784bbc45f4 fix(xiaohongshu): improve image-text publish flow (#447)
* fix(xiaohongshu): improve image-text publish flow

Match visible 图文 tab labels instead of relying on narrow class selectors, fail early when the page is still on the video publish surface, and avoid injecting images into a generic file input. Add regression coverage for the image-text tab flow and the video-page failure case.

* test(xiaohongshu): include publish tests in adapter project

* fix(xiaohongshu): wait for image-text surface before upload

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 12:25:57 +08:00
pi-dal 232ad55d0f fix: correct positional arg usage in tests (#449)
* fix yahoo-finance quote e2e invocation

* fix positional args in v2ex topic tests
2026-03-26 11:59:42 +08:00
jakevin 4e5b00beeb fix: restore executable permission on bin entries after tsc build (#446) (#452)
tsc does not preserve the +x bit when compiling, so after clean-dist
removes dist/ and tsc regenerates it, dist/main.js loses its executable
permission. This causes 'Permission denied' when users run 'npm run build'
in the installed directory.

Fix: read bin entries from package.json at the end of build-manifest and
chmod 0o755 them (skipped on Windows). Wrapped in try/catch so it never
breaks the build.

Closes #446
2026-03-26 11:59:14 +08:00
tiaot33 e64046219d feat(linux-do): refactor adapters with unified feed, tags, user commands (#434)
* feat(linux-do): refactor adapters with unified feed, tags, user commands

- Replace hot/latest/category with unified `feed` command (tag/category/view routing)
- Add `tags`, `user-topics`, `user-posts` commands
- Add static data files for categories and tags lookup
- Fix error handling: use CliError subclasses instead of raw Error
- Fix Discourse API field mapping in search (tags, created)
- Add strategy: cookie to all YAML adapters
- Update docs and README command listings
- Update E2E tests for new command signatures

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

* review: resolve linux-do feed from live metadata

* fix: restore linux-do CI

* fix: harden linux-do compatibility

* refactor: stabilize linux-do command migration

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-26 00:45:34 +08:00
jakevin ed706f606b fix: stabilize http download temp file handling (#443) 2026-03-26 00:01:06 +08:00
Conn Ho 824dc38aab fix(weread): restore positional book-id coverage (#433)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 23:58:40 +08:00
jakevin 0872bbec83 fix(weixin): rewrite publish time extraction (#440) 2026-03-25 23:53:14 +08:00
MatrixA a5f90884d8 feat(chatgpt): add model/mode selection and fix response polling (#438)
* feat(chatgpt): add model/mode selection and fix response polling

Add --model option to ask and send commands, and a new standalone
model command for switching ChatGPT Desktop models via Accessibility API.

Supported models: auto, instant, thinking, 5.2-instant, 5.2-thinking.

Changes:
- ax.ts: add AX_MODEL_SCRIPT (opens Options popover, searches within
  AXPopover to avoid matching sidebar items, supports legacy models
  submenu) and AX_GENERATING_SCRIPT (detects "Stop generating" button)
- ask.ts: add --model flag; fix polling to wait for generation to
  complete instead of returning partial/thinking intermediate text
- send.ts: add --model flag
- model.ts: new standalone command to switch model/mode

* review: activate chatgpt before model selection

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 23:45:09 +08:00
AstroHan 79dbd80b88 fix: repair weread private api requests (#436) 2026-03-25 23:43:50 +08:00
jakevin 53b4f2fc8e docs: add Chinese Electron adapter entry guide (#432) 2026-03-25 18:13:57 +08:00
jakevin 16589a9c7d docs: add entry guide for Electron app adapters (#430) 2026-03-25 18:07:16 +08:00
jakevin 8469c894c5 fix(test): harden download tests for Windows EPERM flakiness (#426)
- Clean up temp directories in afterEach to avoid stale file locks
- Add retry(2) on Windows to handle Defender file scanning EPERM
2026-03-25 16:26:33 +08:00
jakevin 2bfd3eeeb3 chore: release v1.4.1 (#425)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-25 16:21:14 +08:00
jakevin bf5f327775 fix(extension): improve UX when daemon is not running (#424)
- Show helpful hint in popup when disconnected: "This is normal. The
  extension connects automatically when you run any opencli command."
- Stop eager reconnect after 6 attempts (reaching 60s backoff) to
  reduce ERR_CONNECTION_REFUSED noise in console; keepalive alarm
  still retries every ~24s at low frequency.
2026-03-25 16:19:02 +08:00
jakevin dba93c2739 fix(test): limit default e2e to bilibili/zhihu/v2ex, gate extended browser tests (#423)
Split browser-public.test.ts: core sites (bilibili, zhihu, v2ex) run
by default; all other 20+ site tests moved to browser-public-extended
and gated behind OPENCLI_E2E=1 to prevent AI agents from launching
dozens of browser instances.
2026-03-25 16:13:45 +08:00
jakevin 03d94ba2e1 chore: trim adapter test suite to bilibili, zhihu, v2ex only (#421)
Remove other adapter sites from vitest config to keep test runs
focused and avoid flaky failures from live site changes.
2026-03-25 16:01:15 +08:00
jakevin 46177e8d1e fix: remove nonexistent readwise external CLI entry (#420)
The npm package @readwiseio/readwise-cli returns 404 and the
GitHub repo readwiseio/readwise-cli doesn't exist.
2026-03-25 15:47:40 +08:00
jakevin 41a630d4f0 fix: remove incorrect gws external CLI entry (#419)
brew install gws installs a git workspace manager, not Google
Workspace CLI. The npm package @nicholasgasior/gws doesn't exist
either. Remove the misleading entry entirely.
2026-03-25 15:42:07 +08:00
jakevin 3e0c18fc7b feat(weibo,youtube): add Weibo commands and YouTube channel/comments (#418)
Weibo: add feed, me, user, post, comments commands with cookie-based
auth and proper AuthRequiredError handling.

YouTube: add channel info and video comments via InnerTube API.

Also remove internal source references from file headers.
2026-03-25 15:37:51 +08:00
nianyi(likai) 39ca8330c5 feat(douyin): add Douyin creator center adapter (14 commands, 8-phase publish pipeline) (#416)
* feat(douyin): add Douyin creator center adapter (14 commands, 8-phase publish pipeline)

- publish: 8-phase pipeline (STS2 → TOS multipart upload w/ resume → ImageX cover → transcode poll → safety check → create_v2)
- draft: save as draft (phases 1-6 + is_draft:1, no timing)
- videos/drafts/delete/profile/update: content management
- hashtag (search/suggest/hot) / location / activities / collections / stats: discovery & analytics
- _shared: tos-upload (AWS Sig V4, multipart, resume), imagex-upload, transcode poller (encode=2), browser-fetch, sts2, creation-id, timing, text-extra
- 124 tests, tsc clean

* fix(douyin): accept unix timestamp strings

* docs(douyin): add browser adapter guide

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 15:36:07 +08:00
AllenS0104 9245bf4529 feat: add url field to 9 search adapters (67% -> 97% coverage) (#414)
* feat(tiktok): add video URL to search results

Add a 'url' field to the TikTok search adapter output, constructed from
the author's uniqueId and the video id returned by the API. This allows
downstream consumers (AI agents, pipelines, scripts) to link directly to
each video instead of only having the author handle.

The URL format is: https://www.tiktok.com/@{author}/video/{videoId}

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: add url field to 9 search adapters missing it

Add url output to search commands that were missing direct links:

YAML adapters:
- hackernews: surface existing url from map step into columns
- zhihu: pass computed url through map step into columns
- linux-do: construct url from topic id
- instagram: construct profile url from username
- xueqiu: pass computed url through map step into columns

TS adapters:
- arxiv: surface existing url from parseEntries into return + columns
- apple-podcasts: add collectionViewUrl from iTunes API
- medium: add url to columns (already computed in utils)
- weread: construct book url from bookId

This brings search adapter url coverage from 67% to 97% (32/33).
The only adapter without url is dictionary (word lookup, no URL concept).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(weread): use query arg in search

---------

Co-authored-by: Allen Song (Beyondsoft) <v-songjun@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 15:24:15 +08:00
pshu 554329fceb feat: add filter option for twitter search (#410)
* feat: add filter option for twitter search

* test: add tests

* docs: 📝 update

* fix(twitter): default search filter safely

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 15:09:38 +08:00
jakevin 4812486482 feat(extension): add popup UI, privacy policy, and CSP for Chrome Web Store (#415)
- Add popup.html/popup.js showing daemon connection status
  (Connected / Reconnecting / No daemon connected)
- Add message listener in background.ts to expose WebSocket state
- Add PRIVACY.md with full privacy policy covering all permissions
- Add content_security_policy to manifest.json
- Update description to be clearer for CWS reviewers
2026-03-25 15:07:40 +08:00
jakevin 15369fa23c chore: release v1.4.0 (#413)
* chore: release v1.4.0

* docs: sync command references across SKILL.md, README, and docs

SKILL.md:
- Add 12 missing sites: apple-podcasts, arxiv, bloomberg, coupang,
  dictionary, doubao, jd, linkedin, pixiv, web, weixin, xiaoyuzhou
- Add 36 missing commands across 6 existing sites (twitter, hackernews,
  yollomi, xueqiu, linux-do, v2ex)

README (EN + zh-CN):
- Add linkedin timeline command

docs/:
- Add 13 missing adapters to vitepress sidebar navigation
- Add 6 missing adapters to docs/adapters/index.md overview table
- Update xueqiu commands with fund-holdings, fund-snapshot
2026-03-25 14:48:46 +08:00
AlexYue a9571b196f ci: add cross-platform E2E and smoke test support (Linux/macOS/Windows) (#411)
* ci: add cross-platform support for E2E and smoke tests

Make headed browser tests (E2E and smoke) runnable on Linux, macOS,
and Windows:

- setup-chrome action: only install xvfb on Linux (macOS/Windows
  have native GUI sessions and don't need a virtual display)
- e2e-headed.yml: add OS matrix, use xvfb-run wrapper only on Linux
- ci.yml smoke-test: add OS matrix, use xvfb-run wrapper only on Linux

The browser-actions/setup-chrome action already supports all three
platforms natively.

* ci: exclude Windows from E2E/smoke matrix (Chrome install hangs)

browser-actions/setup-chrome hangs indefinitely during Chrome MSI
installation on Windows runners (observed 10+ min with no progress).
This is a known limitation of Windows CI runners.

Keep Linux + macOS for headed browser tests. Windows is still covered
by build, unit-test, and adapter-test jobs.
2026-03-25 14:35:24 +08:00
jakevin 594ad50949 fix: pre-release cleanup — bugs, version sync, and error handling (#412)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
* fix: pre-release cleanup — bugs, version sync, and error handling

Bug fixes:
- Fix hasLimit using wrong Set (SEARCH_PARAMS → LIMIT_PARAMS) in
  analysis.ts classifyQueryParams
- Remove phantom scroll step from BROWSER_STEPS and KNOWN_STEP_NAMES
  (declared but never registered, causes runtime crash if used in YAML)
- Add missing download step to KNOWN_STEP_NAMES (was producing
  false-positive validation warnings)

Docs:
- Sync version numbers: SKILL.md, extension/package.json,
  extension/manifest.json → 1.3.3
- Add jd, web to README command tables (both EN and zh-CN)
- Update xueqiu commands with fund-holdings, fund-snapshot

Code quality:
- Replace all 22 catch (err: any) with typed error handling using
  existing getErrorMessage() utility across 13 files

* fix: remove (err as any) casts in error handling

- antigravity/serve.ts: use typed Error.cause instead of (err as any).cause
- external.ts: move instanceof guard into shouldRetryWithCmdShim,
  accept unknown instead of forcing NodeJS.ErrnoException cast at call site
2026-03-25 14:32:29 +08:00
Saeed Al Mansouri 0ff28aa0d8 fix(extension): security hardening — tab isolation, URL validation, cookie scope (#409)
* fix(extension): security hardening — tab isolation, URL validation, cookie scope

Addresses issues raised in #399 (Astro-Han's community triage):

1. Tab isolation bypass: resolveTabId now verifies that an explicit tabId
   belongs to the automation window (tab.windowId === session.windowId)
   before accepting it. Tabs from the user's browsing session are rejected.

2. URL scheme allowlist: isDebuggableUrl switched from a blocklist
   (chrome://, chrome-extension://) to an allowlist (http://, https:// only).
   handleNavigate and tabs.new also reject non-http(s) URLs early, blocking
   file://, javascript:, and data: scheme abuse.

3. Cookie scope restriction: handleCookies now requires domain or url.
   Requests with neither are rejected instead of dumping all browser cookies.

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

* fix(extension): resolve data: URI vs allowlist conflict, plug tabs.select bypass

- Add BLANK_PAGE constant and whitelist it in isDebuggableUrl so
  internal blank tabs are not treated as non-debuggable after the
  blocklist-to-allowlist change.
- Add isSafeNavigationUrl for user-facing URL validation (http/https
  only), keeping it separate from internal isDebuggableUrl.
- Fix tabs.select to verify tab belongs to automation window before
  activating, closing a tab isolation bypass.
- Normalize error message style (-- instead of em dash).

* fix(extension): add try-catch for tabs.select with explicit tabId

Gracefully handle the case where cmd.tabId points to a closed tab
instead of letting the unhandled exception bubble up.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 14:03:18 +08:00
AstroHan e573f3fc32 fix(sort): use localeCompare with natural numeric sort by default (#306)
Replace manual < > comparison with localeCompare({ numeric: true })
so string-encoded numbers (e.g. "99" vs "1000") sort correctly
without requiring an explicit flag. This is a one-line fix that
makes sort just work for all YAML authors.

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 13:45:47 +08:00
AstroHan 78813fcb38 fix(pipeline): evaluate chained || in template engine (#305)
* chore: fix pre-existing biome lint in template.ts

- isNaN → Number.isNaN (2 occurrences)
- string concatenation → template literal
- biome-ignore for intentional control chars in sanitize regex

* fix(pipeline): evaluate chained || in template engine (#303)

The || handler in evalExpr returned the right side as a literal string
instead of recursively evaluating it. This broke chained fallbacks like
`item.a || item.b || 'default'` — when item.a was falsy, the entire
`item.b || 'default'` was returned as text.

Fix: call evalExpr on the right side so chained || works at any depth.

* perf(pipeline): fast-path string literals in evalExpr to skip VM

When the right side of || is a quoted string like 'N/A', detect it
with a simple regex and return directly instead of falling through
to evalJsExpr which spins up a node:vm sandbox.

* refactor(pipeline): simplify evalExpr by removing hand-rolled operator parsing

Replace the manual regex-based || and arithmetic handlers with a
streamlined flow: pipe filters → fast-path literals → resolvePath →
evalJsExpr (VM). The VM already handles ||, ??, arithmetic, ternary,
etc. natively, so reimplementing them with regex was redundant and
bug-prone (see issue #303).

Key improvements:
- Fix pipe | vs || disambiguation with lookbehind/lookahead regex
  (?<!|)|(?!|) so "item.a || item.b | upper" works correctly
- Remove ~20 lines of manual operator handling
- Add numeric literal fast path
- Pipe filter handler now uses evalExpr recursively (not just
  resolvePath), enabling filters on complex expressions

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 13:45:13 +08:00
iridite 9c99cbf8ab feat(xueqiu): add Danjuan fund account commands (#391)
* feat(xueqiu): add danjuan fund account commands

* refactor(xueqiu): convert danjuan fund YAML adapters to TS

- Replace 3 YAML files with 4 TS files (shared utils + 3 commands)
- Extract shared helpers: fetchDanjuanApi, fetchAssetGain, collectHoldings
- Fix double-navigation by using navigateBefore instead of pipeline navigate
- Unify error messages to English with Hint pattern
- Mask real account ID in docs example
- Add explicit default for --account arg

* refactor(xueqiu): optimize danjuan fund adapters

- Single page.evaluate with Promise.all for parallel account fetching
  (1 browser round-trip instead of N+1)
- Merge fund-accounts into fund-holdings (account info visible per row)
- 3 files: danjuan-utils.ts (shared), fund-holdings.ts, fund-snapshot.ts
- Strong TypeScript interfaces for all data shapes
- Update docs to reflect 2-command design

* fix(xueqiu): preserve danjuan pre-navigation metadata

* fix(xueqiu): fail on incomplete danjuan snapshots

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 13:32:41 +08:00
AllenS0104 297fd15f02 feat(tiktok): add video URL to search results (#404)
* feat(tiktok): add video URL to search results

Add a 'url' field to the TikTok search adapter output, constructed from
the author's uniqueId and the video id returned by the API. This allows
downstream consumers (AI agents, pipelines, scripts) to link directly to
each video instead of only having the author handle.

The URL format is: https://www.tiktok.com/@{author}/video/{videoId}

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: guard against empty uniqueId/id producing invalid URL

When uniqueId or id is missing, return empty string instead of
a malformed URL like "https://www.tiktok.com/@/video/".

---------

Co-authored-by: Allen Song (Beyondsoft) <v-songjun@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 13:21:43 +08:00
aresbit 806b358c0e fix windows chatwise connect (#405)
* Add

* test(chatwise): cover missing cdp endpoint guard

* refactor(chatwise): replace site special-case with command metadata

---------

Co-authored-by: ericyangbit <yangyang581@huawei.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 13:13:57 +08:00
AstroHan 541fd2129c fix(pipeline): check HTTP status in fetch step (#384)
* chore: ignore worktree directory

* fix(pipeline): check HTTP status in fetch step

* fix(pipeline): align fetch error semantics

* fix(pipeline): use CliError and add warn logging in fetch step

- Replace bare Error with CliError('FETCH_ERROR') for consistent CLI output
- Return error status from browser evaluate instead of throwing inside it
- Add log.warn() for batch item failures in both browser and non-browser paths

* chore: remove unrelated .worktrees/ from .gitignore

* refactor(fetch): use getErrorMessage(), unify sentinel naming to __httpError

- Use project's existing getErrorMessage() utility instead of manual instanceof checks
- Rename sentinel from __fetchError to __httpError for consistency with other adapters
- Simplify sentinel structure (url already available in outer scope, no need to pass through evaluate)
- Add comment explaining why getErrorMessage() can't be used inside evaluate()
- Add comment explaining CDP error message rewriting behavior

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 13:11:22 +08:00
Ryan Tan d36a43e805 feat(pixiv): add Pixiv adapter (#403)
* feat(pixiv): add Pixiv adapter with 6 commands

Add support for Pixiv (pixiv.net) with the following commands:
- ranking: daily/weekly/monthly illustration rankings
- search: search illustrations by keyword/tag
- user: view artist profile info
- illusts: list illustrations by artist
- detail: view illustration details (tags, stats)
- download: download original-quality images

All commands use COOKIE strategy to reuse Chrome's logged-in session.
YAML adapters for simple API fetches (ranking, detail, user), TypeScript
for complex logic (search, illusts, download with Referer header).

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

* test(pixiv): add unit tests and E2E auth failure tests

- search.test.ts: auth error, result parsing, limit, empty results (4 tests)
- illusts.test.ts: auth error, empty user, two-step fetch, limit (4 tests)
- download.test.ts: auth error, no images, Referer header, partial failure (4 tests)
- Add pixiv to vitest adapter project include list
- Add 5 pixiv commands to E2E browser-auth graceful failure tests

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

* fix(pixiv): correct ranking API path and YAML arg naming

- ranking: use /ranking.php?format=json (not /ajax/ranking which 404s)
- ranking: fix JSON path from data.body.contents to data.contents
- user/detail: rename hyphenated args (user-id → uid, illust-id → id)
  to fix YAML template evaluation (dot access doesn't support hyphens)

All 6 commands verified working against live Pixiv API.

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

* fix(pixiv): use JSON.stringify to prevent code injection in page.evaluate

Address CodeRabbit review: all user inputs (query, userId, illustId,
idsParam) passed to page.evaluate are now serialized via JSON.stringify
instead of direct string interpolation, preventing code injection in
browser context.

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

* refactor(pixiv): address code review feedback

- ranking.yaml: add | json filter to page/limit args for defense-in-depth
- user.yaml: guard illusts/manga/novels with typeof check for robustness
- Extract shared createPageMock to test-utils.ts, deduplicate across 3 test files

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

* refactor(pixiv): use minimal page mock and add download E2E test

- test-utils.ts: slim down to minimal mock (goto, evaluate, getCookies)
  with overrides support, matching upstream's pragmatic mock style
- Add missing download command to E2E browser-auth graceful failure tests

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

* fix(pixiv): address all remaining CodeRabbit review comments

- detail.yaml: add url to columns to match description mentioning "URLs"
- All adapters: differentiate HTTP errors — 401/403 → AuthRequiredError,
  404 → "not found", others → generic "request failed (HTTP N)"
- Tests: use beforeAll to cache registry lookup, avoiding repeated reads
  from global singleton
- Tests: assert error type (AuthRequiredError) not just message content
- Tests: add dedicated test cases for non-auth errors (500) and 404

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

* docs(pixiv): add adapter docs and indexes

- Add pixiv.md documentation page under docs/adapters/browser/
- Update docs/adapters/index.md with pixiv entry
- Add Pixiv to sidebar in docs/.vitepress/config.mts
- Update README.md and README.zh-CN.md adapter tables
- Add pixiv to download support tables in both READMEs

Completes the documentation checklist for the pixiv adapter PR.

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

* fix(pixiv): address code review findings

- Use CommandExecutionError instead of raw Error for HTTP failures
- Add page.goto() before page.evaluate() to establish browser context
- Fix search keyword double-encoding in URL construction
- Fix ranking.yaml using rating_count instead of illust_bookmark_count
- Throw on batch detail fetch failure instead of silent empty return
- Add beforeEach mock reset in download tests
- Add novels column to user.yaml output

Ensures pixiv adapter follows upstream CliError conventions and handles
edge cases correctly before submitting to upstream.

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

* docs(pixiv): improve download description in READMEs

- Replace technical Referer header detail with user-facing description
- Describe what users care about: original quality and multi-page support

Technical details belong in code comments, not user-facing docs.

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

* docs(pixiv): expand usage examples with all options

- Add ranking mode examples including R18 variants
- Add search filter examples (mode, order, pagination)
- Organize examples by command category for readability

Users need to know available options without reading source code.

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

* fix(pixiv): address second round of CodeRabbit review comments

- Validate illust-id is numeric to prevent path traversal
- Move URL parsing inside per-item try block for graceful error handling
- Add auth error handling for batch detail request (consistent with step 1)

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

* refactor(pixiv): extract shared pixivFetch helper, add input validation & batch support

- Create utils.ts with pixivFetch() for unified navigate + fetch + error handling
- Refactor search.ts, illusts.ts, download.ts to use pixivFetch (DRY)
- Add user-id/illust-id numeric validation in TS adapters
- Add batch pagination in illusts.ts for limit > 48 (Pixiv server limit)
- Add comment explaining Pixiv search API dual keyword requirement
- Update tests: new invalid-ID test cases, aligned mock format with pixivFetch

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 12:52:21 +08:00
AlexYue 4eeed2d4d7 ci: add cross-platform CI matrix (Linux/macOS/Windows) (#402)
* ci: add cross-platform matrix (Linux/macOS/Windows) to build, unit-test, adapter-test

Add OS matrix with ubuntu-latest, macos-latest, and windows-latest to
the build, unit-test, and adapter-test CI jobs. This ensures cross-
platform compatibility is verified on every push and PR.

Smoke tests remain Linux-only due to xvfb dependency.

Relates to #392 (Windows plugin path issues).

* test: replace hardcoded /tmp with os.tmpdir() for Windows compatibility

Fix Windows CI failures caused by hardcoded '/tmp' paths that don't
exist on Windows. Use os.tmpdir() which returns the correct platform-
specific temp directory on all operating systems.

Files fixed:
- src/engine.test.ts: 3 occurrences (mkdtemp, discoverClis path)
- src/plugin.test.ts: 2 occurrences (getCommitHash test, mock condition)

* test: fix remaining Windows path issues in test files

- engine.test.ts: use pathToFileURL().href for dynamic import paths
  (path.join produces backslashes on Windows, breaking ES module imports)
- download.test.ts: replace hardcoded '/tmp' with os.tmpdir() + path.join
2026-03-25 10:44:33 +08:00
Saeed Al Mansouri d51f361bd3 fix(plugin): resolve Windows path and symlink issues (#400)
* fix(plugin): resolve Windows path and symlink issues

- Replace `new URL(import.meta.url).pathname` with `fileURLToPath()` from
  node:url — the former returns `/C:/Users/...` on Windows (leading slash
  before drive letter), breaking path resolution for host linking and
  esbuild binary lookup.

- Use junction (`'junction'`) instead of directory symlink (`'dir'`) on
  Windows in linkHostOpencli — junctions don't require admin privileges,
  while `fs.symlinkSync(..., 'dir')` does on Windows.

- Use `where` instead of `which` on Windows for global esbuild lookup.

All changes are platform-conditional and preserve existing Unix behavior.

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

* fix(plugin): additional Windows fixes found during UAT

- npm execFileSync needs shell:true on Windows (.cmd wrapper)
- esbuild binary is a shebang script, needs shell:true on Windows
- resolveEsbuildBin: prefer .cmd in node_modules/.bin/ on Windows
  over import.meta.resolve (which returns a shebang script)
- Updated test to accept .cmd extension on Windows

Found during UAT testing on Windows 11.

* fix: handle multi-line output from 'where' on Windows

'where esbuild' on Windows can return multiple matching paths, one per
line. Take only the first match to get a valid single path for
resolveEsbuildBin().

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: ByteYue <yj976240184@gmail.com>
2026-03-25 10:25:06 +08:00
AlexYue 78d52d984b test(plugin): add E2E integration tests for plugin lifecycle (#389)
* test(plugin): add E2E integration tests for plugin lifecycle

Add plugin-management.test.ts covering the full plugin lifecycle
using real GitHub clone of opencli-plugin-hot-digest:
- plugin install from github:ByteYue/opencli-plugin-hot-digest
- plugin list (table and JSON formats)
- plugin update on installed plugin
- plugin uninstall with cleanup verification
- error paths: invalid source, non-existent plugin, missing args

Tests safely backup/restore existing plugin state to avoid
interfering with user's real installed plugins.

Update TESTING.md to document the new test file.

* test(plugin): isolate lifecycle e2e from user home

* fix(plugin): respect HOME env var for test isolation

The E2E tests for plugin management were failing because os.homedir()
doesn't respect the HOME environment variable. This made test isolation
impossible since all tests would use the real ~/.opencli directory.

Added getHomeDir() helper that checks process.env.HOME first before
falling back to os.homedir(). Updated readLockFile() and writeLockFile()
to use this new function.

Fixes test failures in plugin-management.test.ts where:
- plugin install would write to real home instead of temp dir
- lock file assertions would fail with ENOENT

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 00:23:51 +08:00
AlexYue 1512016967 feat(plugin): add lifecycle hooks API (onStartup, onBeforeExecute, onAfterExecute) (#376)
* feat(plugin): add lifecycle hooks API (onStartup, onBeforeExecute, onAfterExecute)

Introduce a hooks system that allows plugins to tap into opencli's
execution lifecycle without modifying core code.

New files:
- src/hooks.ts: hook registration, emission, and globalThis singleton
- src/hooks.test.ts: 10 unit tests covering registration, ordering,
  error isolation, async support, and globalThis sharing

Modified files:
- src/execution.ts: emit onBeforeExecute/onAfterExecute around command execution
- src/main.ts: emit onStartup after discoverPlugins()
- src/registry-api.ts: export hooks API for plugin consumption

Example plugin: https://github.com/ByteYue/opencli-plugin-audit-log

* fix(discovery): load plugin files that register lifecycle hooks

The isCliModule() check only matched files containing 'cli(' calls,
silently skipping hook-only files like audit-hooks.ts that register
onBeforeExecute/onAfterExecute without any cli() command registration.

Renamed CLI_MODULE_PATTERN → PLUGIN_MODULE_PATTERN and extended the
regex to also match onStartup(, onBeforeExecute(, onAfterExecute(.

* fix(plugin): tighten lifecycle hook semantics

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-25 00:09:43 +08:00
jakevin 8d2ee03d2d DOM 元素检测增强 (browser-use 研究)
Add search element heuristics and label/span wrapper detection

- Add SEARCH_INDICATORS set to detect search-related elements
- Add isSearchElement function for heuristic detection
- Add hasFormControlDescendant to detect wrapped form controls
- Enhance isInteractive for label/span wrapper patterns

Ref: browser-use ClickableElementDetector research
Review: @codex
2026-03-24 23:59:27 +08:00
AstroHan 106ab3a424 fix(download): scope cookies to target domain (#385)
* chore: ignore worktree directory

* fix(security): scope download cookies to target domain

* fix(download): scope yt-dlp cookies per target domain

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 23:28:04 +08:00
AlexYue 316b495c72 review: rebase plugin esbuild resolution on current main (#366)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 22:43:12 +08:00
jakevin cffc043fa7 ci: trim low-value workflows and duplicate checks (#381) 2026-03-24 22:35:37 +08:00
jakevin 3b2a51fcd3 fix(extension): revert #377 and cleanly fix same-url navigation timeout (#380)
* Revert "fix(extension): avoid same-url navigation timeout (#377)"

This reverts commit b7ada0e38c.

* fix(extension): avoid same-url navigation timeout

- Add normalizeUrlForComparison for minimal URL canonicalization
  (root slash + default port only; preserves hash and non-root paths)
- Fast-path: skip navigation when tab is already at the target URL
- Rewrite wait logic with finish() pattern to prevent double-resolve
- Handle both same-URL and redirect scenarios in navigation listener
- Add regression tests for same-URL and hash-route distinction
2026-03-24 22:25:29 +08:00
AlexYue e7e4367827 review: scope plugin lock tracking cleanly (#362)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 22:21:15 +08:00
ayotme b7ada0e38c fix(extension): avoid same-url navigation timeout (#377)
* fix(extension): avoid same-url navigation timeout

* review: preserve hash-aware extension navigation

---------

Co-authored-by: huruichen <huruichen@kanzhun.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 22:05:22 +08:00
AlexYue 93b9db5337 review: scope plugin update-all and sync docs (#368)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 21:54:40 +08:00
jakevin ba5f133cd6 docs: tighten adapter authoring guidelines (#371) 2026-03-24 21:40:29 +08:00
AlexYue 3cf323f68b feat(plugin): validate plugin structure on install and update (#364) 2026-03-24 21:38:56 +08:00
jakevin 180e7eae13 refactor: adopt CliError in social adapters (#375) 2026-03-24 21:26:55 +08:00
jakevin 94c3ef9af1 refactor: simplify codebase with type dedup, shared analysis module, and consistent naming (#373)
- Remove unused re-exports from registry.ts (serializeArg, serializeCommand, etc.)
- Unify FormatOptions into SnapshotOptions from types.ts; rename dom-snapshot's
  SnapshotOptions to DomSnapshotOptions to avoid name collision
- Extract shared analysis.ts module from explore.ts and record.ts, eliminating
  ~200 lines of duplicated logic (urlToPattern, findArrayPath, inferCapabilityName,
  inferStrategy, detectAuth*, classifyQueryParams)
- Merge snapshotFormatter from 7-pass to 4-pass pipeline by combining parse+filter
  with ad/boilerplate subtree skipping, and merging three dedup passes into one
- Rename all CLI adapter shared files to consistent utils.ts naming
  (boss/common.ts, douban/shared.ts, doubao*/common.ts, jike/shared.ts,
  medium/shared.ts, sinablog/shared.ts, substack/shared.ts)
- Merge douban/shared.ts into douban/utils.ts
2026-03-24 21:20:08 +08:00
jakevin 86b59d91a6 refactor: adopt CliError in desktop UI adapters (#372) 2026-03-24 21:12:40 +08:00
jakevin e916c164b6 refactor: use CliError subclasses in remaining adapters (#367)
* refactor: use CliError subclasses in linkedin adapters

Replace raw Error throws with appropriate CliError subclasses:
- linkedin/timeline.ts: AuthRequiredError, EmptyResultError
- linkedin/search.ts: ArgumentError, CommandExecutionError

This enables better error handling and user-facing error messages.

* refactor: use CliError subclasses in twitter adapters

Replace raw Error throws with appropriate CliError subclasses:
- twitter/trending.ts: AuthRequiredError, EmptyResultError
- twitter/bookmarks.ts: AuthRequiredError, CommandExecutionError

This enables better error handling and user-facing error messages.

* refactor: use CliError subclasses in reddit adapters

Replace raw Error throws with appropriate CliError subclasses:
- reddit/read.ts: CommandExecutionError for API-related errors

This enables better error handling and user-facing error messages.

* refactor: use CliError subclasses in adapters for better error handling

- linkedin/timeline: AuthRequiredError, EmptyResultError
- linkedin/search: ArgumentError, CommandExecutionError
- bilibili/subtitle: AuthRequiredError, CommandExecutionError, EmptyResultError, SelectorError
- bilibili/following: CommandExecutionError
- medium/shared: CommandExecutionError
- twitter/delete, twitter/unfollow: CommandExecutionError

This allows the top-level error handler to render consistent,
helpful output with emoji-coded severity and actionable hints.

* refactor: use CliError subclasses in youtube, bilibili, and boss adapters

Replace raw Error throws with appropriate CliError subclasses:
- youtube/transcript.ts: CommandExecutionError, EmptyResultError
- youtube/video.ts: CommandExecutionError
- bilibili/utils.ts: EmptyResultError
- boss/send.ts: EmptyResultError, SelectorError
- boss/mark.ts: ArgumentError, EmptyResultError

This enables better error handling and user-facing error messages.
2026-03-24 21:00:51 +08:00
jakevin 1af0f48023 docs: remove kubectl references from documentation and external CLI list (#363)
Remove kubectl from:
- README.md highlights and external CLI examples table
- README.zh-CN.md highlights and external CLI examples table
- src/external-clis.yaml external CLI registry

kubectl is not relevant to the opencli project scope and should not be showcased as a primary example.
2026-03-24 20:25:15 +08:00
jakevin 2fb7ed131b fix(e2e): remove duplicate closing bracket causing vite:oxc parse error (#361)
The dictionary adapters commit (3d39574) introduced a duplicate
`}, 30_000);` at line 524 of public-commands.test.ts, causing
the vite:oxc transformer to fail with [PARSE_ERROR] Unexpected token
in the E2E Headed Chrome CI workflow.
2026-03-24 20:21:11 +08:00
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
dependabot[bot] b1067b64ee chore(ci): bump peter-evans/repository-dispatch from 3 to 4 (#323)
Bumps [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) from 3 to 4.
- [Release notes](https://github.com/peter-evans/repository-dispatch/releases)
- [Commits](https://github.com/peter-evans/repository-dispatch/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peter-evans/repository-dispatch
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 19:23:59 +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
AstroHan d44a0ab256 docs: add "Why opencli?" section and comparison guide (#331)
* docs: add "Why opencli?" section and comparison guide (#238)

- Add "Why opencli?" section to README.md and README.zh-CN.md
  (between Highlights and Prerequisites)
- Add docs/comparison.md with 5-scenario honest evaluation
- Add Comparison entry to VitePress sidebar

* docs: refine positioning — use approximate numbers, emphasize broad coverage

- Replace specific counts (300+, 55, 20+) with approximate descriptions
- Emphasize broad global + Chinese platform coverage instead of singling out Chinese sites
- Fix Firecrawl description to mention self-hosted option
- Replace "sub-second" / "milliseconds" with accurate "seconds" / "fast deterministic"
- Add testing and AI workflow to Further Reading links
- Add "easy to extend" point to strengths

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-24 19:13:49 +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
jakevin 505c86bae9 docs: add missing adapter docs for jd and web (#349)
Add documentation for jd (item) and web (read) adapters to fix
doc-coverage CI check (55/57 → 57/57).
2026-03-24 16:03:18 +08:00
dependabot[bot] b8b4fa011b chore(deps): bump ws from 8.19.0 to 8.20.0 (#326)
Bumps [ws](https://github.com/websockets/ws) from 8.19.0 to 8.20.0.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.19.0...8.20.0)

---
updated-dependencies:
- dependency-name: ws
  dependency-version: 8.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 15:57:50 +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
dependabot[bot] 37c7ea41b8 chore(deps): bump typescript from 5.9.3 to 6.0.2 (#327)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 6.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 14:51:15 +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 56d4326646 refactor(extension): reuse async detach() in registerListeners (#328)
Replace inline sync chrome.debugger.detach() in onUpdated listener
with the shared async detach() function for consistent cleanup behavior
across all detach paths.
2026-03-24 02:18:34 +08:00
QSam2023 4c9a2b1fde fix: detach debugger before navigation in browser bridge (#322)
* fix: detach debugger before navigation in browser bridge

* refactor: make detach() async, await all detach calls

- cdp.ts: detach() now async, awaits chrome.debugger.detach()
- background.ts: await detach() in handleNavigate and handleTabs close
- Eliminates theoretical race between detach and subsequent tab operations

Co-authored-by: jackwener <jackwener@gmail.com>

---------

Co-authored-by: bluey_heeler <fragwang231@gmail.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
Co-authored-by: jackwener <jackwener@gmail.com>
2026-03-24 02:14:31 +08:00
dependabot[bot] 57fcc50c1b chore(deps): bump vitest from 4.1.0 to 4.1.1 (#325)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.1/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 02:13:04 +08:00
dependabot[bot] 689cd1ebb3 chore(ci): bump actions/upload-artifact from 4 to 7 (#324)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-24 02:12:48 +08:00
jakevin 167c7c784a chore: release v1.3.3 (#321)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-24 01:45:21 +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
jakevin 89a20e11bc docs: sync command references with current registry (#318) 2026-03-24 01:13:55 +08:00
jakevin 760b91e7b3 chore: release v1.3.2 (#317) 2026-03-24 01:05:47 +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 a170873ad6 fix(e2e): broaden xiaoyuzhou skip logic for overseas CI runners (#316)
* fix: remove duplicate getErrorMessage import in discovery.ts

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.

* fix(e2e): broaden xiaoyuzhou skip logic for overseas CI runners

The isExpectedChineseSiteRestriction function only matched FETCH_ERROR
with specific HTTP status codes. On overseas CI runners, xiaoyuzhou may
also return PARSE_ERROR (mangled HTML) or NOT_FOUND (geo-redirected
pages), causing false test failures. Now matches all CliError codes
from the adapter.
2026-03-24 00:47:07 +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 8bf750c4ea docs(SKILL.md): sync command reference — add missing sites and desktop adapters (#314)
- Remove hardcoded '150+ commands across 30+ sites' from description
- Fix verify → validate command name
- Add 15+ missing site command references: douban, facebook, instagram,
  tiktok, medium, substack, sinablog, lobsters, google, devto, steam, wikipedia
- Add Desktop Adapter Commands section with 7 adapters: cursor, codex,
  chatgpt, chatwise, notion, discord-app, doubao-app
2026-03-24 00:37:42 +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
jakevin 4343ec07e0 fix(tests): use positional arg syntax in browser search tests (#302)
Replace redundant --keyword/--query named flags with positional
arg syntax for all search commands that declare positional: true.
Also fix --query usage in tiktok.md docs example.

Affected:
- bilibili, weibo, zhihu, reuters, youtube, smzdm, boss, coupang, xiaohongshu search (browser-public.test.ts)
- linux-do search (browser-auth.test.ts)
- tiktok search (docs/adapters/browser/tiktok.md)
2026-03-23 19:00:12 +08:00
jakevin f00a5d1929 docs: update xiaohongshu search description, fix test count 31→32 (#301)
- docs/adapters/browser/xiaohongshu.md: fill in search command description
  (was empty), update usage examples with keyword positional arg
- TESTING.md: update unit test count 31→32 (search.test.ts added in #298),
  add xiaohongshu/search.test.ts to the adapter test file list
2026-03-23 17:59:35 +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
AstroHan 7073645e30 docs: add gws to External CLI Hub table in README (#286)
* docs: add gws to External CLI Hub table in README

The Google Workspace CLI (gws) was registered in external-clis.yaml
but missing from the README table. Closes #120.

* docs: add gws to Chinese README External CLI Hub table

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 15:36:46 +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 fcb5a9d409 docs: sync adapter lists with codebase (#291)
- Add 9 missing adapters: facebook, google, instagram, tiktok, lobsters,
  medium, sinablog, substack, doubao-app
- Add missing xiaohongshu publish command
- Fix grok mode: Desktop → Browser
- Fix boss commands in docs/adapters/index.md (was incomplete)
- Add doubao-app to Desktop Adapters table
2026-03-23 15:03:23 +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
jakevin 2a52906ed2 fix: add turndown dependency to package.json (#288)
turndown and @types/turndown were used in article-download.ts and
zhihu/download.test.ts but never declared in package.json, causing
CI failures on fresh npm ci installs.
2026-03-23 14:46:08 +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 722c180a0a docs: clarify release follow-up steps (#279) 2026-03-23 11:47:26 +08:00
jakevin 788126198b chore: release v1.3.1 (#273)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-23 00:33:04 +08:00
jakevin 98ecfab8ad chore: bump version to 1.3.0 (#272)
* chore: bump version to 1.3.0

* chore: bump version to 1.3.0
2026-03-23 00:27:55 +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
Zhang ShengYan 3bedaccc25 docs: refresh testing guide (#223)
* docs: refresh testing guide

* docs: include missing twitter/timeline.test.ts in test inventory

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-23 00:11:44 +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
AlexYue 9a77dba139 ci: trigger website rebuild on release (#269) 2026-03-22 23:46:48 +08:00
jakevin 49c2dc7426 docs: remove all --live references (now default behavior) (#267) 2026-03-22 23:03:53 +08:00
jakevin e4a13cb6f0 fix: remove duplicate horizontal rules and extra blank lines in READMEs (#266) 2026-03-22 22:58:54 +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 a778f617ca chore: update package-lock.json (#264) 2026-03-22 22:55:36 +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 7ebe8134cc docs: clean up READMEs - remove redundant sections (#261)
Removed from both EN/CN READMEs:
- Table of Contents (GitHub auto-generates TOC)
- Method 2: Load from npm Package (keep recommended + dev only)
- Bloomberg detailed note (too specific for README)
- Pipeline Step YAML example (developer-internal)
- Releasing New Versions (belongs in CONTRIBUTING.md)

EN README only:
- Simplified Testing section to one-liner + link to TESTING.md
2026-03-22 22:26:49 +08:00
jakevin c44bc62b60 chore: code cleanup + extension conflict troubleshooting (#260)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
Cleanup:
- Remove redundant double-retry in resolveTabId (was retrying data: URI
  with the same data: URI)
- Fix stale comment (30s → 120s idle timeout)
- Remove verbose debug logging in resolveTabId
- Built extension is now smaller (16.66kB vs 17.18kB)

Extension conflict:
- Add hint to attach-failed error when chrome-extension:// URL is detected
- Add troubleshooting entry for extension conflicts (e.g. youmind, New Tab
  Override) to both README.md and README.zh-CN.md

Ref: #249
2026-03-22 22:18:22 +08:00
jakevin 7f57e76485 fix: treat empty tab URL as debuggable (fixes first-run doctor --live failure) (#259)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
When a new automation window is created, the initial tab URL may be
empty briefly while Chrome loads the data: URI. isDebuggableUrl('') was
returning false, causing ensureAttached to reject the tab.

Fix: only reject known non-debuggable URLs (chrome://, chrome-extension://).
Empty/undefined URLs are now treated as debuggable since they represent
tabs still loading.

Also adds 200ms delay after window creation to let Chrome populate the
tab URL.
2026-03-22 22:10:30 +08:00
jakevin e9818c1b41 chore: remove CRX from release pipeline and docs (#258)
CRX files cannot be installed in modern Chrome without Chrome Web Store
publishing. Updated all docs to recommend 'Load unpacked' installation
method only. Added npm package loading method as alternative.

- Removed CRX build step from build-extension.yml workflow
- Removed CRX from artifact upload and release attachment
- Updated README.md, README.zh-CN.md, browser-bridge docs (en/zh)
- Added 'Load from npm package' as installation method
2026-03-22 22:07:33 +08:00
jakevin 3e91876d13 fix: replace all about:blank with data: URI to prevent New Tab Override interception (#257)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
Root cause: getAutomationWindow and resolveTabId used about:blank which
New Tab Override extensions intercept immediately, replacing it with
chrome-extension:// URLs that cannot be debugged.

Changes:
- Window creation: about:blank → data:text/html
- reuseTab fallback: about:blank → data:text/html
- newTab handler: about:blank → data:text/html
- Added diagnostic logging to resolveTabId for debugging
- Synced extension version to 1.2.4

Ref: #249
2026-03-22 22:04:25 +08:00
jakevin 7c02588105 chore: bump version to 1.2.3 (#256)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-22 21:48:17 +08:00
jakevin 112fdefa8d fix: harden resolveTabId against New Tab Override extension interception (#255)
resolveTabId's reuseTab path now verifies the URL is actually debuggable
after navigating to about:blank. If a New Tab Override extension intercepts
it (setting it back to chrome-extension://), falls back to a data: URI,
then creates a fresh tab as last resort.

This fixes the persistent 'attach failed: Cannot access chrome-extension://'
error for users with New Tab Override extensions installed.

Ref: #249
2026-03-22 21:47:44 +08:00
jakevin e077ad2336 chore: bump version to 1.2.2 (#254)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-22 21:27:03 +08:00
jakevin 81384ede00 chore: bump version to 1.2.1 (#252)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-22 21:24:42 +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 b3b9892836 docs: add star history chart (#246) 2026-03-22 19:22:41 +08:00
jakevin 520622ac75 ci: update GitHub Actions runtime versions (#245) 2026-03-22 19:02:21 +08:00
jakevin 2d1b8c1e76 chore: prepare v1.2 release (#244)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
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
plat1ko 9696db9ed4 feat: make primary args positional across all CLIs (#242)
* feat: make primary args positional across all CLIs

Convert primary arguments from named options (--arg) to positional
arguments for a more natural CLI experience.

Affected sites: antigravity, bilibili, boss, chaoxing, coupang, grok,
hf, instagram, jike, jimeng, linkedin, linux-do, tiktok, twitter,
xiaohongshu, youtube

Also adds Arg Design Convention to CONTRIBUTING.md and earnings-date
to xueqiu command list in READMEs.

Usage examples:
  opencli xueqiu search '茅台'       (was: --query '茅台')
  opencli twitter followers elonmusk  (was: --user elonmusk)
  opencli bilibili download BV1xxx    (was: --bvid BV1xxx)

* review: keep config args named in positional cleanup

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-22 17:04:56 +08:00
jakevin c76f86c9cb refactor: fail fast on invalid pipeline steps (#237) 2026-03-22 14:53:57 +08:00
jakevin 4cd0409ded fix: harden twitter timeline review findings (#236) 2026-03-22 14:30:03 +08:00
VK ea113a6471 feat(devto): add devto adapter (#234)
* feat(devto): add devto adapter

* refactor(devto): improve adapters to match project conventions

- Make tag/username args positional for natural CLI usage:
  opencli devto tag javascript (instead of --tag javascript)
  opencli devto user ben (instead of --username ben)
- Add rank field (index + 1) matching hackernews/lobsters pattern
- Add tags field from tag_list for richer output
- Remove redundant author column from user command (already filtering by user)
- Use type: str (project convention) instead of type: string
- Increase default limit from 10 to 20 (matching other adapters)
- Update docs with positional arg examples

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-22 14:18:47 +08:00
AstroHan a439286398 docs(plugin): add juejin plugin to example plugins (#207) 2026-03-22 14:17:12 +08:00
AstroHan 1d56dd77a8 fix(wikipedia): fix search arg name + add random and trending commands (#231)
* fix(wikipedia): fix search arg name + add random and trending commands

- fix: search.ts referenced `args.keyword` but the argument is defined
  as `query`, causing the search term to always be undefined
- feat: add `random` command (random article summary via REST API)
- feat: add `trending` command (most-read articles, yesterday's data)

All commands are PUBLIC strategy, no browser required, reuse wikiFetch.

* refactor(wikipedia): extract shared types + add docs for random/trending

- Extract WikiSummary, WikiMostReadArticle types to utils.ts
- Extract EXTRACT_MAX_LEN/DESC_MAX_LEN constants
- Add formatSummaryRow() helper to eliminate duplicate mapping in
  summary.ts and random.ts
- Update docs with random and trending command examples

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-22 14:14:21 +08:00
AstroHan e98cf756e9 feat(twitter): add --type flag to timeline command (#83) (#232)
Support switching between For You (algorithmic) and Following
(chronological) timelines via `--type for-you|following`.

Both endpoints share the same response structure; only the GraphQL
endpoint name and queryId differ. QueryId is resolved dynamically
from fa0311/twitter-openapi with a hardcoded fallback, and validated
against /^[A-Za-z0-9_-]+$/ to prevent injection from upstream.
2026-03-22 12:11:21 +08:00
Zhang ShengYan 387aa0d6e5 fix: resolve inconsistent doctor --live report (fix #121) (#224)
* fix(doctor): refresh status after live check to resolve #121

* refactor: reorder live check before status read for natural consistency

Instead of calling checkDaemonStatus() twice (before and after the
connectivity check), reorder so that the live connectivity check runs
first, then read daemon status only once. This:
- Eliminates redundant checkDaemonStatus() call
- Naturally avoids the timing inconsistency (fixes #121)
- Also fixes the sessions query using stale status
- Simplifies test assertions to avoid over-coupling to exact wording

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-22 10:59:59 +08:00
jakevin 1ecac25df5 fix: correct SKILL.md github reference and add missing adapter docs (#230)
- SKILL.md: replace non-existent 'opencli github search' with correct
  'opencli gh' external CLI passthrough examples
- SKILL.md: remove 'github search' from public API commands list
- Add missing docs for douban, sinablog, substack adapters (fixes
  doc-check CI failure: 47/50 → 50/50)
- Add new adapter pages to VitePress sidebar config
2026-03-22 10:54:03 +08:00
jakevin 9921e5d696 remove broken desktop adapters (#221) 2026-03-22 04:14:07 +08:00
jakevin 4c8a447ead fix: align positional primary args and docs (#220) 2026-03-22 04:02:59 +08:00
plat1ko fb2a145e36 feat(xueqiu): make primary args positional (#213)
- search: query → positional
- stock: symbol → positional
- earnings-date: symbol → positional
- Fix build-manifest scanYaml to preserve positional field

Usage:
  opencli xueqiu search '茅台'
  opencli xueqiu stock SH600519
  opencli xueqiu earnings-date SH600519
2026-03-22 03:57:14 +08:00
jakevin bd274ce2d7 refactor: type discovery core (#219) 2026-03-22 03:46:24 +08:00
jakevin 28c393ec86 refactor: type browser core (#218) 2026-03-22 03:41:36 +08:00
jakevin 8a4ea411e1 refactor: type pipeline core (#217) 2026-03-22 03:33:59 +08:00
jakevin 45cee57ca0 refactor: reduce core any usage (#216) 2026-03-22 03:23:16 +08:00
AstroHan 4e3259976b feat(google): add search, suggest, news, and trends adapters (#184)
* feat(google): add search, suggest, news, and trends adapters

Four new commands under `google`:
- search: browser-based DOM extraction from google.com/search
- suggest: public JSON API (suggestqueries.google.com)
- news: public RSS feed (top stories + keyword search)
- trends: public RSS feed (daily trending searches by region)

Shared RSS parser in utils.ts with attribute/CDATA support.
Unit tests for parseRssItems, E2E tests with network skip guards.

* refactor(google): downgrade search strategy from COOKIE to PUBLIC

Google search results are public data, no login needed. Browser is
required for DOM rendering, not authentication. Standalone mode
confirmed working in testing.

* fix: update test comment to reflect PUBLIC strategy
2026-03-22 01:02:32 +08:00
Leo Yuan Tsao bdf5967abd feat: add douban, sinablog, substack adapters; upgrade medium to TS (#185)
New adapters:
- douban: book-hot, movie-hot, search (browser/cookie)
- sinablog: hot, search, article, user (search uses public API)
- substack: feed, publication, search (search uses public API)

Medium upgrade (YAML → TS):
- Replace tag.yaml/user.yaml/publication.yaml with TS adapters
- feed.ts (tag feed by topic), search.ts, user.ts with browser scraping
- Richer data: readTime, claps, description

Core pipeline improvements:
- template.ts: trim template before matching (supports multiline expressions)
- template.ts: evalJsExpr fallback for JS expressions in YAML templates
- template.ts: add urlencode/urldecode filters
- transform.ts: inline select inside map params
- build-manifest.ts: TS-over-YAML dedup with warning log
- build-manifest.ts: export scanTs/shouldReplaceManifestEntry for testing

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-22 01:01:21 +08:00
jakevin 7776db83d7 docs: consolidate adapter docs and discovery loading (#212) 2026-03-22 00:24:45 +08:00
plat1ko fae1dce027 feat(xueqiu): add earnings-date command (#211)
Add new YAML adapter to fetch upcoming earnings dates from xueqiu's
company events API (公司大事). Supports A-share and H-share stocks.

Features:
- Filter by subtype=2 (预计财报发布) from event timeline
- Show date, report name, and release status (/)
- --next flag to return only the closest upcoming earnings date
- --limit to control result count

Co-authored-by: nekomoto911 <nekomoto911@gmail.com>
2026-03-22 00:22:30 +08:00
jakevin d831b04d48 feat(browser): advanced DOM snapshot engine with 13-layer pruning pipeline (#210)
Core Changes:
- New dom-snapshot.ts: 13-layer LLM-optimized DOM pruning engine
  - Tag filtering, SVG collapse, ad/noise detection
  - CSS visibility, viewport threshold, paint-order occlusion
  - Shadow DOM traversal, same-origin iframe extraction
  - BBox parent-child dedup, attribute whitelist + synthetic attrs
  - Table → markdown serialization
  - Incremental diff (mark new elements with *)
  - data-opencli-ref annotation for precise click/type targeting
  - Hidden interactive element hints (scroll-to-reveal)

New APIs:
- IPage.scrollTo(ref) — scroll to snapshot-identified elements
- IPage.getFormState() — extract all form fields as structured JSON
- scrollToRefJs(), getFormStateJs() — standalone JS generators

Integration:
- Page (daemon) + CDPPage (direct CDP): use new engine as primary
- dom-helpers click/type: 4-layer fallback (data-opencli-ref → data-ref → CSS → index)
- Exports from browser/index.ts barrel

Testing:
- 21 new tests for dom-snapshot engine
- All 283 tests pass (29 files, 1.07s)
- Split test scripts: npm test (unit only), npm run test:all (full)
2026-03-22 00:11:51 +08:00
jakevin a22875814b refactor: replace hardcoded skipPreNav with declarative navigateBefore field (#208)
Browser adapters using COOKIE/HEADER strategy need the page on the target
domain so credentialed fetch() carries cookies. Previously, execution.ts
hardcoded `cmd.site === 'boss'` to skip this pre-navigation for adapters
that handle their own goto().

Now each adapter self-declares via `navigateBefore: false` on CliCommand.
This is more extensible — new sites that manage their own navigation just
add the field instead of editing execution.ts.

Changes:
- Add `navigateBefore?: boolean | string` to CliCommand interface
- Add `resolvePreNav()` helper in execution.ts (replaces hardcoded check)
- All 14 boss adapters declare `navigateBefore: false`
- Wire through discovery.ts (YAML + manifest) and build-manifest.ts
2026-03-21 23:52:29 +08:00
云比云 ae30763e9b refactor(boss): extract common.ts utilities, fix missing login detection (#200)
* refactor(boss): extract common utilities, fix missing login detection

- Add src/clis/boss/common.ts with shared helpers:
  - bossFetch(): unified XHR template with auto cookie-expiry detection (code 7/37)
  - navigateToChat()/navigateTo(): page navigation helpers
  - checkAuth()/assertOk(): centralized login state validation
  - fetchFriendList()/fetchRecommendList()/findFriendByUid(): data queries
  - clickCandidateInList()/typeAndSendMessage(): UI automation helpers
  - verbose(): conditional debug logging

- Refactor all 14 boss adapters to use common.ts:
  - chatlist.ts: was missing cookie-expiry check (fixes #login-detect)
  - chatmsg.ts: was missing cookie-expiry check (fixes #login-detect)
  - Remaining 12 adapters: deduplicated XHR boilerplate and error handling

- Fix execution.ts: skip redundant pre-navigation for TS adapters
  - TS adapters handle their own goto(), pre-navigating caused double
    page loads and could trigger duplicate login prompts
  - Pre-navigation preserved for YAML pipeline commands that need it

Net reduction: ~730 lines of duplicated code across boss adapters.
All 244 unit tests pass.

* fix(review): fix execution.ts pre-nav regression, sanitize UID input, restore docs

- execution.ts: use site-specific skip (boss only) instead of isYamlPipeline.
  The original check skipped pre-navigation for ALL TS adapters, but weread,
  chaoxing, and others don't do their own goto() and depend on it.
- common.ts: sanitize numericUid to digits-only and use JSON.stringify for
  safe interpolation in page.evaluate() (prevents template literal injection).
- resume.ts: restore HTML structure doc comments (scraping selector guide).
- send.ts: restore MQTT architecture note (explains why UI automation is needed).

* fix: restore DEBUG env support in verbose(), improve skipPreNav comment

- verbose() now checks both OPENCLI_VERBOSE and DEBUG=opencli,
  matching the original behavior from search.ts and detail.ts
- Clarify skipPreNav comment with TODO for future adapter-level flag

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-21 23:37:13 +08:00
jakevin 3669a89323 fix: fix social adapter bugs, sync docs, refactor boss common utils (#204)
* refactor(boss): extract common utilities, fix missing login detection

- Add src/clis/boss/common.ts with shared helpers:
  - bossFetch(): unified XHR template with auto cookie-expiry detection (code 7/37)
  - navigateToChat()/navigateTo(): page navigation helpers
  - checkAuth()/assertOk(): centralized login state validation
  - fetchFriendList()/fetchRecommendList()/findFriendByUid(): data queries
  - clickCandidateInList()/typeAndSendMessage(): UI automation helpers
  - verbose(): conditional debug logging

- Refactor all 14 boss adapters to use common.ts:
  - chatlist.ts: was missing cookie-expiry check (fixes #login-detect)
  - chatmsg.ts: was missing cookie-expiry check (fixes #login-detect)
  - Remaining 12 adapters: deduplicated XHR boilerplate and error handling

- Fix execution.ts: skip redundant pre-navigation for TS adapters
  - TS adapters handle their own goto(), pre-navigating caused double
    page loads and could trigger duplicate login prompts
  - Pre-navigation preserved for YAML pipeline commands that need it

Net reduction: ~730 lines of duplicated code across boss adapters.
All 244 unit tests pass.

* fix: fix social adapter bugs and sync docs with implementation

Instagram:
- Remove 6 non-existent commands from docs (like/unlike/comment/save/unsave/follow/unfollow)
- Fix usage examples to use positional args

Facebook:
- Remove 6 non-existent commands from docs (friends/groups/memories/events/add-friend/join-group)
- Fix search.yaml: URL encode query param, add missing url column
- Fix feed.yaml: add English locale support for engagement regex patterns

TikTok:
- Fix save/unsave: replace broken data-e2e="undefined-icon" with bookmark-icon/collect-icon
- Fix like/unlike: add state detection to prevent toggling (checks aria-label + computed color)
- Fix notifications: rewrite nested setTimeout to async/await
- Fix comment: add post-comment verification, throw on missing post button

---------

Co-authored-by: Wing Huang <huangsen365@gmail.com>
2026-03-21 23:11:24 +08:00
sline eb0ccaf549 feat(instagram,facebook): add write actions and extended commands (#201)
* feat(instagram,facebook): add write actions and extended commands

Instagram write actions (7 commands, internal REST API + CSRF token):
- like/unlike: like or unlike a user's post by username + index
- comment: comment on a user's post
- save/unsave: bookmark or remove bookmark on a post
- follow/unfollow: follow or unfollow a user

Facebook extended commands (6 commands, DOM scraping):
- friends: friend suggestions list
- groups: list your joined groups with last post time
- memories: On This Day memories
- events: browse event categories
- add-friend: send friend request by username
- join-group: join a group by ID

All commands tested with live data. 258 existing tests pass.

* docs: add adapter documentation for instagram, facebook, lobsters

* docs: add missing medium adapter documentation
2026-03-21 23:09:39 +08:00
AstroHan fbf051d539 fix(extension): skip chrome-extension:// tabs in resolveTabId fallback (#198)
* fix(extension): skip chrome-extension:// tabs in resolveTabId fallback

Remove the unsafe fallback that returned `tabs[0]` regardless of URL
type. When no web-accessible tab exists in the automation window (e.g.
a New Tab Override extension replaced about:blank with its own
chrome-extension:// page), we now always create a fresh about:blank
tab instead. This prevents chrome.debugger.attach from failing with
"Cannot access a chrome-extension:// URL of different extension".

Fixes #195, fixes #197

* refactor(extension): rename isWebUrl → isDebuggableUrl & reuse tabs in resolveTabId

Improvements over the original fix:

1. Rename isWebUrl() → isDebuggableUrl(): better reflects the intent —
   the function determines whether a URL can be attached via CDP, not
   just whether it's a "web" URL (about:blank is debuggable but not
   really a web URL).

2. Reuse existing non-debuggable tabs: when a New Tab Override extension
   replaces about:blank with chrome-extension://, use chrome.tabs.update()
   to navigate the existing tab to about:blank instead of creating a new
   one. This prevents orphan tab accumulation since chrome.tabs.create()
   may also get intercepted by the same extension.

3. Only fall back to chrome.tabs.create() when the window has zero tabs,
   which is the truly empty-window edge case.

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-21 23:05:06 +08:00
Kasumi fcff2e40be feat(grok): add opt-in --web flow for grok ask (#193) 2026-03-21 23:00:10 +08:00
sline 4391ccfcb8 feat(tiktok): add TikTok adapter with 15 commands (#202)
* feat(tiktok): add TikTok adapter with 15 commands

TikTok (15 commands, browser mode):

Read commands:
- profile: user profile info via rehydration script parsing
- search: search videos via internal search API
- explore: trending videos from explore page (DOM scraping)
- user: recent videos from a user page (DOM scraping)
- following: list accounts you follow
- friends: friend suggestions
- live: browse live streams with viewer counts
- notifications: activity notifications

Write commands (verified with real interactions):
- like/unlike: like or unlike a video by URL
- save/unsave: add or remove video from Favorites
- follow/unfollow: follow or unfollow a user
- comment: comment on a video

All write operations verified with live TikTok interactions.

* docs: add missing adapter documentation for doc-coverage CI
2026-03-21 22:57:56 +08:00
sline ce484c2a63 feat: add Lobste.rs, Instagram, and Facebook adapters (#199)
* feat(lobsters): add Lobste.rs adapter with hot, newest, active, tag commands

Add public API adapter for Lobste.rs (lobste.rs), a developer-focused
link aggregation community. All commands use the public JSON API and
require no authentication or browser.

Commands:
- hot: hottest stories
- newest: latest stories
- active: most active discussions
- tag: filter stories by tag (e.g. rust, security, programming)

* feat(instagram,facebook): add Instagram and Facebook adapters

Instagram (7 commands, browser mode - internal REST API):
- profile: user profile info (followers, following, posts, bio)
- search: search users
- user: recent posts from a user
- followers: list user's followers
- following: list user's following
- saved: saved posts
- explore: discover trending posts

Facebook (4 commands, browser mode - DOM scraping):
- profile: user/page profile info
- notifications: recent notifications
- feed: news feed posts
- search: search people, pages, posts

All commands require Chrome to be logged in to the respective site.
Instagram uses stable internal API endpoints with cookie auth.
Facebook uses DOM scraping via role attributes and semantic selectors.
2026-03-21 20:43:32 +08:00
VK 06c902aeed feat(medium): add medium adapter (#190) 2026-03-21 20:42:45 +08:00
AlexYue 1d39295f4b feat: plugin system (Stage 0-2)
* feat: plugin system (Stage 0-2)

- Stage 0: discoverPlugins() scans ~/.opencli/plugins/ at startup
- Stage 1: demo plugin repos (github-trending, hot-digest)
- Stage 2: opencli plugin install/uninstall/list commands
- package.json exports ./registry for TS plugin peerDep support
- 17 new/updated tests, tsc --noEmit clean

* fix: CDPBridge connect timeout unit mismatch (seconds vs ms)

opts.timeout is passed in seconds from runtime.ts but CDPBridge
was using it as milliseconds, causing instant timeout (30ms).

* feat: add registry-api public entry point for TS plugin peerDep support

- Add src/registry-api.ts: re-exports core registration API (cli, Strategy,
  getRegistry) without transitive side-effects, safe for plugin imports
- Update package.json exports: './registry' -> './dist/registry-api.js'
- Update src/registry.ts: use globalThis shared registry to ensure single
  instance across npm-linked plugin modules
- Update .gitignore for plugin-related artifacts

* fix: symlink host opencli into plugin node_modules on install

After npm install, replace the npm-installed @jackwener/opencli
with a symlink to the running host's package root. This ensures
TS plugins always resolve '@jackwener/opencli/registry' against
the host installation, avoiding version mismatches when the
published npm package lags behind.

* fix: transpile TS plugins to JS on install, deduplicate .ts/.js discovery

- installPlugin: after symlinking host opencli, transpile any .ts files
  to .js using esbuild from the host's node_modules/.bin/
- discoverPluginDir: skip .ts files when a .js sibling exists (production
  node cannot load .ts directly)
- scanPluginCommands: deduplicate basenames via Set to avoid showing
  'aggregate, aggregate' when both .ts and .js exist

* docs: add plugin system user guide

- New docs/guide/plugins.md covering:
  - Installation/uninstallation commands
  - Creating YAML plugins (zero-dep)
  - Creating TS plugins (with peerDep)
  - TS plugin install lifecycle (clone → deps → symlink → transpile)
  - Example plugins and troubleshooting
- Add Plugins to VitePress sidebar (EN + ZH)
- Link from getting-started.md Next Steps

* fix: address review issues in plugin system

- Security: replace execSync with execFileSync to prevent shell injection
- Replace deprecated npm --production with --omit=dev
- Tighten parseSource regex to [\w.-]+ to reject special chars
- Fix ZH sidebar plugin link (/guide/plugins → /zh/guide/plugins)
- Return plugin name from installPlugin() to avoid duplicated logic
- Use execFileSync for esbuild transpilation
- Fix misleading comment in linkHostOpencli

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-21 19:41:15 +08:00
jakevin 50ec7c6868 fix(docs): remove dead link to deleted github adapter (#194) 2026-03-21 13:48:49 +08:00
jackwener 644b8bcbd4 chore: remove github adapter (covered by gh CLI hub) 2026-03-21 10:55:03 +08:00
jackwener 4e274a92b7 v1.1.1
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-21 10:49:39 +08:00
jakevin d1ade61e8c fix(twitter): rewrite trending from YAML to TS with DOM scraping fallback (#189)
* fix(twitter): rewrite trending from YAML to TS with DOM scraping fallback

The old REST API /i/api/2/guide.json returns 503. Replace with a TS
adapter that:
- Tries legacy guide.json API first (with proper auth headers)
- Falls back to DOM scraping via [data-testid='trend'] elements
- Filters out promoted content
- Follows the same Strategy.COOKIE pattern as timeline.ts

* fix: use 'help' instead of 'description' in Arg (matches Arg interface)

* docs(steam): add adapter documentation, update READMEs

- Create docs/adapters/browser/steam.md
- Add steam entry to README.md and README.zh-CN.md
- Fixes doc-coverage CI check (44/44)
2026-03-21 10:44:32 +08:00
jakevin 5d84c6f63e Merge pull request #187 from yanCode/codex/fix-apple-podcasts
Fix Apple Podcasts search query handling and top chart failures
2026-03-21 10:32:03 +08:00
Noah cbd50ccb91 feat(steam): add top sellers command (#178)
YAML adapter for Steam Store top selling games via public API.
Displays game name, price (in cents), discount %, and store URL.

Made-with: Cursor
2026-03-21 10:30:20 +08:00
Alex Yang 3f16d42e27 feat(twitter): add block, unblock, and hide-reply commands (#182)
Add three new Twitter/X UI-strategy commands:
- `block` / `unblock` — block or unblock a user by username
- `hide-reply` — hide a bot/spam reply on your own tweet thread
2026-03-21 10:27:22 +08:00
Sheng-Yan, Zhang c8e8c773c0 Fix apple-podcasts search and top handling 2026-03-21 09:24:41 +08:00
jakevin 5de920a994 docs: fix additional issues found in deep review (#181)
- Add missing codex/dump to README command tables
- Fix reddit subreddit --name → positional
- Update architecture.md for PR #152 engine.ts split:
  engine.ts → discovery.ts + execution.ts + commanderAdapter.ts
- Add intercept and ui strategies to auth table
2026-03-21 03:10:05 +08:00
Yunxiao_Li 476fe115de docs(chatgpt): sync read docs with AX behavior (#180) 2026-03-21 03:00:34 +08:00
jakevin 8d45019119 docs: sync documentation with PR #150 arg renames and positional changes (#179) 2026-03-21 02:59:22 +08:00
jackwener 36cf3067f7 feat: register gws CLI + use Commander passThroughOptions for external CLI passthrough (closes #147) 2026-03-21 02:29:36 +08:00
jakevin 516f1be3e6 refactor: deep CLI layer architecture improvements (#164)
CLI Layer:
1. execution.ts: auto-manages browser sessions, simplified signature
2. runtime.ts: add getBrowserFactory()
3. serialization.ts: new module for serialization helpers
4. cli.ts: format all built-in commands, extract inferHost()
5. commanderAdapter.ts: pure thin adapter

Src-wide cleanup:
6. download/index.ts: shared VIDEO_PLATFORM_DOMAINS, reuse isBinaryInstalled
7. Move site helpers: coupang/bilibili/chaoxing.ts -> clis/*/utils.ts
8. explore.ts: decompose into analyzeEndpoints, inferCapabilities, writeArtifacts

244 tests pass. 240 commands registered.
2026-03-21 02:24:12 +08:00
jakevin d556eeb512 refactor: deep CLI layer architecture improvements (#152)
1. execution.ts: executeCommand auto-manages browser sessions
   - Signature simplified: (cmd, kwargs, debug) — callers dont handle browser
   - Internal runCommand() does lazy-loading, func/pipeline dispatch
   - shouldUseBrowserSession + domain pre-nav moved here from adapter

2. runtime.ts: add getBrowserFactory()
   - Eliminates 4x duplicate CDPBridge/BrowserBridge selection

3. serialization.ts: new module (79 LOC)
   - serializeArg, serializeCommand, formatArgSummary, formatRegistryHelpText
   - registry.ts re-exports for backward compat (160 -> 96 LOC)

4. cli.ts: format all built-in commands
   - Un-compressed explore/generate/cascade from 300-char single lines
   - Extracted inferHost() helper
   - Uses getBrowserFactory() instead of inline CDPBridge selection
   - Clear section comments

5. commanderAdapter.ts: pure thin adapter (113 LOC)
   - Only does: arg collection → executeCommand → renderOutput
   - Zero browser/session/strategy logic

All 244 tests pass. No behavioral changes.
2026-03-21 02:09:46 +08:00
jakevin d7c895592f refactor!: standardize all CLI arg names to kebab-case (#150)
* refactor!: standardize all CLI arg names to kebab-case

BREAKING CHANGE: All CLI argument names have been renamed for consistency.

Renames:
- keyword -> query (11 search commands)
- bookId -> book-id (weread)
- productId -> product-id (coupang)
- post_id -> post-id (reddit)
- job_id -> job-id (boss)
- tweet_id -> tweet-id (twitter)
- note_id -> note-id (xiaohongshu)
- security_id -> security-id (boss)
- model_name -> model-name (chatwise/codex/cursor)
- max_length -> max-length (reddit)
- experience_level -> experience-level (linkedin)
- job_type -> job-type (linkedin)
- date_posted -> date-posted (linkedin)

36 renames across 32 files. All 240 commands now use kebab-case.

* refactor!: standardize positional vs --named arg style

BREAKING CHANGE: Required "subject" args are now positional.

Rules applied:
- query, id, text, url, username → positional (when required)
- output → always --named

Also fixes engine.ts YAML arg parser to support positional property.

50 changes across 49 files. All 240 commands verified consistent.
2026-03-21 01:53:11 +08:00
jakevin eeace115cb fix: harden external CLI hub — command injection, denylist, sync API, build-copy (#149) 2026-03-21 01:26:02 +08:00
jakevin 35676a101f refactor: extract serialization helpers to registry.ts and stabilize arg schema (#148)
- Add serializeArg() with stable schema (all fields always present)
- Add serializeCommand() for structured output (json/yaml)
- Add formatArgSummary() for human-readable arg display (<required> [--optional])
- Add formatRegistryHelpText() for --help appendix
- Refactor cli.ts to use these shared helpers (~30 lines removed)
- Non-structured formats now show arg signatures instead of comma-joined names
2026-03-21 01:21:18 +08:00
AstroHan cd0c6f874e feat: enhance --help with registry metadata and enrich list --json with full arg schema (#142)
* feat: add `opencli describe` command for unified CLI capability discovery

Add a new `describe` command that helps AI agents discover and understand
both built-in site commands and external CLI tools through a single entry point.

- Built-in commands: reads structured data from CliCommand registry
  (args with type/choices/default, columns, strategy, domain)
- External CLIs: collects help text via `binary --help`, extracts
  subcommand names + summaries, passes through raw help text
- Supports `--format json` for programmatic consumption by AI agents
- Graceful degradation: parse failures return raw help text, uninstalled
  CLIs show install instructions without triggering auto-install

Closes #141

* fix: address code review findings for describe command

- Strip trailing colons from Cobra-style subcommand names (browse: → browse)
- Use CliError instead of bare Error for consistent error handling with hints
- Remove decorative section separators to match project comment style
- Validate --format flag (text/json only) with clear error message
- Truncate raw help output to 50 lines to prevent excessive output
- Add deduplication test for multi-section command groups

* refactor: replace describe command with enhanced --help and list --json

Per maintainer feedback, remove the standalone `describe` command and instead:

1. Enhance --help for all built-in commands:
   - Show argument choices (from registry, not shown by Commander)
   - Show execution metadata: Strategy / Browser / Domain
   - Show output columns

2. Enhance `list -f json/yaml` with full argument schema:
   - args field now includes type, required, positional, choices, default, help
   - Added columns and domain fields for structured formats
   - Table/csv/md formats unchanged (args remain comma-joined names)

This follows the principle that --help is the standard CLI discovery
mechanism and AI models already know to use it.

* fix: stabilize JSON schema and fix positional choices rendering

- Always output columns/domain in json/yaml ([] and null when empty)
- Use <name> instead of --name for positional args with choices
- Remove extra blank line when no choices args present
2026-03-21 01:12:05 +08:00
ajia1206 0b71c6c4da fix: correct xiaohongshu creator metric parsing (#146) 2026-03-21 01:02:51 +08:00
Kasumi 7700704923 feat: add Bloomberg adapter (#145)
* feat: add Bloomberg adapter with RSS feeds and article extraction

* refactor: improve Bloomberg adapter review fixes

- news.ts: reorder flow (goto before loadStory), increase wait times for slow hydration, add clarity comments
- utils.ts: clarify validateBloombergLink regex (use non-capturing group)
- build-manifest.ts: log warning on scanTs parse failure (match scanYaml pattern)
- public-commands.test.ts: use it.each for section RSS tests (better isolation & reporting)

---------

Co-authored-by: ByteYue <yj976240184@gmail.com>
2026-03-21 00:35:30 +08:00
jackwener 4d3b972d67 feat: auto-discover and dynamically register any local CLI on the fly 2026-03-20 23:00:27 +08:00
AlexYue 15d3583c60 docs: add missing adapter docs, fix sidebar 404s, add doc-check CI (#140)
* docs: add missing adapter docs, fix sidebar 404s, add doc-check CI

- Add doc pages for 11 undocumented adapters: arxiv, barchart,
  chaoxing, grok, hf, jike, jimeng, linux-do, sinafinance,
  stackoverflow, weread, wikipedia
- Update adapters/index.md with all new adapter entries
- Update VitePress sidebar config with 12 new entries
- Remove broken zh/ sidebar refs (troubleshooting, testing)
- Add doc-check CI workflow (adapter coverage + build + link check)
- Add scripts/check-doc-coverage.sh for adapter doc enforcement
- Enhance PR template with adapter doc checklist

* fix(ci): use --root-dir instead of --base for lychee link checker

lychee v0.23 requires --base to be a URL or absolute path.
Use --root-dir for resolving root-relative links in local files.

* fix(ci): remove lychee link-check job, rely on VitePress build

VitePress links use extension-less paths (e.g. /adapters/browser/twitter)
which lychee cannot resolve. The docs-build job already catches all
broken internal links via VitePress dead link detection during build.
2026-03-20 22:08:38 +08:00
AlexYue 53a95ed0ce Open ci: migrate docs deployment to cross-repo build via opencli-website (#138) 2026-03-20 20:55:08 +08:00
jackwener 0a2591842c docs: emphasize AI agent integration via AGENT.md 2026-03-20 20:50:34 +08:00
jakevin d9a71da596 chore(main): release 1.1.0 (#134) 2026-03-20 20:40:42 +08:00
jackwener 9a79501bfd 1.1.0
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-20 20:37:30 +08:00
jackwener 46d0f00aa6 chore: bump SKILL.md version to 1.1.0 2026-03-20 20:37:29 +08:00
jackwener b3e32d8a05 feat: add external CLI hub for discovery, auto-installation, and execution of external tools. 2026-03-20 20:30:40 +08:00
jackwener 36bc57a962 fix(serve): update model mappings to match actual Antigravity UI
- Default to 'claude sonnet 4.6'
- Map 'sonnet' -> 'claude sonnet 4.6'
- Map 'opus' -> 'claude opus 4.6'
- Map 'gemini.*pro' -> 'gemini 3.1 pro (high)'
- Map 'gemini.*flash' -> 'gemini 3 flash'
- Map 'gpt' -> 'gpt-oss 120b'
2026-03-20 18:55:45 +08:00
jackwener 0e8c96b6d9 feat(serve): implement auto new conv, model mapping, and precise completion detection
- Auto-click New Conversation if session has only 1 message
- Map Anthropic models (claude-3-7-sonnet) to Antigravity UI models
- Refactor waitForReply to check for Cancel/Stop button presence to
  detect generation completion reliably, with text stability fallback
2026-03-20 18:52:00 +08:00
jackwener c63af6d418 feat(serve): use CDP mouse click + Input.insertText for reliable message injection
- Replace document.execCommand (deprecated) with CDP Input.insertText
- Use Input.dispatchMouseEvent to physically click + focus the Lexical editor
  before text injection (fixes focus issues with JS-only .focus())
- Improve getLastAssistantReply: strip echoed user message, thinking blocks,
  Copy button text, and de-duplicate repeated content artifacts
2026-03-20 18:24:49 +08:00
jackwener 35a0fed8a0 feat: add antigravity serve command — Anthropic API proxy
- New command: opencli antigravity serve --port 8082
- Starts HTTP server compatible with Anthropic /v1/messages API
- Connects to Antigravity via CDP (OPENCLI_CDP_ENDPOINT)
- Uses Input.dispatchKeyEvent for reliable Enter key submission
- Polls for reply with text-change detection + 3s stability check
- Precise DOM walker for extracting last assistant reply
- Lazy CDP connection (connects on first request)
- Auto-reconnect on CDP connection loss
- CORS headers for Claude Code compatibility

Usage:
  OPENCLI_CDP_ENDPOINT=http://127.0.0.1:9224 opencli antigravity serve
  ANTHROPIC_BASE_URL=http://localhost:8082 claude
2026-03-20 18:16:41 +08:00
jackwener 593436e4cb fix(xiaohongshu): use fixed UTC+8 offset in trend timestamp formatting (CI timezone fix) 2026-03-20 17:57:31 +08:00
jackwener 02793e990e feat: add sinafinance 7x24 news adapter (#131)
Based on PR #131 by larria, with fixes applied:
- Renamed command 724 → news for clarity
- Fixed indentation to 2-space project standard
- Added SinaNewsItem type (removed item: any)
- Added res.ok check + CliError for HTTP failures
- Added stripHtml() for rich_text content
- Updated README, README.zh-CN, SKILL.md

Co-authored-by: larria <1115524+larria@users.noreply.github.com>
2026-03-20 17:22:42 +08:00
jackwener 03f067d907 fix: use UTC+8 for XHS timestamp formatting (CI timezone fix)
formatPostTime() used local timezone methods, causing test failure
on UTC CI servers. XHS API timestamps are Beijing time (UTC+8),
so use explicit UTC offset with getUTC*() methods.
2026-03-20 17:13:00 +08:00
云比云 7e973ca592 feat(boss): add 8 new recruitment management commands (#133)
New commands:
- joblist: view my published jobs
- recommend: view recommended candidates (new greetings list)
- greet: send greeting to initiate chat with candidate
- mark: add/remove labels on candidates
- invite: send interview invitation
- stats: job statistics (chats count)
- batchgreet: batch greet recommended candidates
- exchange: request phone/wechat exchange

All commands tested and build passing (222 entries).
2026-03-20 17:10:48 +08:00
jackwener 4600b9d46d fix: type safety for wikiFetch and arxiv abstract truncation
- wikiFetch return Promise<unknown> instead of Promise<any>
- Add WikiSearchResult type, remove r: any
- Type wikiFetch responses with inline type assertions
- Only append ... to abstract when actually truncated
2026-03-20 17:08:30 +08:00
BruceLoveDecimal 3cda14a2ab feat: add arxiv and wikipedia adapters (#132)
Add arXiv (search, paper) and Wikipedia (search, summary) public API adapters.

- arxiv/search: search papers by keyword
- arxiv/paper: get paper details by ID  
- wikipedia/search: search articles with lang support
- wikipedia/summary: get article summary

Type safety fixes applied: wikiFetch returns unknown, typed search results.

Co-authored-by: BruceLoveDecimal <39156883+BruceLoveDecimal@users.noreply.github.com>
2026-03-20 17:08:17 +08:00
jackwener 4f74b45963 refactor: remove raw CDP code, use IPage throughout
- Remove fetchCreatorNotesByCdp() and captureNoteDetailApiPayload() raw
  WebSocket code (~240 lines) — adapters should use IPage, not raw CDP
- Replace direct CDP WebSocket with IPage.evaluate() in-page fetch
- Fix page: any → IPage in all function signatures
- Simplify to two-tier fallback: API+interceptor → DOM parse
- Rebase onto latest main (resolves cdp.ts/daemon.ts conflicts)
2026-03-20 16:34:08 +08:00
ajia1206 8f1725982e feat: xiaohongshu creator flows migration (#124)
Migrated xiaohongshu creator flows to v1.0.2+.
- creator-notes with API + DOM fallback
- creator-note-detail with audience/trend data
- creator-notes-summary batch overview
- Tests: 3 files, 9 tests

Co-authored-by: ajia <491387123@qq.com>
2026-03-20 16:33:48 +08:00
AlexYue 2876750891 fix(docs): use base '/' for custom domain and add CNAME file (#129)
- Change VitePress base from '/opencli/' to '/' for custom domain opencli.info
- Add docs/public/CNAME so GitHub Pages preserves custom domain on re-deploy
2026-03-20 16:31:28 +08:00
jakevin 4ab4f88bcd chore(main): release 1.0.6 (#128)
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-20 16:25:30 +08:00
AlexYue 9eb7a1eaa1 docs: add VitePress documentation site with GitHub Pages deployment (#127)
* docs: deduplicate documentation — single source of truth in docs/

- Remove root CDP.md, CDP.zh-CN.md, CLI-ELECTRON.md (now in docs/advanced/)
- Slim adapter READMEs to one-liner + link to docs/ (11 files)
- Update README.md adapter table links to point to docs/

* docs: set VitePress base path for GitHub Pages deployment
2026-03-20 16:08:34 +08:00
Chencheng Li 4cabca12df fix: use %20 instead of + for spaces in Bilibili WBI signed requests (#126)
URLSearchParams.toString() encodes spaces as +, but Bilibili's WBI
signature verification expects %20. This mismatch causes search
queries with spaces (e.g. "亚马逊 滞销产品") to fail with
TypeError: Failed to fetch due to CORS-blocked error responses.

Fixes #125

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:07:46 +08:00
jackwener fafa990acd v1.0.5
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-20 15:51:52 +08:00
jackwener 3bde01aa1c fix: prevent duplicate command registration crash
The build manifest includes antigravity/serve which collides with the
hardcoded antigravity serve in cli.ts. Add a guard to skip registry
entries whose subcommand already exists in the site group.
2026-03-20 15:51:51 +08:00
jackwener 152cc48091 v1.0.4
Release / release (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
2026-03-20 15:36:06 +08:00
jackwener dff8f1e9c4 refactor: deep audit fixes P0-P3
P0: page.ts screenshot async I/O, cdp.ts send() 30s timeout
P1: cdp.ts event-based goto, implement scroll/screenshot/networkRequests,
    extract dom-helpers.ts shared module for Page/CDPPage
P2: engine.ts readdir withFileTypes, explore.ts parallel refetch
P3: registry.ts strategy ordering, output.ts CSV \r escape,
    interceptor.ts error tracking array
2026-03-20 15:27:07 +08:00
jackwener 9d8b6441be feat: Add antigravity serve command to start an Anthropic-compatible API proxy server for Antigravity via CDP. 2026-03-20 14:31:13 +08:00
Kasumi 1e0e4cd660 fix(manifest): infer browser mode for public TS adapters (#115) 2026-03-20 14:24:26 +08:00
K1tyoo 024d9908b3 feat(hf): add top command for hf papers (daily, weekly, monthly) (#110)
* feat(hf): add top command for hf papers (daily, weekly, monthly)

* feat(footer): add footerExtra support and derive dates from API response

Add footerExtra callback to CliCommand for custom table footer content.
For weekly/monthly periods, derive date range from API response publishedAt
field with local clock fallback.

* fix: truncate long paper titles

* refactor(hf): remove comments column for consistent output

* feat(hf): add --all flag to return all papers

* feat(hf): add paper id column to output

* fix: restore main.ts as bootstrap, sync footerExtra + CDPBridge + domain pre-nav to cli.ts

- main.ts should remain a lightweight entry point delegating to cli.ts
- Preserve CDPBridge fallback (OPENCLI_CDP_ENDPOINT) — PR had hardcoded BrowserBridge only
- Add domain pre-navigation for cookie/header strategies to cli.ts
- footerExtra feature from PR is properly integrated

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-20 14:23:06 +08:00
jackwener 943e286815 chore: track package-lock.json for CI reproducibility 2026-03-20 14:14:01 +08:00
AlexYue 31f58ae699 docs: add VitePress documentation site (#112)
- Add VitePress with full navigation, sidebar, i18n (en/zh), local search
- Create 50+ doc pages: guide, adapters (browser + desktop), developer, advanced
- Migrate content from README.md, CONTRIBUTING.md, TESTING.md, CDP.md, CLI-ELECTRON.md
- Migrate all 11 adapter READMEs to structured documentation
- Add new pages: architecture, yaml-adapter guide, ts-adapter guide, ai-workflow
- Add GitHub Actions workflow for deploying to GitHub Pages
- Add Chinese locale pages (getting-started, installation, browser-bridge, etc.)
- Add docs:dev, docs:build, docs:preview npm scripts
2026-03-20 14:11:56 +08:00
ylongwang 812db29ed8 fix(smzdm): navigate to search page directly instead of deprecated ajax API (#113)
The old adapter called `search.smzdm.com/ajax/?c=<channel>&s=<q>` which
now returns 404. This caused opencli smzdm search to always return empty
results regardless of keyword.

Fix: navigate directly to `search.smzdm.com/?c=home&s=<keyword>&v=b`
and scrape the rendered DOM via querySelectorAll('li.feed-row-wide').

Also switched from async IIFE to sync IIFE since all data is already in
the DOM after page load — no fetch needed.

Tested: opencli smzdm search --keyword A7M5 returns correct results
with prices and mall names.
2026-03-20 14:11:20 +08:00
jackwener 47a898125f feat: add conservative capability routing 2026-03-20 14:10:51 +08:00
VK b60c69950d feat(stackoverflow): add search, hot, unanswered, and bounties commands (#116) 2026-03-20 14:10:15 +08:00
Wing Huang ce38a1604e feat(boss): add resume command to view candidate profile (#119)
Adds 'opencli boss resume --uid <uid>' command that scrapes the chat page
right panel to display candidate resume information including:
- Basic info: name, gender, age, experience, degree, active status
- Work history: time period + company + position
- Education: time period + school + major + degree
- Job being discussed and candidate expectations

Uses UI scraping approach since BOSS Zhipin does not expose a public API
for candidate resume data on the recruiter side.
2026-03-20 14:08:29 +08:00
AstroHan d6e0aa120b feat(jike): add Jike adapter with 10 commands (#117)
Add comprehensive Jike (即刻) adapter covering read and write operations.

Read commands:
- user: user posts via m.okjike.com SSR JSON
- topic: topic/circle posts via m.okjike.com SSR JSON
- post: post detail with comments via m.okjike.com SSR JSON
- feed: home timeline via React fiber tree extraction
- search: search posts via React fiber tree extraction
- notifications: notification list via DOM innerText parsing

Write commands (Strategy.UI, browser DOM automation):
- create: publish post via inline compose box
- comment: comment on post via contenteditable paste
- like: like post via _likeButton_ div click
- repost: repost via action bar → popover menu → confirm

Implementation details:
- Three data extraction strategies: SSR JSON, React fiber, DOM manipulation
- Shared JikePost interface and getPostData helper in shared.ts
- All evaluate blocks include try/catch error handling
- Two rounds of parallel Claude + Codex code review applied
2026-03-20 14:08:12 +08:00
jackwener 44f0bbe94d feat: add workspace-aware browser sessions 2026-03-20 13:41:04 +08:00
jackwener 0ea6e4a15c fix: address review findings and docs cleanup 2026-03-20 12:28:30 +08:00
jackwener ee35ee723f chore: release version 1.0.3
Build Chrome Extension / build (push) Has been cancelled
Release / release (push) Has been cancelled
2026-03-20 11:27:56 +08:00
jackwener 92fc13d60e docs: update extension installation instructions 2026-03-20 11:22:39 +08:00
jackwener f5f7a9500e chore: rename extension to OpenCLI 2026-03-20 11:03:02 +08:00
jackwener 3229294f08 ci: remove redundant build step in release workflow 2026-03-20 11:01:11 +08:00
jackwener f945b51f43 ci: fix non-existent v6 actions causing workflows to fail instantly 2026-03-20 10:59:34 +08:00
jackwener e9a3ef7538 chore: merge feature/ext-github-action and resolve conflicts 2026-03-20 10:57:37 +08:00
jackwener 39b6413e47 Merge branch 'refactor/remove-any'
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2026-03-20 10:56:46 +08:00
jackwener 33957ea0ba chore: save local changes 2026-03-20 10:56:19 +08:00
jackwener 691f835bdf chore: ignore extension build artifacts and pem keys 2026-03-20 10:54:03 +08:00
jackwener 5b447e7a11 refactor: strictly type output and registry pipelines, removing any where possible 2026-03-20 10:53:25 +08:00
jackwener d7bf5d6e04 ci: add github action for building extension zip and crx 2026-03-20 10:51:27 +08:00
AlexYue 390dbe7199 fix: use JSON.stringify for safe JS string interpolation in evaluate() (#109)
Replace ad-hoc string escaping with JSON.stringify() for values
interpolated into JavaScript code strings passed to page.evaluate().

- explore.ts: clickLabels were escaped with only single-quote
  replacement, which breaks on labels containing backslashes or
  newlines. JSON.stringify() handles all edge cases correctly.

- synthesize.ts: buildEvaluateScript() embedded URLs directly inside
  single quotes. JSON.stringify() safely handles URLs containing
  special characters.
2026-03-20 10:47:09 +08:00
jackwener 40846291e6 feat: introduce opencli command-line interface with web exploration, generation, and validation tools, and refactor browser utilities. 2026-03-20 10:47:05 +08:00
Wing Huang f8dea7d8fc feat(boss): add chatlist, chatmsg, and send commands (#95)
- boss/chatlist: List chat conversations (招聘端聊天列表)
  Uses getBossFriendListV2 API with pagination and job filter support.

- boss/chatmsg: Read chat message history with a candidate
  Resolves encryptUid to numeric uid/securityId, fetches via historyMsg API.

- boss/send: Send chat message to a candidate via UI automation
  BOSS chat uses MQTT protocol (not HTTP), so this command automates the
  web chat UI: clicks on user in list → types in contenteditable editor →
  clicks the send button.

All three commands use Strategy.COOKIE and require an active BOSS直聘
login session in Chrome.
2026-03-20 10:40:20 +08:00
Yunxiao_Li 67474bb6db fix(chatgpt): read replies from AX tree instead of clipboard shortcut (#106) 2026-03-20 10:28:27 +08:00
AstroHan d1986f0144 fix(twitter): replace search input approach with pushState+popstate SPA navigation (#105)
The previous approach (nativeSetter + Enter keydown on the search input)
does not reliably trigger Twitter's form submission - the synthetic
KeyboardEvent is ignored by React, leaving the page on /explore with
zero API calls captured.

Use history.pushState + PopStateEvent instead, which triggers React
Router's listener and performs a true SPA navigation to /search.
The interceptor survives because no full page reload occurs.

Tested: "opencli", "it's a test" (single quote), "hello" all return
results with correct author attribution.
2026-03-20 10:20:38 +08:00
zhutiancillm ebc5c09ad9 fix(twitter): fix newline handling in post command via clipboard paste (#107)
Co-authored-by: zhutiancillm <zhutiancillm@users.noreply.github.com>
2026-03-20 10:20:13 +08:00
bhwang 055403abc1 docs: correct note_id params for xiaohongshu (#108) 2026-03-20 10:19:50 +08:00
jackwener 10af754c89 docs: align CDP release notes 2026-03-20 00:51:55 +08:00
jackwener a7e5307226 feat: Add Chrome DevTools Protocol (CDP) support as an alternative browser automation backend, configurable via OPENCLI_CDP_ENDPOINT.
Release / release (push) Has been cancelled
2026-03-20 00:48:39 +08:00
jackwener c21250bd88 1.0.1 2026-03-20 00:29:10 +08:00
dev-Flyblue 0fa9573790 feat: add Chaoxing (学习通) adapter — assignments & exams (#101)
Add CLI commands to view Chaoxing assignments and exams by reusing
Chrome login session via the Browser Bridge.

Chaoxing has no flat API for listing assignments/exams. The adapter
follows the browser flow: establish session → fetch course list via
backclazzdata API → enter each course via stucoursemiddle redirect →
click tab to capture iframe URL → navigate and parse DOM.

Commands:
  opencli chaoxing assignments [--course <name>] [--status] [--limit]
  opencli chaoxing exams [--course <name>] [--status] [--limit]

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 00:14:08 +08:00
AstroHan 65e30b9ac4 fix(intercept): IIFE wrapping for installInterceptor/getInterceptedRequests (#100)
* fix(intercept): use evaluate() for IIFE wrapping in installInterceptor/getInterceptedRequests

Root cause: daemon migration changed these methods from this.evaluate()
to direct sendCommand('exec'), losing the wrapForEval() IIFE wrapping.
CDP received bare arrow functions that were never invoked.

Fixes #98

* fix(twitter): SPA navigation, data path, and author resolution for INTERCEPT commands

- followers/following: install interceptor on profile page, then click
  followers/following link (SPA navigation preserves JS context).
  Use JSON.stringify for targetUser to prevent injection. Throw on
  navigation failure. Update selector: /verified_followers.
- notifications: install interceptor on home, then pushState+popstate
  to /notifications. Validate navigation URL.
- search: fix author resolution (core.screen_name, not legacy).
- All: fix GraphQL data path (remove extra .data level), update author
  resolution to try core.screen_name before legacy.screen_name.
- followers: remove erroneous .filter(r => r?.url) — interceptor stores
  response body JSON, URL filtering happens at capture time.
2026-03-20 00:13:20 +08:00
jackwener 540f3c677a docs: add desktop app adapters section to root READMEs
Integrate README links for all 10 desktop app CLI adapters:
- Cursor, Codex, Antigravity, ChatGPT, ChatWise
- Notion, Discord, Feishu, WeChat, NeteaseMusic
2026-03-20 00:10:20 +08:00
jackwener b5c1b242e2 fix(extension): use idle-timeout for automation window lifecycle
Replace eager close-window (which caused race conditions when
parallel commands shared the window) with an idle-based timer:

- Window auto-closes 30s after the last command completes
- Each incoming command resets the idle timer
- Consecutive commands reuse the same window (faster)
- No race conditions with parallel execution
- Close-window action kept for explicit cleanup if needed
2026-03-20 00:05:48 +08:00
jackwener 2f6d28a3e9 feat(extension): auto-close automation window after command completes
- Add 'close-window' action to extension protocol and background.ts
- Add Page.closeWindow() method to send close-window command
- browserSession() now closes automation window in cleanup
- Remove domain pre-navigation + 2s wait from main.ts (CDP handles
  cross-domain cookies natively, no same-origin workaround needed)
- Net effect: commands run faster, no stale windows left behind
2026-03-19 23:59:31 +08:00
jackwener fde618063f chore: pre-release cleanup
- Delete unused extension/src/executor.ts (chrome.scripting experiment)
- Remove 15 no-op backward-compat exports from doctor.ts
- Remove getTokenFingerprint no-op from browser/index.ts
- Rename PlaywrightMCP → BrowserBridge across all source files
  (backward-compat alias kept in mcp.ts and browser/index.ts)
- Remove unnecessary host_permissions from extension manifest
- Sync extension package.json version to 0.2.0
- All 14 tests pass
2026-03-19 23:51:36 +08:00
jackwener 89947fee50 feat(extension): isolated automation window
All opencli operations now run in a dedicated Chrome window instead
of hijacking the user's active tab. The automation window:
- Created on first command via chrome.windows.create({ focused: false })
- 1280x900 viewport, auto-cleaned up when closed
- All tabs resolved within this window only
- User's main browsing session is never touched

Tested: twitter trending , zhihu hot 
2026-03-19 23:34:27 +08:00
jackwener 2e962b2e7c chore: pre-release cleanup
- Fix daemon per-command timeout: 30s → 120s (was shorter than CLI-layer timeouts)
- Remove debug command: grok/debug.ts
- Sync extension version to 1.0.0
- Rename PlaywrightMCP → BrowserBridge (keep backward-compat alias)
- Add accept/reply-dm to README command tables
- Clean up consoleMessages() JSDoc in page.ts
- Update runtime.ts comment
2026-03-19 22:49:54 +08:00
jackwener 13e2345089 feat(twitter): add scroll-to-load for accept and reply-dm
Both commands now scroll the conversation list to load more items
before processing. Scrolls up to 20-30 times, stops after 3
consecutive scrolls with no new items loaded.

Previously limited to ~14 visible conversations, now loads as many
as needed (up to --max).
2026-03-19 22:03:36 +08:00
jackwener 4bf946edaf feat(twitter): add accept and reply-dm commands
accept: Auto-accept DM requests matching keywords (comma-separated OR)
  opencli twitter accept --keyword '群,微信' --max 20

reply-dm: Send message to recent DM conversations with skip-replied
  opencli twitter reply-dm --text '我的微信 wxkabi' --max 20

Both commands:
- Use click-based DOM interaction (data-testid selectors)
- 10-minute timeout for batch operations
- Support new Twitter /i/chat UI and /messages URL
2026-03-19 21:56:30 +08:00
jackwener 4398202b05 fix(twitter): rewrite accept command + per-command timeout
- Rewrite accept.ts: use [data-testid=conversation] click-based approach
  instead of extracting href links (requests page has no /messages/xxx links)
- Support comma-separated keywords for OR matching (e.g. '群,微信')
- Add timeoutSeconds: 600 (10 min) for batch DM operations
- Bump default OPENCLI_BROWSER_COMMAND_TIMEOUT from 45s to 60s
- Track visited conversations to avoid infinite loops
2026-03-19 21:37:34 +08:00
jackwener b01bf6769b feat(twitter): add accept command to auto-accept DM requests by keyword
Usage:
  opencli twitter accept --keyword '微信' --max 20

Workflow:
1. Navigate to /messages/requests
2. Click into each conversation
3. If message contains keyword, click Accept
4. After accept (auto-redirects to /messages), go back to requests
5. Repeat until --max reached or no more matches
2026-03-19 21:26:08 +08:00
jackwener 6d3e595d36 fix: daemon spawn uses --import tsx/esm for dev mode .ts files
process.execPath is always plain 'node' even under tsx,
so .ts files could not be executed. Use --import tsx/esm
flag to enable TypeScript loading in spawned daemon.
2026-03-19 21:05:48 +08:00
AstroHan edb21ca67b feat: add WeRead (微信读书) adapter with 7 commands (#89)
Add weread adapter for issue #82, covering search, rankings, book details,
bookshelf, notebooks, highlights, and notes.

Public commands (no login required):
- weread search <keyword> — search books
- weread ranking [category] — book rankings (all/rising/category ID)

Private commands (cookie auth via browser):
- weread book <bookId> — book details
- weread shelf — personal bookshelf
- weread notebooks — books with highlights/notes
- weread highlights <bookId> — underlines in a book
- weread notes <bookId> — personal notes on a book

Closes #82
2026-03-19 21:03:20 +08:00
Pleasure1234 1f270397f6 fix: dedupe history and improve Discord channel parsing (#77) 2026-03-19 20:58:40 +08:00
BruceLoveDecimal aa2f37be32 Add apple-podcasts coverage and docs (#92)
Co-authored-by: 刘启灏 <liuqihao@liuqihaodeMacBook-Pro.local>
2026-03-19 20:58:08 +08:00
AstroHan aeb1cb6a3a fix: install XHR interceptor after navigation to prevent context reset (#91)
goto() triggers a full page navigation that resets the JS execution
context, wiping any previously injected fetch/XHR monkey-patches.
The old code installed the interceptor on x.com then navigated away,
so the interceptor was always destroyed before it could capture data.

Fix: navigate directly to the target page, install interceptor after
page load, then scroll to trigger API calls via pagination.

Also fixes the same bug in notifications.ts.

Closes #86
2026-03-19 20:57:28 +08:00
jackwener 4e260ecdeb fix: include pre-built extension dist/ for zero-step install 2026-03-19 20:54:49 +08:00
jackwener f7c7230854 fix: include pre-built extension dist/ in repo for zero-step install 2026-03-19 20:53:45 +08:00
jackwener 48e277bd0b 1.0.0
Release / release (push) Has been cancelled
2026-03-19 17:00:45 +08:00
jackwener 8bb03ecc9b feat: replace Playwright MCP with lightweight daemon + Chrome Extension
Major architecture change:
- Replace @playwright/mcp with lightweight micro-daemon + Chrome Extension
- Zero-config: no tokens, no MCP server, auto-start daemon
- Extension: 10.55KB gzipped, 4+1 action protocol
- Graceful shutdown, exponential backoff, log forwarding
- All docs updated for Browser Bridge architecture
2026-03-19 17:00:29 +08:00
jackwener 3b6f72ca08 docs: fix extension install instructions — no store yet, no restart needed
- Remove 'Chrome Web Store' references (not published yet)
- Add detailed unpacked extension install steps (chrome://extensions)
- Remove 'restart Chrome' advice (Service Worker activates immediately)
- Direct users to chrome://extensions for troubleshooting
2026-03-19 16:57:40 +08:00
jackwener beda0b714c docs: remove all remaining Playwright references from docs
Updated 6 files:
- CDP.md, CDP.zh-CN.md: Browser Bridge instead of Playwright MCP Bridge
- CLI-ELECTRON.md: Browser Bridge / IPage abstraction wording
- CLI-EXPLORER.md: browser tools instead of Playwright MCP tools
- TESTING.md: Browser Bridge extension mode, removed token references
- src/clis/chatgpt/README{,.zh-CN}.md: CDP instead of Playwright

Zero Playwright references remaining across all .md files.
2026-03-19 16:55:20 +08:00
jackwener 3b33ade214 docs: update README, README.zh-CN, SKILL.md for new Browser Bridge architecture
- Replace all Playwright MCP Bridge references with opencli Browser Bridge
- Remove token setup, MCP config, and manual setup sections
- Simplify prerequisites: just install extension, zero config
- Update troubleshooting: daemon status/logs commands
- Update env vars: add OPENCLI_DAEMON_PORT, OPENCLI_VERBOSE
- Update SKILL.md tags: mcp,playwright → chrome-extension,cdp
2026-03-19 16:49:10 +08:00
jackwener ebe4683a9e fix: add screenshot mock to executor.test.ts for IPage compat
tsc --noEmit failed because createMockPage() was missing the
screenshot() method added to IPage in the round 2 review fix.
2026-03-19 16:43:58 +08:00
jackwener 59c0d639a5 refactor: fix 9 issues from round 2 code review
Bug fixes:
- #1 /logs?level=error returned 404 — use pathname for route matching
- #2 Duplicate initialization — added 'initialized' guard flag

Should fix:
- #4 Added screenshot() to IPage interface
- #5 Graceful shutdown rejects pending requests before exit
- #6 Use process.execPath instead of 'npx tsx' for faster daemon spawn

Cleanup:
- #7 Removed duplicate 'browser' keyword in package.json
- #8 Removed unused normalizeEvaluateSource import from browser.ts
- #9 Changed dynamic import to static import in intercept.ts
- #10 Added explicit throw at end of sendCommand for clarity

61 tests pass (4 test files). Extension: 10.55KB.
2026-03-19 16:36:06 +08:00
jackwener 3d1f9640ea feat: forward extension console logs to daemon
Extension side:
- Hook console.log/warn/error → forward via WS as { type: 'log', level, msg, ts }
- Original console output preserved (for chrome://extensions debug)

Daemon side:
- Ring buffer (200 entries) stores extension logs
- Logs printed to daemon stderr with emoji prefix (📋/⚠️/)
- GET /logs — returns buffered logs (optional ?level= filter)
- DELETE /logs — clears log buffer

Usage:
  curl localhost:19825/logs              # view all logs
  curl localhost:19825/logs?level=error  # errors only
  curl -X DELETE localhost:19825/logs    # clear buffer

Extension build: 10.48KB
2026-03-19 16:21:17 +08:00
jackwener 8e8c4a0229 feat: add exponential backoff reconnect + CDP screenshot support
Exponential backoff:
- Reconnect delay: 2s, 4s, 8s, 16s, ..., capped at 60s
- Resets to base delay on successful connection
- Reduces idle CPU waste vs fixed 3s reconnect

Screenshot via CDP Page.captureScreenshot:
- New 'screenshot' action in protocol (5th action)
- Supports format (png/jpeg), quality, fullPage
- Full-page: uses Emulation.setDeviceMetricsOverride for scroll height
- CLI-side: page.screenshot() with optional file save
- Extension build: 9.81KB (+1.7KB from 8.11KB)

Inspired by bb-browser's architecture patterns.
2026-03-19 16:21:17 +08:00
jackwener 01b8b6b5bf refactor: fix 14 issues from deep code review
P0 Critical:
- #1 Fix double IIFE wrapping: unified wrapForEval() replaces
  normalizeEvaluateSource + ad-hoc wrap in page.evaluate()
- #2 Fix navigate race: check tab.status before addListener,
  reduced timeout 30s→15s

P1 Should Fix:
- #8 Remove unused permissions (scripting, host_permissions, content_scripts)
- #10 Add retry (3x, 500ms) + timeout (30s) to sendCommand()

P2 Cleanup:
- #3 Extract isWebUrl() to safely handle undefined tab.url
- #4 Sanitize maxDepth with Math.max/min bounds
- #6 Delete empty src/daemon/ directory
- #7 Remove dead createJsonRpcRequest + its test
- #9 Remove stale IIFE-mode comment
- #11 Validate body.id in daemon request handler
- #12 Guard ensureAttached: detach+re-attach on 'already attached'
- #14 Extract _tabOpt() helper (removes 13x spread duplication)
- #15 Add verbose warning for unsupported consoleMessages()

All 35 unit tests pass.
2026-03-19 16:21:17 +08:00
jackwener b2fa7daf57 feat: replace @playwright/mcp with lightweight daemon + Chrome Extension
Architecture:
- Micro-daemon (HTTP + WebSocket bridge, ~190 lines, auto-start/idle-exit)
- Chrome MV3 Extension using chrome.debugger CDP (10KB build)
- 5 protocol actions: exec, navigate, tabs, cookies, screenshot
- All DOM ops via JS evaluate — no extension update needed for new features

Key features:
- CDP Runtime.evaluate for JS execution in page context
- Tab management, cookie access via Chrome APIs
- Auto-start daemon on cold boot, idle auto-exit (5min)
- Minimal permissions: debugger, tabs, cookies, activeTab, alarms

Tested: zhihu hot (14.3s), twitter timeline (9.3s)
2026-03-19 16:21:17 +08:00
jackwener 0374b77d16 feat: Introduce Netease Music CLI with CDP enabler 2026-03-19 05:06:48 +08:00
jackwener c3efc5b492 0.9.8
Release / release (push) Has been cancelled
2026-03-19 01:38:55 +08:00
jackwener f85464c1aa 0.9.7 2026-03-19 01:38:49 +08:00
backtime1993 a4f94912cd fix(main): navigate to domain before cookie/header strategy commands in CDP mode (#71)
When using CDP mode (OPENCLI_CDP_ENDPOINT), the browser page context is
the user's active tab which may be on an unrelated domain. Cookie/header
strategy commands that use fetch() with credentials: 'include' then fail
with "Failed to fetch" due to the browser's same-origin policy.

Fix: before executing cookie/header strategy commands, navigate to the
command's declared domain so the fetch runs in same-origin context.
This mirrors the pre-navigation already done in the cascade command.

Affects all cookie-strategy adapters (bilibili, twitter, zhihu, xueqiu,
etc.) when OPENCLI_CDP_ENDPOINT is enabled and the active Chrome tab is
on a different site.

Co-authored-by: kensei <backtime1993@gmail.com>
2026-03-19 01:38:10 +08:00
Shuming Ying deb568dbe5 fix(browser): avoid selecting non-server playwright cli paths (#74)
Co-authored-by: root <root@localhost.localdomain>
2026-03-19 01:31:10 +08:00
Jingyu 32619fa553 fix(xiaohongshu): restore user profile note fetching (#69) 2026-03-19 00:10:33 +08:00
dependabot[bot] 1d871b35f0 chore(deps): bump commander from 13.1.0 to 14.0.3 (#67)
Bumps [commander](https://github.com/tj/commander.js) from 13.1.0 to 14.0.3.
- [Release notes](https://github.com/tj/commander.js/releases)
- [Changelog](https://github.com/tj/commander.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tj/commander.js/compare/v13.1.0...v14.0.3)

---
updated-dependencies:
- dependency-name: commander
  dependency-version: 14.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 00:03:01 +08:00
dependabot[bot] 75e6ed4593 chore(ci): bump actions/setup-node from 4 to 6 (#65)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 00:02:29 +08:00
dependabot[bot] b07434b2a1 chore(ci): bump actions/checkout from 4 to 6 (#66)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-18 23:59:47 +08:00
AlexYue 515ce75f3b ci: add Dependabot, security audit, release-please, and CI optimization (#64)
* chore(ci): add Dependabot for npm and GitHub Actions updates

- Weekly npm dependency updates with PR limit of 10
- Weekly GitHub Actions version updates with PR limit of 5
- Conventional commit prefixes (chore(deps), chore(ci))

* ci: add security audit workflow

- Run npm audit on push/PR and weekly schedule
- Fail on high-severity vulnerabilities using audit-ci
- Only audit production dependencies

* ci: add release-please for automated changelog and versioning

- Auto-generate CHANGELOG.md from Conventional Commits
- Create version bump PRs on push to main
- Works alongside existing release.yml for npm publish

* ci: add concurrency controls and Node.js version matrix

- Add concurrency groups to ci, e2e-headed, security workflows
  to cancel duplicate runs on the same branch
- Test unit tests across Node 18/20/22 with fail-fast: false
- Update test step name to show Node version

* chore: bump minimum Node.js version from 18 to 20

- Update engines.node in package.json to >=20.0.0
- Update prerequisites in README.md and README.zh-CN.md
- Remove Node 18 from CI test matrix

* review: fix release token and prod-only audit scope

* docs: align Node 20 troubleshooting guidance

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-18 23:52:49 +08:00
AlexYue f539a44cfd docs: add issue/PR templates and contributing guide (#63)
* docs: add issue/PR templates and contributing guide
- Add GitHub Issue forms: bug report, feature request, new site adapter
- Add PR template with CI-aligned checklist (typecheck, test, validate)
- Add CONTRIBUTING.md with adapter development workflow and testing guide

* docs: simplify adapter request and fix contributor example

* docs: trim contribution and issue templates

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-18 23:49:54 +08:00
jackwener 832370f6e2 feat: add Feishu (飞书/Lark) Desktop adapter via AppleScript (5 commands)
Feishu uses custom 'Lark Framework' (Chromium-based but NOT Electron).
CDP port test failed — --remote-debugging-port has no effect.
Uses AppleScript + clipboard approach (same as WeChat/ChatGPT).

Commands: status, send, read, search (Cmd+K), new (Cmd+N)
Includes adapter READMEs (EN+ZH).
2026-03-18 23:03:23 +08:00
jackwener fc9fc32d14 feat: add WeChat (微信) Desktop adapter via AppleScript (6 commands)
WeChat is a native macOS app (not Electron), so uses AppleScript + clipboard:
- status: check if running + window count
- send: clipboard paste + Enter
- read: Cmd+A → Cmd+C with clipboard backup/restore
- search: Cmd+F + type query
- chats: switch to chats tab (Cmd+1)
- contacts: switch to contacts tab (Cmd+2)

Includes adapter READMEs (EN+ZH).
Total: 30 sites · 157 commands
2026-03-18 22:51:18 +08:00
jackwener 43b753fa02 fix(xiaohongshu): repair command args and request capture 2026-03-18 22:47:39 +08:00
jackwener be194a1849 refactor: rename discord → discord-app to distinguish from web version
Desktop Electron app adapters should use '-app' suffix when a web version also exists.
2026-03-18 22:43:26 +08:00
jackwener 63489fb596 chore: remove untested feishu/wechat adapters, polish CLI-ELECTRON.md
- Remove feishu and wechat adapters (not tested yet, will re-add later)
- Remove their rows from README.md and README.zh-CN.md
- Significantly polish CLI-ELECTRON.md skill guide:
  - Add Electron detection guide (check for Electron Framework)
  - Add Non-Electron AppleScript pattern section
  - Add port assignment table for all CDP adapters
  - Improve code examples with real working TypeScript
2026-03-18 22:29:15 +08:00
stometaverse c370bd0582 feat(xiaohongshu): add 4 creator analytics commands (creator-profile, creator-stats, creator-notes, creator-note-detail) (#49)
* feat(xiaohongshu): add 4 creator analytics commands

Add creator backend support for Xiaohongshu (小红书), enabling
creators to access their analytics data from the command line.

New commands:
- creator-profile: account info (followers, likes, creator level)
- creator-stats: 7-day/30-day overview (views, likes, collects,
  comments, shares, new followers) with daily trend data
- creator-notes: note list with per-note metrics from note manager
- creator-note-detail: single note analytics breakdown
  (organic vs promoted vs video traffic)

API discovery:
- /api/galaxy/creator/home/personal_info (cookie auth, 200 OK)
- /api/galaxy/creator/data/note_detail_new (cookie auth, 200 OK)
- /api/galaxy/creator/data/note_detail?note_id=xxx (cookie auth, 200 OK)
- Note manager DOM extraction for note list (bypasses v2 signature)

All endpoints verified working with real creator account.
Screenshots (redacted) included in docs/screenshots/.

Requires: Chrome logged into creator.xiaohongshu.com

* chore: remove screenshots from repo (will host externally for PR)

* review: fix creator analytics CLI integration

Co-authored-by: stone16 <stone2paul@gmail.com>

* test: add site-scoped test runner

Co-authored-by: stone16 <stone2paul@gmail.com>

* review: ignore publish timestamps in creator note metrics

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-18 22:27:25 +08:00
AlexYue 8a355dfd2d feat: Add download support for xhs, twi, bilibili, zhihu (#22)
* feat: add download support for images, videos, and articles

Add comprehensive download functionality to OpenCLI with support for
multiple platforms and content types.

- Add `src/download/index.ts`: HTTP download with progress, yt-dlp
  wrapper for video platforms, cookie export to Netscape format for
  authenticated downloads
- Add `src/download/progress.ts`: Terminal progress bars, multi-file
  download tracker with status summary
- Add `src/pipeline/steps/download.ts`: New `download` pipeline step
  for declarative YAML pipelines

- Register `download` step in executor.ts
- Add template filters: `slugify`, `sanitize`, `ext`, `basename` for
  filename templating

- `xiaohongshu download`: Download images and videos from notes
- `bilibili download`: Download videos using yt-dlp with cookie auth
- `twitter download`: Download media from user timeline or single tweet
- `zhihu download`: Export articles to Markdown with optional image
  download

```yaml
pipeline:
  - download:
      url: ${{ item.imageUrl }}
      dir: ./downloads
      filename: ${{ item.title | sanitize }}.jpg
      concurrency: 5
      skip_existing: true
      use_ytdlp: false
      type: auto  # auto|image|video|document
```

- Concurrent downloads with configurable parallelism
- Progress bars with file size display
- Skip existing files option
- Cookie forwarding for authenticated downloads
- yt-dlp integration for video platforms (YouTube, Bilibili, Twitter)
- HTML to Markdown conversion for article export

- yt-dlp: Required for video downloads from streaming platforms
- ffmpeg: Optional for video format conversion

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

* docs: add download support documentation

- Add Download Support section to both README.md and README.zh-CN.md
- Document supported platforms: Xiaohongshu, Bilibili, Twitter, Zhihu
- Include prerequisites (yt-dlp installation)
- Add usage examples for all download commands
- Document the `download` pipeline step for YAML adapters
- Update built-in commands table with new `download` commands

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

* fix: preserve zhihu ordered list content

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-18 22:20:13 +08:00
foreverxdord 700d970f13 feat: add grok.com site support (#60)
Add support for grok.com site with two commands:
- ask: Send a message to Grok and get response
- debug: Debug grok page structure

Implementation uses Playwright CDP protocol with fallback DOM selectors
(div.message-bubble, [data-testid="message-bubble"]) for reliability.

Co-authored-by: xdord <xdord@xdorddeMac-mini.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 22:11:36 +08:00
jackwener b1fda7da3b feat: add Feishu (飞书) adapter + Notion favorites command
Feishu/Lark (5 commands via AppleScript):
- status, send, new, search (Cmd+K), read
- Lark Framework wraps Chromium v131 but doesn't expose CDP
- Uses AppleScript + clipboard automation (same as WeChat/ChatGPT)

Notion:
- Added favorites command (list pages from Favorites section)

Total: 32 sites · 162 commands
2026-03-18 21:19:47 +08:00
jackwener 40a6a4cace fix(notion): use precise DOM selectors for favorites extraction 2026-03-18 21:15:24 +08:00
jackwener 920ca3f7e5 feat(notion): add favorites command to list favorited pages 2026-03-18 21:06:18 +08:00
jackwener 799a616359 feat: add WeChat (微信) Desktop adapter via AppleScript (5 commands)
WeChat Mac is native Cocoa (not Electron), so CDP is not available.
Uses AppleScript + clipboard automation instead:
- status: check if WeChat is running
- send: paste + Enter in active conversation
- new: Cmd+N for new chat
- search: Cmd+F and type query
- read: Cmd+A → Cmd+C to copy chat content

Total: 30 sites · 156 commands
2026-03-18 21:03:00 +08:00
jackwener 9c2a983e8b feat: add Notion + Discord Desktop adapters (14 new commands via CDP)
Notion (7 commands):
- status, search (Quick Find), read, new, write, sidebar, export

Discord (7 commands):
- status, send, read, channels, servers, search, members

Both apps are Electron-based, connected via --remote-debugging-port.
Notion port: 9230, Discord port: 9232.
Includes adapter READMEs (EN+ZH) for both.

Total: 29 sites · 151 commands
2026-03-18 20:53:44 +08:00
jackwener 3d1ea9b15c feat: add ChatWise Desktop adapter (9 commands via CDP)
Release / release (push) Has been cancelled
- status, new, send, read, ask, model, screenshot, history, export
- Electron-based multi-LLM client (GPT-4/Claude/Gemini)
- Includes adapter READMEs (EN+ZH)
- Fix truncated README table rows
- Total: 27 sites · 137 commands
2026-03-18 20:48:05 +08:00
AstroHan e1d4a6e5e6 feat(linux-do): add linux.do adapter with 6 commands (#43) (#56)
Add linux.do (Discourse-based forum) support with 6 YAML pipeline commands:
- hot: trending topics with period filter (all/daily/weekly/monthly/yearly)
- latest: newest topics
- categories: list all categories with slug/id for further queries
- category: browse topics within a specific category
- topic: post details with replies (first page)
- search: search topics by keyword

All commands use navigate+evaluate pattern with cookie auth
(linux.do enforces login_required on all endpoints).

Security: user inputs sanitized via | json filter + encodeURIComponent.
HTML content stripped with block-tag spacing and full entity decoding.
2026-03-18 20:25:12 +08:00
stometaverse a06cdbf0ac feat: add jimeng (即梦AI) CLI support (#57)
Add two CLI commands for Jimeng (即梦AI) — ByteDance's AI image generation platform:

- generate: Text-to-image generation with model selection and configurable wait time
- history: View recent generation history with prompt, model, status, and image URLs

Both commands use browser automation with cookie-based authentication on jimeng.jianying.com.
2026-03-18 20:23:45 +08:00
jackwener e76de39f42 feat: desktop adapter improvements — bug fixes + 9 new commands
Release / release (push) Has been cancelled
P0 Bug Fixes:
- codex: add missing args/IPage imports, add wait(0.5) before Enter in send
- cursor: new.ts uses Meta+N shortcut (more robust), composer.ts simplified
- chatgpt: send.ts now backs up and restores clipboard
- antigravity: send.ts/model.ts columns unified to PascalCase
- codex: read.ts column renamed Thread_Content → Content

P1 New Features:
- ask: one-shot send+wait+read for cursor, codex, chatgpt (send → poll DOM → return response)
- screenshot: DOM + accessibility snapshot export for cursor, codex

P2 New Features:
- history: list sidebar chat sessions for cursor, codex
- export: save full conversation as Markdown for cursor, codex

Total: 26 sites · 128 commands
2026-03-18 19:52:39 +08:00
jackwener 813631e468 docs: remove unnecessary --remote-allow-origins, add CDP launch to ChatGPT README
- Remove --remote-allow-origins from antigravity README, README.zh-CN, SKILL.md (not needed for local usage)
- Update ChatGPT README to document both AppleScript and CDP approaches
- Document ChatGPT Electron launch: /Applications/ChatGPT.app/Contents/MacOS/ChatGPT --remote-debugging-port=9224
2026-03-18 19:42:45 +08:00
jackwener 2afbb99660 feat: add ChatGPT Desktop native support + Cursor/Codex advanced commands
Release / release (push) Has been cancelled
- Add ChatGPT macOS Desktop adapter (status, new, send, read) via AppleScript
- Add Cursor composer, model, extract-code commands via CDP
- Add Codex model command via CDP
- Create adapter READMEs for ChatGPT (EN+ZH) and Cursor (EN+ZH)
- Fix README.md duplicate table rows (6 sites were listed twice)
- Update command count: 26 sites · 119 commands
- Bump version to 0.9.5
2026-03-18 19:40:08 +08:00
jackwener aa55c88069 0.9.4
Release / release (push) Has been cancelled
2026-03-18 17:41:41 +08:00
jackwener b32fe1cbc3 feat: add advanced cursor and codex capabilities 2026-03-18 17:41:41 +08:00
jackwener 981cc1bc5e docs: add CLI-ELECTRON.md as an agent skill guide 2026-03-18 17:17:46 +08:00
jackwener cd63231b7e chore(release): 0.9.2
Release / release (push) Has been cancelled
2026-03-18 17:15:01 +08:00
jackwener 685658f7bd build: update cli-manifest 2026-03-18 17:15:01 +08:00
jackwener cd6f7a1f7e fix(codex): use precise selector for read command 2026-03-18 17:14:47 +08:00
jackwener 4ce0345c9a chore(release): 0.9.1
Release / release (push) Has been cancelled
2026-03-18 17:06:47 +08:00
jackwener 3cc2cb5504 feat(codex): implement generic CDP adapters for OpenAI Codex desktop app 2026-03-18 17:06:47 +08:00
jackwener abac070ce4 docs: update root README and SKILL with electron app marketing copy 2026-03-18 16:51:18 +08:00
jackwener 79fbac844e chore(release): 0.9.0
Release / release (push) Has been cancelled
2026-03-18 16:44:36 +08:00
jackwener 7e776e2bd5 feat(antigravity): support cli all electron app via CDP 2026-03-18 16:44:36 +08:00
jackwener bde1c53a3e fix(xiaoyuzhou): validate limits and tighten e2e 2026-03-18 16:38:09 +08:00
AstroHan 5e667b9c2f feat(xiaoyuzhou): add podcast platform adapter (#18) (#53)
Three public commands for Xiaoyuzhou (小宇宙) podcast platform:
- podcast <id>: view podcast profile
- podcast-episodes <id> [--limit]: list recent episodes (up to 15)
- episode <id>: view episode details

Uses __NEXT_DATA__ extraction from SSR pages, no auth required.
Includes unit tests (16), E2E tests (3), and README updates.
2026-03-18 16:34:03 +08:00
jackwener 64e3a2d627 0.8.0
Release / release (push) Has been cancelled
2026-03-18 15:25:51 +08:00
jackwener 849d9faea1 refactor(main): remove duplicate argument coercion in favor of engine validation 2026-03-18 15:24:59 +08:00
jackwener 29ea5ce059 feat(engine): add lightweight runtime validation and coercion for CLI arguments 2026-03-18 15:20:55 +08:00
jackwener 12c4b8853b feat(pipeline): extract STEP_HANDLERS into dynamic PipelineRegistry 2026-03-18 15:19:23 +08:00
jackwener cfad003220 fix(browser): throw explicit BrowserConnectError on Playwright MCP JSON-RPC silent failures 2026-03-18 15:18:01 +08:00
jakevin abfd4b902c feat(browser): add CDP remote connection support for server environments (#52)
* feat(browser): add CDP remote connection support for server environments

This feature enables OpenCLI to connect to a Chrome browser running on a
different machine (e.g., your local computer) from a headless server
environment via Chrome DevTools Protocol (CDP).

Server environments (CI, cloud VMs, headless Linux) cannot run Chrome with
a GUI or install the Playwright MCP Bridge extension. This makes it
impossible to use OpenCLI commands that require browser authentication.

Add support for the `OPENCLI_CDP_ENDPOINT` environment variable, which
tells OpenCLI to connect to a remote Chrome instance via CDP instead of
using the local extension mode.

1. Start Chrome with remote debugging on local machine:
   ```
   chrome --remote-debugging-port=9222 --user-data-dir="$HOME/chrome-debug"
   ```

2. Create SSH tunnel to forward port to server:
   ```
   ssh -R 9222:localhost:9222 your-server
   ```

3. Run OpenCLI on server:
   ```
   export OPENCLI_CDP_ENDPOINT="http://localhost:9222"
   opencli bilibili hot --limit 5
   ```

- src/browser.ts: Add CDP endpoint detection in buildMcpArgs()
- src/doctor.ts: Show CDP mode status in doctor report
- README.md: Add "Remote Chrome (Server/Headless)" section
- README.zh-CN.md: Add corresponding Chinese documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

* docs: extract CDP connection guide into separate files

* docs: clarify CDP vs SSH/Proxy distinction in CDP guides

* docs: restructure CDP guides into 3 distinct phases (preparation, tunnel, execution)

---------

Co-authored-by: ByteYue <yj976240184@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-18 15:11:14 +08:00
Alex Yang 2d52abde7c fix(barchart): add CSRF retry and mostActive fallback to flow command (#51)
The flow command returned no data because:
1. The CSRF token may not be in the DOM yet when Angular is still
   initializing — add a polling loop (up to 5s) to wait for it
2. The unusual_activity list is empty outside market hours — fall back
   to the mostActive list which always has data
3. Remove the DOM table fallback that never matched (barchart uses
   Angular components, not standard <tr> elements)
2026-03-18 14:43:51 +08:00
jackwener f102501e4a chore(release): bump version to v0.7.11
Release / release (push) Has been cancelled
2026-03-18 13:35:13 +08:00
jackwener 2a983b6b8d feat(browser): auto-bootstrap playwright mcp via npx 2026-03-18 13:32:41 +08:00
jackwener c114a9d7f1 ci: gate pkg.pr.new publish workflow 2026-03-18 13:26:37 +08:00
jackwener d2e179ced5 fix(barchart): preserve flow semantics and nearest expiry 2026-03-18 13:23:28 +08:00
Alex Yang c806f795cc feat(barchart): add stock quote, options, greeks, and flow commands (#45)
* feat(barchart): add stock quote, options chain, greeks, and flow commands

Add 4 new barchart.com CLI commands:
- `barchart quote`: stock price, volume, market cap, P/E, EPS
- `barchart options`: options chain with strike, bid/ask, greeks, IV, OI
- `barchart greeks`: near-the-money greeks overview (delta, gamma, theta, vega, rho)
- `barchart flow`: unusual options activity sorted by volume/OI ratio

Auth uses CSRF token from <meta name="csrf-token"> + session cookies
via the internal proxy API, with DOM fallback for the quote command.

* feat(barchart): add --expiration date filter to greeks command
2026-03-18 12:13:25 +08:00
Alex Yang de5495bdd7 ci: add pkg.pr.new workflow for continuous package previews (#46)
Publishes preview versions of the package on every push and PR,
allowing reviewers to install and test exact commit builds.
2026-03-18 11:59:25 +08:00
Zhang ShengYan d6222ff932 fix: discover global @playwright/mcp for nvm/npm installs (#42)
* fix: discover global @playwright/mcp in nvm/npm installs

* test: cover global mcp discovery paths

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-17 22:36:06 +08:00
jackwener e0395ce5ed test: make Vitest project order explicit
Add explicit group ordering for Vitest projects so unit tests run before e2e tests, while keeping the e2e ordering fix from PR #38.\n\nCo-authored-by: RbBtSn0w <hamiltonsnow@gmail.com>
2026-03-17 17:46:03 +08:00
jackwener 4c8c6e8be7 fix(twitter): migrate bookmarks to direct GraphQL
Release / release (push) Has been cancelled
chore: bump version to 0.7.10
2026-03-17 17:30:01 +08:00
jackwener 7b5bdfa7d5 fix(twitter): harden remaining twitter commands
Co-authored-by: Sheng-Yan, Zhang <yancode@qq.com>
2026-03-17 17:26:36 +08:00
jackwener 546c0b997a feat: Enhance setup output with token save confirmation and improved browser connectivity guidance. 2026-03-17 17:20:16 +08:00
jackwener 1e34e7e6d3 chore: bump version to 0.7.9 2026-03-17 17:11:13 +08:00
jackwener 9ae9eb3fc6 fix(twitter): rewrite timeline adapter to use direct GraphQL API
The previous implementation injected a fetch interceptor after page
navigation, but by that time the HomeTimeline API call had already
completed, resulting in 'no data captured' every time.

Rewrote to directly call Twitter's HomeTimeline GraphQL endpoint
(same pattern as profile.ts and thread.ts):
- Dynamic queryId resolution with hardcoded fallback
- Pagination support with cursor
- Filters out promoted content
- Returns structured tweet data (id, author, text, likes, retweets,
  replies, views, created_at, url)

Fixes #36
2026-03-17 17:11:02 +08:00
jackwener 612c0ab1af v0.7.8: P0 architecture refactor - split browser.ts, unified errors, strict mode
Release / release (push) Has been cancelled
2026-03-17 17:02:03 +08:00
jackwener 68840fc85c refactor: P0 architecture improvements
- Split browser.ts (700 lines) into src/browser/ module (page, mcp, errors, discover, tabs, index)
- Add unified error handling: CliError base class + logger module
- Enable TypeScript strict mode, fix 12 type errors
- Extract inline build scripts to scripts/clean-yaml.cjs and copy-yaml.cjs
- All 178 unit tests pass, build produces 83 entries across 19 sites
2026-03-17 17:01:51 +08:00
jackwener 8263a06a85 fix(completion): insert fpath before compinit in .zshrc
The postinstall script was appending the fpath line at the end of .zshrc,
but compinit (called earlier by oh-my-zsh or directly) would have already
finished scanning. This caused zsh completion to silently fail for most
users.

Now the script detects the first compinit / oh-my-zsh source line and
inserts the fpath entry before it, ensuring completion works immediately.
2026-03-17 16:53:07 +08:00
jackwener eb2c3fdf89 fix: support Chrome Dev and Chrome Beta browser variants
Add Chrome Dev and Chrome Beta profile paths to discoverExtensionToken()
and checkExtensionInstalled() across macOS, Linux, and Windows.

Closes #30
2026-03-17 16:36:19 +08:00
jackwener 43ed0ace59 0.7.6
Release / release (push) Has been cancelled
2026-03-17 16:35:20 +08:00
jackwener de962eb5fb feat: support commands completion (#32)
Add full shell tab-completion for opencli, supporting Bash, Zsh, and Fish.

Co-authored-by: RinChanNOWWW <rin_chan_now@outlook.com>

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-17 16:33:19 +08:00
jackwener d1da293ef9 0.7.5
Release / release (push) Has been cancelled
2026-03-17 16:14:02 +08:00
jackwener 25bd872a24 fix: doctor/setup edge cases — format detection, dynamic profiles, fish shell
- upsertJsonConfigToken: detect format by file path (opencode → mcp format,
  others → mcpServers). Previously empty files always got OpenCode format.
- Dynamic Chrome profile enumeration: scan for Default/Profile N directories
  instead of hardcoding 4 profiles.
- Fish shell: use 'set -gx' syntax for config.fish, not 'export'.
- Pass filePath through all callers (setup.ts, applyBrowserDoctorFix).
- Reduce setup auto-verify timeout from 8s to 5s.
- Add 7 new tests (19 total): empty file format, opencode path detection,
  claude.json path detection, fish shell set/replace/append, zshrc fallback.
2026-03-17 16:13:05 +08:00
jackwener ff3e5c6887 feat: enhance setup with precise token scan errors and auto-verify
- When token scan fails, diagnose exact cause via checkExtensionInstalled()
  (extension not installed vs token not in LevelDB)
- Show actionable fix instructions instead of generic warning
- Auto-verify browser connectivity after writing configs (Step 7)
- Simplify README setup flow to 2 steps (install + setup)
2026-03-17 16:07:38 +08:00
jackwener 2e66e3183c docs: reorder setup flow — doctor → setup → doctor --live 2026-03-17 16:02:37 +08:00
jackwener a1bcb23239 docs: reorder setup flow — doctor first, then setup
Logical flow: install extension → doctor (verify token discoverable) →
setup (distribute token to tools). --fix moved to a Tip block for
post-setup maintenance.
2026-03-17 16:00:46 +08:00
jackwener 6024af3aa0 docs: split doctor --fix into interactive and non-interactive examples 2026-03-17 15:58:53 +08:00
jackwener 1393ce3327 docs: sync Chinese README with doctor --live, command table polish 2026-03-17 14:57:13 +08:00
jackwener 0fe3b9b921 0.7.4
Release / release (push) Has been cancelled
2026-03-17 14:55:54 +08:00
jackwener 375beaa744 docs: polish README and SKILL
- Sort command table by count (descending), add Count column
- Add xiaohongshu `me`, boss `detail` to command references
- Add Self-healing setup highlight for doctor/setup workflow
- Document `doctor --live` and `doctor --fix` options
- Bump SKILL version to 0.7.3, expand tags
- Fix site count to 19, update descriptions
2026-03-17 14:50:14 +08:00
SonicKang 341c42c62f fix(opencode): use 'environment' instead of 'env' for MCP config (#29)
OpenCode config schema uses 'environment' property for MCP server
environment variables, not 'env'.

Schema reference: https://opencode.ai/config.json
2026-03-17 14:46:53 +08:00
jackwener 50b71c0936 fix: use binary read for LevelDB token discovery on all platforms
The previous strings+grep pipeline failed because LevelDB's internal
encoding fragments ASCII strings like 'auth-token' and the extension ID
across byte boundaries. Replace extractTokenViaStrings with a unified
binary read approach that scans for the extension ID prefix and searches
a 500-byte window for base64url tokens.

Also removes unused execSync import.
2026-03-17 14:44:34 +08:00
jackwener 981c167a0b feat(doctor): add extension install check and token connectivity test
- checkExtensionInstalled(): scans Chrome/Edge/Chromium Extensions dirs
- checkTokenConnectivity(): actual MCP handshake via --live flag
- Updated DoctorReport type and report rendering
- Added unit tests for new rendering (12/12 pass)
2026-03-17 14:38:16 +08:00
jackwener 2463689105 0.7.3
Release / release (push) Has been cancelled
2026-03-17 13:34:34 +08:00
jackwener c714254d8f docs: add YouTube video and transcript commands to README and SKILL 2026-03-17 13:30:42 +08:00
Ji 8e7490407c feat(youtube): add video metadata and transcript commands (#25)
Add two new YouTube adapters:

- **youtube video**: fetch metadata (title, views, description, etc.) from ytInitialPlayerResponse and ytInitialData
- **youtube transcript**: fetch subtitles via Android InnerTube API to bypass PoToken requirement on Web client caption URLs
  - Two output modes: --mode grouped (sentence merging, speaker detection, chapter headings) and --mode raw (precise sub-second timestamps)
  - CJK support with 30s time-window fallback for unpunctuated captions
  - Language selection with --lang and stderr warning on fallback
  - URL normalization for watch, youtu.be, shorts, embed, live formats

Co-authored-by: Ji Zhang <jizhang.work@gmail.com>
2026-03-17 13:26:30 +08:00
Ji 14dcd2bc5f feat(reddit): add threaded comment tree to read command (#26)
Replace flat top-level-only read.yaml with recursive tree walker:
- Configurable depth and breadth (--depth, --replies)
- Replies sorted by score, top-K selected at each level
- Hidden replies surfaced as [+N more replies]
- Multiline bodies preserve indentation at all depths
- Configurable --max_length (was hard-coded 500 chars)
- Input validation: all numeric params clamped to safe minimums
2026-03-17 13:18:11 +08:00
SiweiMa e9b9beedfe feat(linkedin): add job search adapter (#28)
* feat: add linkedin job search adapter

* fix(linkedin): fix parseCsvArg undefined bug, regex escapes in page.evaluate, replace hardcoded wait, add IPage type

* refactor(linkedin): extract evaluate logic, add progress logging, improve code structure

- Extract Voyager query/URL building into typed standalone functions
- Split fetchJobCards into its own function with per-batch evaluate
- Add SearchInput interface for type safety
- Add progress logging to enrichJobDetails (stderr)
- Add section comments for code organization
- Deduplicate normalize helpers in evaluate strings

---------

Co-authored-by: Siwei Ma <siweima@Siweis-MacBook-Pro.local>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-03-17 13:07:16 +08:00
jackwener 59de5fb3f5 0.7.2
Release / release (push) Has been cancelled
2026-03-17 01:38:29 +08:00
jackwener 7555f14369 refactor: deep code review improvements
- Add *.log to .gitignore, remove debug.log from tracking
- Fix dev-mode FS scan to discover .ts adapter files (not just .js)
- Deduplicate CONNECT_TIMEOUT: browser.ts now uses runtime.ts constant
- Fix CSV output: escape newlines in field values per RFC 4180
- Add proper type interfaces for validate/verify (remove any types)
- Remove unused hadOuterQuotes variable in snapshotFormatter
- Derive CliOptions from CliCommand via Omit+Partial to reduce duplication
- Expand dense one-liner action callbacks in main.ts for readability
2026-03-17 01:38:23 +08:00
jackwener 2652fa40e5 chore: change license from BSD-3-Clause to Apache-2.0 2026-03-17 01:34:51 +08:00
jackwener a7c367a61b docs: update README and SKILL for new Reddit adapters
- Reddit: 4 → 15 commands (popular, read, user, user-posts,
  user-comments, upvote, save, comment, subscribe, saved, upvoted)
- Twitter: add thread command
- Xiaohongshu: remove non-existent me command
- SKILL.md: expand Reddit examples with full 15-command reference
2026-03-17 01:33:43 +08:00
jackwener fbec2f6f5d feat(snapshot): filter contentinfo subtrees, bilibili ad URLs, boilerplate buttons
- Add contentinfo to subtree-level noise filtering (biggest single win)
  - Reuters: 51% → 62%, Google: 57% → 70%, Netflix: 48% → 60%
- Add cm.bilibili.com/cm/api/fees/ ad URL pattern
- Add 广告 keyword to ad detection
- Add back-to-top / 回到顶部 boilerplate button filtering
- Unify ad/boilerplate/contentinfo into single subtree-skip mechanism
- Add vitest config and comprehensive test suite (33 tests)
- Fixture tests skip gracefully when snapshot files are absent

Bump to v0.7.1
2026-03-17 01:26:49 +08:00
jackwener c2a5cbe90e chore(release): 0.7.0
Release / release (push) Has been cancelled
2026-03-16 20:29:27 +08:00
jackwener 34e20d33f2 docs: bump version in SKILL.md to 0.7.0 2026-03-16 20:29:27 +08:00
jackwener 1c496bb85f docs: add new twitter commands (article, follow, unfollow, bookmark, unbookmark)
Also update profile example to use positional argument.
2026-03-16 20:18:50 +08:00
jackwener 0c845d58c8 feat(twitter): implement article, profile, follow, unfollow, bookmark, & unbookmark adapters
This commit introduces the long-form Article adapter, a rewritten Profile adapter, and 4 new UI-based Write commands for managing relationships and bookmarks. Also adds support for positional arguments across the dynamic CLI engine.
2026-03-16 20:17:38 +08:00
jackwener 7f55950fed feat(reddit): add 11 new adapters borrowed from rdt-cli
Phase 1 - YAML adapters (read-only):
- popular: /r/popular feed
- read: read post + comments by ID
- user: view user profile (karma, account age)
- user-posts: user's submitted posts
- user-comments: user's comment history
- search: enhanced with sort/time/subreddit params
- subreddit: enhanced with time filter for top/controversial

Phase 2 - TypeScript adapters (write operations):
- upvote: upvote/downvote posts via /api/vote
- save: save/unsave posts via /api/save
- comment: post comments via /api/comment
- subscribe: subscribe/unsubscribe subreddits
- saved: browse saved posts (auto-resolves username)
- upvoted: browse upvoted posts (auto-resolves username)

Reddit adapters: 4 → 15
2026-03-16 19:56:55 +08:00
jackwener 1576396a21 0.6.3
Release / release (push) Has been cancelled
2026-03-16 19:33:25 +08:00
jackwener 77193a0003 Merge PR #20: feat(boss): add detail adapter + security_id in search
Closes #20

Added boss detail command with fixes:
- district/address field dedup
- template string injection safety
- empty jobInfo guard
- IPage-compatible wait
2026-03-16 19:33:17 +08:00
jackwener 05b7f1bccf fix(boss): improve detail adapter quality
- Fix district/address field duplication (district now uses areaDistrict·businessDistrict)
- Fix template string injection risk in evaluate script (use JSON.stringify)
- Add jobInfo empty guard with user-friendly error message
- Replace raw setTimeout with page.wait for IPage compatibility
- Update README docs to include boss detail command
2026-03-16 19:33:00 +08:00
jackwener 9889a6db11 v0.6.2
Release / release (push) Has been cancelled
2026-03-16 18:14:34 +08:00
jackwener 61ea05bff7 fix: URL injection, strictNullChecks, cross-platform build, +34 tests
Security:
- Fix URL injection in fetch.ts and bilibili.ts (JSON.stringify instead of string interpolation)
- Fix unused scroll() amount parameter

TypeScript:
- Enable strictNullChecks in tsconfig
- Change CliCommand.func signature to IPage (non-null) for browser adapters
- Fix 93 compile errors across all adapters

Build:
- Remove || true from build-manifest (report failures instead of silencing)
- Replace Unix shell commands with Node.js scripts for cross-platform builds

Code quality:
- Remove error-object special detection from pipeline executor
- Unify error handling to throw pattern

Tests:
- New interceptor.test.ts (11 tests)
- New executor.test.ts (13 tests)
- Rewrite output.test.ts with comprehensive coverage (10 tests)
2026-03-16 18:14:27 +08:00
xuelin e781d40408 feat(boss): add security_id to search output
Expose securityId in search results so users can pipe it to
`boss detail` for full job information.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 18:12:46 +08:00
xuelin c230f3e5ad feat(boss): add job detail adapter
Add `boss detail` command to fetch full job posting details using
securityId from search results.

Fields returned: job description, skills, welfare, boss info (name,
title, active time), company info (industry, scale, stage), address.

Tested with real API calls against multiple job postings.

Usage:
  opencli boss detail --security_id <id_from_search>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 18:09:49 +08:00
jackwener 3b2f88b2cf chore: sync package-lock.json version to 0.6.1 2026-03-16 17:38:13 +08:00
jackwener 7eec7ce89f fix: restore tests/ in vitest include for CI compatibility
vitest run tests/e2e/ intersects the CLI path with include patterns,
so tests/ must be in the include glob for CI to find test files.
2026-03-16 17:37:48 +08:00
AlexYue 788b069c02 feat: add E2E testing infrastructure with real Chrome in CI
## Changes

### E2E Test Suite (~52 test cases)
- public-commands.test.ts — Public API commands (hackernews, v2ex)
- browser-public.test.ts — Browser commands for public data across all sites
- browser-auth.test.ts — Graceful failure verification for login-required commands
- management.test.ts — Full coverage of management commands
- output-formats.test.ts — Output format validation (json/yaml/csv/md)
- smoke/api-health.test.ts — Scheduled API health checks

### Auto-detect Browser Mode
- buildMcpArgs uses CI env var to select mode:
  - Local (no CI) → --extension (connect to user's Chrome)
  - CI → standalone (launches its own browser)

### CI Pipeline
- e2e-headed.yml — Real Chrome via setup-chrome + xvfb in headed mode
- ci.yml — build + unit-test (2 shards) + smoke-test (scheduled/manual)
- Composite action for shared Chrome + xvfb setup

### Documentation
- New TESTING.md — Architecture, coverage, local setup, how to add tests

Co-authored-by: AlexYue <yj976240184@qq.com>
2026-03-16 17:35:16 +08:00
jackwener 433ad3a56a 0.6.1
Release / release (push) Has been cancelled
2026-03-16 14:20:22 +08:00
jackwener 50508b954e docs: expand bilibili commands, add setup hint after install, add doctor to troubleshooting 2026-03-16 14:16:54 +08:00
jackwener 35a843b8bd docs: use explicit PLAYWRIGHT_MCP_EXTENSION_TOKEN in auto-discover description 2026-03-16 14:16:12 +08:00
jackwener 486e513d07 fix(setup): only pre-select shell RC, let user choose other configs 2026-03-16 14:15:43 +08:00
jackwener 6c64f617c6 docs: add opencli setup to READMEs, remove hardcoded counts, update SKILL.md to v0.6.0 2026-03-16 14:13:38 +08:00
jackwener cd186bddd3 0.6.0
Release / release (push) Has been cancelled
2026-03-16 14:05:58 +08:00
jackwener 6486a42def fix(setup): clear screen before TUI to prevent page jumping 2026-03-16 14:04:38 +08:00
jackwener b308d5594a refactor(doctor/setup): polish UX and dedup code
- Doctor: chalk-colored output ([OK] green, [MISSING] red, etc.)
- Doctor: paths shortened with ~ and tool labels ([Codex], etc.)
- Doctor --fix: skip already-configured files
- TUI: hide cursor during interaction, proper Ctrl+C exit
- Setup: source hint after shell write, dedup shared helpers
- Tests: strip ANSI for assertions
2026-03-16 14:02:02 +08:00
jackwener 6f629260e7 feat: add interactive 'opencli setup' command with TUI checkbox
New zero-dependency TUI checkbox component (src/tui.ts) with:
  - ↑↓/jk navigation, Space toggle, Tab toggle+move
  - 'a' toggle all, Enter confirm, q/Esc cancel

New 'opencli setup' command (src/setup.ts) that:
  - Auto-discovers token from Chrome LevelDB extension
  - Shows interactive multi-select for config files
  - Displays tool names (Codex, Cursor, Claude Code, etc.)
  - Color-coded status (green=ok, yellow=mismatch, red=missing)
  - Applies changes only to selected files
2026-03-16 13:53:03 +08:00
jackwener 1e0b83bb84 feat(doctor): add Antigravity and Gemini CLI config paths
Add ~/.gemini/settings.json (Gemini CLI) and
~/.gemini/antigravity/mcp_config.json (Antigravity) to the
default MCP config scan list.
2026-03-16 13:45:02 +08:00
jackwener 1ff184e24d feat(doctor): add Claude Code and project .mcp.json config paths
Add ~/.claude.json (Claude Code user-scoped MCP config) and
.mcp.json (Claude Code project-scoped MCP config) to the
default config scan list.
2026-03-16 13:43:03 +08:00
jackwener 8e84145ccc feat(doctor): auto-discover extension token from Chrome LevelDB
Scan Chrome/Edge/Chromium localStorage LevelDB files to extract the
Playwright MCP Bridge auth-token directly from the extension's storage.
Uses a fast 'strings | grep' shell pipeline (~200ms) on macOS/Linux
with a pure-Node fallback for Windows.

The discovered token is now shown in 'opencli doctor' output and takes
priority as the recommended token when using '--fix'.
2026-03-16 13:38:48 +08:00
jackwener c77c8a8e3a feat: add Coupang search and add-to-cart adapters
Add browser-backed Coupang adapters for search and add-to-cart workflows.
- coupang search: multi-strategy data collection (API/JSON-LD/bootstrap/DOM), structured fields (price, rating, rocket, delivery), pagination and rocket filter support
- coupang add-to-cart: logged-in browser session reuse, stops before checkout
- coupang.ts: comprehensive data normalization layer with badge/rocket/delivery mapping
- browser-tab.ts: withTemporaryTab utility for isolated tab operations
- Unit tests for core normalization functions

Co-authored-by: CodeBBakGoSu <127713112+CodeBBakGoSu@users.noreply.github.com>
2026-03-16 13:30:42 +08:00
jackwener 9e024d4e46 0.5.2
Release / release (push) Has been cancelled
2026-03-16 13:22:18 +08:00
jackwener 34a9bff2b3 refactor: eliminate code duplication, improve type safety, add tests
- NEW: src/interceptor.ts — unified XHR/Fetch interceptor (was duplicated 3x)
- NEW: src/version.ts — centralized PKG_VERSION (was duplicated 2x)
- NEW: src/constants.ts — shared VOLATILE_PARAMS, FIELD_ROLES etc.
- NEW: src/engine.test.ts, src/registry.test.ts — 14 new unit tests

- browser.ts: use shared normalizeEval, interceptor, withTimeoutMs, PKG_VERSION
- intercept.ts, tap.ts: use shared interceptor generators
- cascade.ts: extract shared buildFetchProbeJs (90% dedup)
- engine.ts: use InternalCliCommand (no more 'as any' casts)
- executor.ts: remove all 15 'as StepHandler' type casts
- runtime.ts: add withTimeoutMs, IBrowserFactory interface
- registry.ts: add InternalCliCommand type for internal fields
- validate.ts: add pipeline step name validation
- output.ts: remove 'null as any' and '.filter(() => true)' hacks
- explore.ts, synthesize.ts: use shared constants
- docs: fix V2EX commands (3→6), SKILL.md version, verify example

Tests: 88 passed (was 74), tsc --noEmit: 0 errors
2026-03-16 13:22:12 +08:00
jackwener b0c51ddf19 fix: use Playwright MCP --executable-path flag (kebab-case)
Extract buildMcpArgs() helper and fix --executablePath → --executable-path
to match the Playwright MCP CLI's expected flag format.

Closes #16

Co-authored-by: KasumiChen <KasumiChen@users.noreply.github.com>
2026-03-16 12:58:18 +08:00
759 changed files with 69490 additions and 3557 deletions
@@ -0,0 +1,249 @@
---
name: cross-project-adapter-migration
description: "Cross-project CLI command migration workflow for opencli. Use when importing commands from external CLI projects (python/node) like rdt-cli, twitter-cli, etc. Covers: source analysis → gap matrix → batch migration → README/SKILL.md update."
---
# Cross-Project Adapter Migration
> 从外部 CLI 项目(Python/Node/Go 等)批量迁移命令到 opencli 的标准化流程。
## When to Use
- 用户说"把 xxx-cli 的命令迁移过来"
- 用户说"看看 xxx 项目有什么可以借鉴的"
- 用户说"对齐 xxx-cli 的功能"
- 在为新平台扩展 opencli 时,发现已有第三方 CLI 工具
## Prerequisites
- 熟悉 [CLI-EXPLORER.md](file:///Users/jakevin/code/opencli/CLI-EXPLORER.md)adapter 开发决策树)
- 熟悉 [SKILL.md](file:///Users/jakevin/code/opencli/SKILL.md)(命令参考 & 模板)
---
## Phase 1: 源项目分析
### 1.1 克隆 & 理解源项目
```bash
# 克隆源项目到 /tmp 做分析
git clone <source_repo_url> /tmp/<source-cli>
```
分析重点:
- **命令列表**:找到所有可用命令(查看 CLI 入口文件、help 输出或 README
- **认证方式**CookieAPI KeyOAuth?浏览器自动化?
- **数据源**:公开 APIGraphQL?页面抓取?
- **输出字段**:每个命令返回哪些数据字段
### 1.2 生成命令清单
列出源项目所有命令,包括:
| 命令 | 类型 | API/方法 | 输出字段 |
|------|------|---------|---------|
| `xxx feed` | Read | `GET /api/feed` | title, author, time |
| `xxx post` | Write | `POST /api/tweet` | status, id |
---
## Phase 2: 功能对比矩阵
### 2.1 查看 opencli 现有命令
```bash
ls src/clis/<site>/ # 查看已有适配器
opencli list | grep <site> # 确认已注册命令
```
### 2.2 生成对比矩阵
对每个源项目命令,标注三种状态:
| 功能 | 源项目 | opencli 现有 | 行动 |
|------|--------|-------------|------|
| feed | ✅ `xxx feed` | ❌ 无 | ✅ **新增** |
| search | ✅ `xxx search` | ✅ `search.ts` | ❌ 已有,跳过 |
| hot | ✅ `xxx hot` | ⚠️ `hot.yaml`(不完整) | ✅ **增强** |
| like | ✅ `xxx like` | ✅ `like.ts` | ❌ 已有,跳过 |
### 2.3 筛选迁移目标
去掉已有的、低价值的,保留高价值缺失命令,按 Read/Write 分类:
**筛选原则**
- ✅ 高使用频率的命令优先
- ✅ 已有但不完整的命令标记为"增强"
- ❌ 源项目特有但 opencli 架构不支持的功能(如需要持久化存储的)跳过
- ❌ 与现有功能完全重复的跳过
---
## Phase 3: 批量实现
> [!IMPORTANT]
> 实现前必须查阅 [CLI-EXPLORER.md](file:///Users/jakevin/code/opencli/CLI-EXPLORER.md) 确认策略选择。
### 3.1 选择实现方式
基于决策树分类:
| 类别 | 方式 | 适用条件 |
|------|------|---------|
| **Read + 简单 API** | YAML pipeline | 纯 fetch/select/map,无复杂 JS |
| **Read + GraphQL/分页/签名** | TypeScript adapter | 需要 JS 逻辑 |
| **Write 操作** | TypeScript + `Strategy.UI` | 点击/输入等 DOM 操作 |
| **Write + API** | TypeScript + `Strategy.COOKIE/HEADER` | 直接 POST API |
### 3.2 实现顺序
**先 Read 后 Write,先 YAML 后 TS**
1. **Phase A**: YAML Read 适配器(最快,通常每个 10-20 行)
2. **Phase B**: TS Read 适配器(需要 evaluate/intercept 的)
3. **Phase C**: TS Write 适配器(需 UI 自动化或 POST API
### 3.3 实现模板
#### YAML Read 适配器模板(Cookie 策略)
```yaml
site: <site>
name: <command>
description: <描述>
domain: www.<site>.com
strategy: cookie
browser: true
args:
limit:
type: int
default: 20
pipeline:
- navigate: https://www.<site>.com
- evaluate: |
(async () => {
const res = await fetch('<api_endpoint>', { credentials: 'include' });
const d = await res.json();
return (d.data?.items || []).map(item => ({
title: item.title,
// ... map source fields
}));
})()
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
- limit: ${{ args.limit }}
columns: [rank, title]
```
#### TS Write 适配器模板(UI 策略)
```typescript
import { cli, Strategy } from '../../registry.js';
cli({
site: '<site>',
name: '<command>',
description: '<描述>',
strategy: Strategy.UI,
args: [{ name: 'target', required: true, help: '<参数说明>' }],
columns: ['status', 'message'],
func: async (page, kwargs) => {
await page.goto(`https://www.<site>.com/${kwargs.target}`);
await page.wait({ text: '<expected_text>', timeout: 10 });
// 获取 snapshot 找到目标按钮
const snapshot = await page.accessibility.snapshot();
// 点击按钮 ...
return [{ status: 'success', message: '<action> completed' }];
},
});
```
### 3.4 公共模式复用
迁移过程中如果发现多个适配器共享逻辑,考虑提取到 `src/clis/<site>/utils.ts` 工具文件:
```typescript
// src/clis/<site>/utils.ts
export async function fetchWithAuth(page, url) { ... }
export function parseItem(raw) { ... }
```
---
## Phase 4: 验证 & 发布
### 4.1 构建验证
```bash
npx tsc --noEmit # TypeScript 编译检查
opencli list | grep <site> # 确认所有命令已注册
```
### 4.2 运行验证(关键!)
每个新命令必须实际运行:
```bash
# Read 命令
opencli <site> <command> --limit 3 -f json
opencli <site> <command> --limit 3 -v # verbose 查看 pipeline
# Write 命令(谨慎!会实际操作)
opencli <site> <command> <test_target>
```
### 4.3 更新文档
迁移完成后必须更新以下文件:
1. **README.md** — 在对应平台区域添加新命令示例
2. **SKILL.md** — 在 Commands Reference 中添加新命令
### 4.4 提交 & 推送
```bash
git add -A
git commit -m "feat(<site>): migrate <N> commands from <source-cli>
- Phase A: <N> YAML adapters (read operations)
- Phase B: <N> TS adapters (write operations)
- Source: <source_repo_url>"
git push
```
---
## Checklist
- [ ] 源项目命令清单已生成
- [ ] 对比矩阵已确认,高价值缺失命令已筛选
- [ ] 用户确认迁移范围
- [ ] Phase A: YAML Read 适配器已完成
- [ ] Phase B: TS Read 适配器已完成
- [ ] Phase C: TS Write 适配器已完成
- [ ] `npx tsc --noEmit` 编译通过
- [ ] 所有新命令已实际运行验证
- [ ] README.md 已更新
- [ ] SKILL.md 已更新
- [ ] 已 commit + push
## 实战案例参考
### rdt-cli → opencli Reddit2026-03-16
- **源项目**: `rdt-cli`25 个 Python 命令)
- **筛选结果**: 13 个高价值命令
- **实现**: 7 个 YAMLread + 6 个 TSwrite
- **产出**: +11 文件,+767 行代码,Reddit 适配器从 4 → 15+275%
### twitter-cli → opencli Twitter2026-03-16
- **源项目**: `twitter-cli`20+ Python 命令)
- **筛选结果**: 11 个待实现
- **策略**: Read 用 `Strategy.COOKIE` + GraphQL fetchWrite 用 `Strategy.UI`
@@ -0,0 +1,54 @@
---
description: Migrate commands from an external CLI project into opencli adapters
---
// turbo-all
## Steps
1. Clone the source CLI project for analysis:
```bash
git clone <source_repo_url> /tmp/<source-cli>
```
2. Analyze source project: list all commands, auth method, API endpoints, and output fields.
3. Check existing opencli adapters for the target site:
```bash
ls src/clis/<site>/
opencli list | grep <site>
```
4. Generate a comparison matrix table (source commands vs opencli existing). Mark each as: ✅ **New** / ✅ **Enhance** / ❌ **Skip**. Ask user to confirm which commands to migrate.
5. Implement YAML Read adapters first (highest ROI, 10-20 lines each). Place files in `src/clis/<site>/<name>.yaml`.
6. Implement TS Read adapters for complex cases (GraphQL, pagination, signing). Place files in `src/clis/<site>/<name>.ts`.
7. Implement TS Write adapters using `Strategy.UI` or `Strategy.COOKIE`. Place files in `src/clis/<site>/<name>.ts`.
8. Verify build:
```bash
npx tsc --noEmit
```
9. Verify all commands are registered:
```bash
opencli list | grep <site>
```
10. Run each new command to verify it works:
```bash
opencli <site> <command> --limit 3 -f json
```
11. Update README.md with new command examples in the appropriate platform section.
12. Update SKILL.md Commands Reference with new commands.
13. Commit and push:
```bash
git add -A
git commit -m "feat(<site>): migrate <N> commands from <source-cli>"
git push
```
+83
View File
@@ -0,0 +1,83 @@
name: "🐛 Bug Report"
description: Report a bug or unexpected behavior in OpenCLI
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report a bug. A short reproduction and any error output are usually enough.
- type: textarea
id: description
attributes:
label: Description
description: A clear and concise description of the bug.
placeholder: What happened?
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: How can we reproduce this behavior?
value: |
1. Run `opencli ...`
2. ...
3. See error
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: What did you expect to happen?
validations:
required: true
- type: input
id: version
attributes:
label: OpenCLI Version
description: "Run `opencli --version` to find out."
placeholder: "0.8.0"
validations:
required: true
- type: dropdown
id: node-version
attributes:
label: Node.js Version
options:
- "20.x"
- "22.x"
- Other
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating System
options:
- macOS
- Linux
- Windows
- Other
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs / Screenshots
description: |
Paste any relevant error output. Run with `-v` for verbose logs:
```
opencli <command> -v
```
render: shell
validations:
required: false
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: 📖 Documentation
url: https://github.com/jackwener/opencli#readme
about: Check the README and docs before opening an issue.
- name: 🧪 Testing Guide
url: https://github.com/jackwener/opencli/blob/main/TESTING.md
about: How to run and write tests for OpenCLI.
@@ -0,0 +1,42 @@
name: "✨ Feature Request"
description: Suggest a new feature or improvement
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Have an idea to make OpenCLI better? We'd love to hear it!
- type: textarea
id: description
attributes:
label: Feature Description
description: A clear and concise description of the feature you'd like.
validations:
required: true
- type: textarea
id: use-case
attributes:
label: Use Case
description: What problem does this solve? Who benefits from this feature?
placeholder: "As a user, I want to ... so that ..."
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: Proposed Solution
description: If you have a specific implementation in mind, describe it here.
validations:
required: false
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Any alternative approaches you've thought about?
validations:
required: false
@@ -0,0 +1,57 @@
name: "🌐 New Site Adapter Request"
description: Request support for a new website
title: "[Site]: "
labels: ["new-adapter"]
body:
- type: markdown
attributes:
value: |
Want OpenCLI to support a new site? Tell us about it!
- type: input
id: site-name
attributes:
label: Site Name
description: The name of the website.
placeholder: "e.g. Product Hunt"
validations:
required: true
- type: input
id: site-url
attributes:
label: Site URL
description: The main URL of the website.
placeholder: "https://www.producthunt.com"
validations:
required: true
- type: textarea
id: commands
attributes:
label: Desired Commands
description: What commands would you like? List them with a brief description.
value: |
- `hot` — trending / popular items
- `search` — search the site
validations:
required: true
- type: textarea
id: api-examples
attributes:
label: Example Links or API Endpoints
description: Share any example page URLs or API endpoints if you have them (optional).
placeholder: |
Example page: https://www.producthunt.com/posts/example
GET https://api.producthunt.com/v2/posts?order=votes
Response: { "posts": [{ "name": "...", "tagline": "..." }] }
validations:
required: false
- type: checkboxes
id: contribution
attributes:
label: Willing to Contribute?
options:
- label: I'm willing to submit a PR for this adapter
+27
View File
@@ -0,0 +1,27 @@
name: Setup Chrome
description: Install real Chrome for browser testing (with xvfb on Linux)
outputs:
chrome-path:
description: Path to the installed Chrome binary
value: ${{ steps.setup-chrome.outputs.chrome-path }}
runs:
using: composite
steps:
- name: Install real Chrome (stable)
uses: browser-actions/setup-chrome@v1
id: setup-chrome
with:
chrome-version: stable
- name: Verify Chrome installation
shell: bash
run: |
echo "Chrome path: ${{ steps.setup-chrome.outputs.chrome-path }}"
"${{ steps.setup-chrome.outputs.chrome-path }}" --version
- name: Install xvfb (Linux only)
if: runner.os == 'Linux'
shell: bash
run: sudo apt-get install -y xvfb
+27
View File
@@ -0,0 +1,27 @@
version: 2
updates:
# npm dependencies
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 10
labels:
- "dependencies"
commit-message:
prefix: "chore(deps)"
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 5
labels:
- "dependencies"
- "ci"
commit-message:
prefix: "chore(ci)"
+33
View File
@@ -0,0 +1,33 @@
## Description
<!-- Briefly describe your changes and link to any related issues. -->
Related issue:
## Type of Change
- [ ] 🐛 Bug fix
- [ ] ✨ New feature
- [ ] 🌐 New site adapter
- [ ] 📝 Documentation
- [ ] ♻️ Refactor
- [ ] 🔧 CI / build / tooling
## Checklist
- [ ] I ran the checks relevant to this PR
- [ ] I updated tests or docs if needed
- [ ] I included output or screenshots when useful
### Documentation (if adding/modifying an adapter)
- [ ] Added doc page under `docs/adapters/` (if new adapter)
- [ ] Updated `docs/adapters/index.md` table (if new adapter)
- [ ] Updated sidebar in `docs/.vitepress/config.mts` (if new adapter)
- [ ] Updated `README.md` / `README.zh-CN.md` when command discoverability changed
- [ ] Used positional args for the command's primary subject unless a named flag is clearly better
- [ ] Normalized expected adapter failures to `CliError` subclasses instead of raw `Error`
## Screenshots / Output
<!-- If applicable, paste CLI output or screenshots here. -->
+67
View File
@@ -0,0 +1,67 @@
name: Build Chrome Extension
on:
push:
branches: [ "main" ]
tags: [ "v*.*.*" ]
paths:
- 'extension/**'
- '.github/workflows/build-extension.yml'
pull_request:
branches: [ "main" ]
paths:
- 'extension/**'
- '.github/workflows/build-extension.yml'
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 20
cache: 'npm'
cache-dependency-path: extension/package-lock.json
- name: Install extension dependencies
run: npm ci
working-directory: extension
- name: Build extension
run: npm run build
working-directory: extension
- name: Prepare extension package
run: npm run package:release -- --out ../extension-package
working-directory: extension
- name: Create Extension ZIP
run: |
cd extension-package
zip -r ../opencli-extension.zip .
- name: Upload Artifacts (Action Run)
uses: actions/upload-artifact@v7
with:
name: opencli-extension-build
path: |
opencli-extension.zip
retention-days: 7
- name: Attach to GitHub Release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2.6.1
with:
files: |
opencli-extension.zip
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+128 -6
View File
@@ -2,19 +2,32 @@ name: CI
on:
push:
branches: [main]
branches: [main, dev]
pull_request:
branches: [main]
branches: [main, dev]
schedule:
- cron: '0 8 * * 1' # Weekly Monday 08:00 UTC — smoke tests
workflow_dispatch:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
# ── Fast gate: typecheck + build ──
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
@@ -24,3 +37,112 @@ jobs:
- name: Build
run: npm run build
# ── Unit tests (vitest shard) ──
unit-test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: ['20', '22']
shard: [1, 2]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests (Node ${{ matrix.node-version }}, shard ${{ matrix.shard }}/2)
run: npm test -- --reporter=verbose --shard=${{ matrix.shard }}/2
# ── Bun compatibility check ──
bun-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.5
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests under Bun
run: bun vitest run --project unit --reporter=verbose
adapter-test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
needs: build
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run focused adapter tests
run: npm run test:adapter -- --reporter=verbose
# ── Smoke tests (scheduled / manual only) ──
smoke-test:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
needs: build
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# NOTE: Windows excluded — browser-actions/setup-chrome hangs during
# Chrome MSI installation on Windows runners (known issue).
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup Chrome
uses: ./.github/actions/setup-chrome
id: setup-chrome
- name: Build
run: npm run build
- name: Run smoke tests (Linux, via xvfb)
if: runner.os == 'Linux'
run: |
xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" \
npx vitest run tests/smoke/ --reporter=verbose
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
- name: Run smoke tests (macOS / Windows)
if: runner.os != 'Linux'
run: npx vitest run tests/smoke/ --reporter=verbose
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
timeout-minutes: 15
+36
View File
@@ -0,0 +1,36 @@
name: Doc Check
on:
pull_request:
branches: [main, dev]
concurrency:
group: doc-check-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── Adapter doc coverage ──
doc-coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check adapter doc coverage
run: bash scripts/check-doc-coverage.sh --strict
# ── VitePress build validation ──
docs-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build docs (catches broken links & sidebar refs)
run: npm run docs:build
+17
View File
@@ -0,0 +1,17 @@
name: Trigger Website Rebuild (Docs Updated)
on:
push:
branches: [main]
paths: ['docs/**']
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Trigger opencli-website rebuild
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.WEBSITE_DEPLOY_TOKEN }}
repository: jackwener/opencli-website
event-type: docs-updated
+74
View File
@@ -0,0 +1,74 @@
name: E2E Headed Chrome
on:
push:
branches: [main, dev]
paths:
- 'extension/**'
- 'src/browser/**'
- 'src/daemon.ts'
- 'src/execution.ts'
- 'src/interceptor.ts'
- 'tests/e2e/**'
- 'tests/smoke/**'
- '.github/actions/setup-chrome/**'
- '.github/workflows/e2e-headed.yml'
pull_request:
branches: [main, dev]
paths:
- 'extension/**'
- 'src/browser/**'
- 'src/daemon.ts'
- 'src/execution.ts'
- 'src/interceptor.ts'
- 'tests/e2e/**'
- 'tests/smoke/**'
- '.github/actions/setup-chrome/**'
- '.github/workflows/e2e-headed.yml'
workflow_dispatch:
concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e-headed:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# NOTE: Windows excluded — browser-actions/setup-chrome hangs during
# Chrome MSI installation on Windows runners (known issue).
os: [ubuntu-latest, macos-latest]
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup Chrome
uses: ./.github/actions/setup-chrome
id: setup-chrome
- name: Build
run: npm run build
- name: Run E2E tests (Linux, via xvfb)
if: runner.os == 'Linux'
run: |
xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" \
npx vitest run tests/e2e/ --reporter=verbose
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
- name: Run E2E tests (macOS / Windows)
if: runner.os != 'Linux'
run: npx vitest run tests/e2e/ --reporter=verbose
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
+10 -6
View File
@@ -13,9 +13,9 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
@@ -26,11 +26,8 @@ jobs:
- name: Type check
run: npx tsc --noEmit
- name: Build
run: npm run build
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v2.6.1
with:
generate_release_notes: true
@@ -38,3 +35,10 @@ jobs:
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Trigger website rebuild
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.WEBSITE_DEPLOY_TOKEN }}
repository: jackwener/opencli-website
event-type: version-released
+33
View File
@@ -0,0 +1,33 @@
name: Security Audit
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
schedule:
- cron: '0 9 * * 1' # Weekly Monday 09:00 UTC
permissions:
contents: read
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: true
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: npm audit (production)
run: npm audit --omit=dev --audit-level=high
+20
View File
@@ -1,4 +1,24 @@
node_modules/
dist/
!extension/dist/
*.tsbuildinfo
.opencli/
.mcp.json
*.log
.DS_Store
# VitePress
docs/.vitepress/dist
docs/.vitepress/cache
# Extensions & Secrets
*.pem
*.crx
*.zip
.envrc
.windsurf
.claude
.cortex
# Database files
*.db
+237
View File
@@ -0,0 +1,237 @@
# Changelog
## [1.4.1](https://github.com/jackwener/opencli/compare/v1.4.0...v1.4.1) (2026-03-25)
### Features
* **douyin:** add Douyin creator center adapter — 14 commands, 8-phase publish pipeline ([#416](https://github.com/jackwener/opencli/issues/416))
* **weibo,youtube:** add Weibo commands and YouTube channel/comments ([#418](https://github.com/jackwener/opencli/issues/418))
* **twitter:** add filter option for search ([#410](https://github.com/jackwener/opencli/issues/410))
* **extension:** add popup UI, privacy policy, and CSP for Chrome Web Store ([#415](https://github.com/jackwener/opencli/issues/415))
* add url field to 9 search adapters (67% -> 97% coverage) ([#414](https://github.com/jackwener/opencli/issues/414))
### Bug Fixes
* **extension:** improve UX when daemon is not running — show hint in popup, reduce reconnect noise ([#424](https://github.com/jackwener/opencli/issues/424))
* remove incorrect gws and readwise external CLI entries ([#419](https://github.com/jackwener/opencli/issues/419), [#420](https://github.com/jackwener/opencli/issues/420))
### CI
* limit default e2e to bilibili/zhihu/v2ex, gate extended browser tests ([#421](https://github.com/jackwener/opencli/issues/421), [#423](https://github.com/jackwener/opencli/issues/423))
## [1.4.0](https://github.com/jackwener/opencli/compare/v1.3.3...v1.4.0) (2026-03-25)
### Features
* **pixiv:** add Pixiv adapter — ranking, search, user illusts, detail, download ([#403](https://github.com/jackwener/opencli/issues/403))
* **plugin:** add lifecycle hooks API — onStartup, onBeforeExecute, onAfterExecute ([#376](https://github.com/jackwener/opencli/issues/376))
* **plugin:** validate plugin structure on install and update ([#364](https://github.com/jackwener/opencli/issues/364))
* **xueqiu:** add Danjuan fund account commands — fund-holdings, fund-snapshot ([#391](https://github.com/jackwener/opencli/issues/391))
* **tiktok:** add video URL to search results ([#404](https://github.com/jackwener/opencli/issues/404))
* **linkedin:** add timeline feed command ([#342](https://github.com/jackwener/opencli/issues/342))
* **jd:** add JD.com product details adapter ([#344](https://github.com/jackwener/opencli/issues/344))
* **web:** add generic `web read` command for any URL → Markdown ([#343](https://github.com/jackwener/opencli/issues/343))
* **dictionary:** add dictionary search, synonyms, and examples adapters ([#241](https://github.com/jackwener/opencli/issues/241))
### Bug Fixes
* **analysis:** fix hasLimit using wrong Set (SEARCH_PARAMS → LIMIT_PARAMS) ([#412](https://github.com/jackwener/opencli/issues/412))
* **pipeline:** remove phantom scroll step — declared but never registered ([#412](https://github.com/jackwener/opencli/issues/412))
* **validate:** add missing download step to KNOWN_STEP_NAMES ([#412](https://github.com/jackwener/opencli/issues/412))
* **extension:** security hardening — tab isolation, URL validation, cookie scope ([#409](https://github.com/jackwener/opencli/issues/409))
* **sort:** use localeCompare with natural numeric sort by default ([#306](https://github.com/jackwener/opencli/issues/306))
* **pipeline:** evaluate chained || in template engine ([#305](https://github.com/jackwener/opencli/issues/305))
* **pipeline:** check HTTP status in fetch step ([#384](https://github.com/jackwener/opencli/issues/384))
* **plugin:** resolve Windows path and symlink issues ([#400](https://github.com/jackwener/opencli/issues/400))
* **download:** scope cookies to target domain ([#385](https://github.com/jackwener/opencli/issues/385))
* **extension:** fix same-url navigation timeout ([#380](https://github.com/jackwener/opencli/issues/380))
* fix ChatWise Windows connect ([#405](https://github.com/jackwener/opencli/issues/405))
* resolve 6 critical + 11 important bugs from deep code review ([#337](https://github.com/jackwener/opencli/issues/337), [#340](https://github.com/jackwener/opencli/issues/340))
* harden security-sensitive execution paths ([#335](https://github.com/jackwener/opencli/issues/335))
* **stealth:** harden anti-detection against advanced fingerprinting ([#357](https://github.com/jackwener/opencli/issues/357))
### Code Quality
* replace all `catch (err: any)` with typed `getErrorMessage()` across 13 files ([#412](https://github.com/jackwener/opencli/issues/412))
* adopt CliError subclasses in social and desktop adapters ([#367](https://github.com/jackwener/opencli/issues/367), [#372](https://github.com/jackwener/opencli/issues/372), [#375](https://github.com/jackwener/opencli/issues/375))
* simplify codebase with type dedup, shared analysis module, and consistent naming ([#373](https://github.com/jackwener/opencli/issues/373))
* **ci:** add cross-platform CI matrix (Linux/macOS/Windows) ([#402](https://github.com/jackwener/opencli/issues/402))
## [1.3.3](https://github.com/jackwener/opencli/compare/v1.3.2...v1.3.3) (2026-03-25)
### Features
* **browser:** add stealth anti-detection for CDP and daemon modes ([#319](https://github.com/jackwener/opencli/issues/319))
### Bug Fixes
* **stealth:** review fixes — guard plugins, rewrite stack trace cleanup ([#320](https://github.com/jackwener/opencli/issues/320))
## [1.3.2](https://github.com/jackwener/opencli/compare/v1.3.1...v1.3.2) (2026-03-24)
### Features
* **error-handling:** refine error handling with semantic error types and emoji-coded output ([#312](https://github.com/jackwener/opencli/issues/312)) ([b4d64ca](https://github.com/jackwener/opencli/commit/b4d64ca))
### Bug Fixes
* **security:** replace execSync with execFileSync to prevent command injection ([#309](https://github.com/jackwener/opencli/issues/309)) ([41aedf6](https://github.com/jackwener/opencli/commit/41aedf6))
* remove duplicate getErrorMessage import in discovery.ts ([#315](https://github.com/jackwener/opencli/issues/315)) ([75f4237](https://github.com/jackwener/opencli/commit/75f4237))
* **e2e:** broaden xiaoyuzhou skip logic for overseas CI runners ([#316](https://github.com/jackwener/opencli/issues/316)) ([a170873](https://github.com/jackwener/opencli/commit/a170873))
### Documentation
* **SKILL.md:** sync command reference — add missing sites and desktop adapters ([#314](https://github.com/jackwener/opencli/issues/314)) ([8bf750c](https://github.com/jackwener/opencli/commit/8bf750c))
### Chores
* pre-release cleanup — fix dependencies, sync docs, reduce code duplication ([#311](https://github.com/jackwener/opencli/issues/311)) ([c9b3568](https://github.com/jackwener/opencli/commit/c9b3568))
## [1.3.1](https://github.com/jackwener/opencli/compare/v1.3.0...v1.3.1) (2026-03-22)
### Features
* **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))
* **yollomi:** add new commands and update documentation ([#235](https://github.com/jackwener/opencli/issues/235)) ([ea83242](https://github.com/jackwener/opencli/commit/ea83242))
* **record:** add live recording command for API capture ([#300](https://github.com/jackwener/opencli/issues/300)) ([dff0fe5](https://github.com/jackwener/opencli/commit/dff0fe5))
* **weibo:** add weibo search command ([#299](https://github.com/jackwener/opencli/issues/299)) ([c7895ea](https://github.com/jackwener/opencli/commit/c7895ea))
* **v2ex:** add node, user, member, replies, nodes commands ([#282](https://github.com/jackwener/opencli/issues/282)) ([a83027d](https://github.com/jackwener/opencli/commit/a83027d))
* **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))
* **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))
* **doubao:** add doubao browser adapter ([#277](https://github.com/jackwener/opencli/issues/277)) ([9cdc127](https://github.com/jackwener/opencli/commit/9cdc127))
* **xiaohongshu:** add publish command for 图文 note automation ([#276](https://github.com/jackwener/opencli/issues/276)) ([a6d993f](https://github.com/jackwener/opencli/commit/a6d993f))
* **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))
### Bug Fixes
* **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))
* **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))
* ensure standard PATH is available for external CLIs ([#285](https://github.com/jackwener/opencli/issues/285)) ([22f5c7a](https://github.com/jackwener/opencli/commit/22f5c7a))
* **xiaohongshu:** scope image selector to avoid downloading avatars ([#293](https://github.com/jackwener/opencli/issues/293)) ([3a21be6](https://github.com/jackwener/opencli/commit/3a21be6))
* add turndown dependency to package.json ([#288](https://github.com/jackwener/opencli/issues/288)) ([2a52906](https://github.com/jackwener/opencli/commit/2a52906))
## [1.3.0](https://github.com/jackwener/opencli/compare/v1.2.3...v1.3.0) (2026-03-21)
### Features
* **daemon:** harden security against browser CSRF attacks ([#268](https://github.com/jackwener/opencli/issues/268)) ([40bd11d](https://github.com/jackwener/opencli/commit/40bd11d))
### Performance
* smart page settle via DOM stability detection ([#271](https://github.com/jackwener/opencli/issues/271)) ([4b976da](https://github.com/jackwener/opencli/commit/4b976da))
### Refactoring
* 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))
## [1.2.3](https://github.com/jackwener/opencli/compare/v1.2.2...v1.2.3) (2026-03-21)
### Bug Fixes
* 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))
* harden resolveTabId against New Tab Override extension interception ([#255](https://github.com/jackwener/opencli/issues/255)) ([112fdef](https://github.com/jackwener/opencli/commit/112fdef))
## [1.2.2](https://github.com/jackwener/opencli/compare/v1.2.1...v1.2.2) (2026-03-21)
### Bug Fixes
* 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))
## [1.2.1](https://github.com/jackwener/opencli/compare/v1.2.0...v1.2.1) (2026-03-21)
### Bug Fixes
* **twitter:** harden timeline review findings ([#236](https://github.com/jackwener/opencli/issues/236)) ([4cd0409](https://github.com/jackwener/opencli/commit/4cd0409))
* **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))
* 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))
## [1.2.0](https://github.com/jackwener/opencli/compare/v1.1.0...v1.2.0) (2026-03-21)
### Features
* **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))
* **devto:** add devto adapter ([#234](https://github.com/jackwener/opencli/issues/234)) ([ea113a6](https://github.com/jackwener/opencli/commit/ea113a6))
* **twitter:** add --type flag to timeline command ([#83](https://github.com/jackwener/opencli/issues/83)) ([e98cf75](https://github.com/jackwener/opencli/commit/e98cf75))
* **google:** add search, suggest, news, and trends adapters ([#184](https://github.com/jackwener/opencli/issues/184)) ([4e32599](https://github.com/jackwener/opencli/commit/4e32599))
* 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))
* **xueqiu:** add earnings-date command ([#211](https://github.com/jackwener/opencli/issues/211)) ([fae1dce](https://github.com/jackwener/opencli/commit/fae1dce))
* **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))
* **instagram,facebook:** add write actions and extended commands ([#201](https://github.com/jackwener/opencli/issues/201)) ([eb0ccaf](https://github.com/jackwener/opencli/commit/eb0ccaf))
* **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))
* **tiktok:** add TikTok adapter with 15 commands ([#202](https://github.com/jackwener/opencli/issues/202)) ([4391ccf](https://github.com/jackwener/opencli/commit/4391ccf))
* add Lobste.rs, Instagram, and Facebook adapters ([#199](https://github.com/jackwener/opencli/issues/199)) ([ce484c2](https://github.com/jackwener/opencli/commit/ce484c2))
* **medium:** add medium adapter ([#190](https://github.com/jackwener/opencli/issues/190)) ([06c902a](https://github.com/jackwener/opencli/commit/06c902a))
* plugin system (Stage 0-2) ([1d39295](https://github.com/jackwener/opencli/commit/1d39295))
* make primary args positional across all CLIs ([#242](https://github.com/jackwener/opencli/issues/242)) ([9696db9](https://github.com/jackwener/opencli/commit/9696db9))
* **xueqiu:** make primary args positional ([#213](https://github.com/jackwener/opencli/issues/213)) ([fb2a145](https://github.com/jackwener/opencli/commit/fb2a145))
### Refactoring
* replace hardcoded skipPreNav with declarative navigateBefore field ([#208](https://github.com/jackwener/opencli/issues/208)) ([a228758](https://github.com/jackwener/opencli/commit/a228758))
* **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))
* type discovery core ([#219](https://github.com/jackwener/opencli/issues/219)) ([bd274ce](https://github.com/jackwener/opencli/commit/bd274ce))
* type browser core ([#218](https://github.com/jackwener/opencli/issues/218)) ([28c393e](https://github.com/jackwener/opencli/commit/28c393e))
* type pipeline core ([#217](https://github.com/jackwener/opencli/issues/217)) ([8a4ea41](https://github.com/jackwener/opencli/commit/8a4ea41))
* reduce core any usage ([#216](https://github.com/jackwener/opencli/issues/216)) ([45cee57](https://github.com/jackwener/opencli/commit/45cee57))
* fail fast on invalid pipeline steps ([#237](https://github.com/jackwener/opencli/issues/237)) ([c76f86c](https://github.com/jackwener/opencli/commit/c76f86c))
## [1.1.0](https://github.com/jackwener/opencli/compare/v1.0.6...v1.1.0) (2026-03-20)
### Features
* add antigravity serve command — Anthropic API proxy ([35a0fed](https://github.com/jackwener/opencli/commit/35a0fed8a0c1cb714298f672c19f017bbc9a9630))
* add arxiv and wikipedia adapters ([#132](https://github.com/jackwener/opencli/issues/132)) ([3cda14a](https://github.com/jackwener/opencli/commit/3cda14a2ab502e3bebfba6cdd9842c35b2b66b41))
* add external CLI hub for discovery, auto-installation, and execution of external tools. ([b3e32d8](https://github.com/jackwener/opencli/commit/b3e32d8a05744c9bcdfef96f5ff3085ac72bd353))
* add sinafinance 7x24 news adapter ([#131](https://github.com/jackwener/opencli/issues/131)) ([02793e9](https://github.com/jackwener/opencli/commit/02793e990ef4bdfdde9d7a748960b8a9ed6ea988))
* **boss:** add 8 new recruitment management commands ([#133](https://github.com/jackwener/opencli/issues/133)) ([7e973ca](https://github.com/jackwener/opencli/commit/7e973ca59270029f33021a483ca4974dc3975d36))
* **serve:** implement auto new conv, model mapping, and precise completion detection ([0e8c96b](https://github.com/jackwener/opencli/commit/0e8c96b6d9baebad5deb90b9e0620af5570b259d))
* **serve:** use CDP mouse click + Input.insertText for reliable message injection ([c63af6d](https://github.com/jackwener/opencli/commit/c63af6d41808dddf6f0f76789aa6c042f391f0b0))
* xiaohongshu creator flows migration ([#124](https://github.com/jackwener/opencli/issues/124)) ([8f17259](https://github.com/jackwener/opencli/commit/8f1725982ec06d121d7c15b5cf3cda2f5941c32a))
### Bug Fixes
* **docs:** use base '/' for custom domain and add CNAME file ([#129](https://github.com/jackwener/opencli/issues/129)) ([2876750](https://github.com/jackwener/opencli/commit/2876750891bc8a66be577b06ead4db61852c8e81))
* **serve:** update model mappings to match actual Antigravity UI ([36bc57a](https://github.com/jackwener/opencli/commit/36bc57a9624cdfaa50ffb2c1ad7f9c518c5e6c55))
* type safety for wikiFetch and arxiv abstract truncation ([4600b9d](https://github.com/jackwener/opencli/commit/4600b9d46dc7b56ff564c5f100c3a94c6a792c06))
* use UTC+8 for XHS timestamp formatting (CI timezone fix) ([03f067d](https://github.com/jackwener/opencli/commit/03f067d90764487f0439705df36e1a5c969a7f98))
* **xiaohongshu:** use fixed UTC+8 offset in trend timestamp formatting (CI timezone fix) ([593436e](https://github.com/jackwener/opencli/commit/593436e4cb5852f396fbaaa9f87ef1a0b518e76d))
## [1.0.6](https://github.com/jackwener/opencli/compare/v1.0.5...v1.0.6) (2026-03-20)
### Bug Fixes
* use %20 instead of + for spaces in Bilibili WBI signed requests ([#126](https://github.com/jackwener/opencli/issues/126)) ([4cabca1](https://github.com/jackwener/opencli/commit/4cabca12dfa6ca027b938b80ee6b940b5e89ea5c)), closes [#125](https://github.com/jackwener/opencli/issues/125)
+13 -12
View File
@@ -9,12 +9,12 @@
---
## AI Agent 开发者必读:用 Playwright MCP Bridge 探索
## AI Agent 开发者必读:用浏览器探索
> [!CAUTION]
> **你(AI Agent)必须通过 Playwright MCP Bridge 打开浏览器去访问目标网站!**
> **你(AI Agent)必须通过浏览器打开目标网站去探索**
> 不要只靠 `opencli explore` 命令或静态分析来发现 API。
> 你拥有 Playwright MCP 工具,必须主动用它们浏览网页、观察网络请求、模拟用户交互。
> 你拥有浏览器工具,必须主动用它们浏览网页、观察网络请求、模拟用户交互。
### 为什么?
@@ -36,7 +36,7 @@
| ❌ 错误做法 | ✅ 正确做法 |
|------------|------------|
| 只用 `opencli explore` 命令,等结果自动出来 | 用 MCP Bridge 打开浏览器,主动浏览页面 |
| 只用 `opencli explore` 命令,等结果自动出来 | 用浏览器工具打开页面,主动浏览 |
| 直接在代码里 `fetch(url)`,不看浏览器实际请求 | 先在浏览器中确认 API 可用,再写代码 |
| 页面打开后直接抓包,期望所有 API 都出现 | 模拟点击交互(展开评论/切换标签/加载更多) |
| 遇到 HTTP 200 但空数据就放弃 | 检查是否需要 Wbi 签名或 Cookie 鉴权 |
@@ -196,7 +196,7 @@ cat src/clis/<site>/feed.ts # 读最相似的那个
写 TS 适配器之前,先看看你的目标站点有没有**现成的 helper 函数**可以复用:
#### Bilibili (`src/bilibili.ts`)
#### Bilibili (`src/clis/bilibili/utils.ts`)
| 函数 | 用途 | 何时使用 |
|------|------|----------|
@@ -342,10 +342,11 @@ name: search
description: 知乎搜索
args:
keyword:
query:
type: str
required: true
description: Search keyword
positional: true
description: Search query
limit:
type: int
default: 10
@@ -355,7 +356,7 @@ pipeline:
- evaluate: |
(async () => {
const q = encodeURIComponent('${{ args.keyword }}');
const q = encodeURIComponent('${{ args.query }}');
const res = await fetch('/api/v4/search_v3?q=' + q + '&t=general&limit=${{ args.limit }}', {
credentials: 'include'
});
@@ -455,7 +456,7 @@ cli({
name: 'search',
description: 'Search tweets',
strategy: Strategy.HEADER,
args: [{ name: 'keyword', required: true }],
args: [{ name: 'query', required: true, positional: true }],
columns: ['rank', 'author', 'text', 'likes'],
func: async (page, kwargs) => {
await page.goto('https://x.com');
@@ -474,7 +475,7 @@ cli({
'X-Twitter-Auth-Type': 'OAuth2Session',
};
const variables = JSON.stringify({ rawQuery: '${kwargs.keyword}', count: 20 });
const variables = JSON.stringify({ rawQuery: '${kwargs.query}', count: 20 });
const url = '/i/api/graphql/xxx/SearchTimeline?variables=' + encodeURIComponent(variables);
const res = await fetch(url, { headers, credentials: 'include' });
return await res.json();
@@ -631,7 +632,7 @@ git add src/clis/mysite/ && git commit -m "feat(mysite): add hot" && git push
```typescript
import { cli, Strategy } from '../../registry.js';
import type { IPage } from '../../types.js';
import { apiGet } from '../../bilibili.js'; // 复用平台 SDK
import { apiGet } from './utils.js'; // 复用平台 SDK
cli({
site: 'bilibili',
@@ -694,7 +695,7 @@ cli({
| 嵌套字段访问 | `${{ item.node?.title }}` 不工作 | 在 evaluate 中 flatten 数据,不在模板中用 optional chaining |
| 缺少 `strategy: public` | 公开 API 也启动浏览器,7s → 1s | 公开 API 加上 `strategy: public` + `browser: false` |
| evaluate 返回字符串 | map 步骤收到 `""` 而非数组 | pipeline 有 auto-parse,但建议在 evaluate 内 `.map()` 整形 |
| 搜索参数被 URL 编码 | `${{ args.keyword }}` 被浏览器二次编码 | 在 evaluate 内用 `encodeURIComponent()` 手动编码 |
| 搜索参数被 URL 编码 | `${{ args.query }}` 被浏览器二次编码 | 在 evaluate 内用 `encodeURIComponent()` 手动编码 |
| Cookie 过期 | 返回 401 / 空数据 | 在浏览器里重新登录目标站点 |
| Extension tab 残留 | Chrome 多出 `chrome-extension://` tab | 已自动清理;若残留,手动关闭即可 |
| TS evaluate 格式 | `() => {}``result is not a function` | TS 中 `page.evaluate()` 必须用 IIFE`(async () => { ... })()` |
+208
View File
@@ -0,0 +1,208 @@
# Contributing to OpenCLI
Thanks for your interest in contributing to OpenCLI.
## Quick Start
```bash
# 1. Fork & clone
git clone git@github.com:<your-username>/opencli.git
cd opencli
# 2. Install dependencies
npm install
# 3. Build
npm run build
# 4. Run a few checks
npx tsc --noEmit
npm test
npm run test:adapter
# 5. Link globally (optional, for testing `opencli` command)
npm link
```
## Adding a New Site Adapter
This is the most common type of contribution. Start with YAML when possible, and use TypeScript only when you need browser-side logic or multi-step flows.
### YAML Adapter (Recommended for data-fetching commands)
Create a file like `src/clis/<site>/<command>.yaml`:
```yaml
site: mysite
name: trending
description: Trending posts on MySite
domain: www.mysite.com
strategy: public # public | cookie | header
browser: false # true if browser session is needed
args:
query:
positional: true
type: str
required: true
description: Search keyword
limit:
type: int
default: 20
description: Number of items
pipeline:
- fetch:
url: https://api.mysite.com/trending
- map:
rank: ${{ index + 1 }}
title: ${{ item.title }}
score: ${{ item.score }}
url: ${{ item.url }}
- limit: ${{ args.limit }}
columns: [rank, title, score, url]
```
See [`hackernews/top.yaml`](src/clis/hackernews/top.yaml) for a real example.
### TypeScript Adapter (For complex browser interactions)
Create a file like `src/clis/<site>/<command>.ts`:
```typescript
import { cli, Strategy } from '../../registry.js';
cli({
site: 'mysite',
name: 'search',
description: 'Search MySite',
domain: 'www.mysite.com',
strategy: Strategy.COOKIE,
args: [
{ name: 'query', positional: true, required: true, help: 'Search query' },
{ name: 'limit', type: 'int', default: 10, help: 'Max results' },
],
columns: ['title', 'url', 'date'],
func: async (page, kwargs) => {
const { query, limit = 10 } = kwargs;
await page.goto('https://www.mysite.com');
const data = await page.evaluate(`
(async () => {
const res = await fetch('/api/search?q=${encodeURIComponent(query)}', {
credentials: 'include'
});
return (await res.json()).results;
})()
`);
return data.slice(0, Number(limit)).map((item: any) => ({
title: item.title,
url: item.url,
date: item.created_at,
}));
},
});
```
Use `opencli explore <url>` to discover APIs and see [CLI-EXPLORER.md](./CLI-EXPLORER.md) if you need the full adapter workflow.
### Validate Your Adapter
```bash
# Validate YAML syntax and schema
opencli validate
# Test your command
opencli <site> <command> --limit 3 -f json
# Verbose mode for debugging
opencli <site> <command> -v
```
## Arg Design Convention
Use **positional** for the primary, required argument of a command (the "what" — query, symbol, id, url, username). Use **named options** (`--flag`) for secondary/optional configuration (limit, format, sort, page, filters, language, date).
**Rule of thumb**: Think about how the user will type the command. `opencli xueqiu stock SH600519` is more natural than `opencli xueqiu stock --symbol SH600519`.
| Arg type | Positional? | Examples |
|----------|-------------|----------|
| Main target (query, symbol, id, url, username) | ✅ `positional: true` | `search '茅台'`, `stock SH600519`, `download BV1xxx` |
| Configuration (limit, format, sort, page, type, filters) | ❌ Named `--flag` | `--limit 10`, `--format json`, `--sort hot`, `--location seattle` |
Do **not** convert an argument to positional just because it appears first in the file. If the argument is optional, acts like a filter, or selects a mode/configuration, it should usually stay a named option.
YAML example:
```yaml
args:
query:
positional: true # ← primary arg, user types it directly
type: str
required: true
limit:
type: int # ← config arg, user types --limit 10
default: 20
```
TS example:
```typescript
args: [
{ name: 'query', positional: true, required: true, help: 'Search query' },
{ name: 'limit', type: 'int', default: 10, help: 'Max results' },
]
```
## Testing
See [TESTING.md](./TESTING.md) for the full guide and exact test locations.
```bash
npm test # Core unit tests (non-adapter)
npm run test:adapter # Focused adapter tests: zhihu/twitter/reddit/bilibili
npx vitest run tests/e2e/ # E2E tests
npx vitest run # All tests
```
## Code Style
- **TypeScript strict mode** — avoid `any` where possible.
- **ES Modules** — use `.js` extensions in imports (TypeScript output).
- **Naming**: `kebab-case` for files, `camelCase` for variables/functions, `PascalCase` for types/classes.
- **No default exports** — use named exports.
## Commit Convention
We use [Conventional Commits](https://www.conventionalcommits.org/):
```
feat(twitter): add thread command
fix(browser): handle CDP timeout gracefully
docs: update CONTRIBUTING.md
test(reddit): add e2e test for save command
chore: bump vitest to v4
```
Common scopes: site name (`twitter`, `reddit`) or module name (`browser`, `pipeline`, `engine`).
## Submitting a Pull Request
1. Create a feature branch: `git checkout -b feat/mysite-trending`
2. Make your changes and add tests when relevant
3. Run the checks that apply:
```bash
npx tsc --noEmit # Type check
npm test # Core unit tests
npm run test:adapter # Focused adapter tests (if you touched adapter logic)
opencli validate # YAML validation (if applicable)
```
4. Commit using conventional commit format
5. Push and open a PR
## License
By contributing, you agree that your contributions will be licensed under the [Apache-2.0 License](./LICENSE).
+184 -22
View File
@@ -1,28 +1,190 @@
BSD 3-Clause License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2025, jackwener
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Definitions.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to the Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by the Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding any notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2025 jackwener
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+57
View File
@@ -0,0 +1,57 @@
# Privacy Policy — OpenCLI Browser Extension
**Last updated**: 2026-03-25
## What the extension does
The OpenCLI Browser Extension is a bridge between the [OpenCLI](https://github.com/jackwener/opencli) command-line tool and your Chrome browser. It receives commands from a **locally running daemon** process via WebSocket (`localhost` only) and executes them in **isolated Chrome windows** that are separate from your normal browsing session.
## Data collection
The extension does **NOT** collect, store, transmit, or sell any personal data. Specifically:
- **No analytics or telemetry** — no data is sent to any remote server.
- **No user tracking** — no cookies, identifiers, or fingerprints are created.
- **No external network requests** — all communication is strictly `localhost` (WebSocket to `ws://localhost:19825`).
## Permissions explained
| Permission | Why it's needed |
|------------|----------------|
| `debugger` | Required to use Chrome DevTools Protocol (CDP) for browser automation — executing JavaScript, capturing page content, and taking screenshots in isolated windows. |
| `tabs` | Required to create and manage isolated automation windows and tabs, separate from the user's browsing session. |
| `cookies` | Required to read site-specific cookies (scoped by domain) so CLI commands can authenticate with websites the user is already logged into. Cookies are **never written, modified, or transmitted externally**. |
| `activeTab` | Required to identify the currently active tab for context-aware commands. |
| `alarms` | Required to maintain the WebSocket connection to the local daemon via periodic keepalive checks. |
## Data flow
```
User's terminal (opencli CLI)
↓ (spawns)
Local daemon process (localhost:19825)
↓ (WebSocket, localhost only)
Chrome Extension (this extension)
↓ (Chrome APIs)
Isolated Chrome automation window
```
All data stays on the user's machine. No data leaves `localhost`.
## Cookie access
The extension reads cookies **only** when explicitly requested by a CLI command, and **only** for the specific domain the command targets. It cannot and does not dump all cookies. Cookie data is returned to the local daemon process and is never sent to any external server.
## Third-party services
This extension does not integrate with, send data to, or receive data from any third-party service.
## Open source
This extension is fully open source. You can audit the complete source code at:
https://github.com/jackwener/opencli/tree/main/extension
## Contact
For privacy questions or concerns, please open an issue at:
https://github.com/jackwener/opencli/issues
+270 -79
View File
@@ -1,82 +1,100 @@
# OpenCLI
> **Make any website your CLI.**
> Zero risk · Reuse Chrome login · AI-powered discovery
[中文文档](./README.zh-CN.md)
> **Make any website, Electron App, or Local Tool your CLI.**
> Zero risk · Reuse Chrome login · AI-powered discovery · Universal CLI Hub
[![中文文档](https://img.shields.io/badge/docs-%E4%B8%AD%E6%96%87-0F766E?style=flat-square)](./README.zh-CN.md)
[![npm](https://img.shields.io/npm/v/@jackwener/opencli?style=flat-square)](https://www.npmjs.com/package/@jackwener/opencli)
[![Node.js Version](https://img.shields.io/node/v/@jackwener/opencli?style=flat-square)](https://nodejs.org)
[![License](https://img.shields.io/npm/l/@jackwener/opencli?style=flat-square)](./LICENSE)
A CLI tool that turns **any website** into a command-line interface. **57 commands** across **17 sites**bilibili, zhihu, xiaohongshu, twitter, reddit, xueqiu, github, v2ex, hackernews, bbc, weibo, boss, yahoo-finance, reuters, smzdm, ctrip, youtube — powered by browser session reuse and AI-native discovery.
A CLI tool that turns **any website**, **Electron app**, or **local CLI tool** into a command-line interfaceBilibili, Zhihu, 小红书, Twitter/X, Reddit, YouTube, Antigravity, `gh`, `docker`, and [many more](#built-in-commands) — powered by browser session reuse and AI-native discovery.
---
**Built for AI Agents**: Simply configure an instruction in your global `AGENT.md` or `.cursorrules` guiding the AI to execute `opencli list` via Bash to discover available tools. Register your favorite local CLIs (`opencli register mycli`), and the AI will automatically learn how to invoke all your tools perfectly!
## Table of Contents
- [Highlights](#highlights)
- [Prerequisites](#prerequisites)
- [Quick Start](#quick-start)
- [Built-in Commands](#built-in-commands)
- [Output Formats](#output-formats)
- [For AI Agents (Developer Guide)](#for-ai-agents-developer-guide)
- [Troubleshooting](#troubleshooting)
- [Releasing New Versions](#releasing-new-versions)
- [License](#license)
**CLI All Electron Apps! The Most Powerful Update Has Arrived!**
Turn ANY Electron application into a CLI tool! Recombine, script, and extend applications like Antigravity Ultra seamlessly. Now AI can control itself natively. Unlimited possibilities await!
---
## Highlights
- **CLI All Electron** — CLI-ify apps like Antigravity Ultra! Now AI can control itself natively using cc/openclaw!
- **Account-safe** — Reuses Chrome's logged-in state; your credentials never leave the browser.
- **AI Agent ready** — `explore` discovers APIs, `synthesize` generates adapters, `cascade` finds auth strategies.
- **External CLI Hub** — Discover, auto-install, and passthrough commands to any external CLI (gh, obsidian, docker, etc). Zero setup.
- **Self-healing setup** — `opencli doctor` diagnoses and auto-starts the daemon, extension, and live browser connectivity.
- **Dynamic Loader** — Simply drop `.ts` or `.yaml` adapters into the `clis/` folder for auto-registration.
- **Dual-Engine Architecture** — Supports both YAML declarative data pipelines and robust browser runtime typescript injections.
- **Dual-Engine Architecture** — Supports both YAML declarative data pipelines and robust browser runtime TypeScript injections.
## Why opencli?
There are many great browser automation tools. Here's when opencli is the right choice:
| Your need | Best tool | Why |
|-----------|-----------|-----|
| Scheduled data extraction from specific sites | **opencli** | Pre-built adapters, deterministic JSON, zero LLM cost |
| AI agent needs reliable site operations | **opencli** | Hundreds of commands, structured output, fast deterministic response |
| Explore an unknown website ad-hoc | Browser-Use, Stagehand | LLM-driven general browsing for one-off tasks |
| Large-scale web crawling | Crawl4AI, Scrapy | Purpose-built for throughput and scale |
| Control desktop Electron apps from terminal | **opencli** | CDP + AppleScript — the only CLI tool that does this |
**What makes opencli different:**
- **Zero LLM cost** — No tokens consumed at runtime. Run 10,000 times and pay nothing.
- **Deterministic** — Same command, same output schema, every time. Pipeable, scriptable, CI-friendly.
- **Broad coverage** — 50+ sites across global and Chinese platforms (Bilibili, Zhihu, Xiaohongshu, Reddit, HackerNews, and more), plus desktop Electron apps via CDP.
> For a detailed comparison with Browser-Use, Crawl4AI, Firecrawl, and others, see the [Comparison Guide](./docs/comparison.md).
## Prerequisites
- **Node.js**: >= 18.0.0
- **Node.js**: >= 20.0.0 (or **Bun** >= 1.0 — see [Runtime Support](#runtime-support) below)
- **Chrome** running **and logged into the target site** (e.g. bilibili.com, zhihu.com, xiaohongshu.com).
> **⚠️ Important**: Browser commands reuse your Chrome login session. You must be logged into the target website in Chrome before running commands. If you get empty data or errors, check your login status first.
OpenCLI connects to your browser through the Playwright MCP Bridge extension.
### Runtime Support
### Playwright MCP Bridge Extension Setup
1. Install **[Playwright MCP Bridge](https://chromewebstore.google.com/detail/playwright-mcp-bridge/mmlmfjhmonkocbjadbfplnigmagldckm)** extension in Chrome.
2. Obtain your token by clicking the extension icon in the browser toolbar or from the extension settings page.
**You must configure this token in BOTH your MCP configuration AND system environment variables.**
First, add it to your MCP client config (e.g. Claude/Cursor):
```json
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--extension"],
"env": {
"PLAYWRIGHT_MCP_EXTENSION_TOKEN": "<your-token-here>"
}
}
}
}
```
And, so that `opencli` commands can use it directly in the terminal, export it in your shell environment (e.g. `~/.zshrc`):
OpenCLI works with both **Node.js** (≥ 20) and **Bun** (≥ 1.0). All commands and adapters are runtime-agnostic.
```bash
export PLAYWRIGHT_MCP_EXTENSION_TOKEN="<your-token-here>"
# Development with Bun (faster startup)
npm run dev:bun
# Run the built CLI with Bun
npm run start:bun
# Run unit tests under Bun
npm run test:bun
# Run E2E tests with Bun as the runtime
OPENCLI_TEST_RUNTIME=bun npm run test:e2e
```
After configuring, run `opencli doctor` to verify your token is correctly set up across all locations:
Use `opencli doctor` to check your current runtime — it displays the active engine (e.g. `node v22.13.0` or `bun 1.1.42`).
```bash
opencli doctor
```
OpenCLI connects to your browser through a lightweight **Browser Bridge** Chrome Extension + micro-daemon (zero config, auto-start).
### Browser Bridge Extension Setup
You can install the extension via either method:
**Method 1: Download Pre-built Release (Recommended)**
1. Go to the GitHub [Releases page](https://github.com/jackwener/opencli/releases) and download the latest `opencli-extension.zip`.
2. Unzip the file and open `chrome://extensions`, enable **Developer mode** (top-right toggle).
3. Click **Load unpacked** and select the unzipped folder.
**Method 2: Load Source (For Developers)**
1. Open `chrome://extensions` and enable **Developer mode**.
2. Click **Load unpacked** and select the `extension/` directory from this repository.
That's it! The daemon auto-starts when you run any browser command. No tokens, no manual configuration.
> **Tip**: Use `opencli doctor` for ongoing diagnosis:
> ```bash
> opencli doctor # Check extension + daemon connectivity
> ```
## Quick Start
@@ -116,25 +134,171 @@ npm install -g @jackwener/opencli@latest
## Built-in Commands
Run `opencli list` for the live registry.
| Site | Commands | Mode |
|------|----------|------|
| **bilibili** | `hot` `search` `me` `favorite` ... (11 commands) | 🔐 Browser |
| **zhihu** | `hot` `search` `question` | 🔐 Browser |
| **xiaohongshu** | `search` `notifications` `feed` `me` `user` | 🔐 Browser |
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 Browser |
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `following` `followers` `notifications` `post` `reply` `delete` `like` | 🔐 Browser |
| **reddit** | `hot` `frontpage` `search` `subreddit` | 🔐 Browser |
| **weibo** | `hot` | 🔐 Browser |
| **boss** | `search` | 🔐 Browser |
| **youtube** | `search` | 🔐 Browser |
| **yahoo-finance** | `quote` | 🔐 Browser |
| **reuters** | `search` | 🔐 Browser |
| **smzdm** | `search` | 🔐 Browser |
| **ctrip** | `search` | 🔐 Browser |
| **github** | `search` | 🌐 Public |
| **v2ex** | `hot` `latest` `topic` | 🌐 Public |
| **hackernews** | `top` | 🌐 Public |
| **bbc** | `news` | 🌐 Public |
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` `block` `unblock` `hide-reply` | Browser |
| **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | Browser |
| **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | Desktop |
| **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | Browser |
| **codex** | `status` `send` `read` `new` `dump` `extract-diff` `model` `ask` `screenshot` `history` `export` | Desktop |
| **chatwise** | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` | Desktop |
| **doubao** | `status` `new` `send` `read` `ask` | Browser |
| **doubao-app** | `status` `new` `send` `read` `ask` `screenshot` `dump` | Desktop |
| **notion** | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` | Desktop |
| **discord-app** | `status` `send` `read` `channels` `servers` `search` `members` | Desktop |
| **v2ex** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | Public / Browser |
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` `earnings-date` `fund-holdings` `fund-snapshot` | Browser |
| **antigravity** | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` | Desktop |
| **chatgpt** | `status` `new` `send` `read` `ask` `model` | Desktop |
| **xiaohongshu** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | Browser |
| **apple-podcasts** | `search` `episodes` `top` | Public |
| **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | Public |
| **zhihu** | `hot` `search` `question` `download` | Browser |
| **weixin** | `download` | Browser |
| **youtube** | `search` `video` `transcript` | Browser |
| **boss** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | Browser |
| **coupang** | `search` `add-to-cart` | Browser |
| **bbc** | `news` | Public |
| **bloomberg** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | Public / Browser |
| **ctrip** | `search` | Browser |
| **devto** | `top` `tag` `user` | Public |
| **dictionary** | `search` `synonyms` `examples` | Public |
| **arxiv** | `search` `paper` | Public |
| **paperreview** | `submit` `review` `feedback` | Public |
| **wikipedia** | `search` `summary` `random` `trending` | Public |
| **hackernews** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | Public |
| **jd** | `item` | Browser |
| **linkedin** | `search` `timeline` | Browser |
| **reuters** | `search` | Browser |
| **smzdm** | `search` | Browser |
| **web** | `read` | Browser |
| **weibo** | `hot` `search` | Browser |
| **yahoo-finance** | `quote` | Browser |
| **sinafinance** | `news` | 🌐 Public |
| **barchart** | `quote` `options` `greeks` `flow` | Browser |
| **chaoxing** | `assignments` `exams` | Browser |
| **grok** | `ask` | Browser |
| **hf** | `top` | Public |
| **jike** | `feed` `search` `create` `like` `comment` `repost` `notifications` `post` `topic` `user` | Browser |
| **jimeng** | `generate` `history` | Browser |
| **yollomi** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | Browser |
| **linux-do** | `feed` `categories` `tags` `search` `topic` `user-topics` `user-posts` | Browser |
| **stackoverflow** | `hot` `search` `bounties` `unanswered` | Public |
| **steam** | `top-sellers` | Public |
| **weread** | `shelf` `search` `book` `highlights` `notes` `notebooks` `ranking` | Browser |
| **douban** | `search` `top250` `subject` `photos` `download` `marks` `reviews` `movie-hot` `book-hot` | Browser |
| **facebook** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | Browser |
| **google** | `news` `search` `suggest` `trends` | Public |
| **36kr** | `news` `hot` `search` `article` | Public / Browser |
| **imdb** | `search` `title` `top` `trending` `person` `reviews` | Public |
| **producthunt** | `posts` `today` `hot` `browse` | Public / Browser |
| **instagram** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | Browser |
| **lobsters** | `hot` `newest` `active` `tag` | Public |
| **medium** | `feed` `search` `user` | Browser |
| **sinablog** | `hot` `search` `article` `user` | Browser |
| **substack** | `feed` `search` `publication` | Browser |
| **pixiv** | `ranking` `search` `user` `illusts` `detail` `download` | Browser |
| **tiktok** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | Browser |
### External CLI Hub
OpenCLI acts as a universal hub for your existing command-line tools. It provides unified discovery, automatic installation, and pure passthrough execution.
| External CLI | Description | Commands Example |
|--------------|-------------|------------------|
| **gh** | GitHub CLI | `opencli gh pr list --limit 5` |
| **obsidian** | Obsidian vault management | `opencli obsidian search query="AI"` |
| **docker** | Docker command-line interface | `opencli docker ps` |
| **readwise** | Readwise & Reader CLI | `opencli readwise login` |
| **gws** | Google Workspace CLI — Docs, Sheets, Drive, Gmail, Calendar | `opencli gws docs list` |
**Zero Configuration**: OpenCLI purely passes your inputs to the underlying binary via standard I/O streams. The external CLI works exactly as it naturally would, maintaining its standard output formats.
**Auto-Installation**: If you run `opencli gh ...` and `gh` is not installed on your system, OpenCLI will automatically try to install it using your system's package manager (e.g., `brew install gh`) before seamlessly re-running the command.
**Register Your Own**:
Add any local CLI to your OpenCLI registry so AI agents can automatically discover it via the `opencli list` command.
```bash
opencli register mycli
```
### Desktop App Adapters
Each desktop adapter has its own detailed documentation with commands reference, setup guide, and examples:
If you want to add support for a new Electron desktop app, start with [docs/guide/electron-app-cli.md](./docs/guide/electron-app-cli.md) and the deeper [Electron guide](./docs/advanced/electron.md).
| App | Description | Doc |
|-----|-------------|-----|
| **Cursor** | Control Cursor IDE — Composer, chat, code extraction | [Doc](./docs/adapters/desktop/cursor.md) |
| **Codex** | Drive OpenAI Codex CLI agent headlessly | [Doc](./docs/adapters/desktop/codex.md) |
| **Antigravity** | Control Antigravity Ultra from terminal | [Doc](./docs/adapters/desktop/antigravity.md) |
| **ChatGPT** | Automate ChatGPT macOS desktop app | [Doc](./docs/adapters/desktop/chatgpt.md) |
| **ChatWise** | Multi-LLM client (GPT-4, Claude, Gemini) | [Doc](./docs/adapters/desktop/chatwise.md) |
| **Notion** | Search, read, write Notion pages | [Doc](./docs/adapters/desktop/notion.md) |
| **Discord** | Discord Desktop — messages, channels, servers | [Doc](./docs/adapters/desktop/discord.md) |
| **Doubao** | Control Doubao AI desktop app via CDP | [Doc](./docs/adapters/desktop/doubao-app.md) |
## Download Support
OpenCLI supports downloading images, videos, and articles from supported platforms.
### Supported Platforms
| Platform | Content Types | Notes |
|----------|---------------|-------|
| **xiaohongshu** | Images, Videos | Downloads all media from a note |
| **bilibili** | Videos | Requires `yt-dlp` installed |
| **twitter** | Images, Videos | Downloads from user media tab or single tweet |
| **douban** | Images | Downloads poster / still image lists from movie subjects |
| **pixiv** | Images | Downloads original-quality illustrations, supports multi-page works |
| **zhihu** | Articles (Markdown) | Exports articles with optional image download |
| **weixin** | Articles (Markdown) | Exports WeChat Official Account articles |
### Prerequisites
For video downloads from streaming platforms, you need to install `yt-dlp`:
```bash
# Install yt-dlp
pip install yt-dlp
# or
brew install yt-dlp
```
### Usage Examples
```bash
# Download images/videos from Xiaohongshu note
opencli xiaohongshu download abc123 --output ./xhs
# Download Bilibili video (requires yt-dlp)
opencli bilibili download BV1xxx --output ./bilibili
opencli bilibili download BV1xxx --quality 1080p # Specify quality
# Download Twitter media from user
opencli twitter download elonmusk --limit 20 --output ./twitter
# Download single tweet media
opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
# Download Douban posters / stills
opencli douban download 30382501 --output ./douban
# Export Zhihu article to Markdown
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
# Export with local images
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --download-images
# Export WeChat article to Markdown
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --output ./weixin
```
## Output Formats
@@ -151,6 +315,28 @@ opencli bilibili hot -f csv # CSV
opencli bilibili hot -v # Verbose: show pipeline debug steps
```
## Plugins
Extend OpenCLI with community-contributed adapters. Plugins use the same YAML/TS format as built-in commands and are automatically discovered at startup.
```bash
opencli plugin install github:user/opencli-plugin-my-tool # Install
opencli plugin list # List installed
opencli plugin update my-tool # Update to latest
opencli plugin update --all # Update all installed plugins
opencli plugin uninstall my-tool # Remove
```
`opencli plugin list` also shows the tracked short commit hash when a plugin version is recorded in `~/.opencli/plugins.lock.json`.
| Plugin | Type | Description |
|--------|------|-------------|
| [opencli-plugin-github-trending](https://github.com/ByteYue/opencli-plugin-github-trending) | YAML | GitHub Trending repositories |
| [opencli-plugin-hot-digest](https://github.com/ByteYue/opencli-plugin-hot-digest) | TS | Multi-platform trending aggregator |
| [opencli-plugin-juejin](https://github.com/Astro-Han/opencli-plugin-juejin) | YAML | 稀土掘金 (Juejin) hot articles |
See [Plugins Guide](./docs/guide/plugins.md) for creating your own plugin.
## For AI Agents (Developer Guide)
If you are an AI assistant tasked with creating a new command adapter for `opencli`, please follow the AI Agent workflow below:
@@ -175,26 +361,31 @@ opencli cascade https://api.example.com/data
Explore outputs to `.opencli/explore/<site>/` (manifest.json, endpoints.json, capabilities.json, auth.json).
## Testing
See **[TESTING.md](./TESTING.md)** for how to run and write tests.
## Troubleshooting
- **"Failed to connect to Playwright MCP Bridge"**
- Ensure the Playwright MCP extension is installed and **enabled** in your running Chrome.
- Restart the Chrome browser if you just installed the extension.
- **"Extension not connected"**
- Ensure the opencli Browser Bridge extension is installed and **enabled** in `chrome://extensions`.
- **"attach failed: Cannot access a chrome-extension:// URL"**
- Another Chrome extension (e.g. youmind, New Tab Override, or AI assistant extensions) may be interfering. Try **disabling other extensions** temporarily, then retry.
- **Empty data returns or 'Unauthorized' error**
- Your login session in Chrome might have expired. Open a normal Chrome tab, navigate to the target site, and log in or refresh the page to prove you are human.
- Your login session in Chrome might have expired. Open a normal Chrome tab, navigate to the target site, and log in or refresh the page.
- **Node API errors**
- Make sure you are using Node.js >= 18. Some dependencies require modern Node APIs.
- Make sure you are using Node.js >= 20. Some dependencies require modern Node APIs.
- **Daemon issues**
- Check daemon status: `curl localhost:19825/status`
- View extension logs: `curl localhost:19825/logs`
## Releasing New Versions
```bash
npm version patch # 0.1.0 → 0.1.1
npm version minor # 0.1.0 → 0.2.0
git push --follow-tags
```
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=jackwener/opencli&type=Date)](https://star-history.com/#jackwener/opencli&Date)
The CI will automatically build, create a GitHub release, and publish to npm.
## License
[BSD-3-Clause](./LICENSE)
[Apache-2.0](./LICENSE)
+246 -79
View File
@@ -1,82 +1,82 @@
# OpenCLI
> **把任何网站变成你的命令行工具。**
> 零风控 · 复用 Chrome 登录 · AI 自动发现接口
[English](./README.md)
> **把任何网站、本地工具、Electron 应用变成能够让 AI 调用的命令行**
> 零风控 · 复用 Chrome 登录 · AI 自动发现接口 · 全能 CLI 枢纽
[![English](https://img.shields.io/badge/docs-English-1D4ED8?style=flat-square)](./README.md)
[![npm](https://img.shields.io/npm/v/@jackwener/opencli?style=flat-square)](https://www.npmjs.com/package/@jackwener/opencli)
[![Node.js Version](https://img.shields.io/node/v/@jackwener/opencli?style=flat-square)](https://nodejs.org)
[![License](https://img.shields.io/npm/l/@jackwener/opencli?style=flat-square)](./LICENSE)
OpenCLI 将任何网站变成命令行工具。**57 个命令**覆盖 **17 个站点** — B站、知乎、小红书、Twitter、Reddit、雪球、GitHub、V2EX、Hacker News、BBC、微博、BOSS直聘、Yahoo Finance、路透社、什么值得买、携程、YouTube — 复用浏览器登录态,AI 驱动探索。
OpenCLI 将任何网站、本地 CLI 或 Electron 应用(如 Antigravity变成命令行工具 — B站、知乎、小红书、Twitter/X、Reddit、YouTube,以及 `gh``docker` 等[多种站点与工具](#内置命令) — 复用浏览器登录态,AI 驱动探索。
---
**专为 AI Agent 打造**:只需在全局 `.cursorrules``AGENT.md` 中配置简单指令,引导 AI 通过 Bash 执行 `opencli list` 来检索可用的 CLI 工具及其用法。随后,将你常用的 CLI 列表整合注册进去(`opencli register mycli`),AI 便能瞬间学会自动调用相应的本地工具!
## 目录
- [亮点](#亮点)
- [前置要求](#前置要求)
- [快速开始](#快速开始)
- [内置命令](#内置命令)
- [输出格式](#输出格式)
- [致 AI Agent(开发者指南)](#致-ai-agent开发者指南)
- [常见问题排查](#常见问题排查)
- [版本发布](#版本发布)
- [License](#license)
**opencli 支持 CLI 化所有 electron 应用!最强大更新来袭!**
CLI all electron!现在支持把所有 electron 应用 CLI 化,从而组合出各种神奇的能力。
如果你在使用诸如 Antigravity Ultra 等工具时觉得不够灵活或难以扩展,现在通过 OpenCLI 把他 CLI 化,轻松打破界限。
现在,**AI 可以自己控制自己**!结合 cc/openclaw 就可以远程控制任何 electron 应用!无限玩法!!
---
## 亮点
- **57 个命令,17 个站点** — B站、知乎、小红书、Twitter、Reddit、雪球(xueqiu)、GitHub、V2EX、Hacker News、BBC、微博、BOSS直聘、Yahoo Finance、路透社、什么值得买、携程、YouTube
- **CLI All Electron** — 支持把所有 electron 应用(如 Antigravity UltraCLI 化,让 AI 控制自己!
- **多站点覆盖** — 覆盖 B站、知乎、小红书、Twitter、Reddit,以及多种桌面应用
- **零风控** — 复用 Chrome 登录态,无需存储任何凭证
- **外部 CLI 枢纽** — 统一发现、自动安装、透传执行 `gh``docker` 等本地 CLI
- **自修复配置** — `opencli doctor` 自动启动 daemon,诊断扩展和浏览器连接状态
- **AI 原生** — `explore` 自动发现 API`synthesize` 生成适配器,`cascade` 探测认证策略
- **动态加载引擎** — 声明式的 `.yaml` 或者底层定制的 `.ts` 适配器,放入 `clis/` 文件夹即可自动注册生效
## 为什么选 opencli
浏览器自动化工具很多,opencli 适合什么场景?
| 你的需求 | 最佳工具 | 原因 |
|----------|----------|------|
| 定时从特定站点提取结构化数据 | **opencli** | 预定义适配器,确定性 JSON 输出,零 LLM 成本 |
| AI Agent 需要可靠的站点操作 | **opencli** | 数百条命令,结构化输出,快速确定性响应 |
| 临时探索未知网站 | Browser-Use、Stagehand | LLM 驱动的通用浏览,适合一次性任务 |
| 大规模网页爬取 | Crawl4AI、Scrapy | 专为吞吐量和规模设计 |
| 从终端控制桌面 Electron 应用 | **opencli** | CDP + AppleScript,目前唯一能做到这一点的 CLI 工具 |
**opencli 的核心差异:**
- **零 LLM 成本** — 运行时不消耗任何 token,跑一万次不花一分钱
- **确定性** — 同一命令永远返回同一结构,可管道化、可脚本化、CI 友好
- **覆盖广泛** — 50+ 站点,横跨全球与中国平台(B站、知乎、小红书、Reddit、HackerNews 等),并支持通过 CDP 控制桌面 Electron 应用
> 与 Browser-Use、Crawl4AI、Firecrawl 等工具的详细对比,请查看 [Comparison Guide](./docs/comparison.md)。
## 前置要求
- **Node.js**: >= 18.0.0
- **Node.js**: >= 20.0.0
- **Chrome** 浏览器正在运行,且**已登录目标网站**(如 bilibili.com、zhihu.com、xiaohongshu.com
> **⚠️ 重要**:大多数命令复用你的 Chrome 登录状态。运行命令前,你必须已在 Chrome 中打开目标网站并完成登录。如果获取到空数据或报错,请先检查你的浏览器登录状态。
OpenCLI 通过 Playwright MCP Bridge 扩展与你的浏览器通信
OpenCLI 通过轻量化的 **Browser Bridge** Chrome 扩展 + 微型 daemon 与浏览器通信(零配置,自动启动)
### Playwright MCP Bridge 扩展配置
### Browser Bridge 扩展配置
1. 安装 **[Playwright MCP Bridge](https://chromewebstore.google.com/detail/playwright-mcp-bridge/mmlmfjhmonkocbjadbfplnigmagldckm)** 扩展
2. 在浏览器插件栏点击该插件,或者在插件设置页获取你的 Extension Token。
你可以选择以下任一方式安装扩展
**你必须将这个 Token 同时配置到你的 MCP 配置文件 AND 环境变量中。**
**方式一:下载构建好的安装包(推荐)**
1. 到 GitHub [Releases 页面](https://github.com/jackwener/opencli/releases) 下载最新的 `opencli-extension.zip`
2. 解压后打开 Chrome 的 `chrome://extensions`,启用右上角的 **开发者模式**
3. 点击 **加载已解压的扩展程序**,选择解压后的文件夹。
首先,配置你的 MCP 客户端(如 Claude/Cursor 等):
**方式二:加载源码(针对开发者)**
1. 同样在 `chrome://extensions` 开启 **开发者模式**
2. 点击 **加载已解压的扩展程序**,选择本仓库代码树中的 `extension/` 文件夹。
```json
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--extension"],
"env": {
"PLAYWRIGHT_MCP_EXTENSION_TOKEN": "<你的-token>"
}
}
}
}
```
完成!运行任何 opencli 浏览器命令时,后台微型 daemon 会自动启动与浏览器通信。无需配 API Token,零代码配置。
并且,为了让 `opencli` 命令行也能直接使用它,你必须在你的终端系统环境变量中导出它(建议写进 `~/.zshrc``~/.bashrc`
```bash
export PLAYWRIGHT_MCP_EXTENSION_TOKEN="<你的-token>"
```
配置完成后,运行 `opencli doctor` 检测你的 Token 是否在所有位置都正确配置:
```bash
opencli doctor
```
> **Tip**:后续诊断用 `opencli doctor`
> ```bash
> opencli doctor # 检查扩展和 daemon 连通性
> ```
## 快速开始
@@ -116,25 +116,169 @@ npm install -g @jackwener/opencli@latest
## 内置命令
运行 `opencli list` 查看完整注册表。
| 站点 | 命令 | 模式 |
|------|------|------|
| **bilibili** | `hot` `search` `me` `favorite` ...(共11个) | 🔐 浏览器 |
| **zhihu** | `hot` `search` `question` | 🔐 浏览器 |
| **xiaohongshu** | `search` `notifications` `feed` `me` `user` | 🔐 浏览器 |
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` | 🔐 浏览器 |
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `following` `followers` `notifications` `post` `reply` `delete` `like` | 🔐 浏览器 |
| **reddit** | `hot` `frontpage` `search` `subreddit` | 🔐 浏览器 |
| **weibo** | `hot` | 🔐 浏览器 |
| **boss** | `search` | 🔐 浏览器 |
| **youtube** | `search` | 🔐 浏览器 |
| **yahoo-finance** | `quote` | 🔐 浏览器 |
| **reuters** | `search` | 🔐 浏览器 |
| **smzdm** | `search` | 🔐 浏览器 |
| **ctrip** | `search` | 🔐 浏览器 |
| **github** | `search` | 🌐 公共 API |
| **v2ex** | `hot` `latest` `topic` | 🌐 公共 API |
| **hackernews** | `top` | 🌐 公共 API |
| **bbc** | `news` | 🌐 公共 API |
| **twitter** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` `block` `unblock` `hide-reply` | 浏览器 |
| **reddit** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 浏览器 |
| **cursor** | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` | 桌面端 |
| **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 浏览器 |
| **codex** | `status` `send` `read` `new` `dump` `extract-diff` `model` `ask` `screenshot` `history` `export` | 桌面端 |
| **chatwise** | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` | 桌面端 |
| **doubao** | `status` `new` `send` `read` `ask` | 浏览器 |
| **doubao-app** | `status` `new` `send` `read` `ask` `screenshot` `dump` | 桌面端 |
| **notion** | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` | 桌面端 |
| **discord-app** | `status` `send` `read` `channels` `servers` `search` `members` | 桌面端 |
| **v2ex** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | 公开 / 浏览器 |
| **xueqiu** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` `earnings-date` `fund-holdings` `fund-snapshot` | 浏览器 |
| **antigravity** | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` | 桌面端 |
| **chatgpt** | `status` `new` `send` `read` `ask` `model` | 桌面端 |
| **xiaohongshu** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | 浏览器 |
| **apple-podcasts** | `search` `episodes` `top` | 公开 |
| **xiaoyuzhou** | `podcast` `podcast-episodes` `episode` | 公开 |
| **zhihu** | `hot` `search` `question` `download` | 浏览器 |
| **weixin** | `download` | 浏览器 |
| **youtube** | `search` `video` `transcript` | 浏览器 |
| **boss** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | 浏览器 |
| **coupang** | `search` `add-to-cart` | 浏览器 |
| **bbc** | `news` | 公共 API |
| **bloomberg** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | 公共 API / 浏览器 |
| **ctrip** | `search` | 浏览器 |
| **devto** | `top` `tag` `user` | 公开 |
| **dictionary** | `search` `synonyms` `examples` | 公开 |
| **arxiv** | `search` `paper` | 公开 |
| **paperreview** | `submit` `review` `feedback` | 公开 |
| **wikipedia** | `search` `summary` `random` `trending` | 公开 |
| **hackernews** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 公共 API |
| **jd** | `item` | 浏览器 |
| **linkedin** | `search` `timeline` | 浏览器 |
| **reuters** | `search` | 浏览器 |
| **smzdm** | `search` | 浏览器 |
| **web** | `read` | 浏览器 |
| **weibo** | `hot` `search` | 浏览器 |
| **yahoo-finance** | `quote` | 浏览器 |
| **sinafinance** | `news` | 🌐 公开 |
| **barchart** | `quote` `options` `greeks` `flow` | 浏览器 |
| **chaoxing** | `assignments` `exams` | 浏览器 |
| **grok** | `ask` | 浏览器 |
| **hf** | `top` | 公开 |
| **jike** | `feed` `search` `create` `like` `comment` `repost` `notifications` `post` `topic` `user` | 浏览器 |
| **jimeng** | `generate` `history` | 浏览器 |
| **yollomi** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | 浏览器 |
| **linux-do** | `feed` `categories` `tags` `search` `topic` `user-topics` `user-posts` | 浏览器 |
| **stackoverflow** | `hot` `search` `bounties` `unanswered` | 公开 |
| **steam** | `top-sellers` | 公开 |
| **weread** | `shelf` `search` `book` `highlights` `notes` `notebooks` `ranking` | 浏览器 |
| **douban** | `search` `top250` `subject` `photos` `download` `marks` `reviews` `movie-hot` `book-hot` | 浏览器 |
| **facebook** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 浏览器 |
| **google** | `news` `search` `suggest` `trends` | 公开 |
| **36kr** | `news` `hot` `search` `article` | 公开 / 浏览器 |
| **imdb** | `search` `title` `top` `trending` `person` `reviews` | 公开 |
| **producthunt** | `posts` `today` `hot` `browse` | 公开 / 浏览器 |
| **instagram** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 浏览器 |
| **lobsters** | `hot` `newest` `active` `tag` | 公开 |
| **medium** | `feed` `search` `user` | 浏览器 |
| **sinablog** | `hot` `search` `article` `user` | 浏览器 |
| **substack** | `feed` `search` `publication` | 浏览器 |
| **pixiv** | `ranking` `search` `user` `illusts` `detail` `download` | 浏览器 |
| **tiktok** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | 浏览器 |
### 外部 CLI 枢纽
OpenCLI 也可以作为你现有命令行工具的统一入口,负责发现、自动安装和纯透传执行。
| 外部 CLI | 描述 | 示例 |
|----------|------|------|
| **gh** | GitHub CLI | `opencli gh pr list --limit 5` |
| **obsidian** | Obsidian 仓库管理 | `opencli obsidian search query="AI"` |
| **docker** | Docker 命令行工具 | `opencli docker ps` |
| **readwise** | Readwise / Reader CLI | `opencli readwise login` |
| **gws** | Google Workspace CLI — Docs, Sheets, Drive, Gmail, Calendar | `opencli gws docs list` |
**零配置透传**:OpenCLI 会把你的输入原样转发给底层二进制,保留原生 stdout / stderr 行为。
**自动安装**:如果你运行 `opencli gh ...` 时系统中还没有 `gh`,OpenCLI 会优先尝试通过系统包管理器安装,然后自动重试命令。
**注册自定义本地 CLI**
```bash
opencli register mycli
```
### 桌面应用适配器
每个桌面适配器都有自己详细的文档说明,包括命令参考、启动配置与使用示例:
| 应用 | 描述 | 文档 |
|-----|-------------|-----|
| **Cursor** | 控制 Cursor IDE — Composer、对话、代码提取等 | [Doc](./docs/adapters/desktop/cursor.md) |
| **Codex** | 在后台(无头)驱动 OpenAI Codex CLI Agent | [Doc](./docs/adapters/desktop/codex.md) |
| **Antigravity** | 在终端直接控制 Antigravity Ultra | [Doc](./docs/adapters/desktop/antigravity.md) |
| **ChatGPT** | 自动化操作 ChatGPT macOS 桌面客户端 | [Doc](./docs/adapters/desktop/chatgpt.md) |
| **ChatWise** | 多 LLM 客户端(GPT-4、Claude、Gemini | [Doc](./docs/adapters/desktop/chatwise.md) |
| **Notion** | 搜索、读取、写入 Notion 页面 | [Doc](./docs/adapters/desktop/notion.md) |
| **Discord** | Discord 桌面版 — 消息、频道、服务器 | [Doc](./docs/adapters/desktop/discord.md) |
| **Doubao** | 通过 CDP 控制豆包桌面应用 | [Doc](./docs/adapters/desktop/doubao-app.md) |
## 下载支持
OpenCLI 支持从各平台下载图片、视频和文章。
### 支持的平台
| 平台 | 内容类型 | 说明 |
|------|----------|------|
| **小红书** | 图片、视频 | 下载笔记中的所有媒体文件 |
| **B站** | 视频 | 需要安装 `yt-dlp` |
| **Twitter/X** | 图片、视频 | 从用户媒体页或单条推文下载 |
| **Pixiv** | 图片 | 下载原始画质插画,支持多页作品 |
| **知乎** | 文章(Markdown) | 导出文章,可选下载图片到本地 |
| **微信公众号** | 文章(Markdown | 导出微信公众号文章为 Markdown |
| **豆瓣** | 图片 | 下载电影条目的海报 / 剧照图片 |
### 前置依赖
下载流媒体平台的视频需要安装 `yt-dlp`
```bash
# 安装 yt-dlp
pip install yt-dlp
# 或者
brew install yt-dlp
```
### 使用示例
```bash
# 下载小红书笔记中的图片/视频
opencli xiaohongshu download abc123 --output ./xhs
# 下载B站视频(需要 yt-dlp
opencli bilibili download BV1xxx --output ./bilibili
opencli bilibili download BV1xxx --quality 1080p # 指定画质
# 下载 Twitter 用户的媒体
opencli twitter download elonmusk --limit 20 --output ./twitter
# 下载单条推文的媒体
opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
# 下载豆瓣电影海报 / 剧照
opencli douban download 30382501 --output ./douban
# 导出知乎文章为 Markdown
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
# 导出并下载图片
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --download-images
# 导出微信公众号文章为 Markdown
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --output ./weixin
```
## 输出格式
@@ -151,6 +295,28 @@ opencli bilibili hot -f csv # CSV
opencli bilibili hot -v # 详细模式:展示管线执行步骤调试信息
```
## 插件
通过社区贡献的插件扩展 OpenCLI。插件使用与内置命令相同的 YAML/TS 格式,启动时自动发现。
```bash
opencli plugin install github:user/opencli-plugin-my-tool # 安装
opencli plugin list # 查看已安装
opencli plugin update my-tool # 更新到最新
opencli plugin update --all # 更新全部已安装插件
opencli plugin uninstall my-tool # 卸载
```
当 plugin 的版本被记录到 `~/.opencli/plugins.lock.json` 后,`opencli plugin list` 也会显示对应的短 commit hash。
| 插件 | 类型 | 描述 |
|------|------|------|
| [opencli-plugin-github-trending](https://github.com/ByteYue/opencli-plugin-github-trending) | YAML | GitHub Trending 仓库 |
| [opencli-plugin-hot-digest](https://github.com/ByteYue/opencli-plugin-hot-digest) | TS | 多平台热榜聚合 |
| [opencli-plugin-juejin](https://github.com/Astro-Han/opencli-plugin-juejin) | YAML | 稀土掘金热门文章 |
详见 [插件指南](./docs/zh/guide/plugins.md) 了解如何创建自己的插件。
## 致 AI Agent(开发者指南)
如果你是一个被要求查阅代码并编写新 `opencli` 适配器的 AI,请遵守以下工作流。
@@ -177,24 +343,25 @@ opencli cascade https://api.example.com/data
## 常见问题排查
- **"Failed to connect to Playwright MCP Bridge"** 报错
- 确保你当前的 Chrome 已安装且**开启了** Playwright MCP Bridge 浏览器插件
- 如果是刚装完插件,需要重启 Chrome 浏览器。
- **"Extension not connected" 报错**
- 确保你当前的 Chrome 已安装且**开启了** opencli Browser Bridge 扩展(在 `chrome://extensions` 中检查)
- **"attach failed: Cannot access a chrome-extension:// URL" 报错**
- 其他 Chrome 扩展(如 youmind、New Tab Override 或 AI 助手类扩展)可能产生冲突。请尝试**暂时禁用其他扩展**后重试。
- **返回空数据,或者报错 "Unauthorized"**
- Chrome 里的登录态可能已经过期(甚至被要求过滑动验证码)。请打开当前 Chrome 页面,在新标签页重新手工登录或刷新该页面。
- Chrome 里的登录态可能已经过期。请打开当前 Chrome 页面,在新标签页重新手工登录或刷新该页面。
- **Node API 错误 (如 parseArgs, fs 等)**
- 确保 Node.js 版本 `>= 18`。旧版不支持我们使用的现代核心库 API
- 确保 Node.js 版本 `>= 20`
- **Daemon 问题**
- 检查 daemon 状态:`curl localhost:19825/status`
- 查看扩展日志:`curl localhost:19825/logs`
## 版本发布
```bash
npm version patch # 0.1.0 → 0.1.1
npm version minor # 0.1.0 → 0.2.0
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=jackwener/opencli&type=Date)](https://star-history.com/#jackwener/opencli&Date)
# 推送 tagGitHub Actions 将自动执行发版和 npm 发布
git push --follow-tags
```
## License
[BSD-3-Clause](./LICENSE)
[Apache-2.0](./LICENSE)
+566 -37
View File
@@ -1,20 +1,26 @@
---
name: opencli
description: "OpenCLI — Make any website your CLI. Zero risk, AI-powered, reuse Chrome login."
version: 0.5.0
description: "OpenCLI — Make any website or Electron App your CLI. Zero risk, AI-powered, reuse Chrome login."
version: 1.4.1
author: jackwener
tags: [cli, browser, web, mcp, playwright, bilibili, zhihu, twitter, github, v2ex, hackernews, reddit, xiaohongshu, xueqiu, AI, agent]
tags: [cli, browser, web, chrome-extension, cdp, bilibili, zhihu, twitter, github, v2ex, hackernews, reddit, xiaohongshu, xueqiu, youtube, boss, coupang, yollomi, AI, agent]
---
# OpenCLI
> Make any website your CLI. Reuse Chrome login, zero risk, AI-powered discovery.
> Make any website or Electron App your CLI. Reuse Chrome login, zero risk, AI-powered discovery.
> [!CAUTION]
> **AI Agent 必读:创建或修改任何适配器之前,你必须先阅读 [CLI-EXPLORER.md](./CLI-EXPLORER.md)**
> 该文档包含完整的 API 发现工作流(必须使用 Playwright MCP Bridge 浏览器探索)、5 级认证策略决策树、平台 SDK 速查表、`tap` 步骤调试流程、分页 API 模板、级联请求模式、以及常见陷阱。
> 该文档包含完整的 API 发现工作流(必须使用浏览器探索)、5 级认证策略决策树、平台 SDK 速查表、`tap` 步骤调试流程、分页 API 模板、级联请求模式、以及常见陷阱。
> **本文件(SKILL.md)仅提供命令参考和简化模板,不足以正确开发适配器。**
> [!IMPORTANT]
> 创建或修改 adapter 时,再额外遵守 3 条收口规则:
> 1. 主参数优先用 positional arg,不要把 `query` / `id` / `url` 默认做成 `--query` / `--id` / `--url`
> 2. 预期中的 adapter 失败优先抛 `CliError` 子类,不要直接 throw 原始 `Error`
> 3. 新增 adapter 或新增用户可发现命令时,同步更新 adapter docs、`docs/adapters/index.md`、sidebar,以及 README/README.zh-CN 中受影响的入口
## Install & Run
```bash
@@ -34,11 +40,12 @@ npm update -g @jackwener/opencli
Browser commands require:
1. Chrome browser running **(logged into target sites)**
2. [Playwright MCP Bridge](https://chromewebstore.google.com/detail/playwright-mcp-bridge/mmlmfjhmonkocbjadbfplnigmagldckm) extension installed and configured
2. **opencli Browser Bridge** Chrome extension installed (load `extension/` as unpacked in `chrome://extensions`)
3. No further setup needed — the daemon auto-starts on first browser command
> **Note**: You must be logged into the target website in Chrome before running commands. Tabs opened during command execution are auto-closed afterwards.
Public API commands (`hackernews`, `github search`, `v2ex`) need no browser.
Public API commands (`hackernews`, `v2ex`) need no browser.
## Commands Reference
@@ -47,7 +54,7 @@ Public API commands (`hackernews`, `github search`, `v2ex`) need no browser.
```bash
# Bilibili (browser)
opencli bilibili hot --limit 10 # B站热门视频
opencli bilibili search --keyword "rust" # 搜索视频
opencli bilibili search "rust" # 搜索视频 (query positional)
opencli bilibili me # 我的信息
opencli bilibili favorite # 我的收藏
opencli bilibili history --limit 20 # 观看历史
@@ -60,15 +67,19 @@ opencli bilibili following --limit 20 # 我的关注列表 (支持 --uid 查
# 知乎 (browser)
opencli zhihu hot --limit 10 # 知乎热榜
opencli zhihu search --keyword "AI" # 搜索
opencli zhihu question --id 34816524 # 问题详情和回答
opencli zhihu search "AI" # 搜索 (query positional)
opencli zhihu question 34816524 # 问题详情和回答 (id positional)
# 小红书 (browser)
opencli xiaohongshu search --keyword "美食" # 搜索笔记
opencli xiaohongshu search "美食" # 搜索笔记 (query positional)
opencli xiaohongshu notifications # 通知(mentions/likes/connections
opencli xiaohongshu feed --limit 10 # 推荐 Feed
opencli xiaohongshu me # 我的信息
opencli xiaohongshu user --uid xxx # 用户主页
opencli xiaohongshu user xxx # 用户主页 (id positional)
opencli xiaohongshu creator-notes --limit 10 # 创作者笔记列表
opencli xiaohongshu creator-note-detail --note-id xxx # 笔记详情
opencli xiaohongshu creator-notes-summary # 笔记数据概览
opencli xiaohongshu creator-profile # 创作者资料
opencli xiaohongshu creator-stats # 创作者数据统计
# 雪球 Xueqiu (browser)
opencli xueqiu hot-stock --limit 10 # 雪球热门股票榜
@@ -76,32 +87,82 @@ opencli xueqiu stock --symbol SH600519 # 查看股票实时行情
opencli xueqiu watchlist # 获取自选股/持仓列表
opencli xueqiu feed # 我的关注 timeline
opencli xueqiu hot --limit 10 # 雪球热榜
opencli xueqiu search --keyword "特斯拉" # 搜索
opencli xueqiu search "特斯拉" # 搜索 (query positional)
opencli xueqiu earnings-date SH600519 # 股票财报发布日期 (symbol positional)
opencli xueqiu fund-holdings # 蛋卷基金持仓明细 (支持 --account 过滤)
opencli xueqiu fund-snapshot # 蛋卷基金快照(总资产、子账户、持仓)
# GitHub (public)
opencli github search --keyword "cli" # 搜索仓库
# GitHub (via gh External CLI)
opencli gh repo list # 列出仓库 (passthrough to gh)
opencli gh pr list --limit 5 # PR 列表
opencli gh issue list # Issue 列表
# Twitter/X (browser)
opencli twitter trending --limit 10 # 热门话题
opencli twitter bookmarks --limit 20 # 获取收藏的书签推文
opencli twitter search --keyword "AI" # 搜索推文
opencli twitter profile --username elonmusk # 用户资料
opencli twitter search "AI" # 搜索推文 (query positional)
opencli twitter profile elonmusk # 用户资料
opencli twitter timeline --limit 20 # 时间线
opencli twitter thread 1234567890 # 推文 thread(原文 + 回复)
opencli twitter article 1891511252174299446 # 推文长文内容
opencli twitter follow elonmusk # 关注用户
opencli twitter unfollow elonmusk # 取消关注
opencli twitter bookmark https://x.com/... # 收藏推文
opencli twitter unbookmark https://x.com/... # 取消收藏
opencli twitter post "Hello world" # 发布推文 (text positional)
opencli twitter like https://x.com/... # 点赞推文 (url positional)
opencli twitter reply https://x.com/... "Nice!" # 回复推文 (url + text positional)
opencli twitter delete https://x.com/... # 删除推文 (url positional)
opencli twitter block elonmusk # 屏蔽用户 (username positional)
opencli twitter unblock elonmusk # 取消屏蔽 (username positional)
opencli twitter followers elonmusk # 用户的粉丝列表 (user positional)
opencli twitter following elonmusk # 用户的关注列表 (user positional)
opencli twitter notifications --limit 20 # 通知列表
opencli twitter hide-reply https://x.com/... # 隐藏回复 (url positional)
opencli twitter download elonmusk # 下载用户媒体 (username positional, 支持 --tweet-url)
opencli twitter accept "群,微信" # 自动接受含关键词的 DM 请求 (query positional)
opencli twitter reply-dm "消息内容" # 批量回复 DM (text positional)
# Reddit (browser)
opencli reddit hot --limit 10 # 热门帖子
opencli reddit hot --subreddit programming # 指定子版块
opencli reddit frontpage --limit 10 # 首页
opencli reddit search --keyword "AI" # 搜索
opencli reddit subreddit --name rust # 子版块浏览
opencli reddit frontpage --limit 10 # 首页 /r/all
opencli reddit popular --limit 10 # /r/popular 热门
opencli reddit search "AI" --sort top --time week # 搜索(支持排序+时间过滤)
opencli reddit subreddit rust --sort top --time month # 子版块浏览(支持时间过滤)
opencli reddit read --post-id 1abc123 # 阅读帖子 + 评论
opencli reddit user spez # 用户资料(karma、注册时间)
opencli reddit user-posts spez # 用户发帖历史
opencli reddit user-comments spez # 用户评论历史
opencli reddit upvote --post-id xxx --direction up # 投票(up/down/none
opencli reddit save --post-id xxx # 收藏帖子
opencli reddit comment --post-id xxx "Great!" # 发表评论 (text positional)
opencli reddit subscribe --subreddit python # 订阅子版块
opencli reddit saved --limit 10 # 我的收藏
opencli reddit upvoted --limit 10 # 我的赞
# V2EX (public)
# V2EX (public + browser)
opencli v2ex hot --limit 10 # 热门话题
opencli v2ex latest --limit 10 # 最新话题
opencli v2ex topic --id 1024 # 主题详情
opencli v2ex topic 1024 # 主题详情 (id positional)
opencli v2ex daily # 每日签到 (browser)
opencli v2ex me # 我的信息 (browser)
opencli v2ex notifications --limit 10 # 通知 (browser)
opencli v2ex node python # 节点话题列表 (name positional)
opencli v2ex nodes --limit 30 # 所有节点列表
opencli v2ex member username # 用户资料 (username positional)
opencli v2ex user username # 用户发帖列表 (username positional)
opencli v2ex replies 1024 # 主题回复列表 (id positional)
# Hacker News (public)
opencli hackernews top --limit 10 # Top stories
opencli hackernews new --limit 10 # Newest stories
opencli hackernews best --limit 10 # Best stories
opencli hackernews ask --limit 10 # Ask HN posts
opencli hackernews show --limit 10 # Show HN posts
opencli hackernews jobs --limit 10 # Job postings
opencli hackernews search "rust" # 搜索 (query positional)
opencli hackernews user dang # 用户资料 (username positional)
# BBC (public)
opencli bbc news --limit 10 # BBC News RSS headlines
@@ -110,32 +171,363 @@ opencli bbc news --limit 10 # BBC News RSS headlines
opencli weibo hot --limit 10 # 微博热搜
# BOSS直聘 (browser)
opencli boss search --query "AI agent" # 搜索职位
opencli boss search "AI agent" # 搜索职位 (query positional)
opencli boss detail --security-id xxx # 职位详情
opencli boss recommend --limit 10 # 推荐职位
opencli boss joblist --limit 10 # 职位列表
opencli boss greet --security-id xxx # 打招呼
opencli boss batchgreet --job-id xxx # 批量打招呼
opencli boss send --uid xxx "消息内容" # 发消息 (text positional)
opencli boss chatlist --limit 10 # 聊天列表
opencli boss chatmsg --security-id xxx # 聊天记录
opencli boss invite --security-id xxx # 邀请沟通
opencli boss mark --security-id xxx # 标记管理
opencli boss exchange --security-id xxx # 交换联系方式
opencli boss resume # 简历管理
opencli boss stats # 数据统计
# YouTube (browser)
opencli youtube search --query "rust" # 搜索视频
opencli youtube search "rust" # 搜索视频 (query positional)
opencli youtube video "https://www.youtube.com/watch?v=xxx" # 视频元数据
opencli youtube transcript "https://www.youtube.com/watch?v=xxx" # 获取视频字幕/转录
opencli youtube transcript "xxx" --lang zh-Hans --mode raw # 指定语言 + 原始时间戳模式
# Yahoo Finance (browser)
opencli yahoo-finance quote --symbol AAPL # 股票行情
# Sina Finance
opencli sinafinance news --limit 10 --type 1 # 7x24实时快讯 (0=全部 1=A股 2=宏观 3=公司 4=数据 5=市场 6=国际 7=观点 8=央行 9=其它)
# Reuters (browser)
opencli reuters search --query "AI" # 路透社搜索
opencli reuters search "AI" # 路透社搜索 (query positional)
# 什么值得买 (browser)
opencli smzdm search --keyword "耳机" # 搜索好价
opencli smzdm search "耳机" # 搜索好价 (query positional)
# 携程 (browser)
opencli ctrip search --query "三亚" # 搜索目的地
opencli ctrip search "三亚" # 搜索目的地 (query positional)
# Antigravity (Electron/CDP)
opencli antigravity status # 检查 CDP 连接
opencli antigravity send "hello" # 发送文本到当前 agent 聊天框
opencli antigravity read # 读取整个聊天记录面板
opencli antigravity new # 清空聊天、开启新对话
opencli antigravity dump # 导出 DOM 和快照调试信息
opencli antigravity extract-code # 自动抽取 AI 回复中的代码块
opencli antigravity model claude # 切换底层模型
opencli antigravity watch # 流式监听增量消息
# Barchart (browser)
opencli barchart quote --symbol AAPL # 股票行情
opencli barchart options --symbol AAPL # 期权链
opencli barchart greeks --symbol AAPL # 期权 Greeks
opencli barchart flow --limit 20 # 异常期权活动
# Jike 即刻 (browser)
opencli jike feed --limit 10 # 动态流
opencli jike search "AI" # 搜索 (query positional)
opencli jike create "内容" # 发布动态 (text positional)
opencli jike like xxx # 点赞 (id positional)
opencli jike comment xxx "评论" # 评论 (id + text positional)
opencli jike repost xxx # 转发 (id positional)
opencli jike notifications # 通知
# Linux.do (public + browser)
opencli linux-do hot --limit 10 # 热门话题
opencli linux-do latest --limit 10 # 最新话题
opencli linux-do search "rust" # 搜索 (query positional)
opencli linux-do topic 1024 # 主题详情 (id positional)
opencli linux-do categories --limit 20 # 分类列表 (browser)
opencli linux-do category dev 7 # 分类内话题 (slug + id positional, browser)
# StackOverflow (public)
opencli stackoverflow hot --limit 10 # 热门问题
opencli stackoverflow search "typescript" # 搜索 (query positional)
opencli stackoverflow bounties --limit 10 # 悬赏问题
# WeRead 微信读书 (browser)
opencli weread shelf --limit 10 # 书架
opencli weread search "AI" # 搜索图书 (query positional)
opencli weread book xxx # 图书详情 (book-id positional)
opencli weread highlights xxx # 划线笔记 (book-id positional)
opencli weread notes xxx # 想法笔记 (book-id positional)
opencli weread ranking --limit 10 # 排行榜
# Jimeng 即梦 AI (browser)
opencli jimeng generate --prompt "描述" # AI 生图
opencli jimeng history --limit 10 # 生成历史
# Yollomi yollomi.com (browser — 需在 Chrome 登录 yollomi.com,复用站点 session)
opencli yollomi models --type image # 列出图像模型与积分
opencli yollomi generate "提示词" --model z-image-turbo # 文生图
opencli yollomi video "提示词" --model kling-2-1 # 视频
opencli yollomi upload ./photo.jpg # 上传得 URL,供 img2img / 工具链使用
opencli yollomi remove-bg <image-url> # 去背景(免费)
opencli yollomi edit <image-url> "改成油画风格" # Qwen 图像编辑
opencli yollomi background <image-url> # AI 背景生成 (5 credits)
opencli yollomi face-swap --source <url> --target <url> # 换脸 (3 credits)
opencli yollomi object-remover <image-url> <mask-url> # AI 去除物体 (3 credits)
opencli yollomi restore <image-url> # AI 修复老照片 (4 credits)
opencli yollomi try-on --person <url> --cloth <url> # 虚拟试衣 (3 credits)
opencli yollomi upscale <image-url> # AI 超分辨率 (1 credit, 支持 --scale 2/4)
# Grok (default + explicit web)
opencli grok ask --prompt "问题" # 提问 Grok(兼容默认路径)
opencli grok ask --prompt "问题" --web # 显式 grok.com consumer web UI 路径
# HuggingFace (public)
opencli hf top --limit 10 # 热门模型
# 超星学习通 (browser)
opencli chaoxing assignments # 作业列表
opencli chaoxing exams # 考试列表
# Douban 豆瓣 (browser)
opencli douban search "三体" # 搜索 (query positional)
opencli douban top250 # 豆瓣 Top 250
opencli douban subject 1234567 # 条目详情 (id positional)
opencli douban photos 30382501 # 图片列表 / 直链(默认海报)
opencli douban download 30382501 # 下载海报 / 剧照
opencli douban marks --limit 10 # 我的标记
opencli douban reviews --limit 10 # 短评
# Facebook (browser)
opencli facebook feed --limit 10 # 动态流
opencli facebook profile username # 用户资料 (id positional)
opencli facebook search "AI" # 搜索 (query positional)
opencli facebook friends # 好友列表
opencli facebook groups # 群组
opencli facebook events # 活动
opencli facebook notifications # 通知
opencli facebook memories # 回忆
opencli facebook add-friend username # 添加好友 (id positional)
opencli facebook join-group groupid # 加入群组 (id positional)
# Instagram (browser)
opencli instagram explore # 探索
opencli instagram profile username # 用户资料 (id positional)
opencli instagram search "AI" # 搜索 (query positional)
opencli instagram user username # 用户详情 (id positional)
opencli instagram followers username # 粉丝 (id positional)
opencli instagram following username # 关注 (id positional)
opencli instagram follow username # 关注用户 (id positional)
opencli instagram unfollow username # 取消关注 (id positional)
opencli instagram like postid # 点赞 (id positional)
opencli instagram unlike postid # 取消点赞 (id positional)
opencli instagram comment postid "评论" # 评论 (id + text positional)
opencli instagram save postid # 收藏 (id positional)
opencli instagram unsave postid # 取消收藏 (id positional)
opencli instagram saved # 已收藏列表
# TikTok (browser)
opencli tiktok explore # 探索
opencli tiktok search "AI" # 搜索 (query positional)
opencli tiktok profile username # 用户资料 (id positional)
opencli tiktok user username # 用户详情 (id positional)
opencli tiktok following username # 关注列表 (id positional)
opencli tiktok follow username # 关注 (id positional)
opencli tiktok unfollow username # 取消关注 (id positional)
opencli tiktok like videoid # 点赞 (id positional)
opencli tiktok unlike videoid # 取消点赞 (id positional)
opencli tiktok comment videoid "评论" # 评论 (id + text positional)
opencli tiktok save videoid # 收藏 (id positional)
opencli tiktok unsave videoid # 取消收藏 (id positional)
opencli tiktok live # 直播
opencli tiktok notifications # 通知
opencli tiktok friends # 朋友
# Medium (browser)
opencli medium feed --limit 10 # 动态流
opencli medium search "AI" # 搜索 (query positional)
opencli medium user username # 用户主页 (id positional)
# Substack (browser)
opencli substack feed --limit 10 # 订阅动态
opencli substack search "AI" # 搜索 (query positional)
opencli substack publication name # 出版物详情 (id positional)
# Sinablog 新浪博客 (browser)
opencli sinablog hot --limit 10 # 热门
opencli sinablog search "AI" # 搜索 (query positional)
opencli sinablog article url # 文章详情
opencli sinablog user username # 用户主页 (id positional)
# Lobsters (public)
opencli lobsters hot --limit 10 # 热门
opencli lobsters newest --limit 10 # 最新
opencli lobsters active --limit 10 # 活跃
opencli lobsters tag rust # 按标签筛选 (tag positional)
# Google (public)
opencli google news --limit 10 # 新闻
opencli google search "AI" # 搜索 (query positional)
opencli google suggest "AI" # 搜索建议 (query positional)
opencli google trends # 趋势
# DEV.to (public)
opencli devto top --limit 10 # 热门文章
opencli devto tag javascript --limit 10 # 按标签 (tag positional)
opencli devto user username # 用户文章 (username positional)
# Steam (public)
opencli steam top-sellers --limit 10 # 热销游戏
# Apple Podcasts (public)
opencli apple-podcasts top --limit 10 # 热门播客排行榜 (支持 --country us/cn/gb/jp)
opencli apple-podcasts search "科技" # 搜索播客 (query positional)
opencli apple-podcasts episodes 12345 # 播客剧集列表 (id positional, 用 search 获取 ID)
# arXiv (public)
opencli arxiv search "attention" # 搜索论文 (query positional)
opencli arxiv paper 1706.03762 # 论文详情 (id positional)
# Bloomberg (public RSS + browser)
opencli bloomberg main --limit 10 # Bloomberg 首页头条 (RSS)
opencli bloomberg markets --limit 10 # 市场新闻 (RSS)
opencli bloomberg tech --limit 10 # 科技新闻 (RSS)
opencli bloomberg politics --limit 10 # 政治新闻 (RSS)
opencli bloomberg economics --limit 10 # 经济新闻 (RSS)
opencli bloomberg opinions --limit 10 # 观点 (RSS)
opencli bloomberg industries --limit 10 # 行业新闻 (RSS)
opencli bloomberg businessweek --limit 10 # Businessweek (RSS)
opencli bloomberg feeds # 列出所有 RSS feed 别名
opencli bloomberg news "https://..." # 阅读 Bloomberg 文章全文 (link positional, browser)
# Coupang 쿠팡 (browser)
opencli coupang search "耳机" # 搜索商品 (query positional, 支持 --filter rocket)
opencli coupang add-to-cart 12345 # 加入购物车 (product-id positional, 或 --url)
# Dictionary (public)
opencli dictionary search "serendipity" # 单词释义 (word positional)
opencli dictionary synonyms "happy" # 近义词 (word positional)
opencli dictionary examples "ubiquitous" # 例句 (word positional)
# 豆包 Doubao Web (browser)
opencli doubao status # 检查豆包页面状态
opencli doubao new # 新建对话
opencli doubao send "你好" # 发送消息 (text positional)
opencli doubao read # 读取对话记录
opencli doubao ask "问题" # 一键提问并等回复 (text positional)
# 京东 JD (browser)
opencli jd item 100291143898 # 商品详情 (sku positional, 含价格/主图/规格)
# LinkedIn (browser)
opencli linkedin search "AI engineer" # 搜索职位 (query positional, 支持 --location/--company/--remote)
opencli linkedin timeline --limit 20 # 首页动态流
# Pixiv (browser)
opencli pixiv ranking --limit 20 # 插画排行榜 (支持 --mode daily/weekly/monthly)
opencli pixiv search "風景" # 搜索插画 (query positional)
opencli pixiv user 12345 # 画师资料 (uid positional)
opencli pixiv illusts 12345 # 画师作品列表 (user-id positional)
opencli pixiv detail 12345 # 插画详情 (id positional)
opencli pixiv download 12345 # 下载插画 (illust-id positional)
# Web (browser)
opencli web read --url "https://..." # 抓取任意网页并导出为 Markdown
# 微信公众号 Weixin (browser)
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" # 下载公众号文章为 Markdown
# 小宇宙 Xiaoyuzhou (public)
opencli xiaoyuzhou podcast 12345 # 播客资料 (id positional)
opencli xiaoyuzhou podcast-episodes 12345 # 播客剧集列表 (id positional)
opencli xiaoyuzhou episode 12345 # 单集详情 (id positional)
# Wikipedia (public)
opencli wikipedia search "AI" # 搜索 (query positional)
opencli wikipedia summary "Python" # 摘要 (title positional)
```
### Desktop Adapter Commands
```bash
# Cursor (desktop — CDP via Electron)
opencli cursor status # 检查连接
opencli cursor send "message" # 发送消息
opencli cursor read # 读取回复
opencli cursor new # 新建对话
opencli cursor dump # 导出 DOM 调试信息
opencli cursor composer # Composer 模式
opencli cursor model claude # 切换模型
opencli cursor extract-code # 提取代码块
opencli cursor ask "question" # 一键提问并等回复
opencli cursor screenshot # 截图
opencli cursor history # 对话历史
opencli cursor export # 导出对话
# Codex (desktop — headless CLI agent)
opencli codex status # 检查连接
opencli codex send "message" # 发送消息
opencli codex read # 读取回复
opencli codex new # 新建对话
opencli codex dump # 导出调试信息
opencli codex extract-diff # 提取 diff
opencli codex model gpt-4 # 切换模型
opencli codex ask "question" # 一键提问并等回复
opencli codex screenshot # 截图
opencli codex history # 对话历史
opencli codex export # 导出对话
# ChatGPT (desktop — macOS AppleScript/CDP)
opencli chatgpt status # 检查应用状态
opencli chatgpt new # 新建对话
opencli chatgpt send "message" # 发送消息
opencli chatgpt read # 读取回复
opencli chatgpt ask "question" # 一键提问并等回复
# ChatWise (desktop — multi-LLM client)
opencli chatwise status # 检查连接
opencli chatwise new # 新建对话
opencli chatwise send "message" # 发送消息
opencli chatwise read # 读取回复
opencli chatwise ask "question" # 一键提问并等回复
opencli chatwise model claude # 切换模型
opencli chatwise history # 对话历史
opencli chatwise export # 导出对话
opencli chatwise screenshot # 截图
# Notion (desktop — CDP via Electron)
opencli notion status # 检查连接
opencli notion search "keyword" # 搜索页面
opencli notion read # 读取当前页面
opencli notion new # 新建页面
opencli notion write "content" # 写入内容
opencli notion sidebar # 侧边栏导航
opencli notion favorites # 收藏列表
opencli notion export # 导出
# Discord App (desktop — CDP via Electron)
opencli discord-app status # 检查连接
opencli discord-app send "message" # 发送消息
opencli discord-app read # 读取消息
opencli discord-app channels # 频道列表
opencli discord-app servers # 服务器列表
opencli discord-app search "keyword" # 搜索
opencli discord-app members # 成员列表
# Doubao App 豆包桌面版 (desktop — CDP via Electron)
opencli doubao-app status # 检查连接
opencli doubao-app new # 新建对话
opencli doubao-app send "message" # 发送消息
opencli doubao-app read # 读取回复
opencli doubao-app ask "question" # 一键提问并等回复
opencli doubao-app screenshot # 截图
opencli doubao-app dump # 导出 DOM 调试信息
```
### Management Commands
```bash
opencli list # List all commands
opencli list # List all commands (including External CLIs)
opencli list --json # JSON output
opencli list -f yaml # YAML output
opencli install <name> # Auto-install an external CLI (e.g., gh, obsidian)
opencli register <name> # Register a local custom CLI for unified discovery
opencli validate # Validate all CLI definitions
opencli validate bilibili # Validate specific site
opencli doctor # Diagnose browser bridge (auto-starts daemon, includes live test)
```
### AI Agent Workflow
@@ -150,14 +542,26 @@ opencli synthesize <site>
# Generate: one-shot explore → synthesize → register
opencli generate <url> --goal "hot"
# Record: YOU operate the page, opencli captures every API call → YAML candidates
# Opens the URL in automation window, injects fetch/XHR interceptor into ALL tabs,
# polls every 2s, auto-stops after 60s (or press Enter to stop early).
opencli record <url> # 录制,site name 从域名推断
opencli record <url> --site mysite # 指定 site name
opencli record <url> --timeout 120000 # 自定义超时(毫秒,默认 60000)
opencli record <url> --poll 1000 # 缩短轮询间隔(毫秒,默认 2000)
opencli record <url> --out .opencli/record/x # 自定义输出目录
# Output:
# .opencli/record/<site>/captured.json ← 原始捕获数据(带 url/method/body
# .opencli/record/<site>/candidates/*.yaml ← 高置信度候选适配器(score ≥ 8,有 array 结果)
# Strategy Cascade: auto-probe PUBLIC → COOKIE → HEADER
opencli cascade <api-url>
# Explore with interactive fuzzing (click buttons to trigger lazy APIs)
opencli explore <url> --auto --click "字幕,CC,评论"
# Verify: smoke-test a generated adapter
opencli verify <site/name> --smoke
# Validate: validate adapter definitions
opencli validate
```
## Output Formats
@@ -180,6 +584,129 @@ opencli bilibili hot -f csv # CSV
opencli bilibili hot -v # Show each pipeline step and data flow
```
## Record Workflow
`record` 是为「无法用 `explore` 自动发现」的页面(需要登录操作、复杂交互、SPA 内路由)准备的手动录制方案。
### 工作原理
```
opencli record <url>
→ 打开 automation window 并导航到目标 URL
→ 向所有 tab 注入 fetch/XHR 拦截器(幂等,可重复注入)
→ 每 2s 轮询一次:发现新 tab 自动注入,drain 所有 tab 的捕获缓冲区
→ 超时(默认 60s)或按 Enter 停止
→ 分析捕获到的 JSON 请求:去重 → 评分 → 生成候选 YAML
```
**拦截器特性**
- 同时 patch `window.fetch``XMLHttpRequest`
- 只捕获 `Content-Type: application/json` 的响应
- 过滤纯对象少于 2 个 key 的响应(避免 tracking/ping
- 跨 tab 隔离:每个 tab 独立缓冲区,轮询时分别 drain
- 幂等注入:同一 tab 二次注入时先 restore 原始函数再重新 patch,不丢失已捕获数据
### 使用步骤
```bash
# 1. 启动录制(建议 --timeout 给足操作时间)
opencli record "https://example.com/page" --timeout 120000
# 2. 在弹出的 automation window 里正常操作页面:
# - 打开列表、搜索、点击条目、切换 Tab
# - 凡是触发网络请求的操作都会被捕获
# 3. 完成操作后按 Enter 停止(或等超时自动停止)
# 4. 查看结果
cat .opencli/record/<site>/captured.json # 原始捕获
ls .opencli/record/<site>/candidates/ # 候选 YAML
```
### 页面类型与捕获预期
| 页面类型 | 预期捕获量 | 说明 |
|---------|-----------|------|
| 列表/搜索页 | 多(5~20+) | 每次搜索/翻页都会触发新请求 |
| 详情页(只读) | 少(1~5) | 首屏数据一次性返回,后续操作走 form/redirect |
| SPA 内路由跳转 | 中等 | 路由切换会触发新接口,但首屏请求在注入前已发出 |
| 需要登录的页面 | 视操作而定 | 确保 Chrome 已登录目标网站 |
> **注意**:如果页面在导航完成前就发出了大部分请求(服务端渲染 / SSR 注水),拦截器会错过这些请求。
> 解决方案:在页面加载完成后,手动触发能产生新请求的操作(搜索、翻页、切 Tab、展开折叠项等)。
### 候选 YAML → TS CLI 转换
生成的候选 YAML 是起点,通常需要转换为 TypeScript(尤其是 tae 等内部系统):
**候选 YAML 结构**(自动生成):
```yaml
site: tae
name: getList # 从 URL path 推断的名称
strategy: cookie
browser: true
pipeline:
- navigate: https://...
- evaluate: |
(async () => {
const res = await fetch('/approval/getList.json?procInsId=...', { credentials: 'include' });
const data = await res.json();
return (data?.content?.operatorRecords || []).map(item => ({ ... }));
})()
```
**转换为 TS CLI**(参考 `src/clis/tae/add-expense.ts` 风格):
```typescript
import { cli, Strategy } from '../../registry.js';
cli({
site: 'tae',
name: 'get-approval',
description: '查看报销单审批流程和操作记录',
domain: 'tae.alibaba-inc.com',
strategy: Strategy.COOKIE,
browser: true,
args: [
{ name: 'proc_ins_id', type: 'string', required: true, positional: true, help: '流程实例 IDprocInsId' },
],
columns: ['step', 'operator', 'action', 'time'],
func: async (page, kwargs) => {
await page.goto('https://tae.alibaba-inc.com/expense/pc.html?_authType=SAML');
await page.wait(2);
const result = await page.evaluate(`(async () => {
const res = await fetch('/approval/getList.json?taskId=&procInsId=${kwargs.proc_ins_id}', {
credentials: 'include'
});
const data = await res.json();
return data?.content?.operatorRecords || [];
})()`);
return (result as any[]).map((r, i) => ({
step: i + 1,
operator: r.operatorName || r.userId,
action: r.operationType,
time: r.operateTime,
}));
},
});
```
**转换要点**
1. URL 中的动态 ID`procInsId``taskId` 等)提取为 `args`
2. `captured.json` 里的真实 body 结构用于确定正确的数据路径(如 `content.operatorRecords`
3. tae 系统统一用 `{ success, content, errorCode, errorMsg }` 外层包裹,取数据要走 `content.*`
4. 认证方式:cookie`credentials: 'include'`),不需要额外 header
5. 文件放入 `src/clis/<site>/`,无需手动注册,`npm run build` 后自动发现
### 故障排查
| 现象 | 原因 | 解法 |
|------|------|------|
| 捕获 0 条请求 | 拦截器注入失败,或页面无 JSON API | 检查 daemon 是否运行:`curl localhost:19825/status` |
| 捕获量少(1~3 条) | 页面是只读详情页,首屏数据已在注入前发出 | 手动操作触发更多请求(搜索/翻页),或换用列表页 |
| 候选 YAML 为 0 | 捕获到的 JSON 都没有 array 结构 | 直接看 `captured.json` 手写 TS CLI |
| 新开的 tab 没有被拦截 | 轮询间隔内 tab 已关闭 | 缩短 `--poll 500` |
| 二次运行 record 时数据不连续 | 正常,每次 `record` 启动都是新的 automation window | 无需处理 |
## Creating Adapters
> [!TIP]
@@ -188,7 +715,7 @@ opencli bilibili hot -v # Show each pipeline step and data flow
> [!IMPORTANT]
> **完整模式 — 在写任何代码之前,先阅读 [CLI-EXPLORER.md](./CLI-EXPLORER.md)。**
> 它包含:① AI Agent 浏览器探索工作流(必须用 Playwright MCP 抓包验证 API② 认证策略决策树 ③ 平台 SDK(如 Bilibili 的 `apiGet`/`fetchJson`)④ YAML vs TS 选择指南 ⑤ `tap` 步骤调试方法 ⑥ 级联请求模板 ⑦ 常见陷阱表。
> 它包含:① AI Agent 浏览器探索工作流 ② 认证策略决策树 ③ 平台 SDK(如 Bilibili 的 `apiGet`/`fetchJson`)④ YAML vs TS 选择指南 ⑤ `tap` 步骤调试方法 ⑥ 级联请求模板 ⑦ 常见陷阱表。
> **下方仅为简化模板参考,直接使用极易踩坑。**
### YAML Pipeline (declarative, recommended)
@@ -258,7 +785,7 @@ cli({
site: 'mysite',
name: 'search',
strategy: Strategy.INTERCEPT, // Or COOKIE
args: [{ name: 'keyword', required: true }],
args: [{ name: 'query', required: true, positional: true }],
columns: ['rank', 'title', 'url'],
func: async (page, kwargs) => {
await page.goto('https://www.mysite.com/search');
@@ -309,7 +836,7 @@ cli({
```yaml
# Arguments with defaults
${{ args.keyword }}
${{ args.query }}
${{ args.limit | default(20) }}
# Current item (in map/filter)
@@ -335,16 +862,18 @@ ${{ index + 1 }}
| Variable | Default | Description |
|----------|---------|-------------|
| `OPENCLI_DAEMON_PORT` | 19825 | Daemon listen port |
| `OPENCLI_BROWSER_CONNECT_TIMEOUT` | 30 | Browser connection timeout (sec) |
| `OPENCLI_BROWSER_COMMAND_TIMEOUT` | 45 | Command execution timeout (sec) |
| `OPENCLI_BROWSER_EXPLORE_TIMEOUT` | 120 | Explore timeout (sec) |
| `PLAYWRIGHT_MCP_EXTENSION_TOKEN` | — | Auto-approve extension connection |
| `OPENCLI_VERBOSE` | — | Show daemon/extension logs |
## Troubleshooting
| Issue | Solution |
|-------|----------|
| `npx not found` | Install Node.js: `brew install node` |
| `Timed out connecting to browser` | 1) Chrome must be open 2) Install MCP Bridge extension and configure token |
| `Extension not connected` | 1) Chrome must be open 2) Install opencli Browser Bridge extension |
| `Target page context` error | Add `navigate:` step before `evaluate:` in YAML |
| Empty table data | Check if evaluate returns JSON string (MCP parsing) or data path is wrong |
| Empty table data | Check if evaluate returns correct data path |
| Daemon issues | `curl localhost:19825/status` to check, `curl localhost:19825/logs` for extension logs |
+252
View File
@@ -0,0 +1,252 @@
# Testing Guide
> 面向开发者和 AI Agent 的测试参考手册。
## 目录
- [测试架构](#测试架构)
- [当前覆盖范围](#当前覆盖范围)
- [本地运行测试](#本地运行测试)
- [如何添加新测试](#如何添加新测试)
- [CI/CD 流水线](#cicd-流水线)
- [浏览器模式](#浏览器模式)
- [站点兼容性](#站点兼容性)
---
## 测试架构
测试分为三层,全部使用 **vitest** 运行:
```text
tests/
├── e2e/ # E2E 集成测试(子进程运行真实 CLI)
│ ├── helpers.ts # runCli() / parseJsonOutput() 共享工具
│ ├── public-commands.test.ts # 公开 API 命令
│ ├── browser-public.test.ts # 浏览器命令(公开数据)
│ ├── browser-auth.test.ts # 需登录命令(graceful failure
│ ├── management.test.ts # 管理命令(list / validate / verify / help
│ └── output-formats.test.ts # 输出格式校验
├── smoke/
│ └── api-health.test.ts # 外部 API、adapter 定义、命令注册健康检查
src/
└── **/*.test.ts # 单元测试(当前 32 个文件)
```
| 层 | 位置 | 当前文件数 | 运行方式 | 用途 |
|---|---|---:|---|---|
| 单元测试 | `src/**/*.test.ts` | 32 | `npx vitest run src/` | 内部模块、pipeline、adapter 工具函数 |
| E2E 测试 | `tests/e2e/*.test.ts` | 5 | `npx vitest run tests/e2e/` | 真实 CLI 命令执行 |
| 烟雾测试 | `tests/smoke/*.test.ts` | 1 | `npx vitest run tests/smoke/` | 外部 API 与注册完整性 |
---
## 当前覆盖范围
### 单元测试(32 个文件)
| 领域 | 文件 |
|---|---|
| 核心运行时与输出 | `src/browser.test.ts`, `src/browser/dom-snapshot.test.ts`, `src/build-manifest.test.ts`, `src/capabilityRouting.test.ts`, `src/doctor.test.ts`, `src/engine.test.ts`, `src/interceptor.test.ts`, `src/output.test.ts`, `src/plugin.test.ts`, `src/registry.test.ts`, `src/snapshotFormatter.test.ts` |
| pipeline 与下载 | `src/download/index.test.ts`, `src/pipeline/executor.test.ts`, `src/pipeline/template.test.ts`, `src/pipeline/transform.test.ts` |
| 站点 / adapter 逻辑 | `src/clis/apple-podcasts/commands.test.ts`, `src/clis/apple-podcasts/utils.test.ts`, `src/clis/bloomberg/utils.test.ts`, `src/clis/chaoxing/utils.test.ts`, `src/clis/coupang/utils.test.ts`, `src/clis/google/utils.test.ts`, `src/clis/grok/ask.test.ts`, `src/clis/twitter/timeline.test.ts`, `src/clis/weread/utils.test.ts`, `src/clis/xiaohongshu/creator-note-detail.test.ts`, `src/clis/xiaohongshu/creator-notes-summary.test.ts`, `src/clis/xiaohongshu/creator-notes.test.ts`, `src/clis/xiaohongshu/search.test.ts`, `src/clis/xiaohongshu/user-helpers.test.ts`, `src/clis/xiaoyuzhou/utils.test.ts`, `src/clis/youtube/transcript-group.test.ts`, `src/clis/zhihu/download.test.ts` |
这些测试覆盖的重点包括:
- Browser Bridge、DOM snapshot、interceptor、capability routing
- manifest 生成、命令发现、插件安装与注册表
- 输出格式渲染与 snapshot formatting
- pipeline 模板求值、执行器与变换步骤
- 各站点 adapter 的数据归一化、参数处理与容错逻辑
### E2E 测试(5 个文件)
| 文件 | 当前覆盖范围 |
|---|---|
| `tests/e2e/public-commands.test.ts` | `bloomberg``apple-podcasts``hackernews``v2ex``xiaoyuzhou``google suggest` 等公开命令 |
| `tests/e2e/browser-public.test.ts` | `bbc``bloomberg``bilibili``weibo``zhihu``reddit``twitter``xueqiu``reuters``youtube``smzdm``boss``ctrip``coupang``xiaohongshu``google``yahoo-finance``v2ex daily` |
| `tests/e2e/browser-auth.test.ts` | `bilibili``twitter``v2ex``xueqiu``linux-do``xiaohongshu` 的需登录命令 graceful failure |
| `tests/e2e/management.test.ts` | `list``validate``verify``--version``--help`、unknown command |
| `tests/e2e/output-formats.test.ts` | `json` / `yaml` / `csv` / `md` 输出格式校验 |
| `tests/e2e/plugin-management.test.ts` | `plugin install` / `list` / `update` / `uninstall` 全生命周期 |
### 烟雾测试(1 个文件)
| 文件 | 当前覆盖范围 |
|---|---|
| `tests/smoke/api-health.test.ts` | `hackernews``v2ex` 公开 API 可用性,`validate` 全量 adapter 校验,以及命令注册表基础完整性 |
### 快速核对命令
需要刷新测试清单时,直接以仓库文件为准:
```bash
find src -name '*.test.ts' | sort
find tests/e2e -name '*.test.ts' | sort
find tests/smoke -name '*.test.ts' | sort
```
---
## 本地运行测试
### 前置条件
```bash
npm ci # 安装依赖
npm run build # 编译(E2E / smoke 测试需要 dist/main.js
```
### 运行命令
```bash
# 全部单元测试
npx vitest run src/
# 全部 E2E 测试(会真实调用外部 API / 浏览器)
npx vitest run tests/e2e/
# 全部 smoke 测试
npx vitest run tests/smoke/
# 单个测试文件
npx vitest run src/clis/apple-podcasts/commands.test.ts
npx vitest run tests/e2e/management.test.ts
# 全部测试
npx vitest run
# watch 模式(开发时推荐)
npx vitest src/
```
### 浏览器命令本地测试须知
- opencli 通过 Browser Bridge 扩展连接已运行的 Chrome 浏览器
- E2E 测试通过 `tests/e2e/helpers.ts` 里的 `runCli()` 调用已构建的 `dist/main.js`
- `browser-public.test.ts` 使用 `tryBrowserCommand()`,站点反爬或地域限制导致空数据时会 warn + pass
- `browser-auth.test.ts` 验证 **graceful failure**,重点是不 crash、不 hang、错误信息可控
- 如需测试完整登录态,保持 Chrome 登录态并安装 Browser Bridge 扩展,再手动运行对应测试
---
## 如何添加新测试
### 新增 YAML Adapter(如 `src/clis/producthunt/trending.yaml`
1. `opencli validate` 的 E2E / smoke 测试会覆盖 adapter 结构校验
2. 根据 adapter 类型,在对应测试文件补一个 `it()` block
```typescript
// 如果 browser: false(公开 API)→ tests/e2e/public-commands.test.ts
it('producthunt trending returns data', async () => {
const { stdout, code } = await runCli(['producthunt', 'trending', '--limit', '3', '-f', 'json']);
expect(code).toBe(0);
const data = parseJsonOutput(stdout);
expect(Array.isArray(data)).toBe(true);
expect(data.length).toBeGreaterThanOrEqual(1);
expect(data[0]).toHaveProperty('title');
}, 30_000);
```
```typescript
// 如果 browser: true 但可公开访问 → tests/e2e/browser-public.test.ts
it('producthunt trending returns data', async () => {
const data = await tryBrowserCommand(['producthunt', 'trending', '--limit', '3', '-f', 'json']);
expectDataOrSkip(data, 'producthunt trending');
}, 60_000);
```
```typescript
// 如果 browser: true 且需登录 → tests/e2e/browser-auth.test.ts
it('producthunt me fails gracefully without login', async () => {
await expectGracefulAuthFailure(['producthunt', 'me', '-f', 'json'], 'producthunt me');
}, 60_000);
```
### 新增管理命令(如 `opencli export`
`tests/e2e/management.test.ts` 添加测试;如果新命令会影响输出格式,也同步补 `tests/e2e/output-formats.test.ts`
### 新增内部模块
在对应源码旁创建 `*.test.ts`,优先和被测模块放在同一目录下,便于发现与维护。
### 决策流程图
```text
新增功能 → 是内部模块? → 是 → src/ 下加 *.test.ts
↓ 否
是 CLI 命令? → browser: false? → tests/e2e/public-commands.test.ts
↓ true
公开数据? → tests/e2e/browser-public.test.ts
↓ 需登录
tests/e2e/browser-auth.test.ts
```
---
## CI/CD 流水线
### `ci.yml`
| Job | 触发条件 | 内容 |
|---|---|---|
| `build` | push/PR 到 `main`,`dev` | `tsc --noEmit` + `npm run build` |
| `unit-test` | push/PR 到 `main`,`dev` | Node `20``22` 双版本运行 `src/` 单元测试,按 `2` shard 并行 |
| `smoke-test` | `schedule``workflow_dispatch` | 安装真实 Chrome`xvfb-run` 执行 `tests/smoke/` |
### `e2e-headed.yml`
| Job | 触发条件 | 内容 |
|---|---|---|
| `e2e-headed` | push/PR 到 `main`,`dev`,或手动触发 | 安装真实 Chrome,`xvfb-run` 执行 `tests/e2e/` |
E2E 与 smoke 都使用 `./.github/actions/setup-chrome` 准备真实 Chrome,并通过 `OPENCLI_BROWSER_EXECUTABLE_PATH` 注入浏览器路径。
### Sharding
单元测试使用 vitest 内置 shard,并在 Node `20` / `22` 两个版本上运行:
```yaml
strategy:
matrix:
node-version: ['20', '22']
shard: [1, 2]
steps:
- run: npx vitest run src/ --reporter=verbose --shard=${{ matrix.shard }}/2
```
---
## 浏览器模式
opencli 通过 Browser Bridge 扩展连接浏览器:
| 条件 | 模式 | 使用场景 |
|---|---|---|
| 扩展已安装 / 已连接 | Extension 模式 | 本地用户,连接已登录的 Chrome |
| 无扩展 token | CLI 自行拉起浏览器 | CI、无登录态或纯自动化场景 |
CI 中使用 `OPENCLI_BROWSER_EXECUTABLE_PATH` 指定真实 Chrome 路径:
```yaml
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
```
---
## 站点兼容性
GitHub Actions 的美国 runner 上,部分站点会因为地域限制、登录要求或反爬而返回空数据。当前 E2E 对这些场景采用 warn + pass 策略,避免偶发站点限制把整条 CI 打红。
| 站点 | CI 表现 | 常见原因 |
|---|---|---|
| `hackernews``bbc``v2ex``bloomberg` | 通常返回数据 | 公开接口或公开页面 |
| `yahoo-finance``google` | 通常返回数据 | 页面公开,但仍可能受限流影响 |
| `bilibili``zhihu``weibo``xiaohongshu``xueqiu` | 容易空数据 | 地域限制、反爬、登录要求 |
| `reddit``twitter``youtube` | 容易空数据 | 登录态、cookie、机器人检测 |
| `smzdm``boss``ctrip``coupang``linux-do` | 结果波动较大 | 地域限制、风控或页面结构变动 |
> 如果需要更稳定的浏览器 E2E 结果,优先使用具备目标站点网络可达性的 self-hosted runner。
+82
View File
@@ -0,0 +1,82 @@
[CmdletBinding()]
param(
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$OpenCliArgs
)
$ErrorActionPreference = 'Stop'
$chatwiseExe = 'C:\Program Files\ChatWise\ChatWise.exe'
if (-not (Test-Path $chatwiseExe)) {
throw "ChatWise executable not found at $chatwiseExe"
}
$opencli = Get-Command opencli -ErrorAction SilentlyContinue
if (-not $opencli) {
throw 'opencli was not found in PATH'
}
function Clear-LocalProxyEnv {
$vars = 'http_proxy','https_proxy','HTTP_PROXY','HTTPS_PROXY'
foreach ($name in $vars) {
Set-Item -Path "Env:$name" -Value ''
}
$noProxy = '127.0.0.1,localhost'
Set-Item -Path 'Env:NO_PROXY' -Value $noProxy
Set-Item -Path 'Env:no_proxy' -Value $noProxy
}
function Stop-ChatWiseTree {
$candidates = Get-CimInstance Win32_Process |
Where-Object { $_.Name -match '^ChatWise\.exe$|^chatwise\.exe$' }
foreach ($proc in $candidates) {
try {
Stop-Process -Id $proc.ProcessId -Force -ErrorAction Stop
} catch {}
}
Start-Sleep -Seconds 2
}
function Wait-ChatWiseDebugPort {
param(
[int]$Port = 9228,
[int]$TimeoutSeconds = 20
)
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
while ((Get-Date) -lt $deadline) {
try {
$resp = Invoke-WebRequest -UseBasicParsing -TimeoutSec 2 -Uri "http://127.0.0.1:$Port/json/version"
if ($resp.StatusCode -ge 200 -and $resp.StatusCode -lt 300) {
return
}
} catch {}
Start-Sleep -Milliseconds 500
}
throw "ChatWise debugging endpoint did not come up on 127.0.0.1:$Port"
}
Clear-LocalProxyEnv
Stop-ChatWiseTree
$proc = Start-Process -FilePath $chatwiseExe -ArgumentList '--remote-debugging-port=9228' -PassThru
Start-Sleep -Seconds 4
if ($proc.HasExited) {
throw "ChatWise exited early with code $($proc.ExitCode)"
}
Wait-ChatWiseDebugPort
$env:OPENCLI_CDP_ENDPOINT = 'http://127.0.0.1:9228'
if (-not $OpenCliArgs -or $OpenCliArgs.Count -eq 0) {
& $opencli.Source 'chatwise' 'status'
exit $LASTEXITCODE
}
& $opencli.Source @OpenCliArgs
exit $LASTEXITCODE
+228
View File
@@ -0,0 +1,228 @@
import { defineConfig } from 'vitepress'
export default defineConfig({
base: '/docs/',
title: 'OpenCLI',
description: 'Make any website or Electron App your CLI — AI-powered, account-safe, self-healing.',
head: [
['meta', { property: 'og:title', content: 'OpenCLI Documentation' }],
['meta', { property: 'og:description', content: 'Make any website or Electron App your CLI.' }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
],
locales: {
root: {
label: 'English',
lang: 'en',
themeConfig: {
nav: [
{ text: 'Guide', link: '/guide/getting-started' },
{ text: 'Adapters', link: '/adapters/' },
{ text: 'Developer', link: '/developer/contributing' },
{ text: 'Advanced', link: '/advanced/cdp' },
],
sidebar: {
'/guide/': [
{
text: 'Guide',
items: [
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Installation', link: '/guide/installation' },
{ text: 'Comparison', link: '/comparison' },
{ text: 'Browser Bridge', link: '/guide/browser-bridge' },
{ text: 'Troubleshooting', link: '/guide/troubleshooting' },
{ text: 'Add an Electron App CLI', link: '/guide/electron-app-cli' },
{ text: 'Plugins', link: '/guide/plugins' },
],
},
],
'/adapters/': [
{
text: 'Adapters Overview',
items: [
{ text: 'All Adapters', link: '/adapters/' },
],
},
{
text: 'Browser Adapters',
collapsed: false,
items: [
{ text: 'Twitter / X', link: '/adapters/browser/twitter' },
{ text: 'Reddit', link: '/adapters/browser/reddit' },
{ text: 'Bilibili', link: '/adapters/browser/bilibili' },
{ text: 'Zhihu', link: '/adapters/browser/zhihu' },
{ text: 'Xiaohongshu', link: '/adapters/browser/xiaohongshu' },
{ text: 'Weibo', link: '/adapters/browser/weibo' },
{ text: 'YouTube', link: '/adapters/browser/youtube' },
{ text: 'Xueqiu', link: '/adapters/browser/xueqiu' },
{ text: 'V2EX', link: '/adapters/browser/v2ex' },
{ text: 'Bloomberg', link: '/adapters/browser/bloomberg' },
{ text: 'LinkedIn', link: '/adapters/browser/linkedin' },
{ text: 'Coupang', link: '/adapters/browser/coupang' },
{ text: 'BOSS Zhipin', link: '/adapters/browser/boss' },
{ text: 'Ctrip', link: '/adapters/browser/ctrip' },
{ text: 'Reuters', link: '/adapters/browser/reuters' },
{ text: 'SMZDM', link: '/adapters/browser/smzdm' },
{ text: 'Jike', link: '/adapters/browser/jike' },
{ text: 'Jimeng', link: '/adapters/browser/jimeng' },
{ text: 'Yollomi', link: '/adapters/browser/yollomi' },
{ text: 'LINUX DO', link: '/adapters/browser/linux-do' },
{ text: 'Chaoxing', link: '/adapters/browser/chaoxing' },
{ text: 'Grok', link: '/adapters/browser/grok' },
{ text: 'WeRead', link: '/adapters/browser/weread' },
{ text: 'Douban', link: '/adapters/browser/douban' },
{ text: 'Sina Blog', link: '/adapters/browser/sinablog' },
{ text: 'Substack', link: '/adapters/browser/substack' },
{ text: 'Pixiv', link: '/adapters/browser/pixiv' },
{ text: 'Douban', link: '/adapters/browser/douban' },
{ text: 'Doubao', link: '/adapters/browser/doubao' },
{ text: 'Facebook', link: '/adapters/browser/facebook' },
{ text: 'Google', link: '/adapters/browser/google' },
{ text: 'IMDb', link: '/adapters/browser/imdb' },
{ text: 'Instagram', link: '/adapters/browser/instagram' },
{ text: 'JD.com', link: '/adapters/browser/jd' },
{ text: 'Medium', link: '/adapters/browser/medium' },
{ text: 'TikTok', link: '/adapters/browser/tiktok' },
{ text: 'Web (Generic)', link: '/adapters/browser/web' },
{ text: 'Weixin', link: '/adapters/browser/weixin' },
],
},
{
text: 'Public API Adapters',
collapsed: false,
items: [
{ text: 'HackerNews', link: '/adapters/browser/hackernews' },
{ text: 'Dev.to', link: '/adapters/browser/devto' },
{ text: 'Dictionary', link: '/adapters/browser/dictionary' },
{ text: 'BBC', link: '/adapters/browser/bbc' },
{ text: 'Apple Podcasts', link: '/adapters/browser/apple-podcasts' },
{ text: 'Xiaoyuzhou', link: '/adapters/browser/xiaoyuzhou' },
{ text: 'Yahoo Finance', link: '/adapters/browser/yahoo-finance' },
{ text: 'arXiv', link: '/adapters/browser/arxiv' },
{ text: 'paperreview.ai', link: '/adapters/browser/paperreview' },
{ text: 'Barchart', link: '/adapters/browser/barchart' },
{ text: 'Hugging Face', link: '/adapters/browser/hf' },
{ text: 'Sina Finance', link: '/adapters/browser/sinafinance' },
{ text: 'Stack Overflow', link: '/adapters/browser/stackoverflow' },
{ text: 'Wikipedia', link: '/adapters/browser/wikipedia' },
{ text: 'Lobsters', link: '/adapters/browser/lobsters' },
{ text: 'Steam', link: '/adapters/browser/steam' },
],
},
{
text: 'Desktop Adapters',
collapsed: false,
items: [
{ text: 'Cursor', link: '/adapters/desktop/cursor' },
{ text: 'Codex', link: '/adapters/desktop/codex' },
{ text: 'Antigravity', link: '/adapters/desktop/antigravity' },
{ text: 'ChatGPT', link: '/adapters/desktop/chatgpt' },
{ text: 'ChatWise', link: '/adapters/desktop/chatwise' },
{ text: 'Notion', link: '/adapters/desktop/notion' },
{ text: 'Discord', link: '/adapters/desktop/discord' },
{ text: 'Doubao App', link: '/adapters/desktop/doubao-app' },
],
},
],
'/developer/': [
{
text: 'Developer Guide',
items: [
{ text: 'Contributing', link: '/developer/contributing' },
{ text: 'Testing', link: '/developer/testing' },
{ text: 'Architecture', link: '/developer/architecture' },
{ text: 'YAML Adapter Guide', link: '/developer/yaml-adapter' },
{ text: 'TypeScript Adapter Guide', link: '/developer/ts-adapter' },
{ text: 'AI Workflow', link: '/developer/ai-workflow' },
],
},
],
'/advanced/': [
{
text: 'Advanced',
items: [
{ text: 'Chrome DevTools Protocol', link: '/advanced/cdp' },
{ text: 'Electron Apps', link: '/advanced/electron' },
{ text: 'Remote Chrome', link: '/advanced/remote-chrome' },
{ text: 'Download Support', link: '/advanced/download' },
],
},
],
},
},
},
zh: {
label: '中文',
lang: 'zh-CN',
link: '/zh/',
themeConfig: {
nav: [
{ text: '指南', link: '/zh/guide/getting-started' },
{ text: '适配器', link: '/zh/adapters/' },
{ text: '开发者', link: '/zh/developer/contributing' },
{ text: '进阶', link: '/zh/advanced/cdp' },
],
sidebar: {
'/zh/guide/': [
{
text: '指南',
items: [
{ text: '快速开始', link: '/zh/guide/getting-started' },
{ text: '安装', link: '/zh/guide/installation' },
{ text: 'Browser Bridge', link: '/zh/guide/browser-bridge' },
{ text: '给新 Electron 应用生成 CLI', link: '/zh/guide/electron-app-cli' },
{ text: '插件', link: '/zh/guide/plugins' },
],
},
],
'/zh/adapters/': [
{
text: '适配器概览',
items: [
{ text: '所有适配器', link: '/zh/adapters/' },
],
},
],
'/zh/developer/': [
{
text: '开发者指南',
items: [
{ text: '贡献指南', link: '/zh/developer/contributing' },
],
},
],
'/zh/advanced/': [
{
text: '进阶',
items: [
{ text: 'Chrome DevTools Protocol', link: '/zh/advanced/cdp' },
],
},
],
},
},
},
},
themeConfig: {
search: {
provider: 'local',
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/jackwener/opencli' },
{ icon: 'npm', link: 'https://www.npmjs.com/package/@jackwener/opencli' },
],
editLink: {
pattern: 'https://github.com/jackwener/opencli/edit/main/docs/:path',
text: 'Edit this page on GitHub',
},
footer: {
message: 'Released under the Apache-2.0 License.',
copyright: 'Copyright © 2024-present jackwener',
},
},
})
+47
View File
@@ -0,0 +1,47 @@
# 36kr (36氪)
**Mode**: 🌐 Public / 🔐 Browser · **Domain**: `36kr.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli 36kr hot` | 36氪热榜 — trending articles |
| `opencli 36kr news` | Latest tech/startup news from 36kr |
| `opencli 36kr search <query>` | Search 36kr articles |
| `opencli 36kr article <id-or-url>` | Read full article content |
## Usage Examples
```bash
# Trending articles
opencli 36kr hot --limit 10
# Hot by type
opencli 36kr hot --type renqi --limit 10
opencli 36kr hot --type zonghe --limit 10
# Latest news
opencli 36kr news --limit 20
# Search articles
opencli 36kr search "AI" --limit 10
opencli 36kr search "OpenAI" --limit 5
# Read full article (by ID or URL)
opencli 36kr article 3000000123456
opencli 36kr article https://36kr.com/p/3000000123456
# JSON output
opencli 36kr hot -f json
```
## Notes
- `news` uses the public RSS feed and works without Browser Bridge.
- `hot`, `search`, and `article` use Browser Bridge and are best run with Chrome open.
- `hot --type` accepts `catalog`, `renqi`, `zonghe`, and `shoucang`.
## Prerequisites
- No browser required — uses public API
+28
View File
@@ -0,0 +1,28 @@
# Apple Podcasts
**Mode**: 🌐 Public · **Domain**: `podcasts.apple.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli apple-podcasts search` | |
| `opencli apple-podcasts episodes` | |
| `opencli apple-podcasts top` | |
## Usage Examples
```bash
# Quick start
opencli apple-podcasts search --limit 5
# JSON output
opencli apple-podcasts search -f json
# Verbose mode
opencli apple-podcasts search -v
```
## Prerequisites
- No browser required — uses public API
+27
View File
@@ -0,0 +1,27 @@
# arXiv
**Mode**: 🌐 Public · **Domain**: `arxiv.org`
## Commands
| Command | Description |
|---------|-------------|
| `opencli arxiv search` | Search arXiv papers |
| `opencli arxiv paper` | Get arXiv paper details by ID |
## Usage Examples
```bash
# Search for papers
opencli arxiv search "transformer attention" --limit 10
# Get paper details by arXiv ID
opencli arxiv paper 2301.00001
# JSON output
opencli arxiv search "LLM" -f json
```
## Prerequisites
- No browser required — uses public arXiv API
+33
View File
@@ -0,0 +1,33 @@
# Barchart
**Mode**: 🔐 Browser · **Domain**: `barchart.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli barchart quote` | Stock quote with price, volume, and key metrics |
| `opencli barchart options` | Options chain with greeks, IV, volume, and open interest |
| `opencli barchart greeks` | Options greeks overview (IV, delta, gamma, theta, vega) |
| `opencli barchart flow` | Unusual options activity / options flow |
## Usage Examples
```bash
# Get stock quote
opencli barchart quote AAPL
# View options chain
opencli barchart options TSLA
# Options greeks overview
opencli barchart greeks NVDA
# Unusual options flow
opencli barchart flow --limit 20 -f json
```
## Prerequisites
- Chrome running and able to open `barchart.com`
- [Browser Bridge extension](/guide/browser-bridge) installed
+26
View File
@@ -0,0 +1,26 @@
# BBC News
**Mode**: 🌐 Public · **Domain**: `bbc.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli bbc news` | |
## Usage Examples
```bash
# Quick start
opencli bbc news --limit 5
# JSON output
opencli bbc news -f json
# Verbose mode
opencli bbc news -v
```
## Prerequisites
- No browser required — uses public API
+47
View File
@@ -0,0 +1,47 @@
# Bilibili
**Mode**: 🔐 Browser · **Domain**: `bilibili.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli bilibili hot` | |
| `opencli bilibili search` | |
| `opencli bilibili me` | |
| `opencli bilibili favorite` | |
| `opencli bilibili history` | |
| `opencli bilibili feed` | |
| `opencli bilibili subtitle` | |
| `opencli bilibili dynamic` | |
| `opencli bilibili ranking` | |
| `opencli bilibili following` | |
| `opencli bilibili user-videos` | |
| `opencli bilibili download` | |
## Usage Examples
```bash
# Quick start
opencli bilibili hot --limit 5
# Search videos
opencli bilibili search 黑神话 --limit 10
# Read one creator's videos
opencli bilibili user-videos 2 --limit 10
# Fetch subtitles
opencli bilibili subtitle BV1xx411c7mD --lang zh-CN
# JSON output
opencli bilibili hot -f json
# Verbose mode
opencli bilibili hot -v
```
## Prerequisites
- Chrome running and **logged into** bilibili.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+70
View File
@@ -0,0 +1,70 @@
# Bloomberg
**Mode**: 🌐 / 🔐 Mixed · **Domains**: `feeds.bloomberg.com`, `www.bloomberg.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli bloomberg main` | Bloomberg homepage top stories from RSS |
| `opencli bloomberg markets` | Bloomberg Markets top stories from RSS |
| `opencli bloomberg economics` | Bloomberg Economics top stories from RSS |
| `opencli bloomberg industries` | Bloomberg Industries top stories from RSS |
| `opencli bloomberg tech` | Bloomberg Tech top stories from RSS |
| `opencli bloomberg politics` | Bloomberg Politics top stories from RSS |
| `opencli bloomberg businessweek` | Bloomberg Businessweek top stories from RSS |
| `opencli bloomberg opinions` | Bloomberg Opinion top stories from RSS |
| `opencli bloomberg feeds` | List the RSS feed aliases used by the adapter |
| `opencli bloomberg news <link>` | Read a standard Bloomberg story/article page and return title, summary, media links, and article text |
## What works today
- RSS-backed listing commands work without a browser:
- `main`
- `markets`
- `economics`
- `industries`
- `tech`
- `politics`
- `businessweek`
- `opinions`
- `feeds`
- `bloomberg news` works on standard Bloomberg story/article pages that expose `#__NEXT_DATA__` and are accessible to your current Chrome session.
## Current limitations
- Audio pages and some other non-standard Bloomberg URLs may fail.
- Some Bloomberg pages can return bot-protection or access-gated responses instead of article data.
- This adapter is for data retrieval/extraction only. It does **not** bypass Bloomberg paywall, login, entitlement, or other access checks.
## Usage Examples
```bash
# List supported RSS feed aliases
opencli bloomberg feeds
# Fetch Bloomberg homepage headlines
opencli bloomberg main --limit 5
# Fetch a section feed as JSON
opencli bloomberg tech --limit 3 -f json
# Read a standard article page
opencli bloomberg news https://www.bloomberg.com/news/articles/2026-03-19/example -f json
# Relative article paths also work
opencli bloomberg news /news/articles/2026-03-19/example
```
## Prerequisites
- RSS commands do not require Chrome.
- `bloomberg news` requires:
- Chrome running
- a Chrome session that can already access the target Bloomberg article page
- the [Browser Bridge extension](/guide/browser-bridge)
## Notes
- RSS commands support `--limit` with a maximum of 20 items.
- If `bloomberg news` fails on a page from RSS, try a different standard story/article link first; not every Bloomberg URL in feeds is a normal article page.
+53
View File
@@ -0,0 +1,53 @@
# Bluesky
**Mode**: 🌐 Public · **Domain**: `bsky.app`
## Commands
| Command | Description |
|---------|-------------|
| `opencli bluesky profile` | User profile info |
| `opencli bluesky user` | Recent posts from a user |
| `opencli bluesky trending` | Trending topics |
| `opencli bluesky search` | Search users |
| `opencli bluesky feeds` | Popular feed generators |
| `opencli bluesky followers` | User's followers |
| `opencli bluesky following` | Accounts a user follows |
| `opencli bluesky thread` | Post thread with replies |
| `opencli bluesky starter-packs` | User's starter packs |
## Usage Examples
```bash
# User profile
opencli bluesky profile --handle bsky.app
# Recent posts
opencli bluesky user --handle bsky.app --limit 10
# Trending topics
opencli bluesky trending --limit 10
# Search users
opencli bluesky search --query "AI" --limit 10
# Popular feeds
opencli bluesky feeds --limit 10
# Followers / following
opencli bluesky followers --handle bsky.app --limit 10
opencli bluesky following --handle bsky.app
# Post thread with replies
opencli bluesky thread --uri "at://did:.../app.bsky.feed.post/..."
# Starter packs
opencli bluesky starter-packs --handle bsky.app
# JSON output
opencli bluesky profile --handle bsky.app -f json
```
## Prerequisites
None — all commands use the public Bluesky AT Protocol API, no browser or login required.
+28
View File
@@ -0,0 +1,28 @@
# BOSS Zhipin
**Mode**: 🔐 Browser · **Domain**: `zhipin.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli boss search` | |
| `opencli boss detail` | |
## Usage Examples
```bash
# Quick start
opencli boss search --limit 5
# JSON output
opencli boss search -f json
# Verbose mode
opencli boss search -v
```
## Prerequisites
- Chrome running and **logged into** zhipin.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+39
View File
@@ -0,0 +1,39 @@
# 超星学习通 (Chaoxing)
**Mode**: 🔐 Browser · **Domain**: `mooc2-ans.chaoxing.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli chaoxing assignments` | 学习通作业列表 |
| `opencli chaoxing exams` | 学习通考试列表 |
## Usage Examples
```bash
# List all assignments
opencli chaoxing assignments --limit 20
# Filter exams by course name
opencli chaoxing exams --course "高等数学"
# Filter exams by status
opencli chaoxing exams --status ongoing
# JSON output
opencli chaoxing assignments -f json
```
### Options
| Option | Description |
|--------|-------------|
| `--course` | Filter by course name (fuzzy match) |
| `--status` | Filter by status: `all`, `upcoming`, `ongoing`, `finished` |
| `--limit` | Max number of results (default: 20) |
## Prerequisites
- Chrome running and **logged into** mooc2-ans.chaoxing.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+28
View File
@@ -0,0 +1,28 @@
# Coupang
**Mode**: 🔐 Browser · **Domain**: `coupang.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli coupang search` | |
| `opencli coupang add-to-cart` | |
## Usage Examples
```bash
# Quick start
opencli coupang search --limit 5
# JSON output
opencli coupang search -f json
# Verbose mode
opencli coupang search -v
```
## Prerequisites
- Chrome running and **logged into** coupang.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+27
View File
@@ -0,0 +1,27 @@
# Ctrip (携程)
**Mode**: 🔐 Browser · **Domain**: `ctrip.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli ctrip search` | |
## Usage Examples
```bash
# Quick start
opencli ctrip search --limit 5
# JSON output
opencli ctrip search -f json
# Verbose mode
opencli ctrip search -v
```
## Prerequisites
- Chrome running and **logged into** ctrip.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+35
View File
@@ -0,0 +1,35 @@
# Dev.to
**Mode**: 🌐 Public · **Domain**: `dev.to`
Fetch the latest and greatest developer articles from the DEV community without needing an API key.
## Commands
| Command | Description |
|---------|-------------|
| `opencli devto top` | Top DEV.to articles of the day |
| `opencli devto tag` | Latest articles for a specific tag |
| `opencli devto user` | Recent articles from a specific user |
## Usage Examples
```bash
# Top articles today
opencli devto top --limit 5
# Articles by tag (positional argument)
opencli devto tag javascript
opencli devto tag python --limit 20
# Articles by a specific author
opencli devto user ben
opencli devto user thepracticaldev --limit 5
# JSON output
opencli devto top -f json
```
## Prerequisites
- No browser required — uses the public DEV.to API
+27
View File
@@ -0,0 +1,27 @@
# Dictionary
**Mode**: 🌐 Public · **Domain**: `api.dictionaryapi.dev`
Search the open dictionary to quickly fetch native definitions, part of speech contexts, and phonetic pronunciations directly in your IDE terminal.
## Commands
| Command | Description |
|---------|-------------|
| `opencli dictionary search` | Fetch the exact definition of a word |
| `opencli dictionary synonyms` | Find related synonyms for a word |
| `opencli dictionary examples` | Read real-world sentence usage examples |
## Usage Examples
```bash
# Look up a complex term
opencli dictionary search serendipity
# Discover phonetics
opencli dictionary search ephemeral
```
## Prerequisites
- No browser required — utilizes the fast, open JSON definitions API.
+62
View File
@@ -0,0 +1,62 @@
# 豆瓣 (Douban)
**Mode**: 🔐 Browser (Cookie) · **Domain**: `douban.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli douban search` | 搜索豆瓣电影、图书或音乐 |
| `opencli douban top250` | 豆瓣电影 Top 250 |
| `opencli douban subject` | 条目详情 |
| `opencli douban photos` | 获取电影海报/剧照图片列表 |
| `opencli douban download` | 下载电影海报/剧照图片 |
| `opencli douban marks` | 我的标记 |
| `opencli douban reviews` | 我的短评 |
| `opencli douban movie-hot` | 豆瓣电影热门榜单 |
| `opencli douban book-hot` | 豆瓣图书热门榜单 |
## Usage Examples
```bash
# 搜索电影
opencli douban search "流浪地球"
# 搜索图书
opencli douban search --type book "三体"
# 搜索音乐
opencli douban search --type music "周杰伦"
# 电影 Top 250
opencli douban top250 --limit 10
# 条目详情
opencli douban subject 1292052
# 获取海报直链(默认 type=Rb)
opencli douban photos 30382501 --limit 20
# 下载海报到本地目录
opencli douban download 30382501 --output ./douban
# 只下载指定 photo_id 的一张图
opencli douban download 30382501 --photo-id 2913621075 --output ./douban
# 返回 JSON,便于上层界面直接渲染图片并右键取图
opencli douban photos 30382501 -f json
# 电影热门
opencli douban movie-hot --limit 10
# 图书热门
opencli douban book-hot --limit 10
# JSON output
opencli douban top250 -f json
```
## Prerequisites
- Chrome logged into `douban.com`
- Browser Bridge extension installed
+35
View File
@@ -0,0 +1,35 @@
# doubao
Browser adapter for [Doubao Chat](https://www.doubao.com/chat).
## Commands
| Command | Description |
|---------|-------------|
| `opencli doubao status` | Check whether the page is reachable and whether Doubao appears logged in |
| `opencli doubao new` | Start a new Doubao conversation |
| `opencli doubao send "..."` | Send a message to the current Doubao chat |
| `opencli doubao read` | Read the visible Doubao conversation |
| `opencli doubao ask "..."` | Send a prompt and wait for a reply |
## Prerequisites
- Chrome is running
- You are already logged into [doubao.com](https://www.doubao.com/)
- Playwright MCP Bridge / browser bridge is configured for OpenCLI
## Examples
```bash
opencli doubao status
opencli doubao new
opencli doubao send "帮我总结这段文档"
opencli doubao read
opencli doubao ask "请写一个 Python 快速排序示例" --timeout 90
```
## Notes
- The adapter targets the web chat page at `https://www.doubao.com/chat`
- `new` first tries the visible "New Chat / 新对话" button, then falls back to the new-thread route
- `ask` uses DOM polling, so very long generations may need a larger `--timeout`
+75
View File
@@ -0,0 +1,75 @@
# Douyin (抖音创作者中心)
**Mode**: 🔐 Browser · **Domain**: `creator.douyin.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli douyin profile` | 获取账号信息 |
| `opencli douyin videos` | 获取作品列表 |
| `opencli douyin drafts` | 获取草稿列表 |
| `opencli douyin draft` | 上传视频并保存为草稿 |
| `opencli douyin publish` | 定时发布视频到抖音 |
| `opencli douyin update` | 更新视频信息 |
| `opencli douyin delete` | 删除作品 |
| `opencli douyin stats` | 查询作品数据分析 |
| `opencli douyin collections` | 获取合集列表 |
| `opencli douyin activities` | 获取官方活动列表 |
| `opencli douyin location` | 搜索发布可用的地理位置 |
| `opencli douyin hashtag search` | 按关键词搜索话题 |
| `opencli douyin hashtag suggest` | 基于封面 URI 推荐话题 |
| `opencli douyin hashtag hot` | 获取热点词 |
## Usage Examples
```bash
# 账号与作品
opencli douyin profile
opencli douyin videos --limit 10
opencli douyin videos --status scheduled
opencli douyin drafts
# 发布前辅助信息
opencli douyin collections
opencli douyin activities
opencli douyin location "东京塔"
opencli douyin hashtag search "春游"
opencli douyin hashtag hot --limit 10
# 保存草稿
opencli douyin draft ./video.mp4 \
--title "春游 vlog" \
--caption "#春游 先存草稿"
# 定时发布
opencli douyin publish ./video.mp4 \
--title "春游 vlog" \
--caption "#春游 今天去看樱花" \
--schedule "2026-04-08T12:00:00+09:00"
# 也支持 Unix 秒字符串
opencli douyin publish ./video.mp4 \
--title "春游 vlog" \
--schedule 1775617200
# 更新与删除
opencli douyin update 1234567890 --caption "更新后的文案"
opencli douyin update 1234567890 --reschedule "2026-04-09T20:00:00+09:00"
opencli douyin delete 1234567890
# JSON 输出
opencli douyin profile -f json
```
## Prerequisites
- Chrome running and **logged into** `creator.douyin.com`
- The logged-in account must have access to Douyin Creator Center publishing features
- [Browser Bridge extension](/guide/browser-bridge) installed
## Notes
- `publish` requires `--schedule` to be at least 2 hours later and no more than 14 days later
- `draft` and `publish` upload the video through Douyin/ByteDance browser-authenticated APIs, so cookies in the active browser session must be valid
- `hashtag suggest` expects a valid `cover`/`cover_uri` value produced during the publish pipeline; for normal manual use, `hashtag search` and `hashtag hot` are usually more convenient
+36
View File
@@ -0,0 +1,36 @@
# Facebook
**Mode**: 🔐 Browser · **Domain**: `facebook.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli facebook profile` | Get user/page profile info |
| `opencli facebook notifications` | Get recent notifications |
| `opencli facebook feed` | Get news feed posts |
| `opencli facebook search` | Search people, pages, posts |
## Usage Examples
```bash
# View a profile
opencli facebook profile zuck
# Get notifications
opencli facebook notifications --limit 10
# News feed
opencli facebook feed --limit 5
# Search
opencli facebook search "OpenAI" --limit 5
# JSON output
opencli facebook profile zuck -f json
```
## Prerequisites
- Chrome running and **logged into** facebook.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+62
View File
@@ -0,0 +1,62 @@
# Google
**Mode**: 🌐 / 🔐 Mixed · **Domains**: `google.com`, `suggestqueries.google.com`, `news.google.com`, `trends.google.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli google search <keyword>` | Search Google and extract results from the page |
| `opencli google suggest <keyword>` | Get Google search suggestions |
| `opencli google news [keyword]` | Get Google News headlines (top stories or search) |
| `opencli google trends` | Get Google Trends daily trending searches |
## What works today
- Public API commands work without a browser:
- `suggest` — JSON API, no auth needed
- `news` — RSS feed, supports top stories and keyword search
- `trends` — RSS feed, supports different regions
- `google search` uses browser mode to extract results from google.com.
## Current limitations
- `google search` may trigger CAPTCHA in Standalone browser mode. Extension mode (with an established Chrome session) is more reliable.
- Google frequently changes its DOM structure. If `search` stops returning results, selectors may need updating.
- Snippet extraction may return empty for some results depending on Google's layout.
## Usage Examples
```bash
# Search Google
opencli google search "typescript tutorial" --limit 10
# Get search suggestions
opencli google suggest python
# Get top news headlines
opencli google news --limit 5
# Search news for a topic
opencli google news "artificial intelligence" --limit 10 --lang en --region US
# Get trending searches in Japan
opencli google trends --region JP --limit 10
# Output as JSON
opencli google search "machine learning" -f json
```
## Prerequisites
- `suggest`, `news`, `trends` do not require Chrome.
- `search` requires:
- Chrome running (or Standalone mode will auto-launch)
- For best results, use the [Browser Bridge extension](/guide/browser-bridge) with an established Google session
## Notes
- `suggest` defaults to `--lang zh-CN`; other commands default to `--lang en`.
- `news` supports `--lang` and `--region` parameters for localized results.
- `trends` traffic values are raw strings (e.g. "500K+", "1,000,000+"), not numeric.
- `search` output includes three result types: `result` (standard), `snippet` (featured answer box), and `paa` (People Also Ask).
+53
View File
@@ -0,0 +1,53 @@
# Grok
**Mode**: Default Grok adapter + optional explicit consumer web path · **Domain**: `grok.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli grok ask` | Keep the default Grok ask behavior |
| `opencli grok ask --web` | Use the explicit grok.com consumer web UI flow |
## Usage Examples
```bash
# Default / compatibility path
opencli grok ask --prompt "Explain quantum computing in simple terms"
# Explicit consumer web path
opencli grok ask --prompt "Explain quantum computing in simple terms" --web
# Best-effort fresh chat on the consumer web path
opencli grok ask --prompt "Hello" --web --new
# Set custom timeout (default: 120s)
opencli grok ask --prompt "Write a long essay" --web --timeout 180
```
### Options
| Option | Description |
|--------|-------------|
| `--prompt` | The message to send (required) |
| `--timeout` | Wait timeout in seconds (default: 120) |
| `--new` | Start a new chat before sending (default: false) |
| `--web` | Opt into the explicit grok.com consumer web flow (default: false) |
## Behavior
- `opencli grok ask` keeps the upstream/default behavior intact.
- `opencli grok ask --web` switches to the newer hardened consumer-web implementation.
- The `--web` path adds stricter composer detection, clearer blocked/session-gated hints, and waits for a stabilized assistant bubble before returning.
## Prerequisites
- The Grok adapter still depends on browser-backed access to `grok.com`
- For `--web`, Chrome should already be running with an authenticated Grok consumer session
- [Browser Bridge extension](/guide/browser-bridge) installed
## Caveats
- `--web` drives the Grok consumer web UI in the browser, not an API.
- It depends on an already-authenticated session and can fail if Grok shows login, challenge, rate-limit, or other session-gating UI.
- It may break when the Grok composer DOM, submit button behavior, or message bubble structure changes.
+42
View File
@@ -0,0 +1,42 @@
# HackerNews
**Mode**: 🌐 Public · **Domain**: `news.ycombinator.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli hackernews top` | Hacker News top stories |
| `opencli hackernews new` | Hacker News newest stories |
| `opencli hackernews best` | Hacker News best stories |
| `opencli hackernews ask` | Hacker News Ask HN posts |
| `opencli hackernews show` | Hacker News Show HN posts |
| `opencli hackernews jobs` | Hacker News job postings |
| `opencli hackernews search <query>` | Search Hacker News stories |
| `opencli hackernews user <username>` | Hacker News user profile |
## Usage Examples
```bash
# Top stories
opencli hackernews top --limit 5
# Newest stories
opencli hackernews new --limit 10
# Search stories
opencli hackernews search "machine learning" --limit 5
# User profile
opencli hackernews user pg
# JSON output
opencli hackernews top -f json
# Sort search by date
opencli hackernews search "rust" --sort date
```
## Prerequisites
- No browser required — uses public API
+42
View File
@@ -0,0 +1,42 @@
# Hugging Face
**Mode**: 🌐 Public · **Domain**: `huggingface.co`
## Commands
| Command | Description |
|---------|-------------|
| `opencli hf top` | Top upvoted Hugging Face papers |
## Usage Examples
```bash
# Today's top papers
opencli hf top --limit 10
# All papers (no limit)
opencli hf top --all
# Specific date
opencli hf top --date 2025-03-01
# Weekly/monthly top papers
opencli hf top --period weekly
opencli hf top --period monthly
# JSON output
opencli hf top -f json
```
### Options
| Option | Description |
|--------|-------------|
| `--limit` | Number of papers (default: 20) |
| `--all` | Return all papers, ignoring limit |
| `--date` | Date in `YYYY-MM-DD` format (defaults to most recent) |
| `--period` | Time period: `daily`, `weekly`, or `monthly` (default: daily) |
## Prerequisites
- No browser required — uses public Hugging Face API
+47
View File
@@ -0,0 +1,47 @@
# IMDb
**Mode**: 🌐 Public (Browser) · **Domain**: `www.imdb.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli imdb search` | Search movies, TV shows, and people |
| `opencli imdb title` | Get movie or TV show details |
| `opencli imdb top` | IMDb Top 250 Movies |
| `opencli imdb trending` | IMDb Most Popular Movies |
| `opencli imdb person` | Get actor or director info |
| `opencli imdb reviews` | Get user reviews for a title |
## Usage Examples
```bash
# Search for a movie
opencli imdb search "inception" --limit 10
# Get movie details
opencli imdb title tt1375666
# Get TV series details (also accepts full URL)
opencli imdb title "https://www.imdb.com/title/tt0903747/"
# Top 250 movies
opencli imdb top --limit 20
# Currently trending movies
opencli imdb trending --limit 10
# Actor/director info with filmography
opencli imdb person nm0634240 --limit 5
# User reviews
opencli imdb reviews tt1375666 --limit 5
# JSON output
opencli imdb top --limit 5 -f json
```
## Prerequisites
- Chrome with Browser Bridge extension installed
- No login required (all data is public)
+46
View File
@@ -0,0 +1,46 @@
# Instagram
**Mode**: 🔐 Browser · **Domain**: `instagram.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli instagram profile` | Get user profile info |
| `opencli instagram search` | Search users |
| `opencli instagram user` | Get recent posts from a user |
| `opencli instagram explore` | Discover trending posts |
| `opencli instagram followers` | List user's followers |
| `opencli instagram following` | List user's following |
| `opencli instagram saved` | Get your saved posts |
## Usage Examples
```bash
# View a user's profile
opencli instagram profile nasa
# Search users
opencli instagram search nasa --limit 5
# View a user's recent posts
opencli instagram user nasa --limit 10
# Discover trending posts
opencli instagram explore --limit 20
# List followers/following
opencli instagram followers nasa --limit 20
opencli instagram following nasa --limit 20
# Get your saved posts
opencli instagram saved --limit 10
# JSON output
opencli instagram profile nasa -f json
```
## Prerequisites
- Chrome running and **logged into** instagram.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+27
View File
@@ -0,0 +1,27 @@
# JD.com
**Mode**: 🔐 Browser · **Domain**: `item.jd.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli jd item <sku>` | Fetch product details (price, shop, specs, AVIF images) |
## Usage Examples
```bash
# Get product details by SKU
opencli jd item 100291143898
# Limit returned AVIF images
opencli jd item 100291143898 --images 5
# JSON output
opencli jd item 100291143898 -f json
```
## Prerequisites
- Chrome running and **logged into** jd.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+45
View File
@@ -0,0 +1,45 @@
# 即刻 (Jike)
**Mode**: 🔐 Browser · **Domain**: `web.okjike.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli jike feed` | 即刻首页动态流 |
| `opencli jike search` | 搜索即刻帖子 |
| `opencli jike post` | 帖子详情及评论 |
| `opencli jike topic` | 话题详情 |
| `opencli jike user` | 用户资料 |
| `opencli jike create` | 发布即刻动态 |
| `opencli jike comment` | 评论即刻帖子 |
| `opencli jike like` | 点赞即刻帖子 |
| `opencli jike repost` | 转发即刻帖子 |
| `opencli jike notifications` | 即刻通知 |
## Usage Examples
```bash
# View feed
opencli jike feed --limit 10
# Search posts
opencli jike search "AI" --limit 20
# View post details and comments
opencli jike post <post-id>
# Create a new post
opencli jike create --content "Hello Jike!"
# Like a post
opencli jike like <post-id>
# JSON output
opencli jike feed -f json
```
## Prerequisites
- Chrome running and **logged into** web.okjike.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+39
View File
@@ -0,0 +1,39 @@
# 即梦AI (Jimeng)
**Mode**: 🔐 Browser · **Domain**: `jimeng.jianying.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli jimeng generate` | 即梦AI 文生图 — 输入 prompt 生成图片 |
| `opencli jimeng history` | 查看生成历史 |
## Usage Examples
```bash
# Generate an image
opencli jimeng generate --prompt "一只在星空下的猫"
# Use a specific model
opencli jimeng generate --prompt "cyberpunk city" --model high_aes_general_v50
# Set custom wait timeout
opencli jimeng generate --prompt "sunset landscape" --wait 60
# View generation history
opencli jimeng history --limit 10
```
### Options (generate)
| Option | Description |
|--------|-------------|
| `--prompt` | Image description prompt (required) |
| `--model` | Model: `high_aes_general_v50` (5.0 Lite), `high_aes_general_v42` (4.6), `high_aes_general_v40` (4.0) |
| `--wait` | Wait seconds for generation (default: 40) |
## Prerequisites
- Chrome running and **logged into** jimeng.jianying.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+33
View File
@@ -0,0 +1,33 @@
# LinkedIn
**Mode**: 🔐 Browser · **Domain**: `linkedin.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli linkedin search` | |
| `opencli linkedin timeline` | Read posts from your LinkedIn home feed |
## Usage Examples
```bash
# Quick start
opencli linkedin search --limit 5
# Read your home timeline
opencli linkedin timeline --limit 5
# JSON output
opencli linkedin search -f json
opencli linkedin timeline -f json
# Verbose mode
opencli linkedin search -v
```
## Prerequisites
- Chrome running and **logged into** linkedin.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+206
View File
@@ -0,0 +1,206 @@
# LINUX DO
**Mode**: 🔐 Browser · **Domain**: `linux.do`
## Commands
| Command | Description |
|---------|-------------|
| `opencli linux-do feed` | Browse topics (site-wide, by tag, or by category) |
| `opencli linux-do categories` | List all categories |
| `opencli linux-do tags` | List popular tags |
| `opencli linux-do search <query>` | Search topics |
| `opencli linux-do topic <id>` | View topic posts |
| `opencli linux-do user-topics <username>` | Topics created by a user |
| `opencli linux-do user-posts <username>` | Replies posted by a user |
## feed
Browse topic listings. Defaults to latest topics when called with no arguments.
- Supports filtering by `--tag`, `--category`, or both
- `--tag` accepts tag name, slug, or ID
- `--category` accepts category name, slug, ID, or `Parent / Child` path for sub-categories
- Use `--view` to switch between latest / hot / top
### Basic
```bash
# Latest topics (default)
opencli linux-do feed
# Hot topics
opencli linux-do feed --view hot
# Top topics — default period is weekly
opencli linux-do feed --view top
opencli linux-do feed --view top --period daily
opencli linux-do feed --view top --period monthly
# Sort by views descending
opencli linux-do feed --order views
# Sort by created time ascending
opencli linux-do feed --order created --ascending
# Limit results
opencli linux-do feed --limit 10
# JSON output
opencli linux-do feed -f json
```
### Filter by tag
```bash
# By tag name, slug, or ID — all equivalent
opencli linux-do feed --tag "ChatGPT"
opencli linux-do feed --tag chatgpt
opencli linux-do feed --tag 3
# Tag + hot view
opencli linux-do feed --tag "ChatGPT" --view hot
# Tag + top view with period
opencli linux-do feed --tag "OpenAI" --view top --period monthly
```
### Filter by category
Supports both top-level and sub-categories. Sub-categories auto-resolve their parent path.
```bash
# Top-level category — name, slug, or ID
opencli linux-do feed --category "开发调优"
opencli linux-do feed --category develop
opencli linux-do feed --category 4
# Sub-category
opencli linux-do feed --category "开发调优 / Lv1"
opencli linux-do feed --category "网盘资源"
# Category + hot / top view
opencli linux-do feed --category "开发调优" --view hot
opencli linux-do feed --category "开发调优" --view top --period weekly
```
### Category + tag
Combine `--category` and `--tag` to narrow results within a category.
```bash
opencli linux-do feed --category "开发调优" --tag "ChatGPT"
opencli linux-do feed --category "网盘资源" --tag "OpenAI"
opencli linux-do feed --category 94 --tag 4 --view top --period monthly
```
### Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| `--view V` | `latest`, `hot`, `top` | `latest` |
| `--tag VALUE` | Tag name, slug, or ID | — |
| `--category VALUE` | Category name, slug, or ID | — |
| `--limit N` | Number of results | `20` |
| `--order O` | `default`, `created`, `activity`, `views`, `posts`, `category`, `likes`, `op_likes`, `posters` | `default` |
| `--ascending` | Sort ascending instead of descending | off |
| `--period P` | `all`, `daily`, `weekly`, `monthly`, `quarterly`, `yearly` (only with `--view top`) | `weekly` |
Output columns: `title`, `replies`, `created`, `likes`, `views`, `url`
## categories
List forum categories with optional sub-category expansion.
```bash
opencli linux-do categories
opencli linux-do categories --subcategories
opencli linux-do categories --limit 50
```
When `--subcategories` is enabled, sub-categories are rendered as `Parent / Child` so the `name` value can be copied directly into `opencli linux-do feed --category ...`.
Output columns: `name`, `slug`, `id`, `topics`, `description`
## tags
List tags sorted by usage count.
```bash
opencli linux-do tags
opencli linux-do tags --limit 50
```
Output columns: `rank`, `name`, `count`, `url`
## search
Search topics by keyword.
```bash
opencli linux-do search "NixOS"
opencli linux-do search "Docker" --limit 10
opencli linux-do search "Claude" -f json
```
Output columns: `rank`, `title`, `views`, `likes`, `replies`, `url`
## topic
View posts within a topic (first page).
```bash
opencli linux-do topic 1234
opencli linux-do topic 1234 --limit 50
opencli linux-do topic 1234 --main_only -f json | jq -r '.[0].content'
```
Notes:
- `--main_only` returns only the main post row and keeps the body untruncated
Output columns: `author`, `content`, `likes`, `created_at`
## user-topics
List topics created by a user.
```bash
opencli linux-do user-topics neo
opencli linux-do user-topics neo --limit 10
```
Output columns: `rank`, `title`, `replies`, `created_at`, `likes`, `views`, `url`
## user-posts
List replies posted by a user.
```bash
opencli linux-do user-posts neo
opencli linux-do user-posts neo --limit 10
```
Output columns: `index`, `topic_user`, `topic`, `reply`, `time`, `url`
## Compatibility
The legacy commands below are still available as compatibility wrappers while `feed` becomes the canonical entrypoint:
```bash
opencli linux-do latest
opencli linux-do hot --period weekly
opencli linux-do category develop 4
```
Preferred modern forms:
```bash
opencli linux-do feed --view latest
opencli linux-do feed --view top --period weekly
opencli linux-do feed --category 4
```
## Prerequisites
- Chrome running and **logged into** linux.do
- [Browser Bridge extension](/guide/browser-bridge) installed
+32
View File
@@ -0,0 +1,32 @@
# Lobsters
**Mode**: 🌐 Public · **Domain**: `lobste.rs`
## Commands
| Command | Description |
|---------|-------------|
| `opencli lobsters hot` | Hottest stories |
| `opencli lobsters newest` | Latest stories |
| `opencli lobsters active` | Most active discussions |
| `opencli lobsters tag` | Stories by tag |
## Usage Examples
```bash
# Quick start
opencli lobsters hot --limit 10
# Filter by tag
opencli lobsters tag --tag rust --limit 5
# JSON output
opencli lobsters hot -f json
# Verbose mode
opencli lobsters hot -v
```
## Prerequisites
None — all commands use the public JSON API, no browser or login required.
+32
View File
@@ -0,0 +1,32 @@
# Medium
**Mode**: 🌗 Mixed · **Domain**: `medium.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli medium feed` | Get hot Medium posts, optionally scoped to a topic |
| `opencli medium search` | Search Medium posts by keyword |
| `opencli medium user` | Get recent articles by a user |
## Usage Examples
```bash
# Get the general Medium feed
opencli medium feed --limit 10
# Search posts by keyword
opencli medium search ai
# Get articles by a user
opencli medium user @username
# Topic feed as JSON
opencli medium feed --topic programming -f json
```
## Prerequisites
- `opencli medium search` can run without a browser
- `opencli medium feed` and `opencli medium user` require Browser Bridge access to `medium.com`
+43
View File
@@ -0,0 +1,43 @@
# paperreview.ai
**Mode**: 🌐 Public · **Domain**: `paperreview.ai`
## Commands
| Command | Description |
|---------|-------------|
| `opencli paperreview submit` | Submit a PDF to paperreview.ai for review |
| `opencli paperreview review` | Fetch a review by token |
| `opencli paperreview feedback` | Send feedback on a completed review |
## Usage Examples
```bash
# Validate a local PDF without uploading it
opencli paperreview submit ./paper.pdf --email you@example.com --venue RAL --dry-run true
# Request an upload slot but stop before the actual upload
opencli paperreview submit ./paper.pdf --email you@example.com --venue RAL --prepare-only true
# Submit a paper for review
opencli paperreview submit ./paper.pdf --email you@example.com --venue RAL -f json
# Check the review status or fetch the final review
opencli paperreview review tok_123 -f json
# Submit feedback on the review quality
opencli paperreview feedback tok_123 --helpfulness 4 --critical-error no --actionable-suggestions yes
```
## Prerequisites
- No browser required — uses public paperreview.ai endpoints
- The input file must be a local `.pdf`
- paperreview.ai currently rejects files larger than `10MB`
- `submit` requires `--email`; `--venue` is optional
## Notes
- `submit` returns both the review token and the review URL when submission succeeds
- `review` returns `processing` until the paperreview.ai result is ready
- `feedback` expects `yes` / `no` values for `--critical-error` and `--actionable-suggestions`
+92
View File
@@ -0,0 +1,92 @@
# Pixiv
**Mode**: 🔐 Browser · **Domain**: `www.pixiv.net`
## Commands
| Command | Description |
|---------|-------------|
| `opencli pixiv ranking` | Daily/weekly/monthly illustration rankings |
| `opencli pixiv search <query>` | Search illustrations by keyword or tag |
| `opencli pixiv user <uid>` | View artist profile info |
| `opencli pixiv illusts <user-id>` | List illustrations by artist |
| `opencli pixiv detail <id>` | View illustration details |
| `opencli pixiv download <illust-id>` | Download original-quality images |
## Usage Examples
### Ranking
```bash
# Daily rankings (default)
opencli pixiv ranking --limit 10
# Weekly / monthly rankings
opencli pixiv ranking --mode weekly
opencli pixiv ranking --mode monthly
# R18 rankings
opencli pixiv ranking --mode daily_r18
opencli pixiv ranking --mode weekly_r18
# Other modes: rookie, original, male, female
opencli pixiv ranking --mode rookie
```
### Search
```bash
# Search by keyword or tag
opencli pixiv search "初音ミク" --limit 20
# Filter by content rating
opencli pixiv search "風景" --mode safe # Safe-for-work only
opencli pixiv search "風景" --mode r18 # R18 only
opencli pixiv search "風景" --mode all # All (default)
# Sort by popularity
opencli pixiv search "VOCALOID" --order popular_d
# All sort options: date_d (newest), date (oldest), popular_d, popular_male_d, popular_female_d
# Pagination
opencli pixiv search "オリジナル" --page 2 --limit 30
```
### User & Illustrations
```bash
# View artist profile
opencli pixiv user 11
# List artist's illustrations (newest first)
opencli pixiv illusts 11 --limit 10
# View illustration details (tags, stats, type)
opencli pixiv detail 12345678
```
### Download
```bash
# Download all images from an illustration
opencli pixiv download 12345678
# Download to a custom directory
opencli pixiv download 12345678 --output ./my-images
```
### Output Formats
```bash
# JSON output
opencli pixiv ranking -f json
# Verbose mode
opencli pixiv search "test" -v
```
## Prerequisites
- Chrome running and **logged into** pixiv.net
- [Browser Bridge extension](/guide/browser-bridge) installed
+49
View File
@@ -0,0 +1,49 @@
# Product Hunt
**Mode**: 🌐 Public / 🔐 Browser · **Domain**: `www.producthunt.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli producthunt posts` | Latest Product Hunt launches (optional category filter) |
| `opencli producthunt today` | Today's Product Hunt launches (most recent day in feed) |
| `opencli producthunt hot` | Today's top Product Hunt launches with vote counts |
| `opencli producthunt browse <category>` | Best products in a Product Hunt category |
## Usage Examples
```bash
# Today's top launches with vote counts
opencli producthunt hot --limit 10
# Latest posts (RSS feed)
opencli producthunt posts --limit 20
# Filter by category
opencli producthunt posts --category developer-tools --limit 10
# Today's launches only
opencli producthunt today --limit 10
# Browse best products in a category
opencli producthunt browse vibe-coding --limit 10
opencli producthunt browse ai-agents --limit 10
opencli producthunt browse developer-tools --limit 10
# JSON output
opencli producthunt hot -f json
```
## Category Slugs
Common categories for `browse` and `posts --category`:
`ai-agents`, `ai-coding-agents`, `ai-code-editors`, `ai-chatbots`, `ai-workflow-automation`,
`vibe-coding`, `developer-tools`, `productivity`, `design-creative`, `marketing-sales`,
`no-code-platforms`, `llms`, `finance`, `social-community`, `engineering-development`
## Prerequisites
- `posts` and `today` — no browser required (public RSS feed)
- `hot` and `browse` — Chrome running with [Browser Bridge extension](/guide/browser-bridge) installed
+50
View File
@@ -0,0 +1,50 @@
# Reddit
**Mode**: 🔐 Browser · **Domain**: `reddit.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli reddit hot` | |
| `opencli reddit frontpage` | |
| `opencli reddit popular` | |
| `opencli reddit search` | |
| `opencli reddit subreddit` | |
| `opencli reddit read` | |
| `opencli reddit user` | |
| `opencli reddit user-posts` | |
| `opencli reddit user-comments` | |
| `opencli reddit upvote` | |
| `opencli reddit save` | |
| `opencli reddit comment` | |
| `opencli reddit subscribe` | |
| `opencli reddit saved` | |
| `opencli reddit upvoted` | |
## Usage Examples
```bash
# Quick start
opencli reddit hot --limit 5
# Read one subreddit
opencli reddit subreddit python --limit 10
# Read a post thread
opencli reddit read 1abc123 --depth 2
# Comment on a post
opencli reddit comment 1abc123 "Great post"
# JSON output
opencli reddit hot -f json
# Verbose mode
opencli reddit hot -v
```
## Prerequisites
- Chrome running and **logged into** reddit.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+27
View File
@@ -0,0 +1,27 @@
# Reuters
**Mode**: 🔐 Browser · **Domain**: `reuters.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli reuters search` | |
## Usage Examples
```bash
# Quick start
opencli reuters search --limit 5
# JSON output
opencli reuters search -f json
# Verbose mode
opencli reuters search -v
```
## Prerequisites
- Chrome running and **logged into** reuters.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+36
View File
@@ -0,0 +1,36 @@
# 新浪博客 (Sina Blog)
**Mode**: 🌐 Public (search) / 🔐 Browser (hot, article, user) · **Domain**: `blog.sina.com.cn`
## Commands
| Command | Description |
|---------|-------------|
| `opencli sinablog hot` | 获取新浪博客热门文章/推荐 |
| `opencli sinablog search` | 搜索新浪博客文章(通过新浪搜索,无需浏览器) |
| `opencli sinablog article` | 获取新浪博客单篇文章详情 |
| `opencli sinablog user` | 获取新浪博客用户的文章列表 |
## Usage Examples
```bash
# 热门文章
opencli sinablog hot --limit 10
# 搜索文章(公开 API,无需浏览器)
opencli sinablog search "人工智能"
# 文章详情
opencli sinablog article "https://blog.sina.com.cn/s/blog_xxx.html"
# 用户文章列表
opencli sinablog user 1234567890 --limit 10
# JSON output
opencli sinablog hot -f json
```
## Prerequisites
- `search` command: No login required (public API)
- `hot`, `article`, `user` commands: Chrome with `blog.sina.com.cn` accessible, Browser Bridge extension installed
+35
View File
@@ -0,0 +1,35 @@
# 新浪财经 (Sina Finance)
**Mode**: 🌐 Public · **Domain**: `finance.sina.com.cn`
## Commands
| Command | Description |
|---------|-------------|
| `opencli sinafinance news` | 新浪财经 7×24 小时实时快讯 |
## Usage Examples
```bash
# Latest financial news
opencli sinafinance news --limit 20
# Filter by type
opencli sinafinance news --type 1 # A股
opencli sinafinance news --type 2 # 宏观
opencli sinafinance news --type 6 # 国际
# JSON output
opencli sinafinance news -f json
```
### Options
| Option | Description |
|--------|-------------|
| `--limit` | Max results, up to 50 (default: 20) |
| `--type` | News type: `0`=全部, `1`=A股, `2`=宏观, `3`=公司, `4`=数据, `5`=市场, `6`=国际, `7`=观点, `8`=央行, `9`=其它 |
## Prerequisites
- No browser required — uses public API
+27
View File
@@ -0,0 +1,27 @@
# SMZDM (什么值得买)
**Mode**: 🔐 Browser · **Domain**: `smzdm.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli smzdm search` | |
## Usage Examples
```bash
# Quick start
opencli smzdm search --limit 5
# JSON output
opencli smzdm search -f json
# Verbose mode
opencli smzdm search -v
```
## Prerequisites
- Chrome running and **logged into** smzdm.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+35
View File
@@ -0,0 +1,35 @@
# Stack Overflow
**Mode**: 🌐 Public · **Domain**: `stackoverflow.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli stackoverflow hot` | Hot questions |
| `opencli stackoverflow search` | Search questions |
| `opencli stackoverflow bounties` | Questions with active bounties |
| `opencli stackoverflow unanswered` | Unanswered questions |
## Usage Examples
```bash
# Hot questions
opencli stackoverflow hot --limit 10
# Search questions
opencli stackoverflow search "async await" --limit 20
# Active bounties
opencli stackoverflow bounties --limit 10
# Unanswered questions
opencli stackoverflow unanswered --limit 10
# JSON output
opencli stackoverflow hot -f json
```
## Prerequisites
- No browser required — uses public Stack Exchange API
+26
View File
@@ -0,0 +1,26 @@
# Steam
**Mode**: 🌐 Public · **Domain**: `store.steampowered.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli steam top-sellers` | Top selling games on Steam |
## Usage Examples
```bash
# Quick start
opencli steam top-sellers
# Limit results
opencli steam top-sellers --limit 5
# JSON output
opencli steam top-sellers -f json
```
## Prerequisites
- No login required (public API)
+38
View File
@@ -0,0 +1,38 @@
# Substack
**Mode**: 🌐 Public (search) / 🔐 Browser (feed, publication) · **Domain**: `substack.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli substack feed` | Substack 热门文章 Feed |
| `opencli substack search` | 搜索 Substack 文章和 Newsletter(无需浏览器) |
| `opencli substack publication` | 获取特定 Substack Newsletter 的最新文章 |
## Usage Examples
```bash
# 热门 Feed
opencli substack feed --limit 10
# 按分类浏览
opencli substack feed --category tech --limit 10
# 搜索文章(公开 API,无需浏览器)
opencli substack search "AI"
# 搜索 Newsletter
opencli substack search "technology" --type publications
# 查看特定 Newsletter 的最新文章
opencli substack publication "https://example.substack.com" --limit 10
# JSON output
opencli substack search "AI" -f json
```
## Prerequisites
- `search` command: No login required (public API)
- `feed`, `publication` commands: Chrome with `substack.com` accessible, Browser Bridge extension installed
+68
View File
@@ -0,0 +1,68 @@
# TikTok
**Mode**: 🔐 Browser · **Domain**: `tiktok.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli tiktok profile` | Get user profile info |
| `opencli tiktok search` | Search videos |
| `opencli tiktok explore` | Trending videos from explore page |
| `opencli tiktok user` | Get recent videos from a user |
| `opencli tiktok following` | List accounts you follow |
| `opencli tiktok friends` | Friend suggestions |
| `opencli tiktok live` | Browse live streams |
| `opencli tiktok notifications` | Get notifications |
| `opencli tiktok like` | Like a video |
| `opencli tiktok unlike` | Unlike a video |
| `opencli tiktok save` | Add to Favorites |
| `opencli tiktok unsave` | Remove from Favorites |
| `opencli tiktok follow` | Follow a user |
| `opencli tiktok unfollow` | Unfollow a user |
| `opencli tiktok comment` | Comment on a video |
## Usage Examples
```bash
# View a user's profile
opencli tiktok profile --username tiktok
# Search videos
opencli tiktok search "cooking" --limit 10
# Trending explore videos
opencli tiktok explore --limit 20
# Browse live streams
opencli tiktok live --limit 10
# List who you follow
opencli tiktok following
# Friend suggestions
opencli tiktok friends --limit 10
# Like/unlike a video
opencli tiktok like --url "https://www.tiktok.com/@user/video/123"
opencli tiktok unlike --url "https://www.tiktok.com/@user/video/123"
# Save/unsave (Favorites)
opencli tiktok save --url "https://www.tiktok.com/@user/video/123"
opencli tiktok unsave --url "https://www.tiktok.com/@user/video/123"
# Follow/unfollow
opencli tiktok follow --username nasa
opencli tiktok unfollow --username nasa
# Comment on a video
opencli tiktok comment --url "https://www.tiktok.com/@user/video/123" --text "Great!"
# JSON output
opencli tiktok profile --username tiktok -f json
```
## Prerequisites
- Chrome running and **logged into** tiktok.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+56
View File
@@ -0,0 +1,56 @@
# Twitter / X
**Mode**: 🔐 Browser · **Domain**: `twitter.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli twitter trending` | |
| `opencli twitter bookmarks` | |
| `opencli twitter profile` | |
| `opencli twitter search` | |
| `opencli twitter timeline` | |
| `opencli twitter thread` | |
| `opencli twitter following` | |
| `opencli twitter followers` | |
| `opencli twitter notifications` | |
| `opencli twitter post` | |
| `opencli twitter reply` | |
| `opencli twitter delete` | |
| `opencli twitter like` | |
| `opencli twitter article` | |
| `opencli twitter follow` | |
| `opencli twitter unfollow` | |
| `opencli twitter bookmark` | |
| `opencli twitter unbookmark` | |
| `opencli twitter block` | |
| `opencli twitter unblock` | |
| `opencli twitter hide-reply` | |
| `opencli twitter download` | |
| `opencli twitter accept` | |
| `opencli twitter reply-dm` | |
## Usage Examples
```bash
# Quick start
opencli twitter trending --limit 5
# Search top tweets (default)
opencli twitter search "react 19"
# Search latest/live tweets
opencli twitter search "react 19" --filter live
# JSON output
opencli twitter trending -f json
# Verbose mode
opencli twitter trending -v
```
## Prerequisites
- Chrome running and **logged into** twitter.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+53
View File
@@ -0,0 +1,53 @@
# V2EX
**Mode**: 🌐 / 🔐 · **Domain**: `v2ex.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli v2ex hot` | Hot topics |
| `opencli v2ex latest` | Latest topics |
| `opencli v2ex topic <id>` | Topic detail |
| `opencli v2ex node <name>` | Topics by node |
| `opencli v2ex user <username>` | Topics by user |
| `opencli v2ex member <username>` | User profile |
| `opencli v2ex replies <id>` | Topic replies |
| `opencli v2ex nodes` | All nodes (sorted by topic count) |
| `opencli v2ex daily` | Daily hot |
| `opencli v2ex me` | My profile (auth required) |
| `opencli v2ex notifications` | My notifications (auth required) |
## Usage Examples
```bash
# Hot topics
opencli v2ex hot --limit 5
# Browse topics in a node
opencli v2ex node python
# View topic replies
opencli v2ex replies 1000
# User's topics
opencli v2ex user Livid
# User profile
opencli v2ex member Livid
# List all nodes
opencli v2ex nodes --limit 10
# JSON output
opencli v2ex hot -f json
```
## Prerequisites
Most commands (`hot`, `latest`, `topic`, `node`, `user`, `member`, `replies`, `nodes`) use the public V2EX API and **require no browser or login**.
For `daily`, `me`, and `notifications`:
- Chrome running and **logged into** v2ex.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+30
View File
@@ -0,0 +1,30 @@
# Web
**Mode**: 🔐 Browser · **Domain**: any URL
## Commands
| Command | Description |
|---------|-------------|
| `opencli web read <url>` | Fetch any web page and export as Markdown |
## Usage Examples
```bash
# Read a web page and save as Markdown
opencli web read https://example.com/article
# Custom output directory
opencli web read https://example.com/article --output ./my-articles
# Skip image download
opencli web read https://example.com/article --download-images false
# JSON output
opencli web read https://example.com/article -f json
```
## Prerequisites
- Chrome running
- [Browser Bridge extension](/guide/browser-bridge) installed
+31
View File
@@ -0,0 +1,31 @@
# Weibo (微博)
**Mode**: 🔐 Browser · **Domain**: `weibo.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli weibo hot` | |
| `opencli weibo search` | Search Weibo posts by keyword |
## Usage Examples
```bash
# Quick start
opencli weibo hot --limit 5
# JSON output
opencli weibo hot -f json
# Search
opencli weibo search "OpenAI" --limit 5
# Verbose mode
opencli weibo hot -v
```
## Prerequisites
- Chrome running and **logged into** weibo.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+33
View File
@@ -0,0 +1,33 @@
# WeChat (微信公众号)
**Mode**: 🔐 Browser · **Domain**: `mp.weixin.qq.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli weixin download` | 下载微信公众号文章为 Markdown 格式 |
## Usage Examples
```bash
# Export article to Markdown
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --output ./weixin
# Export with locally downloaded images
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --download-images
# Export without images
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --no-download-images
```
## Output
Downloads to `<output>/<article-title>/`:
- `<article-title>.md` — Markdown with frontmatter (title, author, publish time, source URL)
- `images/` — Downloaded images (if `--download-images` is enabled, default: true)
## Prerequisites
- Chrome running and **logged into** mp.weixin.qq.com (for articles behind login wall)
- [Browser Bridge extension](/guide/browser-bridge) installed
+48
View File
@@ -0,0 +1,48 @@
# 微信读书 (WeRead)
**Mode**: 🔐 Browser · **Domain**: `weread.qq.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli weread shelf` | List books on your bookshelf |
| `opencli weread search` | Search books on WeRead |
| `opencli weread book` | View book details |
| `opencli weread ranking` | Book rankings by category |
| `opencli weread notebooks` | List books that have highlights or notes |
| `opencli weread highlights` | List your highlights (underlines) in a book |
| `opencli weread notes` | List your notes (thoughts) on a book |
## Usage Examples
```bash
# View your bookshelf
opencli weread shelf --limit 20
# Search books
opencli weread search "三体"
# View book details
opencli weread book <book-id>
# Book rankings
opencli weread ranking --limit 10
# List books with notes/highlights
opencli weread notebooks
# View highlights for a book
opencli weread highlights <book-id>
# View your notes
opencli weread notes <book-id>
# JSON output
opencli weread shelf -f json
```
## Prerequisites
- Chrome running and **logged into** weread.qq.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+30
View File
@@ -0,0 +1,30 @@
# Wikipedia
**Mode**: 🌐 Public · **Domain**: `wikipedia.org`
## Commands
| Command | Description |
|---------|-------------|
| `opencli wikipedia search` | Search Wikipedia articles |
| `opencli wikipedia summary` | Get Wikipedia article summary |
## Usage Examples
```bash
# Search articles
opencli wikipedia search "quantum computing" --limit 10
# Get article summary
opencli wikipedia summary "Artificial intelligence"
# Use with other languages
opencli wikipedia search "人工智能" --lang zh
# JSON output
opencli wikipedia search "Rust" -f json
```
## Prerequisites
- No browser required — uses public Wikipedia API
+38
View File
@@ -0,0 +1,38 @@
# Xiaohongshu (小红书)
**Mode**: 🔐 Browser · **Domain**: `xiaohongshu.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli xiaohongshu search` | Search notes by keyword (returns title, author, likes, URL) |
| `opencli xiaohongshu notifications` | |
| `opencli xiaohongshu feed` | |
| `opencli xiaohongshu user` | |
| `opencli xiaohongshu download` | |
| `opencli xiaohongshu creator-notes` | |
| `opencli xiaohongshu creator-note-detail` | |
| `opencli xiaohongshu creator-notes-summary` | |
| `opencli xiaohongshu creator-profile` | |
| `opencli xiaohongshu creator-stats` | |
## Usage Examples
```bash
# Search for notes
opencli xiaohongshu search 美食 --limit 10
# JSON output
opencli xiaohongshu search 旅行 -f json
# Other commands
opencli xiaohongshu feed
opencli xiaohongshu notifications
opencli xiaohongshu download <url>
```
## Prerequisites
- Chrome running and **logged into** xiaohongshu.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+28
View File
@@ -0,0 +1,28 @@
# Xiaoyuzhou (小宇宙)
**Mode**: 🌐 Public · **Domain**: `xiaoyuzhou.fm`
## Commands
| Command | Description |
|---------|-------------|
| `opencli xiaoyuzhou podcast` | |
| `opencli xiaoyuzhou podcast-episodes` | |
| `opencli xiaoyuzhou episode` | |
## Usage Examples
```bash
# Quick start
opencli xiaoyuzhou podcast --limit 5
# JSON output
opencli xiaoyuzhou podcast -f json
# Verbose mode
opencli xiaoyuzhou podcast -v
```
## Prerequisites
- No browser required — uses public API
+60
View File
@@ -0,0 +1,60 @@
# Xueqiu (雪球)
**Mode**: 🔐 Browser · **Domain**: `xueqiu.com` / `danjuanfunds.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli xueqiu feed` | 获取雪球首页时间线 |
| `opencli xueqiu earnings-date` | 获取股票预计财报发布日期 |
| `opencli xueqiu hot-stock` | 获取雪球热门股票榜 |
| `opencli xueqiu hot` | 获取雪球热门动态 |
| `opencli xueqiu search` | 搜索雪球股票(代码或名称) |
| `opencli xueqiu stock` | 获取雪球股票实时行情 |
| `opencli xueqiu watchlist` | 获取雪球自选股列表 |
| `opencli xueqiu fund-holdings` | 获取蛋卷基金持仓明细(可用 `--account` 按子账户过滤) |
| `opencli xueqiu fund-snapshot` | 获取蛋卷基金快照(总资产、子账户、持仓,推荐 `-f json` |
## Usage Examples
```bash
# Quick start
opencli xueqiu feed --limit 5
# Search stocks
opencli xueqiu search 茅台
# View one stock
opencli xueqiu stock SH600519
# Upcoming earnings dates
opencli xueqiu earnings-date SH600519 --next
# Danjuan all holdings
opencli xueqiu fund-holdings
# Filter one Danjuan sub-account
opencli xueqiu fund-holdings --account 默认账户
# Full Danjuan snapshot as JSON
opencli xueqiu fund-snapshot -f json
# JSON output
opencli xueqiu feed -f json
# Verbose mode
opencli xueqiu feed -v
```
## Prerequisites
- Chrome running and **logged into** `xueqiu.com`
- For fund commands, Chrome must also be logged into `danjuanfunds.com` and able to open `https://danjuanfunds.com/my-money`
- [Browser Bridge extension](/guide/browser-bridge) installed
## Notes
- `fund-holdings` exposes both market value and share fields (`volume`, `usableRemainShare`)
- `fund-snapshot -f json` is the easiest way to persist a full account snapshot for later analysis or diffing
- If the commands return empty data, first confirm the logged-in browser can directly see the Danjuan asset page
+27
View File
@@ -0,0 +1,27 @@
# Yahoo Finance
**Mode**: 🔐 Browser · **Domain**: `finance.yahoo.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli yahoo-finance quote` | |
## Usage Examples
```bash
# Quick start
opencli yahoo-finance quote AAPL
# JSON output
opencli yahoo-finance quote TSLA -f json
# Verbose mode
opencli yahoo-finance quote NVDA -v
```
## Prerequisites
- Chrome running and able to open `finance.yahoo.com`
- [Browser Bridge extension](/guide/browser-bridge) installed
+69
View File
@@ -0,0 +1,69 @@
# Yollomi
**Mode**: 🔐 Browser · **Domain**: `yollomi.com`
AI image/video generation and editing on [yollomi.com](https://yollomi.com). Uses the same `/api/ai/*` routes as the web app; authentication is your **logged-in Chrome session** (NextAuth cookies).
## Commands
| Command | Description |
|---------|-------------|
| `opencli yollomi generate` | Text-to-image / image-to-image |
| `opencli yollomi video` | Text-to-video / image-to-video |
| `opencli yollomi edit` | Qwen image edit (prompt + image) |
| `opencli yollomi upload` | Upload a local file → public URL for other commands |
| `opencli yollomi models` | List image / video / tool models and credit costs |
| `opencli yollomi remove-bg` | Remove background (free) |
| `opencli yollomi upscale` | Image upscaling |
| `opencli yollomi face-swap` | Face swap between two images |
| `opencli yollomi restore` | Photo restoration |
| `opencli yollomi try-on` | Virtual try-on |
| `opencli yollomi background` | AI background for product/object images |
| `opencli yollomi object-remover` | Remove objects (image + mask URLs) |
## Usage Examples
```bash
# List models
opencli yollomi models --type image
# Text-to-image (default model: z-image-turbo)
opencli yollomi generate "a red apple on a wooden table"
# Choose model and aspect ratio
opencli yollomi generate "sunset" --model flux-schnell --ratio 16:9
# Image-to-image: upload first, then pass URL
opencli yollomi upload ./photo.png
opencli yollomi generate "oil painting style" --model flux-2-pro --image "https://..."
# Video
opencli yollomi video "waves on a beach" --model kling-2-1
# Tools
opencli yollomi remove-bg https://example.com/image.png
opencli yollomi upscale https://example.com/image.png --scale 4
opencli yollomi edit https://example.com/in.png "make it vintage"
```
### Common options
| Option | Applies to | Description |
|--------|------------|-------------|
| `--model` | `generate`, `video` | Model id (see `yollomi models`) |
| `--ratio` | `generate`, `video` | Aspect ratio, e.g. `1:1`, `16:9` |
| `--image` | `generate`, `video` | Image URL for img2img / i2v |
| `--output` | Most | Output directory (default `./yollomi-output`) |
| `--no-download` | Several | Print URLs only, skip saving files |
## Prerequisites
- Chrome running and **logged into** [yollomi.com](https://yollomi.com) (Google OAuth)
- [Browser Bridge extension](/guide/browser-bridge) installed; daemon connects on first command
The CLI ensures the automation tab is on `yollomi.com` before calling APIs (same-origin `fetch` with session cookies).
## Notes
- **Credits**: Each model consumes account credits; insufficient credits returns HTTP 402.
- **Upload**: Local paths for tools are not accepted directly — use `yollomi upload` to get a URL, or pass an existing HTTPS image URL.
+29
View File
@@ -0,0 +1,29 @@
# YouTube
**Mode**: 🔐 Browser · **Domain**: `youtube.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli youtube search` | |
| `opencli youtube video` | |
| `opencli youtube transcript` | |
## Usage Examples
```bash
# Quick start
opencli youtube search --limit 5
# JSON output
opencli youtube search -f json
# Verbose mode
opencli youtube search -v
```
## Prerequisites
- Chrome running and **logged into** youtube.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+30
View File
@@ -0,0 +1,30 @@
# Zhihu
**Mode**: 🔐 Browser · **Domain**: `zhihu.com`
## Commands
| Command | Description |
|---------|-------------|
| `opencli zhihu hot` | |
| `opencli zhihu search` | |
| `opencli zhihu question` | |
| `opencli zhihu download` | |
## Usage Examples
```bash
# Quick start
opencli zhihu hot --limit 5
# JSON output
opencli zhihu hot -f json
# Verbose mode
opencli zhihu hot -v
```
## Prerequisites
- Chrome running and **logged into** zhihu.com
- [Browser Bridge extension](/guide/browser-bridge) installed
+49
View File
@@ -0,0 +1,49 @@
# Antigravity
🔥 **CLI All Electron Apps! The Most Powerful Update Has Arrived!** 🔥
Turn your local Antigravity desktop application into a programmable AI node via Chrome DevTools Protocol (CDP). This allows you to compose complex LLM workflows entirely through the terminal by manipulating the actual UI natively, bypassing any API restrictions.
## Prerequisites
Start the Antigravity desktop app with the Chrome DevTools `remote-debugging-port` flag:
```bash
# Start Antigravity in the background
/Applications/Antigravity.app/Contents/MacOS/Electron \
--remote-debugging-port=9224
```
> Depending on your installation, the executable might be named differently, e.g., `Antigravity` instead of `Electron`.
Then set the target port:
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9224"
```
## Commands
### `opencli antigravity status`
Check the Chromium CDP connection. Returns the current window title and active internal URL.
### `opencli antigravity send <message>`
Send a text prompt to the AI. Automatically locates the Lexical editor input box, types the prompt securely, and hits Enter.
### `opencli antigravity read`
Scrape the entire current conversation history block as pure text.
### `opencli antigravity new`
Click the "New Conversation" button to instantly clear the UI state and start fresh.
### `opencli antigravity dump`
Dump the current DOM and snapshot artifacts to `/tmp` for reverse-engineering and selector debugging.
### `opencli antigravity extract-code`
Extract any multi-line code blocks from the current conversation view. Ideal for automated script extraction (e.g. `opencli antigravity extract-code > script.sh`).
### `opencli antigravity model <name>`
Quickly target and switch the active LLM engine. Example: `opencli antigravity model claude` or `opencli antigravity model gemini`.
### `opencli antigravity watch`
A long-running, streaming process that continuously polls the Antigravity UI for chat updates and outputs them in real-time to standard output.
+49
View File
@@ -0,0 +1,49 @@
# ChatGPT
Control the **ChatGPT macOS Desktop App** directly from the terminal. OpenCLI supports two automation approaches for ChatGPT.
## Approach 1: AppleScript (Default, No Setup)
The current built-in commands use native AppleScript automation — no extra launch flags needed.
### Prerequisites
1. Install the official [ChatGPT Desktop App](https://openai.com/chatgpt/mac/) from OpenAI.
2. Grant **Accessibility permissions** to your terminal app in **System Settings → Privacy & Security → Accessibility**.
### Commands
- `opencli chatgpt status`: Check if the ChatGPT app is currently running.
- `opencli chatgpt new`: Activate ChatGPT and press `Cmd+N` to start a new conversation.
- `opencli chatgpt send "message"`: Copy your message to clipboard, activate ChatGPT, paste, and submit.
- `opencli chatgpt send "message" --model thinking`: Switch model/mode first, then send the message.
- `opencli chatgpt read`: Read the last visible message from the focused ChatGPT window via the Accessibility tree.
- `opencli chatgpt ask "message"`: Send a prompt and wait for the visible reply in one shot.
- `opencli chatgpt ask "message" --model instant`: Run a one-shot prompt using a specific model/mode.
- `opencli chatgpt model thinking`: Switch the active ChatGPT model/mode without sending a message.
Supported model choices: `auto`, `instant`, `thinking`, `5.2-instant`, `5.2-thinking`.
## Approach 2: CDP (Advanced, Electron Debug Mode)
ChatGPT Desktop is also an Electron app and can be launched with a remote debugging port:
```bash
/Applications/ChatGPT.app/Contents/MacOS/ChatGPT \
--remote-debugging-port=9224
```
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9224"
```
> The CDP approach is primarily for advanced automation and future desktop-only commands. The built-in command set above still works in the default AppleScript path unless you explicitly route through `OPENCLI_CDP_ENDPOINT`.
## How It Works
- **AppleScript mode**: Uses `osascript` to control ChatGPT, `pbcopy`/`pbpaste` to paste prompts, and the macOS Accessibility tree to read visible chat messages.
- **CDP mode**: Connects via Chrome DevTools Protocol to the Electron renderer process.
## Limitations
- macOS only (AppleScript dependency)
- AppleScript mode requires Accessibility permissions
- `read` returns the last visible message in the focused ChatGPT window — scroll first if the message you want is not visible
+38
View File
@@ -0,0 +1,38 @@
# ChatWise
Control the **ChatWise Desktop App** from the terminal via Chrome DevTools Protocol (CDP). ChatWise is an Electron-based multi-LLM client supporting GPT-4, Claude, Gemini, and more.
## Prerequisites
1. Install [ChatWise](https://chatwise.app/).
2. Launch with remote debugging port:
```bash
/Applications/ChatWise.app/Contents/MacOS/ChatWise \
--remote-debugging-port=9228
```
## Setup
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9228"
```
## Commands
### Diagnostics
- `opencli chatwise status`: Check CDP connection status.
- `opencli chatwise screenshot`: Export DOM + accessibility snapshot.
### Chat
- `opencli chatwise new`: Start a new conversation (`Cmd+N`).
- `opencli chatwise send "message"`: Send a message to the active chat.
- `opencli chatwise read`: Read the current conversation.
- `opencli chatwise ask "prompt"`: Send + wait for response + return it (one-shot).
### AI Features
- `opencli chatwise model`: Get the current AI model.
- `opencli chatwise model gpt-4`: Switch to a different model.
### Organization
- `opencli chatwise history`: List conversations from the sidebar.
- `opencli chatwise export`: Export conversation as Markdown.
+36
View File
@@ -0,0 +1,36 @@
# Codex
Control the **OpenAI Codex Desktop App** headless or headfully via Chrome DevTools Protocol (CDP). Because Codex is built on Electron, OpenCLI can directly drive its internal UI, automate slash commands, and manipulate its AI agent threads.
## Prerequisites
1. You must have the official OpenAI Codex app installed.
2. Launch it via the terminal and expose the remote debugging port:
```bash
# macOS
/Applications/Codex.app/Contents/MacOS/Codex --remote-debugging-port=9222
```
## Setup
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9222"
```
## Commands
### Diagnostics
- `opencli codex status`: Checks connection and reads the current active window URL/title.
- `opencli codex dump`: Dumps the full UI DOM and Accessibility tree into `/tmp`.
- `opencli codex screenshot`: Captures DOM + snapshot artifacts of the current window.
### Agent Manipulation
- `opencli codex new`: Simulates `Cmd+N` to start a completely fresh and isolated Git Worktree thread context.
- `opencli codex send "message"`: Robustly finds the active Thread Composer and injects your text.
- *Pro-tip*: You can trigger internal shortcuts, e.g., `opencli codex send "/review"`.
- `opencli codex ask "message"`: Send + wait + read in one shot.
- `opencli codex read`: Extracts the entire current thread history and AI reasoning logs.
- `opencli codex extract-diff`: Automatically scrapes any visual Patch chunks and Code Diffs.
- `opencli codex model`: Get the currently active AI model.
- `opencli codex history`: List recent conversation threads from the sidebar.
- `opencli codex export`: Export the current conversation as Markdown.
+37
View File
@@ -0,0 +1,37 @@
# Cursor
Control the **Cursor IDE** from the terminal via Chrome DevTools Protocol (CDP). Since Cursor is built on Electron (VS Code fork), OpenCLI can drive its internal UI, automate Composer interactions, and manipulate chat sessions.
## Prerequisites
1. Install [Cursor](https://cursor.sh/).
2. Launch it with the remote debugging port:
```bash
/Applications/Cursor.app/Contents/MacOS/Cursor --remote-debugging-port=9226
```
## Setup
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9226"
```
## Commands
### Diagnostics
- `opencli cursor status`: Check CDP connection status.
- `opencli cursor dump`: Dump the full DOM and Accessibility snapshot to `/tmp/cursor-dom.html` and `/tmp/cursor-snapshot.json`.
- `opencli cursor screenshot`: Capture DOM + snapshot artifacts of the current window.
### Chat Manipulation
- `opencli cursor new`: Press `Cmd+N` to start a new file/tab.
- `opencli cursor send "message"`: Inject text into the active Composer/Chat input and submit.
- `opencli cursor ask "message"`: Send + wait + read in one shot.
- `opencli cursor read`: Extract the full conversation history from the active chat panel.
### AI Features
- `opencli cursor composer "prompt"`: Open the Composer panel (`Cmd+I`) and send a prompt for inline AI editing.
- `opencli cursor model`: Get the currently active AI model (e.g., `claude-4.5-sonnet`).
- `opencli cursor extract-code`: Extract all code blocks from the current conversation.
- `opencli cursor history`: List recent chat/composer sessions from the sidebar.
- `opencli cursor export`: Export the current conversation as Markdown.
+28
View File
@@ -0,0 +1,28 @@
# Discord
Control the **Discord Desktop App** from the terminal via Chrome DevTools Protocol (CDP).
## Prerequisites
Launch with remote debugging port:
```bash
/Applications/Discord.app/Contents/MacOS/Discord --remote-debugging-port=9232
```
## Setup
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9232"
```
## Commands
| Command | Description |
|---------|-------------|
| `opencli discord-app status` | Check CDP connection |
| `opencli discord-app send "message"` | Send a message in the active channel |
| `opencli discord-app read` | Read recent messages |
| `opencli discord-app channels` | List channels in the current server |
| `opencli discord-app servers` | List all joined servers |
| `opencli discord-app search "query"` | Search messages (Cmd+F) |
| `opencli discord-app members` | List online members |
+35
View File
@@ -0,0 +1,35 @@
# Doubao App (豆包桌面版)
Control the **Doubao AI Desktop App** via Chrome DevTools Protocol (CDP).
## Prerequisites
1. Launch Doubao Desktop with remote debugging enabled:
```bash
/Applications/Doubao.app/Contents/MacOS/Doubao --remote-debugging-port=9225
```
2. Set the CDP endpoint:
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9225"
```
## Commands
| Command | Description |
|---------|-------------|
| `opencli doubao-app status` | Check CDP connection status |
| `opencli doubao-app new` | Start a new conversation |
| `opencli doubao-app send "message"` | Send a message to the current chat |
| `opencli doubao-app read` | Read the latest assistant reply |
| `opencli doubao-app ask "message"` | Send a prompt and wait for the reply |
| `opencli doubao-app screenshot` | Capture a screenshot of the app window |
| `opencli doubao-app dump` | Export DOM and snapshot debug info |
## How It Works
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.
## Limitations
- Requires Doubao Desktop to be launched with `--remote-debugging-port`
- macOS / Linux / Windows (Electron-based, platform independent)
+29
View File
@@ -0,0 +1,29 @@
# Notion
Control the **Notion Desktop App** from the terminal via Chrome DevTools Protocol (CDP).
## Prerequisites
Launch with remote debugging port:
```bash
/Applications/Notion.app/Contents/MacOS/Notion --remote-debugging-port=9230
```
## Setup
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9230"
```
## Commands
| Command | Description |
|---------|-------------|
| `opencli notion status` | Check CDP connection |
| `opencli notion search "query"` | Quick Find search (Cmd+P) |
| `opencli notion read` | Read the current page content |
| `opencli notion new "title"` | Create a new page (Cmd+N) |
| `opencli notion write "text"` | Append text to the current page |
| `opencli notion sidebar` | List pages from the sidebar |
| `opencli notion favorites` | List pages from the Favorites section |
| `opencli notion export` | Export page as Markdown |
+81
View File
@@ -0,0 +1,81 @@
# All Adapters
Run `opencli list` for the live registry.
## Browser Adapters
| Site | Commands | Mode |
|------|----------|------|
| **[twitter](/adapters/browser/twitter)** | `trending` `bookmarks` `profile` `search` `timeline` `thread` `following` `followers` `notifications` `post` `reply` `delete` `like` `article` `follow` `unfollow` `bookmark` `unbookmark` `download` `accept` `reply-dm` `block` `unblock` `hide-reply` | 🔐 Browser |
| **[reddit](/adapters/browser/reddit)** | `hot` `frontpage` `popular` `search` `subreddit` `read` `user` `user-posts` `user-comments` `upvote` `save` `comment` `subscribe` `saved` `upvoted` | 🔐 Browser |
| **[bilibili](/adapters/browser/bilibili)** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 🔐 Browser |
| **[zhihu](/adapters/browser/zhihu)** | `hot` `search` `question` `download` | 🔐 Browser |
| **[xiaohongshu](/adapters/browser/xiaohongshu)** | `search` `notifications` `feed` `user` `download` `publish` `creator-notes` `creator-note-detail` `creator-notes-summary` `creator-profile` `creator-stats` | 🔐 Browser |
| **[xueqiu](/adapters/browser/xueqiu)** | `feed` `hot-stock` `hot` `search` `stock` `watchlist` `earnings-date` `fund-holdings` `fund-snapshot` | 🔐 Browser |
| **[youtube](/adapters/browser/youtube)** | `search` `video` `transcript` | 🔐 Browser |
| **[v2ex](/adapters/browser/v2ex)** | `hot` `latest` `topic` `node` `user` `member` `replies` `nodes` `daily` `me` `notifications` | 🌐 / 🔐 |
| **[bloomberg](/adapters/browser/bloomberg)** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | 🌐 / 🔐 |
| **[weibo](/adapters/browser/weibo)** | `hot` `search` | 🔐 Browser |
| **[linkedin](/adapters/browser/linkedin)** | `search` `timeline` | 🔐 Browser |
| **[coupang](/adapters/browser/coupang)** | `search` `add-to-cart` | 🔐 Browser |
| **[boss](/adapters/browser/boss)** | `search` `detail` `recommend` `joblist` `greet` `batchgreet` `send` `chatlist` `chatmsg` `invite` `mark` `exchange` `resume` `stats` | 🔐 Browser |
| **[ctrip](/adapters/browser/ctrip)** | `search` | 🔐 Browser |
| **[reuters](/adapters/browser/reuters)** | `search` | 🔐 Browser |
| **[smzdm](/adapters/browser/smzdm)** | `search` | 🔐 Browser |
| **[jike](/adapters/browser/jike)** | `feed` `search` `post` `topic` `user` `create` `comment` `like` `repost` `notifications` | 🔐 Browser |
| **[jimeng](/adapters/browser/jimeng)** | `generate` `history` | 🔐 Browser |
| **[yollomi](/adapters/browser/yollomi)** | `generate` `video` `edit` `upload` `models` `remove-bg` `upscale` `face-swap` `restore` `try-on` `background` `object-remover` | 🔐 Browser |
| **[linux-do](/adapters/browser/linux-do)** | `feed` `categories` `tags` `search` `topic` `user-topics` `user-posts` | 🔐 Browser |
| **[chaoxing](/adapters/browser/chaoxing)** | `assignments` `exams` | 🔐 Browser |
| **[grok](/adapters/browser/grok)** | `ask` | 🔐 Browser |
| **[doubao](/adapters/browser/doubao)** | `status` `new` `send` `read` `ask` | 🔐 Browser |
| **[weread](/adapters/browser/weread)** | `shelf` `search` `book` `ranking` `notebooks` `highlights` `notes` | 🔐 Browser |
| **[douban](/adapters/browser/douban)** | `search` `top250` `subject` `photos` `download` `marks` `reviews` `movie-hot` `book-hot` | 🔐 Browser |
| **[facebook](/adapters/browser/facebook)** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 🔐 Browser |
| **[imdb](/adapters/browser/imdb)** | `search` `title` `top` `trending` `person` `reviews` | 🌐 / 🔐 |
| **[instagram](/adapters/browser/instagram)** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 🔐 Browser |
| **[medium](/adapters/browser/medium)** | `feed` `search` `user` | 🔐 Browser |
| **[sinablog](/adapters/browser/sinablog)** | `hot` `search` `article` `user` | 🔐 Browser |
| **[substack](/adapters/browser/substack)** | `feed` `search` `publication` | 🔐 Browser |
| **[pixiv](/adapters/browser/pixiv)** | `ranking` `search` `user` `illusts` `detail` `download` | 🔐 Browser |
| **[tiktok](/adapters/browser/tiktok)** | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` | 🔐 Browser |
| **[google](/adapters/browser/google)** | `news` `search` `suggest` `trends` | 🌐 / 🔐 |
| **[jd](/adapters/browser/jd)** | `item` | 🔐 Browser |
| **[web](/adapters/browser/web)** | `read` | 🔐 Browser |
| **[weixin](/adapters/browser/weixin)** | `download` | 🔐 Browser |
| **[36kr](/adapters/browser/36kr)** | `news` `hot` `search` `article` | 🌐 / 🔐 |
| **[producthunt](/adapters/browser/producthunt)** | `posts` `today` `hot` `browse` | 🌐 / 🔐 |
## Public API Adapters
| Site | Commands | Mode |
|------|----------|------|
| **[hackernews](/adapters/browser/hackernews)** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 🌐 Public |
| **[bbc](/adapters/browser/bbc)** | `news` | 🌐 Public |
| **[devto](/adapters/browser/devto)** | `top` `tag` `user` | 🌐 Public |
| **[dictionary](/adapters/browser/dictionary)** | `search` `synonyms` `examples` | 🌐 Public |
| **[apple-podcasts](/adapters/browser/apple-podcasts)** | `search` `episodes` `top` | 🌐 Public |
| **[xiaoyuzhou](/adapters/browser/xiaoyuzhou)** | `podcast` `podcast-episodes` `episode` | 🌐 Public |
| **[yahoo-finance](/adapters/browser/yahoo-finance)** | `quote` | 🌐 Public |
| **[arxiv](/adapters/browser/arxiv)** | `search` `paper` | 🌐 Public |
| **[paperreview](/adapters/browser/paperreview)** | `submit` `review` `feedback` | 🌐 Public |
| **[barchart](/adapters/browser/barchart)** | `quote` `options` `greeks` `flow` | 🌐 Public |
| **[hf](/adapters/browser/hf)** | `top` | 🌐 Public |
| **[sinafinance](/adapters/browser/sinafinance)** | `news` | 🌐 Public |
| **[stackoverflow](/adapters/browser/stackoverflow)** | `hot` `search` `bounties` `unanswered` | 🌐 Public |
| **[wikipedia](/adapters/browser/wikipedia)** | `search` `summary` `random` `trending` | 🌐 Public |
| **[lobsters](/adapters/browser/lobsters)** | `hot` `newest` `active` `tag` | 🌐 Public |
| **[steam](/adapters/browser/steam)** | `top-sellers` | 🌐 Public |
## Desktop Adapters
| App | Description | Commands |
|-----|-------------|----------|
| **[Cursor](/adapters/desktop/cursor)** | Control Cursor IDE | `status` `send` `read` `new` `dump` `composer` `model` `extract-code` `ask` `screenshot` `history` `export` |
| **[Codex](/adapters/desktop/codex)** | Drive OpenAI Codex CLI agent | `status` `send` `read` `new` `extract-diff` `model` `ask` `screenshot` `history` `export` |
| **[Antigravity](/adapters/desktop/antigravity)** | Control Antigravity Ultra | `status` `send` `read` `new` `dump` `extract-code` `model` `watch` |
| **[ChatGPT](/adapters/desktop/chatgpt)** | Automate ChatGPT macOS app | `status` `new` `send` `read` `ask` |
| **[ChatWise](/adapters/desktop/chatwise)** | Multi-LLM client | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` |
| **[Notion](/adapters/desktop/notion)** | Search, read, write pages | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` |
| **[Discord](/adapters/desktop/discord)** | Desktop messages & channels | `status` `send` `read` `channels` `servers` `search` `members` |
| **[Doubao App](/adapters/desktop/doubao-app)** | Doubao AI desktop app via CDP | `status` `new` `send` `read` `ask` `screenshot` `dump` |
+103
View File
@@ -0,0 +1,103 @@
# Connecting OpenCLI via CDP (Remote/Headless Servers)
If you cannot use the opencli Browser Bridge extension (e.g., in a remote headless server environment without a UI), OpenCLI provides an alternative: connecting directly to Chrome via **CDP (Chrome DevTools Protocol)**.
Because CDP binds to `localhost` by default for security reasons, accessing it from a remote server requires an additional networking tunnel.
This guide is broken down into three phases:
1. **Preparation**: Start Chrome with CDP enabled locally.
2. **Network Tunnels**: Expose that CDP port to your remote server using either **SSH Tunnels** or **Reverse Proxies**.
3. **Execution**: Run OpenCLI on your server.
---
## Phase 1: Preparation (Local Machine)
First, you need to start a Chrome browser on your local machine with remote debugging enabled.
**macOS:**
```bash
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
--user-data-dir="$HOME/chrome-debug-profile" \
--remote-allow-origins="*"
```
**Linux:**
```bash
google-chrome \
--remote-debugging-port=9222 \
--user-data-dir="$HOME/chrome-debug-profile" \
--remote-allow-origins="*"
```
**Windows:**
```cmd
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
--remote-debugging-port=9222 ^
--user-data-dir="%USERPROFILE%\chrome-debug-profile" ^
--remote-allow-origins="*"
```
> **Note**: The `--remote-allow-origins="*"` flag is often required for modern Chrome versions to accept cross-origin CDP WebSocket connections (e.g. from reverse proxies like ngrok).
Once this browser instance opens, **log into the target websites you want to use** (e.g., bilibili.com, zhihu.com) so that the session contains the correct cookies.
---
## Phase 2: Remote Access Methods
Once CDP is running locally on port `9222`, you must securely expose this port to your remote server. Choose one of the two methods below depending on your network conditions.
### Method A: SSH Tunnel (Recommended)
If your local machine has SSH access to the remote server, this is the most secure and straightforward method.
Run this command on your **Local Machine** to forward the remote server's port `9222` back to your local port `9222`:
```bash
ssh -R 9222:localhost:9222 your-server-user@your-server-ip
```
Leave this SSH session running in the background.
### Method B: Reverse Proxy (ngrok / frp / socat)
If you cannot establish a direct SSH connection (e.g., due to NAT or firewalls), you can use an intranet penetration tool like `ngrok`.
Run this command on your **Local Machine** to expose your local port `9222` to the public internet securely via ngrok:
```bash
ngrok http 9222
```
This will print a forwarding URL, such as `https://abcdef.ngrok.app`. **Copy this URL**.
---
## Phase 3: Execution (Remote Server)
Now switch to your **Remote Server** where OpenCLI is installed.
Depending on the network tunnel method you chose in Phase 2, set the `OPENCLI_CDP_ENDPOINT` environment variable and run your commands.
### If you used Method A (SSH Tunnel):
```bash
export OPENCLI_CDP_ENDPOINT="http://localhost:9222"
opencli doctor # Verify connection
opencli bilibili hot --limit 5 # Test a command
```
### If you used Method B (Reverse Proxy like ngrok):
```bash
# Use the URL you copied from ngrok earlier
export OPENCLI_CDP_ENDPOINT="https://abcdef.ngrok.app"
opencli doctor # Verify connection
opencli bilibili hot --limit 5 # Test a command
```
> *Tip: If you provide a standard HTTP/HTTPS CDP endpoint, OpenCLI requests the `/json` target list and picks the most likely inspectable app/page target automatically. If multiple app targets exist, you can further narrow selection with `OPENCLI_CDP_TARGET` (for example `antigravity` or `codex`).*
If you plan to use this setup frequently, you can persist the environment variable by adding the `export` line to your `~/.bashrc` or `~/.zshrc` on the server.
+71
View File
@@ -0,0 +1,71 @@
# Download Support
OpenCLI supports downloading images, videos, and articles from supported platforms.
## Supported Platforms
| Platform | Content Types | Notes |
|----------|---------------|-------|
| **xiaohongshu** | Images, Videos | Downloads all media from a note |
| **bilibili** | Videos | Requires `yt-dlp` installed |
| **twitter** | Images, Videos | Downloads from user media tab or single tweet |
| **douban** | Images | Downloads poster / still image lists from movie subjects |
| **zhihu** | Articles (Markdown) | Exports articles with optional image download |
| **weixin** | Articles (Markdown) | Exports WeChat Official Account articles |
## Prerequisites
For video downloads from streaming platforms, install `yt-dlp`:
```bash
# Install yt-dlp
pip install yt-dlp
# or
brew install yt-dlp
```
## Usage Examples
```bash
# Download images/videos from Xiaohongshu note
opencli xiaohongshu download --note-id abc123 --output ./xhs
# Download Bilibili video (requires yt-dlp)
opencli bilibili download --bvid BV1xxx --output ./bilibili
opencli bilibili download --bvid BV1xxx --quality 1080p
# Download Twitter media from user
opencli twitter download elonmusk --limit 20 --output ./twitter
# Download single tweet media
opencli twitter download --tweet-url "https://x.com/user/status/123" --output ./twitter
# Download Douban posters / stills
opencli douban download 30382501 --output ./douban
# Export Zhihu article to Markdown
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --output ./zhihu
# Export with local images
opencli zhihu download "https://zhuanlan.zhihu.com/p/xxx" --download-images
# Export WeChat article to Markdown
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" --output ./weixin
```
## Pipeline Step (YAML Adapters)
The `download` step can be used in YAML pipelines:
::: v-pre
```yaml
pipeline:
- fetch: https://api.example.com/media
- download:
url: ${{ item.imageUrl }}
dir: ./downloads
filename: ${{ item.title | sanitize }}.jpg
concurrency: 5
skip_existing: true
```
:::
+125
View File
@@ -0,0 +1,125 @@
---
description: How to CLI-ify and automate any Electron Desktop Application via CDP
---
# CLI-ifying Electron Applications (Skill Guide)
Based on the successful automation of **Cursor**, **Codex**, **Antigravity**, **ChatWise**, **Notion**, and **Discord** desktop apps, this guide serves as the standard operating procedure (SOP) for adapting ANY Electron-based application into an OpenCLI adapter.
## Core Concept
Electron apps are essentially local Chromium browser instances. By exposing a debugging port (CDP — Chrome DevTools Protocol) at launch time, we can use the Browser Bridge to pierce through the UI layer, accessing and controlling all underlying state including React/Vue components and Shadow DOM.
> **Note:** Not all desktop apps are Electron. WeChat (native Cocoa) and Feishu/Lark (custom Lark Framework) embed Chromium but do NOT expose CDP. For those apps, use the AppleScript + clipboard approach instead (see [Non-Electron Pattern](#non-electron-pattern-applescript)).
### Launching the Target App
```bash
/Applications/AppName.app/Contents/MacOS/AppName --remote-debugging-port=9222
```
### Verifying Electron
```bash
# Check for Electron Framework in the app bundle
ls /Applications/AppName.app/Contents/Frameworks/Electron\ Framework.framework
# If this directory exists → Electron → CDP works
# If not → check for libEGL.dylib (embedded Chromium/CEF, CDP may not work)
```
## The 5-Command Pattern (CDP / Electron)
Every new Electron adapter should implement these 5 commands in `src/clis/<app_name>/`:
### 1. `status.ts` — Connection Test
```typescript
export const statusCommand = cli({
site: 'myapp',
name: 'status',
domain: 'localhost',
strategy: Strategy.UI,
browser: true, // Requires CDP connection
args: [],
columns: ['Status', 'Url', 'Title'],
func: async (page: IPage) => {
const url = await page.evaluate('window.location.href');
const title = await page.evaluate('document.title');
return [{ Status: 'Connected', Url: url, Title: title }];
},
});
```
### 2. `dump.ts` — Reverse Engineering Core
Modern app DOMs are huge and obfuscated. **Never guess selectors.** Dump first, then extract precise class names with AI or `grep`:
```typescript
const dom = await page.evaluate('document.body.innerHTML');
fs.writeFileSync('/tmp/app-dom.html', dom);
const snap = await page.snapshot({ interactive: false });
fs.writeFileSync('/tmp/app-snapshot.json', JSON.stringify(snap, null, 2));
```
### 3. `send.ts` — Advanced Text Injection
Electron apps often use complex rich-text editors (Monaco, Lexical, ProseMirror). Setting `.value` directly is ignored by React state.
**Best practice:** Use `document.execCommand('insertText')` to perfectly simulate real user input, fully piercing React state:
```javascript
const composer = document.querySelector('[contenteditable="true"]');
composer.focus();
document.execCommand('insertText', false, 'Hello');
```
Then submit with `await page.pressKey('Enter')`.
### 4. `read.ts` — Context Extraction
Don't extract the entire page text. Use `dump.ts` output to find the real "conversation container":
- Look for semantic selectors: `[role="log"]`, `[data-testid="conversation"]`, `[data-content-search-turn-key]`
- Format output as Markdown — readable by both humans and LLMs
### 5. `new.ts` — Keyboard Shortcuts
Many GUI actions respond to native shortcuts rather than button clicks:
```typescript
const isMac = process.platform === 'darwin';
await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
await page.wait(1); // Wait for re-render
```
## Environment Variable
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9222"
```
## Non-Electron Pattern (AppleScript)
For native macOS apps (WeChat, Feishu) that don't expose CDP:
```typescript
export const statusCommand = cli({
site: 'myapp',
strategy: Strategy.PUBLIC,
browser: false, // No browser needed
func: async (page: IPage | null) => {
const output = execSync("osascript -e 'application \"MyApp\" is running'", { encoding: 'utf-8' }).trim();
return [{ Status: output === 'true' ? 'Running' : 'Stopped' }];
},
});
```
Core techniques:
- **status**: `osascript -e 'application "AppName" is running'`
- **send**: `pbcopy` → activate window → `Cmd+V``Enter`
- **read**: `Cmd+A``Cmd+C``pbpaste`
- **search**: Activate → `Cmd+F`/`Cmd+K``keystroke "query"`
## Pitfalls & Gotchas
1. **Port conflicts (EADDRINUSE)**: Only one app per port. Use unique ports: Codex=9222, ChatGPT=9224, Cursor=9226, ChatWise=9228, Notion=9230, Discord=9232
2. **IPage abstraction**: OpenCLI wraps the browser page as `IPage` (`src/types.ts`). Use `page.pressKey()` and `page.evaluate()`, NOT direct DOM APIs
3. **Timing**: Always add `await page.wait(0.5)` to `1.0` after DOM mutations. Returning too early disconnects prematurely
4. **AppleScript requires Accessibility**: Terminal app must be granted permission in System Settings → Privacy & Security → Accessibility
## Port Assignment Table
| App | Port | Mode |
|-----|------|------|
| Codex | 9222 | CDP |
| ChatGPT | 9224 | CDP / AppleScript |
| Cursor | 9226 | CDP |
| ChatWise | 9228 | CDP |
| Notion | 9230 | CDP |
| Discord App | 9232 | CDP |
+99
View File
@@ -0,0 +1,99 @@
# Rate Limiter Plugin
An optional plugin that adds a random sleep between browser-based commands to reduce the risk of platform rate-limiting or bot detection.
## Install
```bash
opencli plugin install github:jackwener/opencli-plugin-rate-limiter
```
Or copy the example below into `~/.opencli/plugins/rate-limiter/` to use it locally without installing from GitHub.
## What it does
After every command targeting a browser platform (xiaohongshu, weibo, bilibili, douyin, tiktok, …), the plugin sleeps for a random duration — 530 seconds by default — before returning control to the caller.
## Configuration
| Variable | Default | Description |
|---|---|---|
| `OPENCLI_RATE_MIN` | `5` | Minimum sleep in seconds |
| `OPENCLI_RATE_MAX` | `30` | Maximum sleep in seconds |
| `OPENCLI_NO_RATE` | — | Set to `1` to disable entirely (local dev) |
```bash
# Shorter delays for light scraping
OPENCLI_RATE_MIN=3 OPENCLI_RATE_MAX=10 opencli xiaohongshu search "AI眼镜"
# Skip delays when iterating locally
OPENCLI_NO_RATE=1 opencli bilibili comments BV1WtAGzYEBm
```
## Local installation (without GitHub)
1. Create the plugin directory:
```bash
mkdir -p ~/.opencli/plugins/rate-limiter
```
2. Create `~/.opencli/plugins/rate-limiter/package.json`:
```json
{ "type": "module" }
```
3. Create `~/.opencli/plugins/rate-limiter/index.js`:
```js
import { onAfterExecute } from '@jackwener/opencli/hooks'
const BROWSER_DOMAINS = [
'xiaohongshu', 'weibo', 'bilibili', 'douyin', 'tiktok',
'instagram', 'twitter', 'youtube', 'zhihu', 'douban',
'jike', 'weixin', 'xiaoyuzhou',
]
onAfterExecute(async (ctx) => {
if (process.env.OPENCLI_NO_RATE === '1') return
const site = ctx.command?.split('/')?.[0] ?? ''
if (!BROWSER_DOMAINS.includes(site)) return
const min = Number(process.env.OPENCLI_RATE_MIN ?? 5)
const max = Number(process.env.OPENCLI_RATE_MAX ?? 30)
const ms = Math.floor(Math.random() * (max - min + 1) + min) * 1000
process.stderr.write(`[rate-limiter] ${site}: sleeping ${(ms / 1000).toFixed(0)}s\n`)
await new Promise(r => setTimeout(r, ms))
})
```
4. Verify it loaded:
```bash
OPENCLI_NO_RATE=1 opencli xiaohongshu search "test" 2>&1 | grep rate-limiter
# → (no output — plugin loaded but rate limit skipped)
opencli xiaohongshu search "test" 2>&1 | grep rate-limiter
# → [rate-limiter] xiaohongshu: sleeping 12s
```
## Writing your own plugin
Plugins are plain JS/TS files in `~/.opencli/plugins/<name>/`. A plugin file must export a hook registration call that matches the pattern `onStartup(`, `onBeforeExecute(`, or `onAfterExecute(` — opencli's discovery engine uses this pattern to identify hook files vs. command files.
```js
// ~/.opencli/plugins/my-plugin/index.js
import { onAfterExecute } from '@jackwener/opencli/hooks'
onAfterExecute(async (ctx) => {
// ctx.command — e.g. "bilibili/comments"
// ctx.args — coerced command arguments
// ctx.error — set if the command threw
console.error(`[my-plugin] finished: ${ctx.command}`)
})
```
See [hooks.ts](../../src/hooks.ts) for the full `HookContext` type.
+72
View File
@@ -0,0 +1,72 @@
# Remote Chrome
Run OpenCLI on a server or headless environment by connecting to a remote Chrome instance.
## Use Cases
- Running CLI commands on a remote server
- CI/CD automation with headed browser
- Shared team browser sessions
## Setup
### 1. Start Chrome on the Remote Machine
```bash
# On the remote machine (or your Mac)
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222
```
### 2. SSH Tunnel (If Needed)
If the remote Chrome is on a different machine, create an SSH tunnel:
```bash
# On your local machine or server
ssh -L 9222:127.0.0.1:9222 user@remote-host
```
::: warning
Use `127.0.0.1` instead of `localhost` in the SSH command to avoid IPv6 resolution issues that can cause timeouts.
:::
### 3. Configure OpenCLI
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9222"
```
### 4. Verify
```bash
# Test the connection
curl http://127.0.0.1:9222/json/version
# Run a diagnostic
opencli doctor
```
## CI/CD Integration
For CI/CD environments, use a real Chrome instance with `xvfb`:
::: v-pre
```yaml
steps:
- uses: browser-actions/setup-chrome@latest
id: setup-chrome
- run: |
xvfb-run --auto-servernum \
${{ steps.setup-chrome.outputs.chrome-path }} \
--remote-debugging-port=9222 &
```
:::
Set the browser executable path:
::: v-pre
```yaml
env:
OPENCLI_BROWSER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
```
:::
+125
View File
@@ -0,0 +1,125 @@
# Comparison Guide
OpenCLI occupies a specific niche in the browser automation ecosystem. This guide honestly evaluates where opencli excels, where it's a viable option, and where other tools are a better fit.
## At a Glance
| Tool | Approach | Best for |
|------|----------|----------|
| **opencli** | Pre-built adapters (YAML/TS) | Deterministic site commands, broad platform coverage, desktop apps |
| **Browser-Use** | LLM-driven browser control | General-purpose AI browser automation |
| **Crawl4AI** | Async web crawler | Large-scale data crawling |
| **Firecrawl** | Scraping API / self-hosted | Clean markdown extraction, managed or self-hosted infrastructure |
| **agent-browser** | Browser primitive CLI | Token-efficient AI agent browsing |
| **Stagehand** | AI browser framework | Developer-friendly browser automation |
| **Skyvern** | Visual AI automation | Cross-site generalized workflows |
## Scenario Comparison
### 1. Scheduled Batch Data Extraction
> "I want to pull trending posts from Bilibili/Reddit/HackerNews every hour into my pipeline."
| Tool | Fit | Notes |
|------|-----|-------|
| **opencli** | Best | One command, structured JSON output, zero runtime cost. Runs in cron/CI without tokens or API keys. |
| Crawl4AI | Good | Strong for large-scale crawling, but requires writing extraction logic per site. |
| Firecrawl | Viable | Managed service with clean output, but costs scale with volume. |
| Browser-Use / Stagehand | Poor | LLM inference on every run is slow, expensive, and non-deterministic for repeated tasks. |
**Why opencli wins here:** A command like `opencli bilibili hot -f json` returns the same structured schema every time, costs nothing to run, and finishes in seconds. For recurring data extraction from known sites, pre-built adapters beat LLM-driven approaches on cost, speed, and reliability.
### 2. AI Agent Site Operations
> "My AI agent needs to search Twitter, read Reddit threads, or post to Xiaohongshu."
| Tool | Fit | Notes |
|------|-----|-------|
| **opencli** | Best | Structured JSON output, fast deterministic execution, hundreds of commands ready to use. |
| agent-browser | Good | Token-efficient browser primitives, but requires LLM reasoning for every step. |
| Browser-Use | Viable | General-purpose, but each operation costs tokens and takes 10-60s. |
| Stagehand | Viable | Good DX, but same LLM-per-action cost model. |
**Why opencli wins here:** When your agent needs `twitter search "AI news" -f json`, a deterministic command that returns in seconds is strictly better than an LLM clicking through a webpage. The agent saves tokens for reasoning, not navigation.
### 3. Authenticated Operations (Login-Required Sites)
> "I need to access my bookmarks, post content, or interact with sites that require login."
| Tool | Fit | Notes |
|------|-----|-------|
| **opencli** | Best | Reuses your Chrome login session via Browser Bridge. No credentials stored or transmitted. |
| Browser-Use | Viable | Can use browser profiles, but credential management is manual. |
| Firecrawl | Poor | Cloud service cannot access your authenticated sessions. |
| Crawl4AI | Poor | Requires manual cookie/session injection. |
**Why opencli wins here:** The Browser Bridge extension reuses your existing Chrome login state in real-time. You log in once in Chrome, and opencli commands work immediately. No OAuth setup, no API keys, no credential files.
### 4. General Web Browsing & Exploration
> "I need to explore an unknown website, fill forms, or navigate complex multi-step flows."
| Tool | Fit | Notes |
|------|-----|-------|
| Browser-Use | Best | LLM-driven, handles arbitrary websites and flows. |
| Stagehand | Best | Clean API for `act()`, `extract()`, `observe()` on any page. |
| agent-browser | Good | Token-efficient primitives for AI agents. |
| Skyvern | Good | Visual AI that generalizes across sites. |
| **opencli** | Poor | Only works with sites that have pre-built adapters. Cannot handle arbitrary websites. |
**opencli is not the right tool here.** If you need to explore unknown websites or handle one-off tasks on sites without adapters, use an LLM-driven browser tool. opencli trades generality for determinism and cost.
### 5. Desktop App Control
> "I want to script Cursor, ChatGPT, Notion, or other Electron apps from the terminal."
| Tool | Fit | Notes |
|------|-----|-------|
| **opencli** | Best | 8 desktop adapters via CDP + AppleScript. The only CLI tool with this capability. |
| All others | N/A | Browser automation tools cannot control desktop applications. |
**This is unique to opencli.** No other tool in this comparison can send a prompt to ChatGPT desktop, extract code from Cursor, or write to Notion pages via CLI.
## Key Trade-offs
### opencli's Strengths
- **Zero LLM cost** — No tokens consumed at runtime. Run 10,000 times for free.
- **Deterministic output** — Same command always returns the same schema. Pipeable, scriptable, CI-friendly.
- **Speed** — Adapter commands return in seconds, not minutes.
- **Broad platform coverage** — 50+ sites spanning global platforms (Reddit, HackerNews, Twitter, YouTube) and Chinese platforms (Bilibili, Zhihu, Xiaohongshu, Douban, Weibo) with adapters that understand local anti-bot patterns.
- **Desktop app control** — CDP adapters for Cursor, Codex, Notion, ChatGPT, Discord, and more.
- **Easy to extend** — Drop a `.yaml` or `.ts` adapter into the `clis/` folder for auto-registration. Contributing a new site adapter is straightforward.
### opencli's Limitations
- **Coverage requires adapters** — opencli only works with sites that have pre-built adapters. Adding a new site means writing a YAML or TypeScript adapter.
- **Adapter maintenance** — When a website updates its DOM or API, the corresponding adapter may need updating. The community maintains these, but breakage is possible.
- **Not general-purpose** — Cannot handle arbitrary websites. For unknown sites, pair opencli with a general browser tool as a fallback.
## Complementary Usage
opencli works best alongside general-purpose browser tools, not as a replacement:
```
Has adapter? ──yes──▶ opencli (fast, free, deterministic)
no
One-off task? ──yes──▶ Browser-Use / Stagehand (LLM-driven)
no
Recurring? ──yes──▶ Write an opencli adapter, then use opencli
```
## Further Reading
- [Architecture Overview](./developer/architecture.md)
- [Writing a YAML Adapter](./developer/yaml-adapter.md)
- [Writing a TypeScript Adapter](./developer/ts-adapter.md)
- [Testing Guide](./developer/testing.md)
- [AI Workflow](./developer/ai-workflow.md)
- [Contributing Guide](./developer/contributing.md)

Some files were not shown because too many files have changed in this diff Show More