
    Pmj%                   0    d Z ddlmZ  G d d          ZdS )u  Billing and subscription handlers for the interactive CLI (god-file decomposition).

This module hosts the Nous billing/subscription methods lifted out of
``cli.py``'s ``HermesCLI`` class. ``HermesCLI`` inherits
``CLIBillingMixin`` so every ``self.<handler>`` call resolves unchanged
via the MRO — behavior-neutral apart from focused billing fixes.

Import discipline mirrors ``hermes_cli.cli_commands_mixin``:
  * Neutral, non-cyclic dependencies are imported at module top level below.
  * cli.py-internal symbols (the ``_cprint``/``_b``/``_d`` helpers and
    display constants) are imported LAZILY inside each method via
    ``from cli import ...``. The mixin never imports ``cli`` at module load
    time, avoiding the cycle created when ``cli.py`` imports this mixin.
    )annotationsc                     e Zd ZdZd3dZd4dZd Zd Zd5dZd Z	dddZ
d Zd ZdddZd Zd6dddZdddZd Zd Zd7d8dZdd d9d"Zd# Zd:d%Zd& Zd3d'Zd( Zd) Zd* Zd+ Zd, Zd- Zddd.d/Zd0 Z d1 Z!d2 Z"dS );CLIBillingMixinz@Mixin holding interactive-CLI billing and subscription handlers.returnboolc                .   ddl m}m}m} 	 ddlm}m}  |            }n# t          $ r d}d}Y nw xY w|&|j        r|d}|j	        p|j
        dk    rdnd}t          |dd          p ||j                  }	|	rd	|	 nd
}
|r,t                        |d |d| |
                       d}|                     ||j	                  D ]} ||           d}|j        r|j         |d|j        d           |j
        dk    r |d |d                      d}n5|j
        dk    r*|j        d|j        dnd}d| d} |d|            d}|rdS ddlm}  |            }|sdS t                       |D ]}t          d|            dS )u  Print the Nous dollar balance block (two-bar view) when a Nous account
        is logged in. Returns True if it printed anything.

        Prefers the shared dollar usage model (``agent.billing_usage`` — two-bar
        plan/top-up view, dollars-only, the /usage + /subscription source of
        truth). Falls back to the legacy ``nous_credits_lines`` text only when the
        model is unavailable. Agent-independent (a portal fetch gated on "a Nous
        account is logged in"), so /usage shows the block even in the TUI
        slash-worker subprocess that resumes WITHOUT a live agent. Fail-open and
        wall-clock-bounded; honors HERMES_DEV_CREDITS_FIXTURE for offline testing.
        r   _cprint_b_d)build_usage_modelformat_renewsNFfreeFreerenews_display    · renews    Plan: T  Total spendable: $,.2fuC   > Free · free models only. Run /subscription to reach paid models.low$under $5   ! Low balance · z# left. Run /topup or /subscription.)nous_credits_lines)clir
   r   r   agent.billing_usager   r   	Exception	available	plan_namestatusgetattr	renews_atprint_usage_bar_lines	has_topuptotal_spendable_usdagent.account_usager   )selfr
   r   r   r   r   usageprinted_anyplanr   renews_bar_ln_amt_lowr   lineslines                    B/home/thesage/.hermes/hermes-agent/hermes_cli/cli_billing_mixin.py_print_nous_credits_blockz)CLIBillingMixin._print_nous_credits_block   s    	('''''''''	!LLLLLLLL%%''EE 	! 	! 	!E MMM	! ]5NK?R1G1GvvTD$U,<dCCe}}UZUdGeGeN7EM3>3332F #:RR 7 7v 7 788::;;;"  00HH # #   " Q5#<#HOu/HOOOPPP|v%%hRR effhhiii"&&?D?X?d;54;;;;jtT4TTTT$$$" t 	;:::::""$$ 	5 	 	D+t++ts    00Nonec                D    ddl m}m}  |d |d                      dS )aE  Print the `/usage` call-to-action pointing at /subscription + /topup.

        Mirrors the TUI's ``USAGE_CTA`` (``session.ts``) so every surface ends a
        usage read with the same nudge. Only called when a Nous account is logged
        in (the balance block printed), since both commands are Nous-account only.
        r   r
   r   r   uA   Run /subscription to change plan · /topup to add to your balanceN)r   r
   r   )r*   r
   r   s      r4   _print_usage_ctaz CLIBillingMixin._print_usage_ctaX   sF     	$#######^RR[\\^^_____    c                   ddl m}m}m} ddlm}m}  |            }|j        s]t                       |j	        r  |d |d|j	                               n& |d |d                      t          d           dS |j
        d	k    rt                        |d
 |d                      t          dd            |j        rE|j        pd                                }d|j         |rd| nd } |d ||                      |j        pd}	t          d|	 d           t          d            |d |d                      dS |                     | ||                     dS )u  `/subscription` (alias `/upgrade`) — view the Nous plan + browser hand-off.

        The CLI mirror of the TUI ``SubscriptionOverlay``: a read of the current
        plan, this cycle's subscription credits, renewal date, and the plans you
        could switch to — then a deep-link to NAS's own ``/manage-subscription``
        page (NOT the Stripe portal; that page routes upgrade→Checkout /
        downgrade→scheduled internally). The terminal NEVER charges for a
        subscription. Fail-open: logged-out / portal hiccup degrades to a clear
        message, never a crash. Mirrors ``_show_billing``
        discipline for the interactive-vs-text split.
        r   r	   )build_subscription_statesubscription_manage_url     💳 zCould not load subscription: Not logged into Nous Portal.z4  Run `hermes portal` to log in, then /subscription.Nteam     ⚕ zTeam subscriptionr   {   ─────────────────────────────────────────r   Org:     · z
a team orgz   This terminal is connected to z. Teams run on a sharedu%     balance · use /topup to add funds.z5Personal subscriptions live on your personal account.)r   r
   r   r   agent.subscription_viewr<   r=   	logged_inr%   errorcontextorg_nameroletitle_subscription_overview)
r*   r
   r   r   r<   r=   staterJ   	_org_lineorgs
             r4   _show_subscriptionz"CLIBillingMixin._show_subscriptiong   s    	(''''''''']]]]]]]]((** 	GGG{ NU""%RU[%R%R"S"SUUVVVVF""%C"D"DFFGGGLMMMF =F""GGGG6RR 34466777#z##$$$~ .
(b//11SENST4QM4MMMrSS	,RR	]],,---.0LCQSQQQRRR9:::GVSTTVVWWWF##E+B+B5+I+IJJJJJr:   c                   ddl m}m}m} ddlm} 	 ddlm}  |            }n# t          $ r d}Y nw xY w|j        }	|	o|	j	         }
|j
        }|	r|	j        p|	j	        n
|r|j        nd}|rt          |dd          nd}| }|rt          |dd          nd}|s|	r|	j        r ||	j                  }d}|	r
|	j        rd	}n|	r|	j        r
d
|	j         }|sd}n^|$|dk    r|j        d|j        d}d| | d| d}n8|rt          |dd          nd}|d|ddnd}|rdn|rd| nd}d| | | | }d}|	r&|	j        r ||	j                  pd}|	j        pdd|f}n,|	r*|	j        r# ||	j                  pd}|	j        pd|	j        |f} |d           |rc|\  }}} |d |d                       |d| d| d |d|z                          |d |d| d                        |d            |d! ||                      t)          dd"            |                     ||          D ]}t)          |           |r:t          |d#d$          r)t          |dd          t)          d%|j        d           |
r |d |d&                      n0|dk    r*||j        d|j        dnd'}d(| d)} |d|            |j        rE|j        pd                                }d*|j         |rd| nd } |d ||                      t)          dd"            |s;t)                        |d |d+                      |rt)          d,|            dS t          | d-d          s3t)                       |r!t)          d.|            t)          d/           dS |
r|                     ||           dS |                     ||           dS )0u  Print the plan read block, then the browser hand-off to manage it.

        Dollars-only (no "credits") — mirrors the TUI overlay: a status line, the
        shared two-bar dollar usage view (plan + top-up) with the plan name on
        the bar, and state-matched free/low nudges. No in-terminal tier picker —
        the only action is managing the subscription on the portal.
        r   r	   r   r   Nr"   r   r   u    → cancelsu    → u   Plan: Free · free models onlyr   r   r   r   rD   z leftr(   u    · $u    · view onlyr   the end of the billing period	your plancancelszthe end of the cycleu     ⏳ zScheduled changer   u    ──▶ u   · z	You keep z (and its credits) until then.rA   rB   r'   Fr   z;> Paid models need a subscription. Start one to reach them.r   r   z1 left. Top up or upgrade before a mid-run cutoff.rC   z%Plan changes need an org admin/owner.  Manage on portal: _appz  Manage your subscription: z5  Open it in your browser, then re-run /subscription.)r   r
   r   r   r   r   r   r   currenttier_idcan_change_plan	tier_namer!   r#   cycle_ends_atcancel_at_period_endpending_downgrade_tier_namer(   cancellation_effective_atpending_downgrade_atr%   r&   rI   rJ   rK   _subscription_free_catalog_subscription_change_menu)r*   rM   
