zrb 1.15.3__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.
- zrb/__init__.py +118 -133
- zrb/attr/type.py +10 -7
- zrb/builtin/__init__.py +55 -1
- zrb/builtin/git.py +12 -1
- zrb/builtin/group.py +31 -15
- zrb/builtin/llm/chat.py +147 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +7 -7
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +5 -5
- zrb/builtin/project/add/fastapp/fastapp_util.py +1 -1
- zrb/builtin/searxng/config/settings.yml +5671 -0
- zrb/builtin/searxng/start.py +21 -0
- zrb/builtin/shell/autocomplete/bash.py +4 -3
- zrb/builtin/shell/autocomplete/zsh.py +4 -3
- zrb/callback/callback.py +8 -1
- zrb/cmd/cmd_result.py +2 -1
- zrb/config/config.py +555 -169
- zrb/config/helper.py +84 -0
- zrb/config/web_auth_config.py +50 -35
- zrb/context/any_shared_context.py +20 -3
- zrb/context/context.py +39 -5
- zrb/context/print_fn.py +13 -0
- zrb/context/shared_context.py +17 -8
- zrb/group/any_group.py +3 -3
- zrb/group/group.py +3 -3
- zrb/input/any_input.py +5 -1
- zrb/input/base_input.py +18 -6
- zrb/input/option_input.py +41 -1
- zrb/input/text_input.py +7 -24
- zrb/llm/agent/__init__.py +9 -0
- zrb/llm/agent/agent.py +215 -0
- zrb/llm/agent/summarizer.py +20 -0
- zrb/llm/app/__init__.py +10 -0
- zrb/llm/app/completion.py +281 -0
- zrb/llm/app/confirmation/allow_tool.py +66 -0
- zrb/llm/app/confirmation/handler.py +178 -0
- zrb/llm/app/confirmation/replace_confirmation.py +77 -0
- zrb/llm/app/keybinding.py +34 -0
- zrb/llm/app/layout.py +117 -0
- zrb/llm/app/lexer.py +155 -0
- zrb/llm/app/redirection.py +28 -0
- zrb/llm/app/style.py +16 -0
- zrb/llm/app/ui.py +733 -0
- zrb/llm/config/__init__.py +4 -0
- zrb/llm/config/config.py +122 -0
- zrb/llm/config/limiter.py +247 -0
- zrb/llm/history_manager/__init__.py +4 -0
- zrb/llm/history_manager/any_history_manager.py +23 -0
- zrb/llm/history_manager/file_history_manager.py +91 -0
- zrb/llm/history_processor/summarizer.py +108 -0
- zrb/llm/note/__init__.py +3 -0
- zrb/llm/note/manager.py +122 -0
- zrb/llm/prompt/__init__.py +29 -0
- zrb/llm/prompt/claude_compatibility.py +92 -0
- zrb/llm/prompt/compose.py +55 -0
- zrb/llm/prompt/default.py +51 -0
- zrb/llm/prompt/markdown/file_extractor.md +112 -0
- zrb/llm/prompt/markdown/mandate.md +23 -0
- zrb/llm/prompt/markdown/persona.md +3 -0
- zrb/llm/prompt/markdown/repo_extractor.md +112 -0
- zrb/llm/prompt/markdown/repo_summarizer.md +29 -0
- zrb/llm/prompt/markdown/summarizer.md +21 -0
- zrb/llm/prompt/note.py +41 -0
- zrb/llm/prompt/system_context.py +46 -0
- zrb/llm/prompt/zrb.py +41 -0
- zrb/llm/skill/__init__.py +3 -0
- zrb/llm/skill/manager.py +86 -0
- zrb/llm/task/__init__.py +4 -0
- zrb/llm/task/llm_chat_task.py +316 -0
- zrb/llm/task/llm_task.py +245 -0
- zrb/llm/tool/__init__.py +39 -0
- zrb/llm/tool/bash.py +75 -0
- zrb/llm/tool/code.py +266 -0
- zrb/llm/tool/file.py +419 -0
- zrb/llm/tool/note.py +70 -0
- zrb/{builtin/llm → llm}/tool/rag.py +33 -37
- zrb/llm/tool/search/brave.py +53 -0
- zrb/llm/tool/search/searxng.py +47 -0
- zrb/llm/tool/search/serpapi.py +47 -0
- zrb/llm/tool/skill.py +19 -0
- zrb/llm/tool/sub_agent.py +70 -0
- zrb/llm/tool/web.py +97 -0
- zrb/llm/tool/zrb_task.py +66 -0
- zrb/llm/util/attachment.py +101 -0
- zrb/llm/util/prompt.py +104 -0
- zrb/llm/util/stream_response.py +178 -0
- zrb/runner/cli.py +21 -20
- zrb/runner/common_util.py +24 -19
- zrb/runner/web_route/task_input_api_route.py +5 -5
- zrb/runner/web_util/user.py +7 -3
- zrb/session/any_session.py +12 -9
- zrb/session/session.py +38 -17
- zrb/task/any_task.py +24 -3
- zrb/task/base/context.py +42 -22
- zrb/task/base/execution.py +67 -55
- zrb/task/base/lifecycle.py +14 -7
- zrb/task/base/monitoring.py +12 -7
- zrb/task/base_task.py +113 -50
- zrb/task/base_trigger.py +16 -6
- zrb/task/cmd_task.py +6 -0
- zrb/task/http_check.py +11 -5
- zrb/task/make_task.py +5 -3
- zrb/task/rsync_task.py +30 -10
- zrb/task/scaffolder.py +7 -4
- zrb/task/scheduler.py +7 -4
- zrb/task/tcp_check.py +6 -4
- zrb/util/ascii_art/art/bee.txt +17 -0
- zrb/util/ascii_art/art/cat.txt +9 -0
- zrb/util/ascii_art/art/ghost.txt +16 -0
- zrb/util/ascii_art/art/panda.txt +17 -0
- zrb/util/ascii_art/art/rose.txt +14 -0
- zrb/util/ascii_art/art/unicorn.txt +15 -0
- zrb/util/ascii_art/banner.py +92 -0
- zrb/util/attr.py +54 -39
- zrb/util/cli/markdown.py +32 -0
- zrb/util/cli/text.py +30 -0
- zrb/util/cmd/command.py +33 -10
- zrb/util/file.py +61 -33
- zrb/util/git.py +2 -2
- zrb/util/{llm/prompt.py → markdown.py} +2 -3
- zrb/util/match.py +78 -0
- zrb/util/run.py +3 -3
- zrb/util/string/conversion.py +1 -1
- zrb/util/truncate.py +23 -0
- zrb/util/yaml.py +204 -0
- zrb/xcom/xcom.py +10 -0
- {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/METADATA +41 -27
- {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/RECORD +129 -131
- {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/WHEEL +1 -1
- zrb/attr/__init__.py +0 -0
- zrb/builtin/llm/chat_session.py +0 -311
- zrb/builtin/llm/history.py +0 -71
- zrb/builtin/llm/input.py +0 -27
- zrb/builtin/llm/llm_ask.py +0 -187
- zrb/builtin/llm/previous-session.js +0 -21
- zrb/builtin/llm/tool/__init__.py +0 -0
- zrb/builtin/llm/tool/api.py +0 -71
- zrb/builtin/llm/tool/cli.py +0 -38
- zrb/builtin/llm/tool/code.py +0 -254
- zrb/builtin/llm/tool/file.py +0 -626
- zrb/builtin/llm/tool/sub_agent.py +0 -137
- zrb/builtin/llm/tool/web.py +0 -195
- zrb/builtin/project/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/service/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
- zrb/builtin/project/create/__init__.py +0 -0
- zrb/builtin/shell/__init__.py +0 -0
- zrb/builtin/shell/autocomplete/__init__.py +0 -0
- zrb/callback/__init__.py +0 -0
- zrb/cmd/__init__.py +0 -0
- zrb/config/default_prompt/file_extractor_system_prompt.md +0 -12
- zrb/config/default_prompt/interactive_system_prompt.md +0 -35
- zrb/config/default_prompt/persona.md +0 -1
- zrb/config/default_prompt/repo_extractor_system_prompt.md +0 -112
- zrb/config/default_prompt/repo_summarizer_system_prompt.md +0 -10
- zrb/config/default_prompt/summarization_prompt.md +0 -16
- zrb/config/default_prompt/system_prompt.md +0 -32
- zrb/config/llm_config.py +0 -243
- zrb/config/llm_context/config.py +0 -129
- zrb/config/llm_context/config_parser.py +0 -46
- zrb/config/llm_rate_limitter.py +0 -137
- zrb/content_transformer/__init__.py +0 -0
- zrb/context/__init__.py +0 -0
- zrb/dot_dict/__init__.py +0 -0
- zrb/env/__init__.py +0 -0
- zrb/group/__init__.py +0 -0
- zrb/input/__init__.py +0 -0
- zrb/runner/__init__.py +0 -0
- zrb/runner/web_route/__init__.py +0 -0
- zrb/runner/web_route/home_page/__init__.py +0 -0
- zrb/session/__init__.py +0 -0
- zrb/session_state_log/__init__.py +0 -0
- zrb/session_state_logger/__init__.py +0 -0
- zrb/task/__init__.py +0 -0
- zrb/task/base/__init__.py +0 -0
- zrb/task/llm/__init__.py +0 -0
- zrb/task/llm/agent.py +0 -243
- zrb/task/llm/config.py +0 -103
- zrb/task/llm/conversation_history.py +0 -128
- zrb/task/llm/conversation_history_model.py +0 -242
- zrb/task/llm/default_workflow/coding.md +0 -24
- zrb/task/llm/default_workflow/copywriting.md +0 -17
- zrb/task/llm/default_workflow/researching.md +0 -18
- zrb/task/llm/error.py +0 -95
- zrb/task/llm/history_summarization.py +0 -216
- zrb/task/llm/print_node.py +0 -101
- zrb/task/llm/prompt.py +0 -325
- zrb/task/llm/tool_wrapper.py +0 -220
- zrb/task/llm/typing.py +0 -3
- zrb/task/llm_task.py +0 -341
- zrb/task_status/__init__.py +0 -0
- zrb/util/__init__.py +0 -0
- zrb/util/cli/__init__.py +0 -0
- zrb/util/cmd/__init__.py +0 -0
- zrb/util/codemod/__init__.py +0 -0
- zrb/util/string/__init__.py +0 -0
- zrb/xcom/__init__.py +0 -0
- {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/entry_points.txt +0 -0
zrb/config/helper.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import platform
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_env(env_name: str | list[str], default: str = "", prefix: str = "ZRB") -> str:
|
|
7
|
+
env_name_list = env_name if isinstance(env_name, list) else [env_name]
|
|
8
|
+
for name in env_name_list:
|
|
9
|
+
value = os.getenv(f"{prefix}_{name}", None)
|
|
10
|
+
if value is not None:
|
|
11
|
+
return value
|
|
12
|
+
return default
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_current_shell() -> str:
|
|
16
|
+
if platform.system() == "Windows":
|
|
17
|
+
return "PowerShell"
|
|
18
|
+
current_shell = os.getenv("SHELL", "")
|
|
19
|
+
if current_shell.endswith("zsh"):
|
|
20
|
+
return "zsh"
|
|
21
|
+
return "bash"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_default_diff_edit_command(editor: str) -> str:
|
|
25
|
+
if editor in [
|
|
26
|
+
"code",
|
|
27
|
+
"vscode",
|
|
28
|
+
"vscodium",
|
|
29
|
+
"windsurf",
|
|
30
|
+
"cursor",
|
|
31
|
+
"zed",
|
|
32
|
+
"zeditor",
|
|
33
|
+
"agy",
|
|
34
|
+
]:
|
|
35
|
+
return f"{editor} --wait --diff {{old}} {{new}}"
|
|
36
|
+
if editor == "emacs":
|
|
37
|
+
return 'emacs --eval \'(ediff-files "{old}" "{new}")\''
|
|
38
|
+
if editor in ["nvim", "vim"]:
|
|
39
|
+
return (
|
|
40
|
+
f"{editor} -d {{old}} {{new}} "
|
|
41
|
+
"-i NONE "
|
|
42
|
+
'-c "wincmd h | set readonly | wincmd l" '
|
|
43
|
+
'-c "highlight DiffAdd cterm=bold ctermbg=22 guibg=#005f00 | highlight DiffChange cterm=bold ctermbg=24 guibg=#005f87 | highlight DiffText ctermbg=21 guibg=#0000af | highlight DiffDelete ctermbg=52 guibg=#5f0000" ' # noqa
|
|
44
|
+
'-c "set showtabline=2 | set tabline=[Instructions]\\ :wqa(save\\ &\\ quit)\\ \\|\\ i/esc(toggle\\ edit\\ mode)" ' # noqa
|
|
45
|
+
'-c "wincmd h | setlocal statusline=OLD\\ FILE" '
|
|
46
|
+
'-c "wincmd l | setlocal statusline=%#StatusBold#NEW\\ FILE\\ :wqa(save\\ &\\ quit)\\ \\|\\ i/esc(toggle\\ edit\\ mode)" ' # noqa
|
|
47
|
+
'-c "autocmd BufWritePost * wqa"'
|
|
48
|
+
)
|
|
49
|
+
return 'vimdiff {old} {new} +"setlocal ro" +"wincmd l" +"autocmd BufWritePost <buffer> qa"' # noqa
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_log_level(level: str) -> int:
|
|
53
|
+
level = level.upper()
|
|
54
|
+
log_levels = {
|
|
55
|
+
"CRITICAL": logging.CRITICAL, # 50
|
|
56
|
+
"FATAL": logging.CRITICAL, # 50
|
|
57
|
+
"ERROR": logging.ERROR, # 40
|
|
58
|
+
"WARN": logging.WARNING, # 30
|
|
59
|
+
"WARNING": logging.WARNING, # 30
|
|
60
|
+
"INFO": logging.INFO, # 20
|
|
61
|
+
"DEBUG": logging.DEBUG, # 10
|
|
62
|
+
"NOTSET": logging.NOTSET, # 0
|
|
63
|
+
}
|
|
64
|
+
if level in log_levels:
|
|
65
|
+
return log_levels[level]
|
|
66
|
+
return logging.WARNING
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_max_token_threshold(
|
|
70
|
+
factor: float, max_tokens_per_minute: int, max_tokens_per_request: int
|
|
71
|
+
) -> int:
|
|
72
|
+
return round(factor * min(max_tokens_per_minute, max_tokens_per_request))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def limit_token_threshold(
|
|
76
|
+
threshold: int,
|
|
77
|
+
factor: float,
|
|
78
|
+
max_tokens_per_minute: int,
|
|
79
|
+
max_tokens_per_request: int,
|
|
80
|
+
) -> int:
|
|
81
|
+
return min(
|
|
82
|
+
threshold,
|
|
83
|
+
get_max_token_threshold(factor, max_tokens_per_minute, max_tokens_per_request),
|
|
84
|
+
)
|
zrb/config/web_auth_config.py
CHANGED
|
@@ -41,58 +41,108 @@ class WebAuthConfig:
|
|
|
41
41
|
return self._secret_key
|
|
42
42
|
return CFG.WEB_SECRET_KEY
|
|
43
43
|
|
|
44
|
+
@secret_key.setter
|
|
45
|
+
def secret_key(self, secret_key: str):
|
|
46
|
+
self._secret_key = secret_key
|
|
47
|
+
|
|
44
48
|
@property
|
|
45
49
|
def access_token_expire_minutes(self) -> int:
|
|
46
50
|
if self._access_token_expire_minutes is not None:
|
|
47
51
|
return self._access_token_expire_minutes
|
|
48
52
|
return CFG.WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES
|
|
49
53
|
|
|
54
|
+
@access_token_expire_minutes.setter
|
|
55
|
+
def access_token_expire_minutes(self, minutes: int):
|
|
56
|
+
self._access_token_expire_minutes = minutes
|
|
57
|
+
|
|
50
58
|
@property
|
|
51
59
|
def refresh_token_expire_minutes(self) -> int:
|
|
52
60
|
if self._refresh_token_expire_minutes is not None:
|
|
53
61
|
return self._refresh_token_expire_minutes
|
|
54
62
|
return CFG.WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES
|
|
55
63
|
|
|
64
|
+
@refresh_token_expire_minutes.setter
|
|
65
|
+
def refresh_token_expire_minutes(self, minutes: int):
|
|
66
|
+
self._refresh_token_expire_minutes = minutes
|
|
67
|
+
|
|
56
68
|
@property
|
|
57
69
|
def access_token_cookie_name(self) -> str:
|
|
58
70
|
if self._access_token_cookie_name is not None:
|
|
59
71
|
return self._access_token_cookie_name
|
|
60
72
|
return CFG.WEB_ACCESS_TOKEN_COOKIE_NAME
|
|
61
73
|
|
|
74
|
+
@access_token_cookie_name.setter
|
|
75
|
+
def access_token_cookie_name(self, name: str):
|
|
76
|
+
self._access_token_cookie_name = name
|
|
77
|
+
|
|
62
78
|
@property
|
|
63
79
|
def refresh_token_cookie_name(self) -> str:
|
|
64
80
|
if self._refresh_token_cookie_name is not None:
|
|
65
81
|
return self._refresh_token_cookie_name
|
|
66
82
|
return CFG.WEB_REFRESH_TOKEN_COOKIE_NAME
|
|
67
83
|
|
|
84
|
+
@refresh_token_cookie_name.setter
|
|
85
|
+
def refresh_token_cookie_name(self, name: str):
|
|
86
|
+
self._refresh_token_cookie_name = name
|
|
87
|
+
|
|
68
88
|
@property
|
|
69
89
|
def enable_auth(self) -> bool:
|
|
70
90
|
if self._enable_auth is not None:
|
|
71
91
|
return self._enable_auth
|
|
72
92
|
return CFG.WEB_ENABLE_AUTH
|
|
73
93
|
|
|
94
|
+
@enable_auth.setter
|
|
95
|
+
def enable_auth(self, enable: bool):
|
|
96
|
+
self._enable_auth = enable
|
|
97
|
+
|
|
74
98
|
@property
|
|
75
99
|
def super_admin_username(self) -> str:
|
|
76
100
|
if self._super_admin_username is not None:
|
|
77
101
|
return self._super_admin_username
|
|
78
102
|
return CFG.WEB_SUPER_ADMIN_USERNAME
|
|
79
103
|
|
|
104
|
+
@super_admin_username.setter
|
|
105
|
+
def super_admin_username(self, username: str):
|
|
106
|
+
self._super_admin_username = username
|
|
107
|
+
|
|
80
108
|
@property
|
|
81
109
|
def super_admin_password(self) -> str:
|
|
82
110
|
if self._super_admin_password is not None:
|
|
83
111
|
return self._super_admin_password
|
|
84
112
|
return CFG.WEB_SUPER_ADMIN_PASSWORD
|
|
85
113
|
|
|
114
|
+
@super_admin_password.setter
|
|
115
|
+
def super_admin_password(self, password: str):
|
|
116
|
+
self._super_admin_password = password
|
|
117
|
+
|
|
86
118
|
@property
|
|
87
119
|
def guest_username(self) -> str:
|
|
88
120
|
if self._guest_username is not None:
|
|
89
121
|
return self._guest_username
|
|
90
122
|
return CFG.WEB_GUEST_USERNAME
|
|
91
123
|
|
|
124
|
+
@guest_username.setter
|
|
125
|
+
def guest_username(self, username: str):
|
|
126
|
+
self._guest_username = username
|
|
127
|
+
|
|
92
128
|
@property
|
|
93
129
|
def guest_accessible_tasks(self) -> list[AnyTask | str]:
|
|
94
130
|
return self._guest_accessible_tasks
|
|
95
131
|
|
|
132
|
+
@guest_accessible_tasks.setter
|
|
133
|
+
def guest_accessible_tasks(self, tasks: list[AnyTask | str]):
|
|
134
|
+
self._guest_accessible_tasks = tasks
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def find_user_by_username_callback(self) -> Callable[[str], "User | None"] | None:
|
|
138
|
+
return self._find_user_by_username
|
|
139
|
+
|
|
140
|
+
@find_user_by_username_callback.setter
|
|
141
|
+
def find_user_by_username_callback(
|
|
142
|
+
self, find_user_by_username: Callable[[str], "User | None"]
|
|
143
|
+
):
|
|
144
|
+
self._find_user_by_username = find_user_by_username
|
|
145
|
+
|
|
96
146
|
@property
|
|
97
147
|
def default_user(self) -> "User":
|
|
98
148
|
from zrb.runner.web_schema.user import User
|
|
@@ -127,41 +177,6 @@ class WebAuthConfig:
|
|
|
127
177
|
return [self.default_user]
|
|
128
178
|
return self._user_list + [self.super_admin, self.default_user]
|
|
129
179
|
|
|
130
|
-
def set_secret_key(self, secret_key: str):
|
|
131
|
-
self._secret_key = secret_key
|
|
132
|
-
|
|
133
|
-
def set_access_token_expire_minutes(self, minutes: int):
|
|
134
|
-
self._access_token_expire_minutes = minutes
|
|
135
|
-
|
|
136
|
-
def set_refresh_token_expire_minutes(self, minutes: int):
|
|
137
|
-
self._refresh_token_expire_minutes = minutes
|
|
138
|
-
|
|
139
|
-
def set_access_token_cookie_name(self, name: str):
|
|
140
|
-
self._access_token_cookie_name = name
|
|
141
|
-
|
|
142
|
-
def set_refresh_token_cookie_name(self, name: str):
|
|
143
|
-
self._refresh_token_cookie_name = name
|
|
144
|
-
|
|
145
|
-
def set_enable_auth(self, enable: bool):
|
|
146
|
-
self._enable_auth = enable
|
|
147
|
-
|
|
148
|
-
def set_super_admin_username(self, username: str):
|
|
149
|
-
self._super_admin_username = username
|
|
150
|
-
|
|
151
|
-
def set_super_admin_password(self, password: str):
|
|
152
|
-
self._super_admin_password = password
|
|
153
|
-
|
|
154
|
-
def set_guest_username(self, username: str):
|
|
155
|
-
self._guest_username = username
|
|
156
|
-
|
|
157
|
-
def set_guest_accessible_tasks(self, tasks: list[AnyTask | str]):
|
|
158
|
-
self._guest_accessible_tasks = tasks
|
|
159
|
-
|
|
160
|
-
def set_find_user_by_username(
|
|
161
|
-
self, find_user_by_username: Callable[[str], "User | None"]
|
|
162
|
-
):
|
|
163
|
-
self._find_user_by_username = find_user_by_username
|
|
164
|
-
|
|
165
180
|
def append_user(self, user: "User"):
|
|
166
181
|
duplicates = [
|
|
167
182
|
existing_user
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations # Enables forward references
|
|
2
2
|
|
|
3
|
+
import sys
|
|
3
4
|
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import TYPE_CHECKING, Any
|
|
5
|
+
from typing import TYPE_CHECKING, Any, TextIO
|
|
5
6
|
|
|
6
7
|
from zrb.dot_dict.dot_dict import DotDict
|
|
7
|
-
from zrb.xcom.xcom import Xcom
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from zrb.session import any_session
|
|
@@ -29,26 +29,32 @@ class AnySharedContext(ABC):
|
|
|
29
29
|
pass
|
|
30
30
|
|
|
31
31
|
@property
|
|
32
|
+
@abstractmethod
|
|
32
33
|
def input(self) -> DotDict:
|
|
33
34
|
pass
|
|
34
35
|
|
|
35
36
|
@property
|
|
37
|
+
@abstractmethod
|
|
36
38
|
def env(self) -> DotDict:
|
|
37
39
|
pass
|
|
38
40
|
|
|
39
41
|
@property
|
|
42
|
+
@abstractmethod
|
|
40
43
|
def args(self) -> list[Any]:
|
|
41
44
|
pass
|
|
42
45
|
|
|
43
46
|
@property
|
|
44
|
-
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def xcom(self) -> DotDict:
|
|
45
49
|
pass
|
|
46
50
|
|
|
47
51
|
@property
|
|
52
|
+
@abstractmethod
|
|
48
53
|
def shared_log(self) -> list[str]:
|
|
49
54
|
pass
|
|
50
55
|
|
|
51
56
|
@property
|
|
57
|
+
@abstractmethod
|
|
52
58
|
def session(self) -> any_session.AnySession | None:
|
|
53
59
|
pass
|
|
54
60
|
|
|
@@ -81,3 +87,14 @@ class AnySharedContext(ABC):
|
|
|
81
87
|
str: The rendered template as a string.
|
|
82
88
|
"""
|
|
83
89
|
pass
|
|
90
|
+
|
|
91
|
+
@abstractmethod
|
|
92
|
+
def shared_print(
|
|
93
|
+
self,
|
|
94
|
+
*values: object,
|
|
95
|
+
sep: str = " ",
|
|
96
|
+
end: str = "\n",
|
|
97
|
+
file: TextIO | None = sys.stderr,
|
|
98
|
+
flush: bool = True,
|
|
99
|
+
):
|
|
100
|
+
pass
|
zrb/context/context.py
CHANGED
|
@@ -63,7 +63,7 @@ class Context(AnyContext):
|
|
|
63
63
|
|
|
64
64
|
@property
|
|
65
65
|
def session(self) -> AnySession | None:
|
|
66
|
-
return self._shared_ctx.
|
|
66
|
+
return self._shared_ctx.session
|
|
67
67
|
|
|
68
68
|
def update_task_env(self, task_env: dict[str, str]):
|
|
69
69
|
self._env.update(task_env)
|
|
@@ -101,6 +101,18 @@ class Context(AnyContext):
|
|
|
101
101
|
return template
|
|
102
102
|
return float(self.render(template))
|
|
103
103
|
|
|
104
|
+
def shared_print(
|
|
105
|
+
self,
|
|
106
|
+
*values: object,
|
|
107
|
+
sep: str = " ",
|
|
108
|
+
end: str = "\n",
|
|
109
|
+
file: TextIO | None = sys.stderr,
|
|
110
|
+
flush: bool = True,
|
|
111
|
+
):
|
|
112
|
+
return self._shared_ctx.shared_print(
|
|
113
|
+
*values, sep=sep, end=end, file=file, flush=flush
|
|
114
|
+
)
|
|
115
|
+
|
|
104
116
|
def print(
|
|
105
117
|
self,
|
|
106
118
|
*values: object,
|
|
@@ -110,16 +122,25 @@ class Context(AnyContext):
|
|
|
110
122
|
flush: bool = True,
|
|
111
123
|
plain: bool = False,
|
|
112
124
|
):
|
|
113
|
-
|
|
125
|
+
if sep is None:
|
|
126
|
+
sep = " "
|
|
127
|
+
if end is None:
|
|
128
|
+
end = "\n"
|
|
114
129
|
message = sep.join([f"{value}" for value in values])
|
|
115
130
|
if plain:
|
|
116
131
|
# self.append_to_shared_log(remove_style(message))
|
|
117
|
-
|
|
132
|
+
self.shared_print(message, sep=sep, end=end, file=file, flush=flush)
|
|
118
133
|
self.append_to_shared_log(remove_style(f"{message}{end}"))
|
|
119
134
|
return
|
|
120
135
|
color = self._color
|
|
121
136
|
icon = self._icon
|
|
122
|
-
|
|
137
|
+
# Handle case where session is None (e.g., in tests)
|
|
138
|
+
if self.session is None:
|
|
139
|
+
max_name_length = len(self._task_name) + len(icon)
|
|
140
|
+
else:
|
|
141
|
+
max_name_length = max(
|
|
142
|
+
len(name) + len(icon) for name in self.session.task_names
|
|
143
|
+
)
|
|
123
144
|
styled_task_name = f"{icon} {self._task_name}"
|
|
124
145
|
padded_styled_task_name = styled_task_name.rjust(max_name_length + 1)
|
|
125
146
|
if self._attempt == 0:
|
|
@@ -131,7 +152,20 @@ class Context(AnyContext):
|
|
|
131
152
|
prefix = f"{formatted_time}{attempt_status} {padded_styled_task_name} ⬤ "
|
|
132
153
|
self.append_to_shared_log(remove_style(f"{prefix} {message}{end}"))
|
|
133
154
|
stylized_prefix = stylize(prefix, color=color)
|
|
134
|
-
|
|
155
|
+
self.shared_print(
|
|
156
|
+
f"{stylized_prefix} {message}", sep=sep, end=end, file=file, flush=flush
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def print_err(
|
|
160
|
+
self,
|
|
161
|
+
*values: object,
|
|
162
|
+
sep: str | None = " ",
|
|
163
|
+
end: str | None = "\n",
|
|
164
|
+
file: TextIO | None = sys.stderr,
|
|
165
|
+
flush: bool = True,
|
|
166
|
+
plain: bool = False,
|
|
167
|
+
):
|
|
168
|
+
self.print(*values, sep=sep, end=end, file=file, flush=flush, plain=plain)
|
|
135
169
|
|
|
136
170
|
def log_debug(
|
|
137
171
|
self,
|
zrb/context/print_fn.py
ADDED
zrb/context/shared_context.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import sys
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any, TextIO
|
|
4
4
|
|
|
5
5
|
from zrb.config.config import CFG
|
|
6
6
|
from zrb.context.any_shared_context import AnySharedContext
|
|
7
|
+
from zrb.context.print_fn import PrintFn
|
|
7
8
|
from zrb.dot_dict.dot_dict import DotDict
|
|
8
9
|
from zrb.session.any_session import AnySession
|
|
9
10
|
from zrb.util.string.conversion import (
|
|
@@ -28,6 +29,7 @@ class SharedContext(AnySharedContext):
|
|
|
28
29
|
xcom: dict[str, Xcom] = {},
|
|
29
30
|
logging_level: int | None = None,
|
|
30
31
|
is_web_mode: bool = False,
|
|
32
|
+
print_fn: PrintFn | None = None,
|
|
31
33
|
):
|
|
32
34
|
self.__logging_level = logging_level
|
|
33
35
|
self._input = DotDict(input)
|
|
@@ -37,14 +39,11 @@ class SharedContext(AnySharedContext):
|
|
|
37
39
|
self._session: AnySession | None = None
|
|
38
40
|
self._log = []
|
|
39
41
|
self._is_web_mode = is_web_mode
|
|
42
|
+
self._print_fn = print_fn if print_fn is not None else print
|
|
40
43
|
|
|
41
44
|
def __repr__(self):
|
|
42
45
|
class_name = self.__class__.__name__
|
|
43
|
-
|
|
44
|
-
args = self._args
|
|
45
|
-
env = self._env
|
|
46
|
-
xcom = self._xcom
|
|
47
|
-
return f"<{class_name} input={input} args={args} xcom={xcom} env={env}>"
|
|
46
|
+
return f"<{class_name}>"
|
|
48
47
|
|
|
49
48
|
@property
|
|
50
49
|
def is_web_mode(self) -> bool:
|
|
@@ -70,7 +69,7 @@ class SharedContext(AnySharedContext):
|
|
|
70
69
|
return self._args
|
|
71
70
|
|
|
72
71
|
@property
|
|
73
|
-
def xcom(self) -> DotDict
|
|
72
|
+
def xcom(self) -> DotDict:
|
|
74
73
|
return self._xcom
|
|
75
74
|
|
|
76
75
|
@property
|
|
@@ -85,7 +84,7 @@ class SharedContext(AnySharedContext):
|
|
|
85
84
|
self._log.append(message)
|
|
86
85
|
session = self.session
|
|
87
86
|
if session is not None:
|
|
88
|
-
session_parent: AnySession = session.parent
|
|
87
|
+
session_parent: AnySession | None = session.parent
|
|
89
88
|
if session_parent is not None:
|
|
90
89
|
session_parent.shared_ctx.append_to_shared_log(message)
|
|
91
90
|
|
|
@@ -112,3 +111,13 @@ class SharedContext(AnySharedContext):
|
|
|
112
111
|
"double_quote": double_quote,
|
|
113
112
|
},
|
|
114
113
|
)
|
|
114
|
+
|
|
115
|
+
def shared_print(
|
|
116
|
+
self,
|
|
117
|
+
*values: object,
|
|
118
|
+
sep: str = " ",
|
|
119
|
+
end: str = "\n",
|
|
120
|
+
file: TextIO | None = sys.stderr,
|
|
121
|
+
flush: bool = True,
|
|
122
|
+
):
|
|
123
|
+
return self._print_fn(*values, sep=sep, end=end, file=file, flush=flush)
|
zrb/group/any_group.py
CHANGED
|
@@ -35,11 +35,11 @@ class AnyGroup(ABC):
|
|
|
35
35
|
pass
|
|
36
36
|
|
|
37
37
|
@abstractmethod
|
|
38
|
-
def add_group(self, group: "AnyGroup |
|
|
38
|
+
def add_group(self, group: "AnyGroup", alias: str | None = None) -> "AnyGroup":
|
|
39
39
|
pass
|
|
40
40
|
|
|
41
41
|
@abstractmethod
|
|
42
|
-
def add_task(self, task: AnyTask, alias: str | None = None) -> AnyTask:
|
|
42
|
+
def add_task(self, task: "AnyTask", alias: str | None = None) -> "AnyTask":
|
|
43
43
|
pass
|
|
44
44
|
|
|
45
45
|
@abstractmethod
|
|
@@ -55,5 +55,5 @@ class AnyGroup(ABC):
|
|
|
55
55
|
pass
|
|
56
56
|
|
|
57
57
|
@abstractmethod
|
|
58
|
-
def get_group_by_alias(self,
|
|
58
|
+
def get_group_by_alias(self, alias: str) -> "AnyGroup | None":
|
|
59
59
|
pass
|
zrb/group/group.py
CHANGED
|
@@ -33,15 +33,15 @@ class Group(AnyGroup):
|
|
|
33
33
|
def subgroups(self) -> dict[str, AnyGroup]:
|
|
34
34
|
names = list(self._groups.keys())
|
|
35
35
|
names.sort()
|
|
36
|
-
return {name: self._groups
|
|
36
|
+
return {name: self._groups[name] for name in names}
|
|
37
37
|
|
|
38
38
|
@property
|
|
39
39
|
def subtasks(self) -> dict[str, AnyTask]:
|
|
40
40
|
alias = list(self._tasks.keys())
|
|
41
41
|
alias.sort()
|
|
42
|
-
return {name: self._tasks
|
|
42
|
+
return {name: self._tasks[name] for name in alias}
|
|
43
43
|
|
|
44
|
-
def add_group(self, group: AnyGroup
|
|
44
|
+
def add_group(self, group: AnyGroup, alias: str | None = None) -> AnyGroup:
|
|
45
45
|
real_group = Group(group) if isinstance(group, str) else group
|
|
46
46
|
alias = alias if alias is not None else real_group.name
|
|
47
47
|
self._groups[alias] = real_group
|
zrb/input/any_input.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
2
3
|
|
|
3
4
|
from zrb.context.any_shared_context import AnySharedContext
|
|
4
5
|
|
|
@@ -35,7 +36,10 @@ class AnyInput(ABC):
|
|
|
35
36
|
|
|
36
37
|
@abstractmethod
|
|
37
38
|
def update_shared_context(
|
|
38
|
-
self,
|
|
39
|
+
self,
|
|
40
|
+
shared_ctx: AnySharedContext,
|
|
41
|
+
str_value: str | None = None,
|
|
42
|
+
value: Any = None,
|
|
39
43
|
):
|
|
40
44
|
pass
|
|
41
45
|
|
zrb/input/base_input.py
CHANGED
|
@@ -58,11 +58,15 @@ class BaseInput(AnyInput):
|
|
|
58
58
|
return f'<input name="{name}" placeholder="{description}" value="{default}" />'
|
|
59
59
|
|
|
60
60
|
def update_shared_context(
|
|
61
|
-
self,
|
|
61
|
+
self,
|
|
62
|
+
shared_ctx: AnySharedContext,
|
|
63
|
+
str_value: str | None = None,
|
|
64
|
+
value: Any = None,
|
|
62
65
|
):
|
|
63
|
-
if
|
|
64
|
-
str_value
|
|
65
|
-
|
|
66
|
+
if value is None:
|
|
67
|
+
if str_value is None:
|
|
68
|
+
str_value = self.get_default_str(shared_ctx)
|
|
69
|
+
value = self._parse_str_value(str_value)
|
|
66
70
|
if self.name in shared_ctx.input:
|
|
67
71
|
raise ValueError(f"Input already defined in the context: {self.name}")
|
|
68
72
|
shared_ctx.input[self.name] = value
|
|
@@ -91,12 +95,20 @@ class BaseInput(AnyInput):
|
|
|
91
95
|
default_str = self.get_default_str(shared_ctx)
|
|
92
96
|
if default_str != "":
|
|
93
97
|
prompt_message = f"{prompt_message} [{default_str}]"
|
|
94
|
-
|
|
95
|
-
value = input()
|
|
98
|
+
value = self._read_line(shared_ctx, prompt_message)
|
|
96
99
|
if value.strip() == "":
|
|
97
100
|
value = default_str
|
|
98
101
|
return value
|
|
99
102
|
|
|
103
|
+
def _read_line(self, shared_ctx: AnySharedContext, prompt_message: str) -> str:
|
|
104
|
+
if not shared_ctx.is_tty:
|
|
105
|
+
print(f"{prompt_message}: ", end="")
|
|
106
|
+
return input()
|
|
107
|
+
from prompt_toolkit import PromptSession
|
|
108
|
+
|
|
109
|
+
reader = PromptSession()
|
|
110
|
+
return reader.prompt(f"{prompt_message}: ")
|
|
111
|
+
|
|
100
112
|
def get_default_str(self, shared_ctx: AnySharedContext) -> str:
|
|
101
113
|
"""Get default value as str"""
|
|
102
114
|
default_value = get_attr(
|
zrb/input/option_input.py
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
1
3
|
from zrb.attr.type import StrAttr, StrListAttr
|
|
2
4
|
from zrb.context.any_shared_context import AnySharedContext
|
|
3
5
|
from zrb.input.base_input import BaseInput
|
|
4
6
|
from zrb.util.attr import get_str_list_attr
|
|
7
|
+
from zrb.util.match import fuzzy_match
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from prompt_toolkit.completion import Completer
|
|
5
11
|
|
|
6
12
|
|
|
7
13
|
class OptionInput(BaseInput):
|
|
@@ -47,9 +53,43 @@ class OptionInput(BaseInput):
|
|
|
47
53
|
option_str = ", ".join(options)
|
|
48
54
|
if default_value != "":
|
|
49
55
|
prompt_message = f"{prompt_message} ({option_str}) [{default_value}]"
|
|
50
|
-
value =
|
|
56
|
+
value = self._get_value_from_user_input(shared_ctx, prompt_message, options)
|
|
51
57
|
if value.strip() != "" and value.strip() not in options:
|
|
52
58
|
value = self._prompt_cli_str(shared_ctx)
|
|
53
59
|
if value.strip() == "":
|
|
54
60
|
value = default_value
|
|
55
61
|
return value
|
|
62
|
+
|
|
63
|
+
def _get_value_from_user_input(
|
|
64
|
+
self, shared_ctx: AnySharedContext, prompt_message: str, options: list[str]
|
|
65
|
+
) -> str:
|
|
66
|
+
from prompt_toolkit import PromptSession
|
|
67
|
+
|
|
68
|
+
if shared_ctx.is_tty:
|
|
69
|
+
reader = PromptSession()
|
|
70
|
+
option_completer = self._get_option_completer(options)
|
|
71
|
+
return reader.prompt(f"{prompt_message}: ", completer=option_completer)
|
|
72
|
+
return input(f"{prompt_message}: ")
|
|
73
|
+
|
|
74
|
+
def _get_option_completer(self, options: list[str]) -> "Completer":
|
|
75
|
+
from prompt_toolkit.completion import CompleteEvent, Completer, Completion
|
|
76
|
+
from prompt_toolkit.document import Document
|
|
77
|
+
|
|
78
|
+
class OptionCompleter(Completer):
|
|
79
|
+
def __init__(self, options: list[str]):
|
|
80
|
+
self._options = options
|
|
81
|
+
|
|
82
|
+
def get_completions(
|
|
83
|
+
self, document: Document, complete_event: CompleteEvent
|
|
84
|
+
):
|
|
85
|
+
search_pattern = document.get_word_before_cursor(WORD=True)
|
|
86
|
+
candidates = []
|
|
87
|
+
for option in self._options:
|
|
88
|
+
matched, score = fuzzy_match(option, search_pattern)
|
|
89
|
+
if matched:
|
|
90
|
+
candidates.append((score, option))
|
|
91
|
+
candidates.sort(key=lambda x: (x[0], x[1]))
|
|
92
|
+
for _, option in candidates:
|
|
93
|
+
yield Completion(option, start_position=-len(search_pattern))
|
|
94
|
+
|
|
95
|
+
return OptionCompleter(options)
|
zrb/input/text_input.py
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import subprocess
|
|
3
|
-
import tempfile
|
|
4
1
|
from collections.abc import Callable
|
|
5
2
|
|
|
6
3
|
from zrb.config.config import CFG
|
|
7
4
|
from zrb.context.any_shared_context import AnySharedContext
|
|
8
5
|
from zrb.input.base_input import BaseInput
|
|
9
|
-
from zrb.util.
|
|
6
|
+
from zrb.util.cli.text import edit_text
|
|
10
7
|
|
|
11
8
|
|
|
12
9
|
class TextInput(BaseInput):
|
|
@@ -85,24 +82,10 @@ class TextInput(BaseInput):
|
|
|
85
82
|
comment_prompt_message = (
|
|
86
83
|
f"{self.comment_start}{prompt_message}{self.comment_end}"
|
|
87
84
|
)
|
|
88
|
-
comment_prompt_message_eol = f"{comment_prompt_message}\n"
|
|
89
85
|
default_value = self.get_default_str(shared_ctx)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if default_value:
|
|
97
|
-
temp_file.write(default_value.encode())
|
|
98
|
-
temp_file.flush()
|
|
99
|
-
subprocess.call([self.editor_cmd, temp_file_name])
|
|
100
|
-
# Read the edited content
|
|
101
|
-
edited_content = read_file(temp_file_name)
|
|
102
|
-
parts = [
|
|
103
|
-
text.strip() for text in edited_content.split(comment_prompt_message, 1)
|
|
104
|
-
]
|
|
105
|
-
edited_content = "\n".join(parts).lstrip()
|
|
106
|
-
os.remove(temp_file_name)
|
|
107
|
-
print(f"{prompt_message}: {edited_content}")
|
|
108
|
-
return edited_content
|
|
86
|
+
return edit_text(
|
|
87
|
+
prompt_message=comment_prompt_message,
|
|
88
|
+
value=default_value,
|
|
89
|
+
editor=self.editor_cmd,
|
|
90
|
+
extension=self._extension,
|
|
91
|
+
)
|