xed-cci 0.0.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.
@@ -0,0 +1,27 @@
1
+ # Python build/cache
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ *.egg
7
+ build/
8
+ dist/
9
+ .eggs/
10
+
11
+ # Virtual environments
12
+ .venv/
13
+ venv/
14
+
15
+ # pytest
16
+ .pytest_cache/
17
+ .coverage
18
+ htmlcov/
19
+
20
+ # IDE / OS
21
+ .vscode/
22
+ .idea/
23
+ .DS_Store
24
+ *.swp
25
+
26
+ # uv
27
+ .uv/
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ Alle bemerkenswerten Änderungen an `xed-cci` werden hier dokumentiert.
4
+
5
+ Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/),
6
+ Versionierung folgt [Semantic Versioning](https://semver.org/lang/de/).
7
+
8
+ ## [0.0.1] — UNRELEASED
9
+
10
+ ### Hinzugefügt (CCI-SS1 — Skelett-Setup)
11
+
12
+ - **CLI-Skelett** mit Typer-app + `--version`/`-V`-Flag + `--help`-Übersicht
13
+ - **`cci inventory` als Stub-Verb** — Skelett-Form, eigentliche Implementation
14
+ folgt in SS5 (Composition + Rich + JSON-Output + `--section`-Filter)
15
+ - **pyproject.toml** mit hatchling-Backend + dynamic version aus
16
+ `_version.py` + Stack-Konsistenz zu ccc/cca (Typer + Rich + psutil + pytest)
17
+ - **Test-Skelett** mit 3 Smoke-Cases (--version, --help, inventory-Stub)
18
+
19
+ ### Architektur-Notizen
20
+
21
+ - Phase 1: Python-CLI Read-Only-Klasse, Drei-Tool-Suite-Symmetrie zu ccc + cca
22
+ - Phase 2 (DeltaChat-Bot) + Phase 3 (cBOX@ /Monitor) sind Vision/Mission,
23
+ NICHT in dieser Version umgesetzt — siehe `WHITEPAPER.md` für vollständige
24
+ Architektur-Entscheidungsfindung
25
+ - Symmetrisches Layout: `docs/firstboot.sh` (Pages-Source), kein `scripts/`-
26
+ Pfad-Mapping (Lehre aus CCC-Layout-Refactor 2026-05-08)
27
+ - Read-Only-Garantie strukturell via `safe_run.py` (kommt SS2)
28
+
29
+ [0.0.1]: https://github.com/XED-dev/CCI/releases/tag/v0.0.1
xed_cci-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 XED /Code ❤️ recode@ /DAO
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
xed_cci-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: xed-cci
3
+ Version: 0.0.1
4
+ Summary: cBOX@ /Container Inventur — Read-Only-Tool für lokale Box-Inventur (OS, CC-Suite, Stack, Datenbanken, Server-Apps). Drittes Mitglied der XED /CC-Suite neben ccc + cca. cBOX.at/YOU by XED.dev Tools via Collective Context (CC).
5
+ Project-URL: Homepage, https://cci.xed.dev
6
+ Project-URL: Repository, https://github.com/XED-dev/CCI
7
+ Project-URL: XED /Suite, https://xed.dev
8
+ Project-URL: Issues, https://github.com/XED-dev/CCI/issues
9
+ Project-URL: Changelog, https://github.com/XED-dev/CCI/blob/main/CHANGELOG.md
10
+ Author-email: Collective Context <code@collective-context.org>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: cbox,cci,cli,container,debian,inventory,kaizen,lxc,monitoring,proxmox,read-only,ubuntu
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: System :: Monitoring
25
+ Classifier: Topic :: System :: Systems Administration
26
+ Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: psutil>=5.9
29
+ Requires-Dist: rich>=13.7
30
+ Requires-Dist: typer>=0.12
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
33
+ Requires-Dist: pytest>=8.0; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # XED /CCI — cBOX@ /Container Inventur
37
+
38
+ > Read-Only-Tool für lokale cBOX-Inventur. Drittes Mitglied der XED /CC-Suite
39
+ > neben [ccc](../XED-CCC/) (Control) und [cca](../XED-CCA/) (App-Installation).
40
+
41
+ **100% Read-Only:** cci verändert NIEMALS den Box-Zustand. Strukturelle
42
+ Garantie via subprocess-Whitelist.
43
+
44
+ ## Was es macht (Phase 1)
45
+
46
+ `cci inventory` liefert eine vollständige Inventur der lokalen Box als
47
+ Rich-Tabelle (Mensch) oder JSON (AI-Agent).
48
+
49
+ **Sektionen:**
50
+ - **OS** — `/etc/os-release` + Kernel
51
+ - **CC-Suite** — installed `xed-ccc` / `xed-cca` / `xed-cci`-Versionen
52
+ - **Stack** — `python3` / `php` / `node` Versionen (wenn installiert)
53
+ - **Databases** — `mysql` / `mariadb` / `postgres` Versionen + Service-Status
54
+ - **Apps** — Server-Apps mit Pfaden + Versionen (v0.0.1: Typo3-Detection)
55
+
56
+ ## Installation
57
+
58
+ **Empfohlen** — auf einer cBOX (LXC) via firstboot.sh:
59
+
60
+ ```bash
61
+ bash <(curl -s https://cci.xed.dev/firstboot.sh)
62
+ ```
63
+
64
+ Das Script installiert Python-Stack + pipx und holt `xed-cci` von PyPI.
65
+
66
+ **Manuell** — wenn pipx schon vorhanden:
67
+
68
+ ```bash
69
+ pipx install xed-cci
70
+ ```
71
+
72
+ ## Verwendung
73
+
74
+ ```bash
75
+ # Komplette Inventur als Rich-Tabelle
76
+ cci inventory
77
+
78
+ # JSON für AI-Agent-Konsumtion
79
+ cci inventory --format json
80
+
81
+ # Gefilterte Sektion
82
+ cci inventory --section os
83
+ cci inventory --section apps
84
+ cci inventory --section stack
85
+ cci inventory --section databases
86
+ cci inventory --section cc-suite
87
+
88
+ # Verb-Übersicht
89
+ cci --help
90
+
91
+ # Tool-Version
92
+ cci --version
93
+ ```
94
+
95
+ ## Use-Case: AI-Agent-Konsultation
96
+
97
+ ```bash
98
+ cci inventory --format json > /tmp/box.json
99
+
100
+ # Dann an AI-Agent (Claude Code, ChatGPT, etc.):
101
+ # "Hier ist meine Box-Inventur. Passt diese Konfiguration für Typo3 v13?
102
+ # Welche Anpassungen sind nötig?"
103
+ ```
104
+
105
+ Der AI-Agent sieht in einem strukturierten JSON-Block alle relevanten
106
+ Box-Daten und kann fundierte Migrations-/Update-Empfehlungen geben.
107
+
108
+ ## Roadmap
109
+
110
+ - **Phase 1 (jetzt):** Python-CLI mit `cci inventory`-Verb (read-only,
111
+ pipx-Distribution, ccc/cca-Pattern-Symmetrie). Erste App-Detection für
112
+ Typo3.
113
+ - **Phase 2 (Vision):** DeltaChat-Bot-Daemon für dezentrale Box-Steuerung
114
+ ohne offene Ports + TLS-Cert-Management. Siehe
115
+ [WHITEPAPER.md §Phase 2](./WHITEPAPER.md#phase-2--deltachat-bot-daemon-visionmission-nicht-für-phase-1-umsetzen).
116
+ - **Phase 3 (Vision):** cBOX@ /Monitor Master-Programm für Inventur-
117
+ Aggregation über tausende cBOX@ /CUBE-Boxen. Siehe
118
+ [WHITEPAPER.md §Phase 3](./WHITEPAPER.md#phase-3--cbox-monitor-visionmission-nicht-für-phase-12-umsetzen).
119
+
120
+ Volle Architektur-Entscheidungsfindung + Vision/Mission siehe
121
+ [WHITEPAPER.md](./WHITEPAPER.md).
122
+
123
+ ## Lizenz
124
+
125
+ MIT
@@ -0,0 +1,90 @@
1
+ # XED /CCI — cBOX@ /Container Inventur
2
+
3
+ > Read-Only-Tool für lokale cBOX-Inventur. Drittes Mitglied der XED /CC-Suite
4
+ > neben [ccc](../XED-CCC/) (Control) und [cca](../XED-CCA/) (App-Installation).
5
+
6
+ **100% Read-Only:** cci verändert NIEMALS den Box-Zustand. Strukturelle
7
+ Garantie via subprocess-Whitelist.
8
+
9
+ ## Was es macht (Phase 1)
10
+
11
+ `cci inventory` liefert eine vollständige Inventur der lokalen Box als
12
+ Rich-Tabelle (Mensch) oder JSON (AI-Agent).
13
+
14
+ **Sektionen:**
15
+ - **OS** — `/etc/os-release` + Kernel
16
+ - **CC-Suite** — installed `xed-ccc` / `xed-cca` / `xed-cci`-Versionen
17
+ - **Stack** — `python3` / `php` / `node` Versionen (wenn installiert)
18
+ - **Databases** — `mysql` / `mariadb` / `postgres` Versionen + Service-Status
19
+ - **Apps** — Server-Apps mit Pfaden + Versionen (v0.0.1: Typo3-Detection)
20
+
21
+ ## Installation
22
+
23
+ **Empfohlen** — auf einer cBOX (LXC) via firstboot.sh:
24
+
25
+ ```bash
26
+ bash <(curl -s https://cci.xed.dev/firstboot.sh)
27
+ ```
28
+
29
+ Das Script installiert Python-Stack + pipx und holt `xed-cci` von PyPI.
30
+
31
+ **Manuell** — wenn pipx schon vorhanden:
32
+
33
+ ```bash
34
+ pipx install xed-cci
35
+ ```
36
+
37
+ ## Verwendung
38
+
39
+ ```bash
40
+ # Komplette Inventur als Rich-Tabelle
41
+ cci inventory
42
+
43
+ # JSON für AI-Agent-Konsumtion
44
+ cci inventory --format json
45
+
46
+ # Gefilterte Sektion
47
+ cci inventory --section os
48
+ cci inventory --section apps
49
+ cci inventory --section stack
50
+ cci inventory --section databases
51
+ cci inventory --section cc-suite
52
+
53
+ # Verb-Übersicht
54
+ cci --help
55
+
56
+ # Tool-Version
57
+ cci --version
58
+ ```
59
+
60
+ ## Use-Case: AI-Agent-Konsultation
61
+
62
+ ```bash
63
+ cci inventory --format json > /tmp/box.json
64
+
65
+ # Dann an AI-Agent (Claude Code, ChatGPT, etc.):
66
+ # "Hier ist meine Box-Inventur. Passt diese Konfiguration für Typo3 v13?
67
+ # Welche Anpassungen sind nötig?"
68
+ ```
69
+
70
+ Der AI-Agent sieht in einem strukturierten JSON-Block alle relevanten
71
+ Box-Daten und kann fundierte Migrations-/Update-Empfehlungen geben.
72
+
73
+ ## Roadmap
74
+
75
+ - **Phase 1 (jetzt):** Python-CLI mit `cci inventory`-Verb (read-only,
76
+ pipx-Distribution, ccc/cca-Pattern-Symmetrie). Erste App-Detection für
77
+ Typo3.
78
+ - **Phase 2 (Vision):** DeltaChat-Bot-Daemon für dezentrale Box-Steuerung
79
+ ohne offene Ports + TLS-Cert-Management. Siehe
80
+ [WHITEPAPER.md §Phase 2](./WHITEPAPER.md#phase-2--deltachat-bot-daemon-visionmission-nicht-für-phase-1-umsetzen).
81
+ - **Phase 3 (Vision):** cBOX@ /Monitor Master-Programm für Inventur-
82
+ Aggregation über tausende cBOX@ /CUBE-Boxen. Siehe
83
+ [WHITEPAPER.md §Phase 3](./WHITEPAPER.md#phase-3--cbox-monitor-visionmission-nicht-für-phase-12-umsetzen).
84
+
85
+ Volle Architektur-Entscheidungsfindung + Vision/Mission siehe
86
+ [WHITEPAPER.md](./WHITEPAPER.md).
87
+
88
+ ## Lizenz
89
+
90
+ MIT
@@ -0,0 +1,80 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "xed-cci"
7
+ dynamic = ["version"]
8
+ description = "cBOX@ /Container Inventur — Read-Only-Tool für lokale Box-Inventur (OS, CC-Suite, Stack, Datenbanken, Server-Apps). Drittes Mitglied der XED /CC-Suite neben ccc + cca. cBOX.at/YOU by XED.dev Tools via Collective Context (CC)."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Collective Context", email = "code@collective-context.org" },
14
+ ]
15
+ keywords = [
16
+ "lxc",
17
+ "proxmox",
18
+ "container",
19
+ "inventory",
20
+ "read-only",
21
+ "cli",
22
+ "cci",
23
+ "cbox",
24
+ "debian",
25
+ "ubuntu",
26
+ "monitoring",
27
+ "kaizen",
28
+ ]
29
+ classifiers = [
30
+ "Development Status :: 3 - Alpha",
31
+ "Environment :: Console",
32
+ "Intended Audience :: System Administrators",
33
+ "License :: OSI Approved :: MIT License",
34
+ "Operating System :: POSIX :: Linux",
35
+ "Programming Language :: Python :: 3",
36
+ "Programming Language :: Python :: 3.10",
37
+ "Programming Language :: Python :: 3.11",
38
+ "Programming Language :: Python :: 3.12",
39
+ "Programming Language :: Python :: 3.13",
40
+ "Topic :: System :: Monitoring",
41
+ "Topic :: System :: Systems Administration",
42
+ "Topic :: Utilities",
43
+ ]
44
+ dependencies = [
45
+ "typer>=0.12",
46
+ "rich>=13.7",
47
+ "psutil>=5.9",
48
+ ]
49
+
50
+ [project.optional-dependencies]
51
+ dev = [
52
+ "pytest>=8.0",
53
+ "pytest-cov>=5.0",
54
+ ]
55
+
56
+ [project.scripts]
57
+ cci = "cci.cli:main"
58
+
59
+ [project.urls]
60
+ Homepage = "https://cci.xed.dev"
61
+ Repository = "https://github.com/XED-dev/CCI"
62
+ "XED /Suite" = "https://xed.dev"
63
+ Issues = "https://github.com/XED-dev/CCI/issues"
64
+ Changelog = "https://github.com/XED-dev/CCI/blob/main/CHANGELOG.md"
65
+
66
+ [tool.hatch.version]
67
+ path = "src/cci/_version.py"
68
+ pattern = '__version__ = "(?P<version>[^"]+)"'
69
+
70
+ [tool.hatch.build.targets.wheel]
71
+ packages = ["src/cci"]
72
+
73
+ [tool.hatch.build.targets.sdist]
74
+ include = [
75
+ "src/cci",
76
+ "README.md",
77
+ "LICENSE",
78
+ "CHANGELOG.md",
79
+ "pyproject.toml",
80
+ ]
@@ -0,0 +1,5 @@
1
+ """xed-cci — cBOX@ /Container Inventur (Read-Only)."""
2
+
3
+ from cci._version import __version__
4
+
5
+ __all__ = ["__version__"]
@@ -0,0 +1,2 @@
1
+ """Version of xed-cci — single source of truth for hatchling-build."""
2
+ __version__ = "0.0.1"
@@ -0,0 +1,73 @@
1
+ """xed-cci CLI — Typer-basiertes Read-Only-Inventur-Tool.
2
+
3
+ Designprinzipien (siehe WHITEPAPER §Vision + §Mission Statement):
4
+ - 100% Read-Only: cci verändert NIEMALS den Box-Zustand
5
+ - Stack-Konsistenz mit ccc + cca (Typer + Rich + pytest + pipx)
6
+ - AI-Agent-Konsumtion via JSON-Output
7
+
8
+ Phase-1-Verben (v0.0.1+):
9
+ - inventory: komplette Box-Inventur (Rich-Tabelle oder JSON)
10
+
11
+ Phase 2 (DeltaChat-Bot) + Phase 3 (cBOX@ /Monitor) sind Vision/Mission,
12
+ NICHT in Phase 1 umgesetzt.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import sys
18
+ from typing import Optional
19
+
20
+ import typer
21
+ from rich.console import Console
22
+
23
+ from cci._version import __version__
24
+ from cci.commands.inventory import inventory_command
25
+
26
+ app = typer.Typer(
27
+ name="cci",
28
+ help="cBOX@ /Container Inventur — Read-Only-Tool für lokale Box-Inventur.\n\n"
29
+ "Liefert OS, CC-Suite-Versionen, Stacks, Datenbanken und Server-Apps\n"
30
+ "als Rich-Tabelle (Mensch) oder JSON (AI-Agent-Konsumtion).\n\n"
31
+ "100% Read-Only: cci verändert NIEMALS den Box-Zustand.\n\n"
32
+ "cBOX.at/YOU by XED.dev Tools via Collective Context (CC).",
33
+ no_args_is_help=True,
34
+ add_completion=False,
35
+ )
36
+ console = Console()
37
+
38
+
39
+ def version_callback(value: bool) -> None:
40
+ if value:
41
+ console.print(f"xed-cci [bold]v{__version__}[/bold]")
42
+ raise typer.Exit()
43
+
44
+
45
+ @app.callback()
46
+ def main_callback(
47
+ version: Optional[bool] = typer.Option(
48
+ None,
49
+ "--version",
50
+ "-V",
51
+ help="Zeige Version und beende.",
52
+ callback=version_callback,
53
+ is_eager=True,
54
+ ),
55
+ ) -> None:
56
+ """xed-cci Top-Level-Callback. Lädt globale Optionen wie --version."""
57
+
58
+
59
+ # inventory-Verb (SS5: Composition + Rich + JSON-Output + --section-Filter)
60
+ app.command("inventory")(inventory_command)
61
+
62
+
63
+ def main() -> None:
64
+ """Entry point für das `cci`-Script (siehe pyproject.toml [project.scripts])."""
65
+ try:
66
+ app()
67
+ except KeyboardInterrupt:
68
+ console.print("\n[red]Abgebrochen.[/red]")
69
+ sys.exit(130)
70
+
71
+
72
+ if __name__ == "__main__":
73
+ main()
File without changes
@@ -0,0 +1,221 @@
1
+ """inventory — cci inventory-Verb (Composition + Rich + JSON + --section).
2
+
3
+ Composition über die fünf Inventur-Sektionen (os/cc-suite/stack/databases/
4
+ apps). Output entweder Rich-Tabelle (Mensch) oder JSON (AI-Agent-Konsumtion).
5
+
6
+ Senior-Pre-Hints H6-H11 (AI039 SS5 2026-05-08):
7
+ - H6: InventoryReport TypedDict matcht WHITEPAPER §JSON-Schema 1:1
8
+ - H7: Rich für Mensch / json.dumps für AI-Agent
9
+ - H8: --section-Filter mit enum-Choices (os/cc-suite/stack/databases/apps/all)
10
+ - H9: datetime.now(timezone.utc).isoformat — KEIN deprecated utcnow()
11
+ - H10: socket.gethostname() — KEIN subprocess hostname
12
+ - H11: _SCHEMA_VERSION als Konstante (Single-Source-of-Truth)
13
+
14
+ Stdlib-Reflex maximiert: keine subprocess-Aufrufe in dieser Datei
15
+ (Composition + Output-Rendering nur).
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import socket
22
+ from datetime import datetime, timezone
23
+ from enum import Enum
24
+ from typing import TypedDict
25
+
26
+ import typer
27
+ from rich.console import Console
28
+ from rich.table import Table
29
+
30
+ from cci.system.inventory.apps import AppInfo, collect_apps_info
31
+ from cci.system.inventory.cc_suite import CCSuiteInfo, collect_cc_suite_info
32
+ from cci.system.inventory.databases import DatabaseInfo, collect_databases_info
33
+ from cci.system.inventory.os import OSInfo, collect_os_info
34
+ from cci.system.inventory.stack import StackInfo, collect_stack_info
35
+
36
+ _SCHEMA_VERSION = "0.0.1"
37
+
38
+
39
+ class InventoryReport(TypedDict):
40
+ """Vollständige Inventur-Composition matching WHITEPAPER §JSON-Schema."""
41
+
42
+ schema_version: str
43
+ timestamp: str
44
+ host: str
45
+ os: OSInfo
46
+ cc_suite: CCSuiteInfo
47
+ stack: StackInfo
48
+ databases: list[DatabaseInfo]
49
+ apps: list[AppInfo]
50
+
51
+
52
+ class Section(str, Enum):
53
+ """Erlaubte --section-Werte (Typer-Choices)."""
54
+
55
+ ALL = "all"
56
+ OS = "os"
57
+ CC_SUITE = "cc-suite"
58
+ STACK = "stack"
59
+ DATABASES = "databases"
60
+ APPS = "apps"
61
+
62
+
63
+ def _utc_timestamp() -> str:
64
+ """ISO-UTC-Zeitstempel mit 'Z'-Suffix (kompatibel zu audit_log-Format)."""
65
+ return (
66
+ datetime.now(timezone.utc)
67
+ .isoformat(timespec="seconds")
68
+ .replace("+00:00", "Z")
69
+ )
70
+
71
+
72
+ def _build_report() -> InventoryReport:
73
+ """Baue komplette InventoryReport via Composition aller collect_X_info()."""
74
+ return InventoryReport(
75
+ schema_version=_SCHEMA_VERSION,
76
+ timestamp=_utc_timestamp(),
77
+ host=socket.gethostname(),
78
+ os=collect_os_info(),
79
+ cc_suite=collect_cc_suite_info(),
80
+ stack=collect_stack_info(),
81
+ databases=collect_databases_info(),
82
+ apps=collect_apps_info(),
83
+ )
84
+
85
+
86
+ def _filter_report(report: InventoryReport, section: Section) -> dict:
87
+ """Filtere Report auf gewählte Sektion (alle wenn Section.ALL).
88
+
89
+ Returns dict statt InventoryReport weil bei Section-Filter die
90
+ TypedDict-Struktur verändert wird (nur Subkey).
91
+ """
92
+ if section is Section.ALL:
93
+ return dict(report)
94
+
95
+ # Schema-Header bleibt immer drin für Kontext, plus die gewählte Sektion
96
+ base = {
97
+ "schema_version": report["schema_version"],
98
+ "timestamp": report["timestamp"],
99
+ "host": report["host"],
100
+ }
101
+ section_key = section.value.replace("-", "_") # 'cc-suite' -> 'cc_suite'
102
+ base[section_key] = report[section_key] # type: ignore[literal-required]
103
+ return base
104
+
105
+
106
+ def _render_rich(console: Console, report: InventoryReport, section: Section) -> None:
107
+ """Rich-Tabellen-Output für gewählte Sektion(en)."""
108
+ if section in (Section.ALL, Section.OS):
109
+ _render_os(console, report["os"])
110
+ if section in (Section.ALL, Section.CC_SUITE):
111
+ _render_cc_suite(console, report["cc_suite"])
112
+ if section in (Section.ALL, Section.STACK):
113
+ _render_stack(console, report["stack"])
114
+ if section in (Section.ALL, Section.DATABASES):
115
+ _render_databases(console, report["databases"])
116
+ if section in (Section.ALL, Section.APPS):
117
+ _render_apps(console, report["apps"])
118
+
119
+ if section is Section.ALL:
120
+ console.print(
121
+ f"[dim]Schema {report['schema_version']} · "
122
+ f"{report['timestamp']} · {report['host']}[/dim]"
123
+ )
124
+
125
+
126
+ def _render_os(console: Console, os_info: OSInfo) -> None:
127
+ table = Table(title="OS", show_header=True, header_style="bold cyan")
128
+ table.add_column("Field")
129
+ table.add_column("Value")
130
+ for key in ("id", "version_id", "pretty_name", "kernel"):
131
+ table.add_row(key, os_info[key]) # type: ignore[literal-required]
132
+ console.print(table)
133
+
134
+
135
+ def _render_cc_suite(console: Console, cc: CCSuiteInfo) -> None:
136
+ table = Table(title="CC-Suite", show_header=True, header_style="bold cyan")
137
+ table.add_column("Tool")
138
+ table.add_column("Version")
139
+ for tool in ("xed-ccc", "xed-cca", "xed-cci"):
140
+ table.add_row(tool, cc[tool]) # type: ignore[literal-required]
141
+ console.print(table)
142
+
143
+
144
+ def _render_stack(console: Console, stack: StackInfo) -> None:
145
+ table = Table(title="Stack", show_header=True, header_style="bold cyan")
146
+ table.add_column("Language")
147
+ table.add_column("Version")
148
+ table.add_row("python3", stack["python3"])
149
+ table.add_row("php", stack["php"] or "[dim]not installed[/dim]")
150
+ table.add_row("node", stack["node"] or "[dim]not installed[/dim]")
151
+ console.print(table)
152
+
153
+
154
+ def _render_databases(console: Console, dbs: list[DatabaseInfo]) -> None:
155
+ table = Table(title="Databases", show_header=True, header_style="bold cyan")
156
+ table.add_column("Engine")
157
+ table.add_column("Version")
158
+ table.add_column("Service Active", justify="center")
159
+ if not dbs:
160
+ table.add_row("[dim](none)[/dim]", "", "")
161
+ for db in dbs:
162
+ active = "[green]✓[/green]" if db["service_active"] else "[red]✗[/red]"
163
+ table.add_row(db["engine"], db["version"], active)
164
+ console.print(table)
165
+
166
+
167
+ def _render_apps(console: Console, apps: list[AppInfo]) -> None:
168
+ table = Table(title="Server-Apps", show_header=True, header_style="bold cyan")
169
+ table.add_column("Name")
170
+ table.add_column("Version")
171
+ table.add_column("Path")
172
+ if not apps:
173
+ table.add_row("[dim](none)[/dim]", "", "")
174
+ for app in apps:
175
+ table.add_row(app["name"], app["version"], app["path"])
176
+ console.print(table)
177
+
178
+
179
+ def inventory_command(
180
+ section: Section = typer.Option(
181
+ Section.ALL,
182
+ "--section",
183
+ case_sensitive=False,
184
+ help="Welche Sektion ausgeben (Default: alle).",
185
+ ),
186
+ output_format: str = typer.Option(
187
+ "rich",
188
+ "--format",
189
+ case_sensitive=False,
190
+ help="Output-Format: 'rich' (Tabellen für Mensch) oder 'json' (AI-Agent).",
191
+ ),
192
+ ) -> None:
193
+ """Box-Inventur als Rich-Tabelle oder JSON.
194
+
195
+ Sektionen: os, cc-suite, stack, databases, apps, all (Default).
196
+
197
+ Beispiele:
198
+ cci inventory
199
+ cci inventory --section os
200
+ cci inventory --format json > /tmp/box.json
201
+ """
202
+ report = _build_report()
203
+
204
+ fmt = output_format.lower()
205
+ if fmt == "json":
206
+ # JSON-Output via stdout, Rich-Console-Output deaktiviert um
207
+ # Format-Kontamination zu vermeiden (Senior-Pre-Hint H7/H8 STOPP-
208
+ # Kriterium: bei --format json darf KEIN Rich-Output dazwischen
209
+ # rutschen).
210
+ filtered = _filter_report(report, section)
211
+ print(json.dumps(filtered, indent=2))
212
+ return
213
+
214
+ if fmt == "rich":
215
+ console = Console()
216
+ _render_rich(console, report, section)
217
+ return
218
+
219
+ raise typer.BadParameter(
220
+ f"Unbekanntes Format: {output_format!r}. Erlaubt: 'rich' oder 'json'."
221
+ )
File without changes