o
    DhF*                    @   s  d dl Zd dlZd dlZd dlZd dlZd dlZd dlZd dl	Zd dl
Z
d dlZd dlZd dlZd dlZd dlZd dlZdZejeZejedZejedZejedZejedZejdZejdZd	Zd
ZdZ ejdZ!i Z"i Z#dZ$dZ%i Z&dd Z'G dd dej(j)Z*dd Z+e,dkre+  e-d e-d e-d e-d e-d e-d e.defe*Z/e-de  e/0  W d   dS 1 sw   Y  dS dS )    NA  ztaskmgr.htmlzauto-config.htmlz
about.htmlzandroid.html~/usr/local/binz~/appsz!http://berrystore.sw7ft.com/bins/z!http://berrystore.sw7ft.com/apps/z!http://berrystore.sw7ft.com/apks/z
~/.profilei,  
   c           	   
   C   s8  t   }|tv rt| \}}|| tk r|S zJtj| }tjj|td1}|jdkr@t	d|  d|j  	 W d   W dS |
  }||ft|< |W  d   W S 1 sYw   Y  W dS  tjjtjfy } z*t	d|  d|  |tv rt| \}}t	d|  |W  Y d}~S W Y d}~dS d}~ww )	z2Get data from cache or fetch from URL with timeouttimeout   zFailed to fetch 	: Status NzError fetching : zUsing stale cache for )timecacheCACHE_DURATIONurllibrequestRequesturlopenREQUEST_TIMEOUTstatusprintreaddecodeerrorURLErrorsocketr   )	url	cache_keycurrent_timecached_timecached_datar   responsedatae r!   _/var/www/vhosts/berrystore.sw7ft.com/httpdocs/apps/archive/taskapp-september-11-2025/taskapp.pyget_cached_or_fetch+   s2   
(r#   c                   @   sf  e Zd Zdd Zdd Zdd Zdd Zd	d
 Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd ZdXddZdd Zdd Zd d! Zd"d# Zd$d% Zd&d' Zd(d) ZdYd+d,Zd-d. ZdYd/d0ZdYd1d2Zd3d4 Zd5d6 Zd7d8 Zd9d: Zd;d< Zd=d> Z dYd?d@Z!dAdB Z"dCdD Z#dEdF Z$dGdH Z%dIdJ Z&dKdL Z'dMdN Z(dOdP Z)dQdR Z*dSdT Z+dUdV Z,dWS )ZTaskManagerHandlerc           	      C   s  t j| j}| jdrt j|j}|ddgd }|ddgd }|ddgd }|ddgd }|d	krE|rE| || n>|d
krQ|rQ| 	| n2|dkr^|r^| 
|| n%|dkrk|rk| || n|dkrx|rx| || n|dkr|r| | | jdd}| d | d| |   d S | jdr| d | dd |   |  }| j|  d S | jdkr|   d S | jdkr|   d S | jdkr|   d S | jdkr|   d S | jdkr|   d S | jdkr|   d S | jdkr|   d S | jdr|   d S | jdr'|   d S | jd kr3|   d S | d | dd |   |   }| j|  d S )!Nz/actionaction r   app_namepidapp_typeclistartstopinstalldeleteenable_auto_startdisable_auto_startReferer/i/  Locationz/auto-configr   Content-type	text/htmlz/aboutz/androidz/runningz
/installedz
/availablez/api/available-cliz/api/available-webz/api/app-detailsz/api/app-errorsz/news)!r   parseurlparsepath
startswithparse_qsqueryget	start_appstop_appinstall_app
delete_appenable_auto_start_appdisable_auto_start_appheaderssend_responsesend_headerend_headersgenerate_auto_config_htmlwfilewriteencodeserve_about_pageserve_android_pageserve_running_appsserve_installed_appsserve_available_appsserve_available_cli_jsonserve_available_web_jsonserve_app_detailsserve_app_errorsserve_news_pagegenerate_html)	selfparsed_pathparamsr%   r'   r(   r)   refererhtmlr!   r!   r"   do_GETK   sl   








zTaskManagerHandler.do_GETc              
   C   ~   z|   }| d | dd |   | j|  W dS  ty> } ztd|  | 	dd W Y d}~dS d}~ww )z1Serve available CLI apps as JSON for lazy loadingr   r4   r5   z"Error serving available CLI apps:   zError loading CLI appsN)
generate_available_cli_htmlrD   rE   rF   rH   rI   rJ   	Exceptionr   
send_errorrV   rZ   r    r!   r!   r"   rP         
z+TaskManagerHandler.serve_available_cli_jsonc              
   C   r\   )z1Serve available web apps as JSON for lazy loadingr   r4   r5   z"Error serving available web apps: r]   zError loading web appsN)
 generate_available_web_apps_htmlrD   rE   rF   rH   rI   rJ   r_   r   r`   ra   r!   r!   r"   rQ      rb   z+TaskManagerHandler.serve_available_web_jsonc              
   C   r\   )z*Serve running apps HTML for BB10 interfacer   r4   r5   zError serving running apps: r]   zError loading running appsN)
generate_processes_htmlrD   rE   rF   rH   rI   rJ   r_   r   r`   ra   r!   r!   r"   rM      rb   z%TaskManagerHandler.serve_running_appsc              
   C   r\   )z,Serve installed apps HTML for BB10 interfacer   r4   r5   zError serving installed apps: r]   zError loading installed appsN)
generate_installed_apps_htmlrD   rE   rF   rH   rI   rJ   r_   r   r`   ra   r!   r!   r"   rN      rb   z'TaskManagerHandler.serve_installed_appsc              
   C   s   z9|   }|  }|rd|v r|rd|v rd}n|pd|pd }| d | dd |   | j|  W d
S  tyY } zt	d|  | 
dd	 W Y d
}~d
S d
}~ww )z,Serve available apps HTML for BB10 interfacez
bb10-emptyu(  
                <div class="bb10-empty">
                    <div class="bb10-empty-icon">📦</div>
                    <div class="bb10-empty-text">No Apps Available</div>
                    <div class="bb10-empty-subtext">All available apps are already installed</div>
                </div>r&   r   r4   r5   zError serving available apps: r]   zError loading available appsN)r^   rc   rD   rE   rF   rH   rI   rJ   r_   r   r`   )rV   cli_htmlweb_htmlcombined_htmlr    r!   r!   r"   rO      s   
