zrb 1.21.29__py3-none-any.whl → 2.0.0a4__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.

Potentially problematic release.


This version of zrb might be problematic. Click here for more details.

Files changed (192) hide show
  1. zrb/__init__.py +118 -129
  2. zrb/builtin/__init__.py +54 -2
  3. zrb/builtin/llm/chat.py +147 -0
  4. zrb/callback/callback.py +8 -1
  5. zrb/cmd/cmd_result.py +2 -1
  6. zrb/config/config.py +491 -280
  7. zrb/config/helper.py +84 -0
  8. zrb/config/web_auth_config.py +50 -35
  9. zrb/context/any_shared_context.py +13 -2
  10. zrb/context/context.py +31 -3
  11. zrb/context/print_fn.py +13 -0
  12. zrb/context/shared_context.py +14 -1
  13. zrb/input/option_input.py +30 -2
  14. zrb/llm/agent/__init__.py +9 -0
  15. zrb/llm/agent/agent.py +215 -0
  16. zrb/llm/agent/summarizer.py +20 -0
  17. zrb/llm/app/__init__.py +10 -0
  18. zrb/llm/app/completion.py +281 -0
  19. zrb/llm/app/confirmation/allow_tool.py +66 -0
  20. zrb/llm/app/confirmation/handler.py +178 -0
  21. zrb/llm/app/confirmation/replace_confirmation.py +77 -0
  22. zrb/llm/app/keybinding.py +34 -0
  23. zrb/llm/app/layout.py +117 -0
  24. zrb/llm/app/lexer.py +155 -0
  25. zrb/llm/app/redirection.py +28 -0
  26. zrb/llm/app/style.py +16 -0
  27. zrb/llm/app/ui.py +733 -0
  28. zrb/llm/config/__init__.py +4 -0
  29. zrb/llm/config/config.py +122 -0
  30. zrb/llm/config/limiter.py +247 -0
  31. zrb/llm/history_manager/__init__.py +4 -0
  32. zrb/llm/history_manager/any_history_manager.py +23 -0
  33. zrb/llm/history_manager/file_history_manager.py +91 -0
  34. zrb/llm/history_processor/summarizer.py +108 -0
  35. zrb/llm/note/__init__.py +3 -0
  36. zrb/llm/note/manager.py +122 -0
  37. zrb/llm/prompt/__init__.py +29 -0
  38. zrb/llm/prompt/claude_compatibility.py +92 -0
  39. zrb/llm/prompt/compose.py +55 -0
  40. zrb/llm/prompt/default.py +51 -0
  41. zrb/llm/prompt/markdown/mandate.md +23 -0
  42. zrb/llm/prompt/markdown/persona.md +3 -0
  43. zrb/llm/prompt/markdown/summarizer.md +21 -0
  44. zrb/llm/prompt/note.py +41 -0
  45. zrb/llm/prompt/system_context.py +46 -0
  46. zrb/llm/prompt/zrb.py +41 -0
  47. zrb/llm/skill/__init__.py +3 -0
  48. zrb/llm/skill/manager.py +86 -0
  49. zrb/llm/task/__init__.py +4 -0
  50. zrb/llm/task/llm_chat_task.py +316 -0
  51. zrb/llm/task/llm_task.py +245 -0
  52. zrb/llm/tool/__init__.py +39 -0
  53. zrb/llm/tool/bash.py +75 -0
  54. zrb/llm/tool/code.py +266 -0
  55. zrb/llm/tool/file.py +419 -0
  56. zrb/llm/tool/note.py +70 -0
  57. zrb/{builtin/llm → llm}/tool/rag.py +8 -5
  58. zrb/llm/tool/search/brave.py +53 -0
  59. zrb/llm/tool/search/searxng.py +47 -0
  60. zrb/llm/tool/search/serpapi.py +47 -0
  61. zrb/llm/tool/skill.py +19 -0
  62. zrb/llm/tool/sub_agent.py +70 -0
  63. zrb/llm/tool/web.py +97 -0
  64. zrb/llm/tool/zrb_task.py +66 -0
  65. zrb/llm/util/attachment.py +101 -0
  66. zrb/llm/util/prompt.py +104 -0
  67. zrb/llm/util/stream_response.py +178 -0
  68. zrb/session/any_session.py +0 -3
  69. zrb/session/session.py +1 -1
  70. zrb/task/base/context.py +25 -13
  71. zrb/task/base/execution.py +52 -47
  72. zrb/task/base/lifecycle.py +7 -4
  73. zrb/task/base_task.py +48 -49
  74. zrb/task/base_trigger.py +4 -1
  75. zrb/task/cmd_task.py +6 -0
  76. zrb/task/http_check.py +11 -5
  77. zrb/task/make_task.py +3 -0
  78. zrb/task/rsync_task.py +5 -0
  79. zrb/task/scaffolder.py +7 -4
  80. zrb/task/scheduler.py +3 -0
  81. zrb/task/tcp_check.py +6 -4
  82. zrb/util/ascii_art/art/bee.txt +17 -0
  83. zrb/util/ascii_art/art/cat.txt +9 -0
  84. zrb/util/ascii_art/art/ghost.txt +16 -0
  85. zrb/util/ascii_art/art/panda.txt +17 -0
  86. zrb/util/ascii_art/art/rose.txt +14 -0
  87. zrb/util/ascii_art/art/unicorn.txt +15 -0
  88. zrb/util/ascii_art/banner.py +92 -0
  89. zrb/util/cli/markdown.py +22 -2
  90. zrb/util/cmd/command.py +33 -10
  91. zrb/util/file.py +51 -32
  92. zrb/util/match.py +78 -0
  93. zrb/util/run.py +3 -3
  94. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/METADATA +9 -15
  95. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/RECORD +100 -128
  96. zrb/attr/__init__.py +0 -0
  97. zrb/builtin/llm/attachment.py +0 -40
  98. zrb/builtin/llm/chat_completion.py +0 -274
  99. zrb/builtin/llm/chat_session.py +0 -270
  100. zrb/builtin/llm/chat_session_cmd.py +0 -288
  101. zrb/builtin/llm/chat_trigger.py +0 -79
  102. zrb/builtin/llm/history.py +0 -71
  103. zrb/builtin/llm/input.py +0 -27
  104. zrb/builtin/llm/llm_ask.py +0 -269
  105. zrb/builtin/llm/previous-session.js +0 -21
  106. zrb/builtin/llm/tool/__init__.py +0 -0
  107. zrb/builtin/llm/tool/api.py +0 -75
  108. zrb/builtin/llm/tool/cli.py +0 -52
  109. zrb/builtin/llm/tool/code.py +0 -236
  110. zrb/builtin/llm/tool/file.py +0 -560
  111. zrb/builtin/llm/tool/note.py +0 -84
  112. zrb/builtin/llm/tool/sub_agent.py +0 -150
  113. zrb/builtin/llm/tool/web.py +0 -171
  114. zrb/builtin/project/__init__.py +0 -0
  115. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/__init__.py +0 -0
  116. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/service/__init__.py +0 -0
  117. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/__init__.py +0 -0
  118. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/__init__.py +0 -0
  119. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/__init__.py +0 -0
  120. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/__init__.py +0 -0
  121. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/__init__.py +0 -0
  122. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/__init__.py +0 -0
  123. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
  124. zrb/builtin/project/create/__init__.py +0 -0
  125. zrb/builtin/shell/__init__.py +0 -0
  126. zrb/builtin/shell/autocomplete/__init__.py +0 -0
  127. zrb/callback/__init__.py +0 -0
  128. zrb/cmd/__init__.py +0 -0
  129. zrb/config/default_prompt/interactive_system_prompt.md +0 -29
  130. zrb/config/default_prompt/persona.md +0 -1
  131. zrb/config/default_prompt/summarization_prompt.md +0 -57
  132. zrb/config/default_prompt/system_prompt.md +0 -38
  133. zrb/config/llm_config.py +0 -339
  134. zrb/config/llm_context/config.py +0 -166
  135. zrb/config/llm_context/config_parser.py +0 -40
  136. zrb/config/llm_context/workflow.py +0 -81
  137. zrb/config/llm_rate_limitter.py +0 -190
  138. zrb/content_transformer/__init__.py +0 -0
  139. zrb/context/__init__.py +0 -0
  140. zrb/dot_dict/__init__.py +0 -0
  141. zrb/env/__init__.py +0 -0
  142. zrb/group/__init__.py +0 -0
  143. zrb/input/__init__.py +0 -0
  144. zrb/runner/__init__.py +0 -0
  145. zrb/runner/web_route/__init__.py +0 -0
  146. zrb/runner/web_route/home_page/__init__.py +0 -0
  147. zrb/session/__init__.py +0 -0
  148. zrb/session_state_log/__init__.py +0 -0
  149. zrb/session_state_logger/__init__.py +0 -0
  150. zrb/task/__init__.py +0 -0
  151. zrb/task/base/__init__.py +0 -0
  152. zrb/task/llm/__init__.py +0 -0
  153. zrb/task/llm/agent.py +0 -204
  154. zrb/task/llm/agent_runner.py +0 -152
  155. zrb/task/llm/config.py +0 -122
  156. zrb/task/llm/conversation_history.py +0 -209
  157. zrb/task/llm/conversation_history_model.py +0 -67
  158. zrb/task/llm/default_workflow/coding/workflow.md +0 -41
  159. zrb/task/llm/default_workflow/copywriting/workflow.md +0 -68
  160. zrb/task/llm/default_workflow/git/workflow.md +0 -118
  161. zrb/task/llm/default_workflow/golang/workflow.md +0 -128
  162. zrb/task/llm/default_workflow/html-css/workflow.md +0 -135
  163. zrb/task/llm/default_workflow/java/workflow.md +0 -146
  164. zrb/task/llm/default_workflow/javascript/workflow.md +0 -158
  165. zrb/task/llm/default_workflow/python/workflow.md +0 -160
  166. zrb/task/llm/default_workflow/researching/workflow.md +0 -153
  167. zrb/task/llm/default_workflow/rust/workflow.md +0 -162
  168. zrb/task/llm/default_workflow/shell/workflow.md +0 -299
  169. zrb/task/llm/error.py +0 -95
  170. zrb/task/llm/file_replacement.py +0 -206
  171. zrb/task/llm/file_tool_model.py +0 -57
  172. zrb/task/llm/history_processor.py +0 -206
  173. zrb/task/llm/history_summarization.py +0 -25
  174. zrb/task/llm/print_node.py +0 -221
  175. zrb/task/llm/prompt.py +0 -321
  176. zrb/task/llm/subagent_conversation_history.py +0 -41
  177. zrb/task/llm/tool_wrapper.py +0 -361
  178. zrb/task/llm/typing.py +0 -3
  179. zrb/task/llm/workflow.py +0 -76
  180. zrb/task/llm_task.py +0 -379
  181. zrb/task_status/__init__.py +0 -0
  182. zrb/util/__init__.py +0 -0
  183. zrb/util/cli/__init__.py +0 -0
  184. zrb/util/cmd/__init__.py +0 -0
  185. zrb/util/codemod/__init__.py +0 -0
  186. zrb/util/string/__init__.py +0 -0
  187. zrb/xcom/__init__.py +0 -0
  188. /zrb/{config/default_prompt/file_extractor_system_prompt.md → llm/prompt/markdown/file_extractor.md} +0 -0
  189. /zrb/{config/default_prompt/repo_extractor_system_prompt.md → llm/prompt/markdown/repo_extractor.md} +0 -0
  190. /zrb/{config/default_prompt/repo_summarizer_system_prompt.md → llm/prompt/markdown/repo_summarizer.md} +0 -0
  191. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/WHEEL +0 -0
  192. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/entry_points.txt +0 -0
