redeploy 0.2.31__tar.gz → 0.2.32__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 (142) hide show
  1. {redeploy-0.2.31 → redeploy-0.2.32}/PKG-INFO +6 -6
  2. {redeploy-0.2.31 → redeploy-0.2.32}/README.md +5 -5
  3. {redeploy-0.2.31 → redeploy-0.2.32}/pyproject.toml +1 -1
  4. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/__init__.py +1 -1
  5. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/models.py +86 -0
  6. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy.egg-info/PKG-INFO +6 -6
  7. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/apply/__init__.py +0 -0
  8. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/apply/exceptions.py +0 -0
  9. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/apply/executor.py +0 -0
  10. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/apply/handlers.py +0 -0
  11. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/apply/progress.py +0 -0
  12. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/apply/rollback.py +0 -0
  13. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/apply/state.py +0 -0
  14. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/audit.py +0 -0
  15. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/__init__.py +0 -0
  16. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/__init__.py +0 -0
  17. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/audit.py +0 -0
  18. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/detect.py +0 -0
  19. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/devices.py +0 -0
  20. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/diagnose.py +0 -0
  21. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/diff.py +0 -0
  22. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/exec_.py +0 -0
  23. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/export.py +0 -0
  24. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/import_.py +0 -0
  25. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/init.py +0 -0
  26. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/inspect.py +0 -0
  27. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/patterns.py +0 -0
  28. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/plan_apply.py +0 -0
  29. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/plugin.py +0 -0
  30. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/probe.py +0 -0
  31. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/state.py +0 -0
  32. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/status.py +0 -0
  33. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/target.py +0 -0
  34. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/version.py +0 -0
  35. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/commands/workflow.py +0 -0
  36. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/core.py +0 -0
  37. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli/display.py +0 -0
  38. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/cli.py +0 -0
  39. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/data_sync.py +0 -0
  40. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/detect/__init__.py +0 -0
  41. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/detect/detector.py +0 -0
  42. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/detect/probes.py +0 -0
  43. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/detect/remote.py +0 -0
  44. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/detect/templates.py +0 -0
  45. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/detect/workflow.py +0 -0
  46. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/discovery.py +0 -0
  47. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl/__init__.py +0 -0
  48. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl/loader.py +0 -0
  49. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl/parser.py +0 -0
  50. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl_python/__init__.py +0 -0
  51. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl_python/context.py +0 -0
  52. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl_python/decorators.py +0 -0
  53. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl_python/docker_steps.py +0 -0
  54. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl_python/exceptions.py +0 -0
  55. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl_python/runner.py +0 -0
  56. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/dsl_python/steps.py +0 -0
  57. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/fleet.py +0 -0
  58. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/iac/__init__.py +0 -0
  59. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/iac/base.py +0 -0
  60. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/iac/docker_compose.py +0 -0
  61. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/iac/parsers/__init__.py +0 -0
  62. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/iac/parsers/compose.py +0 -0
  63. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/iac/registry.py +0 -0
  64. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/markpact/__init__.py +0 -0
  65. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/markpact/compiler.py +0 -0
  66. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/markpact/models.py +0 -0
  67. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/markpact/parser.py +0 -0
  68. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/observe.py +0 -0
  69. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/parse.py +0 -0
  70. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/patterns.py +0 -0
  71. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plan/__init__.py +0 -0
  72. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plan/planner.py +0 -0
  73. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plugins/__init__.py +0 -0
  74. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plugins/builtin/__init__.py +0 -0
  75. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plugins/builtin/browser_reload.py +0 -0
  76. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plugins/builtin/hardware_diagnostic.py +0 -0
  77. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plugins/builtin/notify.py +0 -0
  78. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plugins/builtin/process_control.py +0 -0
  79. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/plugins/builtin/systemd_reload.py +0 -0
  80. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/spec_loader.py +0 -0
  81. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/ssh.py +0 -0
  82. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/steps.py +0 -0
  83. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/__init__.py +0 -0
  84. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_audit.py +0 -0
  85. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_cli.py +0 -0
  86. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_data_sync.py +0 -0
  87. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_discovery.py +0 -0
  88. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_dsl.py +0 -0
  89. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_examples.py +0 -0
  90. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_executor.py +0 -0
  91. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_fleet.py +0 -0
  92. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_git_integration.py +0 -0
  93. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_iac.py +0 -0
  94. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_manifest.py +0 -0
  95. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_markpact_compiler.py +0 -0
  96. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_markpact_parser.py +0 -0
  97. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_models.py +0 -0
  98. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_observe.py +0 -0
  99. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_parse.py +0 -0
  100. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_patterns.py +0 -0
  101. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_planner_edge.py +0 -0
  102. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_plugins.py +0 -0
  103. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_probes.py +0 -0
  104. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_public_api.py +0 -0
  105. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_resume.py +0 -0
  106. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_spec_loader.py +0 -0
  107. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_ssh.py +0 -0
  108. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_templates.py +0 -0
  109. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_verify.py +0 -0
  110. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_version.py +0 -0
  111. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_version_changelog.py +0 -0
  112. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_version_cli.py +0 -0
  113. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_version_manifest.py +0 -0
  114. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_version_sources.py +0 -0
  115. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/tests/test_workflow.py +0 -0
  116. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/verify.py +0 -0
  117. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/__init__.py +0 -0
  118. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/bump.py +0 -0
  119. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/changelog.py +0 -0
  120. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/commits.py +0 -0
  121. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/diff.py +0 -0
  122. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/git_integration.py +0 -0
  123. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/git_transaction.py +0 -0
  124. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/manifest.py +0 -0
  125. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/sources/__init__.py +0 -0
  126. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/sources/base.py +0 -0
  127. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/sources/json_.py +0 -0
  128. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/sources/plain.py +0 -0
  129. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/sources/regex.py +0 -0
  130. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/sources/toml_.py +0 -0
  131. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/sources/yaml_.py +0 -0
  132. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version/transaction.py +0 -0
  133. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy/version.py +0 -0
  134. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy.egg-info/SOURCES.txt +0 -0
  135. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy.egg-info/dependency_links.txt +0 -0
  136. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy.egg-info/entry_points.txt +0 -0
  137. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy.egg-info/requires.txt +0 -0
  138. {redeploy-0.2.31 → redeploy-0.2.32}/redeploy.egg-info/top_level.txt +0 -0
  139. {redeploy-0.2.31 → redeploy-0.2.32}/setup.cfg +0 -0
  140. {redeploy-0.2.31 → redeploy-0.2.32}/tests/test_iac.py +0 -0
  141. {redeploy-0.2.31 → redeploy-0.2.32}/tests/test_parse.py +0 -0
  142. {redeploy-0.2.31 → redeploy-0.2.32}/tests/test_redeploy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redeploy