z'TaskManagerHandler.serve_available_appsc           
   
   C   s  zct j| j}t j|j}|ddgd }|ddgd }|s,| dd W dS | ||}t j	|d	 }d
| d| }||d}| 
d | dd |   | jt|  W dS  ty }	 ztd|	  | dd W Y d}	~	dS d}	~	ww )z/Serve app details including overview.md contentr'   r&   r   r)   r*     App name requiredNz.zipz /action?action=install&app_name=
&app_type=)descriptioninstall_urlr   r4   application/jsonzError serving app details: r]   zError loading app details)r   r6   r7   r8   r:   r;   r<   r`   get_app_descriptionquoterD   rE   rF   rH   rI   jsondumpsrJ   r_   r   )
rV   rW   rX   r'   r)   rl   app_encodedrm   r   r    r!   r!   r"   rR      s.   
z$TaskManagerHandler.serve_app_detailsc                 C   s  zmt j| j}t j|j}|ddgd }|s#| dd W dS t|d}|rLd|dd	|d
d|dd|dd| 	|dd	d}nddd}| 
d | dd |   | jt|  W dS  ty } ztd|  | dd W Y d}~dS d}~ww )z.Serve app startup errors for user notificationr'   r&   r   ri   rj   NTtypeunknownmessagezUnknown error occurreddetails	timestamp)	has_error
error_typeerror_messageerror_detailsrx   suggestionsFzNo errors found for this app)ry   rv   r   r4   rn   zError serving app errors: r]   zError loading app errors)r   r6   r7   r8   r:   r;   r<   r`   
app_errorsget_error_suggestionsrD   rE   rF   rH   rI   rq   rr   rJ   r_   r   )rV   rW   rX   r'   
error_infor   r    r!   r!   r"   rS      s8   





z#TaskManagerHandler.serve_app_errorsc                 C   s:   g dg dg dg dg dg dd}| ||d S )	z1Get user-friendly suggestions based on error type)z*Install missing Python packages using pip3z0Check if all required dependencies are installedz&Verify the app was installed correctly)z,Check file permissions for the app directoryz"Ensure the app.py file is readable Try reinstalling the application)z+Check if another app is using the same portz!Verify the PORT setting in app.pyzTry restarting the Task Manager)z The app may have corrupted filesr   z,Check if the app is compatible with Python 3)z#The app is taking too long to startz#Check system resources (CPU/Memory)zTry starting the app again)zTry restarting the applicationzCheck the app installationz%Contact the app developer for support)import_errorpermission_error
port_errorsyntax_errortimeout_errorru   ru   )r<   )rV   rz   r}   r!   r!   r"   r   &  s    z(TaskManagerHandler.get_error_suggestionsc                 C   s:   |||t j   dt|< td| d| d|  dS )z1Record an app startup error for user notification)rt   rv   rw   rx   zRecorded error for r	   z - N)datetimenow	isoformatr~   r   )rV   r'   rz   rv   rw   r!   r!   r"   record_app_errorJ  s   
z#TaskManagerHandler.record_app_errorc                 C   s   |  }d|v sd|v r"td|}|r |d}dd| fS dS d|v r(d	S d
|v s0d|v r2dS d|v s:d|v r<dS d|v rBdS d|v rHdS | r\|dd  }dd| fS dS )z8Analyze Python error output to categorize the error typemodulenotfounderrorimporterrorz#no module named ['\"]([^'\"]+)['\"]   r   zMissing Python module: )r   zMissing Python dependenciessyntaxerror)r   z&Python syntax error in the applicationpermissionerrorzpermission denied)r   z-Permission denied accessing application fileszaddress already in usebind)r   z-Port is already in use by another applicationfilenotfounderror)file_not_foundz&Required application files are missingr   )r   zApplication startup timed out
r   runtime_errorzRuntime error: )ru   z/Application failed to start for unknown reasons)lowerresearchgroupstripsplit)rV   stderr_textstdout_textstderr_lowerimport_matchmodule_name
first_liner!   r!   r"   analyze_python_errorT  s*   
z'TaskManagerHandler.analyze_python_errorc                 C   sP   zt  t jt j}|d |d|f}|  |dkW S  ty'   Y dS w )z!Check if a port is already in user   	127.0.0.1r   F)r   AF_INETSOCK_STREAM
settimeout
connect_excloser_   )rV   portsockresultr!   r!   r"   is_port_in_usew  s   

z!TaskManagerHandler.is_port_in_use   c                 C   sL   ddl }|  }|  | |k r$| |rdS |d |  | |k sdS )z>Verify that an app is actually listening on the specified portr   NTg      ?F)r
   r   sleep)rV   r   r   r
   
start_timer!   r!   r"   verify_app_listening  s   

z'TaskManagerHandler.verify_app_listeningc           	   
   C   sP  z\|dkrt jt| d}n
t jt| d}t j|}t jj|td*}|j	dkrD|
 d}| |}|W  d   W S d| dW  d   W S 1 sUw   Y  W dS  t jjt jjtjfy } ztd	| d
|  d| dW  Y d}~S d}~w ty } ztd| d
|  d| dW  Y d}~S d}~ww )z
        Fetch app description from overview.md file on the server.
        Returns formatted HTML description or fallback text.
        r*   z/overview.mdr   r   utf-8Nz <p>No description available for z.</p>zError fetching overview.md for r	   z!Error processing overview.md for )r   r6   urljoinAVAILABLE_APPS_URLWEB_APPS_URLr   r   r   r   r   r   r   markdown_to_htmlr   r   	HTTPErrorr   r   r   r_   )	rV   r'   r)   overview_urlr   r   contenthtml_contentr    r!   r!   r"   ro     s,   


