Skip to content

chore: 同步上游 OpenCLI main#32

Merged
mylukin merged 38 commits into
mainfrom
chore/sync-upstream-main-20260517
May 17, 2026
Merged

chore: 同步上游 OpenCLI main#32
mylukin merged 38 commits into
mainfrom
chore/sync-upstream-main-20260517

Conversation

@mylukin

@mylukin mylukin commented May 17, 2026

Copy link
Copy Markdown

做了什么

  • 将上游 jackwener/OpenCLI@0c488bbf 合并到 EasyMetaAu/opencli 当前 main@71bf8bdc
  • 解决 Douyin 相关冲突:保留上游 Browser Bridge envelope/typed error 加固,同时保留 fork 里的 YouTube publish adapter 文档入口。
  • 本 PR 只做上游同步,不主动修改 EasyMetaAu main,合并后再部署 node20。

Acceptance Criteria

  • EasyMetaAu/opencli 保留现有 main 差异,并包含上游 jackwener/OpenCLI 最新 main 提交 0c488bbf
  • 冲突文件不含 conflict marker,Douyin publish/delete/upload 相关测试通过。
  • docs/adapters/index.md 同时保留 YouTube publish 和上游新增 Youdao adapter。
  • 项目可以完成 TypeScript build,并重新生成 cli-manifest.json

测试方法

  • npm run test:adapter -- clis/douyin/_shared/browser-fetch.test.js clis/douyin/_shared/vod-upload.test.js clis/douyin/delete.test.js clis/douyin/publish-upload-id.test.js clis/douyin/user-videos.test.js ✅ 5 files / 32 tests passed
  • npm run build ✅ Manifest compiled: 823 entries

GreyC and others added 30 commits May 14, 2026 13:38
Keep stale Browser Bridge WebSocket events from clobbering the active daemon connection.\n\nCo-authored-by: Jeff Chen <jeff@adtiming.com>
* docs(browser): clarify named session lifecycle

* docs(browser): clarify owned versus bound sessions

---------