zrb/task/base_task.py CHANGED
@@ -3,8 +3,9 @@ import inspect
3
3
  from collections.abc import Callable
4
4
  from typing import Any
5
5
 
6
- from zrb.attr.type import BoolAttr, fstring
6
+ from zrb.attr.type import fstring
7
7
  from zrb.context.any_context import AnyContext
8
+ from zrb.context.print_fn import PrintFn
8
9
  from zrb.env.any_env import AnyEnv
9
10
  from zrb.input.any_input import AnyInput
10
11
  from zrb.session.any_session import AnySession
@@ -55,7 +56,7 @@ class BaseTask(AnyTask):
55
56
  input: list[AnyInput | None] | AnyInput | None = None,
56
57
  env: list[AnyEnv | None] | AnyEnv | None = None,
57
58
  action: fstring | Callable[[AnyContext], Any] | None = None,
58
- execute_condition: BoolAttr = True,
59
+ execute_condition: bool | str | Callable[[AnyContext], bool] = True,
59
60
  retries: int = 2,
60
61
  retry_period: float = 0,
61
62
  readiness_check: list[AnyTask] | AnyTask | None = None,
@@ -67,10 +68,20 @@ class BaseTask(AnyTask):
67
68
  upstream: list[AnyTask] | AnyTask | None = None,
