kstlib 3.3.0__tar.gz → 3.3.1__tar.gz

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 (198) hide show
  1. {kstlib-3.3.0/src/kstlib.egg-info → kstlib-3.3.1}/PKG-INFO +1 -1
  2. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/app.py +3 -3
  3. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/config.py +4 -4
  4. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/rapi/__init__.py +2 -2
  5. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/rapi/call.py +52 -70
  6. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/rapi/list.py +3 -3
  7. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/rapi/show.py +6 -6
  8. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/common.py +12 -4
  9. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/meta.py +1 -1
  10. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/rapi/__init__.py +2 -0
  11. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/rapi/config.py +10 -6
  12. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/rapi/exceptions.py +27 -0
  13. {kstlib-3.3.0 → kstlib-3.3.1/src/kstlib.egg-info}/PKG-INFO +1 -1
  14. {kstlib-3.3.0 → kstlib-3.3.1}/LICENSE.md +0 -0
  15. {kstlib-3.3.0 → kstlib-3.3.1}/MANIFEST.in +0 -0
  16. {kstlib-3.3.0 → kstlib-3.3.1}/README.md +0 -0
  17. {kstlib-3.3.0 → kstlib-3.3.1}/pyproject.toml +0 -0
  18. {kstlib-3.3.0 → kstlib-3.3.1}/setup.cfg +0 -0
  19. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/__init__.py +0 -0
  20. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/__main__.py +0 -0
  21. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/_shared/__init__.py +0 -0
  22. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/_shared/jinja.py +0 -0
  23. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/_shared/logging_helpers.py +0 -0
  24. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/_shared/redaction.py +0 -0
  25. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/__init__.py +0 -0
  26. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/channels/__init__.py +0 -0
  27. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/channels/base.py +0 -0
  28. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/channels/email.py +0 -0
  29. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/channels/slack.py +0 -0
  30. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/exceptions.py +0 -0
  31. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/manager.py +0 -0
  32. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/models.py +0 -0
  33. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/alerts/throttle.py +0 -0
  34. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/__init__.py +0 -0
  35. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/callback.py +0 -0
  36. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/check.py +0 -0
  37. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/config.py +0 -0
  38. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/errors.py +0 -0
  39. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/models.py +0 -0
  40. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/providers/__init__.py +0 -0
  41. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/providers/base.py +0 -0
  42. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/providers/oauth2.py +0 -0
  43. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/providers/oidc.py +0 -0
  44. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/session.py +0 -0
  45. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/auth/token.py +0 -0
  46. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cache/__init__.py +0 -0
  47. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cache/decorator.py +0 -0
  48. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cache/strategies.py +0 -0
  49. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/__init__.py +0 -0
  50. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/__init__.py +0 -0
  51. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/__init__.py +0 -0
  52. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/check.py +0 -0
  53. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/common.py +0 -0
  54. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/login.py +0 -0
  55. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/logout.py +0 -0
  56. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/providers.py +0 -0
  57. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/status.py +0 -0
  58. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/token.py +0 -0
  59. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/auth/whoami.py +0 -0
  60. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/ops/__init__.py +0 -0
  61. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/ops/attach.py +0 -0
  62. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/ops/common.py +0 -0
  63. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/ops/list_sessions.py +0 -0
  64. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/ops/logs.py +0 -0
  65. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/ops/start.py +0 -0
  66. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/ops/status.py +0 -0
  67. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/ops/stop.py +0 -0
  68. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/secrets/__init__.py +0 -0
  69. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/secrets/common.py +0 -0
  70. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/secrets/decrypt.py +0 -0
  71. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/secrets/doctor.py +0 -0
  72. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/secrets/encrypt.py +0 -0
  73. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/cli/commands/secrets/shred.py +0 -0
  74. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/config/__init__.py +0 -0
  75. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/config/exceptions.py +0 -0
  76. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/config/export.py +0 -0
  77. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/config/loader.py +0 -0
  78. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/config/sops.py +0 -0
  79. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/db/__init__.py +0 -0
  80. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/db/aiosqlcipher.py +0 -0
  81. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/db/cipher.py +0 -0
  82. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/db/database.py +0 -0
  83. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/db/exceptions.py +0 -0
  84. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/db/pool.py +0 -0
  85. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/helpers/__init__.py +0 -0
  86. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/helpers/exceptions.py +0 -0
  87. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/helpers/time_trigger.py +0 -0
  88. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/kstlib.conf.yml +0 -0
  89. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/limits.py +0 -0
  90. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/logging/__init__.py +0 -0
  91. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/logging/manager.py +0 -0
  92. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/__init__.py +0 -0
  93. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/_helpers.py +0 -0
  94. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/builder.py +0 -0
  95. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/collector.py +0 -0
  96. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/exceptions.py +0 -0
  97. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/filesystem.py +0 -0
  98. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/throttle.py +0 -0
  99. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/transport.py +0 -0
  100. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/transports/__init__.py +0 -0
  101. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/transports/gmail.py +0 -0
  102. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/transports/resend.py +0 -0
  103. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/transports/ses.py +0 -0
  104. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/mail/transports/smtp.py +0 -0
  105. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/metrics/__init__.py +0 -0
  106. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/metrics/decorators.py +0 -0
  107. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/metrics/exceptions.py +0 -0
  108. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/__init__.py +0 -0
  109. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/_styles.py +0 -0
  110. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/cell.py +0 -0
  111. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/config.py +0 -0
  112. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/delivery.py +0 -0
  113. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/exceptions.py +0 -0
  114. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/image.py +0 -0
  115. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/kv.py +0 -0
  116. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/list.py +0 -0
  117. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/metric.py +0 -0
  118. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/monitoring.py +0 -0
  119. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/renderer.py +0 -0
  120. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/service.py +0 -0
  121. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/table.py +0 -0
  122. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/monitoring/types.py +0 -0
  123. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ops/__init__.py +0 -0
  124. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ops/base.py +0 -0
  125. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ops/container.py +0 -0
  126. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ops/exceptions.py +0 -0
  127. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ops/manager.py +0 -0
  128. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ops/models.py +0 -0
  129. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ops/tmux.py +0 -0
  130. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ops/validators.py +0 -0
  131. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/__init__.py +0 -0
  132. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/base.py +0 -0
  133. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/exceptions.py +0 -0
  134. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/models.py +0 -0
  135. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/runner.py +0 -0
  136. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/steps/__init__.py +0 -0
  137. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/steps/_base.py +0 -0
  138. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/steps/_helpers.py +0 -0
  139. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/steps/callable.py +0 -0
  140. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/steps/python.py +0 -0
  141. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/steps/shell.py +0 -0
  142. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/pipeline/validators.py +0 -0
  143. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/py.typed +0 -0
  144. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/rapi/client.py +0 -0
  145. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/rapi/credentials.py +0 -0
  146. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/resilience/__init__.py +0 -0
  147. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/resilience/circuit_breaker.py +0 -0
  148. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/resilience/exceptions.py +0 -0
  149. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/resilience/heartbeat.py +0 -0
  150. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/resilience/rate_limiter.py +0 -0
  151. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/resilience/shutdown.py +0 -0
  152. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/resilience/watchdog.py +0 -0
  153. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/__init__.py +0 -0
  154. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/exceptions.py +0 -0
  155. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/models.py +0 -0
  156. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/providers/__init__.py +0 -0
  157. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/providers/base.py +0 -0
  158. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/providers/environment.py +0 -0
  159. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/providers/keyring.py +0 -0
  160. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/providers/kms.py +0 -0
  161. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/providers/kwargs.py +0 -0
  162. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/providers/sops.py +0 -0
  163. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/resolver.py +0 -0
  164. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secrets/sensitive.py +0 -0
  165. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secure/__init__.py +0 -0
  166. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secure/fs.py +0 -0
  167. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secure/passwords.py +0 -0
  168. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/secure/permissions.py +0 -0
  169. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ssl.py +0 -0
  170. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/transform/__init__.py +0 -0
  171. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/transform/chain.py +0 -0
  172. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/transform/config.py +0 -0
  173. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/transform/exceptions.py +0 -0
  174. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/transform/primitives.py +0 -0
  175. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/transform/validators.py +0 -0
  176. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ui/__init__.py +0 -0
  177. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ui/exceptions.py +0 -0
  178. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ui/panels.py +0 -0
  179. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ui/spinner.py +0 -0
  180. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/ui/tables.py +0 -0
  181. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/__init__.py +0 -0
  182. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/dict.py +0 -0
  183. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/formatting.py +0 -0
  184. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/http_trace.py +0 -0
  185. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/lazy.py +0 -0
  186. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/secure_delete.py +0 -0
  187. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/serialization.py +0 -0
  188. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/text.py +0 -0
  189. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/utils/validators.py +0 -0
  190. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/websocket/__init__.py +0 -0
  191. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/websocket/exceptions.py +0 -0
  192. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/websocket/manager.py +0 -0
  193. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib/websocket/models.py +0 -0
  194. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib.egg-info/SOURCES.txt +0 -0
  195. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib.egg-info/dependency_links.txt +0 -0
  196. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib.egg-info/entry_points.txt +0 -0
  197. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib.egg-info/requires.txt +0 -0
  198. {kstlib-3.3.0 → kstlib-3.3.1}/src/kstlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kstlib
