loopix-sdk 2.30.0__py3-none-any.whl

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 (238) hide show
  1. loopix/__init__.py +260 -0
  2. loopix/api/__init__.py +287 -0
  3. loopix/api/client/__init__.py +8 -0
  4. loopix/api/client/api/__init__.py +1 -0
  5. loopix/api/client/api/sandboxes/__init__.py +1 -0
  6. loopix/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
  7. loopix/api/client/api/sandboxes/get_sandboxes.py +176 -0
  8. loopix/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
  9. loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
  10. loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
  11. loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +212 -0
  12. loopix/api/client/api/sandboxes/get_v2_sandboxes.py +230 -0
  13. loopix/api/client/api/sandboxes/get_v_2_sandboxes_sandbox_id_logs.py +254 -0
  14. loopix/api/client/api/sandboxes/post_sandboxes.py +172 -0
  15. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
  16. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +187 -0
  17. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +181 -0
  18. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +189 -0
  19. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_snapshots.py +195 -0
  20. loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +193 -0
  21. loopix/api/client/api/sandboxes/put_sandboxes_sandbox_id_network.py +199 -0
  22. loopix/api/client/api/snapshots/__init__.py +1 -0
  23. loopix/api/client/api/snapshots/get_snapshots.py +202 -0
  24. loopix/api/client/api/tags/__init__.py +1 -0
  25. loopix/api/client/api/tags/delete_templates_tags.py +174 -0
  26. loopix/api/client/api/tags/get_templates_template_id_tags.py +172 -0
  27. loopix/api/client/api/tags/post_templates_tags.py +176 -0
  28. loopix/api/client/api/templates/__init__.py +1 -0
  29. loopix/api/client/api/templates/delete_templates_template_id.py +157 -0
  30. loopix/api/client/api/templates/get_templates.py +172 -0
  31. loopix/api/client/api/templates/get_templates_aliases_alias.py +167 -0
  32. loopix/api/client/api/templates/get_templates_template_id.py +195 -0
  33. loopix/api/client/api/templates/get_templates_template_id_builds_build_id_logs.py +272 -0
  34. loopix/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +232 -0
  35. loopix/api/client/api/templates/get_templates_template_id_files_hash.py +180 -0
  36. loopix/api/client/api/templates/patch_templates_template_id.py +183 -0
  37. loopix/api/client/api/templates/patch_v_2_templates_template_id.py +185 -0
  38. loopix/api/client/api/templates/post_templates.py +172 -0
  39. loopix/api/client/api/templates/post_templates_template_id.py +181 -0
  40. loopix/api/client/api/templates/post_templates_template_id_builds_build_id.py +170 -0
  41. loopix/api/client/api/templates/post_v2_templates.py +172 -0
  42. loopix/api/client/api/templates/post_v3_templates.py +176 -0
  43. loopix/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +192 -0
  44. loopix/api/client/api/volumes/__init__.py +1 -0
  45. loopix/api/client/api/volumes/delete_volumes_volume_id.py +161 -0
  46. loopix/api/client/api/volumes/get_volumes.py +140 -0
  47. loopix/api/client/api/volumes/get_volumes_volume_id.py +163 -0
  48. loopix/api/client/api/volumes/post_volumes.py +172 -0
  49. loopix/api/client/client.py +286 -0
  50. loopix/api/client/errors.py +16 -0
  51. loopix/api/client/models/__init__.py +185 -0
  52. loopix/api/client/models/admin_build_cancel_result.py +67 -0
  53. loopix/api/client/models/admin_sandbox_kill_result.py +67 -0
  54. loopix/api/client/models/assign_template_tags_request.py +67 -0
  55. loopix/api/client/models/assigned_template_tags.py +68 -0
  56. loopix/api/client/models/aws_registry.py +85 -0
  57. loopix/api/client/models/aws_registry_type.py +8 -0
  58. loopix/api/client/models/build_log_entry.py +89 -0
  59. loopix/api/client/models/build_status_reason.py +95 -0
  60. loopix/api/client/models/connect_sandbox.py +59 -0
  61. loopix/api/client/models/created_access_token.py +100 -0
  62. loopix/api/client/models/created_team_api_key.py +166 -0
  63. loopix/api/client/models/delete_template_tags_request.py +67 -0
  64. loopix/api/client/models/disk_metrics.py +91 -0
  65. loopix/api/client/models/error.py +67 -0
  66. loopix/api/client/models/gcp_registry.py +69 -0
  67. loopix/api/client/models/gcp_registry_type.py +8 -0
  68. loopix/api/client/models/general_registry.py +77 -0
  69. loopix/api/client/models/general_registry_type.py +8 -0
  70. loopix/api/client/models/identifier_masking_details.py +83 -0
  71. loopix/api/client/models/listed_sandbox.py +179 -0
  72. loopix/api/client/models/log_level.py +11 -0
  73. loopix/api/client/models/logs_direction.py +9 -0
  74. loopix/api/client/models/logs_source.py +9 -0
  75. loopix/api/client/models/machine_info.py +83 -0
  76. loopix/api/client/models/max_team_metric.py +78 -0
  77. loopix/api/client/models/mcp_type_0.py +44 -0
  78. loopix/api/client/models/new_access_token.py +59 -0
  79. loopix/api/client/models/new_sandbox.py +224 -0
  80. loopix/api/client/models/new_team_api_key.py +59 -0
  81. loopix/api/client/models/new_volume.py +59 -0
  82. loopix/api/client/models/node.py +160 -0
  83. loopix/api/client/models/node_detail.py +160 -0
  84. loopix/api/client/models/node_metrics.py +122 -0
  85. loopix/api/client/models/node_status.py +12 -0
  86. loopix/api/client/models/node_status_change.py +82 -0
  87. loopix/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
  88. loopix/api/client/models/post_sandboxes_sandbox_id_snapshots_body.py +60 -0
  89. loopix/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
  90. loopix/api/client/models/resumed_sandbox.py +68 -0
  91. loopix/api/client/models/sandbox.py +145 -0
  92. loopix/api/client/models/sandbox_auto_resume_config.py +60 -0
  93. loopix/api/client/models/sandbox_detail.py +267 -0
  94. loopix/api/client/models/sandbox_lifecycle.py +70 -0
  95. loopix/api/client/models/sandbox_log.py +70 -0
  96. loopix/api/client/models/sandbox_log_entry.py +93 -0
  97. loopix/api/client/models/sandbox_log_entry_fields.py +44 -0
  98. loopix/api/client/models/sandbox_logs.py +91 -0
  99. loopix/api/client/models/sandbox_logs_v2_response.py +73 -0
  100. loopix/api/client/models/sandbox_metric.py +126 -0
  101. loopix/api/client/models/sandbox_network_config.py +118 -0
  102. loopix/api/client/models/sandbox_network_config_rules.py +72 -0
  103. loopix/api/client/models/sandbox_network_rule.py +74 -0
  104. loopix/api/client/models/sandbox_network_transform.py +79 -0
  105. loopix/api/client/models/sandbox_network_transform_headers.py +47 -0
  106. loopix/api/client/models/sandbox_network_update_config.py +114 -0
  107. loopix/api/client/models/sandbox_network_update_config_rules.py +71 -0
  108. loopix/api/client/models/sandbox_on_timeout.py +9 -0
  109. loopix/api/client/models/sandbox_pause_request.py +62 -0
  110. loopix/api/client/models/sandbox_state.py +9 -0
  111. loopix/api/client/models/sandbox_volume_mount.py +67 -0
  112. loopix/api/client/models/sandboxes_with_metrics.py +59 -0
  113. loopix/api/client/models/snapshot_info.py +70 -0
  114. loopix/api/client/models/team.py +83 -0
  115. loopix/api/client/models/team_api_key.py +158 -0
  116. loopix/api/client/models/team_metric.py +86 -0
  117. loopix/api/client/models/team_user.py +75 -0
  118. loopix/api/client/models/template.py +225 -0
  119. loopix/api/client/models/template_alias_response.py +67 -0
  120. loopix/api/client/models/template_build.py +139 -0
  121. loopix/api/client/models/template_build_file_upload.py +70 -0
  122. loopix/api/client/models/template_build_info.py +126 -0
  123. loopix/api/client/models/template_build_logs_response.py +73 -0
  124. loopix/api/client/models/template_build_request.py +115 -0
  125. loopix/api/client/models/template_build_request_v2.py +88 -0
  126. loopix/api/client/models/template_build_request_v3.py +107 -0
  127. loopix/api/client/models/template_build_start_v2.py +184 -0
  128. loopix/api/client/models/template_build_status.py +11 -0
  129. loopix/api/client/models/template_legacy.py +207 -0
  130. loopix/api/client/models/template_request_response_v3.py +99 -0
  131. loopix/api/client/models/template_step.py +91 -0
  132. loopix/api/client/models/template_tag.py +78 -0
  133. loopix/api/client/models/template_update_request.py +59 -0
  134. loopix/api/client/models/template_update_response.py +59 -0
  135. loopix/api/client/models/template_with_builds.py +156 -0
  136. loopix/api/client/models/update_team_api_key.py +59 -0
  137. loopix/api/client/models/volume.py +67 -0
  138. loopix/api/client/models/volume_and_token.py +75 -0
  139. loopix/api/client/models/volume_token.py +59 -0
  140. loopix/api/client/py.typed +1 -0
  141. loopix/api/client/types.py +54 -0
  142. loopix/api/client_async/__init__.py +74 -0
  143. loopix/api/client_sync/__init__.py +73 -0
  144. loopix/api/metadata.py +14 -0
  145. loopix/connection_config.py +309 -0
  146. loopix/envd/api.py +170 -0
  147. loopix/envd/filesystem/filesystem_connect.py +193 -0
  148. loopix/envd/filesystem/filesystem_pb2.py +80 -0
  149. loopix/envd/filesystem/filesystem_pb2.pyi +272 -0
  150. loopix/envd/process/process_connect.py +174 -0
  151. loopix/envd/process/process_pb2.py +96 -0
  152. loopix/envd/process/process_pb2.pyi +316 -0
  153. loopix/envd/rpc.py +139 -0
  154. loopix/envd/versions.py +11 -0
  155. loopix/exceptions.py +133 -0
  156. loopix/io_utils.py +57 -0
  157. loopix/paginator.py +52 -0
  158. loopix/py.typed +0 -0
  159. loopix/sandbox/_git/__init__.py +85 -0
  160. loopix/sandbox/_git/args.py +363 -0
  161. loopix/sandbox/_git/auth.py +132 -0
  162. loopix/sandbox/_git/config.py +32 -0
  163. loopix/sandbox/_git/parse.py +222 -0
  164. loopix/sandbox/_git/types.py +149 -0
  165. loopix/sandbox/commands/command_handle.py +69 -0
  166. loopix/sandbox/commands/main.py +39 -0
  167. loopix/sandbox/filesystem/filesystem.py +337 -0
  168. loopix/sandbox/filesystem/watch_handle.py +70 -0
  169. loopix/sandbox/main.py +227 -0
  170. loopix/sandbox/mcp.py +1949 -0
  171. loopix/sandbox/network.py +8 -0
  172. loopix/sandbox/sandbox_api.py +624 -0
  173. loopix/sandbox/signature.py +47 -0
  174. loopix/sandbox/utils.py +34 -0
  175. loopix/sandbox_async/commands/command.py +396 -0
  176. loopix/sandbox_async/commands/command_handle.py +298 -0
  177. loopix/sandbox_async/commands/pty.py +257 -0
  178. loopix/sandbox_async/filesystem/filesystem.py +720 -0
  179. loopix/sandbox_async/filesystem/watch_handle.py +97 -0
  180. loopix/sandbox_async/git.py +1100 -0
  181. loopix/sandbox_async/main.py +987 -0
  182. loopix/sandbox_async/paginator.py +140 -0
  183. loopix/sandbox_async/sandbox_api.py +504 -0
  184. loopix/sandbox_async/utils.py +7 -0
  185. loopix/sandbox_domains.py +5 -0
  186. loopix/sandbox_sync/commands/command.py +420 -0
  187. loopix/sandbox_sync/commands/command_handle.py +239 -0
  188. loopix/sandbox_sync/commands/pty.py +279 -0
  189. loopix/sandbox_sync/filesystem/filesystem.py +710 -0
  190. loopix/sandbox_sync/filesystem/watch_handle.py +102 -0
  191. loopix/sandbox_sync/git.py +1077 -0
  192. loopix/sandbox_sync/main.py +975 -0
  193. loopix/sandbox_sync/paginator.py +140 -0
  194. loopix/sandbox_sync/sandbox_api.py +491 -0
  195. loopix/template/consts.py +45 -0
  196. loopix/template/dockerfile_parser.py +286 -0
  197. loopix/template/logger.py +232 -0
  198. loopix/template/main.py +1368 -0
  199. loopix/template/readycmd.py +144 -0
  200. loopix/template/types.py +194 -0
  201. loopix/template/utils.py +426 -0
  202. loopix/template_async/build_api.py +419 -0
  203. loopix/template_async/main.py +528 -0
  204. loopix/template_sync/build_api.py +409 -0
  205. loopix/template_sync/main.py +529 -0
  206. loopix/volume/client/__init__.py +8 -0
  207. loopix/volume/client/api/__init__.py +1 -0
  208. loopix/volume/client/api/volumes/__init__.py +1 -0
  209. loopix/volume/client/api/volumes/delete_volumecontent_volume_id_path.py +174 -0
  210. loopix/volume/client/api/volumes/get_volumecontent_volume_id_dir.py +204 -0
  211. loopix/volume/client/api/volumes/get_volumecontent_volume_id_file.py +179 -0
  212. loopix/volume/client/api/volumes/get_volumecontent_volume_id_path.py +176 -0
  213. loopix/volume/client/api/volumes/patch_volumecontent_volume_id_path.py +203 -0
  214. loopix/volume/client/api/volumes/post_volumecontent_volume_id_dir.py +239 -0
  215. loopix/volume/client/api/volumes/put_volumecontent_volume_id_file.py +259 -0
  216. loopix/volume/client/client.py +286 -0
  217. loopix/volume/client/errors.py +16 -0
  218. loopix/volume/client/models/__init__.py +13 -0
  219. loopix/volume/client/models/error.py +67 -0
  220. loopix/volume/client/models/patch_volumecontent_volume_id_path_body.py +77 -0
  221. loopix/volume/client/models/volume_entry_stat.py +145 -0
  222. loopix/volume/client/models/volume_entry_stat_type.py +11 -0
  223. loopix/volume/client/py.typed +1 -0
  224. loopix/volume/client/types.py +54 -0
  225. loopix/volume/client_async/__init__.py +88 -0
  226. loopix/volume/client_sync/__init__.py +80 -0
  227. loopix/volume/connection_config.py +145 -0
  228. loopix/volume/types.py +62 -0
  229. loopix/volume/utils.py +52 -0
  230. loopix/volume/volume_async.py +639 -0
  231. loopix/volume/volume_sync.py +639 -0
  232. loopix_connect/__init__.py +1 -0
  233. loopix_connect/client.py +534 -0
  234. loopix_connect/py.typed +0 -0
  235. loopix_sdk-2.30.0.dist-info/METADATA +98 -0
  236. loopix_sdk-2.30.0.dist-info/RECORD +238 -0
  237. loopix_sdk-2.30.0.dist-info/WHEEL +4 -0
  238. loopix_sdk-2.30.0.dist-info/licenses/LICENSE +9 -0
