
    /i              4          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mZm	Z	 d dl
Z
d dlZd dlZd dlmZ d dlmZ d dlmZ d dlmZmZ d dlmZmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z' d dl(m)Z) d d	l*m+Z+ d d
l,m-Z-  e       Z.de/fdZ0de/fdZ1de2e3e3f   fdZ4de3de3de3de3de3f
dZ5dee3ef   fdZ6dee3   de/fdZ7dee3   de/fdZ8de3dedede9fdZ:d Z;de3de<fdZ=d e	ee3ef      fd!Z>d"e"fd#Z?d"e"fd$Z@d% ZAd& ZBd' ZC e j                  d(d)d*gi+      d,        ZEeEj                  d-      d.        ZFeFj                  d/      d0        ZHeFj                  d1       e j                  d2d3 e j                  d4d5g      d4d67       e j                  d8d9e<d:d;7       e j                  d<d=d>?      d@e3dAe<dBe9fdC                            ZKeFj                  dD      dE        ZLeFj                  dF       e j                  dGdHdIJ      dee3   fdK              ZMeFj                  dL       e j                  d2d3 e j                  d4d5g      ddM7       e j                  d8d9e<ddN7       e j                  d<dd>?      d@ee3   dAee<   dBee9   fdO                            ZNeEj                  dP       e j                  dQdRdSJ       e j                  d8dTe<d:d;7       e j                  d2d3 e j                  d4d5g      d4d67       e j                  dUd=d>V       e j                  dWd=dXV      dYee3   dAe<d@e3dBe9dZe9f
d[                                          ZOeEj                  d\       e j                  dd=]       e j                  d^d_ e jZ                  d=`      dab       e j                  dcdd e jZ                  d=`      deb       e j                  dfdg e jZ                  d=`      dhb       e j                  didj e jZ                  d=`      dkb       e j                  dldmdndoddpq       e j                  drds e jZ                  d=`      dtb       e j                  dud3e3e6dvw       e j                  dxdye3e6dzw       e j                  d{d| e j                  g d}      d~       e j                  dd e jZ                         db       e j                  ddd=d=d       e j                  dddJ       e j                  ddd=       e j                  dd9dJ       e j                  d e j                  g d      db       e j                  de<dd7      de3de3de3de3de3de3de3d-edede3de3de9de3de9de3de3de<f"d                                                                                                                              ZQeEj                  d      d        ZReEj                  d      d        ZSeSj                  d      d        ZTeSj                  d       e j                  dd=]      de3fd              ZUeSj                  d       e j                  dd=]       e j                  dd=]      de3de3fd                     ZVeEj                  d       d        ZWeEj                  do       e j                  ddn]       e j                  dd=dV       e j                  d^d_ e jZ                  d=`      dab       e j                  dcdd e jZ                  d=`      deb       e j                  dfdg e jZ                  d=`      dhb       e j                  didj e jZ                  d=`      dkb       e j                  dldmdndoddpq       e j                  drds e jZ                  d=`      dtb       e j                  dud3e3e6dvw       e j                  dxdye3e6dzw       e j                  d{d| e j                  g d}      d~       e j                  dd=d=d       e j                  dddJ       e j                  ddd=       e j                  dd9dJ       e j                  d e j                  g d      db       e j                  de<dd7      de3de9de3de3de3de3de3de3d-edede3de9de3de9de3de3de<f"d                                                                                                                              ZXd ZYeZdk(  r eY        yy)    N)DictAnyOptionalList)Console)Table)Panel)PromptConfirm)	CacheModeAsyncWebCrawlerCrawlResultBrowserConfigCrawlerRunConfigLLMExtractionStrategyLXMLWebScrapingStrategyJsonCssExtractionStrategyJsonXPathExtractionStrategyBM25ContentFilterPruningContentFilterBrowserProfilerDefaultMarkdownGenerator	LLMConfigBFSDeepCrawlStrategyDFSDeepCrawlStrategyBestFirstCrawlingStrategy)USER_SETTINGS)
completion)Pathreturnc                      t        j                         dz  } | dz  }|j                         s| j                  dd       i S t	        |      5 }t        j                  |      xs i cd d d        S # 1 sw Y   y xY w)N	.crawl4ai
global.ymlT)parentsexist_ok)r   homeexistsmkdiropenyaml	safe_load)
config_dirconfig_filefs      H/mnt/e/genesis-system/.venv/lib/python3.12/site-packages/crawl4ai/cli.pyget_global_configr0   +   sp    {*J|+K5		k	 'a~~a &B' ' 's   A00A9configc                     t        j                         dz  dz  }t        |d      5 }t        j                  | |       d d d        y # 1 sw Y   y xY w)Nr"   r#   w)r   r&   r)   r*   dump)r1   r-   r.   s      r/   save_global_configr5   6   sF    ))++l:K	k3	 1		&!  s   AAc                     t               } | j                  d      }| j                  d      }|sTt        j                  d       t        j                  d       t        j                  d       t        j                  d      }|j                  d      s|st        j                  d|z   d	
      }nd}| j                  d      r| j                  d      s*|| d<   || d<   t        |        t        j                  d       ||fS )NDEFAULT_LLM_PROVIDERDEFAULT_LLM_PROVIDER_TOKENz$
No default LLM provider configured.zUProvider format: 'company/model' (e.g., 'openai/gpt-4o', 'anthropic/claude-3-sonnet')zBSee available providers at: https://docs.litellm.ai/docs/providerszEnter providerzollama/zEnter API token for T)
hide_inputzno-tokenz.
Configuration saved to ~/.crawl4ai/global.yml)r0   getclickechoprompt
startswithr5   )r1   providertokens      r/   setup_llm_configrA   ;   s     Fzz01HJJ34E

:;

jk

WX<< 01y)LL!7(!BtTE::,-VZZ@\5])1%&/4+,6"

DEU?    urlmarkdownqueryr?   r@   c           	         K   t        ||d|  dddd| d| ddgd	      }|D ]-  }|d
   d   d   j                  d      x}s t        |dd       / t                y w)NzhYou are Crawl4ai assistant, answering user question based on the provided context which is crawled from .system)contentrolez<|start of context|>
z
<|end of context|>

userT)modelapi_keymessagesstreamchoicesr   deltarI    )endflush)r   r:   print)rC   rD   rE   r?   r@   responsechunkrI   s           r/   stream_llm_responserX   T   s      F  GJ  FK  KL  M 
 4H:=UV[U\]	
 H   /I&q)'266yAA7A'r./ 