3
- Version: 3.3.0
3
+ Version: 3.3.1
4
4
  Summary: Config-driven helpers for Python projects (dynamic config, secure secrets, preset logging, and more…)
5
5
  Author-email: Michel TRUONG <michel.truong@gmail.com>
6
6
  Maintainer-email: Michel TRUONG <michel.truong@gmail.com>
@@ -24,7 +24,7 @@ from kstlib.cli.commands.ops import register_cli as register_ops_cli
24
24
  from kstlib.cli.commands.rapi import register_cli as register_rapi_cli
25
25
  from kstlib.cli.commands.secrets import register_cli as register_secrets_cli
26
26
  from kstlib.cli.commands.secrets import shred as secrets_shred
27
- from kstlib.cli.common import console
27
+ from kstlib.cli.common import console, err_console
28
28
  from kstlib.logging import LogManager, get_logger, init_logging
29
29
 
30
30
  app = typer.Typer(add_completion=False, name=meta.__app_name__)
@@ -223,8 +223,8 @@ def main( # pylint: disable=unused-argument
223
223
  # Explicit --log-level takes precedence
224
224
  level = log_level.upper()
225
225
  if level not in LOG_LEVELS:
226
- console.print(f"[red]Invalid log level: {log_level}[/]")
227
- console.print(f"[dim]Valid levels: {', '.join(LOG_LEVELS)}[/]")
226
+ err_console.print(f"[red]Invalid log level: {log_level}[/]")
227
+ err_console.print(f"[dim]Valid levels: {', '.join(LOG_LEVELS)}[/]")
228
228
  raise typer.Exit(1)
229
229
  elif verbose > 0:
230
230
  # -v/-vv/-vvv flags
@@ -8,7 +8,7 @@ from typing import Annotated
8
8
  import typer
9
9
  from rich.panel import Panel
10
10
 
11
- from kstlib.cli.common import console
11
+ from kstlib.cli.common import console, err_console
12
12
  from kstlib.config.export import (
13
13
  ConfigExportError,
14
14
  ConfigExportOptions,
@@ -59,18 +59,18 @@ def export_command(
59
59
  try:
60
60
  result = export_configuration(options)
61
61
  except ConfigExportError as exc:
62
- console.print(f"[bold red]{exc}[/bold red]")
62
+ err_console.print(f"[bold red]{exc}[/bold red]")
63
63
  raise typer.Exit(code=1) from exc
64
64
 
65
65
  if stdout:
66
66
  if result.content is None:
67
- console.print("[bold red]Export failed: empty content.[/bold red]")
67
+ err_console.print("[bold red]Export failed: empty content.[/bold red]")
68
68
  raise typer.Exit(code=1)
69
69
  console.print(result.content)
70
70
  return
71
71
 
72
72
  if result.destination is None:
73
- console.print("[bold red]Export failed: missing destination file.[/bold red]")
73
+ err_console.print("[bold red]Export failed: missing destination file.[/bold red]")
74
74
  raise typer.Exit(code=1)
75
75
  console.print(
76
76
  Panel.fit(
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
7
7
  import typer
8
8
  from typer.core import TyperGroup
9
9
 
10
- from kstlib.cli.common import console
10
+ from kstlib.cli.common import err_console
11
11
  from kstlib.rapi import load_rapi_config
12
12
 
13
13
  if TYPE_CHECKING:
@@ -35,7 +35,7 @@ def _load_config_or_exit() -> RapiConfigManager:
35
35
  try:
36
36
  return load_rapi_config()
37
37
  except Exception as exc: # pylint: disable=broad-exception-caught
38
- console.print(f"[red]Failed to load rapi config: {exc}[/]")
38
+ err_console.print(f"[red]Failed to load rapi config: {exc}[/]")
39
39
  raise typer.Exit(code=1) from exc
40
40
 
41
41
 
@@ -6,6 +6,7 @@ import json
6
6
  import re
7
7
  from typing import TYPE_CHECKING, Annotated, Any, cast
8
8
 
9
+ import httpx
9
10
  import typer
10
11
  from typer.core import TyperCommand
11
12
 
@@ -16,7 +17,9 @@ from kstlib.rapi import (
16
17
  CredentialError,
17
18
  EndpointAmbiguousError,
18
19
  EndpointNotFoundError,
20
+ PathParameterError,
19
21
  RapiClient,
22
+ RapiError,
20
23
  RapiResponse,
21
24
  RequestError,
22
25
  ResponseTooLargeError,
@@ -739,6 +742,44 @@ def _render_response(
739
742
  _format_output(response, fmt, quiet, out, raw=raw, minify=minify)
740
743
 
741
744
 
745
+ def _payload(key: str, value: object) -> dict[str, Any] | None:
746
+ """Build a single-key error payload, or None when the value is empty."""
747
+ return {key: value} if value else None
748
+
749
+
750
+ def _build_call_error_result(e: RapiError | httpx.InvalidURL) -> CommandResult:
751
+ """Map a failed rapi call error to its ERROR CommandResult.
752
+
753
+ Handles every exception caught by the ``call`` command except
754
+ ``AuthExpiredError`` (which keeps its dedicated exit-code-4 handling).
755
+ Each branch preserves the exact message and payload of the original
756
+ per-type handler.
757
+ """
758
+ payload: dict[str, Any] | None = None
759
+ if isinstance(e, EndpointNotFoundError):
760
+ message = f"Endpoint not found: {e.endpoint_ref}"
761
+ payload = _payload("searched_apis", e.searched_apis)
762
+ elif isinstance(e, EndpointAmbiguousError):
763
+ message = f"Ambiguous endpoint: '{e.endpoint_name}' exists in multiple APIs"
764
+ payload = {"matching_apis": e.matching_apis}
765
+ elif isinstance(e, ServerNotFoundError):
766
+ message = f"Server profile not found: '{e.server_name}'. Available: {e.available or '(none configured)'}"
767
+ payload = _payload("available_servers", e.available)
768
+ elif isinstance(e, CredentialError):
769
+ message = f"Credential error: {e}"
770
+ payload = _payload("credential_name", e.credential_name)
771
+ elif isinstance(e, RequestError):
772
+ message = f"Request failed: {e}"
773
+ payload = {"status_code": e.status_code, "retryable": e.retryable}
774
+ elif isinstance(e, ResponseTooLargeError):
775
+ message = f"Response too large: {e.response_size} bytes (max: {e.max_size})"
776
+ elif isinstance(e, PathParameterError):
777
+ message = f"Path parameter error: {e}"
778
+ else:
779
+ message = f"Invalid URL: {e}"
780
+ return CommandResult(status=CommandStatus.ERROR, message=message, payload=payload)
781
+
782
+
742
783
  def call(
743
784
  endpoint: Annotated[
744
785
  str,
@@ -965,78 +1006,19 @@ def call(
965
1006
  if not response.ok:
966
1007
  raise typer.Exit(code=1)
967
1008
 
968
- except EndpointNotFoundError as e:
969
- exit_with_result(
970
- CommandResult(
971
- status=CommandStatus.ERROR,
972
- message=f"Endpoint not found: {e.endpoint_ref}",
973
- payload={"searched_apis": e.searched_apis} if e.searched_apis else None,
974
- ),
975
- quiet=quiet,
976
- exit_code=1,
977
- cause=e,
978
- )
979
- except EndpointAmbiguousError as e:
980
- exit_with_result(
981
- CommandResult(
982
- status=CommandStatus.ERROR,
983
- message=f"Ambiguous endpoint: '{e.endpoint_name}' exists in multiple APIs",
984
- payload={"matching_apis": e.matching_apis},
985
- ),
986
- quiet=quiet,
987
- exit_code=1,
988
- cause=e,
989
- )
990
- except ServerNotFoundError as e:
991
- exit_with_result(
992
- CommandResult(
993
- status=CommandStatus.ERROR,
994
- message=(
995
- f"Server profile not found: '{e.server_name}'. Available: {e.available or '(none configured)'}"
996
- ),
997
- payload={"available_servers": e.available} if e.available else None,
998
- ),
999
- quiet=quiet,
1000
- exit_code=1,
1001
- cause=e,
1002
- )
1003
- except CredentialError as e:
1004
- exit_with_result(
1005
- CommandResult(
1006
- status=CommandStatus.ERROR,
1007
- message=f"Credential error: {e}",
1008
- payload={"credential_name": e.credential_name} if e.credential_name else None,
1009
- ),
1010
- quiet=quiet,
1011
- exit_code=1,
1012
- cause=e,
1013
- )
1014
1009
  except AuthExpiredError as e:
1015
1010
  _handle_auth_expired_error(e, quiet=quiet)
1016
- except RequestError as e:
1017
- exit_with_result(
1018
- CommandResult(
1019
- status=CommandStatus.ERROR,
1020
- message=f"Request failed: {e}",
1021
- payload={
1022
- "status_code": e.status_code,
1023
- "retryable": e.retryable,
1024
- },
1025
- ),
1026
- quiet=quiet,
1027
- exit_code=1,
1028
- cause=e,
1029
- )
1030
- except ResponseTooLargeError as e:
1031
- exit_with_result(
1032
- CommandResult(
1033
- status=CommandStatus.ERROR,
1034
- message=f"Response too large: {e.response_size} bytes (max: {e.max_size})",
1035
- ),
1036
- quiet=quiet,
1037
- exit_code=1,
1038
- cause=e,
1039
- )
1011
+ except (
1012
+ EndpointNotFoundError,
1013
+ EndpointAmbiguousError,
1014
+ ServerNotFoundError,
1015
+ CredentialError,
1016
+ RequestError,
1017
+ ResponseTooLargeError,
1018
+ PathParameterError,
1019
+ httpx.InvalidURL,
1020
+ ) as e:
1021
+ exit_with_result(_build_call_error_result(e), quiet=quiet, exit_code=1, cause=e)
1040
1022
 
1041
1023
 
1042
1024
  __all__ = ["call"]
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Annotated
7
7
  import typer
8
8
  from rich.table import Table
9
9
 
10
- from kstlib.cli.common import console
10
+ from kstlib.cli.common import console, err_console
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from kstlib.rapi.config import ApiConfig, EndpointConfig
@@ -228,8 +228,8 @@ def list_endpoints(
228
228
  # Filter by API name if specified
229
229
  if api:
230
230
  if api not in apis:
231
- console.print(f"[red]API '{api}' not found.[/]")
232
- console.print(f"[dim]Available APIs: {', '.join(apis.keys())}[/]")
231
+ err_console.print(f"[red]API '{api}' not found.[/]")
232
+ err_console.print(f"[dim]Available APIs: {', '.join(apis.keys())}[/]")
233
233
  raise typer.Exit(code=1)
234
234
  apis = {api: apis[api]}
235
235
 
@@ -11,7 +11,7 @@ from rich.markup import escape
11
11
  from rich.panel import Panel
12
12
  from rich.table import Table
13
13
 
14
- from kstlib.cli.common import console
14
+ from kstlib.cli.common import console, err_console
15
15
  from kstlib.limits import HARD_MAX_DISPLAY_VALUE_LENGTH, HARD_MAX_ENDPOINT_REF_LENGTH
16
16
  from kstlib.rapi import EndpointNotFoundError
17
17
  from kstlib.rapi.config import _PATH_PARAM_PATTERN
@@ -33,12 +33,12 @@ def _truncate(value: str, max_length: int = HARD_MAX_DISPLAY_VALUE_LENGTH) -> st
33
33
  def _validate_endpoint_ref(endpoint_ref: str) -> None:
34
34
  """Validate endpoint reference for security (deep defense)."""
35
35
  if len(endpoint_ref) > HARD_MAX_ENDPOINT_REF_LENGTH:
36
- console.print(f"[red]Endpoint reference too long: {len(endpoint_ref)} > {HARD_MAX_ENDPOINT_REF_LENGTH}[/]")
36
+ err_console.print(f"[red]Endpoint reference too long: {len(endpoint_ref)} > {HARD_MAX_ENDPOINT_REF_LENGTH}[/]")
37
37
  raise typer.Exit(code=1)
38
38
 
39
39
  if not _ENDPOINT_REF_PATTERN.match(endpoint_ref):
40
- console.print("[red]Endpoint reference contains invalid characters.[/]")
41
- console.print("[dim]Allowed: alphanumeric, underscore, dot, hyphen[/]")
40
+ err_console.print("[red]Endpoint reference contains invalid characters.[/]")
41
+ err_console.print("[dim]Allowed: alphanumeric, underscore, dot, hyphen[/]")
42
42
  raise typer.Exit(code=1)
43
43
 
44
44
 
@@ -183,8 +183,8 @@ def show_endpoint(
183
183
  try:
184
184
  api_config, ep_config = config_manager.resolve(endpoint_ref)
185
185
  except EndpointNotFoundError as e:
186
- console.print(f"[red]Endpoint not found: {endpoint_ref}[/]")
187
- console.print(f"[dim]Available APIs: {', '.join(e.searched_apis)}[/]")
186
+ err_console.print(f"[red]Endpoint not found: {endpoint_ref}[/]")
187
+ err_console.print(f"[dim]Available APIs: {', '.join(e.searched_apis)}[/]")
188
188
  raise typer.Exit(code=1) from e
189
189
 
190
190
  path_params = _PATH_PARAM_PATTERN.findall(ep_config.path)
@@ -12,6 +12,7 @@ from rich.panel import Panel
12
12
  from rich.pretty import Pretty
13
13
 
14
14
  console = Console()
15
+ err_console = Console(stderr=True)
15
16
 
16
17
 
17
18
  class CommandStatus(str, Enum):
@@ -39,17 +40,23 @@ STYLE_MAP = {
39
40
 
40
41
 
41
42
  def render_result(result: CommandResult) -> None:
42
- """Render a command result using Rich components."""
43
+ """Render a command result using Rich components.
44
+
45
+ ERROR results render to stderr (keeping stdout clean for shell capture);
46
+ OK and WARNING results render to stdout.
47
+ """
43
48
  style = STYLE_MAP[result.status]
44
- console.print(Panel(result.message, title=result.status.value.upper(), style=style, border_style=style))
49
+ target = err_console if result.status is CommandStatus.ERROR else console
50
+ target.print(Panel(result.message, title=result.status.value.upper(), style=style, border_style=style))
45
51
  if result.payload is not None:
46
- console.print(Pretty(result.payload))
52
+ target.print(Pretty(result.payload))
47
53
 
48
54
 
49
55
  def emit_result(result: CommandResult, quiet: bool) -> None:
50
56
  """Output a command result honoring the quiet flag."""
51
57
  if quiet:
52
- console.print(result.message, style=STYLE_MAP[result.status])
58
+ target = err_console if result.status is CommandStatus.ERROR else console
59
+ target.print(result.message, style=STYLE_MAP[result.status])
53
60
  else:
54
61
  render_result(result)
55
62
 
@@ -80,6 +87,7 @@ __all__ = [
80
87
  "CommandStatus",
81
88
  "console",
82
89
  "emit_result",
90
+ "err_console",
83
91
  "exit_error",
84
92
  "exit_with_result",
85
93
  "render_result",
@@ -39,7 +39,7 @@ __logo__ = (
39
39
  )
40
40
 
41
41
  __app_name__ = "kstlib"
42
- __version__ = "3.3.0"
42
+ __version__ = "3.3.1"
43
43
  __description__ = (
44
44
  "Config-driven helpers for Python projects (dynamic config, secure secrets, preset logging, and more…)"
45
45
  )
@@ -98,6 +98,7 @@ from kstlib.rapi.exceptions import (
98
98
  EndpointNotFoundError,
99
99
  EnvVarError,
100
100
  MinifyRequiresRawError,
101
+ PathParameterError,
101
102
  RapiError,
102
103
  RequestError,
103
104
  ResponseTooLargeError,
@@ -119,6 +120,7 @@ __all__ = [
119
120
  "HmacConfig",
120
121
  "MinifyRequiresRawError",
121
122
  "MultipartConfig",
123
+ "PathParameterError",
122
124
  "RapiClient",
123
125
  "RapiConfigManager",
124
126
  "RapiError",
@@ -28,6 +28,7 @@ from kstlib.rapi.exceptions import (
28
28
  EndpointCollisionError,
29
29
  EndpointNotFoundError,
30
30
  EnvVarError,
31
+ PathParameterError,
31
32
  SafeguardMissingError,
32
33
  ServerNotFoundError,
33
34
  )
@@ -53,13 +54,16 @@ def _validate_path_param(name: str, value: str) -> None:
53
54
  value: Substituted value to check.
54
55
 
55
56
  Raises:
56
- ValueError: If the value contains path traversal or null bytes.
57
+ PathParameterError: If the value contains null bytes, control
58
+ characters, or path traversal segments.
57
59
 
58
60
  """
59
61
  if "\x00" in value:
60
- raise ValueError(f"Null bytes not allowed in path parameter '{name}'")
62
+ raise PathParameterError(f"Null bytes not allowed in path parameter '{name}'")
63
+ if any(ord(c) < 0x20 or ord(c) == 0x7F for c in value):
64
+ raise PathParameterError(f"Control characters not allowed in path parameter '{name}'")
61
65
  if any(seg in value for seg in _DANGEROUS_PATH_SEGMENTS):
62
- raise ValueError(f"Path traversal not allowed in parameter '{name}': {value!r}")
66
+ raise PathParameterError(f"Path traversal not allowed in parameter '{name}': {value!r}")
63
67
 
64
68
 
65
69
  # Deep defense: allowed values for HMAC config (hardcoded limits)
@@ -788,7 +792,7 @@ class EndpointConfig:
788
792
  Formatted path string.
789
793
 
790
794
  Raises:
791
- ValueError: If required parameters are missing.
795
+ PathParameterError: If required parameters are missing or invalid.
792
796
 
793
797
  Examples:
794
798
  >>> config = EndpointConfig(
@@ -816,7 +820,7 @@ class EndpointConfig:
816
820
  _validate_path_param(placeholder, value)
817
821
  path = path.replace(f"{{{placeholder}}}", value)
818
822
  else:
819
- raise ValueError(f"Missing positional argument {idx} for path {self.path}")
823
+ raise PathParameterError(f"Missing positional argument {idx} for path {self.path}")
820
824
  elif placeholder in kwargs:
821
825
  # Named: {name}
822
826
  value = str(kwargs[placeholder])
@@ -829,7 +833,7 @@ class EndpointConfig:
829
833
  path = path.replace(f"{{{placeholder}}}", value)
830
834
  args = args[1:]
831
835
  else:
832
- raise ValueError(f"Missing parameter '{placeholder}' for path {self.path}")
836
+ raise PathParameterError(f"Missing parameter '{placeholder}' for path {self.path}")
833
837
 
834
838
  return path
835
839
 
@@ -462,6 +462,32 @@ class MinifyRequiresRawError(RapiError):
462
462
  super().__init__(message or self.DEFAULT_MESSAGE)
463
463
 
464
464
 
465
+ class PathParameterError(RapiError, ValueError):
466
+ """Raised when a path parameter is missing or contains an invalid value.
467
+
468
+ Covers both failure modes of :meth:`EndpointConfig.build_path`: a required
469
+ ``{name}`` / ``{0}`` placeholder has no supplied value, or a supplied value
470
+ is rejected by validation (null bytes, control characters, path traversal).
471
+
472
+ Subclasses :class:`ValueError` as well as :class:`RapiError` so callers that
473
+ catch ``ValueError`` keep working while the CLI routes it through the typed
474
+ RAPI error chain for a clean message instead of a raw traceback.
475
+
476
+ Attributes:
477
+ message: Human-readable error message.
478
+ details: Additional context (e.g. the offending parameter name).
479
+
480
+ Examples:
481
+ >>> raise PathParameterError("invalid path parameter")
482
+ Traceback (most recent call last):
483
+ ...
484
+ kstlib.rapi.exceptions.PathParameterError: invalid path parameter
485
+ >>> isinstance(PathParameterError("x"), ValueError)
486
+ True
487
+
488
+ """
489
+
490
+
465
491
  __all__ = [
466
492
  "ConfirmationRequiredError",
467
493
  "CredentialError",
@@ -470,6 +496,7 @@ __all__ = [
470
496
  "EndpointNotFoundError",
471
497
  "EnvVarError",
472
498
  "MinifyRequiresRawError",
499
+ "PathParameterError",
473
500
  "RapiError",
474
501
  "RequestError",
475
502
  "ResponseTooLargeError",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kstlib
3
- Version: 3.3.0
3
+ Version: 3.3.1
4
4
  Summary: Config-driven helpers for Python projects (dynamic config, secure secrets, preset logging, and more…)
5
5
  Author-email: Michel TRUONG <michel.truong@gmail.com>
6
6
  Maintainer-email: Michel TRUONG <michel.truong@gmail.com>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes