§
    ¬	ÊjV  ã                  óZ  — U d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m
Z
 ddlmZmZmZmZ ddlmZ dId„ZdJd„Zdddddddddddœ
Zded<   dKd „ZeZdddddd!œdLd*„Z G d+„ d,e¦  «        ZdMd0„ZdNd2„ZdOd3„Zdd4lmZ dd5l m!Z! e G d6„ d7¦  «        ¦   «         Z"dd8d8ddddd9dd:œ	dPdH„Z#dS )QzÊDirect-launch helpers shared by the Playwright wrapper and the profile
manager: write a user.js from a prefs dict, and build the subprocess env the
patched binary reads at startup. No Playwright, no Qt.é    )ÚannotationsN)Ú	dataclassÚfield)ÚPath)ÚAnyÚDictÚListÚOptionalé   )ÚDESKTOP_ENVÚnameÚstrÚvr   Úreturnc                óJ  — t          |t          ¦  «        r|rdndS t          |t          ¦  «        rt          |¦  «        S t          |t          t          f¦  «        r!t          j        t          |¦  «        ¦  «        S t          d| ›dt          |¦  «        j	        ›d�¦  «        ‚)u9  Serialize a pref value the way Firefox's prefs parser accepts it.

    Firefox prefs are int / bool / string only - there is no float pref type;
    a fractional value (e.g. device-pixel-ratio) is stored as a STRING and the
    float pref parses it back. ``json.dumps`` would emit a bare ``1.25``, which
    Firefox rejects with ``prefs parse error: unexpected character`` and which
    invalidates every ``user_pref`` line after it. That is not hypothetical:
    ``ui.textScaleFactor`` written as a number once killed the browser on the
    second context, and the failure looked nothing like a prefs problem.

    â›” ANY OTHER TYPE IS REFUSED HERE, not written. ``None`` or a list came out
    as ``null`` / ``[...]``, the same parse error, and the Juggler client's own
    writer turned them into the string ``"None"`` instead - a pref that exists
    with a value nobody asked for. Neither occurs in what the core composes
    (bool, int and str over 300 seeds, measured when the writers became one);
    a caller's pref of another type is a mistake to say, at its birth.
    ÚtrueÚfalsezpref z is a zK; Firefox prefs are bool, int or string (a fraction is written as a string))
Ú
isinstanceÚboolÚintr   ÚfloatÚjsonÚdumpsÚ	TypeErrorÚtypeÚ__name__)r   r   s     úEC:\Projects\Muse2API\.venv\Lib\site-packages\invisible_core/launch.pyÚ_pref_literalr      s£   € õ$ �!•TÑÔð (ØÐ'ˆvˆv Ð'Ý�!•SÑÔð Ý�1‰vŒvˆÝ�!•e�S�\Ñ"Ô"ð "ÝŒz�#˜a™&œ&Ñ!Ô!Ð!Ý
ˆ)Ø=A¸T¸TÅ4ÈÁ7Ä7ÔCSÐCSÐCSðUñ Vô Vð Vó    Úprofile_dirú'str | os.PathLike[str]'ÚprefsúDict[str, Any]r   c                ó  — t          | ¦  «        }|                     dd¬¦  «         |dz  }d„ |                     ¦   «         D ¦   «         }|                     d                     |¦  «        dz                        d¦  «        ¦  «         |S )uA  Write ``prefs`` as ``user_pref(...)`` lines into ``<profile_dir>/user.js``.

    Creates ``profile_dir`` if missing; overwrites any existing ``user.js``.

    â›” THE ONE WRITER, since 38.34.0. The Juggler client carried its own
    (`_write_user_js`), which every wrapper's launch went through, while this
    one served the direct launch: they agreed on the lines and differed on the
    bytes, because this one wrote in text mode and Windows turned every newline
    into CRLF. They became one when the client moved here (decision D85).

    â›” THE PREFS ARE WRITTEN, NOT SENT, and BEFORE the browser starts. Our fork
    removed prefs from the protocol - `Browser.enable` does not accept them -
    because a browser configured on the second launch was wrong on the first.
    The Python path once threw `firefoxUserPrefs` away and started Firefox on
    an empty profile: everything worked, every test was green, and every
    stealth declaration was absent. A closed shadow root made it visible, since
    the patch that reaches inside one is gated on a pref.

    â›” INSERTION ORDER, NO HEADER, LF ONLY, and the reason is one: the file has
    to match what Playwright's driver writes BYTE FOR BYTE (its `defaultArgs`
    uses `Object.keys()` and writes no comment), and the wrapper's
    `tests/gates/prefs_byte_parity.py` is the judge. Sorting produces a file
    that is equally correct and not identical, which that gate caught on its
    first real run. Bytes, not text: a text-mode write on Windows translates
    every newline, and a prefs file is read by the browser, not by git.
    T©ÚparentsÚexist_okzuser.jsc           	     óf   — g | ].\  }}d t          j        |¦  «        › dt          ||¦  «        › d�‘Œ/S )z
