kstlib 2.7.0__tar.gz → 3.0.0__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 (196) hide show
  1. {kstlib-2.7.0/src/kstlib.egg-info → kstlib-3.0.0}/PKG-INFO +4 -3
  2. {kstlib-2.7.0 → kstlib-3.0.0}/pyproject.toml +4 -2
  3. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/__init__.py +2 -0
  4. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/errors.py +81 -0
  5. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/rapi/call.py +98 -3
  6. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/export.py +2 -1
  7. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/kstlib.conf.yml +2 -0
  8. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/meta.py +1 -1
  9. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/__init__.py +2 -0
  10. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/client.py +366 -4
  11. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/exceptions.py +42 -0
  12. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/exceptions.py +5 -1
  13. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/websocket/manager.py +70 -3
  14. {kstlib-2.7.0 → kstlib-3.0.0/src/kstlib.egg-info}/PKG-INFO +4 -3
  15. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib.egg-info/requires.txt +3 -2
  16. {kstlib-2.7.0 → kstlib-3.0.0}/LICENSE.md +0 -0
  17. {kstlib-2.7.0 → kstlib-3.0.0}/MANIFEST.in +0 -0
  18. {kstlib-2.7.0 → kstlib-3.0.0}/README.md +0 -0
  19. {kstlib-2.7.0 → kstlib-3.0.0}/setup.cfg +0 -0
  20. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/__init__.py +0 -0
  21. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/__main__.py +0 -0
  22. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/_shared/__init__.py +0 -0
  23. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/_shared/jinja.py +0 -0
  24. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/_shared/redaction.py +0 -0
  25. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/__init__.py +0 -0
  26. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/channels/__init__.py +0 -0
  27. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/channels/base.py +0 -0
  28. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/channels/email.py +0 -0
  29. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/channels/slack.py +0 -0
  30. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/exceptions.py +0 -0
  31. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/manager.py +0 -0
  32. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/models.py +0 -0
  33. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/throttle.py +0 -0
  34. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/callback.py +0 -0
  35. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/check.py +0 -0
  36. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/config.py +0 -0
  37. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/models.py +0 -0
  38. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/providers/__init__.py +0 -0
  39. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/providers/base.py +0 -0
  40. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/providers/oauth2.py +0 -0
  41. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/providers/oidc.py +0 -0
  42. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/session.py +0 -0
  43. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/token.py +0 -0
  44. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cache/__init__.py +0 -0
  45. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cache/decorator.py +0 -0
  46. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cache/strategies.py +0 -0
  47. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/__init__.py +0 -0
  48. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/app.py +0 -0
  49. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/__init__.py +0 -0
  50. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/__init__.py +0 -0
  51. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/check.py +0 -0
  52. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/common.py +0 -0
  53. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/login.py +0 -0
  54. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/logout.py +0 -0
  55. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/providers.py +0 -0
  56. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/status.py +0 -0
  57. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/token.py +0 -0
  58. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/whoami.py +0 -0
  59. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/config.py +0 -0
  60. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/__init__.py +0 -0
  61. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/attach.py +0 -0
  62. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/common.py +0 -0
  63. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/list_sessions.py +0 -0
  64. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/logs.py +0 -0
  65. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/start.py +0 -0
  66. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/status.py +0 -0
  67. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/stop.py +0 -0
  68. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/rapi/__init__.py +0 -0
  69. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/rapi/list.py +0 -0
  70. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/rapi/show.py +0 -0
  71. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/__init__.py +0 -0
  72. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/common.py +0 -0
  73. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/decrypt.py +0 -0
  74. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/doctor.py +0 -0
  75. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/encrypt.py +0 -0
  76. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/shred.py +0 -0
  77. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/common.py +0 -0
  78. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/__init__.py +0 -0
  79. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/exceptions.py +0 -0
  80. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/loader.py +0 -0
  81. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/sops.py +0 -0
  82. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/__init__.py +0 -0
  83. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/aiosqlcipher.py +0 -0
  84. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/cipher.py +0 -0
  85. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/database.py +0 -0
  86. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/exceptions.py +0 -0
  87. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/pool.py +0 -0
  88. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/helpers/__init__.py +0 -0
  89. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/helpers/exceptions.py +0 -0
  90. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/helpers/time_trigger.py +0 -0
  91. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/limits.py +0 -0
  92. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/logging/__init__.py +0 -0
  93. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/logging/manager.py +0 -0
  94. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/__init__.py +0 -0
  95. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/_helpers.py +0 -0
  96. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/builder.py +0 -0
  97. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/collector.py +0 -0
  98. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/exceptions.py +0 -0
  99. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/filesystem.py +0 -0
  100. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/throttle.py +0 -0
  101. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transport.py +0 -0
  102. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/__init__.py +0 -0
  103. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/gmail.py +0 -0
  104. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/resend.py +0 -0
  105. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/ses.py +0 -0
  106. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/smtp.py +0 -0
  107. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/metrics/__init__.py +0 -0
  108. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/metrics/decorators.py +0 -0
  109. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/metrics/exceptions.py +0 -0
  110. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/__init__.py +0 -0
  111. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/_styles.py +0 -0
  112. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/cell.py +0 -0
  113. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/config.py +0 -0
  114. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/delivery.py +0 -0
  115. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/exceptions.py +0 -0
  116. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/image.py +0 -0
  117. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/kv.py +0 -0
  118. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/list.py +0 -0
  119. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/metric.py +0 -0
  120. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/monitoring.py +0 -0
  121. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/renderer.py +0 -0
  122. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/service.py +0 -0
  123. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/table.py +0 -0
  124. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/types.py +0 -0
  125. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/__init__.py +0 -0
  126. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/base.py +0 -0
  127. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/container.py +0 -0
  128. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/exceptions.py +0 -0
  129. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/manager.py +0 -0
  130. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/models.py +0 -0
  131. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/tmux.py +0 -0
  132. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/validators.py +0 -0
  133. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/__init__.py +0 -0
  134. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/base.py +0 -0
  135. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/exceptions.py +0 -0
  136. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/models.py +0 -0
  137. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/runner.py +0 -0
  138. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/__init__.py +0 -0
  139. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/_base.py +0 -0
  140. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/_helpers.py +0 -0
  141. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/callable.py +0 -0
  142. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/python.py +0 -0
  143. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/shell.py +0 -0
  144. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/validators.py +0 -0
  145. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/py.typed +0 -0
  146. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/config.py +0 -0
  147. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/credentials.py +0 -0
  148. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/__init__.py +0 -0
  149. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/circuit_breaker.py +0 -0
  150. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/exceptions.py +0 -0
  151. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/heartbeat.py +0 -0
  152. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/rate_limiter.py +0 -0
  153. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/shutdown.py +0 -0
  154. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/watchdog.py +0 -0
  155. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/__init__.py +0 -0
  156. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/models.py +0 -0
  157. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/__init__.py +0 -0
  158. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/base.py +0 -0
  159. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/environment.py +0 -0
  160. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/keyring.py +0 -0
  161. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/kms.py +0 -0
  162. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/kwargs.py +0 -0
  163. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/sops.py +0 -0
  164. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/resolver.py +0 -0
  165. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/sensitive.py +0 -0
  166. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secure/__init__.py +0 -0
  167. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secure/fs.py +0 -0
  168. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secure/permissions.py +0 -0
  169. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ssl.py +0 -0
  170. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/__init__.py +0 -0
  171. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/chain.py +0 -0
  172. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/config.py +0 -0
  173. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/exceptions.py +0 -0
  174. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/primitives.py +0 -0
  175. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/validators.py +0 -0
  176. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/__init__.py +0 -0
  177. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/exceptions.py +0 -0
  178. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/panels.py +0 -0
  179. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/spinner.py +0 -0
  180. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/tables.py +0 -0
  181. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/__init__.py +0 -0
  182. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/dict.py +0 -0
  183. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/formatting.py +0 -0
  184. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/http_trace.py +0 -0
  185. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/lazy.py +0 -0
  186. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/secure_delete.py +0 -0
  187. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/serialization.py +0 -0
  188. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/text.py +0 -0
  189. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/validators.py +0 -0
  190. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/websocket/__init__.py +0 -0
  191. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/websocket/exceptions.py +0 -0
  192. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/websocket/models.py +0 -0
  193. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib.egg-info/SOURCES.txt +0 -0
  194. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib.egg-info/dependency_links.txt +0 -0
  195. {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib.egg-info/entry_points.txt +0 -0
  196. {kstlib-2.7.0 → kstlib-3.0.0}/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: 2.7.0
3
+ Version: 3.0.0
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>
@@ -42,11 +42,12 @@ Requires-Dist: websockets<16,>=15.0
42
42
  Requires-Dist: jinja2<4,>=3.1.5
43
43
  Requires-Dist: humanize<5,>=4.11
44
44
  Requires-Dist: httpx<1,>=0.28
45
- Requires-Dist: authlib<2,>=1.6.11
45
+ Requires-Dist: authlib<2,>=1.6.12
46
46
  Requires-Dist: pendulum<4,>=3.0
47
47
  Requires-Dist: cryptography>=46.0.7
48
+ Requires-Dist: idna>=3.15
48
49
  Requires-Dist: requests>=2.33.0
49
- Requires-Dist: urllib3>=2.6.3
50
+ Requires-Dist: urllib3>=2.7.0
50
51
  Provides-Extra: dev
51
52
  Requires-Dist: pytest<10,>=9.0.3; extra == "dev"
52
53
  Requires-Dist: pytest-cov<8,>=7.0; extra == "dev"
@@ -56,15 +56,16 @@ dependencies = [
56
56
 
57
57
  # --- HTTP Client & Auth ---
58
58
  "httpx>=0.28,<1", # Modern async HTTP client (OAuth2/OIDC flows)
59
- "authlib>=1.6.11,<2", # OAuth2/OIDC client + JWT signature verification (GHSA-jj8c-mmj3-mmgv: CSRF in cache-backed OAuth state)
59
+ "authlib>=1.6.12,<2", # OAuth2/OIDC client + JWT signature verification (>=1.6.12 open-redirect fix; GHSA-jj8c-mmj3-mmgv CSRF)
60
60
 
61
61
  # --- Time & Scheduling ---
62
62
  "pendulum>=3.0,<4", # Modern datetime library with timezone support
63
63
 
64
64
  # --- Security: transitive dep lower bounds (CVE) ---
65
65
  "cryptography>=46.0.7", # CVE-2026-26007 + CVE-2026-34073 + CVE-2026-39892
66
+ "idna>=3.15", # CVE-2026-45409 (DoS quadratic-time, bypass of CVE-2024-3651 mitigation; via httpx)
66
67
  "requests>=2.33.0", # CVE-2024-47081 + CVE-2026-25645 (via httpx)
67
- "urllib3>=2.6.3", # CVE-2025-50182/50181 + CVE-2025-66418/66471 + CVE-2026-21441 (via requests)
68
+ "urllib3>=2.7.0", # CVE-2025-50182/50181 + CVE-2025-66418/66471 + CVE-2026-21441 + CVE-2026-44431 + CVE-2026-44432 (via requests)
68
69
  ]
69
70
  classifiers = [
70
71
  "Development Status :: 5 - Production/Stable",
@@ -573,4 +574,5 @@ exclude_lines = [
573
574
  "if __name__ == .__main__.:",
574
575
  "if TYPE_CHECKING:",
575
576
  "@abstractmethod",
577
+ "^\\s*\\.\\.\\.\\s*$",
576
578
  ]
@@ -58,6 +58,7 @@ from kstlib.auth.config import (
58
58
  )
59
59
  from kstlib.auth.errors import (
60
60
  AuthError,
61
+ AuthExpiredError,
61
62
  AuthorizationError,
62
63
  CallbackServerError,
63
64
  ConfigurationError,
@@ -100,6 +101,7 @@ __all__ = [
100
101
  "AbstractTokenStorage",
101
102
  # Errors
102
103
  "AuthError",
104
+ "AuthExpiredError",
103
105
  # Models
104
106
  "AuthFlow",
105
107
  "AuthProviderConfig",
@@ -119,8 +119,89 @@ class PreflightError(AuthError):
119
119
  self.reason = reason
120
120
 
121
121
 
122
+ class AuthExpiredError(AuthError):
123
+ """Raised when an authenticated request returns HTTP 401 indicating token expiration.
124
+
125
+ Surfaced by ``kstlib.rapi.client`` (and any other consumer) when a
126
+ server response signals that the previously-valid access token has
127
+ expired or been invalidated during the session. The user must
128
+ re-authenticate via the appropriate channel (for example,
129
+ ``sas-admin auth login`` for Viya, or via a dedicated OAuth client
130
+ when configured in :mod:`kstlib.auth`).
131
+
132
+ Note:
133
+ Distinct from :class:`TokenExpiredError`. The two cover
134
+ different lifecycle points and originate from different
135
+ sub-systems :
136
+
137
+ - ``AuthExpiredError`` (this class, inherits from
138
+ :class:`AuthError`) is raised by ``kstlib.rapi.client`` when
139
+ the server returns HTTP 401 at runtime, signalling that a
140
+ token which was valid at send time has been expired or
141
+ invalidated by the identity provider during the session.
142
+
143
+ - :class:`TokenExpiredError` (inherits from :class:`TokenError`)
144
+ is raised by ``kstlib.auth`` when a loaded token is detected
145
+ as already expired before the request is sent (client-side
146
+ pre-flight check).
147
+
148
+ Attributes:
149
+ token_source: Optional label identifying where the token was
150
+ loaded from (for example, ``'~/.sas/credentials.json'``,
151
+ ``'env:KSTLIB_TOKEN'``, ``'sops:secrets/api.sops.json'``).
152
+ ``None`` when the source is unknown.
153
+ suggested_action: Optional human-readable hint guiding the
154
+ user toward a successful re-authentication (for example,
155
+ ``'Run: sas-admin auth login -u <user>'``). ``None`` when
156
+ no contextual hint is available.
157
+
158
+ Examples:
159
+ >>> err = AuthExpiredError(
160
+ ... "Access token expired (HTTP 401).",
161
+ ... token_source="~/.sas/credentials.json",
162
+ ... suggested_action="Run: sas-admin auth login -u <user>",
163
+ ... )
164
+ >>> err.token_source
165
+ '~/.sas/credentials.json'
166
+ >>> isinstance(err, AuthError)
167
+ True
168
+
169
+ """
170
+
171
+ def __init__(
172
+ self,
173
+ message: str,
174
+ *,
175
+ token_source: str | None = None,
176
+ suggested_action: str | None = None,
177
+ ) -> None:
178
+ """Initialize AuthExpiredError.
179
+
180
+ Args:
181
+ message: Human-readable description of the expiration
182
+ (typically including the HTTP status and a short
183
+ rationale, never the raw token or response body).
184
+ token_source: Optional label for where the token came from
185
+ (used by callers to surface a contextual hint without
186
+ exposing the secret material itself).
187
+ suggested_action: Optional hint pointing the user to the
188
+ right re-authentication procedure.
189
+
190
+ """
191
+ super().__init__(
192
+ message,
193
+ details={
194
+ "token_source": token_source,
195
+ "suggested_action": suggested_action,
196
+ },
197
+ )
198
+ self.token_source = token_source
199
+ self.suggested_action = suggested_action
200
+
201
+
122
202
  __all__ = [
123
203
  "AuthError",
204
+ "AuthExpiredError",
124
205
  "AuthorizationError",
125
206
  "CallbackServerError",
126
207
  "ConfigurationError",
@@ -7,6 +7,7 @@ from typing import Annotated, Any, cast
7
7
 
8
8
  import typer
9
9
 
10
+ from kstlib.auth import AuthExpiredError
10
11
  from kstlib.cli.common import CommandResult, CommandStatus, console, exit_error, exit_with_result
11
12
  from kstlib.limits import get_rapi_render_config
12
13
  from kstlib.rapi import (
@@ -119,6 +120,96 @@ def _parse_body(body: str | None) -> dict[str, Any] | list[Any] | None:
119
120
  exit_error(f"Invalid JSON body: {e}")
120
121
 
121
122
 
123
+ def _validate_format(fmt: str) -> None:
124
+ """Reject output formats outside the supported set with a friendly error.
125
+
126
+ Extracted from the ``call`` command body so the cyclomatic
127
+ complexity budget keeps room for the exception handlers that
128
+ cover the runtime error paths (auth expiration, HTTP errors,
129
+ safeguard checks, etc.).
130
+
131
+ Args:
132
+ fmt: Format value passed by the user via ``--format`` / ``-f``.
133
+
134
+ Raises:
135
+ typer.Exit: Exit code 1 if ``fmt`` is not one of ``json``,
136
+ ``text``, or ``full``.
137
+
138
+ """
139
+ if fmt not in ("json", "text", "full"):
140
+ exit_error(f"Invalid output format: '{fmt}'\nValid formats: json, text, full")
141
+
142
+
143
+ def _is_multipart_file_body(endpoint_config: Any, body: str | None) -> bool:
144
+ """Return True when the call targets a multipart endpoint with a ``@file`` body.
145
+
146
+ Extracted from the ``call`` command body to keep its cyclomatic
147
+ complexity in check. The compound check is pulled out so each
148
+ short-circuit (``and``) does not count as a separate branch
149
+ against the caller's complexity budget.
150
+ """
151
+ return endpoint_config.is_multipart and body is not None and body.startswith("@")
152
+
153
+
154
+ def _handle_auth_expired_error(error: AuthExpiredError, *, quiet: bool) -> None:
155
+ """Render an :class:`AuthExpiredError` and exit with code 4.
156
+
157
+ Distinct exit code lets shell scripts detect token expiration
158
+ specifically and trigger an automated re-login flow (decision
159
+ D5 in the v3.0.0 roadmap). The message body is built from the
160
+ exception attributes : ``Error: <message>``, optionally
161
+ ``Source: <token_source>``, optionally ``Hint: <suggested_action>``.
162
+
163
+ Args:
164
+ error: The caught :class:`AuthExpiredError`.
165
+ quiet: Whether the user passed ``--quiet`` / ``-q``.
166
+
167
+ Raises:
168
+ typer.Exit: Always exits with code 4.
169
+
170
+ """
171
+ message_lines = [f"Error: {error.message}"]
172
+ if error.token_source:
173
+ message_lines.append(f"Source: {error.token_source}")
174
+ if error.suggested_action:
175
+ message_lines.append(f"Hint: {error.suggested_action}")
176
+ exit_with_result(
177
+ CommandResult(
178
+ status=CommandStatus.ERROR,
179
+ message="\n".join(message_lines),
180
+ payload={
181
+ "token_source": error.token_source,
182
+ "suggested_action": error.suggested_action,
183
+ },
184
+ ),
185
+ quiet=quiet,
186
+ exit_code=4,
187
+ cause=error,
188
+ )
189
+
190
+
191
+ def _validate_output_flags(raw: bool, minify: bool) -> None:
192
+ """Reject ``--minify`` without ``--raw`` at command entry.
193
+
194
+ Rich console rendering reformats output regardless of compact JSON
195
+ flags, so ``--minify`` without ``--raw`` is silently ineffective.
196
+ Fail fast with a hint instead of swallowing the user intent.
197
+
198
+ Args:
199
+ raw: True if the user passed ``--raw``.
200
+ minify: True if the user passed ``--minify``.
201
+
202
+ Raises:
203
+ typer.BadParameter: If ``minify`` is True and ``raw`` is False.
204
+ The message contains the hint ``--raw --minify``.
205
+
206
+ """
207
+ if minify and not raw:
208
+ raise typer.BadParameter(
209
+ "--minify requires --raw (Rich rendering ignores compact JSON formatting). Hint: use --raw --minify.",
210
+ )
211
+
212
+
122
213
  def _serialize_json(data: Any, *, minify: bool = False, indent: int = 2) -> str:
123
214
  """Serialize data to JSON string.
124
215
 
@@ -331,8 +422,10 @@ def call(
331
422
  headers = _parse_headers(header or [])
332
423
 
333
424
  # Validate output format
334
- if fmt not in ("json", "text", "full"):
335
- exit_error(f"Invalid output format: '{fmt}'\nValid formats: json, text, full")
425
+ _validate_format(fmt)
426
+
427
+ # Reject --minify without --raw before any work is done.
428
+ _validate_output_flags(raw=raw, minify=minify)
336
429
 
337
430
  from kstlib.cli.commands.rapi import _load_config_or_exit
338
431
 
@@ -346,7 +439,7 @@ def call(
346
439
 
347
440
  # For multipart endpoints with @file body, pass raw string to client
348
441
  # (client reads file as binary instead of CLI parsing as JSON)
349
- if endpoint_config.is_multipart and body is not None and body.startswith("@"):
442
+ if _is_multipart_file_body(endpoint_config, body):
350
443
  parsed_body: Any = body
351
444
  else:
352
445
  parsed_body = _parse_body(body)
@@ -413,6 +506,8 @@ def call(
413
506
  exit_code=1,
414
507
  cause=e,
415
508
  )
509
+ except AuthExpiredError as e:
510
+ _handle_auth_expired_error(e, quiet=quiet)
416
511
  except RequestError as e:
417
512
  exit_with_result(
418
513
  CommandResult(
@@ -20,6 +20,7 @@ from typing import Any, Final, cast
20
20
 
21
21
  import yaml
22
22
 
23
+ from kstlib.config.exceptions import ConfigError
23
24
  from kstlib.config.loader import CONFIG_FILENAME
24
25
 
25
26
  try:
@@ -38,7 +39,7 @@ _SUPPORTED_EXTENSIONS: Final[dict[str, str]] = {
38
39
  _DEFAULT_FORMAT: Final[str] = "yaml"
39
40
 
40
41
 
41
- class ConfigExportError(RuntimeError):
42
+ class ConfigExportError(ConfigError, RuntimeError):
42
43
  """Raised when configuration export fails."""
43
44
 
44
45
 
@@ -228,6 +228,7 @@ logger:
228
228
  tracebacks_show_locals: true
229
229
  file:
230
230
  level: TRACE
231
+ format: "[%(asctime)s | %(levelname)-8s] ::: PID %(process)d / TID %(thread)d ::: [%(filename)s:%(lineno)d %(funcName)s] %(message)s"
231
232
  icons:
232
233
  show: true
233
234
 
@@ -239,6 +240,7 @@ logger:
239
240
  tracebacks_show_locals: true
240
241
  file:
241
242
  level: TRACE
243
+ format: "[%(asctime)s | %(levelname)-8s] ::: PID %(process)d / TID %(thread)d ::: [%(filename)s:%(lineno)d %(funcName)s] %(message)s"
242
244
  icons:
243
245
  show: true
244
246
 
@@ -39,7 +39,7 @@ __logo__ = (
39
39
  )
40
40
 
41
41
  __app_name__ = "kstlib"
42
- __version__ = "2.7.0"
42
+ __version__ = "3.0.0"
43
43
  __description__ = (
44
44
  "Config-driven helpers for Python projects (dynamic config, secure secrets, preset logging, and more…)"
45
45
  )
@@ -97,6 +97,7 @@ from kstlib.rapi.exceptions import (
97
97
  EndpointAmbiguousError,
98
98
  EndpointNotFoundError,
99
99
  EnvVarError,
100
+ MinifyRequiresRawError,
100
101
  RapiError,
101
102
  RequestError,
102
103
  ResponseTooLargeError,
@@ -116,6 +117,7 @@ __all__ = [
116
117
  "EnvVarError",
117
118
  "FilePayload",
118
119
  "HmacConfig",
120
+ "MinifyRequiresRawError",
119
121
  "MultipartConfig",
120
122
  "RapiClient",
121
123
  "RapiConfigManager",