§
    øžyj•[ ã                  óà  — d Z ddlmZ ddlZddlZddl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mZ ddlmZmZmZmZmZmZ  ej        e¦  «        ZdZdZd	Zd
ZdZdZdZdZ dZ!dZ"dZ#dZ$d­d„Z%d®d„Z&d¯d„Z'd°d„Z(dZ) G d „ d!e*¦  «        Z+d±d#„Z,d²d$„Z-d³d&„Z.d'Z/d´d)„Z0h d*£Z1h d+£Z2dµd/„Z3d¶d3„Z4d³d4„Z5d³d5„Z6d³d6„Z7d·d8„Z8d¸d:„Z9d¹d<„Z:d¹d=„Z; G d>„ d?¦  «        Z<dºdA„Z=d»dF„Z>ddGœd¼dN„Z?d½dO„Z@d½dP„ZAd¾dR„ZB G dS„ dTe*¦  «        ZC G dU„ dVe*¦  «        ZD G dW„ dX¦  «        ZEd·dY„ZFd·dZ„ZGd²d[„ZHd¿d]„ZId^d_œdÀdc„ZJdÁde„ZKedfœdÂdi„ZLddjœdÃdm„ZMdÄdn„ZNdÅdp„ZOd^d_œdÆds„ZPd^d_œdÇdv„ZQdÈdw„ZRdÉdy„ZS	 dÊdddzd{œdËd€„ZTdÌd†„ZUdÍdŠ„ZVdÎdŒ„ZWdÏd�„ZX	 dÊddŽœdÐd�„ZYd¹d�„ZZdzd‘œdÑd’„Z[dÒd“„Z\d²d”„Z]d¹d•„Z^d–Z_d²d—„Z`d³d˜„Zad™ZbdÓd›„ZcdÔdœ„Zdd·d�„Ze	 dÊddŽœdÐdž„ZfdºdŸ„ZgdÕd „ZhdÖd¡„Zid×d£„ZjdØd¥„ZkdÊdÙd¦„ZldÚd§„ZmdÛd¨„Zn	 dÊddd©œdÜdª„ZodÒd«„ZpdÝd¬„ZqdS )Þu†  
Skill Sync client -- the low-level sync layer.

This is the LOW-LEVEL sync layer. It builds content-addressed objects
(blob/tree/commit) from local skills, talks the sync wire contract to a sync
plane (push objects + CAS a ref, pull the owner's HEAD, three-way merge on a
409), and is driven by:

  * a debounced push hook in ``skill_manage`` (after the write-gate passes),
  * a periodic pull hook (``maybe_pull_skills``) at the curator tick sites,
  * the ``hermes sync status|pull|push|now`` CLI.

It lives beside ``tools/skills_sync.py`` (NOT under ``hermes_cli/``) so the
low-level sync layer never imports the CLI -- same rule the bundled-skills
sync module documents at ``skills_sync.py:43-50``.

Contract: the Skill Sync wire contract (version 1, frozen
for Milestone 1). Endpoint shapes, object model, canonicalization, and status
codes below all trace to that document.

--- ACCESS GATE (pre-launch) ---------------------------------------------
Client sync is INERT (no push, no pull, no-op) unless the signed-in user is a
**Nous admin**. We read that off the access token, which rides on the same
bearer ``resolve_nous_runtime_credentials()`` returns; we decode the JWT
payload (no signature verification -- the server re-verifies) and check the
claim before doing any sync work.

NAMING: the claim on the wire is ``tool_gateway_admin``, which is misleading
-- it is NOT a tool-gateway-specific right. NAS populates it from
``Permissions.ADMIN_ACCESS`` (access-token-issuer.ts), the same global portal
admin permission that guards ``/admin/*``; the claim is simply named for its
first consumer. We keep the wire name (other services read it) but call it
what it means everywhere on this side.

This gate is pre-launch containment, not the shipping entitlement. Admin
status conflates "may administer Nous" with "has Skill Sync enabled", and has
no middle setting for a beta cohort -- opening it up would mean handing out
portal admin. Replace it with a real entitlement (a ``sync:*`` scope, a tier
check, or a per-cohort feature flag) before shipping to users.

--- OPT-IN DEFAULT (M1-D, provisional) -----------------------------------
Nothing syncs unless the user marks a skill for sync. The user's local intent
is toggled via ``hermes sync enable/disable`` (a ``sync`` flag on the skill's
``.usage.json`` sidecar, alongside ``pinned``/``created_by``), but the DURABLE,
CROSS-DEVICE opt-in state is a committed ``sync-manifest`` object in the sync
plane (design.md Â§2.8): a root-level blob in the tree at
``refs/user/<owner>/HEAD`` recording per-skill ``{name, enabled}``. Push writes
the manifest from local intent; pull reconciles local intent FROM it, so a skill
opted in on one device becomes opted in on the others. The plane manifest is
authoritative; the local flag is just the editable intent. Only agent-created +
user-authored skills under ``~/.hermes/skills/`` are eligible; bundled and
hub-installed skills are excluded.
é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚPathÚPurePosixPath)ÚAnyÚCallableÚDictÚListÚOptionalÚTupleÚ1i  �ÚblobÚtreeÚcommitÚfileÚexecÚdirÚskillzsync-manifesté   ÚskillsúDict[str, bool]ÚreturnÚbytesc                ó’   — t           t          d„ t          |                      ¦   «         ¦  «        D ¦   «         dœ}t	          |¦  «        S )a5  Serialize the per-skill opt-in map into canonical ``sync-manifest`` bytes.

    ``skills`` maps skill name -> enabled. Emits the shape gateway-gateway's
    ``parseSyncManifest`` validates: ``{type, version:1, skills:[{name,enabled}]}``.
    Skill entries are sorted by name for a stable content address.
    c                ó8   — g | ]\  }}|t          |¦  «        d œ‘ŒS ))ÚnameÚenabled)Úbool)Ú.0r   r   s      ú>/home/ragecks/.hermes/hermes-agent/tools/skills_sync_client.pyú
<listcomp>z-build_sync_manifest_bytes.<locals>.<listcomp>€   s:   € ð 
ð 
ð 
á��gð ¥d¨7¡m¤mÐ4Ð4ð
ð 
ð 
ó    )ÚtypeÚversionr   )ÚSYNC_MANIFEST_TYPEÚSYNC_MANIFEST_VERSIONÚsortedÚitemsÚcanonical_json_bytes)r   Úmanifests     r!   Úbuild_sync_manifest_bytesr,   v   sR   € õ #Ý(ð
ð 
å!'¨¯ª©¬Ñ!7Ô!7ð
ñ 
ô 
ðð €Hõ   Ñ)Ô)Ð)r#   ÚdataúOptional[Dict[str, bool]]c                ó€  — 	 t          j        |                      d¦  «        ¦  «        }n# t          $ r Y dS w xY wt	          |t
          ¦  «        sdS |                     d¦  «        t          k    rdS |                     d¦  «        t          k    rdS |                     d¦  «        }t	          |t          ¦  «        sdS i }|D ]{}t	          |t
          ¦  «        s dS |                     d¦  «        }|                     d¦  «        }t	          |t          ¦  «        r|s dS t	          |t          ¦  «        s dS |||<   Œ||S )u’  Parse ``sync-manifest`` bytes into ``{name: enabled}``, or ``None`` if the
    bytes are not a well-formed manifest.

    Strict (mirrors gateway-gateway ``parseSyncManifest``): an unknown ``type``,
    a missing/!=1 ``version``, a non-array ``skills``, or a malformed skill entry
    all reject rather than being coerced â€” a malformed manifest must not be
    mistaken for "no skills opted in."
    úutf-8Nr$   r%   r   r   r   )ÚjsonÚloadsÚdecodeÚ	ExceptionÚ
isinstanceÚdictÚgetr&   r'   ÚlistÚstrr   )r-   ÚvalueÚ
raw_skillsÚoutÚrawr   r   s          r!   Úparse_sync_manifestr>   ˆ   sU  € ðÝ”
˜4Ÿ;š; wÑ/Ô/Ñ0Ô0ˆˆøÝð ð ð Øˆtˆtðøøøå�e�TÑ"Ô"ð ØˆtØ‡y‚y�ÑÔÕ.Ò.Ð.ØˆtØ‡y‚y�ÑÔÕ4Ò4Ð4ØˆtØ—’˜8Ñ$Ô$€JÝ�j¥$Ñ'Ô'ð ØˆtØ€CØð 	ð 	ˆÝ˜#�tÑ$Ô$ð 	Ø�4�4Ø�wŠw�v‰ŒˆØ—'’'˜)Ñ$Ô$ˆÝ˜$¥Ñ$Ô$ð 	¨Dð 	Ø�4�4Ý˜'¥4Ñ(Ô(ð 	Ø�4�4ØˆˆD‰	ˆ	Ø€Js   ‚'* ª
8·8r9   c                óT   — dt          j        | ¦  «                             ¦   «         z   S )z;Return ``sha256:<64-hex>`` -- the wire address of ``data``.zsha256:)ÚhashlibÚsha256Ú	hexdigest)r-   s    r!   Úwire_addressrC   µ   s#   € à•w”~ dÑ+Ô+×5Ò5Ñ7Ô7Ñ7Ð7r#   ÚobjúDict[str, Any]c                óX   — t          j        | ddd¬¦  «                             d¦  «        S )a¿  Canonical JSON serialization for tree/commit hashing (sync contract).

    UTF-8, keys sorted lexicographically, no insignificant whitespace
    (``separators=(",", ":")``), no trailing newline. Arrays must already be
    in the contract-specified order by the caller (tree entries by ``name``,
    commit ``parents`` in significance order). Both client and server MUST
    produce byte-identical output or a push fails ``422 hash_mismatch``.
    T)Ú,ú:F)Ú	sort_keysÚ
separatorsÚensure_asciir0   )r1   ÚdumpsÚencode)rD   s    r!   r*   r*   º   s6   € õ Œ:ØØØØð	ñ ô ÷
 ‚fˆW�o„oðr#   Útool_gateway_adminc                  ó   — e Zd ZdZdS )ÚSyncInertErrorzŽRaised (and caught by the gate-and-swallow hooks) when sync must no-op:

    not logged in, no bearer, or the caller is not a Nous admin.
    N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© r#   r!   rP   rP   Û   s   € € € € € ðð ð ð r#   rP   Útokenc                ó¬   — 	 ddl }|                     | dddœ¬¦  «        pi S # t          $ r'}t                               d|¦  «         i cY d}~S d}~ww xY w)a9  Decode a JWT payload WITHOUT signature verification.

    Safe here: we never trust these claims for authz -- the server re-verifies
    every call. We only read the dev-gate claim to decide whether to attempt
    sync at all. Mirrors the diagnostic decode in
    plugins/dashboard_auth/nous/__init__.py:463.
    r   NF)Úverify_signatureÚ
verify_exp)Úoptionsz1skills_sync_client: JWT payload decode failed: %s)Újwtr3   r4   ÚloggerÚdebug)rV   r[   Úes      r!   Ú_decode_jwt_payload_unverifiedr_   â   sˆ   € ð	Øˆ
ˆ
ˆ
à�zŠzØØ).¸eÐDÐDð ñ 
ô 
ð ð ð	øõ ð ð ð Ý�ŠÐHÈ!ÑLÔLÐLØˆ	ˆ	ˆ	ˆ	ˆ	ˆ	øøøøðøøøs   ‚" ¢
A¬AÁAÁAc                 óì  — 	 ddl m}   | ¦   «         }n%# t          $ r}t          d|› �¦  «        |‚d}~ww xY w|pi                      d¦  «        }|st          d¦  «        ‚t          |¦  «        }|                     d¦  «        p+|                     d¦  «        p|                     d	¦  «        pd
}|                     t          ¦  «        du }||pi                      d¦  «        t          |¦  «        ||dœS )us  Resolve the Nous bearer + owner + dev-gate flag.

    Returns a dict: ``{api_key, base_url, owner, nous_admin, claims}``.
    Raises :class:`SyncInertError` if not logged in / no bearer.

    ``owner`` is the token-verified subject; the server derives the real owner
    from the bearer regardless (contract Â§0.4), so this is advisory for local
    ref naming only.
    r   )Ú resolve_nous_runtime_credentialszno Nous credentials: NÚapi_keyzno bearer token availableÚsubÚ	privy_didÚtidÚunknownTÚbase_url)rb   rg   ÚownerÚ
nous_adminÚclaims)Úhermes_cli.authra   r4   rP   r7   r_   ÚNOUS_ADMIN_CLAIMr9   )ra   Úcredsr^   rb   rj   rh   ri   s          r!   Úresolve_identityrn   ö   s8  € ðAØDÐDÐDÐDÐDÐDà0Ð0Ñ2Ô2ˆˆøÝð Að Að AÝÐ8°QÐ8Ð8Ñ9Ô9¸qÐ@øøøøðAøøøð ˆ{˜×Ò 	Ñ*Ô*€GØð :ÝÐ8Ñ9Ô9Ð9å+¨GÑ4Ô4€Fà�
Š
�5ÑÔð 	Ø�:Š:�kÑ"Ô"ð	à�:Š:�eÑÔð	ð ð	 
ð —’Õ,Ñ-Ô-°Ð5€JàØ�[˜b×%Ò% jÑ1Ô1Ý�U‘”Ø Øðð ð s   ‚ “
5�0°5r   c                 óÞ   — 	 t          t          ¦   «                              d¦  «        ¦  «        S # t          $ r Y dS t          $ r&} t
                               d| ¦  «         Y d} ~ dS d} ~ ww xY w)z3Whether the access gate permits sync. Never raises.ri   Fz2skills_sync_client: dev_gate_open check failed: %sN)r   rn   r7   rP   r4   r\   r]   ©r^   s    r!   Údev_gate_openrq     s„   € ðÝÕ$Ñ&Ô&×*Ò*¨<Ñ8Ô8Ñ9Ô9Ð9øÝð ð ð ØˆuˆuÝð ð ð Ý�ŠÐIÈ1ÑMÔMÐMØˆuˆuˆuˆuˆuøøøøðøøøs   ‚-0 °
A,½	A,ÁA'Á'A,z(https://gateway-gateway.nousresearch.comúOptional[str]c                 ó:  — t          j        d¦  «        } | r;|                      ¦   «         r'|                      ¦   «                              d¦  «        S 	 ddlm}  |¦   «         pi }|                     d¦  «        pi }|                     d¦  «        }t          |t          ¦  «        r;|                     ¦   «         r'|                     ¦   «                              d¦  «        S n2# t          $ r%}t                               d|¦  «         Y d}~nd}~ww xY wt          pdS )	uä  Resolve the sync-plane base URL.

    Order: HERMES_SYNC_BASE_URL env bridge -> config.yaml ``sync.base_url`` ->
    the production plane. Returns a base without a trailing slash (e.g.
    ``https://host``); the ``/v1/sync/`` prefix is appended by the client.

    The production default means a normal user never configures a URL â€” the
    env var and config key exist to point a dev/staging build at another
    plane. Returns None only if the default is somehow blanked out.
    ÚHERMES_SYNC_BASE_URLú/r   ©Úload_configÚsyncrg   z8skills_sync_client: config sync.base_url read failed: %sN)ÚosÚgetenvÚstripÚrstripÚhermes_cli.configrw   r7   r5   r9   r4   r\   r]   ÚDEFAULT_SYNC_BASE_URL)Úenvrw   ÚcfgÚsync_cfgÚbaser^   s         r!   Úresolve_sync_base_urlrƒ   3  s'  € õ Œ)Ð*Ñ
+Ô
+€CØ
ð 'ˆs�yŠy‰{Œ{ð 'Ø�yŠy‰{Œ{×!Ò! #Ñ&Ô&Ð&ðTð 	2Ð1Ð1Ð1Ð1Ð1àˆk‰mŒmÐ!˜rˆØ—7’7˜6‘?”?Ð( bˆØ�|Š|˜JÑ'Ô'ˆÝ�d�CÑ Ô ð 	, T§Z¢Z¡\¤\ð 	,Ø—:’:‘<”<×&Ò& sÑ+Ô+Ð+øøÝð Tð Tð TÝ�ŠÐOÐQRÑSÔSÐSÐSÐSÐSÐSÐSøøøøðTøøøå Ð( DÐ(s   ÁBC" Ã"
DÃ,DÄD>   r   ÚonÚyesÚtrue>   Ú Ú0ÚnoÚoffÚfalser:   r   úOptional[bool]c                óÎ   — t          | t          ¦  «        r| S | €dS t          | ¦  «                             ¦   «                              ¦   «         }|t
          v rdS |t          v rdS dS )z“Parse a config/env bool. Returns None if unrecognized (so callers can
    fall through to the next precedence layer). Accepts real bools + strings.NTF)r5   r   r9   r{   ÚlowerÚ_TRUEÚ_FALSE)r:   Úss     r!   Ú_parse_boolr’   f  si   € õ �%�ÑÔð ØˆØ€}ØˆtÝˆE‰
Œ
×ÒÑÔ× Ò Ñ"Ô"€AØ�E€z€zØˆtØ�F€{€{ØˆuØˆ4r#   Úenv_varÚ
config_keyÚdefaultc               óX  — t          t          j        | ¦  «        ¦  «        }|�|S 	 ddlm}  |¦   «         pi }|                     d¦  «        pi }t          |                     |¦  «        ¦  «        }|�|S n3# t          $ r&}t                               d||¦  «         Y d}~nd}~ww xY w|S )zMResolve a boolean sync knob: ``env_var`` -> ``sync.<config_key>`` -> default.Nr   rv   rx   z2skills_sync_client: config sync.%s read failed: %s)	r’   ry   rz   r}   rw   r7   r4   r\   r]   )	r“   r”   r•   Úenv_valrw   r€   r�   Úcfg_valr^   s	            r!   Ú_sync_config_boolr™   u  sØ   € å�"œ) GÑ,Ô,Ñ-Ô-€GØÐØˆð	ZØ1Ð1Ð1Ð1Ð1Ð1àˆk‰mŒmÐ!˜rˆØ—7’7˜6‘?”?Ð( bˆÝ˜hŸlšl¨:Ñ6Ô6Ñ7Ô7ˆØÐØˆNð øåð Zð Zð ZÝ�ŠÐIÈ:ÐWXÑYÔYÐYÐYÐYÐYÐYÐYøøøøðZøøøà€Ns   §AA7 Á7
B'ÂB"Â"B'c                 ó&   — t          ddd¬¦  «        S )u¡  Whether the sync feature is turned on for this instance (env-first).

    ``HERMES_SYNC_ENABLED`` -> ``sync.enabled`` -> False. This is the master
    switch a Hermes Cloud deployment sets to opt its instances into sync by
    default. It is checked by the gate-and-swallow entrypoints IN ADDITION to
    the Nous-admin token gate and a configured base URL â€” all three must hold for
    background sync to run.
    ÚHERMES_SYNC_ENABLEDr   F©r•   ©r™   rU   r#   r!   Úsync_feature_enabledrž   ‡  s   € õ Ð2°IÀuÐMÑMÔMÐMr#   c                 ó&   — t          ddd¬¦  «        S )a~  Whether an agent/user edit to an org skill is proposed automatically.

    ``HERMES_SYNC_ORG_AUTO_PROPOSE`` -> ``sync.org_auto_propose`` -> False.

    False (default): edits to an org-shared skill stay LOCAL until the user
    runs ``hermes sync propose <skill>``. The skill keeps working with the
    edit applied; the organisation just doesn't see it yet.

    True: every local edit to an org skill is submitted to the org as a
    proposal right away (an admin still approves it, unless the editor is an
    admin). Suits a small, high-trust team that wants improvements to flow
    back without anyone remembering to push them.
    ÚHERMES_SYNC_ORG_AUTO_PROPOSEÚorg_auto_proposeFrœ   r�   rU   r#   r!   Úsync_org_auto_proposer¢   “  s#   € õ Ø&Ð(:ÀEðñ ô ð r#   c                 ó&   — t          ddd¬¦  «        S )u€  The personal sync default opt-in policy (env-first).

    ``HERMES_SYNC_DEFAULT_OPT_IN`` -> ``sync.default_opt_in`` -> False.

    False (default): opt-IN â€” a skill syncs only after an explicit
    ``hermes sync enable`` (or a plane manifest that opted it in). True: opt-OUT
    â€” every sync-eligible skill is treated as opted in unless explicitly
    disabled, which is the "your skills follow you with no setup" default a
    Hermes Cloud deployment wants. Per the design notes, this default is
    provisional and expected to flip; exposing it as env config lets the
    operator choose per deployment without a protocol change.
    ÚHERMES_SYNC_DEFAULT_OPT_INÚdefault_opt_inFrœ   r�   rU   r#   r!   Úsync_default_opt_inr¦   ¦  s   € õ Ð9Ð;KÐUZÐ[Ñ[Ô[Ð[r#   r   c                 ó(   — ddl m}   | ¦   «         dz  S )Nr   ©Úget_hermes_homer   )Úhermes_constantsr©   r¨   s    r!   Ú_skills_dirr«   ¾  s'   € Ø0Ð0Ð0Ð0Ð0Ð0àˆ?ÑÔ˜xÑ'Ð'r#   Ú
skill_namec                ó®  — 	 ddl m}m}m} ddlm} n# t          $ r Y dS w xY w || ¦  «        s || ¦  «        rdS  || ¦  «        }|€dS  ||¦  «        rdS 	 |                     ¦   «                              t          ¦   «                              ¦   «         ¦  «        }|j
        r|j
        d         t          k    rdS n# t          t          f$ r Y nw xY wdS )uÃ  Whether *skill_name* is a candidate for sync (before the opt-in check).

    Eligible = present locally under ~/.hermes/skills/, NOT bundled, NOT
    hub-installed, NOT an external-dir skill, and NOT under the org mirror
    (``_org/`` â€” enterprise-managed content pulls from the org HEAD and must
    never ride a personal push; the sync contract / the design notes). Mirrors the
    exclusion logic used by the curator (tools/skill_usage.py).
    r   )Ú
is_bundledÚis_hub_installedÚ_find_skill_dir)Úis_external_skill_pathFNT)Útools.skill_usager®   r¯   r°   Úagent.skill_utilsr±   r4   ÚresolveÚrelative_tor«   ÚpartsÚORG_DIR_NAMEÚOSErrorÚ
ValueError)r¬   r®   r¯   r°   r±   Ú	skill_dirÚrels          r!   Úis_sync_eligibler¼   Ä  s6  € ðØSÐSÐSÐSÐSÐSÐSÐSÐSÐSØ<Ð<Ð<Ð<Ð<Ð<Ð<øÝð ð ð Øˆuˆuðøøøà€z�*ÑÔð Ð!1Ð!1°*Ñ!=Ô!=ð ØˆuØ� 
Ñ+Ô+€IØÐØˆuØÐ˜iÑ(Ô(ð ØˆuðØ×ÒÑ!Ô!×-Ò-­k©m¬m×.CÒ.CÑ.EÔ.EÑFÔFˆØŒ9ð 	˜œ 1œ­Ò5Ð5Ø�5øøÝ•ZÐ ð ð ð Øˆðøøøàˆ4s   ‚ “
! !ÁA"B> Â>CÃCú	List[str]c                 ó¤  — 	 ddl m}  n# t          $ r g cY S w xY w | ¦   «         pi }t          ¦   «         r•g }t	          ¦   «         D ]h}|                     |¦  «        }t          |t          ¦  «        r|                     d¦  «        du rŒDt          |¦  «        r| 	                    |¦  «         Œit          t          |¦  «        ¦  «        S g }|                     ¦   «         D ]U\  }}t          |t          ¦  «        r;|                     d¦  «        du r$t          |¦  «        r| 	                    |¦  «         ŒVt          t          |¦  «        ¦  «        S )u]  Return the names of skills that should sync, honoring the opt-in policy.

    Two policies (``sync_default_opt_in()``, env-first â€” see that function):

    - **opt-in (default):** a skill syncs only when its usage record carries
      ``sync: true`` AND it is eligible. Nothing syncs by default.
    - **opt-out (Hermes Cloud "on by default"):** every *eligible* skill syncs
      UNLESS its usage record explicitly carries ``sync: false``. This is what a
      deployment sets (via ``HERMES_SYNC_DEFAULT_OPT_IN``) so a user's skills
      follow them with no per-skill setup.

    Sorted, deduped.
    r   )Ú
load_usagerx   FT)r²   r¿   r4   r¦   Ú_all_local_skill_namesr7   r5   r6   r¼   Úappendr(   Úsetr)   )r¿   ÚusageÚnamesr   Úrecs        r!   Úlist_synced_skill_namesrÆ   â  se  € ðØ0Ð0Ð0Ð0Ð0Ð0Ð0øÝð ð ð Øˆ	ˆ	ˆ	ðøøøàˆJ‰LŒLÐ˜B€EåÑÔð 	"àˆÝ*Ñ,Ô,ð 	#ð 	#ˆDØ—)’)˜D‘/”/ˆCÝ˜#�tÑ$Ô$ð ¨¯ª°©¬¸EÐ)AÐ)AØÝ Ñ%Ô%ð #Ø—’˜TÑ"Ô"Ð"øÝ•c˜%‘j”jÑ!Ô!Ð!ð €EØ—[’[‘]”]ð ð ‰	ˆˆcÝ�c�4Ñ Ô ð 	 S§W¢W¨V¡_¤_¸Ð%<Ð%<ÕAQÐRVÑAWÔAWÐ%<Ø�LŠL˜ÑÔÐøÝ•#�e‘*”*ÑÔÐs   ‚	 ‰—c                 óî  — g } t          ¦   «         }	 |                     ¦   «         sg S |                     d¦  «        D ]j}|                     ¦   «         rŒd}	 ddlm}  |||j        j        ¦  «        }n# t          $ r |j        j        }Y nw xY w|r|  	                    |¦  «         Œkn2# t          $ r%}t                               d|¦  «         Y d}~nd}~ww xY wt          t          | ¦  «        ¦  «        S )ae  Best-effort enumeration of every locally-present skill name (used by the
    opt-out policy). A skill is any directory under ~/.hermes/skills/ containing
    a ``SKILL.md``; the name is its frontmatter ``name`` (falling back to the
    directory name). Eligibility (bundled/hub/external exclusion) is applied by
    the caller via ``is_sync_eligible``.
    úSKILL.mdNr   )Ú_read_skill_namez6skills_sync_client: local skill enumeration failed: %s)r«   ÚexistsÚrglobÚ
is_symlinkr²   rÉ   Úparentr   r4   rÁ   r¸   r\   r]   r(   rÂ   )rÄ   ÚrootÚskill_mdr   rÉ   r^   s         r!   rÀ   rÀ   	  s:  € ð €EÝ‰=Œ=€DðRØ�{Š{‰}Œ}ð 	ØˆIØŸ
š
 :Ñ.Ô.ð 	#ð 	#ˆHØ×"Ò"Ñ$Ô$ð ØØ"&ˆDð,Ø>Ð>Ð>Ð>Ð>Ð>à'Ð'¨°(´/Ô2FÑGÔG��øÝð ,ð ,ð ,Ø”Ô+���ð,øøøàð #Ø—’˜TÑ"Ô"Ð"øð	#øõ ð Rð Rð RÝ�ŠÐMÈqÑQÔQÐQÐQÐQÐQÐQÐQøøøøðRøøøå•#�e‘*”*ÑÔÐs@   ’B) ¨.B) ÁA4Á3B) Á4BÂ
B) ÂBÂB) Â)
CÂ3CÃCc                  ó*   — e Zd ZdZdd„Zdd	„Zdd„ZdS )Ú	ObjectSetz‡Accumulates objects to push: hash -> (kind, bytes).

    Deduped by content address, so identical blobs across skills upload once.
    r   ÚNonec                ó   — i | _         d S ©N)Úobjects©Úselfs    r!   Ú__init__zObjectSet.__init__4  s   € Ø57ˆŒˆˆr#   Úkindr9   r-   r   c                ó^   — t          |¦  «        }| j                             |||f¦  «         |S rÔ   )rC   rÕ   Ú
setdefault)r×   rÙ   r-   Úaddrs       r!   ÚaddzObjectSet.add7  s0   € Ý˜DÑ!Ô!ˆØŒ×Ò  t¨T lÑ3Ô3Ð3Øˆr#   Úintc                ó*   — t          | j        ¦  «        S rÔ   )ÚlenrÕ   rÖ   s    r!   Ú__len__zObjectSet.__len__<  s   € Ý�4”<Ñ Ô Ð r#   N©r   rÒ   )rÙ   r9   r-   r   r   r9   )r   rÞ   )rQ   rR   rS   rT   rØ   rÝ   rá   rU   r#   r!   rÑ   rÑ   .  sZ   € € € € € ðð ð
8ð 8ð 8ð 8ðð ð ð ð
!ð !ð !ð !ð !ð !r#   rÑ   Úpathc                óÂ   — 	 |                       ¦   «         j        t          j        t          j        z  t          j        z  z  rt          S n# t          $ r Y nw xY wt          S )u‚   Return the tree mode for a regular file: ``exec`` if +x else ``file``
    (contract Â§2.3). No symlinks / other modes are emitted.)	ÚstatÚst_modeÚ_statÚS_IXUSRÚS_IXGRPÚS_IXOTHÚ	MODE_EXECr¸   Ú	MODE_FILE)rã   s    r!   Ú
_file_moderí   @  s`   € ðØ�9Š9‰;Œ;Ô¥%¤-µ%´-Ñ"?Å%Ä-Ñ"OÑPð 	ÝÐð	øåð ð ð ØˆðøøøåÐs   ‚AA
 Á

AÁAÚdir_pathrÕ   Úmax_object_bytesrÞ   c          	     óB  — g }t          |                      ¦   «         d„ ¬¦  «        D �]/}|                     ¦   «         rt                               d|¦  «         Œ3|                     ¦   «         r<t          |||¬¦  «        }|                     |j        t          |t          dœ¦  «         Œƒ|                     ¦   «         r˜|                     ¦   «         }t          |¦  «        |k    r%t          d|› dt          |¦  «        › d|› �¦  «        ‚|                     t           |¦  «        }|                     |j        t           |t#          |¦  «        dœ¦  «         �Œ1|                     d	„ ¬¦  «         t          |d
œ}|                     t          t'          |¦  «        ¦  «        S )uv  Recursively build objects for *dir_path*; return the tree address.

    Regular files become blobs; subdirectories become nested trees. Symlinks,
    sockets, and other special files are skipped (contract Â§2.3 security: no
    symlinks). Blobs over *max_object_bytes* raise :class:`ValueError` so the
    caller can surface / skip the artifact (contract Â§4.3 -> 413).
    c                ó   — | j         S rÔ   )r   )Úps    r!   ú<lambda>zbuild_tree.<locals>.<lambda>T  s   € ¸!¼&€ r#   ©Úkeyz'skills_sync_client: skipping symlink %s©rï   ©r   rÙ   ÚhashÚmodezfile ú is z bytes > max_object_bytes c                ó   — | d         S ©Nr   rU   rp   s    r!   ró   zbuild_tree.<locals>.<lambda>o  ó
   € ˜q œy€ r#   ©r$   Úentries)r(   ÚiterdirrÌ   r\   r]   Úis_dirÚ
build_treerÁ   r   Ú	KIND_TREEÚMODE_DIRÚis_fileÚ
read_bytesrà   r¹   rÝ   Ú	KIND_BLOBrí   Úsortr*   )	rî   rÕ   rï   rÿ   ÚchildÚsub_hashr-   Ú	blob_hashÚtree_objs	            r!   r  r  K  sº  € ð %'€GÝ˜×(Ò(Ñ*Ô*Ð0@Ð0@ÐAÑAÔAð ñ ˆØ×ÒÑÔð 	Ý�LŠLÐBÀEÑJÔJÐJØØ�<Š<‰>Œ>ð 	Ý! %¨ÐCSÐTÑTÔTˆHØ�NŠNØœ­YÀÕRZÐ[Ð[ñô ð ð ð �]Š]‰_Œ_ð 	Ø×#Ò#Ñ%Ô%ˆDÝ�4‰yŒyÐ+Ò+Ð+Ý ð*˜Eð *ð *¥s¨4¡y¤yð *ð *Ø'ð*ð *ñô ð ð  Ÿš¥I¨tÑ4Ô4ˆIØ�NŠNà!œJÝ%Ø%Ý& uÑ-Ô-ð	ð ñô ð ùð ‡L‚LÐ(Ð(€LÑ)Ô)Ð)Ý!¨gÐ6Ð6€HØ�;Š;•yÕ"6°xÑ"@Ô"@ÑAÔAÐAr#   )ÚtsÚ	tree_hashÚparentsrh   ÚdeviceÚmessager  c               óü   — t           | t          |¦  «        ||dœ|p0t          j        t          j        ¦  «                             d¦  «        |t          dœ}|                     t           t          |¦  «        ¦  «        S )a   Build a commit object (sync contract) and return its address.

    ``parents``: 0 for first commit, 1 for a normal edit, 2 for a merge commit
    (order significant: parents[0] = base fast-forwarded from, parents[1] =
    the other head being merged).
    )rh   r  z%Y-%m-%dT%H:%M:%SZ)r$   r   r  Úauthorr  r  Úartifact_type)
ÚKIND_COMMITr8   r   Únowr   ÚutcÚstrftimeÚARTIFACT_TYPE_SKILLrÝ   r*   )r  r  rh   r  r  rÕ   r  Ú
commit_objs           r!   Úbuild_commitr  t  sr   € õ" ØÝ˜‘=”=Ø!¨VÐ4Ð4ØÐM•H”L¥¤Ñ.Ô.×7Ò7Ð8LÑMÔMØÝ,ðð €Jð �;Š;•{Õ$8¸Ñ$DÔ$DÑEÔEÐEr#   c                 óˆ  — ddl } ddl}|                     ¦   «         j        dd…         }	 |                      ¦   «         pd}n# t
          $ r d}Y nw xY w|                     d¦  «        d                              ¦   «         }d                     d„ |D ¦   «         ¦  «        pd}|r|› d|› �n|                     ¦   «         j        S )zÓA human-friendly default device label: the short hostname plus a short
    random suffix for uniqueness (two machines can share a hostname). Falls back
    to a bare uuid if the hostname is unavailable/unusable.r   Né   r‡   ú.c              3  óJ   K  — | ]}|                      ¦   «         s|d v ¯|V — ŒdS )z-_N)Úisalnum)r    Úcs     r!   ú	<genexpr>z(_default_device_label.<locals>.<genexpr>Ÿ  s3   è è € ÐAÐA˜!¨¯	ª	©¬ÐA°q¸D°y°y�A°y°y°y°yÐAÐAr#   Ú-)	ÚsocketÚuuidÚuuid4ÚhexÚgethostnamer¸   Úsplitr{   Újoin)r$  r%  ÚsuffixÚhostÚshorts        r!   Ú_default_device_labelr.  �  sà   € ð €M€M€MØ€K€K€Kà�ZŠZ‰\Œ\Ô˜b˜q˜bÔ!€FðØ×!Ò!Ñ#Ô#Ð) rˆˆøÝð ð ð Øˆˆˆðøøøð �JŠJ�s‰OŒO˜AÔ×$Ò$Ñ&Ô&€Eà�GŠGÐAÐA˜uÐAÑAÔAÑAÔAÐGÀR€EØ"'Ð=ˆeÐÐ�fÐÐÐ¨T¯ZªZ©\¬\Ô-=Ð=s   «A ÁAÁAc                 ó"  — t          ¦   «         dz  } 	 |                      ¦   «         r,|                      d¬¦  «                             ¦   «         }|r|S n# t          $ r Y nw xY wddl}|j                             d¦  «        pd                     ¦   «         }|r|nt          ¦   «         }	 | j	         
                    dd¬	¦  «         |                      |d¬¦  «         n2# t          $ r%}t                               d
|¦  «         Y d}~nd}~ww xY w|S )uD  Return a stable per-device label for commit ``author.device`` (contract
     -- advisory, never an auth input). Persisted under
    ~/.hermes/skills/.sync_device_id.

    New devices are seeded with a HUMAN-FRIENDLY default (short hostname + a
    short random suffix, e.g. ``bens-macbook-a1b2c3``) so the sync console shows
    something recognizable instead of an opaque hash. Existing ``.sync_device_id``
    files are honored verbatim (backward-compatible â€” a machine keeps its id).
    Use ``set_device_name()`` / ``hermes sync device --name`` to set an explicit
    label.ú.sync_device_idr0   ©Úencodingr   NÚHERMES_SYNC_DEVICE_NAMEr‡   T©r  Úexist_okz3skills_sync_client: could not persist device id: %s)r«   rÊ   Ú	read_textr{   r¸   ry   Úenvironr7   r.  rÍ   ÚmkdirÚ
write_textr\   r]   )rã   Úvalry   Úenv_namer^   s        r!   Ústable_device_idr<  £  s@  € õ ‰=Œ=Ð,Ñ,€DðØ�;Š;‰=Œ=ð 	Ø—.’.¨'�.Ñ2Ô2×8Ò8Ñ:Ô:ˆCØð Ø�
øøÝð ð ð Øˆðøøøð €I€I€Ià”
—’Ð8Ñ9Ô9Ð?¸R×FÒFÑHÔH€HØÐ
;ˆ(ˆ(Õ$9Ñ$;Ô$;€CðOØŒ×Ò $°ÐÑ6Ô6Ð6Ø�Š˜ gˆÑ.Ô.Ð.Ð.øÝð Oð Oð OÝ�ŠÐJÈAÑNÔNÐNÐNÐNÐNÐNÐNøøøøðOøøøà€Js)   “?A Á
A!Á A!Â)3C Ã
DÃ'DÄDr   c                óÜ   — | pd                      ¦   «         }|st          d¦  «        ‚t          ¦   «         dz  }|j                             dd¬¦  «         |                     |d¬¦  «         |S )uo  Set the human-friendly device label used for commit ``author.device``.

    Writes the (trimmed) name to ~/.hermes/skills/.sync_device_id, overwriting
    any previous value. The label is advisory metadata only â€” never an auth
    input (contract Â§2.4) â€” so any non-empty string is accepted. Returns the
    stored value. Raises ValueError on an empty name.
    r‡   z&device name must be a non-empty stringr0  Tr4  r0   r1  )r{   r¹   r«   rÍ   r8  r9  )r   Úcleanedrã   s      r!   Úset_device_namer?  É  sv   € ð ˆz�r× Ò Ñ"Ô"€GØð CÝÐAÑBÔBÐBÝ‰=Œ=Ð,Ñ,€DØ„K×Ò˜d¨TÐÑ2Ô2Ð2Ø‡O‚O�G g€OÑ.Ô.Ð.Ø€Nr#   c                  ó*   ‡ — e Zd ZdZddœd	ˆ fd„Zˆ xZS )
Ú	SyncErrorz?A non-recoverable wire error (4xx that the client can't retry).N©Ústatusr  r9   rC  úOptional[int]c               óX   •— t          ¦   «                              |¦  «         || _        d S rÔ   )ÚsuperrØ   rC  )r×   r  rC  Ú	__class__s      €r!   rØ   zSyncError.__init__å  s&   ø€ Ý‰Œ×Ò˜Ñ!Ô!Ð!ØˆŒˆˆr#   )r  r9   rC  rD  ©rQ   rR   rS   rT   rØ   Ú__classcell__©rG  s   @r!   rA  rA  â  sP   ø€ € € € € ØIÐIà@Dð ð ð ð ð ð ð ð ð ð ð ð r#   rA  c                  ó$   ‡ — e Zd ZdZdˆ fd„Zˆ xZS )ÚSyncConflicta[  CAS lost (409). NOT a rejection -- pushed objects are already durable.

    ``actual`` is the current head to merge against, or **None** when the ref
    does not exist server-side (the server reports that as an empty string).
    None means "there is nothing to merge against, retry as a create" -- it
    must never be fetched as an object.
    Úactualrr   c                ó~   •— |pd | _         t          ¦   «                              | j         r
d| j         › �nd¦  «         d S )NzCAS conflict; actual head z(CAS conflict; the ref does not exist yet)rM  rF  rØ   )r×   rM  rG  s     €r!   rØ   zSyncConflict.__init__ó  sS   ø€ ð &, ^¨tˆŒÝ‰Œ×ÒàŒ{ð<Ð6¨¬Ð6Ð6Ð6à;ñ	
ô 	
ð 	
ð 	
ð 	
r#   )rM  rr   rH  rJ  s   @r!   rL  rL  ê  sG   ø€ € € € € ðð ð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
r#   rL  c                  ó~   — e Zd ZdZddœd&d	„Zd'd„Zd(d„Zddœd)d„Zddœd*d„Zddœd+d„Z	ddœd,d„Z
ddœd-d„Zd.d$„Zd%S )/Ú
SyncClientzJSync client bound to a base URL + bearer (routes under
    ``/v1/sync/``).g      >@©Útimeoutrg   r9   rb   rR  Úfloatc               ó´   — |                      d¦  «        | _        || _        || _        dd l}|                     ¦   «         | _        d|› �| j        j        d<   d S )Nru   r   zBearer ÚAuthorization)r|   r‚   rb   rR  ÚrequestsÚSessionÚ_sessionÚheaders)r×   rg   rb   rR  rV  s        r!   rØ   zSyncClient.__init__  s\   € Ø—O’O CÑ(Ô(ˆŒ	ØˆŒØˆŒØˆˆˆà ×(Ò(Ñ*Ô*ˆŒØ1D¸7Ð1DÐ1DˆŒÔ˜oÑ.Ð.Ð.r#   rã   r   c                ó@   — | j         › d|                     d¦  «        › �S )Nz	/v1/sync/ru   )r‚   Úlstrip)r×   rã   s     r!   Ú_urlzSyncClient._url  s$   € Ø”)Ð8Ð8 d§k¢k°#Ñ&6Ô&6Ð8Ð8Ð8r#   rE   c                óä   — | j                              |                      d¦  «        | j        ¬¦  «        }|j        dk    rt          d|j        › �|j        ¬¦  «        ‚|                     ¦   «         S )z<GET /v1/sync/capabilities (sync contract). No auth required.ÚcapabilitiesrQ  éÈ   zcapabilities failed: rB  ©rX  r7   r\  rR  Ústatus_coderA  r1   )r×   Úrs     r!   r^  zSyncClient.capabilities  sd   € àŒM×Ò˜dŸiši¨Ñ7Ô7ÀÄÐÑNÔNˆØŒ=˜CÒÐÝÐC°A´MÐCÐCÈAÌMÐZÑZÔZÐZØ�vŠv‰xŒxˆr#   F©Ú	org_scopeÚprefixrd  r   úList[Dict[str, str]]c               óT  ‡— |rdnd}|rdnd‰i}| j                              |                      |¦  «        || j        ¬¦  «        }|j        dk    rt          d|j        › �|j        ¬¦  «        ‚|                     ¦   «         pi                      dg ¦  «        }|rˆfd	„|D ¦   «         }|S )
a©  GET /v1/sync/refs?prefix=... (or the org route when ``org_scope``).

        Org refs live behind a SEPARATE endpoint, not behind a prefix filter on
        the personal one: the personal route is hard-scoped to the token's own
        owner, so asking it for ``refs/org/<id>/`` silently returns the
        caller's personal refs instead of an error. Callers reading an org ref
        MUST pass ``org_scope=True``.
        zorg/refsÚrefsNre  )ÚparamsrR  r_  zget_refs failed: rB  c                ó€   •— g | ]:}t          |                     d d¦  «        ¦  «                             ‰¦  «        ¯8|‘Œ;S )r   r‡   )r9   r7   Ú
startswith)r    Úr_re  s     €r!   r"   z'SyncClient.get_refs.<locals>.<listcomp>*  sB   ø€ ÐTÐTÐT˜2­¨R¯VªV°F¸BÑ-?Ô-?Ñ)@Ô)@×)KÒ)KÈFÑ)SÔ)SÐT�BÐTÐTÐTr#   r`  )r×   re  rd  rã   ri  rb  rh  s    `     r!   Úget_refszSyncClient.get_refs  sÆ   ø€ ð 'Ð2ˆzˆz¨FˆØ"Ð:��¨°6Ð(:ˆØŒM×Ò˜dŸiši¨™oœo°fÀdÄlÐÑSÔSˆØŒ=˜CÒÐÝÐ?°´Ð?Ð?ÈÌÐVÑVÔVÐVØ—’‘”�˜B×#Ò# F¨BÑ/Ô/ˆØð 	Uð UÐTÐTÐT ÐTÑTÔTˆDØˆr#   Úobj_hashúTuple[str, bytes]c               ó¨  — |rd|› �nd|› �}| j                              |                      |¦  «        | j        ¬¦  «        }|j        dk    rt          d|› d�d¬¦  «        ‚|j        dk    rt          d|› d	�d¬¦  «        ‚|j        d
k    rt          d|j        › �|j        ¬¦  «        ‚|j                             d¦  «        pt          }||j        fS )aÁ  GET /v1/sync/objects/:hash (or the org route when ``org_scope``).

        Kind comes from the object-type response header for tree/commit; a blob
        (application/octet-stream) is returned as ``blob``.

        Org objects are stored under the ``org:<org_id>`` scope key and are NOT
        readable through the personal route (it scopes to the token's owner),
        so walking an org commit requires ``org_scope=True`` on every hop.
        zorg/objects/zobjects/rQ  i”  zobject z
 not foundrB  é“  z not readabler_  zget_object failed: zX-HSP-Object-Type)	rX  r7   r\  rR  ra  rA  rY  r  Úcontent)r×   rn  rd  rã   rb  rÙ   s         r!   Ú
get_objectzSyncClient.get_object-  sõ   € ð -6ÐPÐ(˜hÐ(Ð(Ð(Ð;PÀhÐ;PÐ;PˆØŒM×Ò˜dŸiši¨™oœo°t´|ÐÑDÔDˆØŒ=˜CÒÐÝÐ: hÐ:Ð:Ð:À3ÐGÑGÔGÐGØŒ=˜CÒÐÝÐ= hÐ=Ð=Ð=ÀcÐJÑJÔJÐJØŒ=˜CÒÐÝÐA°!´-ÐAÐAÈ!Ì-ÐXÑXÔXÐXØŒy�}Š}Ð0Ñ1Ô1Ð>µYˆØ�Q”YˆÐr#   Úcommit_hashc               óÄ   — |                       ||¬¦  «        \  }}|t          k    rt          |› d|› d�¦  «        ‚t          j        |                     d¦  «        ¦  «        S )z3Fetch a commit object and parse its canonical JSON.rc  rú   z, expected commitr0   )rs  r  rA  r1   r2   r3   )r×   rt  rd  rÙ   r-   s        r!   Úget_commit_jsonzSyncClient.get_commit_jsonB  sc   € ð —_’_ [¸I�_ÑFÔF‰
ˆˆdØ•;ÒÐÝ˜{ÐGÐG°ÐGÐGÐGÑHÔHÐHÝŒz˜$Ÿ+š+ gÑ.Ô.Ñ/Ô/Ð/r#   r  c               óÄ   — |                       ||¬¦  «        \  }}|t          k    rt          |› d|› d�¦  «        ‚t          j        |                     d¦  «        ¦  «        S )z1Fetch a tree object and parse its canonical JSON.rc  rú   z, expected treer0   )rs  r  rA  r1   r2   r3   )r×   r  rd  rÙ   r-   s        r!   Úget_tree_jsonzSyncClient.get_tree_jsonK  sc   € ð —_’_ Y¸)�_ÑDÔD‰
ˆˆdØ•9ÒÐÝ˜yÐCÐC¨dÐCÐCÐCÑDÔDÐDÝŒz˜$Ÿ+š+ gÑ.Ô.Ñ/Ô/Ð/r#   rÕ   úDict[str, Tuple[str, bytes]]c               ó¾  — d„ |                      ¦   «         D ¦   «         }| j                             |                      d¦  «        ||rddind| j        ¬¦  «        }|j        dk    rt          dd¬	¦  «        ‚|j        d
k    rt          d|j        › �d
¬	¦  «        ‚|j        dvrt          d|j        › �|j        ¬	¦  «        ‚|j        r| 	                    ¦   «         ni S )u¡  POST /v1/sync/objects (sync contract). Batch multi-object upload.

        Contract Â§1 requires raw object bytes on the wire (NOT base64-in-JSON),
        and  specifies "a length-prefixed or multipart stream of
        {hash, type, bytes}". We use multipart/form-data: one part per object,
        the part's field name = the claimed ``sha256:<hex>`` hash, its
        ``filename`` carries the object ``type`` (blob|tree|commit), and the
        part body is the raw object bytes. The server recomputes each hash from
        the received bytes and rejects the whole batch with 422 on mismatch.
        Idempotent: a known hash is a no-op ``already_present``.

        M2 (contract Â§11.5): ``org_scope=True`` adds ``?scope=org`` so the
        objects land in the ORG scope (org-readable; required before an org
        CAS/propose). Gated server-side on the token's org_role claim.

        NOTE (framing choice within contract latitude): Â§4.2 says "length-
        prefixed OR multipart"; this picks multipart/form-data with
        (field=hash, filename=type, body=raw-bytes). The server strand must
        parse the same framing -- flagged for cross-strand alignment.
        c                ó(   — g | ]\  }\  }}|||d ff‘ŒS )zapplication/octet-streamrU   )r    ÚhrÙ   r-   s       r!   r"   z*SyncClient.put_objects.<locals>.<listcomp>q  s=   € ð 
ð 
ð 
á�‘<�D˜$ð ��tÐ7Ð8Ð9ð
ð 
ð 
r#   rÕ   ÚscopeÚorgN)Úfilesri  rR  i�  zobject too large (413)rB  i¦  zhash_mismatch (422): )r_  éÉ   zput_objects failed: )
r)   rX  Úpostr\  rR  ra  rA  Útextrr  r1   )r×   rÕ   rd  r  rb  s        r!   Úput_objectszSyncClient.put_objectsV  s  € ð6
ð 
à#*§=¢=¡?¤?ð
ñ 
ô 
ˆð ŒM×ÒØ�IŠI�iÑ Ô ØØ'0Ð:�G˜UÐ#Ð#°dØ”Lð	 ñ 
ô 
ˆð Œ=˜CÒÐÝÐ4¸SÐAÑAÔAÐAØŒ=˜CÒÐÝÐ<°A´FÐ<Ð<ÀSÐIÑIÔIÐIØŒ= 
Ð*Ð*ÝÐB°1´=ÐBÐBÈ1Ì=ÐYÑYÔYÐYØœ9Ð,ˆq�vŠv‰xŒxˆx¨"Ð,r#   r   Ú	from_hashrr   Úto_hashc                ó  — | j                              |                      d|› �¦  «        ||dœ| j        ¬¦  «        }|j        dk    r#|j        r|                     ¦   «         ni }ddi|¥S |j        dk    r7t          |                     ¦   «         pi                      dd	¦  «        ¦  «        ‚|j        d
k    rt          dd
¬¦  «        ‚|j        dk    rt          d|j        › �|j        ¬¦  «        ‚|j        r|                     ¦   «         ni S )uM  POST /v1/sync/refs/:name -- atomic compare-and-swap (sync contract).

        Raises :class:`SyncConflict` (carrying the actual head) on 409.

        M2 (contract Â§11.5): a non-admin member's CAS on an org HEAD is never
        rejected â€” the server converts it to a proposal and returns
        ``202 {proposal_id, ref}``. Surfaced as
        ``{"proposal_pending": True, ...}`` so callers can tell "merged" (200)
        from "proposed, awaiting review" (202) without exceptions â€” a 202 is a
        SUCCESS-shaped outcome, never to be presented as live (error table Â§5).
        zrefs/)ÚfromÚto)r1   rR  éÊ   Úproposal_pendingTé™  rM  r‡   rq  z#forbidden (403) -- owner/permissionrB  r_  zcas_ref failed: )
rX  r�  r\  rR  ra  rr  r1   rL  r7   rA  )r×   r   r„  r…  rb  Úbodys         r!   Úcas_refzSyncClient.cas_refƒ  s  € ð ŒM×ÒØ�IŠI�n˜d�n�nÑ%Ô%Ø#¨7Ð3Ð3Ø”Lð ñ 
ô 
ˆð
 Œ=˜CÒÐØ œyÐ0�1—6’6‘8”8�8¨bˆDØ&¨Ð5°Ð5Ð5ØŒ=˜CÒÐõ  §¢¡¤ ¨B×3Ò3°H¸bÑAÔAÑBÔBÐBØŒ=˜CÒÐÝÐAÈ#ÐNÑNÔNÐNØŒ=˜CÒÐÝÐ>¨q¬}Ð>Ð>ÀqÄ}ÐUÑUÔUÐUØœ9Ð,ˆq�vŠv‰xŒxˆx¨"Ð,r#   N)rg   r9   rb   r9   rR  rS  )rã   r9   r   r9   ©r   rE   )re  r9   rd  r   r   rf  )rn  r9   rd  r   r   ro  )rt  r9   rd  r   r   rE   )r  r9   rd  r   r   rE   )rÕ   ry  rd  r   r   rE   )r   r9   r„  rr   r…  r9   r   rE   )rQ   rR   rS   rT   rØ   r\  r^  rm  rs  rv  rx  rƒ  r�  rU   r#   r!   rP  rP  ÿ  s6  € € € € € ðð ð IMð Eð Eð Eð Eð Eð Eð9ð 9ð 9ð 9ð
ð ð ð ð :?ð ð ð ð ð ð ð* >Cð ð ð ð ð ð ð, 6;ð0ð 0ð 0ð 0ð 0ð 0ð 49ð0ð 0ð 0ð 0ð 0ð 0ð  ð	+-ð +-ð +-ð +-ð +-ð +-ðZ-ð -ð -ð -ð -ð -r#   rP  c                 ó$   — t          ¦   «         dz  S )Nz.sync_state©r«   rU   r#   r!   Ú_sync_state_pathr‘  ²  s   € Ý‰=Œ=˜=Ñ(Ð(r#   c                 ó$   — t          ¦   «         dz  S )Nz.sync_manifestr�  rU   r#   r!   Ú_legacy_sync_state_pathr“  ¶  s   € Ý‰=Œ=Ð+Ñ+Ð+r#   c                 ó¶  — t          ¦   «         } |                      ¦   «         �st          ¦   «         }|                     ¦   «         rà	 t          j        |                     d¬¦  «        ¦  «        }t          |t          ¦  «        rc|                     dd¦  «         |                     di ¦  «         t          |¦  «         	 | 
                    ¦   «          n# t          $ r Y nw xY w|S n># t          t          j        f$ r%}t                               d|¦  «         Y d}~nd}~ww xY wdi dœS 	 t          j        |                      d¬¦  «        ¦  «        }t          |t          ¦  «        r.|                     dd¦  «         |                     di ¦  «         |S n># t          t          j        f$ r%}t                               d|¦  «         Y d}~nd}~ww xY wdi dœS )	aÄ  Read the local sync state. Returns a default on missing/corrupt.

    Shape: ``{"head": "sha256:...|null", "skills": {name: {tree, commit}}}``.
    ``head`` is the last profile-root HEAD commit we reconciled with.

    Migrates a legacy ``.sync_manifest`` file (pre-rename) transparently: if the
    new ``.sync_state`` is absent but the legacy file exists, it is read and
    rewritten to the new path so an existing device keeps its head record.
    r0   r1  ÚheadNr   z8skills_sync_client: legacy sync state migrate failed: %s)r•  r   z.skills_sync_client: sync state read failed: %s)r‘  rÊ   r“  r1   r2   r6  r5   r6   rÛ   Úwrite_sync_stateÚunlinkr¸   ÚJSONDecodeErrorr\   r]   )rã   Úlegacyr-   r^   s       r!   Úread_sync_staterš  º  s  € õ ÑÔ€DØ�;Š;‰=Œ=ñ ,Ý(Ñ*Ô*ˆØ�=Š=‰?Œ?ð 	\ð\Ý”z &×"2Ò"2¸GÐ"2Ñ"DÔ"DÑEÔE�Ý˜d¥DÑ)Ô)ð  Ø—O’O F¨DÑ1Ô1Ð1Ø—O’O H¨bÑ1Ô1Ð1Ý$ TÑ*Ô*Ð*ðØŸš™œ˜˜øÝ"ð ð ð Ø˜ðøøøà�Kð øõ �TÔ1Ð2ð \ð \ð \Ý—’ÐWÐYZÑ[Ô[Ð[Ð[Ð[Ð[Ð[Ð[øøøøð\øøøà¨Ð+Ð+Ð+ðJÝŒz˜$Ÿ.š.°'˜.Ñ:Ô:Ñ;Ô;ˆÝ�d�DÑ!Ô!ð 	Ø�OŠO˜F DÑ)Ô)Ð)Ø�OŠO˜H bÑ)Ô)Ð)ØˆKð	øõ •TÔ)Ð*ð Jð Jð JÝ�ŠÐEÀqÑIÔIÐIÐIÐIÐIÐIÐIøøøøðJøøøà BÐ'Ð'Ð'sV   ÁA8C( Ã C ÃC( Ã
C"ÃC( Ã!C"Ã"C( Ã(D#Ã>DÄD#Ä,A*F ÆGÆ.GÇGrÒ   c                ó¾  — ddl }t          ¦   «         }	 |j                             dd¬¦  «         |                     t          |j        ¦  «        dd¬¦  «        \  }}	 t          j        |dd	¬
¦  «        5 }t          j	        | |ddd¬¦  «         | 
                    ¦   «          t          j        |                     ¦   «         ¦  «         ddd¦  «         n# 1 swxY w Y   t          j        ||¦  «         dS # t          $ r( 	 t          j        |¦  «         n# t           $ r Y nw xY w‚ w xY w# t"          $ r&}t$                               d|¦  «         Y d}~dS d}~ww xY w)z3Write the local sync state atomically. Best-effort.r   NTr4  z.sync_state_z.tmp)r   re  r+  Úwr0   r1  é   F)ÚindentrI   rK   z/skills_sync_client: sync state write failed: %s)Útempfiler‘  rÍ   r8  Úmkstempr9   ry   Úfdopenr1   ÚdumpÚflushÚfsyncÚfilenoÚreplaceÚBaseExceptionr—  r¸   r4   r\   r]   )r-   rŸ  rã   ÚfdÚtmpÚfr^   s          r!   r–  r–  á  sÆ  € à€O€O€OåÑÔ€DðKØŒ×Ò $°ÐÑ6Ô6Ð6Ø×"Ò"¥s¨4¬;Ñ'7Ô'7ÀÐW]Ð"Ñ^Ô^‰ˆˆCð	Ý”˜2˜s¨WÐ5Ñ5Ô5ð %¸Ý”	˜$ ¨!°tÈ%ÐPÑPÔPÐPØ—’‘	”	�	Ý”˜Ÿš™œÑ$Ô$Ð$ð%ð %ð %ñ %ô %ð %ð %ð %ð %ð %ð %øøøð %ð %ð %ð %õ ŒJ�s˜DÑ!Ô!Ð!Ð!Ð!øÝð 	ð 	ð 	ðÝ”	˜#‘”��øÝð ð ð Ø�ðøøøàð	øøøøõ ð Kð Kð KÝ�ŠÐFÈÑJÔJÐJÐJÐJÐJÐJÐJÐJøøøøðKøøøss   ”A	D, ÁC7 Á5ACÃ	C7 ÃCÃC7 ÃCÃC7 Ã7
D)ÄDÄD)Ä
D$Ä!D)Ä#D$Ä$D)Ä)D, Ä,
EÄ6EÅEFrc  ÚclientÚdestrd  c               ó  — |                      dd¬¦  «         |                      ||¬¦  «        }|                     dg ¦  «        D �]E}|                     dd¦  «        }|rd|v s|dv rt                               d	|¦  «         Œ?||z  }|                     d
¦  «        }|t
          k    rt          | |d         ||¬¦  «         Œ~|t          k    r¼|                      |d         |¬¦  «        \  }	}
| 	                    |
¦  «         |                     d¦  «        t          k    ri	 |                     ¦   «         j        }|                     |t          j        z  t          j        z  t          j        z  ¦  «         �Œ4# t$          $ r Y �ŒAw xY w�ŒGdS )a)  Write the tree at *tree_hash* into *dest* (created if needed).

    Blobs become files (with +x restored for ``exec`` mode), nested trees
    become subdirectories. Does NOT delete files absent from the tree -- the
    caller decides removal semantics. Refuses path traversal via entry names.
    Tr4  rc  rÿ   r   r‡   ru   )r  z..z1skills_sync_client: skipping unsafe tree entry %rrÙ   rø   rù   N)r8  rx  r7   r\   Úwarningr  Úmaterialize_treer  rs  Úwrite_bytesrë   rå   ræ   Úchmodrç   rè   ré   rê   r¸   )r«  r  r¬  rd  r   Úentryr   ÚtargetrÙ   Ú_r-   Ústs               r!   r¯  r¯  ý  s˜  € ð 	‡J‚J�t d€JÑ+Ô+Ð+Ø×Ò 	°YÐÑ?Ô?€DØ—’˜) RÑ(Ô(ð ñ ˆØ�yŠy˜ Ñ$Ô$ˆØð 	�s˜d�{�{ d¨kÐ&9Ð&9Ý�NŠNÐNÐPTÑUÔUÐUØØ˜‘ˆØ�yŠy˜Ñ Ô ˆØ•9ÒÐÝ˜V U¨6¤]°FÀiÐPÑPÔPÐPÐPØ•YÒÐØ×'Ò'¨¨f¬ÀÐ'ÑKÔK‰GˆAˆtØ×Ò˜tÑ$Ô$Ð$Ø�yŠy˜Ñ Ô ¥IÒ-Ð-ðØŸš™œÔ.�BØ—L’L ¥e¤mÑ!3µe´mÑ!CÅeÄmÑ!SÑTÔTÐTÑTøÝð ð ð Ø‘Dðøøøùð!ð s   Ä"AE9Å9
FÆFúOptional[PurePosixPath]c                óR  — 	 ddl m} n# t          $ r Y dS w xY w || ¦  «        }|€dS 	 |                     ¦   «                              t          ¦   «                              ¦   «         ¦  «        }n# t          t          f$ r Y dS w xY wt          | 	                    ¦   «         ¦  «        S )zGReturn the skill's path relative to ~/.hermes/skills/ (posix), or None.r   ©r°   N)
r²   r°   r4   r´   rµ   r«   r¸   r¹   r   Úas_posix)r¬   r°   rº   r»   s       r!   Ú_skill_rel_pathrº  %  sË   € ðØ5Ð5Ð5Ð5Ð5Ð5Ð5øÝð ð ð Øˆtˆtðøøøà� 
Ñ+Ô+€IØÐØˆtðØ×ÒÑ!Ô!×-Ò-­k©m¬m×.CÒ.CÑ.EÔ.EÑFÔFˆˆøÝ•ZÐ ð ð ð Øˆtˆtðøøøå˜Ÿš™œÑ(Ô(Ð(s   ‚	 ‰
–ªAA0 Á0BÂBrö   Úskill_namesú%Tuple[ObjectSet, str, Dict[str, str]]c               óh  — ddl m} t          ¦   «         }i }i }t          t	          | ¦  «        ¦  «        D ]³}t          |¦  «        } ||¦  «        }|�|€Œ!	 t          |||¬¦  «        }	n3# t          $ r&}
t           	                    d||
¦  «         Y d}
~
Œ`d}
~
ww xY w|	||<   t          |j        ¦  «        }|}|dd…         D ]}|                     |i ¦  «        }Œd|	i||d         <   Œ´d„ |D ¦   «         }|                     t          t          |¦  «        ¦  «        }t!          |||¬	¦  «        }|||fS )
u?  Build all objects for *skill_names* + the profile-root tree.

    Returns ``(objects, root_tree_hash, skill_tree_map)`` where
    ``skill_tree_map`` is ``{skill_name: tree_hash}``. Skills whose blobs
    exceed *max_object_bytes* are skipped (surfaced via logger).

    The root tree nests category directories: a skill at ``devops/foo`` yields
    a root entry ``devops`` (tree) containing ``foo`` (tree). Flat skills yield
    a direct root entry.

    The root tree also carries a ``sync-manifest`` BLOB (design.md Â§2.8)
    recording the per-skill opt-in state, so opt-in is durable + cross-device
    rather than a device-local ``.usage.json`` flag. Every skill in
    ``skill_names`` is recorded ``enabled: true`` (they ARE the opted-in set);
    the manifest is the authoritative record the plane + other devices read.
    r   r¸  Nrö   z#skills_sync_client: skipping %s: %séÿÿÿÿÚ__tree__c                ó   — i | ]}|d “ŒS )TrU   )r    r   s     r!   ú
<dictcomp>z$snapshot_profile.<locals>.<dictcomp>e  s   € Ð:Ð:Ð: 4�D˜$Ð:Ð:Ð:r#   ©Úmanifest_hash)r²   r°   rÑ   r(   rÂ   rº  r  r¹   r\   r®  r8   r¶   rÛ   rÝ   r  r,   Ú_build_root_tree)r»  rï   r°   rÕ   Úskill_tree_maprÎ   r   r»   rº   r  r^   r¶   ÚnodeÚpartÚmanifest_maprÃ  Ú	root_hashs                    r!   Úsnapshot_profilerÊ  5  s…  € ð& 2Ð1Ð1Ð1Ð1Ð1å‰kŒk€GØ%'€Nà€Då•s˜;Ñ'Ô'Ñ(Ô(ð 2ð 2ˆÝ˜dÑ#Ô#ˆØ#�O DÑ)Ô)ˆ	Øˆ;˜)Ð+Øð	Ý" 9¨gÐHXÐYÑYÔYˆIˆIøÝð 	ð 	ð 	Ý�NŠNÐ@À$ÈÑJÔJÐJØˆHˆHˆHˆHøøøøð	øøøð  )ˆ�tÑå�S”Y‘”ˆØˆØ˜#˜2˜#”Jð 	-ð 	-ˆDØ—?’? 4¨Ñ,Ô,ˆDˆDØ% yÐ1ˆˆU�2ŒY‰ˆð ;Ð:¨>Ð:Ñ:Ô:€LØ—K’KÝÕ,¨\Ñ:Ô:ñô €Mõ !  w¸mÐLÑLÔL€IØ�I˜~Ð-Ð-s   ÁA*Á*
BÁ4BÂBrÂ  rÆ  rÃ  c               ó8  — g }|                       ¦   «         D ]�\  }}t          |t          ¦  «        rBd|v r>t          |¦  «        dk    r+|                     |t
          |d         t          dœ¦  «         Œ\t          ||¦  «        }|                     |t
          |t          dœ¦  «         Œ‘|�)|                     t          t          |t          dœ¦  «         |                     d„ ¬¦  «         t
          |dœ}|                     t
          t          |¦  «        ¦  «        S )u.  Recursively canonicalize the nested root structure into trees.

    ``manifest_hash`` (only passed at the top level) adds a root-level
    ``sync-manifest`` BLOB entry (design.md Â§2.8) alongside the skill subtrees.
    It cannot collide with a skill dir (skill entries are trees; this is a blob).
    r¿  r   r÷   Nc                ó   — | d         S rü   rU   rp   s    r!   ró   z"_build_root_tree.<locals>.<lambda>‹  rý   r#   rô   rþ   )r)   r5   r6   rà   rÁ   r  r  rÄ  ÚSYNC_MANIFEST_ENTRY_NAMEr  rì   r  rÝ   r*   )rÆ  rÕ   rÃ  rÿ   r   r	  r
  r  s           r!   rÄ  rÄ  n  s1  € ð %'€GØ—z’z‘|”|ð 	ð 	‰ˆˆeÝ�e�TÑ"Ô"ð 	 z°UÐ':Ð':½sÀ5¹z¼zÈQº¸Ø�NŠNØ¥y¸%À
Ô:KÕU]Ð^Ð^ñô ð ð õ (¨¨wÑ7Ô7ˆHØ�NŠNØ¥y¸(ÍHÐUÐUñô ð ð ð Ð Ø�Šå0Ý!Ø%Ý!ð	ð ñ	
ô 	
ð 	
ð ‡L‚LÐ(Ð(€LÑ)Ô)Ð)Ý!¨gÐ6Ð6€HØ�;Š;•yÕ"6°xÑ"@Ô"@ÑAÔAÐAr#   c                ó   — d| › d�S )Nú
refs/user/ú/HEADrU   )rh   s    r!   Úuser_head_refrÑ  ”  s   € Ø$˜Ð$Ð$Ð$Ð$r#   Únc                ó   — d| › d|› �S )NrÏ  ú
/conflict/rU   )rh   rÒ  s     r!   Úuser_conflict_refrÕ  ˜  s   € Ø,˜Ð,Ð,¨Ð,Ð,Ð,r#   ú'SyncClient'rt  c               ó<   — |                       ||¬¦  «        d         S )z,Return the tree hash referenced by a commit.rc  r   )rv  )r«  rt  rd  s      r!   Ú_root_tree_of_commitrØ  œ  s"   € ð ×!Ò! +¸Ð!ÑCÔCÀFÔKÐKr#   Úroot_tree_hashúDict[str, str]c               ó<   ‡ ‡‡‡— i Šdˆˆ ˆˆfd„Š ‰|d¦  «         ‰S )	a  Flatten a profile-root tree into ``{posix_rel_path: skill_tree_hash}``.

    A skill tree is any tree containing a ``SKILL.md`` blob entry. We walk the
    root tree; a subtree with a SKILL.md is treated as a skill leaf keyed by
    its path, so category nesting is preserved.
    r  r9   re  r   rÒ   c                óB  •— ‰                      | ‰	¬¦  «        }|                     dg ¦  «        }t          d„ |D ¦   «         ¦  «        }|r	|r| ‰
|<   d S |D ]I}|                     d¦  «        t          k    r)|r|› d|d         › �n|d         } ‰|d         |¦  «         ŒJd S )Nrc  rÿ   c              3  ó„   K  — | ];}|                      d ¦  «        dk    o|                      d¦  «        t          k    V — Œ<dS )r   rÈ   rÙ   N)r7   r  )r    r^   s     r!   r"  z6_skill_trees_of_root.<locals>._walk.<locals>.<genexpr>±  sV   è è € ð 
ð 
ØKLˆA�EŠE�&‰MŒM˜ZÒ'ÐF¨A¯EªE°&©M¬M½YÒ,Fð
ð 
ð 
ð 
ð 
ð 
r#   rÙ   ru   r   rø   )rx  r7   Úanyr  )r  re  r   rÿ   Úhas_skill_mdr^   Úchild_prefixÚ_walkr«  rd  Úresults          €€€€r!   rá  z#_skill_trees_of_root.<locals>._walk®  sâ   ø€ Ø×#Ò# I¸Ð#ÑCÔCˆØ—(’(˜9 bÑ)Ô)ˆÝð 
ð 
ØPWð
ñ 
ô 
ñ 
ô 
ˆð ð 	˜Fð 	Ø&ˆF�6‰NØˆFØð 	/ð 	/ˆAØ�uŠu�V‰}Œ}¥	Ò)Ð)Ø:@ÐO &Ð6Ð6¨1¨V¬9Ð6Ð6Ð6ÀaÈÄi�Ø��a˜”i Ñ.Ô.Ð.øð	/ð 	/r#   r‡   )r  r9   re  r9   r   rÒ   rU   )r«  rÙ  rd  rá  râ  s   ` `@@r!   Ú_skill_trees_of_rootrã  £  sS   øøøø€ ð  €Fð/ð /ð /ð /ð /ð /ð /ð /ð /ð 
€Eˆ.˜"ÑÔÐØ€Mr#   c                ó  — 	 |                       |¦  «        }n3# t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w|                     dg ¦  «        D ]£}|                     d¦  «        t
          k    rƒ|                     d¦  «        t          k    re	 |                      |d         ¦  «        \  }}n4# t          $ r'}t                               d|¦  «         Y d}~ dS d}~ww xY wt          |¦  «        c S Œ¤dS )uw  Read the ``sync-manifest`` blob at the root of *root_tree_hash* into
    ``{name: enabled}`` (design.md Â§2.8), or ``None`` if there is no manifest
    entry / it is malformed.

    The manifest is a root-level BLOB entry named ``sync-manifest`` (never a
    skill subtree). This is how a device learns the cross-device opt-in state
    written by another device's push.
    z1skills_sync_client: manifest root read failed: %sNrÿ   r   rÙ   rø   z2skills_sync_client: manifest blob fetch failed: %s)	rx  r4   r\   r]   r7   rÍ  r  rs  r>   )r«  rÙ  r   r^   Ú_kindr-   Úexs          r!   Úread_manifest_of_rootrç  À  s(  € ðØ×#Ò# NÑ3Ô3ˆˆøÝð ð ð Ý�ŠÐHÈ!ÑLÔLÐLØˆtˆtˆtˆtˆtøøøøðøøøð �XŠX�i Ñ$Ô$ð -ð -ˆØ�5Š5�‰=Œ=Õ4Ò4Ð4¸¿º¸v¹¼Í)Ò9SÐ9SðØ$×/Ò/°°&´	Ñ:Ô:‘��t�tøÝð ð ð Ý—’ÐQÐSUÑVÔVÐVØ�t�t�t�t�t�tøøøøðøøøõ ' tÑ,Ô,Ð,Ð,Ð,øØˆ4s,   ‚ ˜
A¢AÁAÂ B?Â?
C0Ã	C+Ã+C0Úcapsc                óÒ   — t          |                      d¦  «        pd¦  «        }|                     dd¦  «        d         }|t          k    rt	          d|›dt          › d�¦  «        ‚d	S )
z<Reject an incompatible server major version (sync contract).Úhsp_versionr‡   r  r   r   z this server speaks sync version z, but this Hermes speaks u"    â€” update Hermes to sync with itN)r9   r7   r)  ÚWIRE_VERSIONrA  )rè  ÚverÚmajors      r!   Ú_check_versionrî  Û  s†   € å
ˆd�hŠh�}Ñ%Ô%Ð+¨Ñ
,Ô
,€CØ�IŠI�c˜1ÑÔ˜aÔ €EØ•ÒÐÝð@¨sð @ð @Ýð@ð @ð @ñ
ô 
ð 	
ð Ðr#   zhermes skill sync)r»  Úidentityr  úOptional['SyncClient']úOptional[List[str]]rï  úOptional[Dict[str, Any]]c               ó>  — |€t          ¦   «         }|d         }| €,t          ¦   «         }|sddddœS t          ||d         ¦  «        } |€t          ¦   «         }|sddddœS |                      ¦   «         }t          |¦  «         t          |                     d	¦  «        pt          ¦  «        }t          ||¬
¦  «        \  }}	}
t          ¦   «         }|                     d¦  «        }|r |                     d¦  «        |	k    rd|dddœS t          ¦   «         }|r|gng }t          |	|||||¬¦  «        }|                      |j        ¦  «         t          |¦  «        }	 |                      |||¦  «         ||d<   |	|d<   t#          |¦  «         d|t%          |¦  «        dœS # t&          $ rv}|j        sI|                      |d|¦  «         ||d<   |	|d<   t#          |¦  «         d|t%          |¦  «        ddœcY d}~S t+          | ||j        |	|||||¦	  «	        cY d}~S d}~ww xY w)u#  Push opted-in skills to the owner's HEAD (sync contract).

    Uploads all new objects, then CAS-es ``refs/user/<owner>/HEAD``. On a 409,
    fetches the actual head, three-way merges, and retries once (Â§4.4 / M1-C).
    Returns a result dict; never raises for the inert / no-op cases.
    Nrh   Fúno sync base url configuredT©ÚokÚreasonÚnooprb   zno skills opted into syncrï   rö   r•  rÎ   Ú	unchanged)rö  r•  r÷  rø  ©rh   r  r  rÕ   )rö  r•  Úpushed_objects)rö  r•  rû  Úrecovered_stale_head)rn   rƒ   rP  rÆ   r^  rî  rÞ   r7   ÚDEFAULT_MAX_OBJECT_BYTESrÊ  rš  r<  r  rƒ  rÕ   rÑ  r�  r–  rà   rL  rM  Ú_resolve_push_conflict)r«  r»  rï  r  rh   r‚   rè  Ú	max_bytesrÕ   rÉ  r´  r+   Ú	base_headr  r  rt  ÚrefÚconflicts                     r!   Úpush_skillsr  ê  sÅ  € ð ÐÝ#Ñ%Ô%ˆØ�WÔ€EØ€~Ý$Ñ&Ô&ˆØð 	XØÐ+HÐRVÐWÐWÐWÝ˜D (¨9Ô"5Ñ6Ô6ˆàÐÝ-Ñ/Ô/ˆØð QØÐ&AÈ4ÐPÐPÐPà×ÒÑ Ô €DÝ�4ÑÔÐÝ�D—H’HÐ/Ñ0Ô0ÐLÕ4LÑMÔM€Iå,¨[È9ÐUÑUÔUÑ€GˆY˜åÑ Ô €HØ—’˜VÑ$Ô$€Ið
 ð T�X—\’\ &Ñ)Ô)¨YÒ6Ð6Ø I¸ÈdÐSÐSÐSåÑÔ€FØ&Ð.ˆyˆkˆk¨B€GÝØ�7 %°ÀÐQXðñ ô €Kð ×Ò�w”Ñ'Ô'Ð'Ý
˜Ñ
Ô
€Cð
Ø�Š�s˜I {Ñ3Ô3Ð3Ø&ˆ�ÑØ$ˆ�ÑÝ˜Ñ"Ô"Ð"Ø KÅ3ÀwÁ<Ä<ÐPÐPÐPøÝð 
ð 
ð 
ØŒð 	ð �NŠN˜3  kÑ2Ô2Ð2Ø*ˆH�VÑØ(ˆH�VÑÝ˜XÑ&Ô&Ð&àØ#Ý"% g¡,¤,Ø(,ð	ð ð ð ð ð ð ð õ &Ø�H˜hœo¨y¸+Ø�[ '¨9ñ
ô 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øøøøð
øøøs,   ÅAF Æ
HÆ&A
HÇ0HÇ6HÈHÈHÚactual_headÚour_rootÚ
our_commitú'ObjectSet'r   c	           	     ór  — |d         }	t          ¦   «         }
t          | |¦  «        }|rt          | |¦  «        nd }t          | |¦  «        }t          | |¦  «        }|rt          | |¦  «        ni }i }g }t          |¦  «        t          |¦  «        z  t          |¦  «        z  }|D ] }|                     |¦  «        }|                     |¦  «        }|                     |¦  «        }t          |||¦  «        }|dk    r|                     |¦  «         |�|||<   Œu|dk    r|�|||<   Œƒ|dk    r|�|||<   Œ‘|dk    r	|�|n|||<   Œ¡|rrt          | |	¦  «        }t          |	|¦  «        }	 |  	                    |d |¦  «         n# t          $ r Y nw xY wdd|t          |¦  «        |t          |¦  «        › d|› d	�d
œS t          ¦   «         }|j                             ¦   «         D ]\  }\  }}||f|j        |<   Œt!          | ||¦  «        }t#          |||g|	|
d|› �|¬¦  «        }|                      |j        ¦  «         	 |  	                    t'          |	¦  «        ||¦  «         n,# t          $ r} ddd| j        › d�| j        dœcY d } ~ S d } ~ ww xY wt+          ¦   «         }!||!d<   ||!d<   t-          |!¦  «         d|ddœS )Nrh   ÚoverlapÚoursÚtheirsÚeitherFTz' skill(s) changed on both sides; wrote z-. Resolve out-of-band (hermes sync / NAS UI).)rö  r  Úconflict_refÚoverlapping_skillsr  r  zmerge: rú  zmerge CAS lost again (head now z); retry sync.)rö  r  r  r  r•  rÎ   )rö  r•  Úmerged)r<  rØ  rã  rÂ   r7   Ú_merge_skillrÁ   Ú_next_conflict_indexrÕ  r�  rL  r(   rà   rÑ   rÕ   r)   Ú_assemble_root_from_skill_treesr  rƒ  rÑ  rM  rš  r–  )"r«  rï  r  r  r  rÕ   r»  r  r   rh   r  Útheirs_rootÚ	base_rootÚ
ours_treesÚtheirs_treesÚ
base_treesr  ÚoverlapsÚ	all_pathsrã   ÚoÚtÚbÚdecisionrÒ  r  Úmerge_objectsr|  rÙ   r-   Úmerged_rootÚmerge_commitÚc2r+   s"                                     r!   rþ  rþ  L  s›  € ð �WÔ€EÝÑÔ€Få& v¨{Ñ;Ô;€KØ;DÐNÕ$ V¨YÑ7Ô7Ð7È$€Iå% f¨hÑ7Ô7€JÝ'¨°Ñ<Ô<€LØ<EÐMÕ% f¨iÑ8Ô8Ð8È2€Jà€FØ€HÝ�J‘”¥# lÑ"3Ô"3Ñ3µc¸*±o´oÑE€IØð 5ð 5ˆØ�NŠN˜4Ñ Ô ˆØ×Ò˜TÑ"Ô"ˆØ�NŠN˜4Ñ Ô ˆÝ  1 aÑ(Ô(ˆØ�yÒ Ð Ø�OŠO˜DÑ!Ô!Ð!ð ˆ}Ø ��t‘øØ˜ÒÐ A MØˆF�4‰LˆLØ˜Ò!Ð! a mØˆF�4‰LˆLØ˜Ò!Ð!Ø ! ˜1˜1°1ˆF�4‰Løð ð 
å  ¨Ñ/Ô/ˆÝ(¨°Ñ2Ô2ˆð	Ø�NŠN˜<¨¨zÑ:Ô:Ð:Ð:øÝð 	ð 	ð 	ØˆDð	øøøð ØØ(Ý"(¨Ñ"2Ô"2Ø&å�x‘=”=ð Oð OØðOð Oð Oð

ð 

ð 
	
õ ‘K”K€Mà"œ?×0Ò0Ñ2Ô2ð 0ð 0‰ˆ‰<ˆD�$Ø$(¨$ <ˆÔ˜aÑ Ð Ý1°&¸&À-ÑPÔP€KÝØØ	�jÐ!ØØØ#˜'Ð#Ð#Øðñ ô €Lð ×Ò�}Ô,Ñ-Ô-Ð-ð
Ø�Š•} UÑ+Ô+¨[¸,ÑGÔGÐGÐGøÝð 
ð 
ð 
àØØR¸¼ÐRÐRÐRØœ9ð	
ð 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øøøøð
øøøõ Ñ Ô €HØ#€HˆVÑØ"€HˆVÑÝ�XÑÔÐØ ¸Ð=Ð=Ð=s0   Å(F  Æ 
FÆFÈ;$I  É 
J	É*JÉ>J	ÊJ	r‚   r
  r  c                óN   — ||k    r|�dndS || k    }|| k    }|r|sdS |r|sdS dS )aa  Three-way decision for one skill's tree hash.

    Returns one of: ``ours``, ``theirs``, ``either``, ``overlap``, ``none``.
    Mirrors the origin/user/incoming decision block of skills_sync.py:619-643:
    a side "modified" the skill when its hash differs from the common base
    (analogous to ``_is_tracked_user_modification(origin, current)``).
    Nr  Únoner
  r  r	  rU   )r‚   r
  r  Úours_changedÚtheirs_changeds        r!   r  r  «  s[   € ð ˆv‚~€~ØÐ+ˆxˆx°Ð7Ø˜4’<€LØ˜t’^€NØð ˜Nð ØˆvØð ˜lð Øˆxàˆ9r#   Úskill_treesc                óæ   — i }|                      ¦   «         D ]K\  }}t          |¦  «        j        }|}|dd…         D ]}|                     |i ¦  «        }Œd|i||d         <   ŒLt	          ||¦  «        S )a(  Build a profile-root tree object from ``{posix_rel_path: tree_hash}``.

    Rebuilds the intermediate category trees. The referenced skill trees are
    assumed already durable (they came from either side of the merge); only
    the new intermediate/root tree objects are added to *objects*.
    Nr¾  r¿  )r)   r   r¶   rÛ   rÄ  )	r«  r&  rÕ   rÎ   rã   r  r¶   rÆ  rÇ  s	            r!   r  r  ¿  s�   € ð €DØ&×,Ò,Ñ.Ô.ð 2ð 2‰ˆˆiÝ˜dÑ#Ô#Ô)ˆØˆØ˜#˜2˜#”Jð 	-ð 	-ˆDØ—?’? 4¨Ñ,Ô,ˆDˆDØ% yÐ1ˆˆU�2ŒY‰ˆÝ˜D 'Ñ*Ô*Ð*r#   c                ód  — 	 |                       d|› d�¦  «        }n# t          $ r Y dS w xY wg }|D ]j}|                     dd¦  «        }|                     dd¦  «        d         }|                     ¦   «         r"|                     t          |¦  «        ¦  «         Œk|rt          |¦  «        dz   ndS )z4Pick the next free conflict ref index for the owner.rÏ  rÔ  r   r   r‡   ru   r¾  )rm  rA  r7   ÚrsplitÚisdigitrÁ   rÞ   Úmax)r«  rh   rh  Úusedrb  r   Útails          r!   r  r  Ò  sÉ   € ðØ�ŠÐ=¨EÐ=Ð=Ð=Ñ>Ô>ˆˆøÝð ð ð Øˆqˆqðøøøà€DØð #ð #ˆØ�uŠu�V˜RÑ Ô ˆØ�{Š{˜3 Ñ"Ô" 2Ô&ˆØ�<Š<‰>Œ>ð 	#Ø�KŠK�˜D™	œ	Ñ"Ô"Ð"øØ"Ð)�C�‰IŒI˜‰MˆM¨Ð)s   ‚ œ
*©*©rï  c               óî  — |€t          ¦   «         }|d         }| €,t          ¦   «         }|sddddœS t          ||d         ¦  «        } |                      ¦   «         }t	          |¦  «         |                      t          |¦  «        ¦  «        }d}|D ]?}|                     d¦  «        t          |¦  «        k    r|                     d	¦  «        } nŒ@|sdd
ddœS t          ¦   «         }||                     d¦  «        k    rdd|ddœS t          | |¦  «        }	t          | |	¦  «        }
g }t          | |	¦  «        }|r“	 ddlm}m}m} |                     ¦   «         D ]@\  }}|sŒ ||¦  «        sŒ ||¦  «        s! ||d¦  «         |                     |¦  «         ŒAn2# t$          $ r%}t&                               d|¦  «         Y d}~nd}~ww xY wt+          t-          ¦   «         ¦  «        }g }|
                     ¦   «         D ]C\  }}|r||vrŒt/          ¦   «         |z  }t1          | ||¦  «         |                     |¦  «         ŒD||d<   t3          |¦  «         d|t5          |¦  «        t5          |¦  «        dœS )a—  Pull the owner's HEAD and materialize opted-in skills to disk.

    Fetches ``refs/user/<owner>/HEAD``; if it advanced past our recorded head,
    walks the profile-root tree and writes each skill tree into
    ~/.hermes/skills/. Only paths the user has opted into (``sync: true``) are
    materialized, so a pull never resurrects a skill the user hasn't chosen.
    Best-effort; returns a result dict.
    Nrh   Frô  Trõ  rb   r   rø   zno remote HEAD yetr•  zalready up to date)rö  r÷  r•  rø  r   )Úset_syncÚis_curation_eligibleÚis_sync_enabledz8skills_sync_client: manifest opt-in reconcile failed: %s)rö  r•  ÚupdatedÚopt_in_adopted)rn   rƒ   rP  r^  rî  rm  rÑ  r7   rš  rØ  rã  rç  r²   r0  r1  r2  r)   rÁ   r4   r\   r]   rÂ   Ú_opted_in_rel_pathsr«   r¯  r–  r(   )r«  rï  rh   r‚   rè  rh  r•  rb  r+   Ú	root_treeÚremote_treesÚreconciled_from_manifestÚremote_manifestr0  r1  r2  Úsnamer   r^   Úopted_inr3  rã   r  r¬  s                           r!   Úpull_skillsr<  å  s$  € ð ÐÝ#Ñ%Ô%ˆØ�WÔ€EØ€~Ý$Ñ&Ô&ˆØð 	XØÐ+HÐRVÐWÐWÐWÝ˜D (¨9Ô"5Ñ6Ô6ˆà×ÒÑ Ô €DÝ�4ÑÔÐà�?Š?�=¨Ñ/Ô/Ñ0Ô0€DØ€DØð ð ˆØ�5Š5�‰=Œ=�M¨%Ñ0Ô0Ò0Ð0Ø—5’5˜‘=”=ˆDØˆEð 1ð ð JØÐ&:ÀDÐIÐIÐIåÑ Ô €HØˆx�|Š|˜FÑ#Ô#Ò#Ð#ØÐ&:ÀDÐRVÐWÐWÐWå$ V¨TÑ2Ô2€IÝ'¨°	Ñ:Ô:€Lð +-ÐÝ+¨F°IÑ>Ô>€OØð Xð	XØYÐYÐYÐYÐYÐYÐYÐYÐYÐYà"1×"7Ò"7Ñ"9Ô"9ð ;ð ;‘��wØð ØØ+Ð+¨EÑ2Ô2ð ØØ&� uÑ-Ô-ð ;Ø�H˜U DÑ)Ô)Ð)Ø,×3Ò3°EÑ:Ô:Ð:øð;øõ ð 	Xð 	Xð 	XÝ�LŠLÐSÐUVÑWÔWÐWÐWÐWÐWÐWÐWøøøøð	Xøøøõ Õ&Ñ(Ô(Ñ)Ô)€HØ€GØ'×-Ò-Ñ/Ô/ð ð ‰ˆˆið ð 	˜ HÐ,Ð,ØÝ‰}Œ}˜tÑ#ˆÝ˜ ¨DÑ1Ô1Ð1Ø�Š�tÑÔÐÐà€HˆVÑÝ�XÑÔÐàØÝ˜'‘?”?Ý Ð!9Ñ:Ô:ð	ð ð s   Ä;AF Æ
G
Æ%GÇG
c                 óœ   — g } t          ¦   «         D ]:}t          |¦  «        }|�'|                      |                     ¦   «         ¦  «         Œ;| S )z<Relative posix paths of skills the user has opted into sync.)rÆ   rº  rÁ   r¹  )Úpathsr   r»   s      r!   r5  r5  :  sN   € à€EÝ'Ñ)Ô)ð )ð )ˆÝ˜dÑ#Ô#ˆØˆ?Ø�LŠL˜Ÿš™œÑ(Ô(Ð(øØ€Lr#   )r  c                ó:  — 	 t          ¦   «         }|                     d¦  «        sdS t          ¦   «         sdS t          ¦   «         sdS t	          ¦   «         sdS t          || ¬¦  «        S # t          $ r(}t                               d|d¬¦  «         Y d}~dS d}~ww xY w)z†Best-effort push if all gates pass. Returns a result dict or None.
    Never raises. Called from the debounced skill_manage push hook.ri   N©rï  r  z0skills_sync_client: maybe_push_skills failed: %sT©Úexc_info)	rn   r7   rž   rƒ   rÆ   r  r4   r\   r]   )r  rï  r^   s      r!   Úmaybe_push_skillsrC  M  s½   € ðÝ#Ñ%Ô%ˆØ�|Š|˜LÑ)Ô)ð 	Ø�4Ý#Ñ%Ô%ð 	Ø�4Ý$Ñ&Ô&ð 	Ø�4Ý&Ñ(Ô(ð 	Ø�4Ý H°gÐ>Ñ>Ô>Ð>øÝð ð ð Ý�ŠÐGÈÐUYˆÑZÔZÐZØˆtˆtˆtˆtˆtøøøøðøøøs-   ‚#A( §A( ·A( ÁA( ÁA( Á(
BÁ2BÂBc                 ó  — 	 t          ¦   «         } |                      d¦  «        sdS t          ¦   «         sdS t          ¦   «         sdS t	          | ¬¦  «        S # t
          $ r(}t                               d|d¬¦  «         Y d}~dS d}~ww xY w)z¥Best-effort pull if all gates pass. Returns a result dict or None.
    Never raises. Invoked at the curator tick sites (gateway housekeeping loop
    + CLI startup).ri   Nr.  z0skills_sync_client: maybe_pull_skills failed: %sTrA  )rn   r7   rž   rƒ   r<  r4   r\   r]   )rï  r^   s     r!   Úmaybe_pull_skillsrE  `  s¨   € ðÝ#Ñ%Ô%ˆØ�|Š|˜LÑ)Ô)ð 	Ø�4Ý#Ñ%Ô%ð 	Ø�4Ý$Ñ&Ô&ð 	Ø�4Ý HÐ-Ñ-Ô-Ð-øÝð ð ð Ý�ŠÐGÈÐUYˆÑZÔZÐZØˆtˆtˆtˆtˆtøøøøðøøøs'   ‚#A §A ·A ÁA Á
B	Á!BÂB	c                 óp  — ddt          ¦   «         t          ¦   «         t          ¦   «         g dddddg g dœ} 	 t          ¦   «         }d| d<   |                     d¦  «        | d<   t          |                     d¦  «        ¦  «        | d<   n=# t          $ r Y n1t          $ r%}t           	                    d|¦  «         Y d}~nd}~ww xY w	 t          ¦   «         | d	<   t          ¦   «                              d
¦  «        | d<   n# t          $ r Y nw xY w	 t          ¦   «         }d| d<   |                     d¦  «        | d<   |                     d¦  «        | d<   t          ¦   «         | d<   t          | d         ¦  «        | d<   n=# t          $ r Y n1t          $ r%}t           	                    d|¦  «         Y d}~nd}~ww xY w| S )zBReturn a status snapshot for ``hermes sync status``. Never raises.FN)ri   Ú	logged_inÚfeature_enabledr¥   rg   Úopted_in_skillsÚ
local_headrh   Úorg_availableÚorg_idÚorg_roleÚ
org_skillsÚorg_skills_modifiedTrG  rh   ri   z3skills_sync_client: sync_status identity failed: %srI  r•  rJ  rK  rL  rM  rN  rO  z5skills_sync_client: sync_status org lookup failed: %s)rž   r¦   rƒ   rn   r7   r   rP   r4   r\   r]   rÆ   rš  Úresolve_org_identityÚlist_org_skill_namesÚ list_locally_modified_org_skills)rC  rï  r^   Úorg_identitys       r!   Úsync_statusrT  r  s1  € ð ØÝ/Ñ1Ô1Ý-Ñ/Ô/Ý)Ñ+Ô+ØØØð ØØØà!ð#ð €Fð&OÝ#Ñ%Ô%ˆØ"ˆˆ{ÑØ"Ÿ,š, wÑ/Ô/ˆˆw‰Ý# H§L¢L°Ñ$>Ô$>Ñ?Ô?ˆˆ|ÑÐøÝð ð ð ØˆÝð Oð Oð OÝ�ŠÐJÈAÑNÔNÐNÐNÐNÐNÐNÐNøøøøðOøøøðÝ$;Ñ$=Ô$=ˆÐ Ñ!Ý.Ñ0Ô0×4Ò4°VÑ<Ô<ˆˆ|ÑÐøÝð ð ð ØˆðøøøðQÝ+Ñ-Ô-ˆØ"&ˆˆÑØ'×+Ò+¨HÑ5Ô5ˆˆxÑØ)×-Ò-¨jÑ9Ô9ˆˆzÑÝ3Ñ5Ô5ˆˆ|ÑÝ(HØ�8Ôñ)
ô )
ˆÐ$Ñ%Ð%øõ ð ð ð ØˆÝð Qð Qð QÝ�ŠÐLÈaÑPÔPÐPÐPÐPÐPÐPÐPøøøøðQøøøà€MsO   ¶AB Â
CÂ	CÂB<Â<CÃ5C; Ã;
DÄDÄA,E9 Å9
F3Æ	F3ÆF.Æ.F3c                 óú  — g } 	 ddl m}  |t          ¦   «         ¦  «        }|s| S t          ¦   «         |z  }|                     ¦   «         s| S |                     d¦  «        D ]Y}|j                             |¦  «        }|j        r6|  	                    t          |¦  «                             dd¦  «        ¦  «         ŒZn2# t          $ r%}t                               d|¦  «         Y d}~nd}~ww xY wt          | ¦  «        S )zESkill names present in the local org mirror (empty when none pulled).r   ©Úread_active_org_idrÈ   ú\ru   z0skills_sync_client: org skill listing failed: %sN)r³   rW  r«   Ú_org_dirr  rË   rÍ   rµ   r¶   rÁ   r9   r¦  r4   r\   r]   r(   )rÄ   rW  rL  rÎ   rÏ   r»   r^   s          r!   rQ  rQ  ¥  s   € à€EðLØ8Ð8Ð8Ð8Ð8Ð8à#Ð#¥K¡M¤MÑ2Ô2ˆØð 	ØˆLÝ‰zŒz˜FÑ"ˆØ�{Š{‰}Œ}ð 	ØˆLØŸ
š
 :Ñ.Ô.ð 	:ð 	:ˆHØ”/×-Ò-¨dÑ3Ô3ˆCØŒyð :Ø—’�S ™XœX×-Ò-¨d°CÑ8Ô8Ñ9Ô9Ð9øð	:øõ ð Lð Lð LÝ�ŠÐGÈÑKÔKÐKÐKÐKÐKÐKÐKøøøøðLøøøå�%‰=Œ=Ðs#   „ B< ¥&B< ÁA/B< Â<
C+ÃC&Ã&C+Ú_orgc                 ó@  — t          ¦   «         } |                      d¦  «        pi }|                     d¦  «        }|                     d¦  «        }|st          d¦  «        ‚t          |t          ¦  «        r|st          d¦  «        ‚t	          |¦  «        | d<   || d<   | S )u^  Resolve identity + org context for org-skill operations.

    Returns ``resolve_identity()``'s dict extended with ``org_id`` and
    ``org_role``. Raises :class:`SyncInertError` when the token carries no
    ``org_role`` claim (personal org / issuer predates org support) â€” the
    caller should treat org sync as unavailable, NOT as an error.
    rj   rL  rM  z,no organisation associated with this accountz4this account isn't a member of a shared organisation)rn   r7   rP   r5   r9   )rï  rj   rL  rM  s       r!   rP  rP  Ð  s®   € õ  Ñ!Ô!€HØ�\Š\˜(Ñ#Ô#Ð) r€FØ�ZŠZ˜Ñ!Ô!€FØ�zŠz˜*Ñ%Ô%€HØð MÝÐKÑLÔLÐLÝ�h¥Ñ$Ô$ð 
¨Hð 
ÝØBñ
ô 
ð 	
õ ˜V™œ€HˆXÑØ#€HˆZÑØ€Or#   c                 óF   — 	 t          ¦   «          dS # t          $ r Y dS w xY w)zETrue iff this token can see the org-skill surface (multi-member org).TF)rP  r4   rU   r#   r!   Úorg_sync_availabler]  ç  s:   € ðÝÑÔÐØˆtøÝð ð ð Øˆuˆuðøøøs   ‚ ’
 Ÿ é   rL  c                ór   ‡— |                       d‰› d�d¬¦  «        }t          ˆfd„|D ¦   «         d¦  «        S )ay  Current ``refs/org/<org_id>/HEAD``, or None if the org has no content.

    Reads through the ORG endpoint. The personal refs route is scoped to the
    caller's own owner and answers an ``refs/org/...`` prefix with the caller's
    PERSONAL refs, so a personal-route read here silently reports "no org head"
    and every subsequent CAS races against a head it never saw.
    ú	refs/org/ru   Trc  c              3  ót   •K  — | ]2}|                      d ¦  «        t          ‰¦  «        k    ¯(|d         V — Œ3dS )r   rø   N)r7   Úorg_head_ref)r    rb  rL  s     €r!   r"  z!_read_org_head.<locals>.<genexpr>   sC   øè è € ÐJÐJ�q A§E¢E¨&¡M¤Mµ\À&Ñ5IÔ5IÒ$IÐ$Iˆˆ6ŒÐ$IÐ$IÐ$IÐ$IÐJÐJr#   N)rm  Únext)r«  rL  rh  s    ` r!   Ú_read_org_headrd  ö  sQ   ø€ ð �?Š?Ð0 vÐ0Ð0Ð0¸Dˆ?ÑAÔA€DÝØJÐJÐJÐJ˜DÐJÑJÔJÈDñô ð r#   c                ó   — d| › d�S )Nr`  rÐ  rU   ©rL  s    r!   rb  rb    s   € Ø$�vÐ$Ð$Ð$Ð$r#   c                 ó.   — t          ¦   «         t          z  S )z<Local mirror root for org skills (read-only by convention ).)r«   r·   rU   r#   r!   rY  rY    s   € å‰=Œ=�<Ñ'Ð'r#   c               óˆ  — |pt          ¦   «         }d|vrt          d¦  «        ‚|d         }| €5t          ¦   «         }|st          d¦  «        ‚t          ||d         ¦  «        } |                      ¦   «         }t          |¦  «         d|                     d¦  «        pg vrt          d¦  «        ‚t          | |¦  «        }t          |¦  «         |sd	|dg d
œS |  	                    |d	¬¦  «        }|d         }t          | |d	¬¦  «        }t          ¦   «         |z  }	g }
g }t          |¦  «        }t          |                     ¦   «         ¦  «        D �]#\  }}|	t          |¦  «        z  }	 |                     ¦   «         rot#          ||¦  «        rF|                     |¦  «        pi }|                     d¦  «        |k    r|                     |¦  «         Œƒddl}|                     |¦  «         |                     d	d	¬¦  «         t-          | ||d	¬¦  «         t/          |¦  «        |dœ||<   |
                     |¦  «         Œñ# t0          $ r'}t2                               d||¦  «         Y d}~�Œd}~ww xY wt7          ||||                     d¦  «        pi                      dd¦  «        |                     d¦  «        pi                      dd¦  «        |                     dd¦  «        |
dœ¦  «         t9          ||¦  «         |r<t2                               dt;          |¦  «        d                     |¦  «        ¦  «         d	|||
|dœS )uÏ  Pull the org canonical set into ``~/.hermes/skills/_org/<org_id>/``.

    Fast-forward only (design.md Â§2.6: no client merge on the org path): the
    mirror is replaced with the org HEAD's content. Local edits under _org/
    are NOT merged â€” they are overwritten on pull; a member's change of record
    is `propose_skill` (the fork lives in their personal skills, not _org/).
    Returns {ok, org_id, head, updated} (updated = skill rel-paths written).
    rL  z!no organisation context availableNúno sync base URL configuredrb   r~  Úfeaturesú.this server does not support org-shared skillsT)rö  rL  r•  r3  rc  r   r   r4  )Úfingerprintr   z;skills_sync_client: org skill materialize failed for %s: %sr  rh   r‡   r  r  )rL  r•  Úauthor_user_idÚauthor_devicer  r   z]skills_sync_client: %d org skill(s) have local edits AND upstream changes; left untouched: %sz, )rö  rL  r•  r3  Ú
conflicted)rP  rP   rƒ   rP  r^  rî  r7   rd  Ú_write_active_org_markerrv  rã  rY  Ú_read_org_baseliner(   r)   r   rÊ   Úorg_skill_is_locally_modifiedrÁ   ÚshutilÚrmtreer8  r¯  Ú_skill_dir_fingerprintr4   r\   r®  Ú_write_org_provenanceÚ_write_org_baselinerà   r*  )r«  rï  rL  rg   rè  r•  Úhead_commitr6  r&  Ú	dest_rootr3  ro  ÚbaselineÚrel_pathr  r¬  Úprevrs  r^   s                      r!   Úpull_org_skillsr}    sÄ  € ð Ð1Õ/Ñ1Ô1€HØ�xÐÐÝÐ@ÑAÔAÐAØ�hÔ€FØ€~Ý(Ñ*Ô*ˆØð 	@Ý Ð!>Ñ?Ô?Ð?Ý˜H h¨yÔ&9Ñ:Ô:ˆà×ÒÑ Ô €DÝ�4ÑÔÐØ�T—X’X˜jÑ)Ô)Ð/¨RÐ0Ð0ÝÐMÑNÔNÐNå˜& &Ñ)Ô)€Dõ ˜VÑ$Ô$Ð$Øð KØ f°dÀrÐJÐJÐJà×(Ò(¨¸Ð(Ñ>Ô>€KØ˜FÔ#€IÝ& v¨yÀDÐIÑIÔI€Kå‘
”
˜VÑ#€IØ€Gð €JÝ! &Ñ)Ô)€HÝ% k×&7Ò&7Ñ&9Ô&9Ñ:Ô:ð ñ Ñˆ�)Ø�=¨Ñ2Ô2Ñ2ˆð	Ø�{Š{‰}Œ}ð $õ
 1°¸6ÑBÔBð Ø#Ÿ<š<¨Ñ1Ô1Ð7°R�Dð —x’x Ñ'Ô'¨9Ò4Ð4Ø"×)Ò)¨(Ñ3Ô3Ð3ØØ���à—’˜dÑ#Ô#Ð#Ø�JŠJ˜t¨dˆJÑ3Ô3Ð3Ý˜V Y°ÀÐEÑEÔEÐEå5°dÑ;Ô;Ø!ð"ð "ˆH�XÑð �NŠN˜8Ñ$Ô$Ð$Ð$øÝð 	ð 	ð 	Ý�NŠNØMØØñô ð ð ð ð ð ñ øøøøð	øøøõ ØàØØ*Ÿš¨xÑ8Ô8Ð>¸B×CÒCÀGÈRÑPÔPØ)Ÿošo¨hÑ7Ô7Ð=¸2×BÒBÀ8ÈRÑPÔPØ—/’/ $¨Ñ+Ô+Øð	
ð 	
ñ
ô 
ð 
õ ˜ Ñ)Ô)Ð)Øð 
Ý�Šð*å�
‰OŒOØ�IŠI�jÑ!Ô!ñ		
ô 	
ð 	
ð ØØØØ ðð ð s    Å%A)H=ÇA-H=È=
I.ÉI)É)I.c                ót  — t          j        ¦   «         }	 t          d„ |                      d¦  «        D ¦   «         ¦  «        D ]¯}|                     t          |                     | ¦  «        ¦  «                             dd¦  «                             d¦  «        ¦  «         |                     d¦  «         |                     | 	                    ¦   «         ¦  «         |                     d¦  «         Œ°n4# t          $ r'}t                               d| |¦  «         Y d}~d	S d}~ww xY w|                     ¦   «         S )
a  Stable content hash of a materialized skill directory.

    Used to tell "the user/agent edited this org skill" from "this is exactly
    what upstream shipped". Hashes every file's relative path + bytes, sorted,
    so it is independent of filesystem ordering and mtimes.
    c              3  óB   K  — | ]}|                      ¦   «         ¯|V — Œd S rÔ   )r  )r    rò   s     r!   r"  z)_skill_dir_fingerprint.<locals>.<genexpr>„  s/   è è € ÐBÐB˜a°a·i²i±k´kÐB˜ÐBÐBÐBÐBÐBÐBr#   Ú*rX  ru   r0   ó    z1skills_sync_client: fingerprint failed for %s: %sNr‡   )r@   rA   r(   rË   Úupdater9   rµ   r¦  rM   r  r¸   r\   r]   rB   )rã   r|  rª  r^   s       r!   ru  ru  {  s  € õ 	ŒÑÔ€AðÝÐBÐB 4§:¢:¨c¡?¤?ÐBÑBÔBÑBÔBð 	ð 	ˆAØ�HŠH•S˜Ÿš tÑ,Ô,Ñ-Ô-×5Ò5°d¸CÑ@Ô@×GÒGÈÑPÔPÑQÔQÐQØ�HŠH�U‰OŒOˆOØ�HŠH�Q—\’\‘^”^Ñ$Ô$Ð$Ø�HŠH�U‰OŒOˆOˆOð		øõ
 ð ð ð Ý�ŠÐHÈ$ÐPQÑRÔRÐRØˆrˆrˆrˆrˆrøøøøðøøøð �;Š;‰=Œ=Ðs   •CC2 Ã2
D#Ã<DÄD#c                ó6   — ddl m} t          ¦   «         | z  |z  S )zBSidecar recording the upstream fingerprint of each mirrored skill.r   )ÚORG_BASELINE_FILE)r³   r„  rY  )rL  r„  s     r!   Ú_org_baseline_pathr…  �  s)   € à3Ð3Ð3Ð3Ð3Ð3å‰:Œ:˜ÑÐ!2Ñ2Ð2r#   c                ó’   — 	 t          j        t          | ¦  «                             d¬¦  «        ¦  «        S # t          $ r i cY S w xY w)Nr0   r1  )r1   r2   r…  r6  r4   rf  s    r!   rq  rq  –  sU   € ðÝŒzÕ,¨VÑ4Ô4×>Ò>ÈÐ>ÑPÔPÑQÔQÐQøÝð ð ð Øˆ	ˆ	ˆ	ðøøøs   ‚47 ·AÁArz  c                ó  — 	 t          | ¦  «        }|j                             dd¬¦  «         |                     t	          j        |dd¬¦  «        d¬¦  «         d S # t          $ r&}t                               d|¦  «         Y d }~d S d }~ww xY w)NTr4  r�  )rž  rI   r0   r1  z-skills_sync_client: baseline write failed: %s)	r…  rÍ   r8  r9  r1   rL   r4   r\   r]   )rL  rz  rò   r^   s       r!   rw  rw  �  s¤   € ðIÝ˜vÑ&Ô&ˆØ	Œ�Š˜t¨dˆÑ3Ô3Ð3Ø	�Š•T”Z °¸dÐCÑCÔCÈgˆÑVÔVÐVÐVÐVøÝð Ið Ið IÝ�ŠÐDÀaÑHÔHÐHÐHÐHÐHÐHÐHÐHøøøøðIøøøs   ‚AA Á
BÁ%BÂBÚskill_rel_pathc                ó>  — t          ¦   «         |z  t          | ¦  «        z  }|                     ¦   «         sdS t          |¦  «                             | ¦  «        pi }t          |t          ¦  «        r|                     d¦  «        n|}|sdS t          |¦  «        |k    S )zITrue when the local copy of an org skill differs from what upstream sent.Frl  )rY  r   r  rq  r7   r5   r6   ru  )rˆ  rL  r¬  r²  Úrecordeds        r!   rr  rr  ¦  s™   € å‰:Œ:˜Ñ¥¨~Ñ!>Ô!>Ñ>€DØ�;Š;‰=Œ=ð ØˆuÝ˜vÑ&Ô&×*Ò*¨>Ñ:Ô:Ð@¸b€EÝ+5°e½TÑ+BÔ+BÐMˆu�yŠy˜Ñ'Ô'Ð'È€HØð ð ˆuÝ! $Ñ'Ô'¨8Ò3Ð3r#   c                ó  ‡ — 	 ddl m} ‰ p |t          ¦   «         ¦  «        Š ‰ sg S t          ‰ ¦  «        }t	          ˆ fd„|D ¦   «         ¦  «        S # t
          $ r'}t                               d|¦  «         g cY d}~S d}~ww xY w)z7Org skills with local edits that upstream has not seen.r   rV  c              3  ó<   •K  — | ]}t          |‰¦  «        ¯|V — Œd S rÔ   )rr  )r    r»   rL  s     €r!   r"  z3list_locally_modified_org_skills.<locals>.<genexpr>½  sG   øè è € ð 
ð 
ØÕ'DÀSÈ&Ñ'QÔ'Qð
Øð
ð 
ð 
ð 
ð 
ð 
r#   z,skills_sync_client: modified-scan failed: %sN)r³   rW  r«   rq  r(   r4   r\   r]   )rL  rW  rz  r^   s   `   r!   rR  rR  ´  sË   ø€ ðØ8Ð8Ð8Ð8Ð8Ð8àÐ<Ð-Ð-­k©m¬mÑ<Ô<ˆØð 	ØˆIÝ% fÑ-Ô-ˆÝð 
ð 
ð 
ð 
Ø#ð
ñ 
ô 
ñ 
ô 
ð 	
øõ ð ð ð Ý�ŠÐCÀQÑGÔGÐGØˆ	ˆ	ˆ	ˆ	ˆ	ˆ	øøøøðøøøs"   ƒ"A ¦)A Á
BÁA<Á6BÁ<Bc                óø   — 	 ddl m} t          ¦   «         }|                     dd¬¦  «         ||z                       | d¬¦  «         dS # t
          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w)	zBRecord which org's mirror may resolve (best-effort, never raises).r   ©ÚORG_ACTIVE_MARKERTr4  r0   r1  z6skills_sync_client: active-org marker write failed: %sN)r³   r�  rY  r8  r9  r4   r\   r]   )rL  r�  rÎ   r^   s       r!   rp  rp  Å  s¨   € ðRØ7Ð7Ð7Ð7Ð7Ð7å‰zŒzˆØ�
Š
˜4¨$ˆ
Ñ/Ô/Ð/Ø	Ð!Ñ	!×-Ò-¨f¸wÐ-ÑGÔGÐGÐGÐGøÝð Rð Rð RÝ�ŠÐMÈqÑQÔQÐQÐQÐQÐQÐQÐQÐQøøøøðRøøøs   ‚AA	 Á	
A9ÁA4Á4A9c                ó&  — 	 ddl m} t          ¦   «         | z  }|                     dd¬¦  «         ||z                       t          j        |d¬¦  «        d¬¦  «         d
S # t          $ r&}t           	                    d	|¦  «         Y d
}~d
S d
}~ww xY w)zDPersist the org HEAD provenance sidecar (best-effort, never raises).r   )ÚORG_PROVENANCE_FILETr4  r�  )rž  r0   r1  z3skills_sync_client: org provenance write failed: %sN)
r³   r‘  rY  r8  r9  r1   rL   r4   r\   r]   )rL  r-   r‘  r¬  r^   s        r!   rv  rv  Ñ  sË   € ð	OØ9Ð9Ð9Ð9Ð9Ð9å‰zŒz˜FÑ"ˆØ�
Š
˜4¨$ˆ
Ñ/Ô/Ð/Ø	Ð#Ñ	#×/Ò/ÝŒJ�t AÐ&Ñ&Ô&°ð 	0ñ 	
ô 	
ð 	
ð 	
ð 	
øõ ð Oð Oð OÝ�ŠÐJÈAÑNÔNÐNÐNÐNÐNÐNÐNÐNøøøøðOøøøs   ‚AA  Á 
BÁ*BÂBr@  c               ó¨  — |pt          ¦   «         }|d         }|€5t          ¦   «         }|st          d¦  «        ‚t          ||d         ¦  «        }|                     ¦   «         }t          |¦  «         d|                     d¦  «        pg vrt          d¦  «        ‚t          |                     d¦  «        pt          ¦  «        }t          | ¦  «        }|€t          d	| › d
�¦  «        ‚t          ¦   «         |z  }	|	dz                       ¦   «         st          d	| › d�¦  «        ‚t          ¦   «         }
t          |	|
|¬¦  «        }d}	 |dz  }t          ||¦  «        }|r%t!          ||d¬¦  «        }t#          ||d¬¦  «        }ni }||t%          |¦  «        <   t'          |||
¦  «        }t)          ||r|gng |d         t+          ¦   «         |pd| › �|
¬¦  «        }|                     |
j        d¬¦  «         	 |                     t3          |¦  «        ||¦  «        }nZ# t4          $ rM}|t6          k    rt          d|› d�d¬¦  «        |‚t8                               d|j        |¦  «         Y d}~�Œ'd}~ww xY w|                     d¦  «        r/dd|                     d¦  «        |                     d¦  «        ||dœS dd|                     d|¦  «        ||dœS ) uÅ  Propose a local skill's current content to the org canonical set.

    Snapshots the LOCAL (personal) skill directory as an org-scoped commit
    layered on the current org HEAD tree (splice/replace that one skill
    subtree), uploads the objects with ``?scope=org``, then CAS-es the org
    HEAD (contract Â§11.5):

    - ADMIN/OWNER token â†’ the server merges directly â†’ ``{ok, merged: True}``.
    - MEMBER token â†’ the server converts to a proposal (202) â†’
      ``{ok, proposal_pending: True, proposal_id, ref}``. NEVER presented as
      live/merged.

    Non-interactive by design â€” an automated submitter (curator hook) drives
    this exact function later (Ben's automation trajectory).
    rL  Nri  rb   r~  rj  rk  rï   zskill 'z ' not found under the skills dirrÈ   z' has no SKILL.mdrö   r   Tr   rc  rh   zpropose rú  zEthe organisation's skills changed while this was being proposed, and uA    attempts to catch up all lost the race â€” run the command againr‹  rB  zCpropose_skill: org HEAD moved (actual=%r), re-splicing (attempt %d)rŠ  Úproposal_idr  )rö  rŠ  r“  r  r   rL  rø   )rö  r  r•  r   rL  )rP  rƒ   rP   rP  r^  rî  r7   rÞ   rý  rº  rA  r«   rÊ   rÑ   r  rd  rØ  rã  r9   r  r  r<  rƒ  rÕ   r�  rb  rL  Ú_ORG_CAS_MAX_ATTEMPTSr\   r]   rM  )r¬   r«  rï  r  rL  rg   rè  rÿ  r»   rº   rÕ   Ú
skill_treeÚattemptsr   r  Ú	skill_maprÉ  rt  râ  r  s                       r!   Úpropose_skillr˜  ß  sm  € ð, Ð1Õ/Ñ1Ô1€HØ�hÔ€FØ€~Ý(Ñ*Ô*ˆØð 	@Ý Ð!>Ñ?Ô?Ð?Ý˜H h¨yÔ&9Ñ:Ô:ˆà×ÒÑ Ô €DÝ�4ÑÔÐØ�T—X’X˜jÑ)Ô)Ð/¨RÐ0Ð0ÝÐMÑNÔNÐNÝ�D—H’HÐ/Ñ0Ô0ÐLÕ4LÑMÔM€Iõ ˜*Ñ
%Ô
%€CØ
€{ÝÐN *ÐNÐNÐNÑOÔOÐOÝ‘” Ñ#€IØ˜
Ñ"×*Ò*Ñ,Ô,ð AÝÐ? *Ð?Ð?Ð?Ñ@Ô@Ð@õ ‰kŒk€GÝ˜I wÀÐKÑKÔK€Jð €Hð%Ø�A‰ˆÝ" 6¨6Ñ2Ô2ˆ	Øð 	Ý,¨V°YÈ$ÐOÑOÔOˆIÝ,¨V°YÈ$ÐOÑOÔOˆIˆIàˆIØ(ˆ	•#�c‘(”(Ñå3°F¸IÀwÑOÔOˆ	Ý"ØØ$Ð,ˆYˆKˆK¨"Ø˜7Ô#Ý#Ñ%Ô%ØÐ6Ð6¨*Ð6Ð6Øð
ñ 
ô 
ˆð 	×Ò˜7œ?°dÐÑ;Ô;Ð;ð	Ø—^’^¥L°Ñ$8Ô$8¸)À[ÑQÔQˆFØøÝð 	ð 	ð 	ØÕ0Ò0Ð0Ýð9Ø%-ð9ð 9ð 9ð ð	ñ ô ð
  ð õ �LŠLØUØ”Øñô ð ð
 ˆHˆHˆH‰Høøøøð	øøøð ‡z‚zÐ$Ñ%Ô%ð 
àØ $Ø!Ÿ:š: mÑ4Ô4Ø—:’:˜eÑ$Ô$Ø!Øð
ð 
ð 	
ð ØØ—
’
˜6 ;Ñ/Ô/ØØðð ð s   Ç5$H È
I1È$AI,É,I1c                 ó  — 	 t          ¦   «         } n™# t          $ r^ 	 t          ¦   «         }|                     d¦  «        pi }|                     d¦  «        st	          ¦   «          n# t
          $ r Y nw xY wY dS t
          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w	 t          ¦   «         sdS t          ¦   «         sdS t          | ¬¦  «        S # t
          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w)u   Best-effort org pull if all gates pass. Never raises; None when inert.

    Gates (all must hold): logged in, org_role claim present (multi-member
    org), feature enabled, base URL configured. Personal orgs are inert here
    by construction â€” resolve_org_identity raises SyncInertError without the
    claim.

    Marker hygiene: when the token VERIFIABLY lacks the org claim (logged in,
    personal org / left the org), the active-org marker is cleared so
    previously-mirrored org skills stop resolving. When we simply cannot
    resolve identity (offline, logged out), the marker is left alone â€”
    offline grace keeps already-pulled org skills working.
    rj   rM  Nz:skills_sync_client: maybe_pull_org_skills inert/failed: %sr.  )rP  rP   rn   r7   Ú_clear_active_org_markerr4   r\   r]   rž   rƒ   r}  )rï  Úbase_identityrj   r^   s       r!   Úmaybe_pull_org_skillsrœ  R  sd  € ðÝ'Ñ)Ô)ˆˆøÝð 	ð 	ð 	ð	Ý,Ñ.Ô.ˆMØ"×&Ò& xÑ0Ô0Ð6°BˆFØ—:’:˜jÑ)Ô)ð +Ý(Ñ*Ô*Ð*øøÝð 	ð 	ð 	ØˆDð	øøøàˆtˆtÝð ð ð Ý�ŠØHÈ!ñ	
ô 	
ð 	
ð ˆtˆtˆtˆtˆtøøøøð	øøøð

Ý#Ñ%Ô%ð 	Ø�4Ý$Ñ&Ô&ð 	Ø�4Ý¨Ð1Ñ1Ô1Ð1øÝð ð ð Ý�ŠØHÈ!ñ	
ô 	
ð 	
ð ˆtˆtˆtˆtˆtøøøøð	øøøsc   ‚ ‘
B'œAA%Á$B'Á%
A2Á/B'Á1A2Á2B'Á8	B'ÂB"Â"B'Â+C Â;C ÃC Ã
DÃ%DÄDc                 ó$  — 	 ddl m}  t          ¦   «         | z  }|                     ¦   «         r0|                     ¦   «          t
                               d¦  «         dS dS # t          $ r&}t
                               d|¦  «         Y d}~dS d}~ww xY w)z9Remove the active-org marker (org skills stop resolving).r   rŽ  zgskills_sync_client: cleared active-org marker (token has no org workflow); org skills no longer resolvez+skills_sync_client: marker clear failed: %sN)	r³   r�  rY  rÊ   r—  r\   Úinfor4   r]   )r�  Úmarkerr^   s      r!   rš  rš  ~  sÂ   € ðGØ7Ð7Ð7Ð7Ð7Ð7å‘”Ð/Ñ/ˆØ�=Š=‰?Œ?ð 	Ø�MŠM‰OŒOˆOÝ�KŠKðLñô ð ð ð ð	ð 	øõ ð Gð Gð GÝ�ŠÐBÀAÑFÔFÐFÐFÐFÐFÐFÐFÐFøøøøðGøøøs   ‚AA Á
BÁ)B
Â
B)r   r   r   r   )r-   r   r   r.   )r-   r   r   r9   )rD   rE   r   r   )rV   r9   r   rE   rŽ  )r   r   )r   rr   )r:   r   r   rŒ   )r“   r9   r”   r9   r•   r   r   r   )r   r   )r¬   r9   r   r   )r   r½   )rã   r   r   r9   )rî   r   rÕ   rÑ   rï   rÞ   r   r9   )r  r9   r  r½   rh   r9   r  r9   r  r9   rÕ   rÑ   r  rr   r   r9   )r   r9   )r   r9   r   r9   )r-   rE   r   rÒ   )
r«  rP  r  r9   r¬  r   rd  r   r   rÒ   )r¬   r9   r   r¶  )r»  r½   rï   rÞ   r   r¼  )rÆ  rE   rÕ   rÑ   rÃ  rr   r   r9   )rh   r9   r   r9   )rh   r9   rÒ  rÞ   r   r9   )r«  rÖ  rt  r9   rd  r   r   r9   )r«  rÖ  rÙ  r9   rd  r   r   rÚ  )r«  rÖ  rÙ  r9   r   r.   )rè  rE   r   rÒ   rÔ   )
r«  rð  r»  rñ  rï  rò  r  r9   r   rE   )r«  rÖ  rï  rE   r  r9   r  r9   r  r9   rÕ   r  r»  r½   r  r9   r   rr   r   rE   )r‚   rr   r
  rr   r  rr   r   r9   )r«  rÖ  r&  rÚ  rÕ   r  r   r9   )r«  rÖ  rh   r9   r   rÞ   )r«  rð  rï  rò  r   rE   )r  r9   r   rò  )r   rò  )r«  rÖ  rL  r9   r   rr   )rL  r9   r   r9   )rL  r9   r   r   )rL  r9   r   rE   )rL  r9   rz  rE   r   rÒ   )rˆ  r9   rL  r9   r   r   )rL  rr   r   r½   )rL  r9   r   rÒ   )rL  r9   r-   rE   r   rÒ   )
r¬   r9   r«  rð  rï  rò  r  rr   r   rE   râ   )rrT   Ú
__future__r   r@   r1   Úloggingry   Útimerå   rç   r   r   Úpathlibr   r   Útypingr   r	   r
   r   r   r   Ú	getLoggerrQ   r\   rë  rý  r  r  r  rì   rë   r  r  rÍ  r&   r'   r,   r>   rC   r*   rl   ÚRuntimeErrorrP   r_   rn   rq   r~   rƒ   r�   r�   r’   r™   rž   r¢   r¦   r«   r¼   rÆ   rÀ   rÑ   rí   r  r  r.  r<  r?  rA  rL  rP  r‘  r“  rš  r–  r¯  rº  rÊ  rÄ  rÑ  rÕ  rØ  rã  rç  rî  r  rþ  r  r  r  r<  r5  rC  rE  rT  rQ  r·   rP  r]  r”  rd  rb  rY  r}  ru  r…  rq  rw  rr  rR  rp  rv  r˜  rœ  rš  rU   r#   r!   ú<module>r§     s�  ðð4ð 4ðl #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ø =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =à	ˆÔ	˜8Ñ	$Ô	$€ð €Ø#Ð ð €	Ø€	Ø€ð €	Ø€	Ø€àÐ ð2 +Ð Ø$Ð ØÐ ð*ð *ð *ð *ð$!ð !ð !ð !ðZ8ð 8ð 8ð 8ð
ð ð ð ð< (Ð ðð ð ð ð �\ñ ô ð ðð ð ð ð(#ð #ð #ð #ðLð ð ð ð* CÐ ð)ð )ð )ð )ð^ 	#Ð"Ð"€Ø	(Ð	(Ð	(€ðð ð ð ðð ð ð ð$	Nð 	Nð 	Nð 	Nðð ð ð ð&\ð \ð \ð \ð0(ð (ð (ð (ðð ð ð ð<$ð $ð $ð $ðNð ð ð ðJ!ð !ð !ð !ð !ñ !ô !ð !ð$ð ð ð ð&Bð &Bð &Bð &Bðb ðFð Fð Fð Fð Fð Fð8>ð >ð >ð >ð&#ð #ð #ð #ðLð ð ð ð2ð ð ð ð �ñ ô ð ð
ð 
ð 
ð 
ð 
�<ñ 
ô 
ð 
ð*a-ð a-ð a-ð a-ð a-ñ a-ô a-ð a-ðf)ð )ð )ð )ð,ð ,ð ,ð ,ð$(ð $(ð $(ð $(ðNKð Kð Kð Kð: JOðð ð ð ð ð ðP)ð )ð )ð )ð" 8Pð6.ð 6.ð 6.ð 6.ð 6.ð 6.ðt QUðBð Bð Bð Bð Bð BðL%ð %ð %ð %ð-ð -ð -ð -ð
 BGðLð Lð Lð Lð Lð Lð EJðð ð ð ð ð ð:ð ð ð ð6
ð 
ð 
ð 
ð  &*ðK
ð (,Ø)-Ø&ðK
ð K
ð K
ð K
ð K
ð K
ðD\>ð \>ð \>ð \>ð~ð ð ð ð(+ð +ð +ð +ð&*ð *ð *ð *ð( &*ðRð *.ðRð Rð Rð Rð Rð Rðjð ð ð ð& )<ð ð ð ð ð ð ð&ð ð ð ð$0ð 0ð 0ð 0ðfð ð ð ðP €ðð ð ð ð.ð ð ð ð Ð ðð ð ð ð%ð %ð %ð %ð(ð (ð (ð (ð &*ðkð *.ðkð kð kð kð kð kð\ð ð ð ð(3ð 3ð 3ð 3ðð ð ð ðIð Ið Ið Ið4ð 4ð 4ð 4ðð ð ð ð ð"	Rð 	Rð 	Rð 	RðOð Oð Oð Oð  &*ðpð *.Ø!ðpð pð pð pð pð pðf)ð )ð )ð )ðXGð Gð Gð Gð Gð Gr#   