manage_urlr
   r   r   r   r   r+   cis_free
can_changer!   u_status	view_onlyr   _flipr"   _tot_spend_left_tail_trans_when_from_tor/   r0   r1   rJ   rN   s                                  r4   rL   z&CLIBillingMixin._subscription_overview   s-    	(''''''''' 	655555	======%%''EE 	 	 	EEE	 MQY'*
23]Q[-AIU:\%//X\	5:D75(D111"N	CHR(8$???d 	<! 	< 	<*]1?;;N  	<' 	<"EE 	<10 	<;A9;;E 		?5FF8u#4#49R9^7u0777D?i???D???FFDISWU$94@@@tF282D.F....."E'0pOOYg7o7U^7U7U7UmoE>i>>>u>>F
  	Z' 	Z!M!"=>>aBaE{1kIuEFF 	Z10 	Z!M!"899S=SE{1kA4QSXYF 	 &E3G5RR 23355666GEEE3EE""UU]2C2CEEFFFGPMMMMNNPPQQQGBKKK%F%%&&&:    ,,UI>> 	 	G'NNNN 	KWUK77 	KGEK`bf<g<g<sI)BIIIJJJ  	!G\YZZ\\]]]];@;LQVQjQv7u07777  }GD^t^^^DGKKK   > 	*J$"++--DOO0Mt2OOIG(I(())):   
  	GGGGFCDDFFGGG ;9Z99:::FtVT** 	GGG OAZAABBBMNNNF 	 ++E:>>>F 	&&uj99999   # 22urlstrc                    |sdS 	 ddl m}m}  |            s
 |            sdS n# t          $ r Y nw xY w	 ddl}t          |                    |                    S # t          $ r Y dS w xY w)a\  Open ``url`` in a REAL graphical browser; return whether one opened.

        The one opener behind every "open the portal" path in this mixin. Applies
        the same console-browser / remote-session guard the device-code auth flows
        use (``hermes_cli.auth``): ``webbrowser.open()`` returns ``True`` even when
        it launched a text-mode browser (w3m/lynx over SSH) that hijacks the TTY,
        so we refuse those and let the caller print the URL instead. Returns
        ``False`` on any guard refusal or open failure, ``True`` only when a real
        graphical browser launched.
        Fr   )_can_open_graphical_browser_is_remote_sessionN)hermes_cli.authrw   rx   r   
webbrowserr   open)r*   rt   rw   rx   rz   s        r4   _open_url_in_browserz$CLIBillingMixin._open_url_in_browser  s      	5	WWWWWWWW!!## +F+F+H+H u 	 	 	D		
,,--- 	 	 	55	s   % 
22%A 
A*)A*c           	     x   ddl m}m}m} ddlmm}m}  ||          }|s|                     ||d           dS t                        |d |d                      t          d	d
            t          |d          D ]#\  }	}
t          d	|	 d |
                      $ |d	 |d                      fd|D             }|                    d           |                     dd|          }|pd                                }|                                rHdt          |          cxk    rt!          |          k    r!n n|t          |          dz
           j        n|                     ||          rdk    rt          d           dS  ||          p|}|s |d	 |d                      dS t'          fd|D             d          }|r|j        nd}|                     |          rt          d| d           nt          d| d|            t          d           dS )u  Free + admin/owner + interactive: print the plan catalog, pick one, then
        open the portal manage-subscription deep-link with ``plan=<tier_id>``.

        The catalog mirrors the TUI Free rows (name · $/mo · $credits/mo, from the
        same ``tiers[]`` data via the shared ``selectable_tiers`` / ``format_tier_row``
        helpers). Monthly credits are DOLLARS — rendered ``$X credits/mo`` (hidden
        when absent/zero). Starting a NEW subscription needs a fresh card, so the
        only action is the portal hand-off (the terminal never charges here); the
        picked tier rides along as ``plan=`` so the portal preselects it.
        r   r	   )format_tier_rowselectable_tiersr=   zStart a subscriptionverbNrA   zChoose a planr   rB      z. z:Starting a subscription opens the portal to add your card.c                H    g | ]}|j          |          d |j         dfS )zstart z on the portal)rZ   name).0tr~   s     r4   
<listcomp>z>CLIBillingMixin._subscription_free_catalog.<locals>.<listcomp>D  s;    cccXYAIq113RAF3R3R3RScccr:   cancelCancel
do nothingz%Pick a plan to open it on the portal.rK   detailchoicesr   r   u"     🟡 Cancelled. No plan started.rZ   6   No manage URL available — is your portal configured?c              3  2   K   | ]}|j         k    |V  d S Nr   )r   r   choices     r4   	<genexpr>z=CLIBillingMixin._subscription_free_catalog.<locals>.<genexpr>[  s/      ??Q19+>+>q+>+>+>+>??r:   rU   z  Opening the portal to start u   …z  Open this URL to start : 4  Finish in your browser, then re-run /subscription.)r   r
   r   r   rE   r~   r   r=   _subscription_open_portalr%   	enumerateappend_prompt_text_input_modalstripisdigitintlenrZ   _normalize_slash_confirm_choicenextr   r|   )r*   rM   rd   r
   r   r   r   r=   tiersir   r   raw_digittier_urlpickedlabelr   r~   s                    @@r4   rb   z*CLIBillingMixin._subscription_free_catalog$  s'    	('''''''''	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 ! '' 	**5*CY*ZZZF.O,,..///:   eQ'' 	2 	2DAq0q00OOA..001111WRRTUUWWXXXcccc]bccc9:::++(: , 
 
 )""$$>> 	HS[[ > > > >CJJ > > > > >3v;;?+3FF99#wGGF 	8++6777F +*5&AAAOZ 	GWTUUWWXXXF????%???FF%6;$$X.. 	C=5===>>>> AeAAxAABBBDEEEEEr:   Manage your subscriptionr   c               L   ddl m}m} |s't                        |d |d                      dS t                       d|dfdd	g}|                     |d
|          }|                     ||          }|dk    rF|                     |          st          d|            t                       t          d           dS |dk    rM	 |                     |           t          d|            dS # t          $ r t          d|            Y dS w xY wt          d           dS )u@   Open / copy the manage-subscription URL — the portal hand-off.r   r8   r   r   Nr{   z*open the subscription page in your browser)copyz	Copy linkz2copy the manage-subscription URL to your clipboardr   r   r     Open this URL: r   r   u     📋 Copied: z  Manage URL:      🟡 Cancelled.)	r   r
   r   r%   r   r   r|   _write_osc52_clipboardr   )	r*   rM   rd   r   r
   r   r   r   r   s	            r4   r   z)CLIBillingMixin._subscription_open_portale  s   ######## 	GGGGWTUUWWXXXFTGHW.

 ++$r7+SS55c7CCV,,Z88 86*66777GGGHIIIIIv5++J7774
4455555 5 5 53z334444445 %&&&&&s   	'C2 2DDc                F   |j         }t          |o|j        p|j                  }|r|j        ndpd}|rdd| ddfdg}nddg}|                    d	           |                    d
           |                     dd|          }|                     ||          }|dk    r|                     |           dS |dk    r| 	                    |d           dS |dk    r| 
                    |           dS |dk    r|                     ||           dS t          d           dS )zAThe in-terminal change menu for a paid admin/owner (interactive).NrU   keepzKeep z (undo the scheduled change)zcancel the pending change)changeChange planz$upgrade or downgrade in the terminal)
cancel_subCancel subscription#schedule cancellation at period endportalManage on portalz%open the billing page in your browser)closeCloser   r   r   r   r   )resumeNr   r   u     🟡 Closed. No plan change.)rY   r   r^   r_   r\   r   r   r   _subscription_pick_tier_subscription_apply_subscription_confirm_cancelr   r%   )	r*   rM   rd   re   has_pending	keep_namer   r   r   s	            r4   rc   z)CLIBillingMixin._subscription_change_menu  s   M1Z!"8"YA<Y[[$%/Q[[4?K	
  		HHHHJefQGG R\G 	^___7888++2LUWah+ii55c7CCX((/////v$$U,<=====|##--e44444x**5*=====233333r:   c                    ddl m}m}m} |j        } ||          }|st          d           dS g }|D ]J} |||j                  rdnd}	|                    |j         ||           d|	 d|j         f           K|                    d	           | 	                    d
d|r|j
        nd d|          }
|                     |
|          }|r|dk    rt          d           dS |                     ||           dS )uD   Tier picker → preview → confirm (mirrors the TUI picker screen).r   )r~   
is_upgrader   z6  No other plans are available to switch to right now.Nupgrade	downgraderD   z
switch to r   Backr   r   z	Current: r   z$. Pick a plan to preview the effect.r   r   !     🟡 Cancelled. No plan change.)rE   r~   r   r   rY   r%   rZ   r   r   r   r\   r   !_subscription_preview_and_confirm)r*   rM   r~   r   r   re   
selectabler   r   	directionr   r   s               r4   r   z'CLIBillingMixin._subscription_pick_tier  so   YYYYYYYYYYM &%e,,
 	JKKKF 	g 	gA%/Zqy%A%AR		{INNAI//!*<*<'M'M)'M'MOd\]\bOdOdeffff7888++aa;q{{Vaaa , 
 

 55c7CC 	8++5666F..uf=====r:   Tallow_stepupc                   ddl m}m}m} ddlm} ddlm}m}	m	}
  |d |d                      	  |
|          }n[# |	$ r/ |r| 
                    |d|f	           nt          d
           Y dS |$ r!}|                     ||           Y d}~dS d}~ww xY w ||          }|j        }|j        pd}t                       |dk    r |d |d| d                      dS |dvrPddlm}m}  |||          r|nd} |d|j        pd             |||          }|rt          d|            dS |dk    r|j        d|j        dz  dnd} |d |d           d |d                      |r |d| d| d           n |d| d           d }	 dd!lm}  |d"#          }|j        r|j        nd}||j        d$v r
|j         d%}n# t2          $ r Y nw xY w |d ||                      |rd&| d'nd(}d)|f}d*d+|d,fg}nu|j        r't7          |j                  d-k    r|j        dd-         nd.} |d |d           d |d/                       |d0| d1| d2           d3| }d4|f}d+|d5fd6g}|j        r  |d |d7|j         d8                      |                     |d9|:          }|                     ||          d+k    rt          d;           dS |                     |||<           dS )=a
  Preview the change (chargeless quote), show the effect, then confirm+apply.

        ``allow_stepup=False`` (a post-grant replay) declines a second step-up on a
        repeated scope denial so the flow can't re-prompt/re-open the browser in a
        loop.
        r   r	   )(subscription_change_preview_from_payload)BillingErrorBillingScopeRequiredpost_subscription_previewr   u   Checking the change…subscription_type_idpreview)retry     Remote Spending still isn't active for this terminal — the authorization didn't take. Retry, or make this change on the portal.Nzthe selected planno_opzYou are already on u    — nothing to change.)
charge_now	scheduled)r   r=        🟡 uA   This change cannot be confirmed here — manage it on the portal.r   rW   r   r   d   z.2fzConfirm plan changeu   · charged nowz  Upgrade to z. You will be charged z now (prorated).z.. You will be charged the prorated amount now.z.The card on your subscription will be charged.build_billing_stateg      @)timeout)subPincustomerDefaultu7    — the card on your subscription — will be charged.Pay z & upgrade nowzUpgrade now (prorated charge)r   r   Go backdo not chargeyeszcharge + upgrade now
   rT      · scheduled · not todayz  Change to u    — takes effect z7. No charge now; you keep your current plan until then.zSchedule change to schedulezapply this change)r   r   zdo not changezMonthly credits change: .r   r   r   r   ) r   r
   r   r   rE   r   hermes_cli.nous_billingr   r   r   #_subscription_handle_scope_requiredr%   _subscription_render_erroreffecttarget_tier_namer   r=   reasonamount_due_now_centsagent.billing_viewr   rF   cardresolved_viamaskedr   effective_atr   monthly_credits_deltar   r   r   )r*   rM   rZ   r   r
   r   r   r   r   r   r   payloadexcpr   targetr   r=   _plan_mur0   
_card_liner   _bs_c	pay_labelactionconfirm_choicesrp   r   s                                 r4   r   z1CLIBillingMixin._subscription_preview_and_confirm  s=    	('''''''''TTTTTTiiiiiiiiii3RR01133444
	//WMMMGG# 	 	 	 ]88yRYFZ8[[[[  \  ]  ]  ]FF 	 	 	++E3777FFFFF	 54W==#:':WGTQ&QQQRRTTUUUF444 TSSSSSSS)z%99CGGtEGoahm*mooppp))%???C 42S22333F\!!=>=S=_9q-39999eiDGL122LLbb9I6J6JLLMMM `\\\d\\\]]]]^^^^___
 JJBBBBBB))#666!$8SXXD>bo9V&V&V$&I!f!f!fJ   G)J))***7;`3t3333A`I)F 7	#9:OO
 -.N~s1>?R?RVX?X?XAN3B3''_~EGW122WWbb9T6U6UWWXXXG{6{{U{{{|||6f66I '*F	#676O " 	VGTQq7NQQQRRTTUUU++)BP_+``//_EENN5666F  \ JJJJJs/   A   0B3B7BB"7G 
G'&G'c                   ddl m}m}m} ddlm} |j        }|r|j        r ||j                  ndpd}t                        |d |d           d |d                       |d	|r|j	        nd
 d| d            |d |d                      ddg}| 
                    dd|          }	|                     |	|          dk    rt          d           dS |                     |d           dS )z4Confirm, then schedule a cancellation at period end.r   r	   rR   NrT   r   zConfirm cancellationr   z	  Cancel rU   u    — it stays active until z, then won't renew.zOYou keep your remaining credits for this period. You can resume before it ends.)r   r   r   )r   r   zkeep your planzCancel subscription?r   r   r   u)     🟡 Cancelled. Your plan is unchanged.)r   N)r   r
   r   r   r   r   rY   r]   r%   r\   r   r   r   )
