實(shí)戰(zhàn)指南:從零編寫站點(diǎn) Adapter、Pipeline 與 func() 雙范式及工程規(guī)范)
OpenCLI 貢獻(xiàn)實(shí)戰(zhàn)指南從零編寫站點(diǎn) Adapter、Pipeline 與 func() 雙范式及工程規(guī)范【免費(fèi)下載鏈接】OpenCLIMake Any Website into CLI Use your logged-in browser by AI agent.項(xiàng)目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI本指南以 OpenCLI 官方貢獻(xiàn)文檔為主體系統(tǒng)講解如何為這一「把任意網(wǎng)站變成 CLI」的項(xiàng)目新增一個站點(diǎn)適配器先完成環(huán)境搭建與構(gòu)建驗(yàn)證再分別掌握Pipeline數(shù)據(jù)抓取與func()復(fù)雜瀏覽器交互兩種適配器編寫范式并落地參數(shù)設(shè)計、測試、代碼風(fēng)格與提交規(guī)范。讀完你將具備獨(dú)立向倉庫提交一個全新站點(diǎn)命令如opencli mysite trending的完整能力并理解適配器在底層注冊表與執(zhí)行引擎中的工作方式。一、快速開始環(huán)境搭建與首次構(gòu)建貢獻(xiàn)者第一步是 Fork 并克隆倉庫完成依賴安裝、構(gòu)建與自檢。OpenCLI 是標(biāo)準(zhǔn)的 npm 項(xiàng)目package.json要求 Node.js20.18.1采用 ESM 模塊體系# 1. Fork clone git clone gitgithub.com:your-username/opencli.git cd opencli # 2. Install dependencies npm install # 3. Build npm run build # 4. Run a few checks npx tsc --noEmit npm test # 5. Link globally (optional, for testing opencli command) npm link結(jié)合 package.json 的 scripts 可以進(jìn)一步理解每一步的含義npm run build是一條鏈?zhǔn)矫钕扔蒫lean-dist清空dist/再copy-yaml復(fù)制 YAML 配置最后build-manifest觸發(fā)tsc --build編譯 TypeScript 并生成命令清單cli-manifest.jsonnpx tsc --noEmit對應(yīng)npm run typecheck僅做類型檢查不產(chǎn)出文件是提交前最快的類型把關(guān)手段npm test實(shí)際執(zhí)行vitest run --project unit --project extension --project adapter即「本地默認(rèn)門禁」 單元測試 擴(kuò)展測試 適配器測試三層詳見測試npm link將本地包軟鏈到全局之后即可直接使用opencli命令調(diào)試自己開發(fā)的適配器。二、理解適配器注冊機(jī)制registry 公共 API 與 StrategyOpenCLI 的全部適配器使用 TypeScript 編寫通過jackwener/opencli/registry這一公共入口注冊命令。該入口對應(yīng)源碼 src/registry-api.ts它只重新導(dǎo)出核心注冊 API不引入序列化等傳遞性副作用目的是避免插件在動態(tài)加載discoverPlugins()時發(fā)生循環(huán)依賴死鎖。真正承載注冊邏輯的是 src/registry.ts其中定義了核心概念Strategy枚舉src/registry.tsPUBLIC、LOCAL、COOKIE、INTERCEPT、UI五種策略用于聲明命令的數(shù)據(jù)獲取方式與鑒權(quán)依賴Arg接口src/registry.ts聲明命令參數(shù)支持type、default、required、positional、choices等字段cli(opts)注冊函數(shù)src/registry.ts接收命令定義并寫入全局注冊表隨后可通過fullName()site/name格式在注冊表中取回registerCommand()歸一化邏輯src/registry.ts會把strategy解碼為執(zhí)行路徑實(shí)際讀取的browser、navigateBefore字段——例如COOKIE策略且聲明了domain時會自動推導(dǎo)出「命令執(zhí)行前先預(yù)導(dǎo)航到https://domain」同時自動處理aliases別名映射。此外注冊表使用globalThis上的單一 Mapsrc/registry.ts確保通過npm link/ peerDependency 加載的插件不會因模塊實(shí)例分裂而注冊到不同的 Map 上。三、Pipeline Adapter數(shù)據(jù)抓取命令的推薦范式對于「拉取數(shù)據(jù)」類命令列表、排行、搜索、行情推薦使用pipeline聲明式寫法。創(chuàng)建一個類似clis/site/command.js的文件import { cli, Strategy } from jackwener/opencli/registry; cli({ site: mysite, name: trending, description: Trending posts on MySite, domain: www.mysite.com, strategy: Strategy.PUBLIC, access: read, browser: false, args: [ { name: query, positional: true, required: true, help: Search keyword }, { name: limit, type: int, default: 20, help: Number of items }, ], columns: [rank, title, score, url], pipeline: [ { fetch: { url: https://api.mysite.com/trending } }, { map: { rank: ${{ index 1 }}, title: ${{ item.title }}, score: ${{ item.score }}, url: ${{ item.url }}, }}, { limit: ${{ args.limit }} }, ], });要點(diǎn)說明strategy: Strategy.PUBLICbrowser: false表示該命令無需瀏覽器直接由 Node 側(cè)fetch獲取公開數(shù)據(jù)pipeline數(shù)組按順序執(zhí)行數(shù)據(jù)流步驟${{ ... }}是模板表達(dá)式可訪問item當(dāng)前行、index行號、args命令參數(shù)等上下文columns聲明輸出列必須與最終map產(chǎn)出的字段一致。3.1 真實(shí)示例hackernews/top.js倉庫中 clis/hackernews/top.js 是一個完整的 pipeline 實(shí)戰(zhàn)案例——它先用fetch拿到 Hacker News 的 top stories ID 列表再對每個 ID 并發(fā)拉取詳情最后過濾、映射、截斷輸出pipeline: [ { fetch: { url: https://hacker-news.firebaseio.com/v0/topstories.json } }, { limit: ${{ Math.min((args.limit ? args.limit : 20) 10, 50) }} }, { map: { id: ${{ item }} } }, { fetch: { url: https://hacker-news.firebaseio.com/v0/item/${{ item.id }}.json } }, { filter: item.title !item.deleted !item.dead }, { map: { rank: ${{ index 1 }}, id: ${{ item.id }}, title: ${{ item.title }}, score: ${{ item.score }}, author: ${{ item.by }}, comments: ${{ item.descendants }}, url: ${{ item.url }}, } }, { limit: ${{ args.limit }} }, ],這段代碼展示了 pipeline 的兩個高級能力步驟間數(shù)據(jù)流第二次fetch的 URL 引用了前一步map產(chǎn)出的item.id實(shí)現(xiàn)「逐條展開詳情」表達(dá)式動態(tài)計算limit步驟用Math.min先多取 10 條以留出過濾余量最后再嚴(yán)格截斷到args.limit。3.2 pipeline 步驟從哪來pipeline 的步驟名并非寫死的字符串而是來自可動態(tài)擴(kuò)展的步驟注冊表 src/pipeline/registry.ts核心步驟包括navigate、fetch、click、type、fill、wait、press、snapshot、evaluate、select、map、filter、sort、limit、intercept、tap、download等。第三方插件也可通過registerStep()注冊自定義步驟。opencli validate會依據(jù)該注冊表實(shí)時校驗(yàn)步驟名拼寫避免手寫白名單產(chǎn)生過期快照。四、func() Adapter復(fù)雜瀏覽器交互范式當(dāng)命令需要在瀏覽器內(nèi)執(zhí)行點(diǎn)擊、填表、發(fā)布、抓取需登錄數(shù)據(jù)等復(fù)雜交互時使用func()回調(diào)。創(chuàng)建一個類似clis/site/command.js的文件import { cli, Strategy } from jackwener/opencli/registry; cli({ site: mysite, name: search, description: Search MySite, domain: www.mysite.com, strategy: Strategy.COOKIE, args: [ { name: query, positional: true, required: true, help: Search query }, { name: limit, type: int, default: 10, help: Max results }, ], columns: [title, url, date], func: async (page, kwargs) { const { query, limit 10 } kwargs; await page.goto(https://www.mysite.com); const data await page.evaluate(async (q: string) { const res await fetch(/api/search?q encodeURIComponent(q), { credentials: include }); return (await res.json()).results; }, query); return data.slice(0, Number(limit)).map((item: any) ({ title: item.title, url: item.url, date: item.created_at, })); }, });要點(diǎn)說明browser字段決定func簽名browser: false時簽名是(kwargs, debug?)browser: true時簽名是(page, kwargs, debug?)。把兩者搞反是高頻踩坑點(diǎn)——此時kwargs實(shí)際收到的是 debug 標(biāo)志所有外部參數(shù)會靜默回退到默認(rèn)值詳見 skills/opencli-adapter-author/SKILL.md 中的關(guān)鍵約定page是IPage類型src/registry-api.ts提供goto、evaluate等頁面操作能力Strategy.COOKIE配合domain時引擎會在執(zhí)行前自動預(yù)導(dǎo)航到https://www.mysite.com確保帶 cookie 的登錄態(tài)可用。4.1 共享參數(shù)校驗(yàn)工具對于搜索類命令clis/_shared/search-adapter.js提供了一組可直接復(fù)用的參數(shù)校驗(yàn)與容錯函數(shù)requireSearchQuery關(guān)鍵詞非空校驗(yàn)、requireBoundedInteger整數(shù)范圍校驗(yàn)、requireNonNegativeInteger、unwrapBrowserResult、requireRows結(jié)果形狀校驗(yàn)、toHttpsUrl等。它們配合jackwener/opencli/errors拋出ArgumentError/CommandExecutionError/EmptyResultError等類型化錯誤保證失敗路徑可控、可被上層優(yōu)雅處理。4.2 完整的 adapter 編寫工作流如果希望覆蓋「偵察 → API 發(fā)現(xiàn) → 字段解碼 → verify」的完整流程可以安裝倉庫自帶的opencli-adapter-authorskill。該 skill 強(qiáng)調(diào)先定 strategy 再寫代碼優(yōu)先選擇契約穩(wěn)定的PUBLIC_API/COOKIE_API只有公開接口不可用、UI/DOM 語義不穩(wěn)定時才承擔(dān)PAGE_FETCH/INTERCEPT這類無契約內(nèi)部接口的維護(hù)成本并強(qiáng)制在寫代碼前產(chǎn)出一段 strategy note含 Contract 等級與 Evidence。五、驗(yàn)證你的 Adapter編寫完成后通過以下命令驗(yàn)證# Validate adapter opencli validate # Test your command opencli site command --limit 3 -f json # Verbose mode for debugging opencli site command -vopencli validate的執(zhí)行邏輯在 src/validate.ts它會遍歷全局注冊表并對每個命令做多項(xiàng)檢查description 缺失→ warning瀏覽器命令未聲明domain→ warning登錄態(tài)瀏覽器上下文可能無法工作pipeline 步驟名拼寫錯誤→ warning并提示最近似的合法步驟名白名單實(shí)時取自步驟注冊表既無func也無pipeline→ error命令無法執(zhí)行參數(shù)名重復(fù)→ errorpositional 參數(shù)出現(xiàn)在命名參數(shù)之后→ warning見下文參數(shù)設(shè)計規(guī)范。輸出形如opencli validate: PASS與Checked N command(s)、Errors / Warnings計數(shù)逐條列出問題命令與具體原因。六、Arg Design Convention參數(shù)設(shè)計規(guī)范OpenCLI 的參數(shù)設(shè)計有一條核心原則主目標(biāo)參數(shù)用 positional配置參數(shù)用命名選項(xiàng)--flag。判斷標(biāo)準(zhǔn)不是「誰寫在文件前面」而是「用戶會怎么敲這條命令」——opencli xueqiu stock SH600519比opencli xueqiu stock --symbol SH600519自然得多。Arg typePositional?ExamplesMain target (query, symbol, id, url, username)?positional: truesearch 茅臺,stock SH600519,download BV1xxxConfiguration (limit, format, sort, page, type, filters)? Named--flag--limit 10,--format json,--sort hot,--location seattle不要僅僅因?yàn)槟硞€參數(shù)在文件里排在第一個就把它改成 positional。如果參數(shù)是可選的、行為類似過濾器、或用于選擇模式/配置它通常應(yīng)該保持為命名選項(xiàng)。pipeline 與 func() 兩種寫法均遵循同一套規(guī)范args: [ { name: query, positional: true, required: true, help: Search query }, // ← primary arg { name: limit, type: int, default: 20, help: Max results }, // ← config arg ]Arg接口還支持choices枚舉取值、valueRequired等字段源碼 src/registry.ts 中有完整定義。另外注意 src/validate.ts 的檢查positional 參數(shù)應(yīng)集中放在命名參數(shù)之前且參數(shù)名不可重復(fù)。七、Testing三層門禁與定位指南完整測試指南見 TESTING.md本地常用的命令如下npm test # Default local gate: unit extension adapter tests npm run test:adapter # Adapter-only project (useful while iterating on adapters) npx vitest run tests/e2e/ # E2E tests npx vitest run # All tests測試架構(gòu)由 vitest.config.ts 定義分為多個 project層位置運(yùn)行方式用途單元測試src/**/*.test.tsnpm test內(nèi)部模塊、pipeline、runtimeAdapter 測試clis/**/*.test.{ts,js}npm test/npm run test:adapteradapter 命令與數(shù)據(jù)歸一化E2E 測試tests/e2e/*.test.tsnpx vitest run tests/e2e/真實(shí) CLI 命令執(zhí)行煙霧測試tests/smoke/*.test.tsnpx vitest run tests/smoke/外部 API 與注冊完整性對于新增 adapter建議按 TESTING.md 的決策流程補(bǔ)測試browser: false的命令加入tests/e2e/public-commands.test.tsbrowser: true但公開數(shù)據(jù)加入tests/e2e/browser-public.test.ts站點(diǎn)反爬/地域限制導(dǎo)致空數(shù)據(jù)時 warn pass需登錄的命令加入tests/e2e/browser-auth.test.ts驗(yàn)證 graceful failure不 crash、不 hang、錯誤信息可控。八、Code Style代碼風(fēng)格貢獻(xiàn)代碼須遵守以下風(fēng)格約定TypeScript strict mode—— 盡量避免anyES Modules—— 導(dǎo)入路徑使用.js擴(kuò)展名對應(yīng) TypeScript 輸出命名文件kebab-case變量/函數(shù)camelCase類型/類PascalCase無默認(rèn)導(dǎo)出—— 統(tǒng)一使用命名導(dǎo)出。九、Commit Convention提交信息約定使用 Conventional Commits 規(guī)范常用 scope 為站點(diǎn)名如twitter、reddit或模塊名如browser、pipeline、enginefeat(twitter): add thread command fix(browser): handle CDP timeout gracefully docs: update CONTRIBUTING.md test(reddit): add e2e test for save command chore: bump vitest to v4十、提交 Pull Request創(chuàng)建功能分支git checkout -b feat/mysite-trending完成修改并在相關(guān)時補(bǔ)充測試運(yùn)行適用的檢查項(xiàng)npx tsc --noEmit # Type check npm test # Default local gate: unit extension adapter npm run test:adapter # Adapter-only project (optional while iterating on adapters) opencli validate # Adapter validation使用 conventional commit 格式提交Push 并打開 PR十一、License通過貢獻(xiàn)代碼即表示你同意你的貢獻(xiàn)將依據(jù) Apache-2.0 License 授權(quán)。倉庫根目錄還提供 README.md項(xiàng)目總覽與 CONTRIBUTING.md本文依據(jù)文檔以及面向新站點(diǎn)適配器的完整開發(fā)方法論 skills/opencli-adapter-author/SKILL.md可作為持續(xù)深入?yún)⒖??!久赓M(fèi)下載鏈接】OpenCLIMake Any Website into CLI Use your logged-in browser by AI agent.項(xiàng)目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考