Gs   AA!A!c                 j   |si S i }|j                  d      }|D ]<  }	 |j                  dd      \  }}|j                         dk(  rd}n|j                         dk(  rd}n|j                         rt        |      }n|j	                  dd	d      j                         rt        |      }n|j                  d
      rR|j                  d      rA|dd j                  d      D cg c]#  }|j                         s|j                         % }}n8|j                  d      r'|j                  d      r	 t        j                  |      }|||j                         <   ? |S c c}w # t        j                  $ r t        j                  d|       w xY w# t        $ r t        j                  d|       w xY w)N,=   trueTfalseFrG   rR   []{}zInvalid JSON object: zInvalid key=value pair: )splitlowerisdigitintreplacefloatr>   endswithstripjsonloadsJSONDecodeErrorr;   BadParameter
ValueError)	ctxparamvalueresultpairspairkvxs	            r/   parse_key_valuesrz   l   s   	FKKE H	H::c1%DAqwwyF"Ag%5q#a&a3r!$,,.E!Hc"qzz#()!Bc(:H1aggiQWWYHHc"qzz#J

1A !"F1779H$ M I ++ J,,/DQC-HIIJ  	H$$'?v%FGG	Hs<   CF!E7E	%F/E!FF!,FF"F2pathc           	      @   | si S 	 t        |       5 }| j                  d      rt        j                  |      cd d d        S t	        j
                  |      cd d d        S # 1 sw Y   y xY w# t        $ r)}t        j                  d|  dt        |             d }~ww xY w)N)z.yamlz.ymlzError loading config file z: )
r)   rj   r*   r+   rl   load	Exceptionr;   ro   str)r{   r.   es      r/   load_config_filer      s    	P$Z 	 1}}./~~a(	  	  99Q<	  	  	   P  #=dV2c!fX!NOOPs?   A+ &A	A+ A	A+ A($A+ (A+ +	B4$BBc                     | sy t        |       S )N)r   )r{   s    r/   load_schema_filer      s    D!!rB   browser_cfgcrawler_cfgverbosec                   K   |rat        j                  d       t        j                  d|j                                 t        j                  d|j                                 t        |      4 d {   }	 |j	                  | |       d {   }|cd d d       d {    S 7 37 7 	# t
        $ r&}t        j                  dt        |             d }~ww xY w# 1 d {  7  sw Y   y xY ww)Nz%Starting crawler with configurations:zBrowser config: zCrawler config: )r1   )rC   r1   zCrawling failed: )r;   r<   r4   r   arunr~   ClickExceptionr   )rC   r   r   r   crawlerrt   r   s          r/   run_crawlerr      s     

:;

%k&6&6&8%9:;

%k&6&6&8%9:;k2 E Eg	E"<<C<DDFE E EDE  	E&&):3q6('CDD	E	E E Esx   A4C76B*7C7:C"<B0B,B0C7$B.%C7,B0.C70	C9!CCC""C4(C+)C40C7c                  2    d} t        j                  |        y )Nu  
🚀 Crawl4AI CLI Examples

1️⃣  Basic Usage:
    # Simple crawl with default settings
    crwl https://example.com

    # Get markdown output
    crwl https://example.com -o markdown

    # Verbose JSON output with cache bypass
    crwl https://example.com -o json -v --bypass-cache

2️⃣  Using Config Files:
    # Using browser and crawler configs
    crwl https://example.com -B browser.yml -C crawler.yml

    # CSS-based extraction
    crwl https://example.com -e extract_css.yml -s css_schema.json -o json

    # LLM-based extraction with config file
    crwl https://example.com -e extract_llm.yml -s llm_schema.json -o json
    
    # Quick LLM-based JSON extraction (prompts for LLM provider first time)
    crwl https://example.com -j  # Auto-extracts structured data
    crwl https://example.com -j "Extract product details including name, price, and features"  # With specific instructions

3️⃣  Direct Parameters:
    # Browser settings
    crwl https://example.com -b "headless=true,viewport_width=1280,user_agent_mode=random"

    # Crawler settings
    crwl https://example.com -c "css_selector=#main,delay_before_return_html=2,scan_full_page=true"

4️⃣  Profile Management for Identity-Based Crawling:
    # Launch interactive profile manager
    crwl profiles

    # Create, list, and delete browser profiles for identity-based crawling
    # Use a profile for crawling (keeps you logged in)
    crwl https://example.com -p my-profile-name

    # Example: Crawl a site that requires login
    # 1. First create a profile and log in:
    crwl profiles
    # 2. Then use that profile to crawl the authenticated site:
    crwl https://site-requiring-login.com/dashboard -p my-profile-name

5️⃣  CDP Mode for Browser Automation:
    # Launch browser with CDP debugging on default port 9222
    crwl cdp

    # Use a specific profile and custom port
    crwl cdp -p my-profile -P 9223

    # Launch headless browser with CDP enabled
    crwl cdp --headless

    # Launch in incognito mode (ignores profile)
    crwl cdp --incognito

    # Use the CDP URL with other tools (Puppeteer, Playwright, etc.)
    # The URL will be displayed in the terminal when the browser starts

    
6️⃣  Sample Config Files:

browser.yml:
    headless: true
    viewport_width: 1280
    user_agent_mode: "random"
    verbose: true
    ignore_https_errors: true

extract_css.yml:
    type: "json-css"
    params:
        verbose: true

css_schema.json:
    {
      "name": "ArticleExtractor",
      "baseSelector": ".article",
      "fields": [
        {
          "name": "title",
          "selector": "h1.title",
          "type": "text"
        },
        {
          "name": "link",
          "selector": "a.read-more",
          "type": "attribute",
          "attribute": "href"
        }
      ]
    }

extract_llm.yml:
    type: "llm"
    provider: "openai/gpt-4"
    instruction: "Extract all articles with their titles and links"
    api_token: "your-token"
    params:
        temperature: 0.3
        max_tokens: 1000

llm_schema.json:
    {
      "title": "Article",
      "type": "object",
      "properties": {
        "title": {
          "type": "string",
          "description": "The title of the article"
        },
        "link": {
          "type": "string",
          "description": "URL to the full article"
        }
      }
    }

7️⃣  Advanced Usage:
    # Combine configs with direct parameters
    crwl https://example.com -B browser.yml -b "headless=false,viewport_width=1920"

    # Full extraction pipeline with config files
    crwl https://example.com \
        -B browser.yml \
        -C crawler.yml \
        -e extract_llm.yml \
        -s llm_schema.json \
        -o json \
        -v
        
    # Quick LLM-based extraction with specific instructions
    crwl https://amazon.com/dp/B01DFKC2SO \
        -j "Extract product title, current price, original price, rating, and all product specifications" \
        -b "headless=true,viewport_width=1280" \
        -v

    # Content filtering with BM25
    crwl https://example.com \
        -f filter_bm25.yml \
        -o markdown-fit

    # Authenticated crawling with profile
    crwl https://login-required-site.com \
        -p my-authenticated-profile \
        -c "css_selector=.dashboard-content" \
        -o markdown

For more documentation visit: https://github.com/unclecode/crawl4ai

8️⃣  Q&A with LLM:
    # Ask a question about the content
    crwl https://example.com -q "What is the main topic discussed?"

    # First view content, then ask questions
    crwl https://example.com -o markdown  # See the crawled content first
    crwl https://example.com -q "Summarize the key points"
    crwl https://example.com -q "What are the conclusions?"

    # Advanced crawling with Q&A
    crwl https://example.com \
        -B browser.yml \
        -c "css_selector=article,scan_full_page=true" \
        -q "What are the pros and cons mentioned?"

    Note: First time using -q will prompt for LLM provider and API token.
    These will be saved in ~/.crawl4ai/global.yml for future use.
    
    Supported provider format: 'company/model'
    Examples:
      - ollama/llama3.3
      - openai/gpt-4
      - anthropic/claude-3-sonnet
      - cohere/command
      - google/gemini-pro
    
    See full list of providers: https://docs.litellm.ai/docs/providers
    
    # Set default LLM provider and token in advance
    crwl config set DEFAULT_LLM_PROVIDER "anthropic/claude-3-sonnet"
    crwl config set DEFAULT_LLM_PROVIDER_TOKEN "your-api-token-here"
    
    # Set default browser behavior
    crwl config set BROWSER_HEADLESS false  # Always show browser window
    crwl config set USER_AGENT_MODE random  # Use random user agent

9️⃣ Profile Management:
    # Launch interactive profile manager
    crwl profiles

    # Create a profile and use it for crawling
    crwl profiles  # Create and set up your profile interactively
    crwl https://example.com -p my-profile-name  # Use profile for crawling

    # Example workflow for authenticated site
    # 1. First create a profile and log in to the site:
    crwl profiles  # Select "Create new profile" option
    # 2. Then use that profile to crawl authenticated content:
    crwl https://site-requiring-login.com/dashboard -p my-profile-name

🔄 Builtin Browser Management:
    # Start a builtin browser (runs in the background)
    crwl browser start
    
    # Check builtin browser status
    crwl browser status
    
    # Open a visible window to see the browser
    crwl browser view --url https://example.com
    
    # Stop the builtin browser
    crwl browser stop
    
    # Restart with different options
    crwl browser restart --browser-type chromium --port 9223 --no-headless
    
    # Use the builtin browser in your code
    # (Just set browser_mode="builtin" in your BrowserConfig)
    browser_config = BrowserConfig(
        browser_mode="builtin", 
        headless=True
    )
    
    # Usage via CLI:
    crwl https://example.com -b "browser_mode=builtin"
)r;   r<   )exampless    r/   show_examplesr      s    fHN 
JJxrB   c                    d}t        j                  |       D ]o  \  }}}|D ]d  }t         j                  j                  ||      }t         j                  j	                  |      rC|t         j                  j                  |      z  }f q |S )z0Calculate the total size of a directory in bytesr   )oswalkr{   joinislinkgetsize)r{   
total_sizedirpath_	filenamesr.   fps          r/   get_directory_sizer     sw    J!# 2I 	2Agq)B77>>"%bggoob11
	22
 rB   profilesc           	         | s"t         j                  t        ddd             yt        dddd      }|j	                  d	d
d       |j	                  ddd       |j	                  dd       |j	                  dd       |j	                  dd       |j	                  ddd       t        |       D ]u  \  }}t        |d         }t        j                  |      }|d   j                  d      }|j                  t        |dz         |d   |d   ||d   j                         |       w t         j                  |       y) z(Display a rich table of browser profileszI[yellow]No profiles found. Create one with the 'create' command.[/yellow]zBrowser Profilesbluetitleborder_styleNT	bold cyanr   show_headerheader_styler   #dim   )stylewidthNamecyan)r   no_wrapr   greenr   CreatedyellowBrowsermagentaSizeright)r   justifyr{   createdz%Y-%m-%d %H:%Mr\   nametype)consolerU   r	   r   
add_column	enumerater   humanizenaturalsizestrftimeadd_rowr   
capitalize)r   tableiprofilesize
human_sizer   s          r/   display_profiles_tabler     sH   eg 2I 	J*;eklE	SQ/	V648	V7+	Yh/	Yi0	V67;) 

7!'&/2))$/
 )$--.>? 	!HFOFOFO&&(	

$ MM%rB   profilerc                   K   t         j                  t        dd             t        j                  ddt        t        j                                      }t         j                  d       t         j                  d       	 | j                  |       d	{   }|rt         j                  d
|        y	t         j                  d       y	7 5# t        $ r,}t         j                  dt        |       d       Y d	}~y	d	}~ww xY ww)z#Interactive profile creation wizardz[bold cyan]Create Browser Profile[/bold cyan]
This will open a browser window for you to set up your identity.
Log in to sites, adjust settings, then press 'q' to save.r   r   z[cyan]Enter profile name[/cyan]profile_)defaultz [cyan]Creating profile...[/cyan]zk[yellow]A browser window will open. After logging in to sites, press 'q' in this terminal to save.[/yellow]Nz0[green]Profile successfully created at:[/green] z$[red]Failed to create profile.[/red]z[red]Error creating profile: [/red])
r   rU   r	   r
   askrg   timecreate_profiler~   r   )r   profile_nameprofile_pathr   s       r/   create_profile_interactiver     s     MM% R $*+ ,
 ::?8TWX\XaXaXcTdSeIfgLMM45MM  AF%44\BBMML\N[\MM@A C  F5c!fXVDEEFsN   B DC CC 6D7C DC 	D"C?:D?DDc                 Z   | j                         }|st        j                  d       yt        |       t	        j
                  dt        t        t        |            D cg c]  }t        |dz          c}d      }	 t        |      dz
  }||   }t        j
                  d|d    d	      rP| j                  |d
         }|rt        j                  d|d    d       yt        j                  d|d    d       yyc c}w # t        t        f$ r t        j                  d       Y yw xY w)zInteractive profile deletionz-[yellow]No profiles found to delete.[/yellow]Nz,[red]Enter number of profile to delete[/red]r\   Fr   rP   show_choicesz.[red]Are you sure you want to delete profile 'r   z'?[/red]r{   z[green]Profile 'z' deleted successfully.[/green]z[red]Failed to delete profile 'z'.[/red]z[red]Invalid selection.[/red])list_profilesr   rU   r   r
   r   rangelenr   rg   r   delete_profilerp   
