Commit Graph

381 Commits

Author SHA1 Message Date
Louie df8ca8d440 feat(xianyu): search 服务端价格区间 / 地区筛选 (mtop API) (#2013)
* feat(xianyu): search 支持服务端价格区间 / 地区筛选(--min-price/--max-price/--province/--city)

闲鱼 search 之前只有 query + limit。本次改成直接调 goofish 自己的搜索接口
`mtop.taobao.idlemtopsearch.pc.search`(沿用 item.js 里 window.lib.mtop.request
的用法,签名由页面自带,无需手搓),把价格区间和地区交给服务端筛选 + 分页,
而不是抓一屏 DOM 再在本地过滤。

筛选编码是在登录态浏览器里 hook window.lib.mtop.request、实际操作筛选面板抓到
真实请求后逆出来的,并逐条用接口返回 + 详情接口 publishCity 做了 ground-truth 校验:

- --min-price / --max-price → propValueStr.searchFilter = "priceRange:<min>,<max>;"
  (元;单边区间用 0 / 99999999 兜底)。实测 priceRange:100000,150000 返回价格
  全部落在 112999–149900。
- --province / --city → extraFilterValue = JSON({divisionList:[{province,city}],
  excludeMultiPlacesSellers:"0",extraDivision:""})。city 可单独使用(province 留空)。
  实测「广东」「北京」结果集完全不相交;--city 深圳 / 湛江 的结果用详情接口校验
  publishCity 全部命中。
- 任一筛选生效时 fromFilter=true。limit 最多 60,按需翻页(rowsPerPage=30)。

返回字段改为结构化解析(item_id / title / price / location / want / url),
鉴权与风控错误处理沿用 item.js 的成熟逻辑(AuthRequired / Empty / mtop-not-ready)。

已在登录态浏览器里真跑验证(深圳+10–20万、湛江、无筛选基线均正确),
clis/xianyu 45 个单测全绿,tsc --noEmit 通过,cli-manifest.json 同步重建。

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

* fix(xianyu): harden search filter result handling

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-27 18:28:47 +08:00
Bo Liu 1dd712d609 feat(juejin): add Juejin (掘金) read-only adapter (#2007)
* feat(juejin): add Juejin (掘金) read-only adapter

Two PUBLIC commands for the Juejin developer community: `recommend` (homepage feed) and `hot` (article ranking by category). Native fetch against api.juejin.cn; no browser, no auth. Category aliases (`backend`, `frontend`, `android`, `ios`, `ai`) resolve to Juejin's stable numeric ids.

Closes #1711

* fix(juejin): fail closed on API shape drift

* fix(juejin): expose recommendation cursor

* fix(juejin): classify response cursor drift

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-24 06:18:24 +08:00
Louie ee4820ef59 feat(adapters): add Chinese car-platform adapters — 懂车帝 / 瓜子二手车 / 汽车之家 (no-login) (#2009)
* feat(adapters): add 懂车帝 (dongchedi) + 瓜子二手车 (guazi) car adapters

Two no-login PUBLIC adapters for Chinese car platforms. Both read
server-rendered data (no cookies, no signature, no browser) and ship
pure parsers unit-tested against frozen real-data fixtures.

dongchedi (6 commands) — parses __NEXT_DATA__ SSR JSON:
  search  车系搜索 + 指导价/经销商价
  series  车系概览(品牌/价格/懂车分/销量排名/款型数)
  models  款型列表 + 价格
  specs   配置概览(尺寸/动力/四驱/悬挂/气囊)
  score   懂车分 8 维评分 + 同级对比
  koubei  车主口碑/评价正文
  (Dongchedi's /motor XHR APIs are ByteDance-signature gated; the SSR
   pages expose the same data unsigned, so the adapter reads those.)

guazi (2 commands) — parses m.guazi.com mobile SSR HTML:
  browse  分城市在售二手车列表(售价/里程/年份)
  car     车源详情(售价/上牌/里程/过户/配置/车况)
  (Desktop www.guazi.com is signature-locked; mobile SSR is open. Deep
   pagination/filtering uses the signed API and is intentionally omitted.)

Gates green: tsc, doc-coverage --strict, silent-column-drop (new=0),
typed-error-lint (no new), 24 adapter tests passing.

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

* feat(adapters): add 汽车之家 (autohome) — brand catalog + 口碑 ratings

Third no-login PUBLIC car adapter (search by brand, not free text).

autohome (2 commands):
  brand  按品牌列出全部车系 + 厂商指导价(grade/carhtml/<INITIAL>.html,
         中文品牌名→拼音首字母目录页,DL 块按品牌定位)
  score  车系口碑评分:总分 + 各维度 + 故障率PPH + 竞品对比
         (k.autohome.com.cn/<id> 的 __NEXT_DATA__.baseData,免登录免签名)

Deliberately omitted (would be silently-wrong without a browser running
Autohome's signing/anti-scrape code): free-text keyword search (signature
gated) and full per-trim 参数配置 (rotating CSS font-glyph obfuscation).
Use dongchedi search/specs/koubei for those. Documented in the adapter doc.

Gates green: tsc, doc-coverage --strict (170/170), silent-column-drop
(new=0), typed-error-lint (no new); 31 adapter tests passing across the
three car adapters.

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

* fix(car-adapters): fail closed on parser drift

* fix(guazi): fail closed on empty SSR listings

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-24 06:07:01 +08:00
Ocean 55b053a6f6 feat(bilibili): 给 video / subtitle / download 加 --page 支持分P选集 (#2003)
* feat(bilibili): 给 video / subtitle / download 加 --page 支持分P选集

多P视频(视频选集)此前 video / subtitle / download 都丢弃 `?p=` 参数、永远解析到 P1 的 cid / 标题 / 字幕 / 视频流。新增可选 `--page N`:

- **video**:从 view API 的 `data.pages` 取第 N 集,`title` 换成分集标题,额外透出 `page` / `cid` / `series_title` 字段;缺省不加,保持整集旧行为
- **subtitle**:用 `pages[N-1].cid` 取代默认 P1 cid,拿该集字幕
- **download**:拼 `?p=N` 给 yt-dlp 原生定位该集
- **utils**:共享 `parsePageArg` / `selectVideoPart`(越界结构化报错)

向后兼容:不传 `--page` 时三命令行为完全不变。新增 9 个测试,bilibili 全量 109 通过;`tsc --noEmit` 干净;`check:silent-column-drop` new=0。

* fix(bilibili): harden multipart page selection

---------

Co-authored-by: ml-scout <ml-scout@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-24 05:47:25 +08:00
leo1in88 7af50abd04 feat(github): add github trending adapter (#1953)
* feat(github): add `github trending` adapter

Add a PUBLIC adapter that lists repositories from
https://github.com/trending — the trending view is a public HTML page with
no official REST API, so the data was previously unreachable through opencli.

The adapter fetches the page server-side (no browser, no auth) and parses
each repo's full name, description, primary language, total stars, forks,
and stars gained in the period.

Flags:
- `--since`    daily | weekly | monthly (default daily)
- `--language` filter by language slug, e.g. python, rust, "c++"
- `--limit`    1..25 (GitHub lists at most 25)

Typed errors: ArgumentError for bad --since / --limit, CommandExecutionError
on request/HTTP failure, EmptyResultError when the page yields no repos.

Tests cover parsing (stars/forks/language/description/url, missing language),
limit truncation, language+since URL building, argument validation, the
empty-result path, and non-ok HTTP.

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

* refactor(github-trending): rename to standalone `github-trending repos`

Move the trending scraper out of the `github` site namespace into a
dedicated `github-trending` adapter to avoid confusion with the bundled
`gh` external CLI. Command is now `opencli github-trending repos`
(site=github-trending, name=repos), leaving room for a future
`developers` subcommand. The `github` site retains only login/whoami.

Regenerated cli-manifest.json; 8 fixture tests + typecheck pass.

* fix(github-trending): fail closed on parser drift

---------

Co-authored-by: minh <claude@ttfy.cc>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-22 21:27:57 +08:00
songsp77 e5bbce4fe2 fix(12306): handle endpoint rotation via 302 redirects (#1999)
* fix(12306): handle endpoint rotation via 302 redirects

Three related bugs caused the trains command to fail with
   "non-JSON body" when 12306 rotated its query endpoint:

   1. Node.js fetch defaulted to redirect: 'follow', silently
      following 12306's HTTP 302 to error.html and returning
      HTML instead of JSON.

   2. The 302 response body contained rotation info
      (e.g. {"c_url":"leftTicket/queryB"}) but was never read
      because resp.status === 302 triggered continue before
      consuming the body.

   3. Mutating QUERY_ENDPOINTS via unshift() during a for...of
      loop caused infinite iteration when the new endpoint was
      skipped by the iterator.

   Changes:
   - Set redirect: 'manual' on fetch to capture 302 responses
   - Parse c_url from 302 body and enqueue the rotated endpoint
   - Replace for...of with a while queue + Set-based dedup to
     safely handle dynamic endpoint discovery

   Fixes the trains command against the current 12306 wire
   protocol (queryG → 302 → queryB rotation).

* fix(12306): bound leftTicket endpoint rotation

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-22 18:00:13 +08:00
Mingming Lou 5848754943 fix(gemini/history): expand collapsed Recents sidebar before extraction (#1962)
* fix(gemini/history): expand collapsed Recents sidebar before extraction

opencli gemini history returned EMPTY_RESULT ("No conversation links were
visible in the sidebar") even when logged in, because Gemini collapses the
sidebar "最近"/Recents section by default — the /app/<id> conversation
anchors are absent from the DOM until that section is expanded.

getGeminiConversationList now retries extraction (up to 3 times) and, while
empty, clicks the sidebar-open button plus the Recents expand/collapse toggle
(matched by aria-label in both zh and en) before waiting for the React
sidebar to render and re-extracting.

Verified: opencli gemini history --limit 5 returns 5 conversations (2.5s);
opencli gemini detail <id> reads full conversation content.

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

Generated on: cmcc-i5

Generated by: home-cc

* test(gemini): cover collapsed recents history extraction

* fix(gemini): avoid collapsing expanded recents

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-22 16:53:42 +08:00
pi-dal 19e64da565 feat(chatgpt): add project commands (#1992)
* feat(chatgpt): add project commands

Add ChatGPT project management adapters for listing visible projects and uploading local files into project knowledge.

The new project-list command extracts project links from stable sidebar anchors first, with a React Fiber fallback for sidebar builds that do not expose hrefs. The project-file-add command uploads through the project knowledge flow, validates local files before browser interaction, and waits for filename confirmation before reporting success.

For the current ChatGPT project UI, project knowledge uploads live behind the Sources tab rather than the older Add files dialog. The upload helper now prefers that Sources surface, avoids mistaking the chat composer plus button for project knowledge upload, and dispatches a browser-like pointer/mouse sequence so Radix-powered tabs activate reliably in live sessions before setting the source file input.

This intentionally avoids command-level system proxy mutation. Users who need a proxy should configure the browser or network environment outside this adapter, rather than letting a single command toggle OS proxy settings.

Also update the generated CLI manifest and focused adapter tests for command registration, argument contracts, project id parsing, project link extraction, upload confirmation, live Sources-tab upload behavior, and failure wrapping.

Validation:
- pnpm exec tsx src/main.ts chatgpt project-list -f json --trace retain-on-failure --window foreground --keep-tab true (live authenticated local session; returned visible projects)
- pnpm exec tsx src/main.ts chatgpt project-file-add /tmp/opencli-chatgpt-project-upload-validation-pointer-20260621215147.txt --id 6a1791df8fa88191afb5a016ce1f497e -f json --trace retain-on-failure --window foreground --keep-tab true (live authenticated local session; uploaded one text file to project knowledge)
- pnpm exec vitest run --project adapter clis/chatgpt/commands.test.js clis/chatgpt/envelope.test.js clis/chatgpt/image.test.js clis/chatgpt/model.test.js clis/chatgpt/utils.test.js
- pnpm exec tsc --noEmit
- pnpm run build-manifest

* feat(chatgpt): support project chat routing

Add --project to chatgpt ask/send so messages can start a new chat inside a specified ChatGPT project. Reject --project with --conversation before navigation, and parse project-scoped /g/g-p-.../c/<id> conversation URLs so ask can report the created conversation id.

Validation:

- pnpm exec vitest run --project adapter clis/chatgpt/commands.test.js clis/chatgpt/envelope.test.js clis/chatgpt/image.test.js clis/chatgpt/model.test.js clis/chatgpt/utils.test.js

- pnpm exec tsc --noEmit

- pnpm run build-manifest

* feat(chatgpt): extend project routing

Add --project routing to chatgpt new, image, and model so project-scoped work is available beyond ask/send. New and image open the specified project before preparing the composer; model opens the project before switching the intelligence level.

Validation:

- pnpm exec vitest run --project adapter clis/chatgpt/commands.test.js clis/chatgpt/envelope.test.js clis/chatgpt/image.test.js clis/chatgpt/model.test.js clis/chatgpt/utils.test.js

- pnpm exec tsc --noEmit

- pnpm run build-manifest

- pnpm exec tsx src/main.ts chatgpt new --help / image --help / model --help

* fix(chatgpt): harden project command boundaries

* fix(chatgpt): require stable project ids

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-22 16:26:59 +08:00
Bo Liu acfab26a52 feat(semanticscholar): add Semantic Scholar academic graph adapter (#1994)
* feat(semanticscholar): add Semantic Scholar academic graph adapter

Native fetch against api.semanticscholar.org. Four read commands cover paper detail (with influentialCitationCount and tldr), citation list, AI-curated recommendations, and free-text search. Optional SEMANTIC_SCHOLAR_API_KEY env var lifts the anonymous rate limit.

Closes #1993

* fix(semanticscholar): fail closed on malformed paper rows

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-22 16:25:11 +08:00
jakevin 6557622156 fix(adapters): surface empty results as EmptyResultError, not sentinel rows (#1981)
* fix(adapters): surface empty results as EmptyResultError, not sentinel rows

Four adapters returned a fabricated row (exit 0) on the not-found/empty
path instead of throwing a typed error, so an agent reading the exit code
or rows could not tell "no results" from success — the framework assigns
EMPTY_RESULT its own exit code precisely so this is detectable:

- maimai/search-talents: returned [{error, query}] on zero candidates.
  Worse, `error`/`query` are not in `columns`, so the message was dropped
  by column projection — the user saw an empty/garbage row, never the
  reason. Now throws EmptyResultError (fixes the silent-column-drop too).
- discord-app/search: returned a synthetic "System" row on no matches.
- pixiv/download: returned a failed sentinel when an illust had 0 pages
  (the file already throws typed errors elsewhere). Test updated to
  assert the throw.
- xiaohongshu/download: returned a failed sentinel when a note had no
  media (the file already throws CliError for the security-block branch).

Per-image partial-failure status rows in the download loops are left
as-is (legitimate batch reporting). Auth-path conversions
(tiktok/facebook string-prefix + maimai in-page throw) are a separate
follow-up since they involve in-page-throw handling.

Adapter suites green; typed-error-lint and silent-column-drop audits
report no new violations.

* fix(adapters): fail closed on malformed empty payloads

* fix(audit): bump undici in lockfile

* fix(pixiv): fail closed on missing pages payload

---------

Co-authored-by: codex-mini0 <codex-mini0@slock.local>
2026-06-21 15:44:19 +08:00
jakevin de2ef4ca56 fix(qwen): anchor waitForAnswer to stop returning the previous answer (#1982)
* fix(qwen): anchor waitForAnswer on pre-send turn to stop returning the previous answer

qwen `waitForAnswer` took no baseline and never skipped stale turns — the
`seenAssistantId` variable was assigned but never read (dead code). Since
`getMessageBubbles` returns every turn including the already-complete
previous answer, a follow-up `qwen ask` into an existing conversation
(persistent site session, no --new) saw that prior answer on the first
polls. It was already stable, so the stability check returned it as if it
were the reply to the new prompt — silently wrong output, no error.

Mirror grok's reference fix: capture the last assistant turn's id before
sending (`getBaselineLastAssistantId` in ask.js) and `continue` in
waitForAnswer while the latest assistant id equals that baseline. Removes
the dead `seenAssistantId`.

Tests: getBaselineLastAssistantId helper (mirrors grok), plus a direct
waitForAnswer test asserting the pre-send turn is skipped (times out
rather than returning the stale answer) — reverse-validated.

* fix(qwen): bind answer wait to sent prompt turn

* fix(audit): bump undici in lockfile

* fix(qwen): fail closed when answer anchor is not visible

---------

Co-authored-by: codex-mini0 <codex-mini0@slock.local>
2026-06-21 15:18:36 +08:00
jakevin 001abdf481 fix(deepseek): throw TimeoutError on no-reply instead of a silent sentinel row (#1983)
* fix(deepseek): throw TimeoutError on no-reply instead of a silent sentinel row

deepseek `ask` returned `[{ response: '[NO RESPONSE] No reply within Ns.' }]`
(exit 0) on both the normal and --file paths when no reply arrived — the
same sentinel-row anti-pattern fixed for other adapters in #1981. Every
sibling chat adapter throws a typed error here (claude EmptyResultError,
grok/qwen TimeoutError), so an agent branching on exit code / error type
saw "success" and consumed the literal `[NO RESPONSE] ...` string as if it
were the model's answer.

Both paths now throw TimeoutError (exit code TIMEOUT), making the failure
observable. Tests cover both the normal and --file timeout paths;
reverse-validated.

Note: the deeper root cause — `sendMessage` in utils.js can silently
no-op server-side (execCommand + fixed 800ms, the exact pattern send.js
warns against) — is a separate follow-up: it needs the proven
nativeType + aria-disabled-poll path (extracted as a shared helper with
send.js) plus live smoke against the site, which can't be verified
offline. This PR at least converts that silent no-op into a loud timeout.

* fix(audit): bump undici in lockfile

---------

Co-authored-by: codex-mini0 <codex-mini0@slock.local>
2026-06-21 14:52:09 +08:00
jakevin 1d87cde513 feat(zhihu): add user/answers/articles/following/followers/pins read commands (#1986)
* feat(zhihu): add user profile + answers/articles/following/followers/pins read commands

Enriches Zhihu read coverage with 6 new /api/v4 commands (all live-verified
against a logged-in account):

- `zhihu user <user>`        — profile (follower/following/answer/article/voteup counts)
- `zhihu user-answers <user>`— a user's answers (votes/comments/url)
- `zhihu user-articles <user>`— a user's articles (专栏)
- `zhihu following <user>`   — followees
- `zhihu followers <user>`   — followers
- `zhihu pins <user>`        — 想法 (short posts)

Each accepts a url_token, `user:<slug>`, or people URL (shared `parseZhihuUser`).
List commands share a `fetchZhihuList` paginator (cookie fetch + paging.next +
typed errors: AuthRequiredError on 401/403, NOT_FOUND on 404, FETCH_ERROR else).

Tests: 6 new suites (happy path + auth/limit edges). Full suite 5551 passed;
typed-error-lint and silent-column-drop new=0.

* fix(zhihu): harden user read commands

* fix(zhihu): keep user pagination on same endpoint
2026-06-21 14:48:35 +08:00
Ocean bcd9c124c4 perf(reddit): 删除冗余的首页预导航步,每命令双导航→单导航 (#1987)
reddit 的 popular / subreddit / search / read 命令都先用框架 navigateBefore(domain=reddit.com → 302 到 www.reddit.com)把页面带到 reddit origin,**又**额外硬编码一步导航到 `https://www.reddit.com` 首页,然后才发相对 fetch(`/r/popular.json`、`/comments/<id>.json` 等)。两次导航到同一站点首页纯属冗余 —— 框架那次已经够让相对 fetch 工作。

实测:一次串行抓取仅 reddit 就因此重复导航首页约 24 次(每次 6-15s)。

## 改动

- 删掉 popular / subreddit / search 的 pipeline `{ navigate: 'https://www.reddit.com' }` 步;
- 删掉 read.js func 里的 `await page.goto('https://www.reddit.com')`;
- 框架 navigateBefore 仍把页面带到 reddit origin,相对 fetch 照常工作。

## 配套测试调整

- `read.test.js`:原断言「导航到首页」改为**反向回归保护**(`page.goto` 不再以首页 URL 被调用),锁住本次优化不被回退;
- `popular / search / subreddit.test.js`:删掉一个 pipeline 步后 evaluate / map 的索引各前移一位(`[1]→[0]`、`[2]→[1]`),同步更新断言。

## 验证

- 逐一实测 `opencli reddit popular / subreddit / search / read` 均正常返回数据;
- reddit 全量适配器测试 88 通过;`tsc --noEmit` 干净;`npm run build` 干净。

Co-authored-by: ml-scout <ml-scout@anthropic.com>
2026-06-21 14:28:19 +08:00
cypggs 7879b774e9 feat(kimi): add usage adapter (#1985)
* feat(kimi): add code-console adapter

Read Kimi Code console usage cards: weekly quota, rate limit,
membership, and model permission.

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

* refactor(kimi): rename code-console adapter to usage

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

* refactor(kimi): remove code-console.js after renaming to usage

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

* fix(kimi): register and harden usage adapter

* fix(kimi): fail closed on missing usage cards

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: codex-mini0 <codex-mini0@slock.local>
Co-authored-by: jackwener <jakevingoo@gmail.com>
2026-06-20 23:57:22 +08:00
Singh 84dde77359 fix(linkedin): scope connect invites to profile owner
Final head ff120c1c4e.

Contract: linkedin connect remains a write command and dry-run by default. --profile-url must be an exact LinkedIn profile URL, and --expected-name is required with strict actual profile-name match. Connect availability, More availability, and invite anchors are now scoped to the owner top-card / name-bearing owner action controls, so sidebar or People-also-viewed Connect/More buttons cannot make dry-run falsely connectable or provide the invite URL for the target profile. Top-level anchor Connect accepts only trusted /preload/custom-invite/ links from owner action scope. Button/More path opens Connect only from an owner-named action bar and fails closed when owner controls cannot be proven. Delivery still requires sent-invitations verification for sent_verified; unverified sends return send_unverified rather than a verified success.

Validation: lead+aux content green. Remote statusCheckRollup is empty, merged under the standing no-check override after final poll confirmed OPEN/non-draft, head unchanged, and MERGEABLE/UNSTABLE with no conflict/dirty/content blocker. Local reviewer validation covered focused clis/linkedin/connect.test.js 1 file/21, full LinkedIn adapter 24 files/213, typecheck, build manifest 1225, docs-build, node --check touched LinkedIn files plus dist/src/main.js, typed-error-lint and silent-column-drop no new, listing-id advisory unchanged, and diff-check clean.
2026-06-19 04:37:41 +08:00
Bo Liu d2abdcc4b7 feat(archive): add Internet Archive read-only adapter
Final head 5f5661c270.

Contract: adds a public read-only Internet Archive adapter with archive search, item, wayback, and snapshots commands. All commands are read access, browser:false, with no login, write, upload, or browser UI side effects. Source of truth is Internet Archive Advanced Search response.docs, /metadata/<identifier>, Wayback available closest snapshot, and CDX JSON header/rows. Fail-closed boundaries: search requires response.docs array, true empty maps to EmptyResultError, rows require stable identifier and numeric downloads; item requires metadata.identifier equals requested identifier and files array, while missing metadata/404 remains empty; wayback distinguishes no closest snapshot true empty from available:true missing URL or 14-digit timestamp malformed CommandExecutionError; snapshots requires top-level CDX array, header array, required columns, and per-row timestamp/original/statuscode/mimetype cells, with malformed shapes typed CommandExecutionError rather than empty snapshot URLs or empty status columns.

Validation: lead+aux final green. GitHub final gate OPEN/non-draft/CLEAN, required checks SUCCESS, adapter/smoke skipped. Local reviewer validation covered current-main merge-tree clean, targeted/full clis/archive 1 file/18 tests, typecheck, build manifest 1231, docs-build, doc coverage 165/165, prod audit clean, node --check touched Archive files/tests, diff-check, typed-error-lint and silent-column-drop no new.
2026-06-19 04:28:56 +08:00
Zhongyue Lin e416f5f071 fix(gemini): match Traditional Chinese send label
Final head 1eb03c8581.

Contract: Gemini composer submit-button detection expands the existing submit label matcher from send/发送/submit/提交 to also include Traditional Chinese 傳送. The change is scoped to clis/gemini/utils.js and tests. Button search remains constrained to the composer-near root, requires visible and enabled candidates, excludes main menu, microphone, upload, mode/tools/settings/new chat and other non-submit controls, and keeps the existing vertical-distance/right-side small-button scoring. If no valid button is found, send/ask still fall back to Enter. No change to Gemini send/ask submit confirmation semantics, command surface, docs, or manifest behavior.

Validation: lead+aux final green. GitHub final gate OPEN/non-draft/CLEAN, required checks SUCCESS, adapter/smoke skipped. Local reviewer validation covered focused clis/gemini/utils.test.js 1 file/27, full Gemini adapter 6 files/91, typecheck, build manifest 1227, docs-build, node --check touched Gemini files plus dist/src/main.js, typed-error-lint and silent-column-drop no new, listing-id advisory unchanged, and diff-check clean.
2026-06-19 04:19:14 +08:00
Marvin efd3c03d49 fix(chatgpt): support current intelligence levels
Final head 2279aca321.

Contract: chatgpt model keeps the existing write surface and supports instant, medium, high, extra-high, and pro intelligence levels; thinking remains a backward-compatible alias for high. Unsupported or unknown requested levels fail upfront with ArgumentError. The selection path requires a logged-in ChatGPT composer and native click. The model selector prefers stable test id / exact visible option text, covering current English Instant/Medium/High/Extra High/Pro and Chinese 极速/均衡/高级/超高/专业 labels. Unknown localization falls back to order only when composer-intelligence-picker-content exists and exactly five visible menuitemradio options are present; otherwise it typed-fails with CommandExecutionError instead of treating an ordinary menu or drifted DOM as success. Postcondition re-reads current selector/test id after click; when label recognition is unavailable, it reopens the picker and verifies the target checked index in the five-option intelligence picker. High vs Extra High matching uses longest/exact ordering to avoid substring false success. No ask/send/read/image/history surface changes.

Validation: lead+aux content green. Remote statusCheckRollup is empty, merged under the standing no-check override after final poll confirmed OPEN/non-draft, head unchanged, and MERGEABLE/UNSTABLE with no conflict/dirty/content blocker. Local reviewer validation covered current-main replay clean, focused ChatGPT utils+commands tests 77/77, full ChatGPT 6 files/118, typecheck, build manifest 1227, docs-build, doc coverage 164/164, prod audit clean, node --check touched files/tests, diff-check, typed-error-lint and silent-column-drop no new, and merge-tree clean.
2026-06-17 22:04:30 +08:00
AstroHan 8b765236fb feat(xiaohongshu): expose ask source metadata
Final head 53686e6a62.

Contract: xiaohongshu ask remains a browser-backed write command. Answer success still requires the same-send message_id/conversation_id plus a finished non-empty answer. Source identity/url/xsec_token are still trusted only from a 24-hex note id, xhsdiscover://item/<id>, or trusted XHS note URL. New source metadata is a minimal optional enrichment: note_type, user_id, and published_at are forwarded from the 点点 source payload and omitted when empty; like_count is parsed only from non-negative safe integers, strict decimal compact counts with 万/w/W/亿 and optional +, or legal pure digit/thousands strings. Malformed values such as 1e2, 0x10, 1..2万, bad comma grouping, negatives, and decimal numbers are omitted rather than coerced into successful counts. No extra note/detail round-trip or new write surface.

Validation: lead+aux final green. GitHub final gate OPEN/non-draft/CLEAN, required checks SUCCESS, adapter/smoke skipped. Local reviewer validation covered focused clis/xiaohongshu/ask.test.js 1 file/15, full XHS adapter 20 files/270, typecheck, build manifest 1227, docs-build, node --check ask/touched tests plus dist/src/main.js, diff-check clean, and local merge-tree clean.
2026-06-17 22:01:01 +08:00
Zhongyue Lin ac1684a691 feat(smzdm): expose search metrics and update time
Final head e3da72afcb.

Contract: SMZDM search remains a read-only listing command and now enriches rows with updated_at, zhi_count, buzhi_count, favorite_count, and comments while preserving a complete column set with stable defaults. Argument validation for --limit is strict and pre-navigation: only integer numbers or decimal digit strings are accepted, constrained to 1..100; exponent, hex, blank/coercive forms are rejected. Browser Bridge {session,data} envelopes are unwrapped at the boundary. Non-array extraction payloads typed-fail with CommandExecutionError instead of silently returning an empty list. Result URLs are canonicalized in the browser script and only trusted https://www.smzdm.com, https://post.smzdm.com, or trusted relative paths are kept; off-domain/non-https/malformed rows are skipped. Compact metrics such as 1.2万 and k/K counts normalize correctly. No new command/API abstraction or write behavior.

Validation: lead+aux final green. GitHub final gate OPEN/non-draft/CLEAN, required checks SUCCESS, adapter/smoke skipped. Local reviewer validation covered focused clis/smzdm/search.test.js 1 file/8, typecheck, build manifest 1227, docs-build, node --check touched SMZDM files plus dist/src/main.js, typed-error-lint and silent-column-drop no new, listing-id advisory unchanged, and diff-check clean.
2026-06-17 21:39:16 +08:00
Henry a28390d36f feat(xiaohongshu): support text-image publishing
Final head d41552312e.

Contract: xiaohongshu publish gains text-image publishing behind --card-text. --card-text/--images require at least one content source. Normal image suffix/path validation and text-image gif append are pre-navigation ArgumentError. Text-image flow enters 文字配图, writes/verifies each card, waits for a new active empty card before multi-card input, generates previews, then Next enters the standard editor before title/body/topics. Explicit --card-style must resolve/click or typed-fail with CommandExecutionError, with no silent fallback to 基础. After Generate/Next and image append, visible media count postconditions must prove cards and appended images landed. Publish/draft final success requires a success marker or leaving the publish page; warning-like text cannot fake success.

Validation: lead+aux final green on d4155231. GitHub final gate OPEN/non-draft/CLEAN with required checks SUCCESS and adapter/smoke skipped. Local reviewer validation covered publish tests 31/31, full Xiaohongshu 17 files/246 tests, typecheck, build manifest 1225, docs-build, doc coverage 164/164, production audit clean, node --check, diff-check, typed-error-lint/silent-column-drop no new, and merge-tree clean.
2026-06-17 01:54:14 +08:00
G6-CSE-2246 0a239af512 feat(xiaohongshu): add saved and liked collection scrapers
Adds Xiaohongshu saved and liked collection read commands.\n\nFinal review contract:\n- saved/liked are read-only current-user collection scrapers; no write, unlike, or favorite side effects.\n- --limit validates strictly in 1..100 before browser navigation.\n- Collection page location is read back after goto and after each scroll before trusting captures/DOM.\n- Location must be exactly https://www.xiaohongshu.com/user/profile/<resolvedUserId>; /login or login-wall text maps to AuthRequiredError; other host/path/profile drift maps to CommandExecutionError.\n- Browser Bridge {session,data} envelopes are unwrapped; malformed location, non-array intercepted requests, and non-array DOM extraction fail closed.\n- API/DOM notes require stable note id and xsec_token; output URLs round-trip to note/detail.\n- Auth/private/empty/malformed/API/parser/selector drift typed boundaries are preserved.\n\nValidation:\n- Lead and aux final green on ef12a197.\n- Final GitHub poll: open, non-draft, head ef12a197, statusCheckRollup empty only; merged under standing no-check override because content review is green and no conflict/dirty/content blocker remains.\n- Focused collection/saved/liked tests 3 files / 19.\n- Full XHS adapter 20 files / 253.\n- typecheck, build/manifest 1227, docs-build.\n- node --check touched collection files + dist/src/main.js.\n- typed-error-lint and silent-column-drop no new issues; listing-id advisory 13; diff-check clean.
2026-06-17 01:50:15 +08:00
Ocean 1ff4de3119 fix(xiaohongshu/user): handle login wall during hydration
Handles Xiaohongshu user login walls and hydration races without weakening read contracts.\n\nFinal review contract:\n- xiaohongshu/user remains read-only.\n- Output columns id/title/type/likes/url are unchanged.\n- Profile note URLs remain bound by profile user id + note id + xsec token.\n- Hydration retry waits only when initial user store/notes are not populated and the page is not a login wall.\n- First read with existing notes does not wait unnecessarily.\n- Real empty/private/deleted users still exhaust retry and return EMPTY_RESULT.\n- Initial profile login wall and scroll-continuation login wall both raise AuthRequiredError.\n- Login wall does not degrade into malformed snapshot, generic CommandExecutionError, or empty success.\n- Non-object snapshots, missing store, missing notes, or non-array noteGroups still fail closed with CommandExecutionError.\n- Browser evaluate errors are not swallowed.\n\nValidation:\n- Lead and aux final green on 0cf2dc27.\n- Final GitHub poll MERGEABLE/CLEAN; required checks SUCCESS; adapter/smoke skipped.\n- focused user/user-helpers/rednote tests 3 files / 44.\n- full XHS adapter 17 files / 232.\n- typecheck, build/manifest 1222, docs:build.\n- node --check touched user files + dist/src/main.js.\n- typed-error-lint and silent-column-drop no new issues; diff-check clean; current-main merge-tree clean.
2026-06-15 18:08:39 +08:00
Ocean 98a2e4f91d feat(bilibili,youtube): expose paid video metadata
Exposes paid/member metadata for Bilibili and YouTube video reads.\n\nFinal review contract:\n- Scope remains Bilibili/Youtube video read-only metadata; no download, write, or navigation contract changes.\n- Bilibili paid source is /x/web-interface/view data.rights plus upower and redirect_url fields.\n- Bilibili view payload is unwrapped from Browser Bridge {session,data} before reading paid fields.\n- Missing or type-drifted Bilibili paid metadata fails closed with CommandExecutionError rather than defaulting to free/non-member.\n- YouTube source is watch bootstrap playabilityStatus plus locale-independent BADGE_STYLE_TYPE_MEMBERS_ONLY, after Browser Bridge unwrap.\n- YouTube requires string playabilityStatus/playabilityReason and boolean membersOnly; malformed payload fails closed.\n- Existing row identity and output contract are preserved with metadata additions only.\n\nValidation:\n- Lead and aux final green on de431644.\n- Final GitHub poll MERGEABLE/CLEAN; required checks SUCCESS; adapter/smoke skipped.\n- current-main merge-tree clean.\n- Targeted Bilibili/Youtube video tests 2 files / 18.\n- Full Bilibili + YouTube adapters 15 files / 144.\n- build manifest 1222, typecheck, docs-build, doc coverage 164/164.\n- node --check touched files/tests, diff-check clean.\n- typed-error-lint and silent-column-drop no new issues.
2026-06-15 17:59:26 +08:00
Ocean 237741afd5 fix(bilibili/download): block paid content before download
Adds a paid-content precheck before Bilibili download side effects.\n\nFinal review contract:\n- bilibili/download remains a download command, but checks /x/web-interface/view before any downloadMedia or yt-dlp call.\n- rights.pay covers VIP/paid OGV; rights.ugc_pay or rights.arc_pay covers UGC single-purchase paid videos; is_upower_exclusive covers charging-exclusive content.\n- Paid hits throw structured PAID_CONTENT / NOPERM before download side effects.\n- VIP content is allowed only when /x/web-interface/nav returns vipStatus === 1.\n- --force fully skips precheck for already-entitled users or cases where cheap entitlement probing is insufficient.\n- Transport reject or non-zero view API code preserves old compatibility and does not block, but successful code:0 envelopes must include object data and object data.rights or fail closed with CommandExecutionError.\n- Free video path, BVID/URL identity, title/cookie/output path, quality format, and yt-dlp missing-result semantics are preserved.\n\nValidation:\n- Lead and aux final green on a7297072.\n- Final GitHub poll MERGEABLE/CLEAN; required checks SUCCESS; adapter/smoke skipped.\n- Focused clis/bilibili/download.test.js 1 file / 7.\n- Full Bilibili adapter 10 files / 94.\n- typecheck, build/manifest 1222, docs:build, node --check touched download files + dist/src/main.js.\n- typed-error-lint and silent-column-drop no new issues; diff-check clean; current-main merge-tree clean.
2026-06-15 17:52:37 +08:00
AlexYue b85ab89938 feat(discord-app): add targeted read navigation
Adds targeted Discord desktop-app read/navigation commands with fail-closed identity checks.\n\nFinal review contract:\n- Surface remains read/navigation only: goto, channels, servers, threads, read, and thread-read; no Discord write command is introduced.\n- Browser Bridge evaluate results are unwrapped and shape-guarded at the Node boundary for route state, list rows, messages, and thread lists.\n- True empty list/read/thread-read results map to EmptyResultError; malformed rows or browser output map to CommandExecutionError.\n- Targeted read and thread-read verify message rows bind to the requested channel/thread via channel_id; stale, wrong-channel, parent-channel, or missing identity rows typed-fail.\n- List rows require stable identities: channels require Channel/guild_id/channel_id/url, servers require Server/guild_id/url, threads require Thread/guild_id/channel_id/thread_id/url.\n\nValidation:\n- Lead final and aux final green on e84a7d85.\n- merge-tree clean against base 8ed8ca26.\n- Discord app tests 19/19.\n- build manifest 1225, typecheck, docs-build, doc coverage 164/164, node --check touched files/tests, diff-check.\n- typed-error-lint and silent-column-drop no new issues.\n- GitHub required checks SUCCESS; adapter/smoke skipped.
2026-06-15 17:06:30 +08:00
Ocean 8ed8ca26dd feat(twitter): expose media poster URLs
feat(twitter): expose media poster URLs

Co-authored-by: codex-mini1 <codex-mini1@users.noreply.github.com>
Co-authored-by: First-principles-1 <first-principles-1@users.noreply.github.com>
2026-06-14 04:29:16 +08:00
Zhongyue Lin 1c88aff23f fix(douban): make title splitting self-contained for page evaluate
fix(douban): make title splitting self-contained for page evaluate

Co-authored-by: First-principles-0 <first-principles-0@users.noreply.github.com>
Co-authored-by: codex-mini0 <codex-mini0@users.noreply.github.com>
2026-06-14 04:22:35 +08:00
AstroHan c027944380 feat(xiaohongshu): add ask adapter with citations
feat(xiaohongshu): add ask adapter with citations

Co-authored-by: codex-mini1 <codex-mini1@users.noreply.github.com>
Co-authored-by: First-principles-1 <first-principles-1@users.noreply.github.com>
2026-06-14 04:13:56 +08:00
sundyli ac0a8ba725 feat(huodongxing): add events adapter
feat(huodongxing): add events adapter

Co-authored-by: codex-mini0 <codex-mini0@users.noreply.github.com>
Co-authored-by: First-principles-0 <first-principles-0@users.noreply.github.com>
2026-06-14 04:12:38 +08:00
Jacky 7382a3541f feat(slock): add collaboration adapter
feat(slock): add collaboration adapter

Co-authored-by: codex-mini1 <codex-mini1@users.noreply.github.com>
Co-authored-by: First-principles-1 <first-principles-1@users.noreply.github.com>
2026-06-13 02:52:05 +08:00
gucasbrg 1e40165107 fix(bloomberg): read Businessweek from section page
fix(bloomberg): read Businessweek from section page

Co-authored-by: codex-mini1 <codex-mini1@users.noreply.github.com>
Co-authored-by: First-principles-1 <first-principles-1@users.noreply.github.com>
2026-06-12 18:11:59 +08:00
Bo Liu 2be23cf534 fix(deepseek): reject search with incompatible models pre-navigation
fix(deepseek): reject search with incompatible models pre-navigation

Co-authored-by: codex-mini0 <codex-mini0@users.noreply.github.com>
Co-authored-by: First-principles-0 <first-principles-0@users.noreply.github.com>
2026-06-12 18:04:39 +08:00
Zane a2bd694bdb fix(chatgpt): stabilize response extraction under virtual scrolling
fix(chatgpt): stabilize response extraction under virtual scrolling

Co-authored-by: codex-mini0 <codex-mini0@users.noreply.github.com>
Co-authored-by: First-principles-0 <first-principles-0@users.noreply.github.com>
2026-06-12 02:38:25 +08:00
Ocean 531ef27436 fix(twitter): harden API errors and SearchTimeline metadata
fix(twitter): harden API errors and SearchTimeline metadata

Co-authored-by: codex-mini1 <codex-mini1@users.noreply.github.com>
Co-authored-by: First-principles-1 <first-principles-1@users.noreply.github.com>
2026-06-12 02:27:38 +08:00
Ocean aa468589c5 feat(bilibili,xiaohongshu): add follow and unfollow commands
feat(bilibili,xiaohongshu): add follow and unfollow commands

Co-authored-by: codex-mini0 <codex-mini0@users.noreply.github.com>
Co-authored-by: First-principles-0 <first-principles-0@users.noreply.github.com>
2026-06-12 02:13:10 +08:00
Ocean c50386dcf9 feat(xiaohongshu): add commenter user identity columns
feat(xiaohongshu): add commenter user identity columns

Co-authored-by: codex-mini1 <codex-mini1@users.noreply.github.com>
Co-authored-by: First-principles-1 <first-principles-1@users.noreply.github.com>
2026-06-12 02:10:21 +08:00
Ocean 54cb1f76b3 feat(reddit): add media columns to read output
feat(reddit): add media columns to read output

Co-authored-by: codex-mini1 <codex-mini1@users.noreply.github.com>
2026-06-12 01:56:50 +08:00
Bo Liu 221366d7b6 feat(auth): add login/whoami for nowcoder, jike, maimai, jimeng (#1878)
Covers the four single-site login/whoami TODOs from the tracking issue, each verified against a logged-in session.

Refs #1876
2026-06-11 03:18:29 +08:00
jakevin 678d0086d8 feat(auth): add refresh maintenance command (#1881)
* feat(auth): add refresh maintenance command

* fix(auth): avoid DOM whoami fallback during refresh
2026-06-06 23:54:06 +08:00
jakevin 9139baaef8 feat(auth): wire quickCheck into 50 adapters for auth status (#1880)
Adds the no-navigation `quickCheck` to each adapter's
registerSiteAuthCommands config so `opencli auth status` (PR #1879) resolves
login state in quick mode (CDP getCookies, no per-site goto) instead of
reporting `unknown`.

- quickCheck reuses each adapter's existing poll cookie gate (has<Site>Cookie),
  which is a logged-in-only, no-nav check returning boolean.
- Deliberately NOT wired (stay `unknown` in quick mode, available via --full):
  - gitee/hf/deepseek/quark/reuters/zsxq: no reliable logged-in cookie; they
    detect via no-nav fetch / localStorage / Bearer which need the site origin.
  - doubao/ke/coupang/manus: session cookie is present for anonymous users, so a
    cookie quickCheck would false-positive — `unknown` is more honest.

Live: auth status --site resolves logged_in/not_logged_in for cookie-gate sites
(v2ex/github/zhihu/claude/taobao/twitter/bilibili) in quick mode; excluded
sites report unknown. Audits new=0/new=0; suite 5054 passed.
2026-06-06 21:18:43 +08:00
jakevin f9abec1455 feat(auth): add aggregate status command (#1879) 2026-06-06 21:05:11 +08:00
jakevin 77b29b3d09 feat(auth): add login/whoami for additional sites
Adds site login/whoami coverage for 55 additional auth adapters using the shared site-auth helper, including the final gitee/hf/v2ex/deepseek/quark batch and fixes from live validation.

Review follow-up:
- remove direct email output from ChatGPT/Grok/Gemini/Qwen whoami
- avoid DeepSeek email fallback as display name
- avoid Upwork first/last name output
- avoid leaking Boss wt2 session cookie as user_id
- rebase on latest main and regenerate cli-manifest.json

Validation:
- clean-HOME npm test: 5049 passed, 1 skipped
- npm run check:typed-error-lint: new=0
- npm run check:silent-column-drop: new=0
- npm run build
- npm run docs:build
- git diff --check
- dist list JSON smoke
- GitHub CI green
2026-06-06 19:42:51 +08:00
Semonxue a25a2836e9 fix(xiaohongshu): accept inline topic suggestion with Enter
Reviewed-by: @codex-mini0\nReviewed-by: @First-principles-0-\n\nMerged by @pr-manager under WAWQAQ no-check override: lead+aux content green, local validations passed, GitHub mergeable, statusCheckRollup empty.
2026-06-06 02:56:01 +08:00
flyzstu 5a82ecfd3b feat(grok): add export adapters
Reviewed-by: @codex-mini0\nReviewed-by: @First-principles-0-\n\nMerged by @pr-manager under WAWQAQ no-check override: lead+aux content green, local validations passed, GitHub mergeable, statusCheckRollup empty.
2026-06-06 02:37:58 +08:00
Archer 72d20c9113 fix(doubao): support current message DOM
Reviewed-by: @codex-mini1\nReviewed-by: @First-principles-1\n\nMerged by @pr-manager under WAWQAQ no-check override: lead+aux content green, local validations passed, GitHub mergeable, statusCheckRollup empty.
2026-06-06 02:27:14 +08:00
jakevin 82dda11a2a feat(auth): add site login and whoami commands (#1852)
* feat(auth): add site login and whoami commands

* fix(auth): satisfy docs and column audits

* fix(auth): keep login browser sessions open

* chore(auth): simplify whoami probe handler
2026-06-05 21:28:13 +08:00
Zhongyue Lin 46c8fe8e2e fix(instagram): paginate following endpoint for high limits
Reviewed-by: codex-mini0
Reviewed-by: First-principles-0-
2026-06-05 03:26:33 +08:00
Bo Liu 944ca3a105 fix(xiaohongshu): prioritize visible title input
Reviewed-by: codex-mini1
Reviewed-by: First-principles-1
2026-06-04 15:39:00 +08:00