3
- Version: 0.2.31
3
+ Version: 0.2.32
4
4
  Summary: Infrastructure migration toolkit: detect → plan → apply
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -27,17 +27,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
27
27
 
28
28
  ## AI Cost Tracking
29
29
 
30
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.31-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
31
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-16.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
30
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.32-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
31
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-17.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
32
32
 
33
- - 🤖 **LLM usage:** $7.5000 (51 commits)
34
- - 👤 **Human dev:** ~$1597 (16.0h @ $100/h, 30min dedup)
33
+ - 🤖 **LLM usage:** $7.5000 (52 commits)
34
+ - 👤 **Human dev:** ~$1755 (17.5h @ $100/h, 30min dedup)
35
35
 
36
36
  Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
37
37
 
38
38
  ---
39
39
 
40
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.31-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
40
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.32-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
41
41
 
42
42
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
43
43
 
@@ -3,17 +3,17 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.31-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-16.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.32-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-17.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $7.5000 (51 commits)
10
- - 👤 **Human dev:** ~$1597 (16.0h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $7.5000 (52 commits)
10
+ - 👤 **Human dev:** ~$1755 (17.5h @ $100/h, 30min dedup)
11
11
 
12
12
  Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
16
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.31-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
16
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.32-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
17
17
 
18
18
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
19
19
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "redeploy"
7
- version = "0.2.31"
7
+ version = "0.2.32"
8
8
  description = "Infrastructure migration toolkit: detect → plan → apply"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -7,7 +7,7 @@ Public API (stable from 0.2.0, semver guaranteed)::
7
7
 
8
8
  Everything not listed in ``__all__`` is internal and may change without notice.
9
9
  """
10
- __version__ = "0.2.31"
10
+ __version__ = "0.2.32"
11
11
 
12
12
  # ── Core models ───────────────────────────────────────────────────────────────
13
13
  from .models import ( # noqa: F401
@@ -120,6 +120,92 @@ class AppHealthInfo(BaseModel):
120
120
  response_ms: Optional[float] = None
121
121
 
122
122
 
123
+ # ── Hardware (output of hardware probe) ───────────────────────────────────────
124
+
125
+ class DrmOutput(BaseModel):
126
+ """One DRM connector (e.g. card1-DSI-2, card2-HDMI-A-1)."""
127
+ name: str # e.g. "card1-DSI-2"
128
+ connector: str # e.g. "DSI-2", "HDMI-A-1"
129
+ status: str = "unknown" # connected / disconnected
130
+ enabled: str = "unknown" # enabled / disabled
131
+ modes: list[str] = Field(default_factory=list) # e.g. ["1280x800@60"]
132
+ transform: str = "normal"
133
+ position: str = "0,0"
134
+ scale: str = "1.0"
135
+
136
+
137
+ class BacklightInfo(BaseModel):
138
+ """Sysfs backlight device."""
139
+ name: str # e.g. "11-0045"
140
+ brightness: int = 0
141
+ max_brightness: int = 255
142
+ bl_power: int = 0 # 0=on, 4=off
143
+ display_name: Optional[str] = None # e.g. "DSI-2"
144
+
145
+
146
+ class I2CBusInfo(BaseModel):
147
+ bus: int
148
+ devices: list[str] = Field(default_factory=list) # hex addresses found
149
+
150
+
151
+ class HardwareDiagnostic(BaseModel):
152
+ """Problem found during hardware probe."""
153
+ component: str # "dsi", "backlight", "i2c", "gpio", "overlay"
154
+ severity: str = "warning" # info / warning / error / critical
155
+ message: str
156
+ fix: Optional[str] = None # suggested fix command or action
157
+
158
+
159
+ class HardwareInfo(BaseModel):
160
+ """Hardware state produced by hardware probe."""
161
+ board: Optional[str] = None # e.g. "Raspberry Pi 5 Model B Rev 1.0"
162
+ kernel: Optional[str] = None # uname -r
163
+ config_txt: str = "" # full /boot/firmware/config.txt
164
+
165
+ # Display
166
+ drm_outputs: list[DrmOutput] = Field(default_factory=list)
167
+ backlights: list[BacklightInfo] = Field(default_factory=list)
168
+ framebuffers: list[str] = Field(default_factory=list) # /dev/fb* names
169
+ wlr_outputs: list[dict] = Field(default_factory=list) # raw wlr-randr output
170
+
171
+ # DSI specific
172
+ dsi_overlays: list[str] = Field(default_factory=list) # active dtoverlay lines
173
+ dsi_dmesg: list[str] = Field(default_factory=list) # relevant dmesg lines
174
+
175
+ # I2C
176
+ i2c_buses: list[I2CBusInfo] = Field(default_factory=list)
177
+
178
+ # GPIO header
179
+ gpio_4pin_detected: Optional[bool] = None # 5V/GND/SDA/SCL header present
180
+
181
+ # Diagnostics
182
+ diagnostics: list[HardwareDiagnostic] = Field(default_factory=list)
183
+
184
+ @property
185
+ def has_dsi(self) -> bool:
186
+ return any("DSI" in o.name for o in self.drm_outputs)
187
+
188
+ @property
189
+ def dsi_connected(self) -> bool:
190
+ return any("DSI" in o.name and o.status == "connected" for o in self.drm_outputs)
191
+
192
+ @property
193
+ def dsi_enabled(self) -> bool:
194
+ return any("DSI" in o.name and o.enabled == "enabled" for o in self.drm_outputs)
195
+
196
+ @property
197
+ def backlight_on(self) -> bool:
198
+ return any(b.bl_power == 0 and b.brightness > 0 for b in self.backlights)
199
+
200
+ @property
201
+ def errors(self) -> list[HardwareDiagnostic]:
202
+ return [d for d in self.diagnostics if d.severity in ("error", "critical")]
203
+
204
+ @property
205
+ def warnings(self) -> list[HardwareDiagnostic]:
206
+ return [d for d in self.diagnostics if d.severity == "warning"]
207
+
208
+
123
209
  class InfraState(BaseModel):
124
210
  """Full detected state of infrastructure — output of `detect`."""
125
211
  host: str # "root@1.2.3.4" or "local"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redeploy
3
- Version: 0.2.31
3
+ Version: 0.2.32
4
4
  Summary: Infrastructure migration toolkit: detect → plan → apply
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -27,17 +27,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
27
27
 
28
28
  ## AI Cost Tracking
29
29
 
30
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.31-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
31
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-16.0h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
30
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.32-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
31
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-17.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
32
32
 
33
- - 🤖 **LLM usage:** $7.5000 (51 commits)
34
- - 👤 **Human dev:** ~$1597 (16.0h @ $100/h, 30min dedup)
33
+ - 🤖 **LLM usage:** $7.5000 (52 commits)
34
+ - 👤 **Human dev:** ~$1755 (17.5h @ $100/h, 30min dedup)
35
35
 
36
36
  Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
37
37
 
38
38
  ---
39
39
 
40
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.31-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
40
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.32-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
41
41
 
42
42
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
43
43
 
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