IndexError)r   r   r   idxr   successs         r/   delete_profile_interactiver     s   %%'HEF 8$ **6#(X#78aQqS8	C7#hl3- ;;GPVGXX`ab--gfo>G 00AA`ab ??PPXYZ c 9  
# 7567s   C>>A!D  D $D*)D*c                 V  K   t         j                  d| d|  d       t        dd|       }t               }t	        j
                  dg dd	
      }	 t        |||d       d{   }|dk(  r9t         j                  t        j                  |j                         d             n|dk(  rHt         j                  t        j                  t        j                  |j                        d             nb|dv r*t         j                  |j                  j                         n4|dk(  r/t         j                  |j                  j                  dd             t         j                  d|        |S 7 # t         $ r+}t         j                  dt#        |              Y d}~yd}~ww xY ww)z(Use a profile to crawl a website via CLIz[cyan]Crawling [bold]z[/bold] using profile at [bold]z[/bold][/cyan]FT)headlessuse_managed_browseruser_data_dirz[cyan]Output format[/cyan])allrl   rD   mdr   rD   rP   r   Nr      indentrl   rD   r   r   zNo title foundz$[green]Successfully crawled[/green] z[red]Error crawling:[/red] )r   rU   r   r   r
   r   r   rl   dumps
model_dumprm   extracted_contentrD   raw_markdownmetadatar:   r~   r   )r   rC   r   r   output_formatrt   r   s          r/   crawl_with_profile_clir     s]    MM)#.Ml^[ijk   "K #$K JJ$:M"3[$GG E!MM$**V%6%6%8CDf$MM$**TZZ0H0H%IRSTU00MM&//667g%MM&//--g7GHI<SEBC H  3CF8<=sC   AF)E2 #E/$D
E2 .F)/E2 2	F&;!F!F)!F&&F)c                  *  K   t               } | j                         }|st        j                  d       yt	        |       t        j                  dt        t        t        |            D cg c]  }t        |dz          c}d      }	 t        |      dz
  }||   }t        j                  d      }|rt        |d   |       d{    yt        j                  d	       yc c}w 7  # t        t        f$ r t        j                  d
       Y yw xY ww)z*Interactive profile selection for crawlingz5[yellow]No profiles found. Create one first.[/yellow]Nz+[cyan]Enter number of profile to use[/cyan]r\   Fr   z[cyan]Enter URL to crawl[/cyan]r{   z[red]No URL provided[/red]z[red]Invalid selection[/red])r   r   r   rU   r   r
   r   r   r   r   rg   r   rp   r   )r   r   r   r   r   rC   s         r/   use_profile_to_crawlr     s      H%%'HMN 8$ **5#(X#78aQqS8	C6#hl3- jj:;(#>>>MM67 9 ? 
