zrb 1.0.0a20__py3-none-any.whl → 1.0.0b3__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 (195) hide show
  1. zrb/__init__.py +2 -1
  2. zrb/__main__.py +3 -3
  3. zrb/builtin/__init__.py +3 -0
  4. zrb/builtin/group.py +1 -0
  5. zrb/builtin/llm/llm_chat.py +5 -3
  6. zrb/builtin/llm/tool/cli.py +1 -1
  7. zrb/builtin/llm/tool/rag.py +108 -145
  8. zrb/builtin/llm/tool/web.py +1 -1
  9. zrb/builtin/project/add/fastapp/fastapp_task.py +2 -0
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -2
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +80 -20
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +150 -42
  13. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service.py +113 -0
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service_factory.py +9 -0
  15. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/my_entity_db_repository.py +0 -10
  16. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/my_entity_repository.py +37 -16
  17. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/{factory.py → my_entity_repository_factory.py} +2 -2
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py +16 -6
  19. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/client_method.py +57 -0
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +74 -0
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/format_task.py +1 -1
  22. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/input.py +13 -0
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +23 -0
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +42 -0
  25. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +7 -0
  26. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_api_client.py +6 -0
  27. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/{any_client.py → my_module_client.py} +1 -1
  28. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_client_factory.py +11 -0
  29. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_direct_client.py +5 -0
  30. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/route.py +11 -11
  31. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +2 -2
  32. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +8 -8
  33. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/util.py +47 -20
  34. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app_factory.py +29 -0
  35. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_db_repository.py +230 -102
  36. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +236 -0
  37. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/{db_engine.py → db_engine_factory.py} +1 -1
  38. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/error.py +12 -0
  39. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/logger_factory.py +10 -0
  40. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/parser_factory.py +7 -0
  41. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/app.py +47 -0
  42. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +105 -0
  43. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/user_agent.py +58 -0
  44. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +37 -0
  45. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +37 -1
  46. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/main.py +1 -1
  47. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_api_client.py +16 -0
  48. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client.py +169 -0
  49. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client_factory.py +9 -0
  50. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_direct_client.py +15 -0
  51. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +160 -0
  52. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration_metadata.py +18 -1
  53. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/route.py +7 -3
  54. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service.py +117 -0
  55. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service_factory.py +11 -0
  56. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_db_repository.py +26 -0
  57. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository.py +61 -0
  58. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository_factory.py +13 -0
  59. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +89 -0
  60. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository.py +67 -0
  61. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository_factory.py +13 -0
  62. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +137 -0
  63. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service_factory.py +7 -0
  64. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +179 -12
  65. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +67 -17
  66. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository_factory.py +2 -2
  67. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +127 -0
  68. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service_factory.py +7 -0
  69. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +43 -14
  70. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +200 -30
  71. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +74 -0
  72. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +6 -0
  73. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/homepage.html +6 -0
  74. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-192x192.png +0 -0
  75. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-512x512.png +0 -0
  76. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/favicon-32x32.png +0 -0
  77. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.amber.min.css +4 -0
  78. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.blue.min.css +4 -0
  79. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.cyan.min.css +4 -0
  80. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.fuchsia.min.css +4 -0
  81. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.green.min.css +4 -0
  82. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.grey.min.css +4 -0
  83. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.indigo.min.css +4 -0
  84. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.jade.min.css +4 -0
  85. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.lime.min.css +4 -0
  86. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.min.css +4 -0
  87. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.orange.min.css +4 -0
  88. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pink.min.css +4 -0
  89. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pumpkin.min.css +4 -0
  90. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.purple.min.css +4 -0
  91. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.red.min.css +4 -0
  92. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.sand.min.css +4 -0
  93. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.slate.min.css +4 -0
  94. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.violet.min.css +4 -0
  95. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.yellow.min.css +4 -0
  96. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.zinc.min.css +4 -0
  97. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +34 -0
  98. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +1 -0
  99. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +17 -5
  100. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +78 -4
  101. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/session.py +48 -0
  102. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +69 -5
  103. zrb/builtin/python.py +1 -1
  104. zrb/builtin/random.py +61 -0
  105. zrb/cmd/cmd_val.py +6 -5
  106. zrb/config.py +16 -3
  107. zrb/content_transformer/any_content_transformer.py +7 -0
  108. zrb/content_transformer/content_transformer.py +6 -0
  109. zrb/runner/cli.py +14 -7
  110. zrb/runner/web_app.py +28 -238
  111. zrb/runner/web_config/config.py +91 -0
  112. zrb/runner/web_config/config_factory.py +26 -0
  113. zrb/runner/web_route/docs_route.py +17 -0
  114. zrb/runner/web_route/error_page/serve_default_404.py +28 -0
  115. zrb/runner/{web_controller/error_page/controller.py → web_route/error_page/show_error_page.py} +4 -3
  116. zrb/runner/{web_controller → web_route}/error_page/view.html +6 -0
  117. zrb/runner/web_route/home_page/home_page_route.py +51 -0
  118. zrb/runner/{web_controller → web_route}/home_page/view.html +1 -0
  119. zrb/runner/web_route/login_api_route.py +31 -0
  120. zrb/runner/web_route/login_page/login_page_route.py +39 -0
  121. zrb/runner/{web_controller → web_route}/login_page/view.html +1 -0
  122. zrb/runner/web_route/logout_api_route.py +18 -0
  123. zrb/runner/web_route/logout_page/logout_page_route.py +40 -0
  124. zrb/runner/{web_controller → web_route}/logout_page/view.html +1 -0
  125. zrb/runner/{web_controller/group_info_page/controller.py → web_route/node_page/group/show_group_page.py} +3 -3
  126. zrb/runner/{web_controller/group_info_page → web_route/node_page/group}/view.html +1 -0
  127. zrb/runner/web_route/node_page/node_page_route.py +50 -0
  128. zrb/runner/{web_controller/session_page/controller.py → web_route/node_page/task/show_task_page.py} +5 -5
  129. zrb/runner/{web_controller/session_page → web_route/node_page/task}/view.html +1 -0
  130. zrb/runner/web_route/refresh_token_api_route.py +38 -0
  131. zrb/runner/web_route/static/refresh-token.template.js +22 -0
  132. zrb/runner/{web_controller/static → web_route/static/resources}/session/current-session.js +6 -3
  133. zrb/runner/{web_controller/static → web_route/static/resources}/session/event.js +10 -8
  134. zrb/runner/{web_controller/static → web_route/static/resources}/session/past-session.js +9 -3
  135. zrb/runner/web_route/static/static_route.py +44 -0
  136. zrb/runner/web_route/task_input_api_route.py +47 -0
  137. zrb/runner/web_route/task_session_api_route.py +147 -0
  138. zrb/runner/web_schema/session.py +5 -0
  139. zrb/runner/web_schema/token.py +11 -0
  140. zrb/runner/web_schema/user.py +32 -0
  141. zrb/runner/web_util/cookie.py +29 -0
  142. zrb/runner/{web_util.py → web_util/html.py} +1 -18
  143. zrb/runner/web_util/token.py +72 -0
  144. zrb/runner/web_util/user.py +63 -0
  145. zrb/session/session.py +6 -4
  146. zrb/session_state_logger/{default_session_state_logger.py → session_state_logger_factory.py} +1 -1
  147. zrb/task/base_task.py +56 -8
  148. zrb/task/base_trigger.py +2 -0
  149. zrb/task/cmd_task.py +9 -5
  150. zrb/task/http_check.py +2 -0
  151. zrb/task/llm_task.py +184 -71
  152. zrb/task/make_task.py +2 -0
  153. zrb/task/rsync_task.py +2 -0
  154. zrb/task/scaffolder.py +8 -5
  155. zrb/task/scheduler.py +2 -0
  156. zrb/task/tcp_check.py +2 -0
  157. zrb/task_status/task_status.py +4 -3
  158. zrb/util/cmd/command.py +1 -0
  159. zrb/util/file.py +7 -1
  160. zrb/util/llm/tool.py +3 -7
  161. {zrb-1.0.0a20.dist-info → zrb-1.0.0b3.dist-info}/METADATA +9 -52
  162. zrb-1.0.0b3.dist-info/RECORD +307 -0
  163. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/any_client_method.py +0 -27
  164. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_usecase.py +0 -65
  165. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/api_client.py +0 -6
  166. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/direct_client.py +0 -6
  167. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/factory.py +0 -9
  168. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app.py +0 -20
  169. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_usecase.py +0 -245
  170. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/any_client.py +0 -33
  171. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/api_client.py +0 -7
  172. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/direct_client.py +0 -6
  173. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/factory.py +0 -9
  174. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_user_table.py +0 -37
  175. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase.py +0 -53
  176. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase_factory.py +0 -6
  177. zrb/runner/web_config.py +0 -288
  178. zrb/runner/web_controller/home_page/controller.py +0 -33
  179. zrb/runner/web_controller/login_page/controller.py +0 -25
  180. zrb/runner/web_controller/logout_page/controller.py +0 -26
  181. zrb-1.0.0a20.dist-info/RECORD +0 -243
  182. /zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/{create_column_task.py → add_column_task.py} +0 -0
  183. /zrb/{runner/web_controller → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission}/__init__.py +0 -0
  184. /zrb/{runner/web_controller/group_info_page → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role}/__init__.py +0 -0
  185. /zrb/runner/{web_controller/home_page → web_route}/__init__.py +0 -0
  186. /zrb/runner/{web_controller/session_page → web_route/home_page}/__init__.py +0 -0
  187. /zrb/runner/{web_controller/session_page → web_route/node_page/task}/partial/input.html +0 -0
  188. /zrb/runner/{web_controller/static → web_route/static/resources}/common.css +0 -0
  189. /zrb/runner/{web_controller/static → web_route/static/resources}/favicon-32x32.png +0 -0
  190. /zrb/runner/{web_controller/static → web_route/static/resources}/login/event.js +0 -0
  191. /zrb/runner/{web_controller/static → web_route/static/resources}/logout/event.js +0 -0
  192. /zrb/runner/{web_controller/static → web_route/static/resources}/pico.min.css +0 -0
  193. /zrb/runner/{web_controller/static → web_route/static/resources}/session/common-util.js +0 -0
  194. {zrb-1.0.0a20.dist-info → zrb-1.0.0b3.dist-info}/WHEEL +0 -0
  195. {zrb-1.0.0a20.dist-info → zrb-1.0.0b3.dist-info}/entry_points.txt +0 -0
