cmdop 0.1.23__tar.gz → 0.1.25__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. cmdop-0.1.25/PKG-INFO +322 -0
  2. cmdop-0.1.25/README.md +281 -0
  3. {cmdop-0.1.23 → cmdop-0.1.25}/pyproject.toml +1 -1
  4. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/__init__.py +1 -1
  5. cmdop-0.1.25/src/cmdop/helpers/__init__.py +17 -0
  6. cmdop-0.1.25/src/cmdop/helpers/network_analyzer.py +368 -0
  7. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/__init__.py +2 -0
  8. cmdop-0.1.25/src/cmdop/services/browser/capabilities/visual.py +100 -0
  9. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/session.py +10 -0
  10. cmdop-0.1.23/PKG-INFO +0 -330
  11. cmdop-0.1.23/README.md +0 -289
  12. cmdop-0.1.23/src/cmdop/helpers/__init__.py +0 -9
  13. {cmdop-0.1.23 → cmdop-0.1.25}/.gitignore +0 -0
  14. {cmdop-0.1.23 → cmdop-0.1.25}/LICENSE +0 -0
  15. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/__init__.py +0 -0
  16. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/agent_messages_pb2.py +0 -0
  17. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/agent_messages_pb2.pyi +0 -0
  18. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/agent_messages_pb2_grpc.py +0 -0
  19. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/common_types_pb2.py +0 -0
  20. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/common_types_pb2.pyi +0 -0
  21. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/common_types_pb2_grpc.py +0 -0
  22. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/control_messages_pb2.py +0 -0
  23. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/control_messages_pb2.pyi +0 -0
  24. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/control_messages_pb2_grpc.py +0 -0
  25. {cmdop-0.1.23/src/cmdop/_generated/rpc_messages → cmdop-0.1.25/src/cmdop/_generated/file_operations}/__init__.py +0 -0
  26. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/archive_pb2.py +0 -0
  27. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/archive_pb2.pyi +0 -0
  28. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/archive_pb2_grpc.py +0 -0
  29. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/changes_pb2.py +0 -0
  30. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/changes_pb2.pyi +0 -0
  31. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/changes_pb2_grpc.py +0 -0
  32. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/common_pb2.py +0 -0
  33. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/common_pb2.pyi +0 -0
  34. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/common_pb2_grpc.py +0 -0
  35. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/directory_pb2.py +0 -0
  36. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/directory_pb2.pyi +0 -0
  37. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/directory_pb2_grpc.py +0 -0
  38. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/file_crud_pb2.py +0 -0
  39. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/file_crud_pb2.pyi +0 -0
  40. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/file_crud_pb2_grpc.py +0 -0
  41. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/hls_pb2.py +0 -0
  42. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/hls_pb2.pyi +0 -0
  43. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/hls_pb2_grpc.py +0 -0
  44. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/requests_pb2.py +0 -0
  45. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/requests_pb2.pyi +0 -0
  46. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/requests_pb2_grpc.py +0 -0
  47. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/search_pb2.py +0 -0
  48. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/search_pb2.pyi +0 -0
  49. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/search_pb2_grpc.py +0 -0
  50. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/transfer_pb2.py +0 -0
  51. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/transfer_pb2.pyi +0 -0
  52. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations/transfer_pb2_grpc.py +0 -0
  53. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations_pb2.py +0 -0
  54. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations_pb2.pyi +0 -0
  55. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_operations_pb2_grpc.py +0 -0
  56. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/__init__.py +0 -0
  57. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/archive_pb2.py +0 -0
  58. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/archive_pb2.pyi +0 -0
  59. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/archive_pb2_grpc.py +0 -0
  60. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/directory_pb2.py +0 -0
  61. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/directory_pb2.pyi +0 -0
  62. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/directory_pb2_grpc.py +0 -0
  63. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/file_crud_pb2.py +0 -0
  64. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/file_crud_pb2.pyi +0 -0
  65. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/file_crud_pb2_grpc.py +0 -0
  66. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/hls_pb2.py +0 -0
  67. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/hls_pb2.pyi +0 -0
  68. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/hls_pb2_grpc.py +0 -0
  69. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/search_pb2.py +0 -0
  70. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/search_pb2.pyi +0 -0
  71. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc/search_pb2_grpc.py +0 -0
  72. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc_pb2.py +0 -0
  73. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc_pb2.pyi +0 -0
  74. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/file_rpc_pb2_grpc.py +0 -0
  75. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/message_pool.py +0 -0
  76. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/py.typed +0 -0
  77. {cmdop-0.1.23/src/cmdop/_generated/file_operations → cmdop-0.1.25/src/cmdop/_generated/rpc_messages}/__init__.py +0 -0
  78. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/agent_pb2.py +0 -0
  79. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/agent_pb2.pyi +0 -0
  80. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/agent_pb2_grpc.py +0 -0
  81. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/browser_pb2.py +0 -0
  82. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/browser_pb2.pyi +0 -0
  83. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/browser_pb2_grpc.py +0 -0
  84. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/device_pb2.py +0 -0
  85. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/device_pb2.pyi +0 -0
  86. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/device_pb2_grpc.py +0 -0
  87. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/extract_pb2.py +0 -0
  88. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/extract_pb2.pyi +0 -0
  89. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/extract_pb2_grpc.py +0 -0
  90. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/health_pb2.py +0 -0
  91. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/health_pb2.pyi +0 -0
  92. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/health_pb2_grpc.py +0 -0
  93. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/history_pb2.py +0 -0
  94. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/history_pb2.pyi +0 -0
  95. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/history_pb2_grpc.py +0 -0
  96. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/lifecycle_pb2.py +0 -0
  97. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/lifecycle_pb2.pyi +0 -0
  98. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/lifecycle_pb2_grpc.py +0 -0
  99. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/push_pb2.py +0 -0
  100. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/push_pb2.pyi +0 -0
  101. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/push_pb2_grpc.py +0 -0
  102. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/session_pb2.py +0 -0
  103. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/session_pb2.pyi +0 -0
  104. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/session_pb2_grpc.py +0 -0
  105. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/terminal_pb2.py +0 -0
  106. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/terminal_pb2.pyi +0 -0
  107. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages/terminal_pb2_grpc.py +0 -0
  108. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages_pb2.py +0 -0
  109. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages_pb2.pyi +0 -0
  110. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/rpc_messages_pb2_grpc.py +0 -0
  111. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/service_pb2.py +0 -0
  112. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/service_pb2.pyi +0 -0
  113. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/service_pb2_grpc.py +0 -0
  114. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/tunnel_pb2.py +0 -0
  115. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/tunnel_pb2.pyi +0 -0
  116. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/_generated/tunnel_pb2_grpc.py +0 -0
  117. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/__init__.py +0 -0
  118. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/__init__.py +0 -0
  119. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/__init__.py +0 -0
  120. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/client.py +0 -0
  121. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/enums.py +0 -0
  122. {cmdop-0.1.23/src/cmdop/api/generated/workspaces → cmdop-0.1.25/src/cmdop/api/generated/machines}/logger.py +0 -0
  123. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/machines__api__machine_sharing/__init__.py +0 -0
  124. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/machines__api__machine_sharing/client.py +0 -0
  125. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/machines__api__machine_sharing/models.py +0 -0
  126. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/machines__api__machine_sharing/sync_client.py +0 -0
  127. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/machines__api__machines/__init__.py +0 -0
  128. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/machines__api__machines/client.py +0 -0
  129. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/machines__api__machines/models.py +0 -0
  130. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/machines__api__machines/sync_client.py +0 -0
  131. {cmdop-0.1.23/src/cmdop/api/generated/workspaces → cmdop-0.1.25/src/cmdop/api/generated/machines}/retry.py +0 -0
  132. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/schema.json +0 -0
  133. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/machines/sync_client.py +0 -0
  134. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/__init__.py +0 -0
  135. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/client.py +0 -0
  136. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/enums.py +0 -0
  137. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/logger.py +0 -0
  138. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/retry.py +0 -0
  139. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/schema.json +0 -0
  140. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/sync_client.py +0 -0
  141. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/system__api__oauth/__init__.py +0 -0
  142. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/system__api__oauth/client.py +0 -0
  143. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/system__api__oauth/models.py +0 -0
  144. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/system__api__oauth/sync_client.py +0 -0
  145. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/system__api__system/__init__.py +0 -0
  146. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/system__api__system/client.py +0 -0
  147. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/system__api__system/models.py +0 -0
  148. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/system/system__api__system/sync_client.py +0 -0
  149. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/__init__.py +0 -0
  150. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/client.py +0 -0
  151. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/enums.py +0 -0
  152. {cmdop-0.1.23/src/cmdop/api/generated/machines → cmdop-0.1.25/src/cmdop/api/generated/workspaces}/logger.py +0 -0
  153. {cmdop-0.1.23/src/cmdop/api/generated/machines → cmdop-0.1.25/src/cmdop/api/generated/workspaces}/retry.py +0 -0
  154. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/schema.json +0 -0
  155. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/sync_client.py +0 -0
  156. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/workspaces__api__workspaces/__init__.py +0 -0
  157. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/workspaces__api__workspaces/client.py +0 -0
  158. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/workspaces__api__workspaces/models.py +0 -0
  159. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/api/generated/workspaces/workspaces__api__workspaces/sync_client.py +0 -0
  160. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/client.py +0 -0
  161. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/config.py +0 -0
  162. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/discovery.py +0 -0
  163. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/exceptions.py +0 -0
  164. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/helpers/cleaner.py +0 -0
  165. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/helpers/formatting.py +0 -0
  166. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/logging.py +0 -0
  167. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/models/__init__.py +0 -0
  168. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/models/agent.py +0 -0
  169. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/models/base.py +0 -0
  170. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/models/config.py +0 -0
  171. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/models/extract.py +0 -0
  172. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/models/files.py +0 -0
  173. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/models/terminal.py +0 -0
  174. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/py.typed +0 -0
  175. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/__init__.py +0 -0
  176. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/agent.py +0 -0
  177. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/base.py +0 -0
  178. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/__init__.py +0 -0
  179. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/_base.py +0 -0
  180. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/_helpers.py +0 -0
  181. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/dom.py +0 -0
  182. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/fetch.py +0 -0
  183. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/input.py +0 -0
  184. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/network.py +0 -0
  185. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/scroll.py +0 -0
  186. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/capabilities/timing.py +0 -0
  187. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/js/__init__.py +0 -0
  188. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/js/core.py +0 -0
  189. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/js/fetch.py +0 -0
  190. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/js/interaction.py +0 -0
  191. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/js/scroll.py +0 -0
  192. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/models.py +0 -0
  193. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/parsing.py +0 -0
  194. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/service/__init__.py +0 -0
  195. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/service/_helpers.py +0 -0
  196. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/service/aio.py +0 -0
  197. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/browser/service/sync.py +0 -0
  198. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/extract.py +0 -0
  199. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/files.py +0 -0
  200. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/services/terminal.py +0 -0
  201. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/streaming/__init__.py +0 -0
  202. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/streaming/base.py +0 -0
  203. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/streaming/handlers.py +0 -0
  204. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/streaming/terminal.py +0 -0
  205. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/transport/__init__.py +0 -0
  206. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/transport/auth.py +0 -0
  207. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/transport/base.py +0 -0
  208. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/transport/discovery.py +0 -0
  209. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/transport/local.py +0 -0
  210. {cmdop-0.1.23 → cmdop-0.1.25}/src/cmdop/transport/remote.py +0 -0