# 6456sT   A)D+C" 	D
<C) C'C) DC) !D'C) )$DDDDc                    K   t               } dddddd}	 t        j                  t        dd	             |j	                         D ]D  \  }}|d
k(  rdn|dk(  rdn|dk(  rdn|dk(  rdnd}t        j                  d| d| d| d|        F t        j                  dt        |j                               d
      }|d
k(  r| j                         }t        |       n]|dk(  rt        |        d{    nD|dk(  rt        |        n3|dk(  rt                d{    n|dk(  rt        j                  d       yt        j                  d       ;7 `7 9w)z#Interactive profile management menuzList profileszCreate new profilezDelete profilez Use a profile to crawl a websiteExit)12345z.[bold cyan]Browser Profile Manager[/bold cyan]r   r   r   r   r   r   r   redr   r   r_   r`   z[/z]. zEnter choicer   Nr   z%[cyan]Exiting profile manager.[/cyan]
)r   r   rU   r	   itemsr
   r   listkeysr   r   r   r   r   )r   optionskeyrs   colorchoicer   s          r/   manage_profilesr  >  s[     H !/G eL[abc!--/ 	@JC"czG3#:xTW[^T^5nquxnxdj  EEMMAeWAcU"UG3ug>?	@ ND4HRUVS=--/H"8,s],X666s]&x0s]&(((s]MMAB 	dA   7 )s$   C.E0E1(EE8EEhelp_option_namesz-hz--help)context_settingsc                       y)zICrawl4AI CLI - Web content extraction and browser profile management toolN r  rB   r/   clir  n  s     	rB   browserc                       y)a(  Manage browser instances for Crawl4AI
    
    Commands to manage browser instances for Crawl4AI, including:
    - status - Check status of the builtin browser
    - start - Start a new builtin browser
    - stop - Stop the running builtin browser
    - restart - Restart the builtin browser
    Nr  r  rB   r/   browser_cmdr
  t  s     	rB   statusc                     t               } 	 t        j                  | j                        }|d   rm|d   }t        j                  t        d|d    d|d    d|d    d	|d
    dt        j                  dt        j                  |d                ddd             yt        j                  t        ddd             y# t        $ rA}t        j                  dt        |       d       t        j                  d       Y d}~yd}~ww xY w)z"Show status of the builtin browserrunninginfoz:[green]Builtin browser is running[/green]

CDP URL: [cyan]cdp_urlz[/cyan]
Process ID: [yellow]pidz[/yellow]
Browser type: [blue]browser_typez&[/blue]
User data directory: [magenta]r   z[/magenta]
Started: [cyan]z%Y-%m-%d %H:%M:%S
start_time[/cyan]zBuiltin Browser Statusr   r   zd[yellow]Builtin browser is not running[/yellow]

Use 'crwl browser start' to start a builtin browserr   z$[red]Error checking browser status: r   r\   N)r   anyiorunget_builtin_browser_statusr   rU   r	   r   r   	localtimer~   r   sysexit)r   r  r  r   s       r/   browser_status_cmdr    s     H8>>?)&>DMM%""&y/!2 3''+E{m 4''+N';&< =115o1F0G H""&--0CT^^TXYeTfEg"h!iipr /$	 	 MM%F.%	   <SVHFKLs   BB? !B? ?	D	7DD	startz--browser-typez-bchromiumfirefoxz Browser type (default: chromium))r   r   helpz--portz-p$  zDebugging port (default: 9222)z--headless/--no-headlessTzRun browser in headless mode)r   r  r  portr   c                    t               }t        j                  |j                        }|d   r)t        j                  t        d|d    ddd             y	 t        j                  t        d	|  d
| d|rdnd ddd             t        j                  |j                  | ||      }|r&t        j                  t        d| ddd             yt        j                  t        ddd             t        j                  d       y# t        $ rA}t        j                  dt        |       d       t        j                  d       Y d}~yd}~ww xY w)zStart a builtin browser instance
    
    This will start a persistent browser instance that can be used by Crawl4AI
    by setting browser_mode="builtin" in BrowserConfig.
    r  zD[yellow]Builtin browser is already running[/yellow]

CDP URL: [cyan]r  z:[/cyan]

Use 'crwl browser restart' to restart the browserzBuiltin Browser Startr   r   Nz<[cyan]Starting builtin browser[/cyan]

Browser type: [green]![/green]
Debugging port: [yellow][/yellow]
Headless: [cyan]YesNor  r   zD[green]Builtin browser started successfully[/green]

CDP URL: [cyan]zT[/cyan]

This browser will be used automatically when setting browser_mode='builtin'r   z*[red]Failed to start builtin browser[/red]r   r\   z%[red]Error starting builtin browser: r   )r   r  r  r  r   rU   r	   launch_builtin_browserr  r  r~   r   )r  r   r   r   r  r  r   s          r/   browser_start_cmdr'    sk     H YYx::;Fie$Y/0 1@@ *!
 	 	#e$$0> 2''+f -(0ud;7D *
 	 ))++	
 MM%"") +^^ .$  MM%<-" 
 HHQK =c!fXVLMs   A8D	 6D	 		E7EEstopc                  t   t               } 	 t        j                  | j                        }|d   s"t        j                  t        ddd             yt        j                  t        ddd             t        j                  | j                        }|r"t        j                  t        d	dd
             yt        j                  t        ddd             t        j                  d       y# t        $ rA}t        j                  dt        |       d       t        j                  d       Y d}~yd}~ww xY w)z Stop the running builtin browserr  z8[yellow]No builtin browser is currently running[/yellow]zBuiltin Browser Stopr   r   Nz([cyan]Stopping builtin browser...[/cyan]r   z3[green]Builtin browser stopped successfully[/green]r   z)[red]Failed to stop builtin browser[/red]r   r\   z%[red]Error stopping builtin browser: r   )r   r  r  r  r   rU   r	   kill_builtin_browserr  r  r~   r   )r   r  r   r   s       r/   browser_stop_cmdr+    s     H#8>>?i MM%J,% 
 e6(
 	 ))H99:MM%E,$  MM%;," 
 HHQK =c!fXVLMs%   AC- A#C- 66C- -	D767D22D7viewz--urlz-uz,URL to navigate to (defaults to about:blank))r  c           	      R   t               }	 t        j                  |j                        }|d   s"t        j                  t        ddd             y|d   }|d   }t        j                  t        d	| d
| xs d ddd             ddl}ddl}t        j                  dk(  rdg}nt        j                  dk(  rdg}ndg}d|d    dddg}| r|j                  |        	 |j                  ||z          t        j                  d       y# t        $ rE}	t        j                  dt        |	       d       t        j                  d| d       Y d}	~	yd}	~	ww xY w# t        $ rA}	t        j                  dt        |	       d       t        j                  d       Y d}	~	yd}	~	ww xY w) z
    Open a visible window of the builtin browser
    
    This command connects to the running builtin browser and opens a visible window,
    allowing you to see what the browser is currently viewing or navigate to a URL.
    r  zs[yellow]No builtin browser is currently running[/yellow]

Use 'crwl browser start' to start a builtin browser firstzBuiltin Browser Viewr   r   Nr  r  zR[cyan]Opening visible window connected to builtin browser[/cyan]

CDP URL: [green]z[/green]
URL to load: [yellow]zabout:blank	[/yellow]r   r   darwinz</Applications/Google Chrome.app/Contents/MacOS/Google Chromewin32z5C:\Program Files\Google\Chrome\Application\chrome.exezgoogle-chromez--remote-debugging-port=debugging_portz$--remote-debugging-address=localhostz--no-first-runz--no-default-browser-checkzE[green]Browser window opened. Close it when finished viewing.[/green]z[red]Error launching browser: r   z#[yellow]Try connecting manually to z@ in Chrome or using the '--remote-debugging-port' flag.[/yellow]z$[red]Error viewing builtin browser: r\   )r   r  r  r  r   rU   r	   
subprocessr   r  platformappendPopenr~   r   r  )
rC   r   r  r  r  r2  r   r
  browser_argsr   s
             r/   browser_view_cmdr7    s     H98>>?i MM%L,%	  f~y/e&i ($$'$8=#9D )
 	 	 <<8#YZK\\W$WXK*+K 't,<'=&>?2(	
 $	K[<78MMab 	KMM:3q6(&IJMM?y  II  J  K  K	K  <SVHFKLsC   AE BE !)D 	E;EE EE 	F&%7F!!F&restartz*Browser type (defaults to same as current)z,Debugging port (defaults to same as current)c                    t               }	 t        j                  |j                        }i }|d   r|d   }|d   |d   dd}t        j                  t        ddd	
             t        j                  |j                        }|s6t        j                  t        ddd
             t        j                  d       | xs |j                  dd      } |xs |j                  dd      }||n|j                  dd      }t        j                  t        d|  d| d|rdnd ddd	
             t        j                  |j                  | ||      }|r&t        j                  t        d| ddd
             yt        j                  t        ddd
             t        j                  d       y# t        $ rA}	t        j                  dt        |	       d       t        j                  d       Y d}	~	yd}	~	ww xY w)zRestart the builtin browser
    
    Stops the current builtin browser if running and starts a new one.
    By default, uses the same configuration as the current browser.
    r  r  r  r1  T)r  r   r   z0[cyan]Stopping current builtin browser...[/cyan]zBuiltin Browser Restartr   r   z)[red]Failed to stop current browser[/red]r   r\   r  r   r  Nr   z@[cyan]Starting new builtin browser[/cyan]

