zrb 1.8.2__py3-none-any.whl → 1.8.3__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.
zrb/__main__.py CHANGED
@@ -60,8 +60,9 @@ def serve_cli():
60
60
  # run the CLI
61
61
  cli.run(sys.argv[1:])
62
62
  except KeyboardInterrupt:
63
+ # The exception is handled by the task runner
63
64
  print(stylize_warning("\nStopped"), file=sys.stderr)
64
- sys.exit(1)
65
+ pass
65
66
  except RuntimeError as e:
66
67
  if f"{e}".lower() != "event loop is closed":
67
68
  raise e
@@ -22,6 +22,7 @@ install_asdf_prerequisites = CmdTask(
22
22
  name="install-asdf-prerequisites",
23
23
  input=[package_manager_input, use_sudo_input],
24
24
  cmd=get_install_prerequisites_cmd,
25
+ is_interactive=True,
25
26
  )
26
27
 
27
28
 
@@ -13,6 +13,7 @@ install_tmux = CmdTask(
13
13
  name="install-tmux",
14
14
  input=[package_manager_input, use_sudo_input],
15
15
  cmd=get_install_tmux_cmd,
16
+ is_interactive=True,
16
17
  )
17
18
 
18
19
 
@@ -1,10 +1,18 @@
1
1
  from zrb.builtin.group import setup_group
2
2
  from zrb.task.cmd_task import CmdTask
3
3
 
4
- update_ubuntu = CmdTask(name="update-ubuntu", cmd="sudo apt update", render_cmd=False)
4
+ update_ubuntu = CmdTask(
5
+ name="update-ubuntu",
6
+ cmd="sudo apt update",
7
+ render_cmd=False,
8
+ is_interactive=True,
9
+ )
5
10
 
6
11
  upgrade_todo = CmdTask(
7
- name="upgrade-ubuntu", cmd="sudo apt upgrade -y", render_cmd=False
12
+ name="upgrade-ubuntu",
13
+ cmd="sudo apt upgrade -y",
14
+ render_cmd=False,
15
+ is_interactive=True,
8
16
  )
9
17
  update_ubuntu >> upgrade_todo
10
18
 
@@ -22,6 +30,7 @@ setup_ubuntu = setup_group.add_task(
22
30
  "cmake make tree tmux zsh neovim xdotool xsel",
23
31
  ],
24
32
  render_cmd=False,
33
+ is_interactive=True,
25
34
  ),
26
35
  alias="ubuntu",
27
36
  )
@@ -13,6 +13,7 @@ install_zsh = CmdTask(
13
13
  name="install-zsh",
14
14
  input=[package_manager_input, use_sudo_input],
15
15
  cmd=get_install_zsh_cmd,
16
+ is_interactive=True,
16
17
  )
17
18
 
