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.
- {kstlib-2.7.0/src/kstlib.egg-info → kstlib-3.0.0}/PKG-INFO +4 -3
- {kstlib-2.7.0 → kstlib-3.0.0}/pyproject.toml +4 -2
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/__init__.py +2 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/errors.py +81 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/rapi/call.py +98 -3
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/export.py +2 -1
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/kstlib.conf.yml +2 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/meta.py +1 -1
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/__init__.py +2 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/client.py +366 -4
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/exceptions.py +42 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/exceptions.py +5 -1
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/websocket/manager.py +70 -3
- {kstlib-2.7.0 → kstlib-3.0.0/src/kstlib.egg-info}/PKG-INFO +4 -3
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib.egg-info/requires.txt +3 -2
- {kstlib-2.7.0 → kstlib-3.0.0}/LICENSE.md +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/MANIFEST.in +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/README.md +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/setup.cfg +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/__main__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/_shared/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/_shared/jinja.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/_shared/redaction.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/channels/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/channels/base.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/channels/email.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/channels/slack.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/manager.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/models.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/alerts/throttle.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/callback.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/check.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/config.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/models.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/providers/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/providers/base.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/providers/oauth2.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/providers/oidc.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/session.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/auth/token.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cache/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cache/decorator.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cache/strategies.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/app.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/check.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/common.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/login.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/logout.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/providers.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/status.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/token.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/auth/whoami.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/config.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/attach.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/common.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/list_sessions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/logs.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/start.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/status.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/ops/stop.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/rapi/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/rapi/list.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/rapi/show.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/common.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/decrypt.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/doctor.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/encrypt.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/commands/secrets/shred.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/cli/common.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/loader.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/config/sops.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/aiosqlcipher.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/cipher.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/database.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/db/pool.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/helpers/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/helpers/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/helpers/time_trigger.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/limits.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/logging/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/logging/manager.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/_helpers.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/builder.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/collector.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/filesystem.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/throttle.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transport.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/gmail.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/resend.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/ses.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/mail/transports/smtp.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/metrics/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/metrics/decorators.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/metrics/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/_styles.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/cell.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/config.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/delivery.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/image.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/kv.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/list.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/metric.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/monitoring.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/renderer.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/service.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/table.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/monitoring/types.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/base.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/container.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/manager.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/models.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/tmux.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ops/validators.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/base.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/models.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/runner.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/_base.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/_helpers.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/callable.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/python.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/steps/shell.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/pipeline/validators.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/py.typed +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/config.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/rapi/credentials.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/circuit_breaker.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/heartbeat.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/rate_limiter.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/shutdown.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/resilience/watchdog.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/models.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/base.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/environment.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/keyring.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/kms.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/kwargs.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/providers/sops.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/resolver.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secrets/sensitive.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secure/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secure/fs.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/secure/permissions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ssl.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/chain.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/config.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/primitives.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/transform/validators.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/panels.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/spinner.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/ui/tables.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/dict.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/formatting.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/http_trace.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/lazy.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/secure_delete.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/serialization.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/text.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/utils/validators.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/websocket/__init__.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/websocket/exceptions.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib/websocket/models.py +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib.egg-info/SOURCES.txt +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib.egg-info/dependency_links.txt +0 -0
- {kstlib-2.7.0 → kstlib-3.0.0}/src/kstlib.egg-info/entry_points.txt +0 -0
- {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:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
335
|
-
|
|
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
|
|
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
|
|
|
@@ -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",
|