user_pref(z, z);)r   r   r   )Ú.0Úkr   s      r   ú
<listcomp>z!write_user_js.<locals>.<listcomp>J   sB   € Ð]Ð]Ð]ÁtÀqÈ!ÐB�$œ* Q™-œ-ÐBÐB­=¸¸AÑ+>Ô+>ÐBÐBÐBÐ]Ð]Ð]r   ú
zutf-8)r   ÚmkdirÚitemsÚwrite_bytesÚjoinÚencode)r    r"   ÚdÚoutÚliness        r   Úwrite_user_jsr5   ,   s�   € õ6 	ˆ[ÑÔ€AØ‡G‚G�D 4€GÑ(Ô(Ð(Ø
ˆi‰-€CØ]Ð]ÈuÏ{Ê{É}Ì}Ð]Ñ]Ô]€EØ‡O‚O�T—Y’Y˜uÑ%Ô%¨Ñ,×4Ò4°WÑ=Ô=Ñ>Ô>Ð>Ø€Jr   ÚEST5EDTÚCST6CDTÚMST7MDTÚPST8PDTÚMST7Ú	AKST9AKDTÚHST10)
zAmerica/New_YorkzAmerica/DetroitzAmerica/Indiana/IndianapoliszAmerica/Kentucky/LouisvillezAmerica/ChicagozAmerica/DenverzAmerica/Los_AngeleszAmerica/PhoenixzAmerica/AnchoragezPacific/HonoluluúDict[str, str]Ú_IANA_TO_POSIX_TZÚtimezonec                ó8   — t                                | | ¦  «        S )a¹  The value to put in ``TZ`` for an IANA zone.

    PUBLIC since 18.5.0. It was private, so the wrapper kept its own byte-identical
    copy of both this and the table - and the core's own comment admitted the table
    was "copied verbatim from the wrapper". A private name is not a reason to
    duplicate ten entries whose Phoenix row exists because getting it wrong made an
    identification service deduce the wrong origin timezone.
    )r>   Úget)r?   s    r   Útz_envrB   _   s   € õ × Ò  ¨8Ñ4Ô4Ð4r   )r?   Úsrflx_declaredÚmanifest_pathÚbase_envÚdisplay_envúOptional[str]rC   rD   ú"'Optional[str | os.PathLike[str]]'rE   úOptional[Dict[str, str]]rF   ú"Optional[Dict[str, Optional[str]]]c               ó¢  — t          |€t          j        n|¦  «        }|rt          |¦  «        |d<   |�#|                     dt          |¦  «        ¦  «         |                     d¦  «        p|}|r
