Skip to content

feat(stealth): engine owns all anti-detect stealth#2

Merged
codeisalifestyle merged 4 commits into
mainfrom
feat/stealth-ownership
Jun 13, 2026
Merged

feat(stealth): engine owns all anti-detect stealth#2
codeisalifestyle merged 4 commits into
mainfrom
feat/stealth-ownership

Conversation

@codeisalifestyle

Copy link
Copy Markdown
Owner

Summary

Completes the architectural realignment: the engine (mithwire) now owns every browser-altering anti-detect capability, and any client (the MCP, or any custom script) is a pure consumer. The engine is fully agnostic about how it's used.

  • New mithwire/stealth/ package:
    • fingerprint.pyFingerprintConfig (moved into the engine; was previously owned client-side).
    • controller.pyStealth controller that applies the anti-detect patch set (window.chrome shim, headless UA cleanup, WebRTC leak protection, JS injections, etc.) against a live browser.
    • __init__.pycompute_launch_args helper for stealth-specific Chrome flags (--lang, --force-webrtc-ip-handling-policy, --window-size).
  • Config accepts fingerprint + webrtc_leak_protection and injects stealth launch args via compute_launch_args.
  • Browser.start() applies stealth automatically after connect/attach and exposes the live Stealth instance as browser.stealth so a client can re-apply an identity later (e.g. once a proxy egress geo resolves). Stealth presence is inferred purely from launch flags, so the engine never depends on a client's proxy abstraction.
  • Engine root re-exports FingerprintConfig, Stealth, compute_launch_args.

Bundled fixes required to land this safely:

  • test(connect): stub _apply_stealth in the connect-retry suite (same rationale as the existing attach/update_targets stubs — it's a post-connect side effect needing a real browser, and its ~1.2s settle sleep would otherwise break the warm-connect timing assertion).
  • fix(cdp): repair an invalid latin-1 byte in network.py that broke import mithwire from a clean checkout.
  • ci(release): port the two release-pipeline fixes proven on mithwire-mcp (fix: ship the mithwire.stealth subpackage in the wheel #5, chore(main): release 0.50.5 #6) — the latent release_created gate bug and the success() skip-cascade — before the engine's first release-please-driven publish trips over them. Also adds the tag-replay dispatch escape hatch.

Test plan

  • import mithwire + from mithwire import Stealth, FingerprintConfig, compute_launch_args resolve from a clean uv sync.
  • uv run pytest -q3 passed (connect-retry regression suite green after the _apply_stealth stub).
  • CI green (mocked, no Chrome).
  • Post-merge: release-please cuts a minor bump (0.50.3 → 0.51.0); publish chain reaches PyPI.

Note: this branch ships the engine-side ownership + auto-application. It does not yet remove the now-duplicated stealth implementation from mithwire-mcp (browser.py / fingerprint.py); the MCP keeps working via its existing path and its mithwire>=0.50,<0.60 pin already accepts the new release. The MCP-side delegation is a tracked follow-up.

Made with Cursor

codeisalifestyle and others added 4 commits June 13, 2026 17:10
A raw 0xB1 (latin-1 '±') byte in a generated docstring made
mithwire/cdp/network.py invalid UTF-8 with no encoding declaration, so a
clean-source 'import mithwire' failed (it only worked off cached bytecode).
Re-encode the byte as proper UTF-8.

Co-authored-by: Cursor <cursoragent@cursor.com>
Move the anti-detect implementation out of the mithwire-mcp client and into
the engine, which now owns every browser-altering capability. Any client (the
MCP server, or a custom script) gets identical stealth by describing the
identity it wants; it never reimplements patching.

- add mithwire/stealth/ package:
  - FingerprintConfig: declarative identity (moved verbatim from the MCP)
  - Stealth: applies fingerprint, headless UA cleanup, WebRTC leak protection,
    the new-document shim, and the timezone override over CDP/JS
  - compute_launch_args: stealth launch flags (--lang,
    --force-webrtc-ip-handling-policy, headless window size)
- Config accepts fingerprint + webrtc_leak_protection and injects the launch
  flags; proxy presence is inferred from --proxy-server (engine stays agnostic
  of any client's proxy abstraction)
- Browser.start() applies stealth post-launch and exposes browser.stealth for
  client re-application (e.g. once a proxy egress geo is resolved)
- start()/Config thread the new params through

Co-authored-by: Cursor <cursoragent@cursor.com>
The stealth-ownership change added `await self._apply_stealth()` to
`Browser.start`, a post-connect side effect that drives CDP overrides and
new-document scripts against the live tab. The connect-retry regression
suite already stubs `attach`/`update_targets` for the same reason (they
need a real browser/websocket and aren't the unit under test); without
also stubbing `_apply_stealth` the mocked CDP raised StopIteration ->
RuntimeError, and its real ~1.2s settle sleep broke the warm-connect
timing assertion. Stub it alongside the others.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ispatch

Port the two release-pipeline fixes proven on mithwire-mcp (#5, #6) before
the engine's first release-please-driven publish exercises the same code.

- Gate the publish chain on the top-level `release_created` output.
  release-please-action@v4 leaves the path-prefixed
  `outputs['.--release_created']` key empty for a single-root manifest
  config; the engine never hit this only because v0.50.3 shipped via
  workflow_dispatch and bypassed the gate. The release-please push path
  would otherwise tag + release without ever publishing.
- Add `always() && needs.build.result == 'success'` to testpypi/pypi.
  GitHub Actions' implicit success() gate evaluates the transitive needs
  graph, so a skipped release-please (workflow_dispatch path) cascades a
  skip through build into the publish jobs regardless of build's outcome.
- Add a `tag` workflow_dispatch input to (re)publish an existing tag
  verbatim -- an escape hatch for any tag-without-publish drift.
- Consolidate the prerelease decision in the build job's `publish_pypi`
  output so the release-please and tag-replay paths share one rule.

Co-authored-by: Cursor <cursoragent@cursor.com>
@codeisalifestyle codeisalifestyle merged commit cda7806 into main Jun 13, 2026
1 check passed
@codeisalifestyle codeisalifestyle deleted the feat/stealth-ownership branch June 13, 2026 21:15
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.

1 participant