68
69
  fallback: list[AnyTask] | AnyTask | None = None,
69
70
  successor: list[AnyTask] | AnyTask | None = None,
71
+ print_fn: PrintFn | None = None,
70
72
  ):
71
- caller_frame = inspect.stack()[1]
72
- self.__decl_file = caller_frame.filename
73
- self.__decl_line = caller_frame.lineno
73
+ # Optimized stack retrieval
74
+ frame = inspect.currentframe()
75
+ if frame is not None:
76
+ caller_frame = frame.f_back
77
+ self.__decl_file = (
78
+ caller_frame.f_code.co_filename if caller_frame else "unknown"
79
+ )
80
+ self.__decl_line = caller_frame.f_lineno if caller_frame else 0
81
+ else:
82
+ self.__decl_file = "unknown"
83
+ self.__decl_line = 0
84
+
74
85
  self._name = name
75
86
  self._color = color
76
87
  self._icon = icon
@@ -80,10 +91,10 @@ class BaseTask(AnyTask):
80
91
  self._envs = env
81
92
  self._retries = retries
82
93
  self._retry_period = retry_period
83
- self._upstreams = upstream
84
- self._fallbacks = fallback
85
- self._successors = successor
86
- self._readiness_checks = readiness_check
94
+ self._upstreams = self._ensure_task_list(upstream)
95
+ self._fallbacks = self._ensure_task_list(fallback)
96
+ self._successors = self._ensure_task_list(successor)
97
+ self._readiness_checks = self._ensure_task_list(readiness_check)
87
98
  self._readiness_check_delay = readiness_check_delay
88
99
  self._readiness_check_period = readiness_check_period
89
100
  self._readiness_failure_threshold = readiness_failure_threshold
@@ -91,6 +102,14 @@ class BaseTask(AnyTask):
91
102
  self._monitor_readiness = monitor_readiness
92
103
  self._execute_condition = execute_condition
93
104
  self._action = action
105
+ self._print_fn = print_fn
106
+
107
+ def _ensure_task_list(self, tasks: AnyTask | list[AnyTask] | None) -> list[AnyTask]:
108
+ if tasks is None:
109
+ return []
110
+ if isinstance(tasks, list):
111
+ return tasks
112
+ return [tasks]
94
113
 
95
114
  def __repr__(self):
96
115
  return f"<{self.__class__.__name__} name={self.name}>"
@@ -132,18 +151,10 @@ class BaseTask(AnyTask):
132
151
  @property
133
152
  def fallbacks(self) -> list[AnyTask]:
134
153
  """Returns the list of fallback tasks."""
135
- if self._fallbacks is None:
136
- return []
137
- elif isinstance(self._fallbacks, list):
138
- return self._fallbacks
139
- return [self._fallbacks] # Assume single task
154
+ return self._fallbacks
140
155
 
141
156
  def append_fallback(self, fallbacks: AnyTask | list[AnyTask]):
142
157
  """Appends fallback tasks, ensuring no duplicates."""
143
- if self._fallbacks is None:
144
- self._fallbacks = []
145
- elif not isinstance(self._fallbacks, list):
146
- self._fallbacks = [self._fallbacks]
147
158
  to_add = fallbacks if isinstance(fallbacks, list) else [fallbacks]
148
159
  for fb in to_add:
149
160
  if fb not in self._fallbacks:
@@ -152,18 +163,10 @@ class BaseTask(AnyTask):
152
163
  @property
153
164
  def successors(self) -> list[AnyTask]:
154
165
  """Returns the list of successor tasks."""
155
- if self._successors is None:
156
- return []
157
- elif isinstance(self._successors, list):
158
- return self._successors
159
- return [self._successors] # Assume single task
166
+ return self._successors
160
167
 
161
168
  def append_successor(self, successors: AnyTask | list[AnyTask]):
162
169
  """Appends successor tasks, ensuring no duplicates."""
163
- if self._successors is None:
164
- self._successors = []
165
- elif not isinstance(self._successors, list):
166
- self._successors = [self._successors]
167
170
  to_add = successors if isinstance(successors, list) else [successors]
168
171
  for succ in to_add:
169
172
  if succ not in self._successors:
@@ -172,18 +175,10 @@ class BaseTask(AnyTask):
172
175
  @property
173
176
  def readiness_checks(self) -> list[AnyTask]:
174
177
  """Returns the list of readiness check tasks."""
175
- if self._readiness_checks is None:
176
- return []
177
- elif isinstance(self._readiness_checks, list):
178
- return self._readiness_checks
179
- return [self._readiness_checks] # Assume single task
178
+ return self._readiness_checks
180
179
 
181
180
  def append_readiness_check(self, readiness_checks: AnyTask | list[AnyTask]):
182
181
  """Appends readiness check tasks, ensuring no duplicates."""