||d<   d|d<   |                     t          d¦  «         |pi  	                    ¦   «         D ]#\  }}	|	€|                     |d¦  «         Œ|	||<   Œ$|S )a˜  Subprocess env for the patched binary - the ONE place that composes it.

    The wrapper's `_session.build_env` delegates here after verifying the font
    manifest against the executable; until 34.26.0 it was a twin of this body,
    and the hidden-surface half of the contract lived only in the twin, so a
    consumer on this builder could not express a removal at all.

    TZ tunes the libc clock. STEALTHFOX_WEBRTC_PUBLIC_IP feeds nICEr the proxy
    egress IP; an already-set value in base_env wins.

    STEALTHFOX_FONT_MANIFEST points the engine at the font manifest THIS package
    declares, and it has to be an env var rather than the pref of the same name:
    the font list is built during app startup, inside
    InitSharedFontListForPlatform, and on the Juggler path the caller's prefs
    never reach the profile at all - they travel over the wire and are applied
    by JS once the browser is already running. Measured 2026-08-08: removing a
    family from this package's copy changed nothing, because the pref read empty
    and the engine's own file won. An env var is set at process creation and
    inherited by every content process.

    It carries a PATH. The manifest is ~20 KB and an environment block is not
    the place for it. `manifest_path=None` writes no variable, which leaves the
    engine on its packaged copy - the floor a browser launched without this
    package still has to stand on.
    NÚTZÚSTEALTHFOX_FONT_MANIFESTÚSTEALTHFOX_WEBRTC_PUBLIC_IPÚ1ÚSTEALTHFOX_WEBRTC_DISABLE_IPV6)
ÚdictÚosÚenvironÚ_tz_envÚ
setdefaultr   rA   Úpopr   r.   )
r"   r?   rC   rD   rE   rF   ÚenvÚ	webrtc_ipr*   r   s
             r   Úbuild_launch_envrY   r   sö   € õ` ˜XÐ-�rŒzˆz°8Ñ
<Ô
<€CØð &Ý˜HÑ%Ô%ˆˆD‰	ØÐ ð 	�ŠÐ1µ3°}Ñ3EÔ3EÑFÔFÐFØ—’Ð5Ñ6Ô6ÐH¸.€IØð 4Ø-6ˆÐ)Ñ*ð 14ˆÐ,Ñ-Ø‡G‚G�K˜ÑÔÐØÐ" ×)Ò)Ñ+Ô+ð ð ‰ˆˆ1Øˆ9Ø�GŠG�A�tÑÔÐÐàˆC�‰FˆFØ€Jr   c                  ó   — e Zd ZdZdS )ÚFontManifestMismatchzDThe manifest this package declares describes faces the engine lacks.N)r   Ú
__module__Ú__qualname__Ú__doc__© r   r   r[   r[   ¼   s   € € € € € ØNÐNÐNÐNr   r[   ÚmanifestÚbinaryú'Optional[List[str]]'c                óÊ  ‡— t          |¦  «        j        dz  }g }|                      ¦   «         D ]b}|                     d¦  «        rK|                     d¦  «        }t          |¦  «        dk    r#|d         r|                     |d         ¦  «         Œc|sg S |                     ¦   «         sdS d„ |                     ¦   «         D ¦   «         Št          ˆfd„|D ¦   «         ¦  «        S )a©  Face files `manifest` names that the engine lacks, or None if unchecked.

    THREE outcomes, not two, and collapsing the last two is the trap:
      * `[]`   - checked, every face is present. Safe.
      * `[...]`- checked, these are missing. The caller should refuse.
      * `None` - could NOT check: the engine has no `fonts/` directory beside
                 it, so it is not a bundle-only layout this can inspect.

    `None` is not a pass and must not be treated as `[]`. It is returned rather
    than an empty list for the same reason `download.py` prints "an absent
    check is not a passed check" when an asset carries no omni.ja digest. What
    the caller may reasonably do with it is proceed anyway: an engine with no
    bundled fonts of its own cannot be made worse by a manifest it will read
    against files it does not have either way, and refusing would break every
    layout that is not ours - including every test fixture, which is how this
    distinction got written.

    This is the check that makes handing our manifest to the engine safe, and
    it deliberately is NOT "the two manifests are equal". They are supposed to
    diverge: the whole point of carrying one here is that this package can move
    ahead of a published engine. What must hold is narrower and is the thing
    that fails silently - every `f|` record gives vertical metrics for a named
    FILE, and metrics for a file the engine does not have are not an error, they
    are text laid out a few pixels wrong on a page nobody is measuring.

    Empty result means safe. The caller decides what a non-empty one costs: at
    launch it is worth refusing over, because the alternative is shipping a
    wrong layout to every session, whereas a browser that will not start is at
    least loud.

    An engine with no fonts directory at all returns every file as missing
    rather than an empty list - an absent check must never read like a passed
    one.
    Úfontszf|ú|r   Nc                ó   — h | ]	}|j         ’Œ
S r_   )r   )r)   Úps     r   ú	<setcomp>z'verify_font_manifest.<locals>.<setcomp>ï   s   € Ð0Ð0Ð0�qˆAŒFÐ0Ð0Ð0r   c                ó   •— h | ]}|‰v¯|’Œ	S r_   r_   )r)   ÚfÚhaves     €r   rh   z'verify_font_manifest.<locals>.<setcomp>ð   s   ø€ Ð:Ð:Ð:˜¨A°T¨M¨M�1¨M¨M¨Mr   )
r   ÚparentÚ
splitlinesÚ
startswithÚsplitÚlenÚappendÚis_dirÚiterdirÚsorted)r`   ra   Ú	fonts_dirÚ
referencedÚlineÚpartsrk   s         @r   Úverify_font_manifestry   À   sô   ø€ õH �V‘”Ô# gÑ-€IØ€JØ×#Ò#Ñ%Ô%ð ,ð ,ˆØ�?Š?˜4Ñ Ô ð 	,Ø—J’J˜s‘O”OˆEÝ�5‰zŒz˜AŠ~ˆ~ %¨¤(ˆ~Ø×!Ò! %¨¤(Ñ+Ô+Ð+øØð Øˆ	Ø×ÒÑÔð ØˆtØ0Ð0˜I×-Ò-Ñ/Ô/Ð0Ñ0Ô0€DÝÐ:Ð:Ð:Ð:˜jÐ:Ñ:Ô:Ñ;Ô;Ð;r   ú'Path | None'c                óT  — | sdS |                       d¦  «        }t          j        |¦  «                             ¦   «         dd…         }t	          ¦   «         dz  }|                     dd¬¦  «         |d|› d�z  }|                     ¦   «         r*|                     ¦   «         j        t          |¦  «        k    rqt          d	d
g¦  «        |vs
J d¦   «         ‚|                     dt          j        ¦   «         › d�¦  «        }|                     |¦  «         t          j        ||¦  «         |S )aó  Materialise `manifest` somewhere the engine can read it, once per content.

    The Playwright path does not own the profile directory - Playwright creates
    it - so the file cannot simply live beside the profile. Addressing it by the
    sha256 of its own bytes gives a stable path per manifest: launching a
    thousand sessions with the same core writes one file, and a session with a
    pinned manifest gets its own without disturbing the others. Nothing has to
    be cleaned up on teardown, which is the failure mode a per-session temp dir
    would introduce.

    Returns None for an empty manifest: the engine reads that as "use my own
    copy", and writing an empty file would say the same thing while looking
    like a configured one.
    NÚasciié   rd   Tr%   zbundle-fonts-z.listé   é
   ú il manifest ha terminazioni CRLFú.z.tmp)r1   ÚhashlibÚsha256Ú	hexdigestÚ