Co-authored-by: Jeff Chen <jeff@adtiming.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
…h timeline/search) (jackwener#1464)

* feat(twitter/list-tweets): include media via extractMedia (parity with timeline/search)

list-tweets was the only X recall path that dropped media. timeline.js and
search.js both call extractMedia(legacy) and emit has_media/media_urls;
list-tweets returned only text fields, so downstream consumers (e.g.
ml-scout's rate UI) couldn't render image/video thumbnails on tweets pulled
from a list timeline.

Changes:
- Import extractMedia from ./shared.js
- Spread extractMedia(legacy) into extractTimelineTweet return
- Add has_media, media_urls to columns array (--format columns parity)
- Update unit test to assert the new shape; add coverage for photo and
  video extraction

* chore(manifest): rebuild cli-manifest.json for list-tweets media columns

---------

Co-authored-by: ml-scout <ml-scout@anthropic.com>
…jackwener#1555)

Mirrors PR jackwener#1464 (list-tweets) and the timeline/search/tweets/likes/thread
family: spread `...extractMedia(legacy)` into the row and surface
`has_media` + `media_urls` columns. Pure parity, no behavior change for
existing callers — media keys do not collide with the original columns.

- bookmarks.js: import `extractMedia` from ./shared.js, spread into
  extractBookmarkTweet row, append columns, export __test__.
- bookmark-folder.js: same change on extractFolderTweet, export
  extractFolderTweet via __test__.
- bookmarks.test.js (new): baseline + photo + video + entities-only
  fallback + dedup + envelope + empty-envelope (8 tests).
- bookmark-folder.test.js: update existing baseline expectation with
  has_media/media_urls, add 3 new media tests (photo / mp4 / no-media).
- cli-manifest.json: regenerated; only the two `columns` entries change.

Reverse-validated: tests fail when extractMedia spread is removed.

Audits unchanged: typed-error-lint 189/189, silent-column-drop 102/103
(pre-existing main resolution noted but not consumed here).
…ackwener#1559)

* refactor(notion): replace built-in CDP adapter with external ntn CLI

Notion has shipped an official CLI at https://ntn.dev. It uses the
public Notion API (blocks / databases / properties / comments) instead
of reverse-engineering the Desktop UI, so it survives Notion app
updates and exposes a wider command surface than the in-tree adapter
could.

Changes:
  - `src/external-clis.yaml` — register `ntn` as first-class external CLI
    (binary `ntn`, homepage ntn.dev, install via the shell-pipe script
    on mac/linux)
  - `clis/notion/` — entire directory removed (8 commands: status /
    search / read / new / write / sidebar / favorites / export)
  - `docs/adapters/desktop/notion.md` — removed
  - `docs/.vitepress/config.mts` — drop nav entry
  - `docs/adapters/index.md` — drop adapter row
  - `README.md` / `README.zh-CN.md` — drop notion from feature lines,
    drop adapter table row, add `ntn` to CLI hub examples
  - `docs/index.md` / `docs/zh/index.md` / `docs/guide/getting-started.md`
    — drop notion from electron-control feature copy
  - `skills/opencli-usage/SKILL.md` — drop notion from electron list
  - `cli-manifest.json` — rebuilt with --allow-removals=8

Migration for users:
  `curl -fsSL https://ntn.dev | bash`  (or `opencli external install ntn`)
  Then use `opencli ntn <command>` in place of `opencli notion <command>`.

Rationale: the in-tree adapter was reverse-engineered against Notion
Desktop CDP and shipped only 8 commands. The official CLI gives users
the full Notion API surface and reduces our maintenance burden to zero.
Same pattern as gh / obsidian / lark-cli / tg-cli / discord-cli / wx-cli.

Verification:
  - `npx tsc --noEmit` clean
  - `npx vitest run --project unit` → 1091/1 skipped
  - `npm run build` (with --allow-removals=8) — manifest 809 entries
  - grep notion in user-facing docs (README / docs / skills) — only
    descriptive mentions remain in non-blocking places (comparison /
    site-recon / electron how-to / design doc), no broken adapter
    references

* fix(notion): align ntn external migration

* docs(notion): clarify ntn manual install
…pter (jackwener#1561)

* fix(xiaohongshu,rednote): unwrap page.evaluate envelope in search adapter

`page.evaluate(...)` returns a `{ session, data }` envelope rather than
the raw IIFE return value, but the search adapters were calling
`Array.isArray(payload)` directly on the envelope. `Array.isArray` is
always false on the envelope, so every search result was silently
dropped — status=success, exit 0, empty array, no error.

The rednote adapter had this same bug; both share `buildSearchExtractJs`
from `xiaohongshu/search.js`.

Introduces `unwrapEvaluateResult(payload)` as a shared helper in
`clis/xiaohongshu/search.js` (re-exported via the existing import line
from `rednote/search.js`). The helper is a defensive ternary: it
unwraps when payload looks like an envelope with an array `.data`,
otherwise it passes the value through unchanged. This keeps the change
back-compat with bridge versions that return the raw value, and
preserves the existing `Array.isArray(payload)` typecheck at each call
site.

Verified manually against `opencli xiaohongshu search "补墙洞"` (a query
known to return 20+ results in a logged-in browser tab): previously
`[]`, now returns the expected ranked rows with all declared columns
(`rank, title, author, likes, published_at, url`) populated.

Adds 5 unit tests for `unwrapEvaluateResult` covering raw array passthrough,
envelope unwrap, non-envelope object passthrough, null/undefined safety,
and the "data is not an array" guard. The existing 19 search tests in
`clis/xiaohongshu/search.test.js` still pass — the unwrap is invisible
to the existing mocks which already return raw arrays.

* fix(xhs): unwrap search evaluate envelopes

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(extension): reuse existing adapter tab group

* fix(extension): choose best existing adapter group

---------

Co-authored-by: Jeff Chen <jeff@adtiming.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
…#1546)

* feat: add DuckDuckGo, Brave, and Yahoo web search adapters

Add three new search engine adapters with browser-based DOM extraction:

- duckduckgo/search: Search DuckDuckGo via html.duckduckgo.com
  Supports region, time filters, and XHR-based pagination (--offset)
- duckduckgo/suggest: Search suggestion autocomplete (no browser needed)
- brave/search: Search Brave Search via search.brave.com
  Supports GET-based pagination (--offset)
- yahoo/search: Search Yahoo (Bing-powered) via search.yahoo.com
  Supports GET-based pagination (--page)

All search adapters use Strategy.PUBLIC with browser:true, navigating
the target site and extracting results via page.evaluate() DOM queries.
Includes full test coverage (16 tests).

* fix: use clampInt from shared utils and add adapter docs

- Replace Math.max/Math.min patterns with clampInt() from _shared/common.js
  to pass the typed-error-lint gate (4 silent-clamp violations resolved)
- Add adapter documentation for duckduckgo, brave, and yahoo to fix
  the doc-coverage CI check
- Regenerate cli-manifest.json and typed-error-lint-baseline.json

* fix: avoid silent-column-drop overlap in brave/yahoo extractors

Change buildExtractorJs to return arrays instead of objects whose keys
matched columns. This prevents silent-column-drop audit false positives
as per opencli-adapter-author conventions.

* fix(search): tighten browser search adapters

* chore(search): drop baseline churn

* fix(duckduckgo): execute search extractor safely

* fix(yahoo): reject unsafe redirect targets

---------

Co-authored-by: huzekang <huzekang@opencode.ai>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(boss): support job-seeker chatlist and chatmsg

* fix(boss): type chat-side failure boundaries

* fix(boss): guard malformed chat API payloads

---------

Co-authored-by: Jeff Chen <jeff@adtiming.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
…ackwener#1538)

* fix(facebook/feed): add fallback extraction for empty article nodes

Add fallback extraction for Facebook feed posts when [role=article] nodes exist but contain empty text. Includes diagnostic errors, content/author cleanup, nested-container dedupe, and an evaluate-script syntax regression test.

* fix(facebook): bound feed fallback extraction

* fix(facebook): keep feed fallback available after chrome articles

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
…wener#1573)

Recruiter-only BOSS commands (recommend, joblist, stats, resume, mark,
exchange, invite, greet, batchgreet) returned a generic
`COMMAND_EXEC: 请切换身份后再试 (code=24)` when called from a job-seeker
account. The original error hid the actionable bit: this command set
needs a recruiter (BOSS-side) account.

chatlist / chatmsg already special-case code=24 by falling back to the
geek-side fetch when --side=auto. Recruiter-only commands have no
geek-side equivalent and were just leaking the raw API code.

Fix: add a `checkRecruiterSide` step inside `assertOk` that maps
code=24 to AuthRequiredError with a clear message. All 9 recruiter-only
commands inherit it through their existing `bossFetch` calls; no
adapter-level changes needed. chatlist / chatmsg are unaffected because
they use `allowNonZero: true` and never hit the auto-error path.

Closes jackwener#1572.
…#1568)

* fix(weibo): unwrap page.evaluate envelope in read adapters (jackwener#1567)

`page.evaluate(...)` returns a `{ session, data }` envelope rather than
the raw IIFE return value, so all weibo cookie-strategy read adapters
silently dropped their results on v1.7.19:

- `getSelfUid` returned the envelope object instead of the uid string,
  so `'10001' + uid` produced `'10001[object Object]'` and every
  feed/me/favorites request hit a broken list_id.
- `feed`, `hot`, `comments`, `search`, `favorites` did `Array.isArray`
  on the envelope (always false) and returned `[]`.
- `me`, `user`, `post` returned the envelope wrapper itself instead of
  the inner profile/post object.

Same pattern as jackwener#1561 for xiaohongshu/rednote. Adds an
`unwrapEvaluateResult` helper to `clis/weibo/utils.js` (kept local
rather than cross-importing from `xiaohongshu/search.js` since weibo
is an unrelated site) and wraps every `await page.evaluate(...)` in
the 8 read adapters plus the two helper calls in `getSelfUid`.

Skipped `publish.js` (write command, out of scope for this read fix).

Verified live:
- `opencli weibo hot --limit 3` returns 3 real trending items
- `opencli weibo feed --limit 3` returns 3 timeline posts with
  correct `https://weibo.com/<uid>/<mblogid>` URLs (proves
  `getSelfUid` unwrap works)
- `opencli weibo me` returns the logged-in profile object
- All 20 weibo unit tests pass (6 new for `unwrapEvaluateResult`)

* fix(weibo): fail typed on malformed evaluate payloads

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
…jackwener#1585)

`ntn`, `dws`, and `wecom-cli` are opaque executable names — users seeing them
in `opencli list` or root help have no way to know they correspond to Notion,
DingTalk Workspace, and 企业微信. Repurpose the existing `package` field to
double as a human-readable brand label, so help output renders as
`ntn(notion)`, `dws(DingTalk Workspace)`, `wecom-cli(企业微信)`.

- `src/external-clis.yaml`: add `package:` to ntn / dws / wecom-cli
- `src/external.ts`: update JSDoc on `package` to cover both upstream
  distribution names (tg-cli, discord-cli) and brand labels (notion, 企业微信)
- `src/cli.ts:629` (`opencli list`): use `formatExternalCliLabel` so the
  listing matches root help, which already used it
- `src/external.test.ts`: regression test for brand-alias labels

Verification:
- npx vitest run --project unit src/external.test.ts: 9/9 pass
- npm run typecheck: clean
- npm run build: 813 manifest entries
- Smoke: `opencli list` and `opencli --help` both render the new labels
External CLI ergonomics + two adapter envelope/auth fixes.

- feat(external): longbridge CLI passthrough (jackwener#1584)
- feat(external-cli): brand alias rendering for ntn/dws/wecom-cli (jackwener#1585)
- fix(boss): map code=24 → AuthRequiredError (jackwener#1573)
- fix(weibo): unwrap page.evaluate envelope in read adapters (jackwener#1568)
…-01 --end 2025-06-02 (jackwener#1379)

* docs: add weibo search_by_user command design spec

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

* test(weibo): add search_by_user helper function tests

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

* feat(weibo): add search_by_user command for timed post download to Markdown

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

* fix(weibo): remove dead hasori ternary and hardcoded hastext/haspic filters

The hasori ternary always evaluated to 1 (bug), and hastext=1 + haspic=1
silently excluded text-only and link-only posts from results.

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

* test(weibo): add integration tests for search_by_user helpers

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

* bak

* fix(weibo): reshape user posts into read adapter

---------

Co-authored-by: andrew.asa <asa.andrew@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(youtube): unwrap transcript caption results

* fix(youtube): validate transcript caption info shape

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
…ackwener#1580)

* fix(chatgpt): unwrap page.evaluate envelope across browser commands

The browser bridge wraps every `page.evaluate(...)` return value in a
`{ session, data }` envelope. Adapters that read `.length` or
`Array.isArray(payload)` directly on the envelope silently see "no
data" — same failure mode addressed for `xiaohongshu`/`rednote` in
jackwener#1561 and `weibo` in jackwener#1568.

This sweep applies the same `unwrapEvaluateResult` helper across every
chatgpt `page.evaluate` consumer site, plus typed shape guards
(`requireArrayEvaluateResult`, `requireObjectEvaluateResult`) on the
critical extraction paths so envelope misses fail loud instead of
silently returning empty.

## Sites wrapped

`clis/chatgpt/utils.js`:

- `currentChatGPTUrl` — string URL
- `getPageState` — login/composer probe object
- `sendChatGPTMessage` — composer write + send-button readiness
- `getVisibleMessages` — conversation transcript array
- `getConversationList` / `extractConversationLinks` — sidebar items
- `waitForChatGPTUploadPreview` — image upload readiness probe
- `uploadChatGPTImages` fallback — DataTransfer upload result
- `isGenerating` — boolean "still generating?" probe
- `getChatGPTVisibleImageUrls` — visible image URL array
- `waitForChatGPTImages` — inline `window.location.href` poll
- `getChatGPTImageAssets` — exported asset array

`clis/chatgpt/image.js`:

- `currentChatGPTLink` — used for error hints + conv link reporting

## Drive-by

`getChatGPTImageAssets` was also passing a redundant `urls` second arg
to `page.evaluate(string, urls)`. The IIFE inside the string already
receives the URL list via the `${urlsJson}` template substitution, and
the browser bridge guard in `browser/utils.ts` rejects the second form
for string scripts with:

    page.evaluate string input does not accept args;
    use page.evaluate(fn, ...args) instead

So `opencli chatgpt image <prompt>` blows up at the download step
without `--sd true`. Drop the trailing arg as part of the asset-export
cleanup. (This supersedes jackwener#1556 — same one-line fix is included here.)

## Validation

- `npx tsc --noEmit` — clean
- `npx vitest run --project adapter clis/chatgpt/` — 38/38 pass
  (25 existing + 13 new in `envelope.test.js`)
- `npm test` — 3644 passing across 364 files
- Live (browser bridge, daemon v1.7.19):
  `opencli chatgpt image "<prompt>"` → end-to-end generate + download
  succeeds; the envelope wrap is defensive in 1.7.19 (no envelope
  observed yet), but pre-empts the same silent-failure mode that hit
  the merged xiaohongshu/weibo PRs.

* fix(chatgpt): fail fast on malformed evaluate payloads

---------

Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat: add Flomo memos reader adapter

Read your Flomo memos via the signed API.

- flomo memos: Lists recent memos with content, tags, timestamps
  Uses the Flomo v1 API with MD5 signing (secret embedded).
  Requires FLOMO_ACCESS_TOKEN env variable.
  Supports pagination via --slug cursor and --limit.

* fix: add --token arg for Flomo auth

* fix: use COOKIE strategy with browser-based API call

Use Strategy.COOKIE + browser:true instead of PUBLIC + manual token.
The adapter now reads flomo_token from localStorage in the browser,
and makes the signed API call from within the page context via fetch().
Signature is computed in Node.js and injected into the browser eval.
No env var or --token flag needed.

* fix: use access_token from localStorage.me for API auth

Flomo API requires Bearer token from access_token field in
localStorage.me (not api_token). Adapter now reads access_token
from the browser's localStorage and calls the signed API from
Node.js with the Bearer header.

* feat: add --since filter, refine flomo adapter API

- Add --since <unix_ts> to filter memos by updated_at
- Add --limit 200 to fetch all memos in one call
- Mark --slug as experimental (cursor pagination unreliable)
- 5 tests passing

* feat: add images column to flomo memos output

* docs: add flomo adapter documentation

* fix: use clampInt and rebuild manifest

* fix(flomo): harden memos reader contract

---------

Co-authored-by: huzekang <huzekang@opencode.ai>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(douyin): restore publish and delete flow

- Use upload auth v5 API instead of legacy STS2 for VOD credentials
- Switch TOS upload from AWS4-signature to gateway multipart protocol (init/transfer/finish)
- Add ApplyUploadInner → CommitUploadInner pipeline for VOD upload
- Bypass enable/transend endpoints that hang for gateway-uploaded videos
- Handle fast_detect/pre_check empty responses gracefully with retry+backoff
- Add creator backend delete fallback (via work_list id matching) when legacy delete returns permission error
- Use CommitUploadInner Vid for create_v2, not completed TOS object key
- Accept item_id as fallback when create_v2 returns no aweme_id

* fix(douyin): harden publish delete write contracts

---------

Co-authored-by: Lukin <mylukin@gmail.com>
)

- Replace 2-line tagline (websites/browser/electron/local + reuse logged-in browser) with a single line emphasizing the two core capabilities side by side: 把任意网站变成 CLI & 让 AI Agent 操控登录态浏览器
- Add "Help me fill out this form" as the leading opencli-browser skill example so the table surfaces browser-side capabilities, not just scraping
* feat: add Youdao Notes shared note reader adapter

Add a new adapter for reading publicly shared Youdao Notes (有道云笔记).

- youdao note <url>: Fetches a public shared note by its share URL
  using browser-based DOM extraction. Extracts title, content, and
  keyword tags from the React-rendered page.
- Supports note.youdao.com and note.youdao.cn share URLs.
- Includes test coverage (3 tests) and documentation.

Closes jackwener#1418

* fix: extract full note content from React Redux store

Previously the adapter only extracted the AI summary section from the
DOM. Now it accesses the React fiber tree to read the full note content
from the Redux store (store.content.data.content), which contains the
complete note body in Youdao's structured format.

The extractor recursively walks Youdao's proprietary node format (key '8'
for text content) to reconstruct the full note as plain text.

* fix(youdao): harden shared note reader contract

---------

Co-authored-by: huzekang <huzekang@opencode.ai>
Co-authored-by: jackwener <jakevingoo@gmail.com>
hanzili and others added 7 commits May 16, 2026 14:21
* feat(linkedin): add messaging commands

Add fail-closed LinkedIn inbox, connect, safe-send, and thread-snapshot commands with adapter tests and docs.

* fix(linkedin): align commands with current UI

Update inbox to read LinkedIn's normalized messaging API response and connect to use the current custom-invite route.

* chore(linkedin): sync cli-manifest.json with rebuilt inbox command

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

* fix(linkedin): pass silent-column-drop gate

Drop the intermediate timestamp_ms field from inbox rows (it is converted to the timestamp column) and baseline the connect command internal profile-probe object.

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

* fix(linkedin): validate inbox --limit with a typed error

Reject an out-of-range --limit with ArgumentError instead of silently clamping it, satisfying the typed-error lint gate.

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

* fix(linkedin): harden messaging command contracts

* fix(linkedin): reject inbox conversations without thread id

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
Resolve the remaining silent-empty-fallback typed-error baseline entries across Douyin, Jike, and WeRead adapters.
…jackwener#1590)

* feat(bilibili): add summary command for the official AI video summary

Adds `opencli bilibili summary <bvid>` — fetches Bilibili's official
AI-generated video summary (the "AI总结" shown on the video page) via
/x/web-interface/view/conclusion/get.

Returns the overall summary followed by the timestamped section outline,
so you get a structured digest of a video without watching it.

- Resolves cid + up_mid from the view endpoint (both required by the
  conclusion API), then calls the WBI-signed conclusion endpoint.
- Throws a clear EmptyResultError when a video has no AI summary —
  Bilibili only generates them for some videos.

Covered by clis/bilibili/summary.test.js (5 cases): summary + outline,
summary without outline, no-summary, view-resolution failure, API error.

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

* fix(bilibili): harden summary command contract

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
Avoid classifying fallback text inside thrown error messages as silent row data.
* fix(barchart): surface greeks fetch failures

* fix(barchart): harden greeks failure contract

* fix(barchart): reject malformed greeks row identity

---------

Co-authored-by: 你的用户名 <你的邮箱>
Co-authored-by: jackwener <jakevingoo@gmail.com>
Per WAWQAQ feedback: the previous Highlights list was bloated with hollow
marketing phrases and overlapping bullets (e.g. "Browser Automation for AI
Agents" + "AI Agent ready" said the same thing twice, "Pipeable, scriptable,
CI-friendly" is generic CLI filler).

Cut "AI Agent ready", "Account-safe" (folded into Live Browser Automation),
"Deterministic"'s second sentence (folded into Zero LLM cost), and merged
"Website → CLI" with "CLI Hub" into "100+ adapters + CLI Hub". Result is 5
concrete capability bullets instead of 9, each tied to a real feature.

EN and ZH READMEs kept in sync.
Co-Authored-By: Cody <builder.bot@easymeta.au>
@mylukin

mylukin commented May 17, 2026

Copy link
Copy Markdown
Author

Review 结论:NEEDS WORK

对抗性检查

  • ✅ 冲突残留:未发现 conflict marker;git diff --check origin/main...HEAD 通过。
  • ✅ Douyin 冲突主线:browser-fetch / vod-upload / delete 的 Browser Bridge envelope、typed error、删除后确认逻辑都保住了,相关测试通过。
  • ✅ EasyMeta YouTube publish:clis/youtube/publish.js、manifest entry、docs/adapters/index.mdyoutube publish 都还在。
  • ❌ 文档索引回归:docs/adapters/index.md:59tiktok publish 从总索引里删了,但 clis/tiktok/publish.jsclis/tiktok/publish.test.jsdocs/adapters/browser/tiktok.mdcli-manifest.json 都仍然暴露 opencli tiktok publish。这是 merge conflict resolution 顺手吃掉了 fork/custom docs 入口,用户从总索引看会误以为 TikTok publish 不存在。
    • 修复:把 publish 加回 docs/adapters/index.md 的 tiktok command list;不需要改代码。

Acceptance Criteria

  • ✅ 同步上游:merge commit c1f700bd 父提交为 EasyMetaAu/main@71bf8bdc + jackwener/OpenCLI@0c488bbf
  • ✅ YouTube publish 保留:已核 clis/youtube/publish.jscli-manifest.jsondocs/adapters/index.md
  • ✅ 构建:npm run build 通过,Manifest compiled: 823 entries。
  • ✅ 回归测试:npm run test:adapter -- clis/douyin/_shared/browser-fetch.test.js clis/douyin/_shared/vod-upload.test.js clis/douyin/delete.test.js clis/douyin/publish-upload-id.test.js clis/douyin/user-videos.test.js clis/youtube/publish.test.js clis/tiktok/publish.test.js 通过,7 files / 43 tests passed。
  • ✅ 质量门:npm run check:typed-error-lintnpm run check:silent-column-drop 通过。
  • ⚠️ CI:GitHub 仍有 audit / bun-test / doc-coverage / docs-build 等 pending/queued;合并前必须等完。

总结:代码主线没看到 blocker,但文档索引有一处明确回归。修掉 docs/adapters/index.md:59 后我再复审。— Trent 🏛️

Co-Authored-By: Cody <builder.bot@easymeta.au>
@mylukin

mylukin commented May 17, 2026

Copy link
Copy Markdown
Author

修复 Trent NEEDS WORK:

  • 上次 FAIL 原因:docs/adapters/index.md 的 TikTok 总索引漏掉 publish,和 clis/tiktok/publish.js / adapter docs / manifest 暴露能力不一致。
  • 本次修复:在 docs/adapters/index.md 的 TikTok 行恢复 creator-videos / publish
  • 验证:npm run build ✅,Manifest compiled: 823 entries。

最新提交:89cafc48

@mylukin

mylukin commented May 17, 2026

Copy link
Copy Markdown
Author

Retry Review:PASS(上次 blocking 已修)

复审范围只看上次 FAIL 点:docs/adapters/index.md 的 TikTok 总索引是否漏掉 publish

验证结果:

  • ✅ PR live head:89cafc48dad4605f50baf8bb07789a0164d22cdfOPEN / MERGEABLE / UNSTABLE
  • docs/adapters/index.md:59 现在包含 creator-videos / publish
  • cli-manifest.json 仍有 site=tiktok + name=publish + modulePath=tiktok/publish.js
  • ✅ 上次完整复核已覆盖:build、Douyin/YouTube/TikTok adapter 测试、typed-error lint、silent-column-drop

代码/文档层 PASS。剩余门禁不是代码 review:GitHub 仍有 audit / bun-test / doc-coverage / docs-build / 部分 build checks queued 或 in-progress,合并前等它们结束,或由授权人按流程明确授权跳过 pending CI。— Trent 🏛️

@mylukin mylukin merged commit dde2340 into main May 17, 2026
7 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.