(
z&TaskManagerHandler.get_app_descriptionc              
   C   s$  z| d}g }d}|D ]}| }|s#|r|d d}|d q|dr6|d|dd  d	 q|d
rI|d|dd  d q|dr\|d|dd  d q|dsf|dr}|so|d d}|d|dd  d qd|v rd|v rd|v rddl}|dd|}|d| d qd |v r|d d!d"d d#d"}|d| d qd$|v r|d$d%d"d$d&d"}|d| d q|r|d d}|d| d q|r|d d|W S  ty } zt	d'|  d| dW  Y d}~S d}~ww )(z
        Convert basic markdown to HTML for display in modal.
        Handles common markdown elements like headers, paragraphs, lists, and links.
        r   F</ul>z<br>z# z<h2>   Nz</h2>z## z<h3>   z</h3>z### z<h4>   z</h4>z- z* <ul>Tz<li>z</li>[z]()r   z\[([^\]]+)\]\(([^)]+)\)z#<a href="\2" target="_blank">\1</a>z<p>z</p>z**z<strong>r   z	</strong>*z<em>z</em>z#Error converting markdown to HTML: )
r   r   appendr9   r   subreplacejoinr_   r   )rV   markdown_textlines
html_linesin_listliner   r    r!   r!   r"   r     sZ   








z#TaskManagerHandler.markdown_to_htmlc              
   C   s   z#|   }| |}| d | dd |   | j|  W dS  tyC } zt	d|  | 
dd W Y d}~dS d}~ww )z/Serve the news page with content from news.jsonr   r4   r5   zError serving news page: r]   zError loading newsN)load_news_datagenerate_news_htmlrD   rE   rF   rH   rI   rJ   r_   r   r`   )rV   	news_data	news_htmlr    r!   r!   r"   rT     s   

z"TaskManagerHandler.serve_news_pagec              
   C   s   z3t jtd}t j|sg ddW S t|ddd}t|W  d   W S 1 s,w   Y  W dS  tyQ } zt	d|  g ddW  Y d}~S d}~ww )	z"Load news data from news.json filez	news.jsonr&   )newslast_updatedrr   )encodingNzError loading news data: )
osr8   r   BASE_DIRexistsopenrq   loadr_   r   )rV   news_file_pathfr    r!   r!   r"   r     s   (z!TaskManagerHandler.load_news_datac           
      C   s   zYd}| dr?|d D ]1}| |d }| dd}| dd}| dd}|d	|d
  d| d| d| d| d7 }qn|d7 }| dd}|rS|d| d7 }|d7 }|W S  tys }	 ztd|	  W Y d}	~	dS d}	~	ww )zGenerate HTML for the news pagea&  <!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="initial-scale=1, user-scalable=no">
    <title>BB10 News</title>
    <style>
        body {
            background-color: #1e1e1e;
            color: white;
            font-family: "Slate Pro", Slate, "Myriad Pro", Helvetica, sans-serif;
            font-size: 14px;
            margin: 0;
            padding: 0;
        }
        .header {
            background-color: #2b2b2b;
            border-bottom: 2px solid #00769e;
            padding: 10px 20px;
            color: white;
            height: 60px;
            line-height: 40px;
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            z-index: 1000;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        h1 {
            font-size: 20px;
            flex-grow: 1;
            text-align: center;
            margin: 0;
        }
        .hamburger {
            cursor: pointer;
            width: 30px;
            height: 25px;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
        }
        .hamburger div {
            width: 100%;
            height: 4px;
            background-color: white;
            border-radius: 2px;
        }
        .dropdown {
            position: relative;
            display: inline-block;
        }
        .dropdown-content {
            display: none;
            position: absolute;
            right: 0;
            top: 35px;
            background-color: #2b2b2b;
            min-width: 160px;
            box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
            z-index: 1001;
            border: 1px solid #00769e;
            border-radius: 4px;
        }
        .dropdown-content a {
            color: white;
            padding: 12px 16px;
            text-decoration: none;
            display: block;
        }
        .dropdown-content a:hover {
            background-color: #00769e;
        }
        .dropdown:hover .dropdown-content {
            display: block;
        }
        .content {
            padding: 80px 20px 20px 20px;
        }
        .news-item {
            background-color: #2b2b2b;
            border: 1px solid #00769e;
            border-radius: 8px;
            margin-bottom: 20px;
            padding: 20px;
        }
        .news-header {
            border-bottom: 1px solid #00769e;
            padding-bottom: 10px;
            margin-bottom: 15px;
        }
        .news-title {
            font-size: 18px;
            font-weight: bold;
            color: #00769e;
            margin: 0 0 5px 0;
        }
        .news-meta {
            font-size: 12px;
            color: #888;
        }
        .news-category {
            display: inline-block;
            background-color: #00769e;
            color: white;
            padding: 2px 8px;
            border-radius: 4px;
            font-size: 10px;
            margin-left: 10px;
        }
        .news-content {
            line-height: 1.6;
        }
        .news-content p {
            margin: 0 0 10px 0;
        }
        .news-content ul {
            margin: 10px 0;
            padding-left: 20px;
        }
        .news-content li {
            margin: 5px 0;
        }
        .news-content strong {
            color: #00769e;
        }
        .no-news {
            text-align: center;
            color: #888;
            font-style: italic;
            padding: 40px 20px;
        }
        .last-updated {
            text-align: center;
            color: #888;
            font-size: 12px;
            margin-top: 20px;
            padding-top: 20px;
            border-top: 1px solid #333;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>BB10 News</h1>
        <div class="dropdown">
            <div class="hamburger">
                <div></div>
                <div></div>
                <div></div>
            </div>
            <div class="dropdown-content">
                <a href="/">Back to Task Manager</a>
                <a href="/auto-config">Auto Start Apps</a>
                <a href="/android">Android Apps</a>
                <a href="/about">About Sw7ft</a>
            </div>
        </div>
    </div>
    <div class="content">
        <h2>Latest Updates</h2>r   r   dateUnknownauthorcategorygeneralzo
        <div class="news-item">
            <div class="news-header">
                <div class="news-title">titlezC</div>
                <div class="news-meta">
                    z by z1
                    <span class="news-category">zi</span>
                </div>
            </div>
            <div class="news-content">
                z"
            </div>
        </div>z
        <div class="no-news">
            <p>No news available at the moment.</p>
            <p>Check back soon for updates!</p>
        </div>r   r&   z>
        <div class="last-updated">
            Last updated: z
        </div>z
    </div>
</body>
</html>zError generating news HTML: Nz5<html><body><h1>Error loading news</h1></body></html>)r<   r   r_   r   )
rV   r   rZ   	news_itemcontent_htmldate_str
author_strcategory_strr   r    r!   r!   r"   r     sD    
&

z%TaskManagerHandler.generate_news_htmlc              
   C   s   z3t td}| }W d   n1 sw   Y  | d | dd |   | j|  W dS  t	yS } zt
d|  | dd W Y d}~dS d}~ww )	zServe the about.html page.r   Nr   r4   r5   zError loading about.html: r]   zError loading about.html)r   ABOUT_TEMPLATE_PATHr   rD   rE   rF   rH   rI   rJ   r_   r   r`   )rV   filerZ   r    r!   r!   r"   rK     s   