r*   rM   r
   r   r   r   re   _endr  r   s
             r4   r   z,CLIBillingMixin._subscription_confirm_cancel  sm   ''''''''''555555M34SSao...twXwTRR.//TT226Q3R3RTTUUUuA>Q[[;uu\`uuuvvvlRRijjllmmmQ3
 ++2HQS]l+mm//_EENN=>>>F  (899999r:   Nc               D   ddl m}m}m}m} ddlm}	m}
m}m	}m
}m}m}m} |\  }}d}|dk    rddlm} |p	 |            }	 |dk    r|	  |||          pi }n# |$ r  |
||f$ r!}|                     ||           Y d}~dS d}~w|	$ re}t#          |dd          }t#          |d	d          }|d
v s||dk    r|                     |           n|                     ||           Y d}~dS d}~ww xY w|                    d          }|                    d          pd}|                    d          }|dk    r |d| d| d|            nt|dk    r |d| d| d|            nY|dk    r |d           |r |d|            n7|dk    r |d           |r |d|            n|                     d           dS |dk    r ||            |d| d|            nE|dk    r |d             |d| d!|            n!|d"k    r |              |d| d#|             |d |d$                      dS # |$ r0 |r|                     |||%           Y dS t+          d&           Y dS |	$ r!}|                     ||           Y d}~dS d}~ww xY w)'a  Run the mutation for `action`, handling the scope step-up + the result.

        `action` is one of ("upgrade", tier_id) / ("schedule", tier_id) /
        ("cancel", None) / ("resume", None). insufficient_scope routes to the
        step-up and replays; the upgrade idempotency key is reused across the replay.
        ``allow_stepup=False`` (a post-grant replay) declines a second step-up on a
        repeated scope denial so the flow can't re-prompt/re-open the browser in a loop.
        r   r
   r   _DIM_RST)r   BillingTransientBillingRemoteSpendingRevokedr   BillingSessionRevoked"delete_subscription_pending_changepost_subscription_upgradeput_subscription_pending_changeNr   new_idempotency_key)r   idempotency_keyr"   rG   )network_errorendpoint_unavailablei  targetTierNamezyour new planrecoveryUrlalready_on_tierr   u   ✓ You are already on r   upgradedu   ✓ Upgraded to z,. Your new monthly credits land in a moment.requires_actionuL     🟡 This upgrade needs extra verification (3DS). Finish it on the portal.
  Portal: payment_faileduV     🔴 Your card was declined. Update your payment method on the portal and try again.r   r   ut   ✓ Scheduled — your plan doesn't change today. You keep it until the end of the billing period, then it switches.r   T)r   uu   ✓ Scheduled — your plan stays active until the end of the billing period, then it cancels. Nothing changes today.r   u-   ✓ Undone — you stay on your current plan.z*Re-run /subscription anytime to review it.)r   r  r   )r   r
   r   r  r  r   r   r  r	  r   r
  r  r  r  r   r  r   r#   &_subscription_render_upgrade_ambiguousgetr   r%   )r*   rM   r   r  r   r
   r   r  r  r   r  r	  r   r
  r  r  r  kindargkeyr  resr   _status_coder"   r   _urls                               r4   r   z#CLIBillingMixin._subscription_apply5  s    	0///////////		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 	c9>>>>>>!:%8%8%:%:C=	8y  33^abbbhfhCC+   (*?A]^    33E3???FFFFF#   %c8T::G#C$77E IIIW_`gkn`n`n
 CCCHHHH 77sCCCFFFFF **ww/00COww}--...GKKKdKKTKKLLLLz))Goootooimoopppp000Gjkkk 5 3T 3 3444///Gtuuu 5 3T 3 3444 ??EEEz!!//SIIII  ^T  ^  ^  X\  ^  ^  _  _  _  _!!//t<<<<  _T  _  _  Y]  _  _  `  `  `  `!!22444VTVVPTVVWWWGKHIIKKLLLLL# 	] 	] 	] ]88f^a8bbbbbb  \  ]  ]  ]  ]  ]  ] 	8 	8 	8++E3777777777	8si   I 	A I C/&B<I C/
AC*$I *C//CI B I J(J:J>JJ)r  c               V   ddl m}m}m}m} t                       t          d            |d |d                      t          | dd          st          d           dS d	d
g}|                     dd|          }	|                     |	|          dk    rt          d           dS t          d           	 ddl	m
}
  |
d          }n*# t          $ r}t          d|            Y d}~dS d}~ww xY w|st          d           dS  |d| d|            	 ddlm} |                                 n# t          $ r Y nw xY wddlm} 	  |            }n# t          $ r |}Y nw xY w|\  }}|dk    r|                     ||d           dS |                     |||d           dS )u?  insufficient_scope → allow remote spending (step-up), then replay `retry`.

        Mirrors _billing_handle_scope_required: the classic CLI calls
        step_up_nous_billing_scope directly (it opens the browser + blocks), then
        replays the held preview/mutation so the user never re-runs the command.
        r   r    ! One-time setupr   zTo change your plan from the terminal, allow Remote Spending once. It opens your browser to authorize, then your change picks up right here.rX   NzK  Run `hermes portal` and allow Remote Spending, then re-run /subscription.r   Allow Remote Spendingzopen your browser to authorizenozNot nowr   r&  .Opens your browser to authorize this terminal.r   r   z:  No change made. Allow Remote Spending when you're ready.2     Opening your browser to allow Remote Spending…step_up_nous_billing_scopeTopen_browser"  Couldn't allow Remote Spending: uZ     Couldn't allow Remote Spending — an org admin or owner has to approve it for this org.u   ✓ Remote Spending allowed.)nous_billing)r<   r   Fr   )r  r   )r   r
   r   r  r  r%   r#   r   r   ry   r,  r   
hermes_clir0  invalidate_cached_tokenrE   r<   r   r   )r*   rM   r   r  r
   r   r  r  r  r   r,  grantedr   _nbr<   freshrkindrargs                     r4   r   z3CLIBillingMixin._subscription_handle_scope_required  s    	0///////////"###  jRR  g  h  h  j  j  	k  	k  	ktVT** 	_```FN'
 ++)C# , 
 

 //_EENNNOOOFBCCC	BBBBBB00dCCCGG 	 	 	<s<<===FFFFF	  	noooF=T==t==>>>
	666666'')))) 	 	 	D	 	EDDDDD	,,..EE 	 	 	EEE	tI225$U2SSSSS$$UE?af$gggggs<   :C 
C4C//C4D7 7
EE
E E('E(c                   ddl m} t          |dd          }t          |          pd}|dk    r |d           n!|dv r |d	|            n |d
|            t          |dd          }|r |d|            dS dS )zLRender a subscription BillingError (a lighter _billing_render_charge_error).r   )r
   rG   NzSomething went wrong.insufficient_scopeu?     🟡 Remote Spending isn't allowed yet. Allow it, then retry.)subscription_mutation_rejectedpreview_rejectedr        🔴 
portal_urlr  )r   r
   r#   ru   )r*   rM   r   r
   codemsgr"  s          r4   r   z*CLIBillingMixin._subscription_render_error  s    sGT**#hh11'''GUVVVVKKKGOcOO$$$$GOcOO$$$sL$// 	)G'''(((((	) 	)r:   c                    ddl m}m}  |d            |d |d                      |t          |dd          nd}|r |d|            dS dS )	ue  A charge-route failure (transport / timeout / 500 / unknown status) is
        AMBIGUOUS — NAS may have already prorated + charged. Steer to a re-check,
        never a flat failure that invites a blind retry (mirrors the TUI's
        upgradeResult(null) — the CLI can't persist the key across a command re-run,
        so a re-check is the safe path).r   r8   uS     🟡 Couldn't confirm the upgrade — your card may or may not have been charged.r   z<Re-run /subscription to check your plan before trying again.Nr=  r  )r   r
   r   r#   )r*   r   r
   r   r"  s        r4   r  z6CLIBillingMixin._subscription_render_upgrade_ambiguous  s     	$#######efffYRRVWWYYZZZ36?wsL$/// 	)G'''(((((	) 	)r:   /topupcommandc                ,   ddl m}m} ddlm}  |            }|j        s_t                       |j        r"d|j         } |d ||                      n& |d |d                      t          d           dS |                     |           dS )	u  `/topup` — Remote Spending for Nous (one interactive modal).

        ZERO sub-commands: any argument is ignored. Bare ``/topup`` always
        opens the Overview (Screen 1), whose numbered menu is the *only* way to
        reach the Buy / Auto-reload / Monthly-limit sub-screens. (Per the unified
        UX spec §0.4 — ``/topup buy`` etc. are gone; we don't error on a stray
        arg, we just open the menu.)

        Interactive CLI uses the prompt_toolkit modal; non-interactive contexts
        (TUI slash-worker / no live app) render text + the portal deep-link, never
        prompting (the URL is the affordance), same discipline as ``_show_subscription``.
        All money is Decimal end-to-end; the terminal never collects card details.
        r   r8   r   zCouldn't load billing: r>   r?   z-  Run `hermes portal` to log in, then /topup.N)	r   r
   r   r   r   rF   r%   rG   _billing_overview)r*   rB  r
   r   r   rM   _msgs          r4   _show_billingzCLIBillingMixin._show_billing  s     	$#######::::::##%% 	GGG{ G>>>,""T((,,----F""%C"D"DFFGGGEFFFF 	u%%%%%r:   r   r   r   c               |    t          |dd          }|sdS |rt          d|            t          d|            dS )zCPrint a portal deep-link line (the funnel for portal-only actions).r=  Nr   rW   )r#   r%   )r*   rM   r   rt   s       r4   _billing_portal_hintz$CLIBillingMixin._billing_portal_hint  sY    e\400 	F 	!-v--   *S**+++++r:   c           	     P   ddl m}m}m} ddlm} 	 ddlm}  |            }n# t          $ r d}Y nw xY wt                        |d |d ||j
                                         |j        rE|j        pd                                }d	|j         |rd
