Implements the skill mapping layer per opencli-generate SKILL.md:
- mapOutcomeToSkillOutput: thin translation from P1 contract to agent-facing output
- executeGenerateSkill: entry point accepting SkillInput (url + goal?)
- Message templates for all StopReason and EscalationReason values
- 8 tests covering all outcome paths and contract shape validation
- Add getDaemonHealth() returning 'stopped' | 'no-extension' | 'ready'
- Delete discover.ts (thin wrapper with no value)
- Bridge uses getDaemonHealth() + _pollUntilReady() (eliminates duplicate polling)
- Doctor simplified: live check auto-starts daemon; no-live mode does minimal
auto-start only when stopped (avoids misreporting idle-exit as failure)
- CommanderAdapter preserves error message/hint detail (not just generic title)
- All callers use single unified status entry point
* feat: add GitHub Trending, Binance, and Weather (Open Meteo) adapters
GitHub Trending (2 commands, browser mode):
- repos: trending repositories with stars, forks, language filter
- developers: trending developers with popular repos
Supports --since daily/weekly/monthly and --language filter
Binance (11 commands, public API via data-api.binance.vision):
- top: top trading pairs by 24h volume
- price: single pair 24h price stats
- prices: latest prices for all pairs
- ticker: 24h ticker statistics
- gainers: top gaining pairs by 24h change
- losers: top losing pairs by 24h change
- trades: recent trades for a pair
- depth: order book bid prices
- asks: order book ask prices
- klines: candlestick/kline data
- pairs: list active trading pairs
Weather / Open Meteo (11 commands, free public API, no key needed):
- current: current weather for a city
- forecast: daily forecast up to 16 days
- hourly: hourly forecast
- search: city geocoding lookup
- air: air quality index (simple)
- air-quality: detailed air quality (US/EU AQI, PM2.5, PM10, ozone, NO2, SO2)
- sunrise: sunrise/sunset times with UV index
- wind: detailed wind forecast with gusts and 80m altitude
- precipitation: rain/snow forecast with probability
- history: historical weather up to 92 past days
- compare: side-by-side weather comparison across cities
All 24 commands tested with live data. 258 existing tests pass.
* docs: add missing douban, sinablog, substack adapter documentation
* fix(binance): sort numeric metrics and filter active pairs
* chore: drop non-binance adapters from pr214
* chore: drop binance docs from pr214
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat: add Self-Repair protocol for automatic adapter fixing
When an AI agent uses opencli and a command fails, the agent
automatically diagnoses the failure, fixes the adapter, and retries.
- Add CLAUDE.md with Self-Repair protocol (auto-loaded by Claude Code)
- Add designs/self-repair-protocol.md documenting the approach
- Update opencli-repair skill: add Safety Boundaries (AUTH/BROWSER → STOP,
sourcePath-only scope, max 3 rounds), fix AUTH_REQUIRED guidance
- Update opencli-usage skill: add Self-Repair section
Key design decisions:
- Repair target is always RepairContext.adapter.sourcePath (works for both
repo-local clis/ and user-local ~/.opencli/clis/)
- Only adapter files may be modified, never core src/
- Max 3 repair rounds per failure
- AUTH_REQUIRED and BROWSER_CONNECT are hard stops (report, don't modify)
* fix: align auth boundary and scope language across all documents
- Remove "Auth changed (AUTH_REQUIRED)" exploration section from
opencli-repair skill — contradicted the hard stop rule above it
- Update design doc: scope language matches repo-local + explicit skill
delivery model, not universal product behavior
- Update usage skill: reference sourcePath instead of "files under clis/"
* fix: replace remaining repo-relative clis/ paths with sourcePath in design doc
* refactor: rename opencli-repair to opencli-autofix, remove CLAUDE.md
CLAUDE.md was wrong — users don't work inside the opencli repo, and
the protocol shouldn't assume Claude Code. The skill is the portable
delivery mechanism for any AI agent.
- Rename skills/opencli-repair → skills/opencli-autofix
- Remove CLAUDE.md (not the right delivery mechanism)
- Update all references in usage skill and design doc
- Design doc rewritten to reflect skill-first approach
* fix: use sourcePath in example repair session
* feat: emit AutoFix hint on repairable adapter errors
When a command fails with a repairable error (SELECTOR, EMPTY_RESULT,
COMMAND_EXEC, or generic http/not-found), the error output now includes
a hint telling agents to re-run with OPENCLI_DIAGNOSTIC=1 for repair
context. This is the trigger mechanism that bridges the gap between
"command failed" and "agent enters autofix loop".
Non-repairable errors (AUTH_REQUIRED, BROWSER_CONNECT, ARGUMENT) do not
emit the hint — these require user action, not adapter fixes.
* fix: narrow AutoFix hint to adapter-drift errors only
Remove hint from CommandExecutionError (covers env/launcher/runtime
issues, not adapter drift) and generic http errors (often temporary
site issues). Keep hint only for SelectorError, EmptyResultError,
and generic not-found — clear adapter-drift signals.
When the Browser Bridge extension is older than the CLI, sending
'network-capture-start' to the daemon returns 'Unknown action',
causing explore and operate-open to crash with an unhandled error.
Wrap startNetworkCapture calls with .catch() so they degrade
gracefully — explore continues without network capture data, and
operate-open falls back to the JS interceptor injection.
- engine.ts: replace `git add -A` with scope-aware `execFileSync` to
stage only files matching config.scope globs, and guard against empty
scope degenerating into staging all files
- fix.ts: pass prompt via stdin `input` option instead of shell string
interpolation to prevent $, backtick, and other metacharacter expansion
- generate.ts: update stale comment that claimed unimplemented pipeline
steps (register, verify, Strategy Cascade)
* refactor: remove scoring heuristic, replace with noise filter + metadata
The scoring mechanism was a pre-LLM heuristic that compressed rich endpoint
metadata into a single number. Since this project is designed for AI Agents,
the agent can reason about structured metadata directly.
Changes:
- Remove scoreEndpoint/scoreRequest/scoreWriteRequest and all score fields
- Replace with isNoiseUrl() filter (tracking/beacon/pixel) + isUsefulEndpoint()
- Remove artificial confidence percentages (was score/20)
- Sort by itemCount (transparent, observable) instead of weighted score
- Endpoints now expose full structured metadata for agent consumption
- Net reduction: -43 lines
* fix: widen endpoint filter to keep single-object JSON and stats/metric URLs
- Remove stats/metric from noise pattern — these are often business APIs
- Relax isUsefulEndpoint to keep any JSON endpoint, not just arrays
(preserves /me, /profile, /detail and other single-object APIs)
* fix: add deterministic endpoint ordering for generate/synthesize path
The AI agent path doesn't need ranking, but generate/synthesize still
pick candidates[0] as default — this needs a stable, explainable order.
- Add endpointSortKey() with transparent observable signals: array items,
detected fields, API path patterns, query params
- Update synthesize chooseEndpoint fallback to use itemCount + field count
- Sort key is internal only; not exposed as score to external consumers
* refactor: extract shared scoring logic and consolidate time format utils
- Extract applyUrlScoreAdjustments() and scoreArrayResponse() to analysis.ts,
eliminating duplicated endpoint scoring between explore.ts and record.ts
- Consolidate formatDuration/formatUptime into a single formatDuration(ms)
in download/progress.ts, reused by commands/daemon.ts
* fix: preserve explore scoring semantics and round daemon uptime
- Revert explore.ts scoreEndpoint to original inline /api/ /x/ bonus
without record's tracking/analytics penalty (blocker from review)
- Math.round uptime*1000 to avoid floating-point noise in daemon status
* feat(operate): unify network capture + implement CDP consoleMessages
- operate open: start session capture before navigation (catches initial requests)
- operate network: prefer readNetworkCapture() over JS interceptor
- CDPPage: implement consoleMessages() via Runtime.consoleAPICalled
Part of #810
* fix(operate): use correct daemon/CDP entry field names for network capture
Daemon and CDP capture entries use responseStatus/responseContentType/
responsePreview (not status/contentType/responseBody). Fix the
normalization in operate network to match the actual entry shape from
extension/src/cdp.ts.
* fix(cdp): capture Runtime.exceptionThrown in consoleMessages
- Register Runtime.exceptionThrown handler to capture uncaught exceptions
as error-level messages (most valuable diagnostic signal)
- 'error' filter now returns both console.error() and warning/exception
entries, matching typical severity-based logging semantics
* feat(cdp): implement session-level network capture for CDPPage
Implements startNetworkCapture() and readNetworkCapture() on CDPPage using
CDP Network domain events. Updates explore.ts to prefer session capture
over Performance API networkRequests().
Closes part of #810
* fix(cdp): use Network.loadingFinished for reliable body capture
- Move getResponseBody call from responseReceived to loadingFinished,
matching the extension's implementation pattern
- Use extension-compatible entry shape (responseStatus, responseContentType,
responsePreview) instead of custom field names
- Remove unreliable 100ms sleep hack in readNetworkCapture()
- Align with extension/src/cdp.ts:419-437 for consistency
* fix(cdp): drain buffer on readNetworkCapture to match daemon contract
readNetworkCapture() must clear the buffer after reading, matching the
daemon Page's read-and-drain behavior. Without this, repeated reads
would return stale entries.
* fix(cdp): await in-flight body fetches before returning from readNetworkCapture
Track all pending getResponseBody promises and await them in
readNetworkCapture() before draining the buffer. This ensures
explore/diagnostic consumers always get entries with responsePreview
populated, not empty shells where the body fetch hasn't resolved yet.
* fix(explore): handle both legacy and capture entry field names
parseNetworkRequests now maps both shapes:
- Legacy: status, contentType, responseBody
- Capture (extension/CDP): responseStatus, responseContentType, responsePreview
Also clears _pendingBodyFetches on startNetworkCapture reset.
* fix: add safety boundaries to diagnostic output
- Redact sensitive headers (Authorization, Cookie, etc.) from network requests
- Redact sensitive URL query parameters (token, key, secret, etc.)
- Cap individual fields: snapshot (100K chars), adapter source (50K chars),
network requests (50 entries, 4K body each), stack trace (5K chars)
- Enforce 256KB total output budget with graceful degradation:
drops snapshot first, then page state entirely
- Export truncate/redactUrl helpers for testing
* fix: add free-text redaction for all diagnostic string channels
Addresses review feedback: snapshot, consoleErrors, error message/hint/stack
could contain inline secrets (Bearer tokens, JWTs, cookie values, token=value
patterns). All string channels now pass through redactText() before emission.
- Add redactText() with patterns for Bearer tokens, JWTs, cookie values,
and inline key=value secrets
- Apply redactText to: error.message, error.hint, error.stack,
page.snapshot, page.consoleErrors
- Add 6 new test cases for redactText and error message redaction
* fix: resolve adapter source path and add page state collection timeout
Fixes#808 items 1 and 3:
1. adapter.source was missing for all command types because buildRepairContext
only checked cmd._modulePath (set only for manifest lazy-loaded TS).
Now resolveAdapterSourcePath() checks cmd.source first, skips manifest:
pseudo-paths, and maps dist/clis/*.js back to source clis/*.ts.
3. collectPageState() had no timeout — a hung CDP connection would block
error propagation indefinitely. Now wrapped with 5s Promise.race timeout,
falling back to emitting diagnostic without page state.
* fix: track sourceFile in manifest for YAML adapter source resolution
YAML commands inlined in the manifest previously lost their original file
path, causing resolveAdapterSourcePath() to return undefined. Add
sourceFile field to ManifestEntry so discovery can reconstruct the
editable source path for both YAML and TS commands.
* feat: add structured diagnostic output for AI-driven adapter repair
When OPENCLI_DIAGNOSTIC=1 is set, failed commands emit a RepairContext
JSON to stderr containing the error, adapter source, and browser state
(DOM snapshot, network requests, console errors). AI Agents consume
this to diagnose and fix adapters when websites change.
Also adds the opencli-repair skill guide for AI Agents.
* fix: correct e2e test binary path to dist/src/main.js
The e2e helpers pointed to dist/main.js but the actual build output
is at dist/src/main.js (matching package.json "main" field). This
caused all e2e-headed tests to fail with "Cannot find module".
* fix: correct dist/main.js path in autoresearch scripts
* fix: emit diagnostic for pre-session browser failures
When browser connection fails before the session callback runs
(e.g., BrowserConnectError), the inner diagnostic catch never fires.
Use a flag to ensure the outer catch emits diagnostic as a fallback.
* test: tolerate unavailable Bloomberg RSS feeds in e2e
* test: skip flaky bloomberg businessweek e2e test
The Bloomberg Businessweek RSS feed is intermittently unavailable,
causing CI failures unrelated to code changes.
* revert: restore bloomberg businessweek e2e coverage
* fix(discovery): expose runtime deps to user adapters
* fix: route copied adapters through opencli exports
* refactor: route adapter status output through logger
* refactor: migrate adapter imports to package exports
Replace all relative imports (../../src/registry.js, ../../browser/cdp.js, etc.)
with package exports (@jackwener/opencli/registry, @jackwener/opencli/errors, etc.)
across all 484 adapter files.
This decouples adapter import resolution from directory structure:
- User CLIs in ~/.opencli/clis/ resolve via node_modules symlink
- Internal adapters resolve via Node.js self-referencing
- No more shim files needed for import resolution
Changes:
- package.json: add sub-path exports for all public modules
- clis/**: replace relative imports with @jackwener/opencli/...
- discovery.ts: simplify ensureUserCliCompatShims to symlink-only
- registry-api.ts: export CommandArgs type
- Remove root-level shim directories (browser/, download/, pipeline/)
- Remove shim entries from tsconfig.json include and package.json files
* test: add regression tests for package exports
Prevents regressions like #788/#791 by:
1. Scanning all adapter files for forbidden relative imports
(../../src/, ../../browser/, etc.) — fails if any remain
2. Verifying every package.json export maps to an existing source file
18 new test cases.
* fix: use junction on Windows + broaden test patterns
- discovery.ts: use 'junction' symlink type on Windows (no admin required)
- package-exports.test.ts: generalize forbidden patterns to catch any
depth of ../ traversal (not just ../../ and ../../../)
* fix: update stale vi.mock/importActual paths in adapter tests
Test files still used old relative paths for vi.mock() and
vi.importActual() calls. Updated 5 test files to use package exports.
Also broadened regression test patterns to catch mock/importActual paths.
* fix: use rm instead of unlink for symlink cleanup, add warn on failure
Addresses review feedback from Astro-Han:
- rm() handles both symlinks and stale directories (unlink fails on dirs)
- Log a warning when symlink creation fails instead of silent catch
* docs: update import examples to use package exports
Update all documentation, contributing guides, and skills to use
@jackwener/opencli/registry instead of ../../src/registry.js.
Without this, users following the docs would write adapters with
broken imports since the old shim files are no longer created.
Bug 1: version.ts used a single-level parent lookup for package.json,
which broke after #784 changed rootDir from "src" to "." (version.js
now lives in dist/src/ instead of dist/). Walk up until package.json
is found — works in both dev (src/) and prod (dist/src/).
Bug 2: adapters copied to ~/.opencli/clis/ import ../../src/registry.js
etc., which resolves to ~/.opencli/src/. Derive src/ compat shims from
the existing rootShims list so these imports resolve correctly.
* refactor: simplify core modules — remove root shims, consolidate error classification, streamline cascade/interceptor, clean up synthesize
1. Remove root-level shim files (errors.ts, logger.ts, registry.ts, types.ts, utils.ts, launcher.ts) — update all ~840 adapter imports to reference src/ directly
2. Consolidate interceptor: reuse shared DISGUISE_FN in tap interceptor instead of reimplementing
3. Unify error classification: single ClassifiedError type with icon/exitCode/hint lookup table, eliminating duplicated pattern matching between resolveExitCode and renderError
4. Simplify cascade probe: replace repetitive switch cases with PROBE_OPTIONS lookup map
12. Clean up synthesize.ts: remove deprecated snake_case field aliases (recommended_args, recommended_columns, recommendedColumnsLegacy) and unnecessary constant aliases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: update import paths in contributor docs and skill templates
Update all documentation and skill files to reference src/ directly,
matching the shim removal in the previous commit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update new Gemini adapter imports to use src/ paths
Fix imports in newly added deep-research adapter files that were
still referencing the deleted root shim files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update xiaohongshu tests for /search_result/ URL change
Tests now expect /search_result/<id> for bare note IDs (matching
the note-helpers.ts change from PR #774) and updated empty-shell
hint assertion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update LessWrong and hupu adapter imports to use src/ paths
Fix imports in newly merged LessWrong and hupu/mentions adapter
files that were still referencing the deleted root shim files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(xueqiu): mock logger via src path
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: review follow-ups — better first-run log, OPENCLI_FETCH=1 skips version check
- Clarify first-run log message: "copying adapters (one-time setup)"
- Add comment explaining why scriptPath uses two levels of ../
- OPENCLI_FETCH=1 now bypasses version-skip to allow forced refresh
* fix: update doc-coverage script path after clis/ move
check-doc-coverage.sh still referenced src/clis/ after PR #782 moved
adapters to root clis/. This caused CI to fail with "0/1 documented".
* fix: resolve package root dynamically for symlink and first-run paths
The symlink at ~/.opencli/node_modules/@jackwener/opencli pointed to
dist/ instead of the package root in prod mode, breaking user TS CLIs
that import from '@jackwener/opencli/registry'.
The first-run scriptPath also resolved incorrectly in dev mode.
Extract findPackageRoot() that walks up to find package.json, fixing
both paths for dev (src/) and prod (dist/src/) layouts.
* refactor: move adapters from src/clis/ to root clis/ for monorepo separation
Separates CLI adapters from the core runtime to prepare for independent
adapter distribution via postinstall fetch.
Key changes:
- Move src/clis/ → clis/ (adapters at repo root)
- Change tsconfig rootDir from "src" to "." so tsc compiles both
- Create root-level shim files (registry.ts, errors.ts, etc.) so adapter
relative imports (../../registry.js) resolve correctly
- Update build-manifest.ts, main.ts paths for new dist/src/ structure
- Expand ensureUserCliCompatShims() to cover all adapter import targets
(types, utils, logger, launcher, browser/*, download/*, pipeline/*)
- Add scripts/fetch-adapters.js postinstall for ~/.opencli/clis/ sync
- Update vitest.config.ts adapter test paths
- Add package.json files field to exclude adapters from npm package
Official adapter files are unconditionally overwritten on update;
user-created files not in the manifest are preserved.
* fix: add dist/clis/ and cli-manifest.json to npm files, harden fetch-adapters
- Add dist/clis/ and dist/cli-manifest.json to package.json files field
so built-in adapters and manifest ship with the npm package
- Replace execSync with execFileSync to prevent command injection
- Add version check to skip redundant adapter fetches
- Track tmpRoot explicitly for reliable cleanup
* fix: address review blockers — manifest-based updates, global-only fetch, first-run fallback
1. Manifest-based update strategy:
- Read old manifest to identify previously-official files
- Clean up files removed upstream (in old manifest but not new)
- User-created files (never in any manifest) remain untouched
2. Only run fetch-adapters on global install (npm_config_global=true)
or explicit OPENCLI_FETCH=1, preventing heavy side effects for
local/dev installs
3. First-run fallback in discovery.ts:
- ensureUserAdapters() checks for adapter-manifest.json
- If missing and ~/.opencli/clis/ is empty, spawns fetch-adapters.js
- Guarantees adapters are available even with --ignore-scripts
* fix: remove OPENCLI_FETCH env var, use internal _OPENCLI_FIRST_RUN instead
* feat: also support OPENCLI_FETCH=1 for explicit adapter fetch trigger
* simplify: replace git clone with local copy from dist/clis/
Adapters already ship in the npm package (dist/clis/), so there's no
need to clone from GitHub. Copy directly from the installed package:
- Eliminates git, curl, tar dependencies
- No network calls in postinstall
- No timeout/offline issues
- Version always matches the installed CLI
- ~65 lines of clone/download code replaced by one cpSync loop
* feat(hupu): add hupu cli adapter
* fix(hupu): prevent detail from returning the wrong thread
* refactor: deduplicate shared utilities in hupu adapter
- Merge postHupuJson and postHupuReplyJson into single function with mode parameter
- Move stripHtml and decodeHtmlEntities to utils.ts, remove duplicate definitions
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat: 推特新增回复图片能力支持本地路径和网络路径
* fix(twitter/reply): fix image upload fallback, restore execCommand, add size limit
- Fix attachReplyImage fallback: use uploaded flag instead of checking
page.setFileInput existence, so base64 fallback actually runs when
CDP setFileInput throws "Unknown action"
- Restore execCommand('insertText') as primary text input method for
Twitter's Draft.js editor, with paste event as fallback
- Add 20MB size limit for remote image downloads to prevent OOM
- Remove unsafe buttons[0] fallback that could click invisible buttons
* fix(twitter/reply): add local image size check and base64 fallback warning
Local images were not validated for size — a 100MB file would fail only
at upload time. Remote images already had MAX_IMAGE_SIZE_BYTES checks.
Also add a console.warn when using the base64 fallback with large
payloads, consistent with xiaohongshu/publish.ts behavior.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(xiaoe): add 小鹅通 (Xiaoe-tech) student platform adapter
Add 5 YAML adapters for 小鹅通 (xiaoe-tech.com), the leading Chinese
online education platform:
- courses: list purchased courses with URLs and shop names
- detail: course info (name, price, user count, shop)
- catalog: full course outline supporting normal courses (type 50),
columns (type 6), and big columns (type 8)
- play-url: get M3U8 play URL via direct API for video courses,
and Vue component tree search + Performance API polling for
live replay courses
- content: extract rich-text page content as plain text
Technical notes:
- Strategy: cookie (reuses Chrome login session)
- Framework: Vue 2 + Vuex Store (SPA)
- Video courses use a two-step API chain:
detail_info.get → play_sign → getPlayUrl → M3U8
- Live replays use Performance API + Vue data tree polling
- Catalog expands chapters via Vue component method getSecitonList()
- Supports multiple stores (cross-domain cookie sharing via
study.xiaoe-tech.com)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* review: stop truncating xiaoe content
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix: add -v/--verbose to explore, record, generate, cascade
Built-in browser commands were registered directly in cli.ts and
missed the -v/--verbose flag that commanderAdapter.ts wires up for
adapter commands. Also switch explore's lone log.debug() call to
log.verbose() so the flag has visible effect.
Closes#716
* refactor(cli): make builtin command wiring testable
* refactor(cli): simplify verbose wiring, use normal Commander pattern
Replace registerVerboseAction wrapper with simple applyVerbose() helper.
The wrapper broke Commander's builder chain and created awkward
indentation. Now each command uses standard .option().action() with
applyVerbose(opts) as the first line — easier to read and maintain.
* fix(cli): add -v/--verbose to doctor and synthesize commands
These commands were also missing verbose support, same root cause as
explore/record/generate/cascade — registered directly in cli.ts,
bypassing commanderAdapter's automatic -v wiring.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(twitter): add --images flag to post command
Support attaching up to 4 images when posting tweets via
`opencli twitter post "text" --images /path/a.png,/path/b.jpg`.
Uses the existing CDP DOM.setFileInputFiles mechanism (page.setFileInput)
to inject files into Twitter's file input. Includes proper file validation,
graceful error handling for older extensions, and polling-based upload
readiness detection instead of fixed delays.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(twitter): use attachments DOM signal for upload detection, add tests
Replace unreliable tweet-button-only polling with dual-condition check:
wait for [data-testid="attachments"] with correct [role="group"] count
AND button enabled. Increase timeout to 30s. Add 8 unit tests covering
image upload flow, file validation, and error paths.
Addresses PR #666 review feedback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(twitter): use top-level imports, fix test mocks, faster upload poll
- Use top-level fs/path imports instead of dynamic imports inside func
- Fix test statSync mock to return undefined (not null) for missing files
- Fix test path mock to preserve other exports via importOriginal
- Fix null type error in no-browser-session test
- Reduce upload poll interval from 1s to 500ms for faster detection
- Use JSON.stringify for imageCount interpolation for consistency
* refactor(twitter): extract validation, fail-fast, reduce duplication
- Extract validateImagePaths() with extension validation (jpg/png/gif/webp)
matching xiaohongshu publish pattern
- Validate images before browser navigation (fail-fast on bad input)
- Remove try/catch wrapper around setFileInput — let errors propagate
naturally instead of masking the original error
- Deduplicate tweetButton/tweetButtonInline lookups using fallback OR
- Use constants for MAX_IMAGES, UPLOAD_POLL_MS, UPLOAD_TIMEOUT_MS
- Add tests: unsupported format, validates-before-navigating
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* fix(gemini): stabilize ask reply state handling
* fix: use CommandExecutionError for composer failures and clean up formatting
- Replace raw Error with CommandExecutionError for Node-side composer
failures (prepareComposer, insertText) to match adapter error conventions
- Remove extra blank lines after __test__ export
* refactor: remove dead code and add Chinese sign-in label
- Remove unused areGeminiTurnsEqual and areGeminiLinesEqual functions
- Add Chinese sign-in label (登录) to sign-in detection for consistency
with other Chinese labels already added in this PR
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
Move data processing (HTML stripping, answer mapping) from browser-side
evaluate to Node-side, keeping the evaluate minimal: just fetch + status
check. Uses __httpError sentinel consistent with pixivFetch convention.
- Remove hover_price_text as MOQ source in search normalizeSearchCandidate
to prevent price fields from being misinterpreted as MOQ data
- Rename firstLine() to firstWord() to match its actual behavior (splits
by whitespace, not newlines)
- Add missing "单" unit to item.ts extractSalesText regex
- Add test case verifying hover_price_text is not used for MOQ
* fix(zhihu): make question runtime-compatible
* fix: validate questionId is numeric to prevent interpolation issues
* refactor: simplify evaluate string and harden against injection
- Build URL in Node.js, embed via JSON.stringify for safety-by-design
- Remove unnecessary (page as any) cast — IPage already has evaluate
- Simplify error message construction (no nested ternaries)
- Replace implementation-detail test with numeric ID validation test
* refactor: simplify zhihu question — move stripHtml into evaluate, return clean data
* fix: add colon separator in fetch error message for readability
"request failed Failed to fetch" → "request failed: Failed to fetch"
---------
Co-authored-by: Kyrie <kyrie@mallab.world>
Co-authored-by: jackwener <jakevingoo@gmail.com>
1. marks: correct pageSize from 30 to 15 — douban grid mode shows 15
items per page, causing pagination to stop after the first page.
2. subject: split title/originalTitle correctly — v:itemreviewed contains
both Chinese and original titles concatenated.
3. subject: extract country/region from #info as list, split by "/".
4. subject: extract duration as pure number (min) from v:runtime or #info.
5. subject: return casts as list instead of comma-joined string.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- bilibili subtitle/comments tests: use importOriginal to include
resolveBvid in utils mock
- comments test: use valid BV ID format for aid-resolution error test
- launcher test: skip pgrep test on win32 (detectProcess early-returns)
* fix(windows): graceful degradation and manual CDP override for Electron apps
* fix: validate OPENCLI_CDP_ENDPOINT with probeCDP before use
Fail-fast with a clear error if the manual CDP endpoint is not reachable,
instead of passing a bad URL downstream and getting a confusing error.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(bilibili): support b23.tv short URL/short code resolution
Add resolveBvid() in utils.ts to automatically resolve b23.tv short URLs
and short codes to BV IDs. Supports all input formats:
- BV ID: BV1MV9NBtENN (pass through)
- Short code: XYzsqGa
- Short URL: https://b23.tv/XYzsqGa, b23.tv/XYzsqGa
Uses Node.js https.get with 302 redirect only (no body download),
typically ~100-250ms resolution time.
Applied to: subtitle, comments, download commands.
* fix: add timeout, input coercion, and tests for resolveBvid
- 5s timeout on https.get to prevent hanging on unresponsive b23.tv
- Accept unknown input type with String() coercion
- Simplify callers (remove redundant String().trim() wrappers)
- Add unit tests for BV ID passthrough and edge cases
---------
Co-authored-by: chenruinian <chenruinian@Sa1kas-MacBookPro.local>
Co-authored-by: jackwener <jakevingoo@gmail.com>