Browser type: [green]r"  r#  r$  r%  r  zF[green]Builtin browser restarted successfully[/green]

CDP URL: [cyan]r   z,[red]Failed to restart builtin browser[/red]z'[red]Error restarting builtin browser: r   )r   r  r  r  r   rU   r	   r*  r  r  r:   r&  r~   r   )
r  r   r   r   r  current_configr  r   r  r   s
             r/   browser_restart_cmdr;  N  s     HD8>>?)&>D $^ 4-. N MM%B/#  ii = =>Ge?3!& 
  $U~'9'9.*'U7~))&$7'389K9KJX\9] 	e$$0> 2''+f -(0ud;7D ,
 	 ))++	
 MM%"")'3/$	  MM%>/" 
 HHQK ?AxvNOs   E)F- 66F- -	G767G22G7cdpz--user-data-dirz-dzGDirectory to use for browser data (will be created if it doesn't exist)z-Pz
--headless)is_flagr  z--incognitoz5Run in incognito/private mode (ignores user-data-dir)r   	incognitoc                    t               }	 d}|sr| rpt        j                  j                  |       }t        j                  j	                  |      s0t
        j                  d| d       t        j                  |d       |}t
        j                  t        d| d| d|xs d	 d
|rdnd d|rdnd ddd             t        j                  |j                  ||||      }|s+t
        j                  d       t        j                  d       yy# t        $ rA}	t
        j                  dt        |	       d       t        j                  d       Y d}	~	yd}	~	ww xY w)a  Launch a standalone browser with CDP debugging enabled
    
    This command launches a browser with Chrome DevTools Protocol (CDP) debugging enabled,
    prints the CDP URL, and keeps the browser running until you press 'q'.
    
    The CDP URL can be used for various automation and debugging tasks.
    
    Examples:
        # Launch Chromium with CDP on default port 9222
        crwl cdp
        
        # Use a specific directory for browser data and custom port
        crwl cdp --user-data-dir ~/browser-data --port 9223
        
        # Launch in headless mode
        crwl cdp --headless
        
        # Launch in incognito mode (ignores user-data-dir)
        crwl cdp --incognito
    Nz[yellow]Directory 'z&' doesn't exist. Creating it.[/yellow]T)r%   zH[cyan]Launching browser with CDP debugging[/cyan]

Browser type: [green]r"  z%[/yellow]
User data directory: [cyan]zTemporary directoryz[/cyan]
Headless: [cyan]r$  r%  z[/cyan]
Incognito: [cyan]z5[/cyan]

[yellow]Press 'q' to quit when done[/yellow]zCDP Browserr   r   z2[red]Failed to launch browser or get CDP URL[/red]r\   z"[red]Error launching CDP browser: r   )r   r   r{   
expanduserr'   r   rU   makedirsr	   r  r  launch_standalone_browserr  r  r~   r   )
r   r   r  r   r>  r   data_direxpanded_pathr  r   s
             r/   cdp_cmdrE    sV   8  H*]GG..}=M 77>>-0 3M?BhijMD9$H 	e$$0> 2''+f -**2*K6K)L M(0ud; < )2= >;<  

 
	 ))..
 MMNOHHQK   :3q6(&IJs   DD 	E7EEcrawl)requiredz--browser-configz-B)r'   zBrowser config file (YAML/JSON))r   r  z--crawler-configz-CzCrawler config file (YAML/JSON)z--filter-configz-fzContent filter config filez--extraction-configz-ezExtraction strategy config filez--json-extractz-jFrR   z;Extract structured data using LLM with optional description)r=  
flag_valuer   r  z--schemaz-szJSON schema for extractionz	--browserz-Browser parameters as key1=value1,key2=value2)r   callbackr  z	--crawlerz-cz-Crawler parameters as key1=value1,key2=value2z--outputz-o)r   rl   rD   r   markdown-fitmd-fitr   )r   r   z--output-filez-Oz"Output file path (default: stdout)z--bypass-cachez-bczBypass cache when crawling)r=  r   r  z
--questionz-qz(Ask a question about the crawled contentz	--verbosez-v)r=  z	--profilez(Use a specific browser profile (by name)z--deep-crawl)bfsdfs
best-firstzFEnable deep crawling with specified strategy (bfs, dfs, or best-first)z--max-pages
   z3Maximum number of pages to crawl in deep crawl modebrowser_configcrawler_configfilter_configextraction_configjson_extractschemar   outputoutput_filebypass_cachequestionr   
deep_crawl	max_pagesc           
      $   |rt               }|j                  |      }|sM|j                         }|r%t        j	                  d| d       t        |       yt        j	                  d       y|si }||d<   d|d<   |rt        j	                  d|        	 t        j                  t        |            }t        j                  t        |            }|r |j                  d;i |}|r |j                  d;i |}|s|	d	v r|rt        |      }n|s