z#TaskManagerHandler.serve_about_pagec              
   C   s   z_|   }ttd}| }W d   n1 sw   Y  |s#d}nd}|D ]}tjt|}|d| d| d7 }q'|d7 }|d	|}| 	d
 | 
dd |   | j|  W dS  ty } ztd|  | dd W Y d}~dS d}~ww )zMServe the android.html page, after populating it with the list of .apk files.r   Nz<p>No APKs found.</p>r   z<li><a href="z" target="_blank">z	</a></li>r   z<!-- apks_list -->r   r4   r5   zError serving android.html: r]   zError serving android.html)fetch_android_apksr   ANDROID_TEMPLATE_PATHr   r   r6   r   APKS_URLr   rD   rE   rF   rH   rI   rJ   r_   r   r`   )rV   apksr   rZ   	apks_htmlapkapk_linkr    r!   r!   r"   rL     s,   

z%TaskManagerHandler.serve_android_pager*   c                 C   s&  zs|dkrt jt|}t}n|dkrt jt|}t}n
td|  W d S td|  t j	|}t jj
|td&}|jdkrXtd| d|j  	 W d    W d S | }W d    n1 sfw   Y  tj|s}t| td	|  tj||}t|d
}	|	| W d    n1 sw   Y  td| d|  t|dZ}
|dkrtj|d }tj||}tj|dd |
| td| d|  n'tjd}tjd}|
 D ]}|drM|dd  }|rLtj||}tjtj|dd |
|%}t|d
}||  W d    n	1 s-w   Y  W d    n	1 s=w   Y  td| d|  q|dr|dd  }|rtj||}tjtj|dd |
|%}t|d
}||  W d    n	1 sw   Y  W d    n	1 sw   Y  td| d|  q|dstj||}tjtj|dd |
|%}t|d
}||  W d    n	1 sw   Y  W d    n	1 sw   Y  td| d|  qW d    n	1 sw   Y  |dkrf|   |
 }|D ]D}|dsd|dr;tj||dd  }ntj||}tj|rdt|t j!t j"B t j#B t j$B t j%B  td| d q!t&| td|  W d S  t'y } ztd| d|  W Y d }~d S d }~ww ) Nr*   webzUnknown app type:zDownloading r   r   zFailed to download r   z#Created installation directory at: wbzDownloaded z to r   r   T)exist_okz
Extracted ~/usr/local/libr   zlib/r   zExtracted library: zbin/zExtracted binary: r2   zExtracted file: zMade z executablezRemoved temporary zip file: zError installing app r	   )(r   r6   r   r   CLI_APPS_DIRr   WEB_APPS_DIRr   r   r   r   r   r   r   r   r8   r   makedirsr   r   rI   zipfileZipFilesplitext
extractall
expandusernamelistr9   dirnameendswithensure_cli_pathschmodstatS_IRWXUS_IRGRPS_IXGRPS_IROTHS_IXOTHremover_   )rV   r'   r)   download_urlinstall_dirr   r   zip_datazip_pathr   zip_ref
app_folderextract_pathlib_dirbin_dirmemberlib_filetarget_pathsourcetargetbin_fileextracted_filesr   	file_pathr    r!   r!   r"   r?     s   



   
*&
"zTaskManagerHandler.install_appc                 C   s>   t tjdfD ]}tj|st| td|  qdS )zEnsure CLI paths existr   zCreated directory: N)r   r   r8   r   r   r   r   )rV   r8   r!   r!   r"   r   q  s   
z#TaskManagerHandler.ensure_cli_pathsc              
   C   s   zM|dkr$t jt|}t j|r!t | td|  W d S W d S |dkrHt jt|}t j|rKdd l}|	| td|  W d S W d S W d S  t
yj } ztd| d|  W Y d }~d S d }~ww )Nr*   zDeleted CLI app: r   r   zDeleted web app: zError deleting app r	   )r   r8   r   r   r   r  r   r   shutilrmtreer_   )rV   r'   r)   app_pathr  r    r!   r!   r"   r@   x  s&   

"zTaskManagerHandler.delete_appc              
   C   s"  zd|t v r	t |= |dkrtjt|d}tj|s1| |dd| d td|  W dS d}z5t|d&}|	 }t
d	|}|rXt|d
}td| d| d W d   n1 sbw   Y  W n( ty } z| |ddt| d td|  W Y d}~W dS d}~ww |s| |ddd td| d W dS | |r| |dd| dd| d td| d W dS ztjd|gtjtjtj|d}	td| d|	j  |t|	j< td| d|  td |	 durK|	 \}
}|
jd d!d" }|jd d!d" }| ||\}}| |||| td#|	j d$ td%|  td&|  |	jtv rGt|	j= W W dS | j|d'd(s|	 \}
}|
jd d!d" }|jd d!d" }| |d)d*| d+| d,|  W W dS td-| d.|  W W d/S  ty } z| |d0d1t| d td1|  W Y d}~W dS d}~ww tjt|}tj|s| |dd2| d td2|  W dS zbt |t!|j"t!j#B  tj|gtjtjd3}	td4| d|	j  td
 |	 dur9|	 \}
}|
jd d!d" }|jd d!d" }|r9| ||\}}| |||| W W dS W W d/S  tye } z| |d0d5t| d td5|  W Y d}~W dS d}~ww  ty } z| |d0d6t| d td7| d8|  W Y d}~dS d}~ww )9zGEnhanced start_app with comprehensive error detection and user feedbackr   app.pyr   zWeb app not found: r&   FNr   PORT\s*=\s*(\d+)r   zFound port z in z/app.pyr   zError reading app.py: z Error reading port from app.py: r   z+Could not find PORT configuration in app.pyz7Make sure your app.py contains a line like: PORT = 8080zCould not find PORT in zPort z) is already in use by another applicationz.Try stopping other apps or change the PORT in z is already in usepython3)stdoutstderrcwdzStarted web app: z with PID: zUsing port z for r   r   ignore)errorszERROR: Process z failed to start!zProcess output: zProcess errors: r   r   startup_errorz)App started but is not listening on port zstdout: z	
stderr: zSuccessfully started z	 on port Tsystem_errorzFailed to start process: zCLI app not found: )r  r  zStarted CLI app: zFailed to start CLI app: zUnexpected error starting app: zError starting app r	   )$r~   r   r8   r   r   r   r   r   r   r   r   r   intr   r_   strr   
subprocessPopenPIPEr   r(   	app_portsr
   r   pollcommunicater   r   r   r   r   r   r   st_modeS_IXUSR)rV   r'   r)   r  r   r   r   
port_matchr    processr  r  r   r   rz   r{   r!   r!   r"   r=     s   








zTaskManagerHandler.start_appc                 C   s  z4t d| d|  tjt|d}tj|rzt|d}| }ddl}g d}|D ]l\}}	|	|||j
}
|
rzRt|
d}d	|  krOd
krn n=t d| d|	 d ttjtj}|d |d|f}|  |dkrt d| d |W   W  d   W W S W q/ ttfy   Y q/w q/W d   n1 sw   Y  W n ty } zt d|  W Y d}~nd}~ww ztjddgdtjd }| d}|D ]S}t||v r0d|v r0| }|D ]>}d|v r/z*t|dd }d	|  krd
kr!n nt d| d |W     W W S W q ttfy.   Y qw qqg }|D ]D}d|v rx| }|D ]5}d|v rvzt|dd }d|  kr_dkrfn n|| W qB ttfyu   Y qBw qBq5t|dkrt d|d   |d W W S |rt d|  d|v rW W dS t|W W S W n ty } zt d |  W Y d}~nd}~ww g d!}g }|D ]2}z%ttjtj}|d |d|f}|  |dkr|| W q ty   Y qw t|dkrt d"|d   |d W S |r,t d#|  d|v r'W dS t|W S t d$|  W dS  tyS } zt d%| d&|  W Y d}~dS d}~ww )'z[
        Detect the actual port a web app is running on by checking netstat output
        zDEBUG: Detecting port for PID z, app r  r   r   N))r  zPORT constant)zport\s*=\s*(\d+)zport variable)z'server_address\s*=\s*\([^,]+,\s*(\d+)\)zserver_address tuple)z!HTTPServer\(\s*\([^,]+,\s*(\d+)\)zHTTPServer tuple)zapp\.run\([^)]*port\s*=\s*(\d+)zapp.run)zTCPServer\([^)]*(\d+)\)	TCPServerr   i   i  zDEBUG: Found port z in app.py (r   g?r   zDEBUG: Verified port z
 is activezError parsing app.py for port: netstatz-anr   r   r  r   LISTEN:z via netstat (PID match)@  i(#  z(DEBUG: Found single listening web port: z!DEBUG: Found multiple web ports: zDEBUG: netstat check failed: )r5  i  r   iB  iC  iD  iE  iF  iG  iH  iI  iJ  z5DEBUG: Found single listening port via socket check: z.DEBUG: Multiple ports found via socket check: zDEBUG: No port found for PID zError detecting port for PID r	   ) r   r   r8   r   r   r   r   r   r   r   
IGNORECASEr#  r   r   r   r   r   r   r   
ValueError
IndexErrorr_   r%  check_outputDEVNULLr   r   r   r$  r   lenmin)rV   r(   r'   r  r   r   r   patternspatterndescr-  r   r   r   r    outputr   r   partspart	web_portscommon_portslistening_portsr!   r!   r"   detect_app_port  s   	












z"TaskManagerHandler.detect_app_portc              
   C   s~   z!t t|tj td|  t|tv rtt|= W d S W d S  ty> } ztd| d|  W Y d }~d S d }~ww )NzStopped app with PID: zError stopping app r	   )r   killr#  signalSIGTERMr   r(  r_   )rV   r(   r    r!   r!   r"   r>     s   "zTaskManagerHandler.stop_appc           	   
   C   s   zZt td}| }W d    n1 sw   Y  |  }|  }d}d}|d ur,|nd}|d ur4|nd}|d|}|d|}|d|}|d|}d	}|d
|d
 }|W S  tyt } ztd|  W Y d }~dS d }~ww )Nr   z9<div id="available-cli-loading">Loading CLI apps...</div>z9<div id="available-web-loading">Loading web apps...</div>r&   z<!-- running_processes -->z<!-- installed_apps -->z<!-- available_cli_apps -->z<!-- available_web_apps -->a  
            <script>
            // ES5 compatible lazy loading
            function loadAvailableApps() {
                // Load CLI apps
                var cliContainer = document.getElementById('available-cli-loading');
                if (cliContainer) {
                    var xhr1 = new XMLHttpRequest();
                    xhr1.open('GET', '/api/available-cli', true);
                    xhr1.onreadystatechange = function() {
                        if (xhr1.readyState === 4) {
                            if (xhr1.status === 200) {
                                cliContainer.innerHTML = xhr1.responseText;
                            } else {
                                cliContainer.innerHTML = '<p>Error loading CLI apps</p>';
                            }
                        }
                    };
                    xhr1.send();
                }

                // Load web apps
                var webContainer = document.getElementById('available-web-loading');
                if (webContainer) {
                    var xhr2 = new XMLHttpRequest();
                    xhr2.open('GET', '/api/available-web', true);
                    xhr2.onreadystatechange = function() {
                        if (xhr2.readyState === 4) {
                            if (xhr2.status === 200) {
                                webContainer.innerHTML = xhr2.responseText;
                            } else {
                                webContainer.innerHTML = '<p>Error loading web apps</p>';
                            }
                        }
                    };
                    xhr2.send();
                }
            }

            // Load available apps after page loads
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', loadAvailableApps);
            } else {
                loadAvailableApps();
            }
            </script>
            z</body>zError loading HTML template: z><html><body><h1>Error loading HTML template</h1></body></html>)r   HTML_TEMPLATE_PATHr   rd   re   r   r_   r   )	rV   r   rZ   processes_htmlinstalled_apps_htmlavailable_cli_htmlavailable_web_htmllazy_scriptr    r!   r!   r"   rU     s,   
/z TaskManagerHandler.generate_htmlc              
   C   s  zd|v r| d}t|dkr|d  dd }|W S nVd|v r8| d}t|dkr7|d   d }|W S n;d|v rsd|v rX| d}t|dkrW|d  dd }|W S nd|v rs| d}t|dkrs|d  dd }|W S d|vrd	| vr|  d }|W S |dr|dd
d}tj|}|r|W S |  rtj|  d W S dW S  t	y } zt
d| d|  W Y d}~dS d}~ww )a  
        Extract a user-friendly app name from a command string.
        Examples:
        - 'python3 /data/apps/myapp/app.py' -> 'myapp'
        - '/usr/local/bin/tool' -> 'tool'
        - 'python3 /data/apps/webserver/app.py' -> 'webserver'
        - 'myapp' -> 'myapp'
        /data/apps/r   r2   r   /usr/local/bin/r  z/apps//data/pythonr&   zUnknown Appz(Error extracting app name from command 'z': N)r   r;  r   r   r   rstripr   r8   basenamer_   r   )rV   cmdrA  app_partr'   	clean_cmdr    r!   r!   r"   extract_app_name_from_command  sN   	




$z0TaskManagerHandler.extract_app_name_from_commandc              
   C   s@  z|   }tdt| d |sW dS d}|D ]j\}}| |}t|}d }|tv r0t| }n$d|v s8d|v rT| ||}|rT|t|< |}td| d| d	| d
 |r\d| dnd}	|d|d   d| d|	 d7 }|rz|d| d7 }|d| d7 }q|W S  ty }
 ztd|
  W Y d }
~
dS d }
~
ww )NzDEBUG: Found z
 processesu  
                <div class="bb10-empty">
                    <div class="bb10-empty-icon">⚡</div>
                    <div class="bb10-empty-text">No Running Apps</div>
                    <div class="bb10-empty-subtext">Start an app to see it here</div>
                </div>r&   r  rP  zDetected port z for existing process z (r   z,<div class="bb10-running-address">127.0.0.1:z</div>uA   <div class="bb10-running-address" style="opacity: 0.3;">—</div>zr
                <div class="bb10-running-item bb10-slide-in">
                    <div class="bb10-running-icon">r   zr</div>
                    <div class="bb10-running-info">
                        <div class="bb10-running-name">z</div>
                        zR
                    </div>
                    <div class="bb10-running-actions">z3
                        <a href="http://127.0.0.1:z." target="_blank" class="bb10-link">LAUNCH</a>zM
                        <div class="bb10-toggle active" onclick="toggleApp('z', true)">
                            <div class="bb10-toggle-handle"></div>
                        </div>
                    </div>
                </div>z!Error generating processes HTML: zX<div class="bb10-empty"><div class="bb10-empty-text">Error loading processes</div></div>)	get_python_processesr   r;  rY  r#  r(  rF  upperr_   )rV   	processes
cards_htmlr(   rV  r'   pid_intr   detected_portaddress_infor    r!   r!   r"   rd   "  sN   



	
z*TaskManagerHandler.generate_processes_htmlc           
   
   C   s   zX|  td}|  td}|| }|sW dS d}|D ];\}}tj|}|dkr*dnd}|d|d   d	| d
| d7 }|dkrJ|d| d7 }|d| d| d7 }q|W S  tyr }	 ztd|	  W Y d }	~	dS d }	~	ww )Nr*   r   u  
                <div class="bb10-empty">
                    <div class="bb10-empty-icon">📱</div>
                    <div class="bb10-empty-text">No Installed Apps</div>
                    <div class="bb10-empty-subtext">Install apps to see them here</div>
                </div>r&   zWeb AppzCLI Appzi
                <div class="bb10-app-card bb10-fade-in">
                    <div class="bb10-app-icon">r   6</div>
                    <div class="bb10-app-name">z8</div>
                    <div class="bb10-app-status">z9</div>
                    <div class="bb10-app-actions">z@
                        <a href="/action?action=start&app_name=z4&app_type=web" class="bb10-button success">Start</a>zA
                        <a href="/action?action=delete&app_name=rk   zY" class="bb10-button danger">Delete</a>
                    </div>
                </div>z&Error generating installed apps HTML: z]<div class="bb10-empty"><div class="bb10-empty-text">Error loading installed apps</div></div>)	scan_apps_directoryr   r   r   r6   rp   r[  r_   r   )
rV   cli_appsweb_appsall_appsr]  appr)   rs   display_typer    r!   r!   r"   re   [  s@   


z/TaskManagerHandler.generate_installed_apps_htmlc              
      sV  z|dkrdt jt|d d ndt jt| d ttd}| }W d    n1 s1w   Y  d| d}d| d}t fd	d
|D rXt	d| d W d S ttd}|
d| d |
  |
| d W d    n1 s~w   Y  t	d| d W d S  ty } zt	d| d|  W Y d }~d S d }~ww )Nr   	python3 "r  z" &
r   # <<< Auto-Start for  >>># <<< End Auto-Start for c                 3   s     | ]}   |  v V  qd S N)r   ).0r   app_commandr!   r"   	<genexpr>  s    z;TaskManagerHandler.enable_auto_start_app.<locals>.<genexpr>zAuto-start for z is already enabled.ar   zEnabled auto-start for .zError enabling auto start for r	   )r   r8   r   r   r   r   PROFILE_FILE	readlinesanyr   rI   r_   )rV   r'   r)   r   profile_contentsAUTO_START_MARKER_STARTAUTO_START_MARKER_ENDr    r!   rn  r"   rA     s,   

"z(TaskManagerHandler.enable_auto_start_appc           
   
   C   s4  z|t jtstt d W d S d| d}d| d}ttd}| }W d    n1 s1w   Y  g }d}|D ]}| |krGd}q<| |krPd}q<|sW|| q<ttd}|	| W d    n1 smw   Y  td	| d
 W d S  t
y }	 ztd| d|	  W Y d }	~	d S d }	~	ww )Nz does not exist.ri  rj  rk  r   FTwzDisabled auto-start for rr  zError disabling auto start for r	   )r   r8   r   rs  r   r   rt  r   r   
writelinesr_   )
rV   r'   rw  rx  r   profile_linesupdated_linesskipr   r    r!   r!   r"   rB     s:   

"z)TaskManagerHandler.disable_auto_start_appc                       zS|   }|d u rW dS tdd | tdD   fdd|D }|s'W dS d}|D ]%}tj|d	 }tj	|}|d
| d|d	 
  d| d| d	7 }q+|W S  tym } ztd|  W Y d }~dS d }~ww )Nze<div class="bb10-empty"><div class="bb10-empty-text">Error loading Command Line Utilities</div></div>c                 s       | ]\}}|V  qd S rl  r!   rm  rf  _r!   r!   r"   rp        zATaskManagerHandler.generate_available_cli_html.<locals>.<genexpr>r*   c                    8   g | ]}t j|d   vrt j|d  dkr|qS r   taskappr   r8   r   rm  zinstalled_cli_appsr!   r"   
<listcomp>     8 zBTaskManagerHandler.generate_available_cli_html.<locals>.<listcomp>u2  
                <div class="bb10-empty">
                    <div class="bb10-empty-icon">⚙️</div>
                    <div class="bb10-empty-text">No CLI Apps Available</div>
                    <div class="bb10-empty-subtext">All available CLI apps are already installed</div>
                </div>r&   r   M
                <div class="bb10-app-card bb10-fade-in" onclick="openModal('zT', 'cli')" style="cursor: pointer;">
                    <div class="bb10-app-icon">ra  z</div>
                    <div class="bb10-app-status">CLI App</div>
                    <div class="bb10-app-actions">
                        <a href="/action?action=install&app_name=z`&app_type=cli" class="bb10-button">Install</a>
                    </div>
                </div>z*Error generating available CLI apps HTML: )fetch_available_zipssetrb  r   r   r8   r   r   r6   rp   r[  r_   r   rV   available_zips	apps_htmlzip_filer'   rs   r    r!   r  r"   r^     6   
	z.TaskManagerHandler.generate_available_cli_htmlc                    r~  )NzW<div class="bb10-empty"><div class="bb10-empty-text">Error loading Web Apps</div></div>c                 s   r  rl  r!   r  r!   r!   r"   rp    r  zFTaskManagerHandler.generate_available_web_apps_html.<locals>.<genexpr>r   c                    r  r  r  r  installed_web_appsr!   r"   r    r  zGTaskManagerHandler.generate_available_web_apps_html.<locals>.<listcomp>u0  
                <div class="bb10-empty">
                    <div class="bb10-empty-icon">🌐</div>
                    <div class="bb10-empty-text">No Web Apps Available</div>
                    <div class="bb10-empty-subtext">All available web apps are already installed</div>
                </div>r&   r   r  zT', 'web')" style="cursor: pointer;">
                    <div class="bb10-app-icon">ra  z</div>
                    <div class="bb10-app-status">Web App</div>
                    <div class="bb10-app-actions">
                        <a href="/action?action=install&app_name=z`&app_type=web" class="bb10-button">Install</a>
                    </div>
                </div>z*Error generating available Web Apps HTML: )fetch_web_appsr  rb  r   r   r8   r   r   r6   rp   r[  r_   r   r  r!   r  r"   rc     r  z3TaskManagerHandler.generate_available_web_apps_htmlc              
   C   sn   t td}|du rg S ztd|}dd |D }|W S  ty6 } ztd|  g W  Y d}~S d}~ww )z
        Fetch and parse the listing of .apk files from APKS_URL.
        Returns a list of filenames (e.g. ['myapp.apk', 'anotherapp.apk', ...]).
        android_apksNzhref=["\']([^"\']+\.apk)["\']c                 S      g | ]}t j|qS r!   r   r8   rU  )rm  rq  r!   r!   r"   r        z9TaskManagerHandler.fetch_android_apks.<locals>.<listcomp>zError parsing Android APKs: )r#   r   r   findallr_   r   )rV   r   	apk_filesr    r!   r!   r"   r     s   
z%TaskManagerHandler.fetch_android_apksc              
   C   l   t td}|d u rd S ztd|}dd |D }|W S  ty5 } ztd|  W Y d }~d S d }~ww )Navailable_cli_zipshref=["\']([^"\']+\.zip)["\']c                 S   r  r!   r  r  r!   r!   r"   r    r  z;TaskManagerHandler.fetch_available_zips.<locals>.<listcomp>z"Error parsing available CLI zips: )r#   r   r   r  r_   r   rV   r   	zip_filesr    r!   r!   r"   r       
z'TaskManagerHandler.fetch_available_zipsc              
   C   r  )Navailable_web_appsr  c                 S   r  r!   r  r  r!   r!   r"   r  ,  r  z5TaskManagerHandler.fetch_web_apps.<locals>.<listcomp>zError parsing web apps: )r#   r   r   r  r_   r   r  r!   r!   r"   r  %  r  z!TaskManagerHandler.fetch_web_appsc                 C   s  g }zt jddgdt jd }| d}|D ]h}| r| }t|dkr|d }d|d	d
 }d| v pPd|v pPd|v pPd|v pP|	doPd|v}|rd|v rg|