| nd }	 |d ||	                      t          dd            |                     |t          |dd                    D ]}
t          |
           |j        }|H|j        r2t          d ||j                   d ||j                              nt          d           |j        rB|j        r;|j        t          d|j        j                    n |d |d                      t          dd            |j        s. |d |d                      |                     |           dS |j        s0 |d |d                      |                     |d           dS t          | dd          s|                     |           dS  |d |d                      |m|j        rf|j        _|j                                        rF|j        ?|j                                        r&d ||j                   d ||j                   d}nd} |d ||                      t          dd            g d}|                     dd|          }|                     ||          }|d k    r|                     |           dS |d!k    r|                     |           dS |d"k    r|                     |           dS |d#k    r|                     |           dS t          d$           dS )%u  Screen 1 — overview: balance in title, two-bar dollar usage, action menu.

        Dollars-only (no "credits") — mirrors the TUI /topup overlay: balance
        leads in the title, the shared plan + top-up bars render below, then the
        reordered menu (Add funds first). No scope preflight — remote spending
        is discovered reactively when a charge 403s insufficient_scope.
        r   r	   format_moneyrS   Nr>   u   Top up · balance r   rC   rD   r   rB   r!   u     Auto-reload: on — below     → reload to z  Auto-reload: offz  Card: uG   No saved card on file — “Add funds” walks you through adding one.+Billing actions require an org admin/owner.$Remote spending is off for this org.zUA billing admin can turn it on from the portal's Hermes Agent page to add funds here.rG  rX   u?   Add funds now — a single charge, added to your balance today.u   Refill when low — charges z- automatically when your balance falls below r   ue   Refill when low — charges your card automatically when your balance falls below the amount you set.))buy	Add fundsz,a single charge, added to your balance today)autoAuto-reloadz/refill automatically when your balance runs low)limitzMonthly limitz&show the monthly spend cap (read-only)r   r   zTop up your balancer   rP  rR  rT  r   z  Cancelled.) r   r
   r   r   r   rL  r   r   r   r%   balance_usdrI   rJ   rK   r&   r#   auto_reloadenabledthreshold_usdreload_to_usdr[   cli_billing_enabledr   displayrI  	is_finiter   r   _billing_buy_flow_billing_auto_reload_flow_billing_limit_screen_billing_open_portal)r*   rM   r
   r   r   rL  r   r+   rJ   rN   r/   ar