cache_rootr-   Úis_fileÚstatÚst_sizerp   ÚbytesÚwith_suffixrR   Úgetpidr/   Úreplace)r`   ÚrawÚdigestr2   r3   Útmps         r   Úcached_font_manifest_pathr�   ó   s  € ð ð ØˆtØ
�/Š/˜'Ñ
"Ô
"€CÝŒ^˜CÑ Ô ×*Ò*Ñ,Ô,¨S¨b¨SÔ1€FÝ‰Œ�wÑ€AØ‡G‚G�D 4€GÑ(Ô(Ð(Ø
Ð+˜fÐ+Ð+Ð+Ñ
+€CØ�;Š;‰=Œ=ð ˜CŸHšH™JœJÔ.µ#°c±(´(Ò:Ð:Ý�b˜"�X‰Œ cÐ)Ð)Ð)Ð+MÑ)Ô)Ð)ð �oŠoÐ3¥"¤)¡+¤+Ð3Ð3Ð3Ñ4Ô4ˆØ�Š˜ÑÔÐÝ
Œ
�3˜ÑÔÐØ€Jr   c                óò   — |sdS t          | ¦  «        }|                     dd¬¦  «         |dz  }|                     d¦  «        }t          ddg¦  «        |vs
J d¦   «         ‚|                     |¦  «         |S )	aœ  Write `manifest` beside the profile and return its path, or None.

    Returns None for an empty manifest rather than writing an empty file: the
    engine treats an unreadable or empty source as "use my own copy", and an
    empty file would be an indistinguishable way of saying the same thing while
    looking like a configured one.

    Written as BYTES with an explicit ascii encode. `write_text` on Windows
    translates every newline to CRLF, and this project has already shipped a
    30k-star repository a commit that was 48 additions and 48 deletions for a
    one-line change because of exactly that. The engine's parser splits on a
    bare newline.
    NTr%   zbundle-fonts.listr|   r~   r   r€   )r   r-   r1   r‰   r/   )r    r`   r2   r3   r�   s        r   Úwrite_font_manifestr’     s�   € ð ð ØˆtÝˆ[ÑÔ€AØ‡G‚G�D 4€GÑ(Ô(Ð(Ø
Ð!Ñ
!€CØ
�/Š/˜'Ñ
"Ô
"€Cõ �"�b�‰?Œ? #Ð%Ð%Ð%Ð'IÑ%Ô%Ð%Ø‡O‚O�CÑÔÐØ€Jr   )r…   )ÚSessionTokenc                  ó^   — e Zd ZU dZded<   ded<   ded<   ded	<    ee¬
¦  «        Zded<   dS )Ú
LaunchPlanz@Everything needed to spawn the patched Firefox for one identity.r   ra   r   r    z	List[str]Úargvr=   rW   )Údefault_factoryr“   Úsession_tokenN)r   r\   r]   r^   Ú__annotations__r   r“   r˜   r_   r   r   r•   r•   8  sb   € € € € € € àJÐJØ€K€K�KØÐÐÑØ€O€O�OØÐÐÑð #( %¸Ð"EÑ"EÔ"E€MÐEÐEÐEÑEÐEÐEr   r•   ÚautoÚ )	Úproxyr?   ÚlocaleÚpinÚ
binary_verÚbinary_pathÚ
extra_argsÚurlÚshow_cursorÚseedr   rœ   r�   rž   úOptional[Dict[str, Any]]rŸ   r    r¡   úOptional[List[str]]r¢   r£   úOptional[bool]c       
        ó2  — ddl m} ddlm} ddlm} ddlm} |rL|                     d¦  «        pd 	                    ¦   «          
                    ¦   «         dvrt          d	¦  «        ‚|rt          |¦  «        }n$t          |r ||¦  «        n	 |¦   «         ¦  «        } ||||¦  «        } || |¬