dtd }	||	d
 }nd|v rz|
dtd }	||	d
 }|||f qW |S  t jy1   zt jg ddt jd }| d}|D ]l}| r| }t|dkr|d }d|d	d
 }d| v pd|v pd|v pd|v p|	dod|v}|rd|v r|
dtd }	||	d
 }nd|v r	|
dtd }	||	d
 }|||f qW Y |S  ty0 }
 ztd|
  W Y d
}
~
Y |S d
}
~
ww  tyK }
 ztd|
  W Y d
}
~
|S d
}
~
ww )z_
        IMPROVED: Better process detection for QNX - looks for all relevant processes
        pidinarr   r1  r   r   r    r   NrS  rP  rQ  r  .pyz
taskapp.pyrR  )r  z-fz%a %A %n %pz$Error getting processes (fallback): zError getting processes: )r%  r9  r:  r   r   r   r;  r   r   r   findr   CalledProcessErrorr_   r   )rV   r\  r@  r   r   rA  r(   rV  is_relevantindexr    r!   r!   r"   rZ  2  s   C

z'TaskManagerHandler.get_python_processesc              
   C   s   g }zKt j|s|W S t |D ]9\}}}|D ]1}|dkr1|dkr0t j||}||df q|dsIt jt j|||}	||	df qqW |S  tyg }
 zt	d|
  W Y d }