183
- if self._readiness_checks is None:
184
- self._readiness_checks = []
185
- elif not isinstance(self._readiness_checks, list):
186
- self._readiness_checks = [self._readiness_checks]
187
182
  to_add = (
188
183
  readiness_checks
189
184
  if isinstance(readiness_checks, list)
@@ -196,18 +191,10 @@ class BaseTask(AnyTask):
196
191
  @property
197
192
  def upstreams(self) -> list[AnyTask]:
198
193
  """Returns the list of upstream tasks."""
199
- if self._upstreams is None:
200
- return []
201
- elif isinstance(self._upstreams, list):
202
- return self._upstreams
203
- return [self._upstreams] # Assume single task
194
+ return self._upstreams
204
195
 
205
196
  def append_upstream(self, upstreams: AnyTask | list[AnyTask]):
206
197
  """Appends upstream tasks, ensuring no duplicates."""
207
- if self._upstreams is None:
208
- self._upstreams = []
209
- elif not isinstance(self._upstreams, list):
210
- self._upstreams = [self._upstreams]
211
198
  to_add = upstreams if isinstance(upstreams, list) else [upstreams]
212
199
  for up in to_add:
213
200
  if up not in self._upstreams:
@@ -240,7 +227,13 @@ class BaseTask(AnyTask):
240
227
  """
241
228
  # Use asyncio.run() to execute the async cleanup wrapper
242
229
  return asyncio.run(
243
- run_and_cleanup(self, session=session, str_kwargs=str_kwargs, kwargs=kwargs)
230
+ run_and_cleanup(
231
+ self,
232
+ session=session,
233
+ print_fn=self._print_fn,
234
+ str_kwargs=str_kwargs,
235
+ kwargs=kwargs,
236
+ )
244
237
  )
245
238
 
246
239
  async def async_run(
@@ -250,7 +243,11 @@ class BaseTask(AnyTask):
250
243
  kwargs: dict[str, Any] | None = None,
251
244
  ) -> Any:
252
245
  return await run_task_async(
253
- self, session=session, str_kwargs=str_kwargs, kwargs=kwargs
246
+ self,
247
+ session=session,
248
+ print_fn=self._print_fn,
249
+ str_kwargs=str_kwargs,
250
+ kwargs=kwargs,
254
251
  )
255
252
 
256
253
  async def exec_root_tasks(self, session: AnySession):
@@ -277,6 +274,8 @@ class BaseTask(AnyTask):
277
274
  try:
278
275
  # Delegate to the helper function for the default behavior
279
276
  return await run_default_action(self, ctx)
277
+ except (KeyboardInterrupt, GeneratorExit):
278
+ raise
280
279
  except BaseException as e:
281
280
  additional_error_note = (
282
281
  f"Task: {self.name} ({self.__decl_file}:{self.__decl_line})"
@@ -298,7 +297,7 @@ class BaseTask(AnyTask):
298
297
 
299
298
  def task_runner_fn(**kwargs) -> Any:
300
299
  task_kwargs = self._get_func_kwargs(kwargs)
301
- shared_ctx = SharedContext()
300
+ shared_ctx = SharedContext(print_fn=self._print_fn)
302
301
  session = Session(shared_ctx=shared_ctx)
303
302
  return self.run(session=session, kwargs=task_kwargs)
304
303
 
@@ -318,7 +317,7 @@ class BaseTask(AnyTask):
318
317
  def _create_fn_docstring(self) -> str:
319
318
  from zrb.context.shared_context import SharedContext
320
319
 
321
- stub_shared_ctx = SharedContext()
320
+ stub_shared_ctx = SharedContext(print_fn=self._print_fn)
322
321
  str_input_default_values = {}
323
322
  for inp in self.inputs:
324
323
  str_input_default_values[inp.name] = inp.get_default_str(stub_shared_ctx)
zrb/task/base_trigger.py CHANGED
@@ -5,7 +5,7 @@ from typing import Any
5
5
  from zrb.attr.type import fstring
6
6
  from zrb.callback.any_callback import AnyCallback
7
7
  from zrb.context.any_context import AnyContext
8
- from zrb.context.any_shared_context import AnySharedContext
8
+ from zrb.context.print_fn import PrintFn
9
9
  from zrb.context.shared_context import SharedContext
10
10
  from zrb.dot_dict.dot_dict import DotDict
11
11
  from zrb.env.any_env import AnyEnv
@@ -50,6 +50,7 @@ class BaseTrigger(BaseTask):
50
50
  upstream: list[AnyTask] | AnyTask | None = None,
51
51
  fallback: list[AnyTask] | AnyTask | None = None,
52
52
  successor: list[AnyTask] | AnyTask | None = None,
53
+ print_fn: PrintFn | None = None,
53
54
  ):
54
55
  """
55
56
  Initializes a new instance of the BaseTrigger class.
@@ -103,6 +104,7 @@ class BaseTrigger(BaseTask):
103
104
  upstream=upstream,
104
105
  fallback=fallback,
105
106
  successor=successor,
107
+ print_fn=print_fn,
106
108
  )
107
109
  self._callbacks = callback
108
110
  self._queue_name = queue_name
@@ -144,6 +146,7 @@ class BaseTrigger(BaseTask):
144
146
  shared_ctx=SharedContext(
145
147
  input=dict(session.shared_ctx.input),
146
148
  xcom=xcom_dict,
149
+ print_fn=self._print_fn,
147
150
  ),
148
151
  parent=session,
149
152
  root_group=session.root_group,
zrb/task/cmd_task.py CHANGED
@@ -6,6 +6,7 @@ from zrb.cmd.cmd_result import CmdResult
6
6
  from zrb.cmd.cmd_val import AnyCmdVal, CmdVal, SingleCmdVal
7
7
  from zrb.config.config import CFG
8
8
  from zrb.context.any_context import AnyContext
9
+ from zrb.context.print_fn import PrintFn
9
10
  from zrb.env.any_env import AnyEnv
10
11
  from zrb.input.any_input import AnyInput
11
12
  from zrb.task.any_task import AnyTask
@@ -48,6 +49,7 @@ class CmdTask(BaseTask):
48
49
  warn_unrecommended_command: bool | None = None,
49
50
  max_output_line: int = 1000,
50
51
  max_error_line: int = 1000,
52
+ execution_timeout: int = 3600,
51
53
  is_interactive: bool = False,
52
54
  execute_condition: BoolAttr = True,
53
55
  retries: int = 2,
@@ -61,6 +63,7 @@ class CmdTask(BaseTask):
61
63
  upstream: list[AnyTask] | AnyTask | None = None,
