Skip to main content
Glama
service.cpython-312.pyc18.2 kB
� C��h�9���dZddlZddlZddlZddlmZmZddlmZmZm Z ddl m Z ddl m Z mZmZmZddlmZej&e�Zgd �ZGd �d �Zy) z0Job management service for background processes.�N)�datetime�timezone)�Dict�List�Optional�)�BackgroundJobConfig)� BackgroundJob� JobStatus� JobSummary� ProcessOutput)�ProcessWrapper) z rm\s+.*-rf.*/z sudo\s+rmz >\s*/dev/z wget.*\|.*shz curl.*\|.*shzcurl.*\|.*bashzdd\s+if=.*of=/dev/zmkfs\.�fdiskz :(){ :|:& };:zcat\s+/dev/urandomz chmod.*777.*/zchown.*root.*/c���eZdZdZddeefd�Zdeddfd�Zdedefd�Z d ede fd �Z d edefd �Z d ede fd �Zd ed ede fd�Zd edede fd�Zdeefd�Zd eddfd�Zdefd�Zd edefd�Zdeeeffd�Zdd�Zy)� JobManagerz2Central service for managing background processes.N�configc���|xs t�|_i|_i|_tj d|jj �d|jj���y)zsInitialize the job manager. Args: config: Configuration object, uses defaults if None z%JobManager initialized with max_jobs=z, max_output_size=N)r r�_jobs� _processes�logger�info�max_concurrent_jobs�max_output_size_bytes)�selfrs �W/Users/dylan/Workspace/mcp/servers/mcp-background-job/src/mcp_background_job/service.py�__init__zJobManager.__init__$s\�� �5� 3� 5�� �/1�� �57���� � �3�D�K�K�4S�4S�3T�U�#�{�{�@�@�A� C� ��command�returnc���tD]M}tj||tj�s�)tj d|���t d|����|jjrnd}|jjD]+}tj||tj�s�)d}n|s&tj d|���t d|����tjd|���y)z�Validate command against security policies. Args: command: Shell command to validate Raises: ValueError: If command contains dangerous patterns or violates policies z#Blocked dangerous command pattern: z7Command contains dangerous pattern and is not allowed: FTz!Command not in allowed patterns: z$Command security validation passed: N) �BLOCKED_COMMAND_PATTERNS�re�search� IGNORECASEr�warning� ValueErrorr�allowed_command_patterns�debug)rr�pattern�allowed�allowed_patterns r�_validate_command_securityz%JobManager._validate_command_security3s���0�G��y�y��'�2�=�=�9����!D�W�I�N�O� �#Z�[b�Zc�!d�e�e�0� �;�;� /� /��G�#'�;�;�#G�#G���9�9�_�g�r�}�}�E�"�G��$H� ����!B�7�)�L�M� �#D�W�I�!N�O�O�� � �;�G�9�E�Frc���K�|r|j�s td��|j|j��td�|jj �D��}||j jk\r#td|j j�d���ttj��}t||j�tjtj t"j$���}t'||j�|j j(��} |j+��d{���|j-�|_||j|<||j0|<t2j5d|�d |j����|S7�b#t6$r9}t2j9d |�d |��� |j;��#Y�xYwd}~wwxYw�w) a:Execute command as background job, return job_id. Args: command: Shell command to execute Returns: UUID v4 job identifier Raises: RuntimeError: If maximum concurrent jobs limit is reached ValueError: If command is empty or invalid zCommand cannot be emptyc3�\K�|]$}|jtjk(s�!d���&y�w)rN)�statusr �RUNNING)�.0�jobs r� <genexpr>z-JobManager.execute_command.<locals>.<genexpr>ds&���� �,�#�� � �i�>O�>O�0O�A�,�s�",�,zMaximum concurrent jobs limit (z ) reached)�job_idrr/�started)r4r�max_output_sizeNz Started job �: zFailed to start job )�stripr&r,�sumr�valuesrr� RuntimeError�str�uuid�uuid4r r r0r�nowr�utcrr�start�get_pid�pidrrr� Exception�error�cleanup)rr� running_jobsr4r2�process_wrapper�es r�execute_commandzJobManager.execute_commandPs������g�m�m�o��6�7� 7� �'�'�� � ��8�� ����*�*�,� � � � �4�;�;�:�:� :��1�$�+�+�2Q�2Q�1R�R[�\�� � �T�Z�Z�\�"�����M�M�O��$�$��L�L����.�  ��)���M�M�O� �K�K�=�=� ��  �!�'�'�)� )� )�&�-�-�/�C�G�"%�D�J�J�v� �&5�D�O�O�F� #� �K�K�,�v�h�b�����0A�B� C��M� *��� � �L�L�/��x�r�!��=� >� ��'�'�)� �� �� �� �sU�D<G<�?F7�F5�A!F7�4G<�5F7�7 G9�G4�G-�,G4�-G1�/G4�4G9�9G<r4c��K�||jvrtd|�d���|j|��d{���|j|jS7��w)z�Get current status of job. Args: job_id: Job identifier Returns: Current job status Raises: KeyError: If job_id doesn't exist �Job � not foundN)r�KeyError�_update_job_statusr/�rr4s r�get_job_statuszJobManager.get_job_status�sX���� ���� #��T�&���4�5� 5��%�%�f�-�-�-��z�z�&�!�(�(�(� .�s�2A�A�Ac��RK�||jvry|j|}|jj|�}|j|��d{���|jt j t jt jfvry|�t j|_y|j�rkt j|_tjtj�|_|j�|_t"j%d|���yy7�ԭw)z�Kill running job. Args: job_id: Job identifier Returns: Kill result: 'killed', 'already_terminated', or 'not_found' � not_foundN�already_terminated� Killed job �killed)rr�getrOr/r � COMPLETED�FAILED�KILLED�killrr?rr@� completed� get_exit_code� exit_coderr)rr4r2rHs r�kill_jobzJobManager.kill_job�s����� ���� #���j�j�� ���/�/�-�-�f�5���%�%�f�-�-�-� �:�:�)�-�-�y�/?�/?��AQ�AQ�R� R�'� � "�"�)�)�C�J�'� � � � !�"�)�)�C�J�$�L�L����6�C�M�+�9�9�;�C�M� �K�K�+�f�X�.� /��'�# .�s�AD'�D%�CD'c��K�||jvrtd|�d���|jj|�}|� t dd��S|j �S�w)z�Get full stdout/stderr output. Args: job_id: Job identifier Returns: ProcessOutput with complete stdout and stderr Raises: KeyError: If job_id doesn't exist rLrM���stdout�stderr)rrNrrWr � get_output)rr4rHs r�get_job_outputzJobManager.get_job_output�s^���� ���� #��T�&���4�5� 5��/�/�-�-�f�5�� � "� ��2�6� 6��)�)�+�+�s�AA�linesc���K�||jvrtd|�d���|dkr td��|jj |�}|� t dd��S|j |�S�w)aFGet last N lines of output. Args: job_id: Job identifier lines: Number of lines to return Returns: ProcessOutput with last N lines of stdout and stderr Raises: KeyError: If job_id doesn't exist ValueError: If lines is not positive rLrMrz Number of lines must be positiverarb)rrNr&rrWr � tail_output)rr4rgrHs r�tail_job_outputzJobManager.tail_job_output�ss���� ���� #��T�&���4�5� 5� �A�:��?�@� @��/�/�-�-�f�5�� � "� ��2�6� 6��*�*�5�1�1�s�A)A+� input_textc��K�||jvrtd|�d���|j|��d{���|j|}|jtj k7rt d|�d|j�d���|jj|�}|�t d|�d���|j|��d{���S7��7��w)atSend input to job stdin, return immediate output. Args: job_id: Job identifier input_text: Text to send to stdin Returns: ProcessOutput with any immediate stdout/stderr output Raises: KeyError: If job_id doesn't exist RuntimeError: If job is not running or stdin not available rLrMNz is not running (status: �)zProcess wrapper for job ) rrNrOr/r r0r;rrW� send_input)rr4rkr2rHs r�interact_with_jobzJobManager.interact_with_job�s����� ���� #��T�&���4�5� 5��%�%�f�-�-�-��j�j�� �� �:�:��*�*� *���f�X�-F�s�z�z�l�RS�T�U� U��/�/�-�-�f�5�� � "��!9�&���L�M� M�$�/�/� �;�;�;� .��<�s"�2C �C�B C �C�C �C c ���K�t|jj��D]} |j|��d{����g}|jj�D]H}|jt|j|j|j|j����J|jd�d��|S7��#t$r%}t j d|�d|���Yd}~��d}~wwxYw�w)z]List all jobs. Returns: List of JobSummary objects for all jobs Nz Failed to update status for job r7)r4r/rr5c��|jS�N)r5)�xs r�<lambda>z&JobManager.list_jobs.<locals>.<lambda>3s��Q�Y�YrT)�key�reverse)�listr�keysrOrDrr%r:�appendr r4r/rr5�sort)rr4rI� summariesr2s r� list_jobszJobManager.list_jobss������4�:�:�?�?�,�-�F� Q��-�-�f�5�5�5�.�� ��:�:�$�$�&�C� � � ���:�:��:�:��K�K��K�K� � �'� ���.���=���%6��� Q����!A�&���A�3�O�P�P�� Q�s?�'C5�C�C�C�A?C5�C� C2� C-�(C5�-C2�2C5c���K�||jvry|j|}|jj|�}|�[|jtj k(r=tj |_tjtj�|_ y|j�}|j|k7r�||_|tjtj tjfvr�|j�6|jxs#tjtj�|_ |j!�|_t$j'd|�d|�d|j"���yyy�w)zdUpdate job status based on process state. Args: job_id: Job identifier NrLz completed with status z , exit_code=)rrrWr/r r0rYrr?rr@r\� get_statusrXrZ� completed_atr]r^rr)rr4r2rH�current_statuss rrOzJobManager._update_job_status6s@���� ���� #� ��j�j�� ���/�/�-�-�f�5�� � "��z�z�Y�.�.�.�&�-�-�� � (� � �X�\�\� :�� � �)�3�3�5�� �:�:�� '�'�C�J���#�#�� � �� � �"�� �=�=�(�$3�$@�$@�%�H�L�L� � � �E�C�M�!0� =� =� ?�� �� � ��6�(�"9�.�9I�J!�!$����1��� (�s�E0E2c�b�d}g}|jj�D]�\}}|jtjtj tj fvs�B|jj|�}|s�` |j�|j|=|dz }tjd|�����|D]}||jvs�|j|=� |dkDrtjd|�d��|S#t$r&}tjd|�d|���Yd}~�� d}~wwxYw) z~Clean up terminated processes and optionally remove old jobs. Returns: Number of jobs cleaned up rrzCleaned up process for job zError cleaning up job r7Nz Cleaned up z completed jobs)r�itemsr/r rXrYrZrrWrFrr(rDr%r)r� cleaned_count�jobs_to_remover4r2rHrIs r�cleanup_completed_jobsz!JobManager.cleanup_completed_jobs`s�� � ����:�:�+�+�-�K�F�C��z�z�i�1�1�9�3C�3C�Y�EU�EU�V�V�"&�/�/�"5�"5�f�"=��"�O�'�/�/�1� �O�O�F�3�%��*� �� � �'B�6�(�%K�L�.� %�F�����#��J�J�v�&�%� �1� � �K�K�+�m�_�O�D� E����%�O����)?��x�r�!��'M�N�N��O�s�:C?�? D.�D)�)D.c��K�||jvrtd|�d���|j|��d{���|j|S7��w)z�Get complete job information. Args: job_id: Job identifier Returns: Complete BackgroundJob object Raises: KeyError: If job_id doesn't exist rLrMN)rrNrOrPs r�get_jobzJobManager.get_job�sR���� ���� #��T�&���4�5� 5��%�%�f�-�-�-��z�z�&�!�!� .�s�2A �A�A c���t|j�ddddd�}|jj�D]�}|jtj k(r|dxxdz cc<�.|jtj k(r|dxxdz cc<�Y|jtjk(r|dxxdz cc<��|jtjk(s��|dxxdz cc<��|S)z_Get job statistics. Returns: Dictionary with job count statistics r)�total�runningr\�failedrVr�rr\r�rV) �lenrr:r/r r0rXrYrZ)r�statsr2s r� get_statszJobManager.get_stats�s�������_�����  ���:�:�$�$�&�C��z�z�Y�.�.�.��i� �A�%� ����y�2�2�2��k�"�a�'�"����y�/�/�/��h��1�$�����y�/�/�/��h��1�$��'�� rc���K�tjd�|jj�D]V\}}|jt j k(s�$ |j|��d{���tjd|�d���X|j�tjd�y7�E#t$r%}tjd|�d|���Yd}~��d}~wwxYw�w)zkGracefully shutdown the job manager. Kills all running processes and cleans up resources. zShutting down JobManager...NrUz during shutdownzError killing job z during shutdown: zJobManager shutdown complete) rrrr�r/r r0r_rDr%r�)rr4r2rIs r�shutdownzJobManager.shutdown�s����� � � �1�2� �:�:�+�+�-�K�F�C��z�z�Y�.�.�.�W��-�-��/�/�/��K�K�+�f�X�5E� F�G� .� �#�#�%�� � �2�3�0�� �W��N�N�%7��x�?Q�RS�QT�#U�V�V��W�sB�AC$�B3�+B1�,B3� (C$�1B3�3 C!�<C�C$�C!�!C$rr)rN)�__name__� __module__� __qualname__�__doc__rr rr<r,rJr rQr_r rf�intrjrorr r|rOr�r r�rr�r��rrrr!s��<�  �x�(;�<�  �G�#�G�$�G�:C�S�C�S�C�J)�3�)�9�)�(!(�S�!(�S�!(�F,�3�,�=�,�*2�C�2��2� �2�4<�c�<�s�<�}�<�<��j�!1��:(�s�(�t�(�T �� �D"�C�"�M�"�&�4��S��>��44rr)r��loggingr"r=rr�typingrrrrr �modelsr r r r �processr� getLoggerr�rr!rr�rr�<module>r�sL��6�� � �'�'�'�'�G�G�#� �� � �8� $����"a4�a4r

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dylan-gluck/mcp-background-job'

If you have feedback or need assistance with the MCP directory API, please join our Discord server