zrb/cmd/cmd_val.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from collections.abc import Callable
3
3
 
4
- from zrb.attr.type import fstring
4
+ from zrb.attr.type import StrAttr, fstring
5
5
  from zrb.context.context import Context
6
+ from zrb.util.attr import get_str_attr
6
7
  from zrb.util.file import read_file
7
8
 
8
9
 
@@ -13,22 +14,22 @@ class AnyCmdVal(ABC):
13
14
 
14
15
 
15
16
  class CmdPath(AnyCmdVal):
16
- def __init__(self, path: str, auto_render: bool = True):
17
+ def __init__(self, path: StrAttr, auto_render: bool = True):
17
18
  self._path = path
18
19
  self._auto_render = auto_render
19
20
 
20
21
  def to_str(self, ctx: Context) -> str:
21
- file_path = ctx.render(self._path) if self._auto_render else self._path
22
+ file_path = get_str_attr(ctx, self._path, "", self._auto_render)
22
23
  return read_file(file_path)
23
24
 
24
25
 
25
26
  class Cmd(AnyCmdVal):
26
- def __init__(self, cmd: str, auto_render: str):
27
+ def __init__(self, cmd: StrAttr, auto_render: bool = True):
27
28
  self._cmd = cmd
28
29
  self._auto_render = auto_render