62
64
  fallback: list[AnyTask] | AnyTask | None = None,
63
65
  successor: list[AnyTask] | AnyTask | None = None,
66
+ print_fn: PrintFn | None = None,
64
67
  ):
65
68
  super().__init__(
66
69
  name=name,
@@ -82,6 +85,7 @@ class CmdTask(BaseTask):
82
85
  upstream=upstream,
83
86
  fallback=fallback,
84
87
  successor=successor,
88
+ print_fn=print_fn,
85
89
  )
86
90
  self._shell = shell
87
91
  self._render_shell = render_shell
@@ -103,6 +107,7 @@ class CmdTask(BaseTask):
103
107
  self._render_cwd = render_cwd
104
108
  self._max_output_line = max_output_line
105
109
  self._max_error_line = max_error_line
110
+ self._execution_timeout = execution_timeout
106
111
  self._should_plain_print = plain_print
107
112
  self._should_warn_unrecommended_command = warn_unrecommended_command
108
113
  self._is_interactive = is_interactive
@@ -142,6 +147,7 @@ class CmdTask(BaseTask):
142
147
  register_pid_method=lambda pid: ctx.xcom.get(xcom_pid_key).push(pid),
143
148
  max_output_line=self._max_output_line,
144
149
  max_error_line=self._max_error_line,
150
+ timeout=self._execution_timeout,
145
151
  is_interactive=self._is_interactive,
146
152
  )
147
153
  # Check for errors
zrb/task/http_check.py CHANGED
@@ -1,15 +1,19 @@
1
1
  import asyncio
2
2
  from collections.abc import Callable
3
+ from typing import TYPE_CHECKING
3
4
 
4
5
  from zrb.attr.type import StrAttr
5
6
  from zrb.context.any_context import AnyContext
6
- from zrb.context.context import Context
7
+ from zrb.context.print_fn import PrintFn
7
8
  from zrb.env.any_env import AnyEnv
8
9
  from zrb.input.any_input import AnyInput
9
10
  from zrb.task.any_task import AnyTask
10
11
  from zrb.task.base_task import BaseTask
11
12
  from zrb.util.attr import get_str_attr
12
13
 
14
+ if TYPE_CHECKING:
15
+ from requests import Response
16
+
13
17
 
14
18
  class HttpCheck(BaseTask):
15
19
  def __init__(
@@ -19,16 +23,17 @@ class HttpCheck(BaseTask):
19
23
  icon: str | None = None,
20
24
  description: str | None = None,
21
25
  cli_only: bool = False,
22
- input: list[AnyInput] | AnyInput | None = None,
23
- env: list[AnyEnv] | AnyEnv | None = None,
26
+ input: list[AnyInput | None] | AnyInput | None = None,
27
+ env: list[AnyEnv | None] | AnyEnv | None = None,
24
28
  url: StrAttr = "http://localhost",
25
29
  render_url: bool = True,
26
30
  http_method: StrAttr = "GET",
27
31
  interval: int = 5,
28
- execute_condition: bool | str | Callable[[Context], bool] = True,
32
+ execute_condition: bool | str | Callable[[AnyContext], bool] = True,
29
33
  upstream: list[AnyTask] | AnyTask | None = None,
30
34
  fallback: list[AnyTask] | AnyTask | None = None,
31
35
  successor: list[AnyTask] | AnyTask | None = None,
36
+ print_fn: PrintFn | None = None,
32
37
  ):
33
38
  super().__init__(
34
39
  name=name,
@@ -43,6 +48,7 @@ class HttpCheck(BaseTask):
43
48
  upstream=upstream,
44
49
  fallback=fallback,
45
50
  successor=successor,
51
+ print_fn=print_fn,
46
52
  )
47
53
  self._url = url
48
54
  self._render_url = render_url
@@ -57,7 +63,7 @@ class HttpCheck(BaseTask):
57
63
  def _get_http_method(self, ctx: AnyContext) -> str:
58
64
  return get_str_attr(ctx, self._http_method, "GET", auto_render=True).upper()
59
65
 
60
- async def _exec_action(self, ctx: AnyContext) -> bool:
66
+ async def _exec_action(self, ctx: AnyContext) -> "bool | Response":
61
67
  import requests
62
68
 
63
69
  url = self._get_url(ctx)
zrb/task/make_task.py CHANGED
@@ -2,6 +2,7 @@ from collections.abc import Callable
2
2
  from typing import Any
3
3
 
4
4
  from zrb.context.any_context import AnyContext
5
+ from zrb.context.print_fn import PrintFn
5
6
  from zrb.env.any_env import AnyEnv
6
7
  from zrb.group.any_group import AnyGroup
7
8
  from zrb.input.any_input import AnyInput
@@ -29,6 +30,7 @@ def make_task(
29
30
  upstream: list[AnyTask] | AnyTask | None = None,
30
31
  fallback: list[AnyTask] | AnyTask | None = None,
31
32
  successor: list[AnyTask] | AnyTask | None = None,
33
+ print_fn: PrintFn | None = None,
32
34
  group: AnyGroup | None = None,
33
35
  alias: str | None = None,
34
36
  ) -> Callable[[Callable[[AnyContext], Any]], AnyTask]:
@@ -54,6 +56,7 @@ def make_task(
54
56
  upstream=upstream,
55
57
  fallback=fallback,
56
58
  successor=successor,
59
+ print_fn=print_fn,
57
60
  )
58
61
  if group is not None:
59
62
  return group.add_task(task, alias=alias)
zrb/task/rsync_task.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from zrb.attr.type import BoolAttr, IntAttr, StrAttr
2
2
  from zrb.context.any_context import AnyContext
3
+ from zrb.context.print_fn import PrintFn
3
4
  from zrb.env.any_env import AnyEnv
4
5
  from zrb.input.any_input import AnyInput
5
6
  from zrb.task.any_task import AnyTask
@@ -44,6 +45,7 @@ class RsyncTask(CmdTask):
44
45
  plain_print: bool = False,
45
46
  max_output_line: int = 1000,
46
47
  max_error_line: int = 1000,
48
+ execution_timeout: int = 3600,
47
49
  execute_condition: BoolAttr = True,