~
|S d }
~
ww )Nr   r  r  r*   zError scanning apps directory: )
r   r8   r   walkrelpathr   r   r   r_   r   )rV   	directoryr)   appsrootdirsfilesr   r
  rel_pathr    r!   r!   r"   rb    s.   
z&TaskManagerHandler.scan_apps_directoryc           	      C   s  zrt td}| }W d   n1 sw   Y  |  }|s#d}nGd}|d7 }|D ]:\}}|dkretj|}| |rR|d| d|  d	| d
| d	7 }q+|d| d|  d| d
| d	7 }q+|d7 }|	d|}|W S  t
y } ztd|  W Y d}~dS d}~ww )z
        Generate the HTML for the auto-config page, showing web apps
        with options to enable or disable auto start.
        r   Nz#<p>No installed web apps found.</p>z<table>z:<tr><th>App Name</th><th>Type</th><th>Auto Start</th></tr>r   z<tr><td>z	</td><td>z=</td><td><a href="/action?action=disable_auto_start&app_name=rk   z"">Disable Auto Start</a></td></tr>z<</td><td><a href="/action?action=enable_auto_start&app_name=z!">Enable Auto Start</a></td></tr>z</table>z<!-- installed_apps_auto -->z)Error loading auto-config HTML template: zE<html><body><h1>Error loading auto-config template</h1></body></html>)r   AUTO_CONFIG_TEMPLATE_PATHr   get_all_installed_appsr   r6   rp   is_auto_start_enabledr[  r   r_   r   )	rV   r   rZ   re  r  rf  r)   rs   r    r!   r!   r"   rG     s0   