¦  «        } |||j        |j        |d|
¬¦  «        j        }t          |¦  «        }t!          ||¦  «         t#          ||j        pd|                     ¦   «         ¬¦  «        }|ddt          |¦  «        g}|t'          |pg ¦  «        z  }|	r|                     |	¦  «         t+          j        ¦   «         }t/          ||||                     |¦  «        |¬¦  «        S )aÙ  The single direct-launch entry point (no Playwright, no Qt).

    Give it a ``seed`` and a ``profile_dir`` (plus optionally a concrete
    ``proxy`` dict, ``timezone``/``locale`` - both default to ``"auto"`` and are
    resolved from the proxy egress - a ``pin`` and a ``binary_ver``); it resolves
    geo, generates the fingerprint profile, translates it to prefs, writes
    ``user.js`` into ``profile_dir``, and returns the binary path + argv + env.
    Any direct-launch consumer uses this so the session-setup logic lives in
    one place (mirrors the wrapper's __aenter__) - the profile manager did,
    until its 2026-08-18 deletion.
    ``proxy`` must already be a concrete endpoint dict (SOCKS/HTTP), not an intent.
    r   )Úensure_binary)Úgenerate_profile)Úprepare_session_geo)Úcompose_session_prefsÚserverr›   )r›   z	direct://aB  build_launch_plan() cannot take a proxy: it starts the binary directly, so there is no protocol connection to send the engine's proxy command on, and a browser launched without the proxy it was given announces one country and connects from another. Drive the proxy through invisible_playwright, which holds the connection.)r¤   rž   T)r�   r?   rœ   Úsurvive_hard_killr£   N)r?   rC   z