|	d	v rd
ddd}d   dk(  rMt        t        |j                  d      |j                  dd      |j                  dd                  |_        nC|d   d
k(  r;t        t        |j                  d      |j                  dd                  |_        |Ot!               \  }}d}d|z   }|r|n|}t#        t%        ||      |t'        |      ddd|      |_        |	dk(  rd}	n|r	t        |      }t'        |      }|j                  d      st+        j,                  d      |d   d vrt+        j,                  d!|d          |d   d"k(  rp|j                  d#      r|j                  d$      st+        j,                  d%      t#        d;t%        |d#   |d$         |d&   |d'|j                  d(i       |_        n3|d   d)k(  rt/        |*      |_        n|d   d+k(  rt1        |*      |_        |rt2        j4                  |_        t9               |_        |re|d,k(  rt=        d-|.      |_        n/|d/k(  rtA        d-|.      |_        n|d0k(  rtC        d-|.      |_        |rt        j	                  d1| d2| d3       tE               }|j                  d4d      |_#        |j                  d4d      |_#        tI        jJ                  tL        | |||      }tO        |tP              r,tS        |      d5k(  rt+        jT                  d6       y|d5   } |}!n|} |g}!|rBt!               \  }}| jV                  jX                  }"tI        jJ                  tZ        | |"|||       y|
sW|	dk(  rtO        |tP              rH|!D #cg c]  }#|#j]                          }$}#t+        jT                  t_        j`                  |$d78             yt+        jT                  t_        j`                  | j]                         d78             y|	dk(  r_t	        | jb                         t_        jd                  | jb                        }%t+        jT                  t_        j`                  |%d78             y|	d9v r*t+        jT                  | jV                  jX                         y|	d	v rt+        jT                  | jV                  jf                         y|	dk(  rti        |
d:      5 }&tO        |tP              rD|!D #cg c]  }#|#j]                          }$}#|&jk                  t_        j`                  |$d78             n4|&jk                  t_        j`                  | j]                         d78             ddd       y|	dk(  r1ti        |
d:      5 }&|&jk                  | jb                         ddd       y|	d9v r;ti        |
d:      5 }&|&jk                  | jV                  jX                         ddd       y|	d	v r;ti        |
d:      5 }&|&jk                  | jV                  jf                         ddd       yyyc c}#w c c}#w # 1 sw Y   yxY w# 1 sw Y   yxY w# 1 sw Y   yxY w# 1 sw Y   yxY w# tl        $ r#}'t+        j,                  to        |'            d}'~'ww xY w)<zfCrawl a website and extract content
    
    Simple Usage:
        crwl crawl https://example.com
    z[red]Profile 'z&' not found. Available profiles:[/red]z=[red]No profiles found. Create one with 'crwl profiles'[/red]Nr   Tr   z&[green]Using browser profile:[/green] )rJ  rK  pruningrR   gQ?)r   rE   	thresholdr   bm25rE   r^  g      ?use_stemming)