(&z,TaskManagerHandler.generate_auto_config_htmlc                 C   s    |  td}|  td}|| S )Nr*   r   )rb  r   r   )rV   rc  rd  r!   r!   r"   r    s   z)TaskManagerHandler.get_all_installed_appsc              
   C   s   z<t jts
W dS ttd}| }W d   n1 sw   Y  |D ]}dt jt|d d|v r9 W dS q%W dS  tyY } zt	d| d	|  W Y d}~dS d}~ww )
z\
        Check if auto-start is enabled for the given app by inspecting ~/.profile.
        Fr   Nrh  r  z" &TzError checking auto-start for r	   )
r   r8   r   rs  r   rt  r   r   r_   r   )rV   r'   r   r{  r   r    r!   r!   r"   r    s    
z(TaskManagerHandler.is_auto_start_enabledN)r   )r*   )-__name__
__module____qualname__r[   rP   rQ   rM   rN   rO   rR   rS   r   r   r   r   r   ro   r   rT   r   r   rK   rL   r?   r   r@   r=   rF  r>   rU   rY  rd   re   rA   rB   r^   rc   r   r  r  rZ  rb  rG   r  r  r!   r!   r!   r"   r$   I   sZ    J"&$
#
B ^
Z

 ~
K99
($%O!r$   c                  C   s   t jdd} | t j}t|vr%tdt d td tdt d t|vr?tdt d td	 tdt d d S d S )
NPATHr&   z	Warning: z is not in your PATH.zAdd to ~/.profile:zexport PATH="z:$PATH"zNote: z?If you want to run web apps from command line, consider adding:)r   environr<   r   pathsepr   r   r   )current_pathpathsr!   r!   r"   
check_path  s   r  __main__z"Starting optimized Task Manager...zPerformance improvements:z%- Caching network requests (5min TTL)z - Timeout handling (10s timeout)z- Optimized pidin usagez!- Lazy loading for available appsr&   zServing on port )1http.serverhttpsocketserverr%  r   rH  urllib.parser   r   urllib.requestr   r   r
   	threadingr   rq   r   PORTr8   r   __file__r   r   rJ  r  r   r   r   r   r   r   r   r   rs  r(  r~   r   r   r   r#   serverBaseHTTPRequestHandlerr$   r  r  r   r/  httpdserve_foreverr!   r!   r!   r"   <module>   sz                 
"