48
50
  retries: int = 2,
49
51
  retry_period: float = 0,
@@ -51,6 +53,7 @@ class RsyncTask(CmdTask):
51
53
  upstream: list[AnyTask] | AnyTask | None = None,
52
54
  fallback: list[AnyTask] | AnyTask | None = None,
53
55
  successor: list[AnyTask] | AnyTask | None = None,
56
+ print_fn: PrintFn | None = None,
54
57
  ):
55
58
  super().__init__(
56
59
  name=name,
@@ -77,6 +80,7 @@ class RsyncTask(CmdTask):
77
80
  plain_print=plain_print,
78
81
  max_output_line=max_output_line,
79
82
  max_error_line=max_error_line,
83
+ execution_timeout=execution_timeout,
80
84
  execute_condition=execute_condition,
81
85
  retries=retries,
82
86
  retry_period=retry_period,
@@ -84,6 +88,7 @@ class RsyncTask(CmdTask):
84
88
  upstream=upstream,
85
89
  fallback=fallback,
86
90
  successor=successor,
91
+ print_fn=print_fn,
87
92
  )
88
93
  self._remote_source_path = remote_source_path
89
94
  self._render_remote_source_path = render_remote_source_path
zrb/task/scaffolder.py CHANGED
@@ -2,10 +2,11 @@ import os
2
2
  import shutil
3
3
  from collections.abc import Callable
4
4
 
5
- from zrb.attr.type import BoolAttr, StrAttr
5
+ from zrb.attr.type import StrAttr
6
6
  from zrb.content_transformer.any_content_transformer import AnyContentTransformer
7
7
  from zrb.content_transformer.content_transformer import ContentTransformer
8
8
  from zrb.context.any_context import AnyContext
9
+ from zrb.context.print_fn import PrintFn
9
10
  from zrb.env.any_env import AnyEnv
10
11
  from zrb.input.any_input import AnyInput
11
12
  from zrb.task.any_task import AnyTask
@@ -24,8 +25,8 @@ class Scaffolder(BaseTask):
24
25
  icon: str | None = None,
25
26
  description: str | None = None,
26
27
  cli_only: bool = False,
27
- input: list[AnyInput] | AnyInput | None = None,
28
- env: list[AnyEnv] | AnyEnv | None = None,
28
+ input: list[AnyInput | None] | AnyInput | None = None,
29
+ env: list[AnyEnv | None] | AnyEnv | None = None,
29
30
  source_path: StrAttr | None = None,
30
31
  render_source_path: bool = True,
31
32
  destination_path: StrAttr | None = None,
@@ -36,7 +37,7 @@ class Scaffolder(BaseTask):
36
37
  list[AnyContentTransformer] | AnyContentTransformer | TransformConfig
37
38
  ) = [],
38
39
  render_transform_content: bool = True,
39
- execute_condition: BoolAttr = True,
40
+ execute_condition: bool | str | Callable[[AnyContext], bool] = True,
40
41
  retries: int = 2,
41
42
  retry_period: float = 0,
42
43
  readiness_check: list[AnyTask] | AnyTask | None = None,
@@ -48,6 +49,7 @@ class Scaffolder(BaseTask):
48
49
  upstream: list[AnyTask] | AnyTask | None = None,
49
50
  fallback: list[AnyTask] | AnyTask | None = None,
50
51
  successor: list[AnyTask] | AnyTask | None = None,
52
+ print_fn: PrintFn | None = None,
51
53
  ):
52
54
  super().__init__(
53
55
  name=name,
@@ -69,6 +71,7 @@ class Scaffolder(BaseTask):
69
71
  upstream=upstream,
70
72
  fallback=fallback,
71
73
  successor=successor,
74
+ print_fn=print_fn,
72
75
  )
73
76
  self._source_path = source_path
74
77
  self._render_source_path = render_source_path
zrb/task/scheduler.py CHANGED
@@ -6,6 +6,7 @@ from zrb.attr.type import StrAttr, fstring
6
6
  from zrb.callback.any_callback import AnyCallback
7
7
  from zrb.context.any_context import AnyContext
8
8
  from zrb.context.any_shared_context import AnySharedContext
9
+ from zrb.context.print_fn import PrintFn
9
10
  from zrb.env.any_env import AnyEnv
10
11
  from zrb.input.any_input import AnyInput
11
12
  from zrb.task.any_task import AnyTask
@@ -39,6 +40,7 @@ class Scheduler(BaseTrigger):
39
40
  upstream: list[AnyTask] | AnyTask | None = None,
40
41
  fallback: list[AnyTask] | AnyTask | None = None,
41
42
  successor: list[AnyTask] | AnyTask | None = None,
43
+ print_fn: PrintFn | None = None,
42
44
  ):
43
45
  super().__init__(
44
46
  name=name,
@@ -62,6 +64,7 @@ class Scheduler(BaseTrigger):
62
64
  upstream=upstream,
63
65
  fallback=fallback,
64
66
  successor=successor,
67
+ print_fn=print_fn,
65
68
  )
66
69
  self._cron_pattern = schedule
67
70
 
zrb/task/tcp_check.py CHANGED
@@ -3,7 +3,7 @@ from collections.abc import Callable
3
3
 
4
4
  from zrb.attr.type import IntAttr, StrAttr
5
5
  from zrb.context.any_context import AnyContext
6
- from zrb.context.context import Context
6
+ from zrb.context.print_fn import PrintFn
7
7
  from zrb.env.any_env import AnyEnv
8
8
  from zrb.input.any_input import AnyInput
9
9
  from zrb.task.any_task import AnyTask
@@ -19,16 +19,17 @@ class TcpCheck(BaseTask):
19
19
  icon: str | None = None,
20
20
  description: str | None = None,
21
21
  cli_only: bool = False,
22
- input: list[AnyInput] | AnyInput | None = None,
23
- env: list[AnyEnv] | AnyEnv | None = None,
22
+ input: list[AnyInput | None] | AnyInput | None = None,
23
+ env: list[AnyEnv | None] | AnyEnv | None = None,
24
24
  host: StrAttr = "localhost",
25
25
  render_host: bool = True,
26
26
  port: IntAttr = 80,