29
30
 
30
31
  def to_str(self, ctx: Context) -> str:
31
- return ctx.render(self._cmd) if self._auto_render else self._cmd
32
+ return get_str_attr(ctx, self._cmd, "", self._auto_render)
32
33
 
33
34
 
34
35
  SingleCmdVal = AnyCmdVal | fstring | Callable[[Context], str]
zrb/config.py CHANGED
@@ -71,13 +71,26 @@ WEB_REFRESH_TOKEN_COOKIE_NAME = os.getenv(
71
71
  WEB_SECRET_KEY = os.getenv("ZRB_WEB_SECRET", "zrb")
72
72
  WEB_ENABLE_AUTH = to_boolean(os.getenv("ZRB_WEB_ENABLE_AUTH", "0"))
73
73
  WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES = int(
74
- os.getenv("ZRB_WEB_ACCESS_TOKEN_EXPIRE", "30")
74
+ os.getenv("ZRB_WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30")
75
75
  )
76
76
  WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES = int(
77
- os.getenv("ZRB_WEB_ACCESS_TOKEN_EXPIRE", f"{60*24*7}")
77
+ os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60")
78
78
  )
79
79
  LLM_MODEL = os.getenv("ZRB_LLM_MODEL", "ollama_chat/llama3.1")
80
- LLM_SYSTEM_PROMPT = os.getenv("ZRB_LLM_SYSTEM_PROMPT", "You are a helpful assistant")
80
+
81
+ _DEFAULT_PROMPT = """
82
+ You are a reliable assistant focused on providing accurate, helpful, and factual information.
83
+
84
+ Key guidelines:
85
+ 1. Prioritize correctness and clarity.
86
+ 2. Avoid guessing—clearly state if more information is needed.
87
+ 3. Distinguish facts from opinions.
88
+ 4. Use phrases like "to the best of my knowledge" when appropriate.
89
+
90
+ If unsure or lacking current data, inform the user and suggest verification.
91
+ Accuracy always takes precedence over completeness.
92
+ """.strip()
93
+ LLM_SYSTEM_PROMPT = os.getenv("ZRB_LLM_SYSTEM_PROMPT", _DEFAULT_PROMPT)
81
94
  LLM_HISTORY_FILE = os.getenv(
82
95
  "ZRB_LLM_HISTORY_FILE",
83
96
  os.path.expanduser(os.path.join("~", ".zrb-llm-history.json")),
@@ -4,6 +4,13 @@ from zrb.context.any_context import AnyContext
4
4
 
5
5
 
6
6
  class AnyContentTransformer(ABC):
7
+
8
+ @property
9
+ @abstractmethod
10
+ def name(self) -> str:
11
+ """Transformer's name"""
12
+ pass
13
+
7
14
  @abstractmethod
8
15
  def match(self, ctx: AnyContext, file_path: str) -> bool:
9
16
  """
@@ -10,6 +10,7 @@ from zrb.util.file import read_file, write_file
10
10
  class ContentTransformer(AnyContentTransformer):
11
11
  def __init__(
12
12
  self,
13
+ name: str,
13
14
  match: list[str] | str | Callable[[AnyContext, str], bool],
14
15
  transform: (
15
16
  dict[str, str | Callable[[AnyContext], str]]
@@ -17,10 +18,15 @@ class ContentTransformer(AnyContentTransformer):
17
18
  ),
18
19
  auto_render: bool = True,
19
20
  ):
21
+ self._name = name
20
22
  self._match = match
21
23
  self._transform_file = transform
22
24
  self._auto_render = auto_render
23
25
 
26
+ @property
27
+ def name(self) -> str:
28
+ return self._name
29
+
24
30
  def match(self, ctx: AnyContext, file_path: str) -> bool:
25
31
  if callable(self._match):
26
32
  return self._match(ctx, file_path)
zrb/runner/cli.py CHANGED
@@ -1,17 +1,15 @@
1
1
  import sys
2
2
  from typing import Any
3
3
 
4
- from zrb.config import BANNER, WEB_HTTP_PORT
4
+ from zrb.config import BANNER, VERSION, WEB_HTTP_PORT
5
5
  from zrb.context.any_context import AnyContext
6
6
  from zrb.context.shared_context import SharedContext
7
7
  from zrb.group.group import Group
8
8
  from zrb.runner.common_util import get_run_kwargs
9
- from zrb.runner.web_app import create_app
10
- from zrb.runner.web_config import web_config
9
+ from zrb.runner.web_app import create_web_app
10
+ from zrb.runner.web_config.config_factory import web_config
11
11
  from zrb.session.session import Session
12
- from zrb.session_state_logger.default_session_state_logger import (
13
- default_session_state_logger,
14
- )
12
+ from zrb.session_state_logger.session_state_logger_factory import session_state_logger
15
13
  from zrb.task.any_task import AnyTask
16
14
  from zrb.task.make_task import make_task
17
15
  from zrb.util.cli.style import (
@@ -151,6 +149,15 @@ class Cli(Group):
151
149
 
152
150
 
153
151
  cli = Cli(name="zrb", description="Your Automation Powerhouse", banner=BANNER)
152
+
153
+
154
+ @make_task(
155
+ name="version", description="🌟 Get current Zrb version", retries=0, group=cli
156
+ )
157
+ def get_version(_: AnyContext):
158
+ return VERSION
159
+
160
+
154
161
  server_group = cli.add_group(
155
162
  Group(name="server", description="🌐 Server related command")
156
163
  )
@@ -167,6 +174,6 @@ server_group = cli.add_group(
167
174
  async def start_server(_: AnyContext):
168
175
  from uvicorn import Config, Server
169
176
 
170
- app = create_app(cli, web_config, default_session_state_logger)
177
+ app = create_web_app(cli, web_config, session_state_logger)
171
178
  server = Server(Config(app=app, host="0.0.0.0", port=WEB_HTTP_PORT, loop="asyncio"))
172
179
  await server.serve()
zrb/runner/web_app.py CHANGED
@@ -1,47 +1,38 @@
1
1
  import asyncio
2
- import json
3
- import os
4
2
  import sys
5
- from datetime import datetime, timedelta
6
- from typing import TYPE_CHECKING, Annotated
3
+ from typing import TYPE_CHECKING
7
4
 
8
5
  from zrb.config import BANNER, VERSION
9
- from zrb.context.shared_context import SharedContext
10
6
  from zrb.group.any_group import AnyGroup
11
- from zrb.runner.common_util import get_run_kwargs
12
- from zrb.runner.web_config import Token, WebConfig
13
- from zrb.runner.web_controller.error_page.controller import show_error_page
14
- from zrb.runner.web_controller.group_info_page.controller import show_group_info_page
15
- from zrb.runner.web_controller.home_page.controller import show_home_page
16
- from zrb.runner.web_controller.login_page.controller import show_login_page
17
- from zrb.runner.web_controller.logout_page.controller import show_logout_page
18
- from zrb.runner.web_controller.session_page.controller import show_session_page
19
- from zrb.runner.web_util import NewSessionResponse
20
- from zrb.session.session import Session
21
- from zrb.session_state_log.session_state_log import SessionStateLog, SessionStateLogList
7
+ from zrb.runner.web_config.config import WebConfig
8
+ from zrb.runner.web_route.docs_route import serve_docs
9
+ from zrb.runner.web_route.error_page.serve_default_404 import serve_default_404
10
+ from zrb.runner.web_route.home_page.home_page_route import serve_home_page
11
+ from zrb.runner.web_route.login_api_route import serve_login_api
12
+ from zrb.runner.web_route.login_page.login_page_route import serve_login_page
13
+ from zrb.runner.web_route.logout_api_route import serve_logout_api
14
+ from zrb.runner.web_route.logout_page.logout_page_route import serve_logout_page
15
+ from zrb.runner.web_route.node_page.node_page_route import serve_node_page
16
+ from zrb.runner.web_route.refresh_token_api_route import serve_refresh_token_api
17
+ from zrb.runner.web_route.static.static_route import serve_static_resources
18
+ from zrb.runner.web_route.task_input_api_route import serve_task_input_api
19
+ from zrb.runner.web_route.task_session_api_route import serve_task_session_api
22
20
  from zrb.session_state_logger.any_session_state_logger import AnySessionStateLogger
23
- from zrb.task.any_task import AnyTask
24
- from zrb.util.group import NodeNotFoundError, extract_node_from_args, get_node_path
25
21
 
26
22
  if TYPE_CHECKING:
27
23
  # We want fastapi to only be loaded when necessary to decrease footprint
28
24
  from fastapi import FastAPI
29
25
 
30
26
 
31
- def create_app(
27
+ def create_web_app(
32
28
  root_group: AnyGroup,
33
29
  web_config: WebConfig,
34
30
  session_state_logger: AnySessionStateLogger,
35
31
  ) -> "FastAPI":
36
32
  from contextlib import asynccontextmanager
37
33
 
38
- from fastapi import Depends, FastAPI, HTTPException, Query, Request, Response
39
- from fastapi.openapi.docs import get_swagger_ui_html
40
- from fastapi.responses import FileResponse, HTMLResponse
41
- from fastapi.security import OAuth2PasswordRequestForm
42
- from fastapi.staticfiles import StaticFiles
34
+ from fastapi import FastAPI
43
35
 
44
- _STATIC_DIR = os.path.join(os.path.dirname(__file__), "web_controller", "static")
45
36
  _COROS = []
46
37
 
47
38
  @asynccontextmanager
@@ -62,218 +53,17 @@ def create_app(
62
53
  lifespan=lifespan,
63
54
  docs_url=None,
64
55
  )
65
- app.mount("/static", StaticFiles(directory=_STATIC_DIR), name="static")
66
-
67
- # Serve static files
68
- @app.get("/static/{file_path:path}", include_in_schema=False)
69
- async def static_files(file_path: str):
70
- full_path = os.path.join(_STATIC_DIR, file_path)
71
- if os.path.isfile(full_path):
72
- return FileResponse(full_path)
73
- raise HTTPException(status_code=404, detail="File not found")
74
-
75
- @app.get("/docs", include_in_schema=False)
76
- async def swagger_ui_html():
77
- return get_swagger_ui_html(
78
- openapi_url="/openapi.json",
79
- title="Zrb",
80
- swagger_favicon_url="/static/favicon-32x32.png",
81
- )
82
-
83
- # Serve homepage
84
- @app.get("/", response_class=HTMLResponse, include_in_schema=False)
85
- @app.get("/ui", response_class=HTMLResponse, include_in_schema=False)
86
- @app.get("/ui/", response_class=HTMLResponse, include_in_schema=False)
87
- async def home_page_ui(request: Request) -> HTMLResponse:
88
- user = await web_config.get_user_by_request(request)
89
- return show_home_page(user, root_group)
90
-
91
- @app.get("/ui/{path:path}", response_class=HTMLResponse, include_in_schema=False)
92
- async def ui_page(path: str, request: Request) -> HTMLResponse:
93
- user = await web_config.get_user_by_request(request)
94
- # Avoid capturing '/ui' itself
95
- if not path:
96
- return show_error_page(user, root_group, 422, "Undefined path")
97
- args = path.strip("/").split("/")
98
- try:
99
- node, node_path, residual_args = extract_node_from_args(root_group, args)
100
- except NodeNotFoundError as e:
101
- return show_error_page(user, root_group, 404, str(e))
102
- url = f"/ui/{'/'.join(node_path)}/"
103
- if isinstance(node, AnyTask):
104
- if not user.can_access_task(node):
105
- return show_error_page(user, root_group, 403, "Forbidden")
106
- shared_ctx = SharedContext(env=dict(os.environ))
107
- session = Session(shared_ctx=shared_ctx, root_group=root_group)
108
- return show_session_page(
109
- user, root_group, node, session, url, residual_args
110
- )
111
- elif isinstance(node, AnyGroup):
112
- if not user.can_access_group(node):
113
- return show_error_page(user, root_group, 403, "Forbidden")
114
- return show_group_info_page(user, root_group, node, url)
115
- return show_error_page(user, root_group, 404, "Not found")
116
-
117
- @app.get("/login", response_class=HTMLResponse, include_in_schema=False)
118
- async def login(request: Request) -> HTMLResponse:
119
- user = await web_config.get_user_by_request(request)
120
- return show_login_page(user, root_group)
121
-
122
- @app.get("/logout", response_class=HTMLResponse, include_in_schema=False)
123
- async def logout(request: Request) -> HTMLResponse:
124
- user = await web_config.get_user_by_request(request)
125
- return show_logout_page(user, root_group)
126
-
127
- @app.post("/api/v1/login")
128
- async def login_api(
129
- response: Response, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
130
- ):
131
- token = web_config.generate_tokens(
132
- username=form_data.username, password=form_data.password
133
- )
134
- _set_auth_cookie(response, token)
135
- return token
136
-
137
- @app.post("/api/v1/refresh-token")
138
- async def refresh_token_api(
139
- response: Response, refresh_token: str = Query(..., description="Refresh token")
140
- ):
141
- token = web_config.refresh_tokens(refresh_token)
142
- _set_auth_cookie(response, token)
143
- return token
144
-
145
- def _set_auth_cookie(response: Response, token: Token):
146
- response.set_cookie(
147
- key=web_config.access_token_cookie_name,
148
- value=token.access_token,
149
- httponly=True,
150
- max_age=web_config.access_token_max_age,
151
- expires=web_config.access_token_max_age,
152
- )
153
- response.set_cookie(
154
- key=web_config.refresh_token_cookie_name,
155
- value=token.refresh_token,
156
- httponly=True,
157
- max_age=web_config.refresh_token_max_age,
158
- expires=web_config.refresh_token_max_age,
159
- )
160
-
161
- @app.get("/api/v1/logout")
162
- @app.post("/api/v1/logout")
163
- async def logout_api(response: Response):
164
- response.delete_cookie(web_config.access_token_cookie_name)
165
- response.delete_cookie(web_config.refresh_token_cookie_name)
166
- return {"message": "Logout successful"}
167
-
168
- @app.post("/api/sessions/{path:path}")
169
- async def create_new_session_api(
170
- path: str,
171
- request: Request,
172
- ) -> NewSessionResponse:
173
- """
174
- Creating new session
175
- """
176
- user = await web_config.get_user_by_request(request)
177
- args = path.strip("/").split("/")
178
- task, _, residual_args = extract_node_from_args(root_group, args)
179
- if isinstance(task, AnyTask):
180
- if not user.can_access_task(task):
181
- raise HTTPException(status_code=403)
182
- session_name = residual_args[0] if residual_args else None
183
- if not session_name:
184
- body = await request.json()
185
- shared_ctx = SharedContext(env=dict(os.environ))
186
- session = Session(shared_ctx=shared_ctx, root_group=root_group)
187
- coro = asyncio.create_task(task.async_run(session, str_kwargs=body))
188
- _COROS.append(coro)
189
- coro.add_done_callback(lambda coro: _COROS.remove(coro))
190
- return NewSessionResponse(session_name=session.name)
191
- raise HTTPException(status_code=404)
192
-
193
- @app.get("/api/inputs/{path:path}", response_model=dict[str, str])
194
- async def get_default_inputs_api(
195
- path: str,
196
- request: Request,
197
- query: str = Query("{}", description="JSON encoded inputs"),
198
- ) -> dict[str, str]:
199
- """
200
- Getting input completion for path
201
- """
202
- user = await web_config.get_user_by_request(request)
203
- args = path.strip("/").split("/")
204
- task, _, _ = extract_node_from_args(root_group, args)
205
- if isinstance(task, AnyTask):
206
- if not user.can_access_task(task):
207
- raise HTTPException(status_code=403)
208
- query_dict = json.loads(query)
209
- run_kwargs = get_run_kwargs(
210
- task=task, args=[], kwargs=query_dict, prompt=False
211
- )
212
- return run_kwargs
213
- raise HTTPException(status_code=404, detail="Not Found")
214
-
215
- @app.get(
216
- "/api/sessions/{path:path}",
217
- response_model=SessionStateLog | SessionStateLogList,
218
- )
219
- async def get_session_api(
220
- path: str,
221
- request: Request,
222
- min_start_query: str = Query(default=None, alias="from"),
223
- max_start_query: str = Query(default=None, alias="to"),
224
- page: int = Query(default=0, alias="page"),
225
- limit: int = Query(default=10, alias="limit"),
226
- ) -> SessionStateLog | SessionStateLogList:
227
- """
228
- Getting existing session or sessions
229
- """
230
- user = await web_config.get_user_by_request(request)
231
- args = path.strip("/").split("/")
232
- task, _, residual_args = extract_node_from_args(root_group, args)
233
- if isinstance(task, AnyTask) and residual_args:
234
- if not user.can_access_task(task):
235
- raise HTTPException(status_code=403)
236
- if residual_args[0] == "list":
237
- task_path = get_node_path(root_group, task)
238
- max_start_time = (
239
- datetime.now()
240
- if max_start_query is None
241
- else datetime.strptime(max_start_query, "%Y-%m-%d %H:%M:%S")
242
- )
243
- min_start_time = (
244
- max_start_time - timedelta(hours=1)
245
- if min_start_query is None
246
- else datetime.strptime(min_start_query, "%Y-%m-%d %H:%M:%S")
247
- )
248
- return _get_existing_sessions(
249
- task_path, min_start_time, max_start_time, page, limit
250
- )
251
- else:
252
- return _read_session(residual_args[0])
253
- raise HTTPException(status_code=404, detail="Not Found")
254
-
255
- def _get_existing_sessions(
256
- task_path: list[str],
257
- min_start_time: datetime,
258
- max_start_time: datetime,
259
- page: int,
260
- limit: int,
261
- ) -> SessionStateLogList:
262
- try:
263
- return session_state_logger.list(
264
- task_path,
265
- min_start_time=min_start_time,
266
- max_start_time=max_start_time,
267
- page=page,
268
- limit=limit,
269
- )
270
- except Exception as e:
271
- raise HTTPException(status_code=500, detail=str(e))
272
-
273
- def _read_session(session_name: str) -> SessionStateLog:
274
- try:
275
- return session_state_logger.read(session_name)
276
- except Exception as e:
277
- raise HTTPException(status_code=500, detail=str(e))
278
56
 
57
+ serve_default_404(app, root_group, web_config)
58
+ serve_static_resources(app, web_config)
59
+ serve_docs(app)
60
+ serve_home_page(app, root_group, web_config)
61
+ serve_login_page(app, root_group, web_config)
62
+ serve_logout_page(app, root_group, web_config)
63
+ serve_node_page(app, root_group, web_config)
64
+ serve_login_api(app, web_config)
65
+ serve_logout_api(app, web_config)
66
+ serve_refresh_token_api(app, web_config)
67
+ serve_task_input_api(app, root_group, web_config)
68
+ serve_task_session_api(app, root_group, web_config, session_state_logger, _COROS)
279
69
  return app
@@ -0,0 +1,91 @@
1
+ from typing import Callable
2
+
3
+ from zrb.runner.web_schema.user import User
4
+ from zrb.task.any_task import AnyTask
5
+
6
+
7
+ class WebConfig:
8
+ def __init__(
9
+ self,
10
+ port: int,
11
+ secret_key: str,
12
+ access_token_expire_minutes: int,
13
+ refresh_token_expire_minutes: int,
14
+ access_token_cookie_name: str,
15
+ refresh_token_cookie_name: str,
16
+ enable_auth: bool,
17
+ super_admin_username: str,
18
+ super_admin_password: str,
19
+ guest_username: str,
20
+ guest_accessible_tasks: list[AnyTask | str] = [],
21
+ find_user_by_username: Callable[[str], User | None] | None = None,
22
+ ):
23
+ self.secret_key = secret_key
24
+ self.access_token_expire_minutes = access_token_expire_minutes
25
+ self.refresh_token_expire_minutes = refresh_token_expire_minutes
26
+ self.access_token_cookie_name = access_token_cookie_name
27
+ self.refresh_token_cookie_name = refresh_token_cookie_name
28
+ self.enable_auth = enable_auth
29
+ self.port = port
30
+ self._user_list = []
31
+ self.super_admin_username = super_admin_username
32
+ self.super_admin_password = super_admin_password
33
+ self.guest_username = guest_username
34
+ self.guest_accessible_tasks = guest_accessible_tasks
35
+ self._find_user_by_username = find_user_by_username
36
+
37
+ @property
38
+ def default_user(self) -> User:
39
+ if self.enable_auth:
40
+ return User(
41
+ username=self.guest_username,
42
+ password="",
43
+ is_guest=True,
44
+ accessible_tasks=self.guest_accessible_tasks,
45
+ )
46
+ return User(
47
+ username=self.guest_username,
48
+ password="",
49
+ is_guest=True,
50
+ is_super_admin=True,
51
+ )
52
+
53
+ @property
54
+ def super_admin(self) -> User:
55
+ return User(
56
+ username=self.super_admin_username,
57
+ password=self.super_admin_password,
58
+ is_super_admin=True,
59
+ )
60
+
61
+ @property
62
+ def user_list(self) -> list[User]:
63
+ if not self.enable_auth:
64
+ return [self.default_user]
65
+ return self._user_list + [self.super_admin, self.default_user]
66
+
67
+ def set_guest_accessible_tasks(self, tasks: list[AnyTask | str]):
68
+ self.guest_accessible_tasks = tasks
69
+
70
+ def set_find_user_by_username(
71
+ self, find_user_by_username: Callable[[str], User | None]
72
+ ):
73
+ self._find_user_by_username = find_user_by_username
74
+
75
+ def append_user(self, user: User):
76
+ duplicates = [
77
+ existing_user
78
+ for existing_user in self.user_list
79
+ if existing_user.username == user.username
80
+ ]
81
+ if len(duplicates) > 0:
82
+ raise ValueError(f"User already exists {user.username}")
83
+ self._user_list.append(user)
84
+
85
+ def find_user_by_username(self, username: str) -> User | None:
86
+ user = None
87
+ if self._find_user_by_username is not None:
88
+ user = self._find_user_by_username(username)
89
+ if user is None:
90
+ user = next((u for u in self.user_list if u.username == username), None)
91
+ return user
@@ -0,0 +1,26 @@
1
+ from zrb.config import (
2
+ WEB_ACCESS_TOKEN_COOKIE_NAME,
3
+ WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES,
4
+ WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES,
5
+ WEB_ENABLE_AUTH,
6
+ WEB_GUEST_USERNAME,
7
+ WEB_HTTP_PORT,
8
+ WEB_REFRESH_TOKEN_COOKIE_NAME,
9
+ WEB_SECRET_KEY,
10
+ WEB_SUPER_ADMIN_PASSWORD,
11
+ WEB_SUPER_ADMIN_USERNAME,
12
+ )
13
+ from zrb.runner.web_config.config import WebConfig
14
+
15
+ web_config = WebConfig(
16
+ port=WEB_HTTP_PORT,
17
+ secret_key=WEB_SECRET_KEY,
18
+ access_token_expire_minutes=WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES,
19
+ refresh_token_expire_minutes=WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES,
20
+ access_token_cookie_name=WEB_ACCESS_TOKEN_COOKIE_NAME,
21
+ refresh_token_cookie_name=WEB_REFRESH_TOKEN_COOKIE_NAME,
22
+ enable_auth=WEB_ENABLE_AUTH,
23
+ super_admin_username=WEB_SUPER_ADMIN_USERNAME,
24
+ super_admin_password=WEB_SUPER_ADMIN_PASSWORD,
25
+ guest_username=WEB_GUEST_USERNAME,
26
+ )
@@ -0,0 +1,17 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ # We want fastapi to only be loaded when necessary to decrease footprint
5
+ from fastapi import FastAPI
6
+
7
+
8
+ def serve_docs(app: "FastAPI") -> None:
9
+ from fastapi.openapi.docs import get_swagger_ui_html
10
+
11
+ @app.get("/docs", include_in_schema=False)
12
+ async def swagger_ui_html():
13
+ return get_swagger_ui_html(
14
+ openapi_url="/openapi.json",
15
+ title="Zrb",
16
+ swagger_favicon_url="/static/favicon-32x32.png",
17
+ )
@@ -0,0 +1,28 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from zrb.group.any_group import AnyGroup
4
+ from zrb.runner.web_config.config import WebConfig
5
+ from zrb.runner.web_route.error_page.show_error_page import show_error_page
6
+ from zrb.runner.web_util.user import get_user_from_request
7
+
8
+ if TYPE_CHECKING:
9
+ # We want fastapi to only be loaded when necessary to decrease footprint
10
+ from fastapi import FastAPI
11
+
12
+
13
+ def serve_default_404(
14
+ app: "FastAPI",
15
+ root_group: AnyGroup,
16
+ web_config: WebConfig,
17
+ ) -> None:
18
+ from fastapi import Request
19
+ from fastapi.exception_handlers import http_exception_handler
20
+ from fastapi.responses import HTMLResponse
21
+
22
+ @app.exception_handler(404)
23
+ async def default_404(request: Request, exc: Exception) -> HTMLResponse:
24
+ if request.url.path.startswith("/api"):
25
+ # Re-raise the exception to let FastAPI handle it
26
+ return await http_exception_handler(request, exc)
27
+ user = await get_user_from_request(web_config, request)
28
+ return show_error_page(user, root_group, 404, "Not found")
@@ -1,8 +1,8 @@
1
1
  import os
2
2
 
3
3
  from zrb.group.any_group import AnyGroup
4
- from zrb.runner.web_config import User
5
- from zrb.runner.web_util import get_html_auth_link
4
+ from zrb.runner.web_schema.user import User
5
+ from zrb.runner.web_util.html import get_html_auth_link
6
6
  from zrb.util.file import read_file
7
7
  from zrb.util.string.format import fstring_format
8
8
 
@@ -23,5 +23,6 @@ def show_error_page(user: User, root_group: AnyGroup, status_code: int, message:
23
23
  "error_status_code": status_code,
24
24
  "error_message": message,
25
25
  },
26
- )
26
+ ),
27
+ status_code=status_code,
27
28
  )
@@ -1,5 +1,6 @@
1
1
  <!doctype html>
2
2
  <html lang="en">
3
+
3
4
  <head>
4
5
  <meta charset="utf-8">
5
6
  <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -9,6 +10,7 @@
9
10
  <title>Zrb</title>
10
11
  <link rel="stylesheet" href="/static/common.css">
11
12
  </head>
13
+
12
14
  <body>
13
15
  <header class="container">
14
16
  <hgroup>
@@ -26,8 +28,12 @@
26
28
  </hgroup>
27
29
  </header>
28
30
  <main class="container">
31
+ <article>
29
32
  <h2>{error_status_code}</h2>
30
33
  <p>{error_message}</p>
34
+ </article>
31
35
  </main>
32
36
  </body>
37
+ <script src="/refresh-token.js"></script>
38
+
33
39
  </html>