-no-remotez-profile)ra   r    r–   rW   r˜   )Údownloadr©   Ú_fpforgerª   Ú_geor«   r"   r¬   rA   ÚstripÚlowerÚ
ValueErrorr   r�   r?   r   r5   rY   Úsrflx_to_declareÚlistrq   r“   Úmintr•   Ústamp)r¤   r    rœ   r?   r�   rž   rŸ   r    r¡   r¢   r£   r©   rª   r«   r¬   ra   ÚgeoÚfpr"   ÚpdirrW   r–   Útokens                          r   Úbuild_launch_planr½   G  s  € ðh (Ð'Ð'Ð'Ð'Ð'Ø*Ð*Ð*Ð*Ð*Ð*Ø)Ð)Ð)Ð)Ð)Ð)Ø,Ð,Ð,Ð,Ð,Ð,ð ð O�%—)’)˜HÑ%Ô%Ð+¨×2Ò2Ñ4Ô4×:Ò:Ñ<Ô<ÐDUÐUÐUÝðNñOô Oð 	Oð ð SÝ�[Ñ!Ô!ˆˆå°*ÐQ�]�] :Ñ.Ô.Ð.À-À-Á/Ä/ÑRÔRˆð
 Ð
˜h¨¨vÑ
6Ô
6€CØ	Ð	˜t¨Ð	-Ñ	-Ô	-€Bð "Ð!Ø
�3”:¨¬Ø tØðñ ô ô ð	 
õ
 �ÑÔ€DÝ�$˜ÑÔÐÝ
˜5¨3¬<Ð+?¸4Ø*-×*>Ò*>Ñ*@Ô*@ðBñ Bô B€Cà�L *­c°$©i¬iÐ8€DØ�D�Ð!˜rÑ"Ô"Ñ"€DØ
ð Ø�Š�CÑÔÐõ ÔÑÔ€EÝ˜V°¸DØŸ+š+ cÑ*Ô*¸%ðAñ Aô Að Ar   )r   r   r   r   r   r   )r    r!   r"   r#   r   r   )r?   r   r   r   )r"   r#   r?   rG   rC   rG   rD   rH   rE   rI   rF   rJ   r   r=   )r`   r   ra   r!   r   rb   )r`   r   r   rz   )r    r!   r`   r   r   rz   )r¤   r   r    r!   rœ   rI   r?   r   r�   r   rž   r¥   rŸ   rG   r    rG   r¡   r¦   r¢   r   r£   r§   r   r•   )$r^   Ú
__future__r   r   r‚   rR   Údataclassesr   r   Úpathlibr   Útypingr   r   r	   r
   Ú	_headlessr   r   r5   r>   r™   rB   rT   rY   ÚRuntimeErrorr[   ry   r�   r’   r¯   r…   Úprocessr“   r•   r½   r_   r   r   ú<module>rÅ      s•  ðð:ð :ð :ð #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø 	€	€	€	Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø Ð Ð Ð Ð Ð Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,à "Ð "Ð "Ð "Ð "Ð "ðVð Vð Vð Vð8 ð  ð  ð  ðL %.Ø$-Ø$-Ø$-Ø$-Ø$-Ø$-Ø$*Ø$/Ø$+ð%ð %Ð ð ð ð ñ ð	5ð 	5ð 	5ð 	5ð  €ð #ð %)Ø8<Ø)-ð 7;ð+Fð Fð Fð Fð Fð FðTOð Oð Oð Oð O˜<ñ Oô Oð Oð0<ð 0<ð 0<ð 0<ðfð ð ð ð@ð ð ð ð@ !Ð  Ð  Ð  Ð  Ð  à !Ð !Ð !Ð !Ð !Ð !ð ðFð Fð Fð Fð Fñ Fô Fñ „ðFð$ '+ØØØ$(Ø $ð "&Ø&*ð ð #'ðIoAð oAð oAð oAð oAð oAð oAð oAr   