_auto_liner   r   r   s                   r4   rD  z!CLIBillingMixin._billing_overview  s9    	('''''''''333333	======%%''EE 	 	 	EEE	 	V""S,,u?P2Q2QSSTTVVWWW> 	*J$"++--DOO0Mt2OOIG(I(())):    ,,UGE;PT4U4UVV 	 	G'NNNN>z ,F<<@P3Q3Q F F%1\"2B%C%CF F   
 *+++   	nU%> 	nz%5!3556666lRR ijjllmmm:    $ 	GLIJJLLMMM%%e,,,F( 	GEBCCEEFFF%%n &    F tVT** 	%%e,,,F 	\RRYZZ\\]]]N
  , **,, - , **,, -S||B<L/M/M S S1=b>N1O1OS S S J2  	%RR
^^%%&&&:   
 
 
 ++' , 
 
 55c7CCU??""5)))))v**511111w&&u-----x%%e,,,,,.!!!!!rs   listc                H   g }|rt          |dd          nd}||j        dk    rt          dt          dt	          |j        dz                                }d|z  dd|z
  z  z   }|j        d|j         dnd	}|pd
                    d          dd         }|                    d| d| d|j	        dd|j        d| 	           |rt          |dd          nd}	|	C|	j	        dk    r8|                    dd                    d           dd d|	j	        dd           |S )u  The plan + top-up dollar bars as ready-to-print lines (filled = remaining).

        Returns [] when there's nothing to draw. The caller resolves ``plan_name``
        (the plan-bar label) and picks its own print fn — block ordering differs
        per surface (``_cprint`` vs ``print`` under patch_stdout). One source of
        truth for the bar format across /usage, /subscription, and /topup.
        plan_barNr   r   u   █u   ░rD   z% usedr   r-      r   [z]  $r   z
 left of $	topup_barztop-upu   ██████████u    · never expires)
r#   	total_usdmaxminroundfill_fractionpct_usedljustr   remaining_usd)
r*   r+   r!   r2   pbfilledbarpct_sr   tbs
             r4   r&   z CLIBillingMixin._usage_bar_lines  sm    16@WUJ---D>blQ..CE"*:R*?$@$@AABBF6>erF{&;<C24+2I.2;....rE(&//222A26ELLjejjcjjr/?jjjPRP\jjchjjkkk27AWUK...T>b.22LLjhnnQ//jj*jj"BRjjjjkkkr:   c                    t          |dd           }|st          d           d S |                     |          st          d|            t          d           d S )Nr=  z  No portal URL available.r   z*  Complete billing changes in the browser.)r#   r%   r|   )r*   rM   rt   s      r4   r`  z$CLIBillingMixin._billing_open_portal  sq    e\400 	.///F((-- 	-+c++,,,:;;;;;r:   c                &   ddl m}m} |j        s<t	                        |d |d                      |                     |           dS |j        s>t	                        |d |d                      |                     |d           dS d	S )
zGGuard charge/auto-reload entry points; print + return False if blocked.r   r8   r>   rN  FrO  zWA billing admin can turn it on from the portal's Hermes Agent page before adding funds.rG  T)r   r
   r   r[   r%   rI  rZ  )r*   rM   r
   r   s       r4   _billing_require_adminz&CLIBillingMixin._billing_require_admin  s    ########$ 	GGGGQbb!NOOQQRRR%%e,,,5( 	GGGGJbb!GHHJJKKK%%p &    5tr:   c           	        ddl m}m}m}m}m} t                        |d |d                       |d            |d |d                      g d}t          d	          D ]}|                     d
d|          }	| 	                    |	|          }
|
dk    r-| 
                    |            |d |d                      c|
dk    rhddlm} 	  |            }n# t          $ r d}Y nw xY w|	|j        r|}|j        " |d| d|j        j         d|            |c S t          d            t          d           dS )u  No saved card → guide adding one on the portal, with a re-check loop.

        Cards are added on the portal (never in-terminal). "I've added it" re-fetches
        billing state so the purchase continues right here once the card is saved —
        this also recovers a transient miss (the card display is best-effort
        server-side). Returns the refreshed state (card present), or None to abandon.
        r   )r
   r   r   r  r  r>   zAdd a card firstz  No saved card on file.r   ua   Add a card once on the portal billing page — after that you can top up right from the terminal.))r   zAdd a card on the portalz&opens the billing page in your browser)rechecku   I've added it — check againz"re-check for the card and continuer   rf  z
Add a cardr   r   r   uC   Add the card on the billing page, then pick “check again” here.rz  r   Nu   ✓ Card found: u    — continuing.uM     Still no card on file — finish adding it on the portal, then check again.  Cancelled. No funds added.)r   r
   r   r   r  r  r%   ranger   r   r`  r   r   r   rF   r   r[  )r*   rM   r
   r   r   r  r  r   _r   r   r   r5  s                r4   _billing_add_card_flowz&CLIBillingMixin._billing_add_card_flow  s    	433333333333332""/0022333*+++~RR{||~~
 
 

 q 	 	A//l2W^/__C99#wGGF!!))%000hRR effhhiii""BBBBBB!//11EE  ! ! ! EEE!$$!E:)Gaaauz7Iaa[_aabbb LLLefff,---ts   
C$$C32C3c                   ddl m}m} ddlmm} |                     |          sdS t          | dd          sd                    fd|j	        D                       }t                        |d |d	                      t          d
|            t          d           |                     |           dS |j         |                     |          }||j        dS g }|j	        D ]0}|                    t          |           |          df           1|                    d           |                    d           |j        }|r
d|j         nd}	|                     d	|	|          }
|                     |
|          }|r|dk    rt          d           dS ddlm} |dk    rj|                     d          }|t          d           dS  ||pd|j        |j                  }|j        st          d|j                    dS |j        }n-	  ||          }n # t6          $ r t          d           Y dS w xY w|                     ||           dS )u@   Screen 2 (preset select) → Screen 3 (confirm + charge + poll).r   )r
   r   rL  validate_charge_amountNrX   z, c              3  .   K   | ]} |          V  d S r    )r   r   rL  s     r4   r   z4CLIBillingMixin._billing_buy_flow.<locals>.<genexpr>  s+      NNAQNNNNNNr:   r>   rQ  z  Presets: z9  Run this in the interactive CLI to complete a purchase.zone-time credit purchase)customu   Custom amount…zenter your own amountr   z	Payment: zNo saved card on filer   r   r{  )Decimalr  z  Amount (USD): r   min_usdmax_usdr<  u     🔴 Invalid selection.)r   r
   r   r   rL  r  rx  r#   joincharge_presetsr%   rI  r   r~  r   ru   r[  r   r   decimalr  _prompt_text_inputr  r  okrG   amountr   _billing_confirm_and_charge)r*   rM   r
   r   r  presetspreset_choicesr   r   r   r   r   r  enteredvr  rL  s                   @r4   r]  z!CLIBillingMixin._billing_buy_flow  s   ########KKKKKKKK**511 	F tVT** 	iiNNNN9MNNNNNGGGGG/bboo//000)))***MNNN%%e,,,F
 ://66E}
 2% 	Y 	YA!!3q66<<??<V"WXXXXUVVV@AAAz/3P+T\+++9P++fn , 
 
 55c>JJ 	8++0111F######X--.@AAG4555&&2u}em  A 4 )))***XFF    1222 	((77777s   H' 'IIc                   ddl m}m}m} ddlm}m} |j        }t                        |d |d                      t          dd            t          d ||                      |r5t          d	|j	                    |j
         |d |d                      t          dd            d}	 |d ||	                      dd ||           ddfddg}
t          | dd
          st          d           d
S |                     d ||           d|r|j	        nd|
          }|                     ||
          }|dk    r|                     |           d
S |dk    rt          d           d
S ddlm}m}m}  |            }	  |||          }nH# |$ r |                     |||           Y d
S |$ r!}|                     ||           Y d
}~d
S d
}~ww xY w|                    d          }|st          d           d
S  |d |d                      |                     |||           d
S ) uF   Screen 3 — confirm total + consent, charge, then poll to settlement.r   r	   )rL  r  r>   zConfirm purchaser   rB   z	  Total: z  Payment: Nz.Your card saved on the portal will be charged.z;By confirming, you allow Nous Research to charge your card.payr   z nowzsubmit the charge)r   r   z)manage your card / billing in the browserr   rX   z3  Run in the interactive CLI to confirm a purchase.?zno saved cardr   r   r{  )r   r   post_charge
amount_usdr  r  r  chargeIdu6     🔴 No charge id returned; please check the portal.u-   Charge submitted — confirming settlement…)r   r
   r   r   r   rL  r  r   r%   r[  
provenancer#   r   r   r`  r   r   r   r  _billing_handle_scope_required_billing_render_charge_errorr  _billing_poll_charge)r*   rM   r  r
   r   r   rL  r  r   _consentr  r   r   r   r   r  r  resultr   	charge_ids                       r4   r  z+CLIBillingMixin._billing_confirm_and_chargeD  sr   ''''''''''HHHHHHHHz2""/0022333:   0,,v..00111 	U.../// &SRR PQQSSTTT:   I 	 	#RR\\##$$$ 5<<//5557JKW2

 tVT** 	GHHHF++0f--000$(=DLLo# , 
 

 55c?KKX%%e,,,FU??0111F	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 "!##		 [FCHHHFF# 	 	 	 //fVY/ZZZFF 	 	 	--eS999FFFFF	 JJz**	 	JKKKFJRRGHHJJKKK!!%F;;;;;s   F   G% G%G  G%c                F   ddl }ddlm} ddlm}m}m} |                                 dz   }	d}
|                                 |	k     r6	  ||          }n^# |$ r6}|j        pd}|                    t          |d                     Y d}~\d}~w|$ r}t          d	|            Y d}~dS d}~ww xY w|                    d
          }|dk    rQ|                    d          }ddlm} |r | ||                    n
 ||          }t          d| d           dS |dk    r+|                     ||                    d                     dS |                    |
           |                                 |	k     6t          d           |                     |           dS )zGPoll loop: 2s interval, 5-min cap, cancellable. settled = ledger truth.r   NrK  )r   r  get_charge_statusi,  g       @      u#     🔴 Could not check the charge: r"   settled	amountUsd)parse_moneyu     ✓ z added to your balance.failedr   us     🟡 Still processing after 5 minutes — this is a timeout, not a failure. Check /billing or the portal shortly.)timer   rL  r   r   r  r  retry_aftersleeprk  r%   r  r  _billing_render_charge_failedrI  )r*   rM   r  r  _timerL  r   r  r  deadlineintervalr"   r   wait	state_stramtr  showns                     r4   r  z$CLIBillingMixin._billing_poll_charge  s<   333333	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 ::<<#%jjllX%%	**955#   +!CbMM***   ACAABBB 