user_querybm25_thresholdr`  )content_filter)ra  r^  a  Analyze the web page content and extract structured data as JSON. 
If the page contains a list of items with repeated patterns, extract all items in an array. 
If the page is an article or contains unique content, extract a comprehensive JSON object with all relevant information.
Look at the content, intention of content, what it offers and find the data item(s) in the page.
Always return valid, properly formatted JSON.zAnalyze the web page content and extract structured data as JSON, following the below instruction and explanation of schema and always return valid, properly formatted JSON. 

Instruction:

)r?   	api_tokenrU  F)
llm_configinstructionrU  extraction_typeapply_chunkingforce_json_responser   r   rl   zExtraction type not specified)llmjson-css
json-xpathzInvalid extraction type: rj  r?   rd  z:LLM provider and API token are required for LLM extractionrf  )re  rf  rU  paramsrk  )rU  rl  rL     )	max_depthr[  rM  rN  z&[green]Deep crawling enabled:[/green] z strategy, max z pagesVERBOSEr   z%No results found during deep crawlingr   r   r   r3   r  )8r   get_profile_pathr   r   rU   r   r   r}   r   r   cloner   r   r:   markdown_generatorr   rA   r   r   r   extraction_strategyr;   r   r   r   r   BYPASS
cache_moder   scraping_strategyr   deep_crawl_strategyr   r   r0   r   r  r  r   
isinstancer   r   r<   rD   r   rX   r   rl   r   r   rm   fit_markdownr)   writer~   r   )(rC   rP  rQ  rR  rS  rT  rU  r  r   rV  rW  rX  rY  r   r   rZ  r[  r   r   r   r   r   filter_confr?   r@   default_instruction#default_instruction_with_user_queryrf  extract_confschema_datar1   rt   main_resultall_resultsrD   routput_dataextracted_itemsr.   r   s(                                           r/   	crawl_cmdr    sX   8 "$009--/Hwi7]^_&x0  ]^ G#/ )-%&MMB7)LMG+#(()9.)IJ&++,<^,LM ++++6g6K++++6g6K F&@@.}="v1K'K%!%
 6"f,1I%6#.??7#;'2{C'H%0__^T%J&2. V$	11I%9#.??7#;"-//+t"D&2. #.0OHe#1 3{  ~J  3J/ BN=SfK /D$h%H''/ ($$(/K+  +,=>L*62K  ##F+**+JKKF#+LL**-F|TZG[F\+]^^F#u,#''
3<;K;KK;X../kll2G 3(,z2JVbcnVop ,] ;&3 #&&x4	3/ f%32K&3/ f%52M&3/ %.%5%5K"(?(A% U"2F'3/ u$2F'3/ |+2K'3/
  FzlRabkallrst"$$jjE:$jjE:  %yy 
 fd#6{a

BC )K K !K!(K .0OHe"++88HII)3(HeT fd+;F"Ga1<<>"GK"GJJtzz+a@AJJtzz+*@*@*B1MN6!k334"&**[-J-J"K

4::oa@A--

;//<<=55

;//<<=+s+ Pq!&$/?J&K!q||~&K&K

;q AB

;+A+A+CA NOP P 6!+s+ ;qGGK99:; ;--+s+ ?qGGK00==>? ?55+s+ ?qGGK00==>? ? 6! 6 #H" 'LP P; ;? ?? ?  +""3q6**+s   N]# %A]# 6]# \)),]# 8]# A#]# 3-]# !.]# ]# !\36\.A\3*]# 3]# \? ]# )]# 9&]]# (]# 8&]]# )]# .\33\<8]# <]# ?]]# ]# ]]# ]# ] ]#  ]# #	^,^

^r   c                      t                y)Show usage examplesN)r   r  rB   r/   examples_cmdr    s	     OrB   c                       y)a  Manage global configuration settings
    
    Commands to view and update global configuration settings:
    - list: Display all current configuration settings
    - get: Get the value of a specific setting
    - set: Set the value of a specific setting
    Nr  r  rB   r/   
config_cmdr    s     	rB   r   c                  b   t               } t        dddd      }|j                  dd       |j                  d	d
       |j                  dd       |j                  dd       t        j                         D ]  \  }}| j                  ||d         }|}|j                  dd      r|rd}|d   dk(  r6t        |      j                         }t        |d         j                         }nt        |d         }|j                  |t        |      ||d           t        j                  |       y)zList all configuration settingszCrawl4AI ConfigurationTr   r   r   Settingr   r   Valuer   Defaultr   Descriptionwhiter   secretF********r   booleandescriptionN)r0   r   r   r   r   r:   r   re   r   r   rU   )r1   r   r   settingrs   display_valuedefault_values          r/   config_list_cmdr    s/     F0dQ\kqrE	Yf-	WG,	Yh/	]'2%++- 
W

3	 23 ;;x'E&M 6?i'J,,.M	 2399;M	 23MM"		

, MM%rB   r:   r   c                 p   t               }| j                         } | t        vrt        j	                  d|  d       y|j                  | t        |    d         }|}t        |    j                  dd      r|rd}t        j	                  d|  d	| d
       t        j	                  dt        |    d    d       y)z$Get a specific configuration setting[red]Error: Unknown setting ''[/red]Nr   r  Fr  z[cyan][/cyan] = [green][/green]z[dim]Description: r  z[/dim])r0   upperr   r   rU   r:   )r   r1   rs   r  s       r/   config_get_cmdr    s      F ))+C
-5cU'BCJJsM#.y9:E MSh.5"MMF3%0xHIMM&}S'9-'H&IPQrB   setrs   c                    t               }| j                         } | t        vrTt        j	                  d|  d       t        j	                  ddj                  t        j                                d       yt        |    }|d   dk(  r@|j                         d	v rd
}nl|j                         dv rd}nWt        j	                  d       y|d   dk(  r9|}d|v r3||d   vr,t        j	                  ddj                  |d          d       y|| <   t        |       |}|j                  dd      r|rd}t        j	                  d|  d| d       y)zSet a configuration settingr  r  z[yellow]Available settings: z, r.  Nr   r  )r]   yesr   yT)r^   no0nFz?[red]Error: Invalid boolean value. Use 'true' or 'false'.[/red]stringr   z"[red]Error: Value must be one of: r   r  r  z&[green]Successfully set[/green] [cyan]r  r  )
r0   r  r   r   rU   r   r   re   r5   r:   )r   rs   r1   r  typed_valuer  s         r/   config_set_cmdr  3  sT   
  F ))+C
-5cU'BC4TYY}?Q?Q?S5T4UU^_`C G v)#;;=55K[[]77KMM[]	H	$ E1C$CMM>tyyQZI[?\>]]cde F3Kv  M{{8U#"MM:3%?PQ^P__ghirB   c                  6    t        j                  t               y)zManage browser profiles interactively
    
    Launch an interactive browser profile manager where you can:
    - List all existing profiles
    - Create new profiles for authenticated browsing
    - Delete unused profiles
    N)r  r  r  r  rB   r/   profiles_cmdr  `  s     
IIorB   )r   z	--exampler  z,Enable deep crawling with specified strategyexamplec                    |rt                y| s8t        j                         }t        j                  |j	                                yt        j                         }|j                  t        | ||||||||	|
||||||       y)a  Crawl4AI CLI - Web content extraction tool

    Simple Usage:
        crwl https://example.com
    
    Run with --example to see detailed usage examples.
    
    Other commands:
        crwl profiles   - Manage browser profiles for identity-based crawling
        crwl crawl      - Crawl a website with advanced options
        crwl cdp        - Launch browser with CDP debugging enabled
        crwl browser    - Manage builtin browser (start, stop, status, restart)
        crwl config     - Manage global configuration settings
        crwl examples   - Show more usage examples
        
    Configuration Examples:
        crwl config list                         - List all configuration settings
        crwl config get DEFAULT_LLM_PROVIDER     - Show current LLM provider
        crwl config set VERBOSE true             - Enable verbose mode globally
        crwl config set BROWSER_HEADLESS false   - Default to visible browser
    N)rC   rP  rQ  rR  rS  rT  rU  r  r   rV  rX  rY  r   r   rZ  r[  )r   r;   get_current_contextr<   get_helpinvoker  )rC   r  rP  rQ  rR  rS  rT  rU  r  r   rV  rX  rY  r   r   rZ  r[  rq   s                     r/   r   r   l  s    V '')

3<<>" 
#
#
%CJJ%%#+!!#  rB   c                      dd l } t        | j                        dk  s| j                  d   t        j                  vr| j                  j                  dd       t                y )Nr   r   r\   rF  )r  r   argvr  commandsinsert)r  s    r/   mainr    s?    
388}qCHHQKs||;7#ErB   __main__)[r;   r   r  r   r   typingr   r   r   r   rl   r*   r  rich.consoler   
rich.tabler   
rich.panelr	   rich.promptr
   r   crawl4air   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   crawl4ai.configr   litellmr   pathlibr   r   dictr0   r5   tupler   rA   rX   rz   r   r   boolr   r   rg   r   r   r   r   r   r   r  groupr  r
  commandr  optionChoicer'  r+  r7  r;  rE  argumentr  r  r  r  r  r  r  r   r  __name__r  rB   r/   <module>r     sb    	 
   , ,        '    & *   )	'4 	't 
%S/ 23 # c S Y\ 04S> 2
P8C= 
PT 
P"8C= "T "
E3 E] EIY Edh EhTS S !T$sCx.%9 !FF F. 7  7D'R 6D,` 2T84DEF	 G	
 9		 		 X > W<5<<Y8O+PZd57h3;[\($=[\7C 7s 7d 7 ] ]7 
7r V' 'R Vgt"PQB(3- B R BH Y<5<<Y8O+PZ^?Ah3;ij($=[\Lhsm L8C= LT\]aTb L ] kA  
L\ U,uvh3;[\<5<<Y8O+PZd57lD/MNmT0ghA8C= A A3 ARV Acg A i O7 ] w AH W% $ZUZZt-DKlm $ZUZZt-DKlmJEJJd,CJfg#T


$0GNopeD  XU  Vj$ZUZZt%<C_`k4c4DKz{k4c4DKz{j$\U\\2m%nx}~ot*%**,=abtTHdelD'QRk4.k4&PQn<5<<0L#M  U]  ^m#r8mni+3 i+ i+S i+QT i+!i+14i+>Ai+LPi+[_i+i+%(i+8<i+HKi+VZi+ehi+vyi+ GJi+ o ^ R / S f c  | | a V q h n n & $i+V Z  8	 	 F   D E%R R & R* E%$'(j (jC (j ( & (jT Z	 	 "&k4.CD $ZUZZt-DKlm $ZUZZt-DKlmJEJJd,CJfg#T


$0GNopeD  XU  Vj$ZUZZt%<C_`k4c4DKz{k4c4DKz{j$\U\\2m%nx}~dA]^lD'QRk4.k4&PQn<5<<0L#M  UC  Dm#r8mn7 7t 7S 7# 7^a 77.17;>7IM7X\77#'7367AE7PS7ad7qt7 o D R / S _  | | a V q h n n E ' $7r
 zF rB   