1592 Commits

Author SHA1 Message Date
jakevin 8271afc67e chore: release v1.8.8 (#2440) v1.8.8 2026-08-31 01:35:37 +08:00
Anupam Mediratta 2c598f5865 fix: javascript.lang.security.detect-child-process.detect-child-process security vulnerability (#2318)
Automated security fix generated by OrbisAI Security
2026-08-30 17:08:16 +08:00
Vec 4871250281 fix(xiaohongshu): stop ask breaking on webpack chunk renumbering, and stop it spending a note search per call (#2420)
* fix(xiaohongshu): locate 点点 conversation store by fingerprint, not a hardcoded module id

webpackRequire(6404) throws "Cannot read properties of undefined (reading
'call')" once Xiaohongshu renumbers its webpack chunks, which it did
(6404 -> 32914). Every `ask` call then fails in ~4s, before any chat happens.
Reported in #2408.

A hardcoded numeric module id has no contract with the site, so patching in the
new number only resets the clock until the next redeploy. This tries the known
ids first (zero scan cost in the common case), then locates the module by what
it *is*: scan webpackRequire.m for a factory whose source mentions both
createConversation and sendMessage. On a live page that is 1 candidate out of
~1600 modules. The scan only calls .toString(), which has no side effects; a
candidate is executed only after its source matches the fingerprint.

Tests execute the generated page script against a fake webpack runtime, so the
lookup is exercised rather than asserted as a substring - including the case
that matters: a store sitting at an id in neither the known list nor any
previous build. Verified the guard can fail: breaking the fingerprint string
turns that one test red and nothing else.

* fix(xiaohongshu): enter ask through /ai_chat instead of search_result?keyword=

`ask` navigated to search_result?keyword=<query> to reach the 点点 conversation
store. That entry point costs something and buys nothing.

Measured on a live logged-in session (in-page fetch/XHR/WebSocket hooks plus
performance resource entries):

1. Cost. Loading search_result?keyword=... fires a real note search
   (so.xiaohongshu.com/api/sns/web/v2/search/notes) before any chat happens - a
   search request spent purely as a side effect of the URL chosen to reach a
   chat store. /ai_chat serves the same conversation store and fires zero
   search/notes for the whole page lifetime.

2. Risk control. #1224 documented that direct navigation to
   search_result?keyword=... triggers Xiaohongshu's security verification in the
   automation browser, and xiaohongshu/search was reworked away from exactly
   that pattern. ask still used it.

Controlled A/B, identical query, same session, minutes apart:

  search_result -> 1238 chars, 5 sources, 1x search/notes
  /ai_chat      -> 1195 chars, 5 sources, 0x search/notes

The navigation contributes nothing to answer quality or citation count; the
3.5% length difference is ordinary model variance. (A first pass compared two
*different* queries and saw 16131 vs 1195, which reads as though the entry page
matters enormously - it was query variance. Only the same-query control settled
it.)

The test now pins /ai_chat and additionally asserts goto is never called with a
search_result URL, so a regression to a search-spending entry point fails loudly
rather than silently costing a request per call.

---------

Co-authored-by: V <vecsat@foxmail.com>
2026-08-29 21:20:55 +08:00
Loturs 2a6929f8f1 fix(linux-do): use current session for whoami (#2397)
* fix(linux-do): use current session for whoami

* test(linux-do): use generic identity fixture
2026-08-29 21:12:32 +08:00
Zhongyue Lin 75c85e5781 fix(xiaohongshu): retry once through a cooldown on risk-control soft blocks (#1825) (#2207)
Reading XHS note-detail pages (note / comments / download) back-to-back trips
velocity-based risk control: a soft block that redirects to
website-login/error?error_code=300017/300031 or renders "安全限制" /
"访问链接异常" (#1825, #962). Today the first block fails the command outright,
and an unattended loop keeps hammering — which escalates the risk state toward
the account-violation / ban path (#842, #677).

XHS soft blocks are frequently transient per-request challenges, so a single
reload after a real cooldown recovers many of them. Add a shared
`readXhsDetailPage` helper that navigates, settles, extracts, and — only on a
security block — waits one long randomized cooldown (8–18s) and reloads exactly
ONCE before surfacing SECURITY_BLOCK. The single-retry cap is structural (a
guarded `if`, no caller-tunable retry count) so it can never devolve into a
hammer loop; `retryOnBlock: false` opts into the previous fail-fast behavior.

note / comments / download now share this helper, replacing three copies of the
inline securityBlock detection. This does NOT throttle request velocity across
separate CLI invocations — that needs session-level pacing and is left as a
follow-up.

Adds offline tests for the helper (transient-block recovery, the structural
single-retry cap, fail-fast, custom settle bounds, non-block pass-through); the
existing note/comments/download command tests pass unchanged.
2026-08-29 21:07:57 +08:00
万物生腾·Omnisurge 1c66cc9eab fix(errors): duck-typing for cross-package CliError in toEnvelope (#2388)
* fix(errors): duck-typing for cross-package CliError in toEnvelope

Plugins resolve their own copy of @jackwener/opencli (own node_modules), so
 fails across package copies and every plugin error
degrades to code: UNKNOWN with the hint lost. Switch to shape-based detection
(code + message strings, optional hint) — real CliError instances behave
identically, plain Errors still map to UNKNOWN.

Also serializes error-like plain objects carrying code/message. Adds tests
for the cross-package shape, plain-object passthrough, and UNKNOWN fallback.

* fix(errors): require exitCode when duck-typing CliError

The shape check accepted any object with string code+message, which also
matches Node system errors (ENOENT, ECONNREFUSED, EACCES) and library
errors carrying a string code. Those would surface their errno as the
envelope code, widening the machine-readable contract callers switch on:

  before this commit: ENOENT -> code "ENOENT"
  intended/base:      ENOENT -> code "UNKNOWN"

CliError's constructor always assigns exitCode (defaulting to
GENERIC_ERROR) while Node system errors never do, so requiring a numeric
exitCode separates genuine cross-package CliError copies from foreign
errors without adding any new concept.

Tests: make the cross-package mock faithful by giving it exitCode, pin
the bare {code,message} object as UNKNOWN, and add an ENOENT regression.

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-29 20:59:06 +08:00
axxop 4e8109b6c8 fix: honor manual CDP endpoint for web adapters (#2148) 2026-08-29 20:56:03 +08:00
陈家名 25dece3f18 fix(pipeline): reject invalid concurrency limits (#2407) 2026-08-29 20:50:15 +08:00
jakevin 49907e53dc fix(dribbble): distinguish empty states from selector drift (#2423)
* fix(dribbble): distinguish empty states from drift

* fix(dribbble): handle promoted and nested list items

---------

Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-28 20:37:04 +08:00
Jamie 5767c07bd8 feat(dribbble): add browser adapter commands (#2403)
* feat(dribbble): add browser adapter commands

* fix(dribbble): improve adapter commands

* fix(dribbble): verify and complete adapter contracts

---------

Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-28 19:07:49 +08:00
Jiacheng 439945fd3d chore(pack): prune @mixmark-io/domino's vendored test suite on install (#2411)
turndown pulls in @mixmark-io/domino, which publishes its full test suite
to npm: 959 files / 7 MB, ~94% of the package's file count. Upstream has
been unmaintained since 2024 (mixmark-io/domino#2), so remove the test
directory in our postinstall instead. Runs before the CI early-return so
packaged app bundles (OpenCLIApp stages node_modules into Resources/)
shrink as well. Best-effort: resolution failure or a missing dir never
fails the install.

Co-authored-by: exe.dev user <exedev@koala-fife.exe.xyz>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 17:36:49 +08:00
Jiacheng c9fb444c0c chore(pack): exclude test files and fixtures from npm package (#2410)
The published tarball ships 601 compiled *.test.js files, ~100 *.test.d.ts,
stray *.test.ts sources, and clis/**/__fixtures__/ HTML snapshots — none of
which are used at runtime. Excluding them shrinks the package from 2340 to
1638 files and from 14.0 MB to 9.2 MB unpacked (tarball 3.1 MB -> 2.2 MB),
which noticeably speeds up npm install.

Verified with npm pack --dry-run against the extracted 1.8.7 tarball
contents: no .test.* or __fixtures__ entries remain.

Co-authored-by: exe.dev user <exedev@koala-fife.exe.xyz>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 17:36:00 +08:00
jakevin 90d507091c chore: refresh convention audit baselines (#2377) 2026-08-26 17:23:21 +08:00
jakevin 50902ffe6b fix(browser): preserve structured network captures (#2406)
* fix(browser): preserve structured network captures

* test(browser): cover direct CDP request capture

* fix(browser): redact credential-shaped request values

* fix(browser): redact bare CSRF request fields

---------

Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-26 17:14:39 +08:00
jakevin c2964f9572 fix(output): keep markdown rows intact for multi-line cells (#2375)
renderMarkdown escaped pipes but not newlines, so a cell containing a
line break terminated the table row and broke the markdown structure.
Render embedded newlines as <br>, matching how renderCsv already
handles multi-line values.
2026-08-26 17:09:36 +08:00
jakevin 6b3dffd398 feat(gmail): add browser-backed Gmail adapter (#2396)
* feat(gmail): add browser-backed adapter

* docs(gmail): document adapter commands

* refactor(gmail): remove unsupported write surface

* chore(gmail): split author workflow into dedicated PR

---------

Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-25 21:37:08 +08:00
jakevin b2fc86eb0f docs(skill): add evidence-first deep recon workflow (#2398)
Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-25 21:34:25 +08:00
jakevin 0a4a863b36 fix(linkedin): make sent invitations and thread snapshots accurate (#2395)
* fix(linkedin): use accurate invitation and thread sources

* fix(linkedin): preserve thread discovery budget

---------

Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-25 16:56:29 +08:00
jakevin 35002644d3 feat(jike): use structured APIs for search and notifications (#2394)
Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-25 16:05:42 +08:00
jakevin 64e6f0e39b fix(ctrip): read structured flight results (#2393)
Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-25 15:19:58 +08:00
Ao Liu cee4713918 fix(nowcoder): validate mixed post entities (#2218)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-25 00:56:06 +08:00
Yisi Liu c6f8c26b8b fix(trip): repair flight extraction — dropped font-black class emptied every row (#2385)
* fix(trip): read airport codes from leaf nodes, not the dropped font-black class

`buildFlightExtractJs` sourced the departure/arrival IATA codes from
`[class*="font-black"]`. Trip.com no longer emits that class, so `codes` was
always empty, the `codes.length < 2` guard dropped every card, and `flight` /
`flight-round` failed with EMPTY_RESULT on every route — including the
`LON -> NYC` example in the adapter docs.

The codes are still in the DOM as plain leaf nodes, and the extractor already
walks the leaves to collect times. Filter the codes out of that same scan and
drop the `font-black` dependency.

The unit fixture pinned the stale markup (`<div class="font-black_x">LGW</div>`),
which is why 114 adapter tests stayed green while the adapter returned nothing
against the live site. Replace it with markup captured from Trip.com in 2026-08
so the fixture can actually falsify the extractor.

Verified against the live site: 0/8 result cards parsed before, 8/8 after;
`opencli trip flight` now returns rows for PVG->LHR, SHA->LON and LON->NYC.

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

* fix(trip): mark later-day arrivals with a +N suffix

Overnight legs lost a day. The extractor built `arrivalTime` from the `HH:MM`
and `AM`/`PM` leaf nodes alone, so a China Southern SHA -> LGW leg departing
19:50 on 2026-09-07 and landing 06:30 on 2026-09-08 was reported as a plain
`6:30 AM`, indistinguishable from a same-day arrival.

Each card already carries `data-testid="flight-time-<ISO>"` on both legs. Diff
the date halves of those two anchors and append `+N` to the arrival time, which
is how Trip.com itself renders it (the card shows a literal `+1` beside the
arrival airport).

Same-day arrivals are unchanged, so the `columns` contract is untouched and only
the previously-wrong rows move.

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

* fix(trip): bind flight routes to endpoint anchors

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-25 00:46:25 +08:00
billow 5d102265e6 feat(pixiv): add novel commands (#2112)
* feat(pixiv): add novel commands

* feat(pixiv): add account bookmarks and novel downloads

* fix(pixiv): harden novel payload validation

* fix(pixiv): tighten auth proof and numeric args

* fix(pixiv): harden archive boundaries

* fix(pixiv): validate novel archive metadata

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 23:52:03 +08:00
jakevin 3e65c17621 chore: npm audit fix for dev-dependency advisories (#2378)
Clears the esbuild, nanoid, and postcss dev-chain advisories
(6 findings → 3). package-lock.json only; package.json ranges
untouched; production dependency audit remains at zero. The remaining
vite/launch-editor + vitepress findings have no in-range fix upstream
yet (dev-only, Windows-only vector) and are documented in the PR.
2026-08-24 23:34:32 +08:00
jakevin dc0a1ef1f5 fix(midjourney): poll job state before timeout (#2383) 2026-08-24 22:32:04 +08:00
jakevin d667222c90 fix(youtube): paginate search results (#2387)
Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-24 21:57:33 +08:00
jakevin 05b2830062 feat(youtube): paginate authenticated watch history (#2386)
Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-24 20:30:27 +08:00
Jared Su 14b9b9c595 feat(execution): support site session env default (#2221)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 20:17:00 +08:00
jakevin 9adf995c34 fix(jike): reject anonymous read sessions (#2384)
* fix(jike): reject anonymous read sessions

* fix(jike): preserve login polling retries

---------

Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-24 19:55:10 +08:00
Mingming Lou b4910ed6a8 feat(xiaoyuzhou): add playback history (#2260)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 19:29:55 +08:00
jakevin c559c996d6 fix(twitter): read current following follower counts (#2382)
Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-24 19:13:33 +08:00
jakevin 527b77ad57 fix: declare the Node >=20.18.1 floor that undici 7.x already requires (#2381)
undici 7.29.0 (pinned since the CVE-2026-13697 fix) requires Node
>=20.18.1, but engines still promised >=20.0.0. First-principles call:
Node 20 reached EOL in April 2026, so 20.0-20.17 users are on an
unpatched runtime either way — staying on the maintained undici 7.x
line and updating the declaration honestly beats downgrading to the
aging 6.x line for a contract that protects nobody. Updates engines,
the lock root mirror, and both README Node claims.
2026-08-24 19:05:47 +08:00
lingjiuu 4a1cc3a457 fix(notebooklm): migrate redirected domain session (#2228) 2026-08-24 18:49:37 +08:00
jakevin 56360604f1 feat(twitter): intercept complete followers timeline (#2380)
Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-24 18:32:15 +08:00
qiuyang bc7d20e6d0 feat(xiaohongshu): add deterministic search filters (#2276)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 18:22:21 +08:00
ele-yufo c16a2539fe feat(bilibili): add creator analytics adapter (#2268)
* feat(bilibili): add creator analytics adapter

* feat(bilibili): narrow creator stats contract

---------

Co-authored-by: yufo <yufo@MacBook-Pro.local>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 17:39:26 +08:00
Octopus 5cb60a8468 feat(minimax): add guarded music generation adapter (#2290)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 17:24:23 +08:00
Ninglin Du 48c151c6e3 feat(jira): add custom field support (#2289)
* feat(twitter): add custom field support

* change to comma fields

* remove ATLASSIAN_JIRA_FIELDS add --fields

* fix(jira): preserve selected field identity

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 16:27:10 +08:00
sleepy dog 3ec6eb086a fix(xiaohongshu): recover collapsed search targets safely (#2355)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 16:07:25 +08:00
kino 14d47c08bc feat(zhihu): export answers with local images (#2304)
* feat(zhihu): export answers with local images

* test(zhihu): cover answer image normalization

* fix(zhihu): harden answer export boundaries

* fix(zhihu): require question URL provenance

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 15:38:10 +08:00
kino 85e95870bb feat(zhihu): preserve comment reply threads (#2306)
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 14:51:40 +08:00
jakevin 2e373b612e chore(xiaohongshu): share evaluate unwrap helper (#2363)
Collapse seven local unwrapEvaluateResult copies (ask, feed, search,
creator-notes, delete-note, follow, unfollow) into a single
clis/xiaohongshu/shared.js export, and point rednote/search at it.

The three variants differed only in an Array.isArray guard and
condition order; arrays cannot carry the session/data envelope keys
across the CDP JSON boundary, so behavior is identical for every
reachable payload. The canonical copy keeps the guard.

Contract tests move to shared.test.js (strengthened with identity
assertions); the direct duplicates in ask.test.js / search.test.js are
deleted as subsumed. creator-notes __test__ drops the imported key.
2026-08-24 14:36:30 +08:00
jakevin c003a1b1c2 refactor(sinafinance): use rolling news API (#2365)
Co-authored-by: OpenCLI-sol <opencli-sol@users.noreply.github.com>
2026-08-24 09:02:41 +08:00
sleepy dog a0fbe90a7f fix(xiaohongshu): harvest search rows during scroll instead of first/last screen union (#2349)
* fix(xiaohongshu): harvest search rows during scroll instead of first/last screen union

The search results page is a virtualized masonry list: cards scrolled past
are evicted from the DOM. The previous flow extracted once, scrolled to the
bottom, then extracted again, so the result set was only the union of the
first and last screens -- capped near 20 rows regardless of --limit.

Harvest inside the scroll loop instead. A single page.evaluate now drives
the scroll and accumulates rows into a page-land Map keyed by note id, so
nothing is lost when a card is recycled. On a query that previously returned
34 rows, `--limit 100` now saturates at 100 across repeated runs.

Three related fixes ride along:

- Rows are merged rather than deduplicated on first sight. Masonry cards
  render in stages -- the link appears before the title -- so a row first
  seen with an empty title used to be cached empty and then dropped by the
  title filter. Empty fields are now backfilled on a later encounter,
  non-empty fields are never overwritten, and an unsigned /explore/ URL can
  be upgraded to an xsec_token-signed one but never downgraded. The likes
  emptiness check treats '0' as a placeholder because the extractor writes
  '0' for an unrendered count.

  For the same reason the target check counts only rows that already have a
  title. Counting raw discoveries let the loop stop the moment 100 cards
  were known, before the last screen had rendered its titles, and the filter
  then silently cut the output back to 84.

- "No new rows this round" is no longer, on its own, a reason to stop.
  It was observed firing at scrollTop=4500 of scrollHeight=6960 -- squarely
  mid-page, where Xiaohongshu had merely paused lazy-loading. Stopping now
  requires the plateau to coincide with either a real bottom or wedged
  scrolling, alongside the target/round/wall-clock bounds.

- Risk-control interstitials are detected and raise SECURITY_BLOCK instead
  of surfacing as a silently short result set indistinguishable from the
  truncation bug above.

Scrolling advances by a viewport-sized step rather than jumping to
document.body.scrollHeight, which would skip whole screens of cards. Round
and wall-clock budgets are derived from the already-validated --limit, so
no new CLI arguments and no cli-manifest.json change. Small --limit values
now finish sooner than before, since reaching the target ends the loop.

buildScrollUntilJs is untouched and buildSearchExtractJs keeps its signature
and semantics, so clis/rednote/search.js -- which imports both -- is
unaffected.

* fix(xiaohongshu): harden virtual search harvesting

* fix(xiaohongshu): validate harvested search rows

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 04:21:02 +08:00
Henry 144629740d feat(bilibili): add --top to fetch pinned comments (#2106)
* feat(bilibili): add --top to fetch pinned comments

The comments command already fetches /x/v2/reply/main, whose response
includes top_replies (置顶评论) alongside regular replies but discarded
them. Add a --top flag that returns only the pinned comments.

- --top reads data.top_replies (reusing formatReplyRow)
- --top is mutually exclusive with --parent (楼中楼 threads have no
  top_replies) and raises ArgumentError before any request
- generalize requireReplies to accept a key; absent top_replies is
  treated as empty, and an empty pinned list raises EmptyResultError

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

* fix(bilibili): harden pinned comment contract

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-08-24 04:01:50 +08:00
jakevin 5bb8f5ac9d fix(weixin): recover draft cover uploads (#2362) 2026-08-24 03:53:24 +08:00
OctoBored d2aec509e8 docs: point star history chart to a working endpoint (#2301)
The star history chart in the README was broken because its data source is no longer reliable under GitHub's current stargazer API restrictions. Update the chart in both README.md and README.zh-CN.md to use a working endpoint so the chart renders correctly again.

Co-authored-by: OctoBored <212877535+OctoBored@users.noreply.github.com>
2026-08-24 03:21:58 +08:00
jakevin d9b018fa0b fix: sync package-lock with undici 7.29.0 bump (#2361)
#2326 pinned undici 7.29.0 in package.json without regenerating the
lockfile, so npm ci fails with EUSAGE on main and every PR merge ref.
Regenerated with npm install --package-lock-only.
2026-08-24 03:04:33 +08:00
Anupam Mediratta 6d2b11e6ea fix: CVE-2026-13697 security vulnerability (#2326)
Automated dependency upgrade by OrbisAI Security
2026-08-24 02:26:21 +08:00
jakevin 87b60a3659 chore(release): bump version to 1.8.7 (#2359) v1.8.7 2026-08-23 23:58:41 +08:00