18
19
  install_omz = CmdTask(
zrb/session/session.py CHANGED
@@ -51,9 +51,9 @@ class Session(AnySession):
51
51
  self._shared_ctx = shared_ctx
52
52
  self._shared_ctx.set_session(self)
53
53
  self._parent = parent
54
- self._action_coros: dict[AnyTask, Coroutine] = {}
55
- self._monitoring_coros: dict[AnyTask, Coroutine] = {}
56
- self._coros: list[Coroutine] = []
54
+ self._action_coros: dict[AnyTask, asyncio.Task] = {}
55
+ self._monitoring_coros: dict[AnyTask, asyncio.Task] = {}
56
+ self._coros: list[asyncio.Task] = []
57
57
  self._colors = [
58
58
  GREEN,
59
59
  YELLOW,
@@ -100,6 +100,12 @@ class Session(AnySession):
100
100
  self._is_terminated = True
101
101
  for task_status in self._task_status.values():
102
102
  task_status.mark_as_terminated()
103
+ for task in self._action_coros.values():
104
+ task.cancel()
105
+ for task in self._monitoring_coros.values():
106
+ task.cancel()
107
+ for task in self._coros:
108
+ task.cancel()
103
109
 
104
110
  @property
105
111
  def is_terminated(self) -> bool:
@@ -180,16 +186,17 @@ class Session(AnySession):
180
186
 
181
187
  def defer_monitoring(self, task: AnyTask, coro: Coroutine):
182
188
  self._register_single_task(task)
183
- self._monitoring_coros[task] = coro
189
+ self._monitoring_coros[task] = asyncio.create_task(coro)
184
190
 
185
191
  def defer_action(self, task: AnyTask, coro: Coroutine):
186
192
  self._register_single_task(task)
187
- self._action_coros[task] = coro
193
+ self._action_coros[task] = asyncio.create_task(coro)
188
194
 
189
195
  def defer_coro(self, coro: Coroutine):
190
- self._coros.append(coro)
196
+ task = asyncio.create_task(coro)
197
+ self._coros.append(task)
191
198
  self._coros = [
192
- existing_coro for existing_coro in self._coros if not existing_coro.done()
199
+ existing_task for existing_task in self._coros if not existing_task.done()
193
200
  ]
194
201
 
195
202
  async def wait_deferred(self):
@@ -175,8 +175,7 @@ async def execute_action_with_retry(task: "BaseTask", session: AnySession) -> An
175
175
  ctx.log_warning("Task cancelled or interrupted")
176
176
  session.get_task_status(task).mark_as_failed() # Mark as failed on cancel
177
177
  # Do not trigger fallbacks/successors on cancellation
178
- return # Or re-raise? Depends on desired cancellation behavior
179
-
178
+ raise
180
179
  except BaseException as e:
181
180
  ctx.log_error(f"Attempt {attempt + 1}/{max_attempt} failed: {e}")
182
181
  session.get_task_status(
@@ -38,26 +38,30 @@ async def run_and_cleanup(
38
38
  ctx = task.get_ctx(session) # Get context for logging
39
39
  ctx.log_info("Terminating session after run completion/error.")
40
40
  session.terminate()
41
-
42
41
  # Clean up other potentially running asyncio tasks (excluding the main one)
43
42
  # Be cautious with blanket cancellation if other background tasks are expected
44
- pending = [
45
- t for t in asyncio.all_tasks() if t is not main_task_coro and not t.done()
46
- ]
47
- if pending:
48
- ctx = task.get_ctx(session) # Get context for logging
49
- ctx.log_debug(f"Cleaning up {len(pending)} pending asyncio tasks...")
50
- for t in pending:
51
- t.cancel()
52
- try:
53
- # Give cancelled tasks a moment to process cancellation
54
- await asyncio.wait(pending, timeout=1.0)
55
- except asyncio.CancelledError:
56
- # Expected if tasks handle cancellation promptly
57
- pass
58
- except Exception as cleanup_exc:
59
- # Log errors during cleanup if necessary
60
- ctx.log_warning(f"Error during task cleanup: {cleanup_exc}")
43
+ try:
44
+ pending = [
45
+ t
46
+ for t in asyncio.all_tasks()
47
+ if t is not main_task_coro and not t.done()
48
+ ]
49
+ if pending:
50
+ ctx = task.get_ctx(session) # Get context for logging
51
+ ctx.log_debug(f"Cleaning up {len(pending)} pending asyncio tasks...")
52
+ for t in pending:
53
+ t.cancel()
54
+ try:
55
+ # Give cancelled tasks a moment to process cancellation
56
+ await asyncio.wait(pending, timeout=1.0)
57
+ except asyncio.CancelledError:
58
+ # Expected if tasks handle cancellation promptly
59
+ pass
60
+ except Exception as cleanup_exc:
61
+ # Log errors during cleanup if necessary
62
+ ctx.log_warning(f"Error during task cleanup: {cleanup_exc}")
63
+ except RuntimeError as cleanup_exc:
64
+ ctx.log_warning(f"Error during task cleanup: {cleanup_exc}")
61
65
 
62
66
 
63
67
  async def run_task_async(
zrb/task/cmd_task.py CHANGED
@@ -48,6 +48,7 @@ class CmdTask(BaseTask):
48
48
  warn_unrecommended_command: bool | None = None,
49
49
  max_output_line: int = 1000,
50
50
  max_error_line: int = 1000,
51
+ is_interactive: bool = False,
51
52
  execute_condition: BoolAttr = True,
52
53
  retries: int = 2,
53
54
  retry_period: float = 0,
@@ -104,6 +105,7 @@ class CmdTask(BaseTask):
104
105
  self._max_error_line = max_error_line
105
106
  self._should_plain_print = plain_print
106
107
  self._should_warn_unrecommended_command = warn_unrecommended_command
108
+ self._is_interactive = is_interactive
107
109
 
108
110
  async def _exec_action(self, ctx: AnyContext) -> CmdResult:
109
111
  """Turn _cmd attribute into subprocess.Popen and execute it as task's action.
@@ -140,6 +142,7 @@ class CmdTask(BaseTask):
140
142
  register_pid_method=lambda pid: ctx.xcom.get(xcom_pid_key).push(pid),
141
143
  max_output_line=self._max_output_line,
142
144
  max_error_line=self._max_error_line,
145
+ is_interactive=self._is_interactive,
143
146
  )
144
147
  # Check for errors
145
148
  if return_code > 0:
zrb/util/cmd/command.py CHANGED
@@ -1,8 +1,11 @@
1
1
  import asyncio
2
2
  import os
3
3
  import re
4
+ import signal
4
5
  import sys
6
+ from collections import deque
5
7
  from collections.abc import Callable
8
+ from typing import TextIO
6
9
 
7
10
  import psutil
8
11
 
@@ -59,67 +62,112 @@ async def run_command(
59
62
  register_pid_method: Callable[[int], None] | None = None,
60
63
  max_output_line: int = 1000,
61
64
  max_error_line: int = 1000,
65
+ is_interactive: bool = False,
62
66
  ) -> tuple[CmdResult, int]:
63
- async def __read_stream(
64
- stream, print_method: Callable[..., None], max_lines: int
65
- ) -> str:
66
- lines = []
67
- if hasattr(stream, "_limit"):
68
- # The limit is set to 10 MB
69
- stream._limit = 1024 * 1024 * 10
70
- while True:
71
- line = None
72
- try:
73
- line = await stream.readline()
74
- except asyncio.exceptions.CancelledError:
75
- pass
76
- except asyncio.exceptions.LimitOverrunError as e:
77
- # Recover by reading a limited chunk instead
78
- await stream.read(e.consumed)
79
- line = "<output line too long>"
80
- except BaseException as e:
81
- line = f"Error while reading stream {type(e)} {e}"
82
- pass
83
- if not line:
84
- break
85
- try:
86
- line = line.decode("utf-8").rstrip()
87
- except Exception:
88
- pass
89
- lines.append(line)
90
- if len(lines) > max_lines:
91
- lines.pop(0) # Keep only the last max_lines
92
- print_method(line)
93
- return "\n".join(lines)
94
-
67
+ """
68
+ Executes a command using the robust `readline` strategy with a memory
69
+ limit, and correctly handles terminal control characters in the output.
70
+ Please note that `interactive` execution is generally not recommended and thus
71
+ disabled by default.
72
+ When using `interactive execution, the command will not be started in new session
73
+ and will share the same stdin as the main process, which might trigger race condition.
74
+ You can use interactive execution for a limited usecase when the command
75
+ require user input.
76
+ """
95
77
  actual_print_method = print_method if print_method is not None else print
96
- cmd_process = None
97
78
  if cwd is None:
98
79
  cwd = os.getcwd()
99
- if env_map is None:
100
- env_map = os.environ
80
+ # While environment variables alone weren't the fix, they are still
81
+ # good practice for encouraging simpler output from tools.
82
+ child_env = (env_map or os.environ).copy()
83
+ child_env["TERM"] = "xterm-256color" # A capable but standard terminal
84
+ child_env["NO_COLOR"] = "0" # Explicitly allow color
101
85
  cmd_process = await asyncio.create_subprocess_exec(
102
86
  *cmd,
103
87
  cwd=cwd,
104
- stdin=sys.stdin if sys.stdin.isatty() else None,
88
+ env=child_env,
89
+ start_new_session=not is_interactive,
90
+ stdin=__get_cmd_stdin(is_interactive),
105
91
  stdout=asyncio.subprocess.PIPE,
106
92
  stderr=asyncio.subprocess.PIPE,
107
- env=env_map,
108
- bufsize=0,
93
+ limit=10 * 10 * 1024, # Buffer memory limit
109
94
  )
110
95
  if register_pid_method is not None:
111
96
  register_pid_method(cmd_process.pid)
97
+ # Use the new, simple, and correct stream reader.
112
98
  stdout_task = asyncio.create_task(
113
99
  __read_stream(cmd_process.stdout, actual_print_method, max_output_line)
114
100
  )
115
101
  stderr_task = asyncio.create_task(
116
102
  __read_stream(cmd_process.stderr, actual_print_method, max_error_line)
117
103
  )
118
- # Wait for process to complete and gather stdout/stderr
119
- return_code = await cmd_process.wait()
120
- stdout = await stdout_task
121
- stderr = await stderr_task
122
- return CmdResult(stdout, stderr), return_code
104
+ try:
105
+ return_code = await cmd_process.wait()
106
+ stdout, stderr = await asyncio.gather(stdout_task, stderr_task)
107
+ return CmdResult(stdout, stderr), return_code
108
+ except (KeyboardInterrupt, asyncio.CancelledError):
109
+ try:
110
+ os.killpg(cmd_process.pid, signal.SIGINT)
111
+ await asyncio.wait_for(cmd_process.wait(), timeout=2.0)
112
+ except asyncio.TimeoutError:
113
+ # If it doesn't terminate, kill it forcefully
114
+ actual_print_method(
115
+ f"Process {cmd_process.pid} did not terminate gracefully, killing."
116
+ )
117
+ kill_pid(cmd_process.pid, print_method=actual_print_method)
118
+ except Exception:
119
+ pass
120
+ # Final cleanup
121
+ stdout_task.cancel()
122
+ stderr_task.cancel()
123
+ await asyncio.gather(stdout_task, stderr_task, return_exceptions=True)
124
+ raise
125
+
126
+
127
+ def __get_cmd_stdin(is_interactive: bool) -> int | TextIO:
128
+ if is_interactive and sys.stdin.isatty():
129
+ return sys.stdin
130
+ return asyncio.subprocess.DEVNULL
131
+
132
+
133
+ async def __read_stream(
134
+ stream: asyncio.StreamReader,
135
+ print_method: Callable[..., None],
136
+ max_lines: int,
137
+ ) -> str:
138
+ """
139
+ Reads from the stream using the robust `readline()` and correctly
140
+ interprets carriage returns (`\r`) as distinct print events.
141
+ """
142
+ captured_lines = deque(maxlen=max_lines if max_lines > 0 else 0)
143
+ while True:
144
+ try:
145
+ line_bytes = await stream.readline()
146
+ if not line_bytes:
147
+ break
148
+ except ValueError:
149
+ # Safety valve for the memory limit.
150
+ error_msg = "[ERROR] A single line of output was too long to process."
151
+ print_method(error_msg)
152
+ if max_lines > 0:
153
+ captured_lines.append(error_msg)
154
+ break
155
+ except (KeyboardInterrupt, asyncio.CancelledError):
156
+ raise
157
+ except Exception:
158
+ break
159
+ decoded_line = line_bytes.decode("utf-8", errors="replace")
160
+ parts = decoded_line.replace("\r", "\n").splitlines()
161
+ for part in parts:
162
+ clean_part = part.rstrip()
163
+ if clean_part:
164
+ try:
165
+ print_method(clean_part, end="\r\n")
166
+ except Exception:
167
+ print_method(clean_part)
168
+ if max_lines > 0:
169
+ captured_lines.append(clean_part)
170
+ return "\r\n".join(captured_lines)
123
171
 
124
172
 
125
173
  def kill_pid(pid: int, print_method: Callable[..., None] | None = None):
@@ -132,10 +180,13 @@ def kill_pid(pid: int, print_method: Callable[..., None] | None = None):
132
180
  Defaults to the built-in print function.
133
181
  """
134
182
  actual_print_method = print_method if print_method is not None else print
135
- parent = psutil.Process(pid)
136
- children = parent.children(recursive=True)
137
- for child in children:
138
- actual_print_method(f"Killing child process {child.pid}")
139
- child.terminate()
140
- actual_print_method(f"Killing process {pid}")
141
- parent.terminate()
183
+ try:
184
+ parent = psutil.Process(pid)
185
+ children = parent.children(recursive=True)
186
+ for child in children:
187
+ actual_print_method(f"Killing child process {child.pid}")
188
+ child.kill()
189
+ actual_print_method(f"Killing process {pid}")
190
+ parent.kill()
191
+ except psutil.NoSuchProcess:
192
+ actual_print_method(f"Process with pid: {pid} already terminated")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zrb
3
- Version: 1.8.2
3
+ Version: 1.8.3
4
4
  Summary: Your Automation Powerhouse
5
5
  Home-page: https://github.com/state-alchemists/zrb
6
6
  License: AGPL-3.0-or-later
@@ -194,31 +194,35 @@ Then open your browser and visit `http://localhost:21213`
194
194
  ![Zrb Web UI](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/zrb-web-ui.png)
195
195
 
196
196
 
197
- # 🐋 As Container
197
+ # 🐋 Running Zrb as a Container
198
198
 
199
- Zrb has two version of container:
200
- - The normal version
201
- - The dind (Docker in Docker) version
199
+ Zrb can be run in a containerized environment, offering two distinct versions to suit different needs:
202
200
 
203
- The dind version has builtin docker command, and is suitable to access host's docker CLI command.
201
+ - **Standard Version**: Ideal for general use cases where Docker CLI access is not required.
202
+ - **Dind (Docker in Docker) Version**: Includes built-in Docker commands, perfect for scenarios where you need to access the host's Docker CLI.
204
203
 
205
- To run the normal version, you can execute:
204
+ ### Standard Version
205
+
206
+ The standard version of the Zrb container is suitable for most automation tasks. To run this version, execute the following command:
206
207
 
207
208
  ```bash
208
- # docker run -v <host-path>:<container-path> -it stalchmst/zrb:<version> <command>
209
+ # Replace <host-path> and <container-path> with your desired paths
209
210
  docker run -v ${HOME}:/zrb-home -it --rm stalchmst/zrb:1.8.1 zrb
210
211
  ```
211
212
 
212
- While to run the dind version, you can execute:
213
+ ### Dind Version
214
+
215
+ The Dind version is tailored for advanced use cases where Docker commands need to be executed within the container. This version allows the container to interact with the host's Docker daemon. To run the Dind version, use the command below:
213
216
 
214
217
  ```bash
215
- # docker run -v <host-path>:<container-path> -it stalchmst/zrb:<version>-dind <command>
218
+ # Replace <host-path> and <container-path> with your desired paths
216
219
  docker run \
217
220
  -v ${HOME}:/zrb-home \
218
221
  -v /var/run/docker.sock:/var/run/docker.sock \
219
222
  -it --rm stalchmst/zrb:1.8.1-dind docker ps
220
223
  ```
221
224
 
225
+ > **Note:** The Dind (Docker in Docker) version of the container is larger in size compared to the standard version due to the inclusion of Docker CLI tools. Consider this when choosing the appropriate version for your needs.
222
226
 
223
227
  # 🎥 Demo & Documentation
224
228
 
@@ -1,5 +1,5 @@
1
1
  zrb/__init__.py,sha256=e0fZglzFsjO-jz0HhHaBV5Vm0e3MZJBtXcrgDOPESB0,3103
2
- zrb/__main__.py,sha256=Kr_AkgmwBEycQcPVkZmrNh4hp2g62G-7ZXeOhJg0Qis,2603
2
+ zrb/__main__.py,sha256=aeIpBjlLef8bfdp0CYumnn5jVkHDPS5bwAxfuCJVUNI,2650
3
3
  zrb/attr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  zrb/attr/type.py,sha256=4TV5gPYMMrKh5V-yB6iRYKCbsXAH_AvGXMsjxKLHcUs,568
5
5
  zrb/builtin/__init__.py,sha256=N-h-BoXWv0jYOldixXwgk6ekiWtrGZsGv57iqonsYdc,2657
@@ -192,15 +192,15 @@ zrb/builtin/project/create/project-template/zrb_init.py,sha256=kY0x5MrkIVl3l1GtN
192
192
  zrb/builtin/project/create/project_task.py,sha256=az-yd2FRDmGZ_7qxslmW11ryTeI3yttRmXtlFnZhF00,1132
193
193
  zrb/builtin/python.py,sha256=e-yyVWovwlxCBLyXGsd9Ek3IAL_X-Q_hpNNOPoRjOgk,349
194
194
  zrb/builtin/random.py,sha256=p9kCvosSiSJyuGQrlrXuIQT8TRDGxXhfiUbsm3GFPd0,1793
195
- zrb/builtin/setup/asdf/asdf.py,sha256=n_dWoyYa7vZ9tBCy8hgxmSWZ-XHltC1R70mIVZQhnXY,2419
195
+ zrb/builtin/setup/asdf/asdf.py,sha256=_k60yiRiKbRPh_eJVI4Nx_ZmmClOlOb9G0b0KhSGo1M,2444
196
196
  zrb/builtin/setup/asdf/asdf_helper.py,sha256=6jARtyIAE1H82HKVQ84D25RrMAsAip_gD28X9ZlaTCk,1205
197
197
  zrb/builtin/setup/common_input.py,sha256=zIVVsZsNlSoV2Fw8kyt7g5B8XAU8cWBsT865NPoddwo,848
198
198
  zrb/builtin/setup/latex/ubuntu.py,sha256=er9wJAT4CpmghIaiIPFb3FvgqAn1aqU5UgX7GHL3FjA,577
199
- zrb/builtin/setup/tmux/tmux.py,sha256=YaiZKQODsBDip2ya5pA02c2-lfLDU8VKGbeHwFWI43s,1403
199
+ zrb/builtin/setup/tmux/tmux.py,sha256=cV-azDzLtNYtwxucmYqpPqoeSVz3lxP2GOc1-I1yhKs,1428
200
200
  zrb/builtin/setup/tmux/tmux_config.sh,sha256=wQCb4Q-mNkxIPOcvpN84X9RUWkGY16u3Vd-pOhVidgg,416
201
201
  zrb/builtin/setup/tmux/tmux_helper.py,sha256=M03l0wfL25TzGGp6lnVfX40ayT_x7N2lz-nz2chO7PU,396
202
- zrb/builtin/setup/ubuntu.py,sha256=oOSN7Eq7arEpY2i0vWHPR2owio6dqqOvceteYrgmbYw,1019
203
- zrb/builtin/setup/zsh/zsh.py,sha256=b9_UDSJpOT246RiL5BNr6N1jqNHzoUQircZ_u6dkU5g,1820
202
+ zrb/builtin/setup/ubuntu.py,sha256=N4F55xzWKy0Hm115Qr19BADO29thDN7HB5myUaHZDBQ,1122
203
+ zrb/builtin/setup/zsh/zsh.py,sha256=wy7WSYIie6GprCqH4J2ygcTjWdOeRnxmOZcz8fRykcg,1845
204
204
  zrb/builtin/setup/zsh/zsh_config.sh,sha256=SRkcXvVT3tdfS1UDT0-dSj2PKXPLohhyakY6tUEQPjc,4764
205
205
  zrb/builtin/setup/zsh/zsh_helper.py,sha256=1zF1FH0oEPVAVhMA20tsdk1H0RPMCkLusYX8twsTbGI,393
206
206
  zrb/builtin/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -315,7 +315,7 @@ zrb/runner/web_util/token.py,sha256=6Yqp6mQJJMAOsSkAN-6dvtdiQbAv5xtll9jOmNYzbUY,
315
315
  zrb/runner/web_util/user.py,sha256=vE61pDjHoaHw9K0YAv1Gu2zWX2WkM2aWG-8776_aAiM,2061
316
316
  zrb/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
317
317
  zrb/session/any_session.py,sha256=x57mS15E-AfUjdVxwOWEzCBjW32zjer7WoeBw0guoDc,5266
318
- zrb/session/session.py,sha256=aDaLV1tuE7PhcC17LMk6c7KiSs7yX7QXI9O-Z4MMZO8,9967
318
+ zrb/session/session.py,sha256=wkWwueMotpbXaMew1JKil4QMR3UbcBbx4IAZWKSFYjY,10272
319
319
  zrb/session_state_log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
320
320
  zrb/session_state_log/session_state_log.py,sha256=VVghDMU72PbrvnzQ7MJuc-KTJ5P5fX0FYuCh3Rlwd9M,709
321
321
  zrb/session_state_logger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -326,13 +326,13 @@ zrb/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
326
326
  zrb/task/any_task.py,sha256=zklUjkLRQ62TEvfnOUUYfXChj8Zk4igee3w8V3_rN08,5846
327
327
  zrb/task/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
328
328
  zrb/task/base/context.py,sha256=73k3fKwup0AJwTTLpay0f_-axJextaxgbTem4w4Bmas,3670
329
- zrb/task/base/execution.py,sha256=lB6cfivk-EM6sZSaPjYs_ufb7jb-A2jLJNhBupwBFgI,11101
330
- zrb/task/base/lifecycle.py,sha256=YIugyqRmEKMnc9MTCDEZr9jVhie3Kwt7LTMtAVAN9Ks,7278
329
+ zrb/task/base/execution.py,sha256=scDLfNYBe8Bc8Ct1LCIKmFtjpPxm7FjqZ2bJXIQAzv8,11042
330
+ zrb/task/base/lifecycle.py,sha256=4tGeN9yE5sQt1lHYq-1_vOFSGk1z8JneNCbVUWFVm-Q,7507
331
331
  zrb/task/base/monitoring.py,sha256=UAOEcPiYNtZR4FFxzWCosuOEFE_P3c4GT5vAhQmohqI,5663
332
332
  zrb/task/base/operators.py,sha256=uAMFqpZJsPnCrojgOl1FUDXTS15mtOa_IqiAXltyYRU,1576
333
333
  zrb/task/base_task.py,sha256=Y5JNzU6Y1E8RKGdj16HTlhny0v8N2I98GCA4D8h6Kmw,10962
334
334
  zrb/task/base_trigger.py,sha256=WSGcmBcGAZw8EzUXfmCjqJQkz8GEmi1RzogpF6A1V4s,6902
335
- zrb/task/cmd_task.py,sha256=3JFkWZEhyrQAwbQJs2pgICBmkohUR9T-hjXw82JyNtA,10720
335
+ zrb/task/cmd_task.py,sha256=irGi0txTcsvGhxjfem4_radR4csNXhgtfcxruSF1LFI,10853
336
336
  zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
337
337
  zrb/task/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
338
338
  zrb/task/llm/agent.py,sha256=6wGSsw03GdY_fj12CsJh7wxB6BnE13N8RYXaWfbiUsk,5451
@@ -361,7 +361,7 @@ zrb/util/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
361
361
  zrb/util/cli/style.py,sha256=D_548KG1gXEirQGdkAVTc81vBdCeInXtnG1gV1yabBA,6655
362
362
  zrb/util/cli/subcommand.py,sha256=umTZIlrL-9g-qc_eRRgdaQgK-whvXK1roFfvnbuY7NQ,1753
363
363
  zrb/util/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
364
- zrb/util/cmd/command.py,sha256=CsS6KKATOAmM9F482Ox5kVV8hRgFcueqvB5obEcJENs,5211
364
+ zrb/util/cmd/command.py,sha256=WpEMWVL9hBsxptvDHmRR93_cJ2zP05BJ2h9-tP93M1Y,7473
365
365
  zrb/util/cmd/remote.py,sha256=NGQq2_IrUMDoZz3qmcgtnNYVGjMHaBKQpZxImf0yfXA,1296
366
366
  zrb/util/codemod/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
367
367
  zrb/util/codemod/modification_mode.py,sha256=z_4U2gjskEHkFm6UtBe_Wbm-erufYaXgPbdCQ6CZMlw,128
@@ -388,7 +388,7 @@ zrb/util/string/name.py,sha256=SXEfxJ1-tDOzHqmSV8kvepRVyMqs2XdV_vyoh_9XUu0,1584
388
388
  zrb/util/todo.py,sha256=VGISej2KQZERpornK-8X7bysp4JydMrMUTnG8B0-liI,20708
389
389
  zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
390
390
  zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
391
- zrb-1.8.2.dist-info/METADATA,sha256=NRlAG2IfE1oZDH9CDlAn9hAaQ8Arn5ytoXyZ6pdDZBA,9086
392
- zrb-1.8.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
393
- zrb-1.8.2.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
394
- zrb-1.8.2.dist-info/RECORD,,
391
+ zrb-1.8.3.dist-info/METADATA,sha256=VMBmGEImGJOvME1Jr6IrdYEWexUsmYcmgh8kmhE-kT4,9760
392
+ zrb-1.8.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
393
+ zrb-1.8.3.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
394
+ zrb-1.8.3.dist-info/RECORD,,
File without changes