@@ -0,0 +1,222 @@
1
+ from typing import List, Optional
2
+ from urllib.parse import urlparse
3
+
4
+ from loopix.exceptions import InvalidArgumentException
5
+ from loopix.sandbox._git.types import GitBranches, GitFileStatus, GitStatus
6
+
7
+
8
+ def derive_repo_dir_from_url(url: str) -> Optional[str]:
9
+ """
10
+ Derive the default repository directory name from a Git URL.
11
+
12
+ :param url: Git repository URL
13
+ :return: Repository directory name, if it can be determined
14
+ """
15
+ parsed = urlparse(url)
16
+ if parsed.scheme not in ("http", "https"):
17
+ return None
18
+ trimmed_path = parsed.path.rstrip("/")
19
+ if not trimmed_path:
20
+ return None
21
+ last_segment = trimmed_path.split("/")[-1]
22
+ if not last_segment:
23
+ return None
24
+ return last_segment[:-4] if last_segment.endswith(".git") else last_segment
25
+
26
+
27
+ def _parse_ahead_behind(segment: Optional[str]) -> tuple[int, int]:
28
+ """
29
+ Parse the ahead/behind segment from porcelain branch info.
30
+
31
+ :param segment: Segment text like "ahead 2, behind 1"
32
+ :return: Tuple of (ahead, behind)
33
+ """
34
+ if not segment:
35
+ return 0, 0
36
+ ahead = 0
37
+ behind = 0
38
+ if "ahead" in segment:
39
+ try:
40
+ ahead = int(segment.split("ahead")[1].split(",")[0].strip())
41
+ except Exception:
42
+ ahead = 0
43
+ if "behind" in segment:
44
+ try:
45
+ behind = int(segment.split("behind")[1].split(",")[0].strip())
46
+ except Exception:
47
+ behind = 0
48
+ return ahead, behind
49
+
50
+
51
+ def _normalize_branch_name(name: str) -> str:
52
+ """
53
+ Normalize branch names from porcelain branch output.
54
+
55
+ :param name: Raw branch name section
56
+ :return: Normalized branch name
57
+ """
58
+ if name.startswith("HEAD (detached at "):
59
+ return name.replace("HEAD (detached at ", "").rstrip(")")
60
+ return (
61
+ name.replace("HEAD (no branch)", "HEAD")
62
+ .replace("No commits yet on ", "")
63
+ .replace("Initial commit on ", "")
64
+ )
65
+
66
+
67
+ def _derive_status(index_status: str, working_status: str) -> str:
68
+ """
69
+ Derive a normalized status label from porcelain status characters.
70
+
71
+ :param index_status: Index status character
72
+ :param working_status: Working tree status character
73
+ :return: Normalized status label
74
+ """
75
+ statuses = {index_status, working_status}
76
+ if "U" in statuses:
77
+ return "conflict"
78
+ if "R" in statuses:
79
+ return "renamed"
80
+ if "C" in statuses:
81
+ return "copied"
82
+ if "D" in statuses:
83
+ return "deleted"
84
+ if "A" in statuses:
85
+ return "added"
86
+ if "M" in statuses:
87
+ return "modified"
88
+ if "T" in statuses:
89
+ return "typechange"
90
+ if "?" in statuses:
91
+ return "untracked"
92
+ return "unknown"
93
+
94
+
95
+ def parse_git_status(output: str) -> GitStatus:
96
+ """
97
+ Parse `git status --porcelain=1 -b` output into a structured object.
98
+
99
+ :param output: Git status output
100
+ :return: Parsed `GitStatus`
101
+ """
102
+ lines = [line.rstrip() for line in output.split("\n") if line.strip()]
103
+ current_branch: Optional[str] = None
104
+ upstream: Optional[str] = None
105
+ ahead = 0
106
+ behind = 0
107
+ detached = False
108
+ file_status: List[GitFileStatus] = []
109
+
110
+ if not lines:
111
+ return GitStatus(
112
+ current_branch=current_branch,
113
+ upstream=upstream,
114
+ ahead=ahead,
115
+ behind=behind,
116
+ detached=detached,
117
+ file_status=file_status,
118
+ )
119
+
120
+ branch_line = lines[0]
121
+ if branch_line.startswith("## "):
122
+ branch_info = branch_line[3:]
123
+ ahead_start = branch_info.find(" [")
124
+ branch_part = branch_info if ahead_start == -1 else branch_info[:ahead_start]
125
+ ahead_part = None if ahead_start == -1 else branch_info[ahead_start + 2 : -1]
126
+ normalized_branch = _normalize_branch_name(branch_part)
127
+ raw_branch = branch_part
128
+ is_detached = raw_branch.startswith("HEAD (detached at ") or (
129
+ "detached" in raw_branch
130
+ )
131
+
132
+ if is_detached or normalized_branch.startswith("HEAD"):
133
+ detached = True
134
+ elif "..." in normalized_branch:
135
+ branch, upstream_branch = normalized_branch.split("...")
136
+ current_branch = branch or None
137
+ upstream = upstream_branch or None
138
+ else:
139
+ current_branch = normalized_branch or None
140
+
141
+ ahead, behind = _parse_ahead_behind(ahead_part)
142
+
143
+ for line in lines[1:]:
144
+ if line.startswith("?? "):
145
+ name = line[3:]
146
+ file_status.append(
147
+ GitFileStatus(
148
+ name=name,
149
+ status="untracked",
150
+ index_status="?",
151
+ working_tree_status="?",
152
+ staged=False,
153
+ )
154
+ )
155
+ continue
156
+
157
+ if len(line) < 3:
158
+ continue
159
+ index_status = line[0]
160
+ working_status = line[1]
161
+ path = line[3:]
162
+ renamed_from: Optional[str] = None
163
+ name = path
164
+ if " -> " in path:
165
+ renamed_from, name = path.split(" -> ", 1)
166
+
167
+ file_status.append(
168
+ GitFileStatus(
169
+ name=name,
170
+ status=_derive_status(index_status, working_status),
171
+ index_status=index_status,
172
+ working_tree_status=working_status,
173
+ staged=index_status not in (" ", "?"),
174
+ renamed_from=renamed_from,
175
+ )
176
+ )
177
+
178
+ return GitStatus(
179
+ current_branch=current_branch,
180
+ upstream=upstream,
181
+ ahead=ahead,
182
+ behind=behind,
183
+ detached=detached,
184
+ file_status=file_status,
185
+ )
186
+
187
+
188
+ def parse_git_branches(output: str) -> GitBranches:
189
+ """
190
+ Parse `git branch --format=%(refname:short)\t%(HEAD)` output.
191
+
192
+ :param output: Git branch output
193
+ :return: Parsed `GitBranches`
194
+ """
195
+ branches: List[str] = []
196
+ current_branch: Optional[str] = None
197
+
198
+ lines = [line.strip() for line in output.split("\n") if line.strip()]
199
+ for line in lines:
200
+ parts = line.split("\t")
201
+ name = parts[0]
202
+ branches.append(name)
203
+ if len(parts) > 1 and parts[1] == "*":
204
+ current_branch = name
205
+
206
+ return GitBranches(branches=branches, current_branch=current_branch)
207
+
208
+
209
+ def parse_remote_url(output: str, remote: str) -> str:
210
+ """
211
+ Parse a git remote URL output and validate it's present.
212
+
213
+ :param output: Git remote get-url output
214
+ :param remote: Remote name for the error message
215
+ :return: Remote URL
216
+ """
217
+ url = output.strip()
218
+ if not url:
219
+ raise InvalidArgumentException(
220
+ f'Remote "{remote}" URL not found in repository.'
221
+ )
222
+ return url
@@ -0,0 +1,149 @@
1
+ from dataclasses import dataclass
2
+ from typing import List, Optional
3
+
4
+ from typing_extensions import Literal
5
+
6
+ GitResetMode = Literal["soft", "mixed", "hard", "merge", "keep"]
7
+ """Mode for a git reset operation."""
8
+
9
+
10
+ @dataclass
11
+ class GitFileStatus:
12
+ """
13
+ Parsed git status entry for a file.
14
+
15
+ :param name: Path relative to the repository root
16
+ :param status: Normalized status string (e.g. "modified", "added")
17
+ :param index_status: Index status character from porcelain output
18
+ :param working_tree_status: Working tree status character from porcelain output
19
+ :param staged: Whether the change is staged
20
+ :param renamed_from: Original path when the file was renamed
21
+ """
22
+
23
+ name: str
24
+ status: str
25
+ index_status: str
26
+ working_tree_status: str
27
+ staged: bool
28
+ renamed_from: Optional[str] = None
29
+
30
+
31
+ @dataclass
32
+ class GitStatus:
33
+ """
34
+ Parsed git repository status.
35
+
36
+ :param current_branch: Current branch name, if available
37
+ :param upstream: Upstream branch name, if available
38
+ :param ahead: Number of commits the branch is ahead of upstream
39
+ :param behind: Number of commits the branch is behind upstream
40
+ :param detached: Whether HEAD is detached
41
+ :param file_status: List of file status entries
42
+ """
43
+
44
+ current_branch: Optional[str]
45
+ upstream: Optional[str]
46
+ ahead: int
47
+ behind: int
48
+ detached: bool
49
+ file_status: List[GitFileStatus]
50
+
51
+ @property
52
+ def is_clean(self) -> bool:
53
+ """
54
+ Return True when there are no tracked or untracked file changes.
55
+ """
56
+ return len(self.file_status) == 0
57
+
58
+ @property
59
+ def has_changes(self) -> bool:
60
+ """
61
+ Return True when there are any tracked or untracked file changes.
62
+ """
63
+ return len(self.file_status) > 0
64
+
65
+ @property
66
+ def has_staged(self) -> bool:
67
+ """
68
+ Return True when at least one file has staged changes.
69
+ """
70
+ return any(item.staged for item in self.file_status)
71
+
72
+ @property
73
+ def has_untracked(self) -> bool:
74
+ """
75
+ Return True when at least one file is untracked.
76
+ """
77
+ return any(item.status == "untracked" for item in self.file_status)
78
+
79
+ @property
80
+ def has_conflicts(self) -> bool:
81
+ """
82
+ Return True when at least one file is in conflict.
83
+ """
84
+ return any(item.status == "conflict" for item in self.file_status)
85
+
86
+ @property
87
+ def total_count(self) -> int:
88
+ """
89
+ Return the total number of changed files.
90
+ """
91
+ return len(self.file_status)
92
+
93
+ @property
94
+ def staged_count(self) -> int:
95
+ """
96
+ Return the number of files with staged changes.
97
+ """
98
+ return sum(1 for item in self.file_status if item.staged)
99
+
100
+ @property
101
+ def unstaged_count(self) -> int:
102
+ """
103
+ Return the number of files with unstaged changes.
104
+ """
105
+ return sum(1 for item in self.file_status if not item.staged)
106
+
107
+ @property
108
+ def untracked_count(self) -> int:
109
+ """
110
+ Return the number of untracked files.
111
+ """
112
+ return sum(1 for item in self.file_status if item.status == "untracked")
113
+
114
+ @property
115
+ def conflict_count(self) -> int:
116
+ """
117
+ Return the number of files with merge conflicts.
118
+ """
119
+ return sum(1 for item in self.file_status if item.status == "conflict")
120
+
121
+
122
+ @dataclass
123
+ class GitBranches:
124
+ """
125
+ Parsed git branch list.
126
+
127
+ :param branches: List of branch names
128
+ :param current_branch: Current branch name, if available
129
+ """
130
+
131
+ branches: List[str]
132
+ current_branch: Optional[str]
133
+
134
+
135
+ @dataclass
136
+ class ClonePlan:
137
+ """
138
+ Prepared arguments and metadata for git clone.
139
+
140
+ :param args: Command arguments for git clone
141
+ :param repo_path: Repository path to use for post-clone adjustments
142
+ :param sanitized_url: Credential-stripped URL to restore
143
+ :param should_strip: Whether to reset the remote URL after clone
144
+ """
145
+
146
+ args: List[str]
147
+ repo_path: Optional[str]
148
+ sanitized_url: Optional[str]
149
+ should_strip: bool
@@ -0,0 +1,69 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from loopix.exceptions import SandboxException
5
+
6
+ Stdout = str
7
+ """
8
+ Command stdout output.
9
+ """
10
+ Stderr = str
11
+ """
12
+ Command stderr output.
13
+ """
14
+ PtyOutput = bytes
15
+ """
16
+ Pty output.
17
+ """
18
+
19
+
20
+ @dataclass
21
+ class PtySize:
22
+ """
23
+ Pseudo-terminal size.
24
+ """
25
+
26
+ rows: int
27
+ """
28
+ Number of rows.
29
+ """
30
+ cols: int
31
+ """
32
+ Number of columns.
33
+ """
34
+
35
+
36
+ @dataclass
37
+ class CommandResult:
38
+ """
39
+ Command execution result.
40
+ """
41
+
42
+ stderr: str
43
+ """
44
+ Command stderr output.
45
+ """
46
+ stdout: str
47
+ """
48
+ Command stdout output.
49
+ """
50
+ exit_code: int
51
+ """
52
+ Command exit code.
53
+
54
+ `0` if the command finished successfully.
55
+ """
56
+ error: Optional[str]
57
+ """
58
+ Error message from command execution if it failed.
59
+ """
60
+
61
+
62
+ @dataclass
63
+ class CommandExitException(SandboxException, CommandResult):
64
+ """
65
+ Exception raised when a command exits with a non-zero exit code.
66
+ """
67
+
68
+ def __str__(self):
69
+ return f"Command exited with code {self.exit_code} and error:\n{self.stderr}"
@@ -0,0 +1,39 @@
1
+ from dataclasses import dataclass
2
+ from typing import Dict, List, Optional
3
+
4
+
5
+ @dataclass
6
+ class ProcessInfo:
7
+ """
8
+ Information about a command, PTY session or start command running in the sandbox as process.
9
+ """
10
+
11
+ pid: int
12
+ """
13
+ Process ID.
14
+ """
15
+
16
+ tag: Optional[str]
17
+ """
18
+ Custom tag used for identifying special commands like start command in the custom template.
19
+ """
20
+
21
+ cmd: str
22
+ """
23
+ Command that was executed.
24
+ """
25
+
26
+ args: List[str]
27
+ """
28
+ Command arguments.
29
+ """
30
+
31
+ envs: Dict[str, str]
32
+ """
33
+ Environment variables used for the command.
34
+ """
35
+
36
+ cwd: Optional[str]
37
+ """
38
+ Executed command working directory.
39
+ """