Files
jackwener__opencli/docs/adapters/browser/facebook.md
jakevin b9b87a5c64 refactor(facebook/notifications): pipeline→func + typed errors + 7-col contract + runtime upfront limit (Phase 3 P5, #1391)
First Facebook adapter — Pattern C HTML scrape (lead 5 + author 4 = 7 endpoint family probe matrix dual-source negative evidence: graphql×3 / m.facebook redirect / login.php / checkpoint.php / fetch-patch / Messenger relay / ajax legacy 全 unauth 不可达, DOM walk over rendered notification rows + path-anchored auth detection 是当前 reviewable boundary).

Caller-visible delta: 3 cols (index/text/time) → 7 cols (+unread/+url/+notif_id/+notif_type).

[Bug fix] — 5 silent failures resolved
- silent-bad-shape: text.substring(0,150) → full body via per-row 'Mark as read' aria-label
- silent-bad-shape: time || '-' sentinel → string|null typed unknown
- silent-column-drop: unread badge / anchor href / notif_id / notif_t 暴露
- silent-empty-row: /login(.php)? + /checkpoint(.php)? redirect 返 [] → AuthRequiredError; empty/no-recoverable-text → EmptyResultError
- silent-clamp: limit 越界 silent clamp → ArgumentError (1-100), upfront before any navigation (navigateBefore: false)

[Structural refactor]
- pipeline → cli() func form + Strategy.COOKIE + navigateBefore: false (runtime upfront invariant 与 #1387 standard 拉齐)
- module-level pure exports: normalizeNotificationsLimit, stripMarkAsReadPrefix, stripAnchorChrome, parseNotifQuery, extractNotificationRowsFromDoc, isFacebookAuthRedirectPath, buildNotificationsScript
- Live IIFE 通过 \${fn.toString()} 嵌入 (dianping #1313 / hupu #1387 / xiaoe #1388 lineage)
- Locale 表 6 prefix / 4 badge label 显式列出
- AUTH_REQUIRED: sentinel → Node-side AuthRequiredError mapper

[Typed-error hardening]
- Path-anchored auth helper: isFacebookAuthRedirectPath(/^\/(?:login|checkpoint)(?:\.php)?(?:\/|\$)/i) — domain-invariant-first encoding (FB top-level auth-only invariant), 排除 /loginhelp /help/login /account/login/identify
- Three-layer navigateBefore=false invariant lock: registration assertion + manifest absence + executeCommand runtime page.goto-zero-call (test layer 与 invariant layer 完整对齐)
- Row-level silent-empty-row defense: anchor rows with no recoverable body text 直接 skip, 不 emit text:null success row

[Doc fix]
- docs/adapters/browser/facebook.md notifications enrichment + Output table (列类型 / null vs sentinel 语义) + auth/empty error contract
- Boy Scout audit: cross-checked profile / feed / search / marketplace-listings / marketplace-inbox 例 commands 与 args 定义一致

Tests
- notifications.test.js 39/39 + src/execution.test.ts 21/21
- Anti-pattern regression guards: not.toMatch(/text\.substring\(0,\s*150\)/) + not.toMatch(/time\s*\|\|/)
- JSDOM frozen-fixture (slim 13 lines, 0 blank): header listitem skip / full text / unread badge / query parsing / null time / blank-row skip / relative href absolute / 19-case auth path matrix
- typed-error-lint baseline 192 → 191 (silent-sentinel resolved 1)

Review iterations (4 head, A 组 codex-mini0 lead + First-principles-0 aux):
1. 052d2b18 (initial 29 tests) → 376cb50f (lead gate fix: Ubuntu lint + auth path-segment + anchor.href + 5 typed-error func tests)
2. 376cb50f → 0d6c1340 (pr-monitor grep cross-verify catch /login.php false-negative; lead 加 \\.php 边界)
3. 0d6c1340 → 3e5a5ff0 (opencli-user 19-case 实测 + lead 抽 named helper isFacebookAuthRedirectPath domain-invariant-first encoding + 2 row-shape silent-failure 顺手 catch)
4. 3e5a5ff0 → 36e44f73 (F-P-0 aux blocker: registry-injected navigateBefore 在 limit validation 之前 fire pre-nav 违反 #1387 upfront boundary; navigateBefore:false + 三层断言 registration/manifest/runtime executeCommand)

Closes #1391
2026-05-07 17:49:57 +08:00

2.6 KiB

Facebook

Mode: 🔐 Browser · Domain: facebook.com

Commands

Command Description
opencli facebook profile Get user/page profile info
opencli facebook notifications Get recent notifications with unread / time / url / notif_id / notif_type
opencli facebook feed Get news feed posts
opencli facebook search Search people, pages, posts
opencli facebook marketplace-listings List your Marketplace seller listings
opencli facebook marketplace-inbox List recent Marketplace buyer/seller conversations

Usage Examples

# View a profile
opencli facebook profile zuck

# Get notifications (default 15, max 100)
opencli facebook notifications --limit 10

# News feed
opencli facebook feed --limit 5

# Search
opencli facebook search "OpenAI" --limit 5

# Marketplace seller listings and inbox
opencli facebook marketplace-listings --limit 10
opencli facebook marketplace-inbox --limit 10

# JSON output
opencli facebook profile zuck -f json

Output

notifications

Column Type Notes
index int 1-based row number across the returned page
unread bool Derived from the explicit <div>未读</div> / <div>Unread</div> badge child; falls back to the anchor text prefix
text string Notification body text. Read first from the per-row "Mark as read" button's aria-label (with the locale prefix stripped) so it does not include the unread badge or trailing time. Full body, no silent truncation
time string | null Time-ago label from the row's <abbr>, e.g. 2天 / 5 hrs. null when the abbr is missing — never the legacy '-' sentinel
url string Full notification anchor href, including notif_id / notif_t query params, so callers can follow up
notif_id string | null notif_id query param parsed from url; null when absent
notif_type string | null notif_t query param (e.g. onthisday, approve_from_another_device, group_recommendation); null when absent

--limit accepts a positive integer in [1, 100]. Out-of-range or non-numeric input raises ArgumentError upfront — no silent clamp.

If Facebook redirects to a login/checkpoint path (for example /login.php, /login/identify/, or /checkpoint/; session expired) the command raises AuthRequiredError. An empty notification list after a successful auth check raises EmptyResultError instead of a silent [].

Prerequisites