27
27
  interval: int = 5,
28
- execute_condition: bool | str | Callable[[Context], bool] = True,
28
+ execute_condition: bool | str | Callable[[AnyContext], bool] = True,
29
29
  upstream: list[AnyTask] | AnyTask | None = None,
30
30
  fallback: list[AnyTask] | AnyTask | None = None,
31
31
  successor: list[AnyTask] | AnyTask | None = None,
32
+ print_fn: PrintFn | None = None,
32
33
  ):
33
34
  super().__init__(
34
35
  name=name,
@@ -43,6 +44,7 @@ class TcpCheck(BaseTask):
43
44
  upstream=upstream,
44
45
  fallback=fallback,
45
46
  successor=successor,
47
+ print_fn=print_fn,
46
48
  )
47
49
  self._host = host
48
50
  self._render_host = render_host
@@ -0,0 +1,17 @@
1
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⣉⡙⣿⣿⣿⣿⣿⣿⣿⣿
2
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⣡⣴⣿⣿⠇⣾⣿⣿⣿⣿⣿⣿⣿
3
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢃⣴⣿⣿⣿⣿⡟⢰⣿⣿⣿⣿⣿⣿⣿⣿
4
+ ⣿⣿⠟⢿⣿⣿⣿⣿⣿⡇⣸⣿⣿⣿⣿⡟⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿
5
+ ⣇⣀⡲⣦⢻⣿⣿⣿⣿⡇⣿⣿⣿⣿⡟⣰⣿⡿⠿⠿⢿⣿⣿⣿⣿⣿
6
+ ⣿⣿⣧⠙⠸⣿⣿⣿⣿⡇⣿⣿⣿⢏⠼⣋⣥⣶⣿⣿⣦⣤⣉⡛⠻⢿
7
+ ⣿⢟⡁⠀⠀⠀⠙⣿⣿⣇⢿⣿⠋⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⠈
8
+ ⡇⠀⡿⠀⠀⠀⠀⠈⠉⣹⣾⠕⠾⣿⣿⣿⣿⡿⠿⠿⠟⠛⢛⣉⣤⣾
9
+ ⣇⠀⠀⢀⠀⠀⠠⢀⣴⣿⠏⠀⢀⣶⡶⣶⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿
10
+ ⣿⣶⣤⣤⣤⣦⣶⡿⠟⠁⠀⢠⣾⣿⠃⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
11
+ ⣿⣿⣿⣿⣿⣿⡄⠀⣀⣠⣴⣿⠟⠁⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
12
+ ⣿⣿⣿⣿⣿⣿⣿⣿⠛⠛⠋⠁⠀⠀⣠⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿
13
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣤⣤⣶⣿⡿⠋⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿
14
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⡁⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿
15
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
16
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
17
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
@@ -0,0 +1,9 @@
1
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢿⣿⣿⣿⣿
2
+ ⣿⣿⣿⣿⡿⢋⠹⣿⣿⣿⣿⣿⠟⢿⣿⣿⣿⣿⣿⣿⠃⠀⢨⣿⣿⣿⣿
3
+ ⣿⣿⣿⠟⣠⣿⣧⣈⣉⣉⣉⣁⣶⡌⢻⣿⣿⣿⣿⣿⠘⠀⣿⣿⣿⣿⣿
4
+ ⣿⣿⠏⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠛⠛⠛⠛⠛⠀⡀⠻⣿⣿⣿⣿
5
+ ⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠘⣿⣿⣿⣿⣿⣿⣦⠈⢋⣉⠙
6
+ ⣿⣿⡎⢿⣿⣶⣶⣿⣿⣿⣿⣤⣼⣿⡟⢠⣿⣿⣿⡿⠿⠟⠛⣀⣡⣤⣴
7
+ ⠟⣁⣴⣦⠉⠛⠛⠛⠛⠛⠿⠟⠛⠁⠀⣋⣉⣥⣴⣶⣾⣿⣿⣿⣿⣿⣿
8
+ ⣤⣭⣤⣶⣶⣿⣿⣿⠋⣠⡼⢋⣤⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
9
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣤⣤⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
@@ -0,0 +1,16 @@
1
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⢋⣉⣉⣉⣉⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿
2
+ ⣿⣿⣿⣿⣿⣿⣿⡿⠋⣠⣾⣿⣿⣿⣿⣿⣷⣦⡈⢻⣿⣿⣿⣿⣿⣿⣿
3
+ ⣿⣿⣿⣿⣿⣿⡿⢁⣾⡟⠁⣨⣿⣿⡅⠈⢻⣿⣿⡄⢻⣿⣿⣿⣿⣿⣿
4
+ ⣿⠏⣤⣤⣉⠛⠃⢸⣿⣷⣾⡟⠉⠉⢻⣶⣾⣿⣿⡇⢸⣿⣿⣿⣿⣿⣿
5
+ ⣿⣧⠄⣹⣿⣿⣷⣬⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⡇⢸⡿⠿⠿⠿⢿⣿
6
+ ⣿⠇⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣴⣿⣿⣿⣿⣿⣥⣤⣶⣶⣶⡶⠆⣹
7
+ ⡏⢰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⢰⣾⣿
8
+ ⣧⠘⢿⡿⠟⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀⣿⣿
9
+ ⣿⣷⣦⣴⠂⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⣿⣿
10
+ ⣿⣿⣿⣿⡀⠻⠿⠋⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⢠⣈⠛⣁⣼⣿⣿
11
+ ⣿⣿⣿⣿⣿⣿⣾⣿⣆⠘⣿⣿⣿⣿⣿⣿⡟⠙⠛⠃⣼⣿⣿⣿⣿⣿⣿
12
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⡘⢿⣿⣿⣿⣿⡀⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿
13
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⡙⢿⣿⣿⣧⡈⠻⣿⣿⣿⣿⣿⣿⣿⣿
14
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣉⡙⠻⠿⣦⠙⢿⣿⣿⣿⣿⣿⣿
15
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣌⠡⠘⣿⣿⣿⣿⣿⣿
16
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣠⣿⣿⣿⣿⣿⣿
@@ -0,0 +1,17 @@
1
+ ⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
2
+ ⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⠀⠙⠿⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
3
+ ⣿⣿⣿⣿⣿⣿⡀⠀⣠⣴⣶⣿⣿⣿⣿⣶⣮⣝⠻⢿⣿⣿⣿⣿⣿
4
+ ⣿⣿⣿⣿⣿⡟⣡⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠙⢿
5
+ ⣿⠿⣿⣿⡿⢰⣿⡿⠋⠉⠉⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⣸
6
+ ⠁⠀⠀⠙⠃⢿⣿⡅⠀⠀⢀⣼⣿⣿⣿⣿⠟⠛⠻⣿⣿⣷⢀⣴⣿
7
+ ⡀⠀⠀⠀⠀⠸⣿⣛⣳⣾⣿⢿⡍⢉⣻⡇⠰⠀⠀⣿⣿⣿⢸⣿⣿
8
+ ⣷⡀⠀⠀⠀⠀⠈⠻⢿⣿⣿⣷⣶⣬⣽⣿⣦⣤⣤⣟⣿⢇⣾⣿⣿
9
+ ⣿⣿⣄⠀⠀⠀⠀⠀⠀⠈⠙⠻⠿⣿⣿⣿⣿⣮⣿⠟⣡⣾⣿⣿⣿
10
+ ⣿⣿⣿⡇⢰⣶⣤⣤⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿
11
+ ⣿⣿⣿⣇⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣤⡀⠀⠀⠀⠀⢻⣿⣿⣿⣿
12
+ ⣿⣿⣿⡈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡄⠀⠀⠀⠀⢿⣿⣿⣿
13
+ ⣿⣿⣿⡇⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⢸⣿⣿⣿
14
+ ⣿⣿⣿⡇⠀⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⢠⣄⣀⣠⣿⣿⣿⣿
15
+ ⣿⣿⣿⣿⠀⠀⠀⠀⠉⣉⣛⣛⡛⠉⠁⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿
16
+ ⣿⣿⣿⣿⣆⠀⠀⠀⣰⣿⣿⣿⣷⡀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⣿
17
+ ⣿⣿⣿⣿⣿⣷⣶⣶⣿⣿⣿⣿⣿⣿⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿
@@ -0,0 +1,14 @@
1
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⣿⣿⣿⣿⠁⠀⠉⠉⠛⠛⠛⠉⠛⢻⣿⣿⣿⣿⣿⣿
2
+ ⣿⣿⣿⣿⣿⠟⠋⠁⠀⠀⠀⠈⠙⢧⣤⣤⡀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿⣿
3
+ ⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⢀⣠⡴⠾⠛⠛⠿⣷⣄⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⣿
4
+ ⣿⣿⣿⠋⠀⠀⠀⠀⣠⣶⣿⠁⠀⠀⣀⡀⠀⠈⠻⣦⡀⠀⠀⠀⠀⠀⣿⣿⣿⣿
5
+ ⣿⣿⡏⠀⠀⠀⠀⣰⡿⠛⠙⠿⠛⠉⠛⠟⢿⣷⣄⣸⡇⠐⢦⣤⣤⠔⠛⠻⣿⣿
6
+ ⣿⣿⡃⠀⠀⠀⠀⡟⠀⣠⠀⠀⠀⠀⢀⠀⠀⠹⠛⠛⣧⠀⠀⠙⠁⠀⠀⠀⠘⣿
7
+ ⣿⣿⣷⡀⠀⠀⢸⠀⢰⣿⡀⠀⠀⠀⢹⣿⠀⢀⡆⠀⠘⣆⣴⣿⠀⠀⠀⠀⠀⠘
8
+ ⣿⡟⠀⠙⣄⠀⢸⠀⠀⢻⣷⣄⠀⠤⠞⠋⣠⡾⠁⠀⢠⠟⠉⠻⡇⠀⠀⠀⠀⢰
9
+ ⡿⠁⠀⠀⣿⣷⣾⣧⠀⠀⠈⠛⢳⠤⠒⠚⠉⠀⢀⡴⠃⠀⠀⠀⠃⠀⠀⠀⣠⣿
10
+ ⡁⠀⠀⠀⣿⠀⠀⠹⣷⠄⢀⣠⣴⣿⣶⣤⣶⡾⠋⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿
11
+ ⣷⡄⠀⠀⢻⠀⠀⠀⠘⢧⡀⠈⠉⠛⠛⠋⠉⠀⠀⠀⣀⣠⣴⣂⣀⠠⠊⢹⣿⣿
12
+ ⣿⣿⣦⡀⢈⡄⠀⠀⠀⠀⠉⠙⠒⠒⠦⠤⠤⣶⣶⣿⣿⠿⠛⠋⠁⠀⢀⣾⣿⣿
13
+ ⣿⣿⣿⣿⣿⣷⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠋⠀⠀⠀⠀⣠⣴⣿⣿⣿⣿
14
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄⡀⠀⠀⢀⣀⣠⣴⣾⣿⣶⣤⣠⣴⣿⣿⣿⣿⣿⣿⣿
@@ -0,0 +1,15 @@
1
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
2
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠛⠉⢠⡟⠋⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿
3
+ ⣿⣿⣿⣿⣿⣿⠿⠛⠋⠉⠑⠁⠀⠀⠈⠀⣼⣿⣿⣿⣿⠿⠟⢛⣫⣽⣶
4
+ ⣿⣿⣿⡿⠋⠁⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⢁⣠⣴⣾⣿⣿⣿⣿
5
+ ⣿⣿⣯⡴⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿
6
+ ⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠀⠈⠁⢻⣿⣿⣿⣿⣿⣿⣿⣿
7
+ ⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⣧⡀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿
8
+ ⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣷⣶⣦⡀⠀⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿
9
+ ⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢿⣷⠀⡀⠀⢸⣿⣿⣿⣿⣿⣿⣿
10
+ ⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣧⣷⣤⣾⣿⣿⣿⣿⣿⣿⣿
11
+ ⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
12
+ ⣿⣿⣿⣿⣦⣄⡀⠀⠀⠈⢦⡀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
13
+ ⣿⣿⣿⣿⣿⣿⣿⣧⣤⣀⠀⣿⡀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
14
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣾⣷⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
15
+ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