8,,II%%jj--:::::::=W[[%5%5666<<PVCWCW=u===>>>H$$225&**X:N:NOOOKK!!!1 jjllX%%6 	 ? 	@ 	@ 	@!!%(((((s$   A B/,B

B/B**B/c                
   |pd                                 }|dk    rt          d           nA|dk    rt          d           n+|dk    rt          d           nt          d|pd	 d
           |                     |           dS )zCBranch the poll `failed` reasons to the right copy + portal funnel.r   authentication_requiredu`     🔴 Your bank requires verification (3DS). Complete it on the portal to finish this purchase.payment_method_expiredu6     🔴 Your card has expired. Update it on the portal.card_declinedu>     🔴 Your card was declined. Try another card on the portal.u%     🔴 The charge didn't go through (processing_errorz).N)r   r%   rI  )r*   rM   r   s      r4   r  z-CLIBillingMixin._billing_render_charge_failed  s    ,B%%''... 4 5 5 5 5///JKKKK&&RSSSSZ&:VDVZZZ[[[!!%(((((r:   c                   ddl m}m}m} t	          |dd          }t	          |dd          }t	          |dd          pt	          |dd          }t          ||          s|dk    r|dk    rd	nd
}	t          d|	 d           nkt          ||          s|dk    rt          d           nD|dk    rt          d           n-|dv st	          |dd          dk    rt          d           n|dk    rt          d           n|dk    rt          d           n|dk    rLt	          |di           pi                     d          }
|
t          d|
 d           nt          d           nt          ||          rMt	          |dd          }|r$d t          d!t          |d"z                       d#nd$}t          d%| d&           n(|d'k    rt          d(           nt          d|            |rt          d)|            dS dS )*z6Render a typed BillingError at submit time (pre-poll).r   )r  r	  r
  rG   Nactorr=  remote_spending_revokedadminz*An admin stopped this terminal's spending.z%You stopped this terminal's spending.r<  u>    Reconnect to restore — run `hermes portal` to re-authorize.session_revokeduH     🔴 Your session was logged out. Run `hermes portal` to log in again.no_payment_methoduC     💳 No card on file — top up and manage billing on the portal.)cli_billing_disabledremote_spending_disabledr>  r  uq     Remote spending is off for this account — a billing admin can turn it on from the portal's Hermes Agent page.role_requiredzO  Adding funds needs an org admin/owner. Ask an admin, or manage on the portal.idempotency_conflictuU     🔴 That charge key was already used for a different amount. Start a fresh top-up.monthly_cap_exceededr   remainingUsdu&     🔴 Monthly spend cap reached — $z headroom left.u!     🔴 Monthly spend cap reached.r  z (try again in ~r   <   z min)r   u!     🟡 Too many charges right nowz. This isn't a payment failure.r9  uM     🔴 Remote Spending needs approval — run /topup to allow it, then retry.r  )
r   r  r	  r
  r#   
isinstancer%   r  rj  rl  )r*   rM   r   r  r	  r
  r>  r  r=  who	remainingr  minss                r4   r  z,CLIBillingMixin._billing_render_charge_error  s   	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 sGT**Wd++S,55[VZ9[9[
c788  	#DD]<]<] w&& @??  _C___````233 	#t?P7P7P\]]]](((WXXXXIIIVT**.HHH  F  G  G  G  G_$$cdddd+++ijjjj+++ i44:??OOI$YyYYYZZZZ9::::-.. 		#3t44DIMUEc!U4"9-=-=&>&>EEEESUD[d[[[\\\\))) abbbb/C//""" 	-+z++,,,,,	- 	-r:   r  c                  ddl m}m} ddlm} | ||          nd}t                       t          d            |d |d| d	                      t          | d
d          st          d           dS ddg}|                     dd|          }	|                     |	|          }
|
dk    rt          d           dS t          d           	 ddl	m
}  |d          }n*# t          $ r}t          d|            Y d}~dS d}~ww xY w|st          d           dS ddlm}  |            }|j        r|j        s&t          d           |                     |           dS |j        =t          d            |d |d                      |                     |           dS |t          d           dS t          d           dd  ||           d!d"fd#g}|                     d$ ||           d%|          }	|                     |	|          dk    rt          d&           dS dd'lm}m} dd(lm} |p	 |            }	  |||)          }n)# |$ r!}|                     ||           Y d}~dS d}~ww xY w|                    d*          }|st          d+           dS  |d |d,                      |                     |||           dS )-u  403 insufficient_scope → in-flight reauth, then resume the held charge.

        The buy path discovers remote spending isn't allowed only when the
        charge 403s — there is no preflight. We allow it in-flight ("Allow
        Remote Spending" → browser device-flow), then on return ask the user to
        press Enter to resume the held ``amount`` (reusing ``idempotency_key`` so
        the resumed charge collapses with the original). Never leaks the raw
        billing:manage scope.
        r   r8   rK  Nzyour top-upr$  r   zcTo charge from this terminal, allow Remote Spending once. It opens your browser to authorize, then z picks up right here.rX   z<  Run `hermes portal` and allow Remote Spending, then retry.r%  r'  r&  r)  r   r   zD  No charge made. Run /topup when you want to allow Remote Spending.r*  r+  Tr-  r/  uh     Couldn't allow Remote Spending — an org admin or owner has to approve it. Your card was not charged.r   z  Remote Spending is allowed for this terminal, but it's still off for this org. A billing admin can turn it on from the portal's Hermes Agent page, then run /topup again.uB     ✓ Remote Spending allowed — but there's no card on file yet.z4Top up and manage billing on the portal to continue.u6     ✓ Remote Spending allowed. Run /topup to continue.u     ✓ Remote Spending allowed.r   zResume z top-upzfinish the held purchase)r   r   r   zResume your top-upu.    is ready to finish — press Enter to resume.r{  )r   r  r  r  r  z1  No charge id returned; please check the portal.u1   Resuming your top-up — confirming settlement…)r   r
   r   r   rL  r%   r#   r   r   ry   r,  r   r   rF   rZ  rI  r   r   r   r  r  r  r  r  )r*   rM   r  r  r
   r   rL  
amount_strr  r   r   r,  r3  r   r   r5  resume_choicesr   r  r  r  r  r  s                          r4   r  z.CLIBillingMixin._billing_handle_scope_required  sp    	$#######333333-3-?\\&)))]
"###  cRR  `  I  `  `  `  a  a  c  c  	d  	d  	dtVT** 	PQQQFN'
 ++)C# , 
 

 55c?KKU??XYYYFBCCC	BBBBBB00dCCCGG 	 	 	<s<<===FFFFF	  	|}}}F
 	;:::::##%% 	E$= 	  @  A  A  A%%e,,,F
 :VWWWGURSSUUVVV%%e,,,F >JKKKF 	.///>f!5!5>>>@Z[1
 ++&"l6**ZZZ" , 
 

 //^DDPP0111F 	FEEEEEEE::::::6!4!4!6!6	 [FCHHHFF 	 	 	--eS999FFFFF	 JJz**	 	EFFFFNRRKLLNNOOO!!%F;;;;;s0   C$ $
D.DD	I I=I88I=c           	     "	   ddl m}m}m} ddlm}m} |                     |          sdS |j        }|j	        }t          |o|j                  }	t                        |d |d                      t          dd             |d |d	                      |rt          d
|j                    n&t          d           |                     |           dS |	r1t          d ||j                   d ||j                              t#          | dd          s&t          d           |                     |           dS |	rg d}
|                     dd ||j                   d ||j                   |
          }|                     ||
          }|dk    r|                     |           dS |dk    rt          d           dS |	r ||j                  nd}d}||rd| dndz  }|                     |          }|t          d           dS |pd                                s
|	r|j        }nH ||pd|j        |j                  }|j        r|j        t          d|j                    dS |j        }|	r ||j                  nd}d}||rd| dndz  }|                     |          }|t          d           dS |pd                                s
|	r|j        }nH ||pd|j        |j                  }|j        r|j        t          d|j                    dS |j        }||||k    rt          d           dS t                       d|j         d  ||           d!} |d ||                      d"d#g}|                     d$d% ||           d ||           |          }|                     ||          }|d&k    rt          d           dS dd'lm}m}m} 	  |d(tA          |          tA          |          )           nE# |$ r | !                    |           Y dS |$ r!}| "                    ||           Y d}~dS d}~ww xY wt          d* ||           d ||           d+           dS ),u7  Screen 4 — auto-reload config: threshold + reload-to → PATCH.

        Prefills the current values from ``state.auto_reload``. Validates both
        amounts (2dp, within bounds, ``reload_to > threshold``). When auto-reload
        is already on, offers a "Turn off" path (PATCH ``enabled:false``).
        r   r	   r  Nr>   rS  r   rB   z1Automatically add funds when your balance is low.z  Card on file: u1     No saved card — manage billing on the portal.z  Currently: below rM  rX   z6  Run in the interactive CLI to configure auto-reload.))editzEdit thresholdsz change when / how much to reload)offzTurn offzdisable auto-reloadr   u   On — below r   r  r  r   z   When balance falls below (USD)z [z]: r   r   r  r<  z  Reload balance to (USD)u;     🔴 Reload-to amount must be greater than the threshold.z5By confirming, you authorize Nous Research to charge z whenever your balance reaches z*. Turn off any time here or on the portal.)agreezAgree and turn onzenable auto-reloadr   zTurn on auto-reload?zBelow r  r   r   patch_auto_top_upTrW  	thresholdtop_up_amountu     ✅ Auto-reload on: below r   )#r   r
   r   r   r   rL  r  rx  r   rV  r   rW  r%   r   rI  rX  rY  r#   r   r   _billing_auto_reload_disabler  r   r  r  r  r  rG   r   r   r   r  floatr  r  )r*   rM   r
   r   r   rL  r  r   ra  currently_ontop_choicesr   topcur_thr
thr_promptthreshold_rawthreshold_amttvcur_rel
rel_prompt
reload_raw
reload_amtrv_ar_consentr  r   r   r   r  r   s                                 r4   r^  z)CLIBillingMixin._billing_auto_reload_flow^  s    	('''''''''KKKKKKKK**511 	FzB-2:..-""]++--...:   NRRKLLNNOOO 	2T[223333EFFF%%e,,,F 	>ll23C&D&D > >)\"*:;;> >  
 tVT** 	JKKK%%e,,,F  	  K
 //#BLL1A$B$B B B!-b.>!?!?B B $ 0  C 66sKHHCe||11%888f}})*** 5AJ,,r/000d7
7<'7''''<
//
;; %&&&F#**,, 		& 		&,MM''#U]EM  B 5 BI-***+++IM 5AJ,,r/000d0
7<'7''''<
,,Z88
%&&&F b'')) 		#l 		#)JJ'' b%-  B 5 BI-***+++J!6*:U:UOPPPF8DK 8 8-9\--H-H8 8 8 	
 	&RR__&&'''@.
 ++(bLL77bbU_H`H`bb# , 
 

 55c?KKW%&&&F	
 	
 	
 	
 	
 	
 	
 	
 	
 	
		m(<(<ER\L]L]     $ 	 	 	//666FF 	 	 	--eS999FFFFF	 	 7\\--H-H 7 7'<
337 7 7 	8 	8 	8 	8 	8s   9(P" "Q$?Q$QQ$c                l   ddl m}m}m} |j        }|r|j        t          |j                  nd}|r|j        t          |j                  nd}	  |d||           nE# |$ r |                     |           Y dS |$ r!}| 	                    ||           Y d}~dS d}~ww xY wt          d           dS )zTurn off auto-reload (PATCH ``enabled:false``).

        The endpoint requires ``threshold``/``topUpAmount`` in the body even when
        disabling, so we echo back the current values (falling back to 0).
        r   r  Ng        Fr  u     ✅ Auto-reload turned off.)r   r   r   r  rV  rX  r  rY  r  r  r%   )	r*   rM   r   r   r  ra  thrrelr   s	            r4   r  z,CLIBillingMixin._billing_auto_reload_disable  s*   	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 )+U0@0LeB$%%%RU)+U0@0LeB$%%%RU	es#NNNNN# 	 	 	//666FF 	 	 	--eS999FFFFF	 	-.....s   A   B"=B"BB"c                   ddl m}m}m} ddlm} t                        |d |d                      t          dd            |j        }||j         |d |d	                      nC ||j	                  } ||j                  }|j
        rd
nd}	t          d| d| d|	            d}
 |d ||
                      |                     |           dS )uA   Screen 5 — monthly spend limit (read-only; cap is portal-only).r   r	   rK  r>   zMonthly spend limitr   rB   Nz/No monthly cap visible (managed on the portal).z (default ceiling)r   z of z used this monthuK   The monthly limit is set on the portal — the terminal shows it read-only.)r   r
   r   r   r   rL  r%   monthly_cap	limit_usdspent_this_month_usdis_default_ceilingrI  )r*   rM   r
   r   r   rL  capspentrT  ceiling_limit_notes              r4   r_  z%CLIBillingMixin._billing_limit_screen  sU   ''''''''''3333335""23355666:   ;#-/GPMNNPPQQQQ L!9::E L//E.1.DL**"GBuBB%BBBBCCC 	 	&RR__&&'''!!%(((((r:   )r   r   )r   r6   )rt   ru   r   r   r   )rA  )rB  ru   )r   ru   r   r6   )r   rc  )#__name__
__module____qualname____doc__r5   r9   rP   rL   r|   rb   r   rc   r   r   r   r   r   r   r  rF  rI  rD  r&   r`  rx  r~  r]  r  r  r  r  r  r^  r  r_  r  r:   r4   r   r      s       JJ? ? ? ?B	` 	` 	` 	`*K *K *KXs: s: s:j   8?F ?F ?FB D^ ' ' ' ' '< 4  4  4D> > >8 QU \K \K \K \K \K|: : :,Y8W[ Y8 Y8 Y8 Y8 Y8v TX =h =h =h =h =h~) ) )") ) )$& & & & &@ <> , , , , , ,A" A" A"F   *< < <   &* * *XG8 G8 G8RD< D< D<L*) *) *)X) ) )-- -- --^ ?CTX i< i< i< i< i<VO8 O8 O8b/ / /2) ) ) ) )r:   r   N)r  
__future__r   r   r  r:   r4   <module>r     s_     # " " " " "J) J) J) J) J) J) J) J) J) J)r:   