cmdop-0.1.25/PKG-INFO ADDED
@@ -0,0 +1,322 @@
1
+ Metadata-Version: 2.4
2
+ Name: cmdop
3
+ Version: 0.1.25
4
+ Summary: Python SDK for CMDOP agent interaction
5
+ Project-URL: Homepage, https://cmdop.com
6
+ Project-URL: Documentation, https://cmdop.com
7
+ Project-URL: Repository, https://github.com/markolofsen/cmdop-client
8
+ Author: CMDOP Team
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,automation,cmdop,terminal
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: beautifulsoup4>=4.12.0
23
+ Requires-Dist: grpcio>=1.60.0
24
+ Requires-Dist: httpx>=0.27.0
25
+ Requires-Dist: lxml>=5.0.0
26
+ Requires-Dist: protobuf>=4.25.0
27
+ Requires-Dist: pydantic-settings>=2.0.0
28
+ Requires-Dist: pydantic>=2.5.0
29
+ Requires-Dist: rich>=13.0.0
30
+ Requires-Dist: toon-python>=0.1.2
31
+ Provides-Extra: dev
32
+ Requires-Dist: beautifulsoup4>=4.12.0; extra == 'dev'
33
+ Requires-Dist: grpcio-tools>=1.60.0; extra == 'dev'
34
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
35
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
36
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
37
+ Requires-Dist: pytest-grpc-aio>=0.3.0; extra == 'dev'
38
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
39
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # cmdop
43
+
44
+ Python SDK for CMDOP browser automation and server control.
45
+
46
+ ## Architecture
47
+
48
+ ```
49
+ Your Code ──── Cloud Relay ──── Agent (on server)
50
+
51
+ Outbound only, works through any NAT/firewall
52
+ ```
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install cmdop
58
+ ```
59
+
60
+ ## Connection
61
+
62
+ ```python
63
+ from cmdop import CMDOPClient, AsyncCMDOPClient
64
+
65
+ # Local (direct IPC to running agent)
66
+ client = CMDOPClient.local()
67
+
68
+ # Remote (via cloud relay)
69
+ client = CMDOPClient.remote(api_key="cmd_xxx")
70
+
71
+ # Async
72
+ async with AsyncCMDOPClient.local() as client:
73
+ await client.files.read("/etc/hostname")
74
+ ```
75
+
76
+ ## Browser
77
+
78
+ ```python
79
+ from cmdop.services.browser.models import WaitUntil
80
+
81
+ with client.browser.create_session(headless=False) as s:
82
+ s.navigate("https://shop.com", wait_until=WaitUntil.NETWORKIDLE)
83
+
84
+ # Core methods
85
+ s.click("button.buy", move_cursor=True)
86
+ s.type("input[name=q]", "search term")
87
+ s.wait_for(".results")
88
+ s.execute_script("return document.title")
89
+ s.screenshot()
90
+ s.get_state() # URL + title
91
+ s.get_page_info() # Full page info
92
+ s.get_cookies()
93
+ s.set_cookies([...])
94
+ ```
95
+
96
+ **WaitUntil options:**
97
+ | Value | Description |
98
+ |-------|-------------|
99
+ | `LOAD` | Wait for load event (default) |
100
+ | `DOMCONTENTLOADED` | Wait for DOMContentLoaded |
101
+ | `NETWORKIDLE` | Wait until network is idle (best for SPA) |
102
+ | `COMMIT` | Return immediately (fastest) |
103
+
104
+ ### Capabilities
105
+
106
+ **`s.scroll`** - Scrolling
107
+ ```python
108
+ s.scroll.js("down", 500) # JS scroll (works on complex sites)
109
+ s.scroll.native("down", 500) # Browser API scroll
110
+ s.scroll.to_bottom() # Scroll to page bottom
111
+ s.scroll.to_element(".item") # Scroll element into view
112
+ s.scroll.info() # Get scroll position/dimensions
113
+
114
+ # Smart infinite scroll with extraction
115
+ items = s.scroll.infinite(
116
+ extract_fn=lambda: extract_new_items(),
117
+ limit=100,
118
+ max_scrolls=50,
119
+ scroll_amount=800,
120
+ )
121
+ ```
122
+
123
+ **`s.input`** - Input
124
+ ```python
125
+ s.input.click_js(".btn") # JS click (reliable)
126
+ s.input.click_all("See more") # Click all matching elements
127
+ s.input.key("Escape") # Press key
128
+ s.input.key("Enter", ".input") # Press key on element
129
+ s.input.hover(".tooltip") # Native hover
130
+ s.input.hover_js(".tooltip") # JS hover
131
+ s.input.mouse_move(500, 300) # Move cursor to coordinates
132
+ ```
133
+
134
+ **`s.timing`** - Delays
135
+ ```python
136
+ s.timing.wait(500) # Wait ms
137
+ s.timing.seconds(2) # Wait seconds
138
+ s.timing.random(0.5, 1.5) # Random delay
139
+ s.timing.timeout(fn, 10, cleanup) # Run with timeout
140
+ ```
141
+
142
+ **`s.dom`** - DOM operations
143
+ ```python
144
+ s.dom.html(".container") # Get HTML
145
+ s.dom.text(".title") # Get text
146
+ s.dom.soup(".items") # → SoupWrapper (chainable BS4)
147
+ s.dom.parse(html_string) # → BeautifulSoup
148
+ s.dom.extract(".items", "href") # Get attr list
149
+ s.dom.select("#country", "US") # Dropdown select
150
+ s.dom.close_modal() # Close dialogs/popups
151
+ ```
152
+
153
+ **`s.fetch`** - HTTP from browser (bypass CORS, inherit cookies)
154
+ ```python
155
+ s.fetch.json("/api/items") # Fetch JSON
156
+ s.fetch.all(["/api/a", "/api/b"]) # Parallel fetch
157
+ s.fetch.execute("return fetch(...)") # Custom JS
158
+ ```
159
+
160
+ **`s.network`** - Traffic capture
161
+ ```python
162
+ s.network.enable(max_exchanges=1000)
163
+ s.navigate(url)
164
+
165
+ # Get exchanges
166
+ exchanges = s.network.get_all()
167
+ api = s.network.last("/api/data")
168
+ data = api.json_body()
169
+
170
+ # Filter
171
+ posts = s.network.filter(
172
+ url_pattern="/api/posts",
173
+ methods=["GET", "POST"],
174
+ status_codes=[200],
175
+ resource_types=["xhr", "fetch"],
176
+ )
177
+
178
+ # Convenience
179
+ s.network.api_calls("/api/") # XHR/Fetch matching pattern
180
+ s.network.last_json("/api/data") # JSON body directly
181
+ s.network.wait_for("/api/", 5000) # Wait for request
182
+ s.network.export_har() # Export to HAR
183
+ s.network.stats() # Capture statistics
184
+ s.network.clear() # Clear captured
185
+ s.network.disable()
186
+ ```
187
+
188
+ **`s.visual`** - Browser overlay (requires CMDOP extension)
189
+ ```python
190
+ s.visual.toast("Loading...") # Show toast
191
+ s.visual.clear_toasts() # Clear all toasts
192
+ s.visual.countdown(30, "Click!") # Countdown timer
193
+ s.visual.highlight(".element") # Highlight element
194
+ s.visual.hide_highlight() # Hide highlight
195
+ s.visual.click(100, 200) # Show click effect
196
+ s.visual.move(0, 0, 100, 200) # Show cursor trail
197
+ s.visual.set_state("busy") # idle/active/busy
198
+ ```
199
+
200
+ ## NetworkAnalyzer
201
+
202
+ Discover API endpoints by capturing traffic while user interacts.
203
+
204
+ ```python
205
+ from cmdop import CMDOPClient
206
+ from cmdop.helpers import NetworkAnalyzer
207
+
208
+ client = CMDOPClient.local()
209
+ with client.browser.create_session(headless=False) as b:
210
+ analyzer = NetworkAnalyzer(b)
211
+
212
+ snapshot = analyzer.capture(
213
+ "https://example.com/cars",
214
+ wait_seconds=30,
215
+ countdown_message="Click pagination!",
216
+ min_size=100, # Ignore tracking pixels
217
+ max_size=500_000, # Ignore heavy assets
218
+ same_origin=True, # Only same domain
219
+ )
220
+
221
+ # Get best data API
222
+ if snapshot.api_requests:
223
+ best = snapshot.best_api()
224
+ print(best.url)
225
+ print(best.item_count)
226
+ print(best.data_key) # "data", "items", etc.
227
+ print(best.item_fields) # Field names
228
+ print(best.to_curl()) # curl command
229
+ print(best.to_httpx()) # Python httpx code
230
+
231
+ # All captured
232
+ for req in snapshot.api_requests:
233
+ print(f"{req.method} {req.url} → {req.item_count} items")
234
+ ```
235
+
236
+ **NetworkSnapshot:**
237
+ - `api_requests` - Requests with data arrays
238
+ - `json_requests` - Other JSON responses
239
+ - `cookies` - Session cookies
240
+ - `total_requests`, `total_bytes`
241
+
242
+ **RequestSnapshot:**
243
+ - `url`, `method`, `headers`, `body`, `cookies`
244
+ - `status`, `content_type`, `size`
245
+ - `data_key`, `item_count`, `item_fields`, `sample_response`
246
+ - `to_curl()`, `to_httpx()`
247
+
248
+ ## Agent
249
+
250
+ Run AI tasks with typed output:
251
+
252
+ ```python
253
+ from pydantic import BaseModel
254
+
255
+ class Health(BaseModel):
256
+ status: str
257
+ cpu: float
258
+ issues: list[str]
259
+
260
+ result = client.agent.run("Check server health", output_schema=Health)
261
+ health: Health = result.output # Typed!
262
+ ```
263
+
264
+ ## Terminal
265
+
266
+ ```python
267
+ session = client.terminal.create()
268
+ client.terminal.send_input(session.session_id, "ls -la\n")
269
+ output = client.terminal.get_history(session.session_id)
270
+ client.terminal.resize(session.session_id, 120, 40)
271
+ client.terminal.send_signal(session.session_id, "SIGINT")
272
+ client.terminal.close(session.session_id)
273
+ ```
274
+
275
+ ## Files
276
+
277
+ ```python
278
+ client.files.list("/var/log")
279
+ client.files.read("/etc/nginx/nginx.conf")
280
+ client.files.write("/tmp/config.json", b'{"key": "value"}')
281
+ client.files.delete("/tmp/old.txt")
282
+ client.files.copy("/src", "/dst")
283
+ client.files.move("/old", "/new")
284
+ client.files.mkdir("/new/dir")
285
+ client.files.info("/path")
286
+ ```
287
+
288
+ ## SDKBaseModel
289
+
290
+ Auto-cleaning Pydantic model:
291
+
292
+ ```python
293
+ from cmdop import SDKBaseModel
294
+
295
+ class Product(SDKBaseModel):
296
+ __base_url__ = "https://shop.com"
297
+ name: str = "" # " iPhone 15 \n" → "iPhone 15"
298
+ price: int = 0 # "$1,299.00" → 1299
299
+ rating: float = 0 # "4.5 stars" → 4.5
300
+ url: str = "" # "/p/123" → "https://shop.com/p/123"
301
+
302
+ products = Product.from_list(raw["items"]) # Auto dedupe + filter
303
+ ```
304
+
305
+ ## Utilities
306
+
307
+ ```python
308
+ from cmdop import get_logger, json_to_toon
309
+
310
+ # Logging (rich console + file)
311
+ log = get_logger(__name__)
312
+ log.info("Starting")
313
+
314
+ # TOON format (30-50% token savings)
315
+ toon = json_to_toon({"name": "Alice", "age": 25})
316
+ # → "name: Alice\nage: 25"
317
+ ```
318
+
319
+ ## Requirements
320
+
321
+ - Python 3.10+
322
+ - CMDOP agent running locally or API key for remote
cmdop-0.1.25/README.md ADDED
@@ -0,0 +1,281 @@
1
+ # cmdop
2
+
3
+ Python SDK for CMDOP browser automation and server control.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ Your Code ──── Cloud Relay ──── Agent (on server)
9
+
10
+ Outbound only, works through any NAT/firewall
11
+ ```
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install cmdop
17
+ ```
18
+
19
+ ## Connection
20
+
21
+ ```python
22
+ from cmdop import CMDOPClient, AsyncCMDOPClient
23
+
24
+ # Local (direct IPC to running agent)
25
+ client = CMDOPClient.local()
26
+
27
+ # Remote (via cloud relay)
28
+ client = CMDOPClient.remote(api_key="cmd_xxx")
29
+
30
+ # Async
31
+ async with AsyncCMDOPClient.local() as client:
32
+ await client.files.read("/etc/hostname")
33
+ ```
34
+
35
+ ## Browser
36
+
37
+ ```python
38
+ from cmdop.services.browser.models import WaitUntil
39
+
40
+ with client.browser.create_session(headless=False) as s:
41
+ s.navigate("https://shop.com", wait_until=WaitUntil.NETWORKIDLE)
42
+
43
+ # Core methods
44
+ s.click("button.buy", move_cursor=True)
45
+ s.type("input[name=q]", "search term")
46
+ s.wait_for(".results")
47
+ s.execute_script("return document.title")
48
+ s.screenshot()
49
+ s.get_state() # URL + title
50
+ s.get_page_info() # Full page info
51
+ s.get_cookies()
52
+ s.set_cookies([...])
53
+ ```
54
+
55
+ **WaitUntil options:**
56
+ | Value | Description |
57
+ |-------|-------------|
58
+ | `LOAD` | Wait for load event (default) |
59
+ | `DOMCONTENTLOADED` | Wait for DOMContentLoaded |
60
+ | `NETWORKIDLE` | Wait until network is idle (best for SPA) |
61
+ | `COMMIT` | Return immediately (fastest) |
62
+
63
+ ### Capabilities
64
+
65
+ **`s.scroll`** - Scrolling
66
+ ```python
67
+ s.scroll.js("down", 500) # JS scroll (works on complex sites)
68
+ s.scroll.native("down", 500) # Browser API scroll
69
+ s.scroll.to_bottom() # Scroll to page bottom
70
+ s.scroll.to_element(".item") # Scroll element into view
71
+ s.scroll.info() # Get scroll position/dimensions
72
+
73
+ # Smart infinite scroll with extraction
74
+ items = s.scroll.infinite(
75
+ extract_fn=lambda: extract_new_items(),
76
+ limit=100,
77
+ max_scrolls=50,
78
+ scroll_amount=800,
79
+ )
80
+ ```
81
+
82
+ **`s.input`** - Input
83
+ ```python
84
+ s.input.click_js(".btn") # JS click (reliable)
85
+ s.input.click_all("See more") # Click all matching elements
86
+ s.input.key("Escape") # Press key
87
+ s.input.key("Enter", ".input") # Press key on element
88
+ s.input.hover(".tooltip") # Native hover
89
+ s.input.hover_js(".tooltip") # JS hover
90
+ s.input.mouse_move(500, 300) # Move cursor to coordinates
91
+ ```
92
+
93
+ **`s.timing`** - Delays
94
+ ```python
95
+ s.timing.wait(500) # Wait ms
96
+ s.timing.seconds(2) # Wait seconds
97
+ s.timing.random(0.5, 1.5) # Random delay
98
+ s.timing.timeout(fn, 10, cleanup) # Run with timeout
99
+ ```
100
+
101
+ **`s.dom`** - DOM operations
102
+ ```python
103
+ s.dom.html(".container") # Get HTML
104
+ s.dom.text(".title") # Get text
105
+ s.dom.soup(".items") # → SoupWrapper (chainable BS4)
106
+ s.dom.parse(html_string) # → BeautifulSoup
107
+ s.dom.extract(".items", "href") # Get attr list
108
+ s.dom.select("#country", "US") # Dropdown select
109
+ s.dom.close_modal() # Close dialogs/popups
110
+ ```
111
+
112
+ **`s.fetch`** - HTTP from browser (bypass CORS, inherit cookies)
113
+ ```python
114
+ s.fetch.json("/api/items") # Fetch JSON
115
+ s.fetch.all(["/api/a", "/api/b"]) # Parallel fetch
116
+ s.fetch.execute("return fetch(...)") # Custom JS
117
+ ```
118
+
119
+ **`s.network`** - Traffic capture
120
+ ```python
121
+ s.network.enable(max_exchanges=1000)
122
+ s.navigate(url)
123
+
124
+ # Get exchanges
125
+ exchanges = s.network.get_all()
126
+ api = s.network.last("/api/data")
127
+ data = api.json_body()
128
+
129
+ # Filter
130
+ posts = s.network.filter(
131
+ url_pattern="/api/posts",
132
+ methods=["GET", "POST"],
133
+ status_codes=[200],
134
+ resource_types=["xhr", "fetch"],
135
+ )
136
+
137
+ # Convenience
138
+ s.network.api_calls("/api/") # XHR/Fetch matching pattern
139
+ s.network.last_json("/api/data") # JSON body directly
140
+ s.network.wait_for("/api/", 5000) # Wait for request
141
+ s.network.export_har() # Export to HAR
142
+ s.network.stats() # Capture statistics
143
+ s.network.clear() # Clear captured
144
+ s.network.disable()
145
+ ```
146
+
147
+ **`s.visual`** - Browser overlay (requires CMDOP extension)
148
+ ```python
149
+ s.visual.toast("Loading...") # Show toast
150
+ s.visual.clear_toasts() # Clear all toasts
151
+ s.visual.countdown(30, "Click!") # Countdown timer
152
+ s.visual.highlight(".element") # Highlight element
153
+ s.visual.hide_highlight() # Hide highlight
154
+ s.visual.click(100, 200) # Show click effect
155
+ s.visual.move(0, 0, 100, 200) # Show cursor trail
156
+ s.visual.set_state("busy") # idle/active/busy
157
+ ```
158
+
159
+ ## NetworkAnalyzer
160
+
161
+ Discover API endpoints by capturing traffic while user interacts.
162
+
163
+ ```python
164
+ from cmdop import CMDOPClient
165
+ from cmdop.helpers import NetworkAnalyzer
166
+
167
+ client = CMDOPClient.local()
168
+ with client.browser.create_session(headless=False) as b:
169
+ analyzer = NetworkAnalyzer(b)
170
+
171
+ snapshot = analyzer.capture(
172
+ "https://example.com/cars",
173
+ wait_seconds=30,
174
+ countdown_message="Click pagination!",
175
+ min_size=100, # Ignore tracking pixels
176
+ max_size=500_000, # Ignore heavy assets
177
+ same_origin=True, # Only same domain
178
+ )
179
+
180
+ # Get best data API
181
+ if snapshot.api_requests:
182
+ best = snapshot.best_api()
183
+ print(best.url)
184
+ print(best.item_count)
185
+ print(best.data_key) # "data", "items", etc.
186
+ print(best.item_fields) # Field names
187
+ print(best.to_curl()) # curl command
188
+ print(best.to_httpx()) # Python httpx code
189
+
190
+ # All captured
191
+ for req in snapshot.api_requests:
192
+ print(f"{req.method} {req.url} → {req.item_count} items")
193
+ ```
194
+
195
+ **NetworkSnapshot:**
196
+ - `api_requests` - Requests with data arrays
197
+ - `json_requests` - Other JSON responses
198
+ - `cookies` - Session cookies
199
+ - `total_requests`, `total_bytes`
200
+
201
+ **RequestSnapshot:**
202
+ - `url`, `method`, `headers`, `body`, `cookies`
203
+ - `status`, `content_type`, `size`
204
+ - `data_key`, `item_count`, `item_fields`, `sample_response`
205
+ - `to_curl()`, `to_httpx()`
206
+
207
+ ## Agent
208
+
209
+ Run AI tasks with typed output:
210
+
211
+ ```python
212
+ from pydantic import BaseModel
213
+
214
+ class Health(BaseModel):
215
+ status: str
216
+ cpu: float
217
+ issues: list[str]
218
+
219
+ result = client.agent.run("Check server health", output_schema=Health)
220
+ health: Health = result.output # Typed!
221
+ ```
222
+
223
+ ## Terminal
224
+
225
+ ```python
226
+ session = client.terminal.create()
227
+ client.terminal.send_input(session.session_id, "ls -la\n")
228
+ output = client.terminal.get_history(session.session_id)
229
+ client.terminal.resize(session.session_id, 120, 40)
230
+ client.terminal.send_signal(session.session_id, "SIGINT")
231
+ client.terminal.close(session.session_id)
232
+ ```
233
+
234
+ ## Files
235
+
236
+ ```python
237
+ client.files.list("/var/log")
238
+ client.files.read("/etc/nginx/nginx.conf")
239
+ client.files.write("/tmp/config.json", b'{"key": "value"}')
240
+ client.files.delete("/tmp/old.txt")
241
+ client.files.copy("/src", "/dst")
242
+ client.files.move("/old", "/new")
243
+ client.files.mkdir("/new/dir")
244
+ client.files.info("/path")
245
+ ```
246
+
247
+ ## SDKBaseModel
248
+
249
+ Auto-cleaning Pydantic model:
250
+
251
+ ```python
252
+ from cmdop import SDKBaseModel
253
+
254
+ class Product(SDKBaseModel):
255
+ __base_url__ = "https://shop.com"
256
+ name: str = "" # " iPhone 15 \n" → "iPhone 15"
257
+ price: int = 0 # "$1,299.00" → 1299
258
+ rating: float = 0 # "4.5 stars" → 4.5
259
+ url: str = "" # "/p/123" → "https://shop.com/p/123"
260
+
261
+ products = Product.from_list(raw["items"]) # Auto dedupe + filter
262
+ ```
263
+
264
+ ## Utilities
265
+
266
+ ```python
267
+ from cmdop import get_logger, json_to_toon
268
+
269
+ # Logging (rich console + file)
270
+ log = get_logger(__name__)
271
+ log.info("Starting")
272
+
273
+ # TOON format (30-50% token savings)
274
+ toon = json_to_toon({"name": "Alice", "age": 25})
275
+ # → "name: Alice\nage: 25"
276
+ ```
277
+
278
+ ## Requirements
279
+
280
+ - Python 3.10+
281
+ - CMDOP agent running locally or API key for remote
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cmdop"
3
- version = "0.1.23"
3
+ version = "0.1.25"
4
4
  description = "Python SDK for CMDOP agent interaction"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -124,7 +124,7 @@ from cmdop.logging import (
124
124
  get_log_dir,
125
125
  )
126
126
 
127
- __version__ = "0.1.23"
127
+ __version__ = "0.1.25"
128
128
 
129
129
  __all__ = [
130
130
  # Version
@@ -0,0 +1,17 @@
1
+ """CMDOP SDK helpers."""
2
+
3
+ from cmdop.helpers.formatting import json_to_toon
4
+ from cmdop.helpers.cleaner import JsonCleaner
5
+ from cmdop.helpers.network_analyzer import (
6
+ NetworkAnalyzer,
7
+ NetworkSnapshot,
8
+ RequestSnapshot,
9
+ )
10
+
11
+ __all__ = [
12
+ "json_to_toon",
13
+ "JsonCleaner",
14
+ "NetworkAnalyzer",
15
+ "NetworkSnapshot",
16
+ "RequestSnapshot",
17
+ ]