pcpulse 0.1.0__py3-none-any.whl

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.
pcpulse/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """PCPulse public API."""
2
+ from .collectors import collect_snapshot
3
+ from .health import run_checks
4
+ from .models import HealthCheck, Snapshot
5
+ __all__ = ["HealthCheck", "Snapshot", "collect_snapshot", "run_checks"]
6
+ __version__ = "0.1.0"
pcpulse/cli.py ADDED
@@ -0,0 +1,67 @@
1
+ import argparse
2
+ import json
3
+
4
+ from . import __version__, collect_snapshot, run_checks
5
+ from .serialization import to_json, to_markdown
6
+
7
+
8
+ def build_parser() -> argparse.ArgumentParser:
9
+ parser = argparse.ArgumentParser(
10
+ prog="pcpulse",
11
+ description="Privacy-first cross-platform PC diagnostics.",
12
+ )
13
+ parser.add_argument("--version", action="version", version=__version__)
14
+
15
+ sub = parser.add_subparsers(dest="command", required=True)
16
+
17
+ snapshot = sub.add_parser("snapshot", help="Collect a local system snapshot.")
18
+ snapshot.add_argument("--json", action="store_true")
19
+ snapshot.add_argument("--markdown", action="store_true")
20
+ snapshot.add_argument("--include-identity", action="store_true")
21
+
22
+ doctor = sub.add_parser("doctor", help="Run conservative local health checks.")
23
+ doctor.add_argument("--json", action="store_true")
24
+
25
+ sub.add_parser("schema", help="Show the current snapshot schema version.")
26
+ return parser
27
+
28
+
29
+ def main() -> int:
30
+ args = build_parser().parse_args()
31
+
32
+ if args.command == "schema":
33
+ print("1.0")
34
+ return 0
35
+
36
+ if args.command == "snapshot":
37
+ snapshot = collect_snapshot(include_identity=args.include_identity)
38
+ if args.json:
39
+ print(to_json(snapshot))
40
+ elif args.markdown:
41
+ print(to_markdown(snapshot), end="")
42
+ else:
43
+ print(f"OS: {snapshot.os}")
44
+ print(f"Kernel: {snapshot.kernel}")
45
+ print(f"Architecture: {snapshot.architecture}")
46
+ print(
47
+ f"CPU: {snapshot.cpu.logical_cores} logical / "
48
+ f"{snapshot.cpu.physical_cores} physical"
49
+ )
50
+ print(f"CPU load: {snapshot.cpu.load_percent}%")
51
+ print(f"Memory: {snapshot.memory.used_percent}% used")
52
+ return 0
53
+
54
+ if args.command == "doctor":
55
+ checks = run_checks(collect_snapshot())
56
+ if args.json:
57
+ print(json.dumps([check.__dict__ for check in checks], indent=2))
58
+ else:
59
+ for check in checks:
60
+ print(f"[{check.status.upper()}] {check.message}")
61
+ return 0 if not any(check.status == "error" for check in checks) else 1
62
+
63
+ return 0
64
+
65
+
66
+ if __name__ == "__main__":
67
+ raise SystemExit(main())
pcpulse/collectors.py ADDED
@@ -0,0 +1,25 @@
1
+ from datetime import datetime, timezone
2
+ import getpass, platform, time
3
+ import psutil
4
+ from .models import CpuInfo, DiskInfo, MemoryInfo, Snapshot
5
+ SCHEMA_VERSION = "1.0"
6
+ def collect_snapshot(*, include_identity: bool = False) -> Snapshot:
7
+ vm = psutil.virtual_memory()
8
+ disks, seen = [], set()
9
+ for part in psutil.disk_partitions(all=False):
10
+ if part.mountpoint in seen: continue
11
+ seen.add(part.mountpoint)
12
+ try:
13
+ u = psutil.disk_usage(part.mountpoint)
14
+ disks.append(DiskInfo(part.mountpoint, u.total, u.free, u.percent))
15
+ except (OSError, PermissionError):
16
+ disks.append(DiskInfo(part.mountpoint, None, None, None))
17
+ return Snapshot(
18
+ collected_at=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
19
+ schema_version=SCHEMA_VERSION, os=platform.system(), kernel=platform.release(),
20
+ architecture=platform.machine(),
21
+ cpu=CpuInfo(psutil.cpu_count(), psutil.cpu_count(logical=False), psutil.cpu_percent(interval=0.05)),
22
+ memory=MemoryInfo(vm.total, vm.available, vm.used, vm.percent), disks=disks,
23
+ uptime_seconds=max(0.0, time.time() - psutil.boot_time()),
24
+ hostname=platform.node() if include_identity else None,
25
+ username=getpass.getuser() if include_identity else None)
pcpulse/health.py ADDED
@@ -0,0 +1,16 @@
1
+ from .models import HealthCheck, Snapshot
2
+ def run_checks(snapshot: Snapshot) -> list[HealthCheck]:
3
+ checks = []
4
+ mem = snapshot.memory.used_percent
5
+ if mem is None: checks.append(HealthCheck("memory.available","unknown","Memory usage is unavailable."))
6
+ elif mem >= 95: checks.append(HealthCheck("memory.usage","error","Memory usage is critically high.",{"used_percent":mem}))
7
+ elif mem >= 85: checks.append(HealthCheck("memory.usage","warning","Memory usage is high.",{"used_percent":mem}))
8
+ else: checks.append(HealthCheck("memory.usage","ok","Memory usage is within the normal threshold.",{"used_percent":mem}))
9
+ for d in snapshot.disks:
10
+ v=d.used_percent
11
+ if v is None: status,msg="unknown",f"Disk usage unavailable for {d.path}."
12
+ elif v >= 95: status,msg="error",f"Disk usage is critically high on {d.path}."
13
+ elif v >= 85: status,msg="warning",f"Disk usage is high on {d.path}."
14
+ else: status,msg="ok",f"Disk usage is within the normal threshold on {d.path}."
15
+ checks.append(HealthCheck(f"disk.usage:{d.path}",status,msg,{"used_percent":v}))
16
+ return checks
pcpulse/models.py ADDED
@@ -0,0 +1,41 @@
1
+ from dataclasses import asdict, dataclass, field
2
+ from typing import Any
3
+ @dataclass(frozen=True)
4
+ class CpuInfo:
5
+ logical_cores: int | None
6
+ physical_cores: int | None
7
+ load_percent: float | None
8
+ @dataclass(frozen=True)
9
+ class MemoryInfo:
10
+ total_bytes: int | None
11
+ available_bytes: int | None
12
+ used_bytes: int | None
13
+ used_percent: float | None
14
+ @dataclass(frozen=True)
15
+ class DiskInfo:
16
+ path: str
17
+ total_bytes: int | None
18
+ free_bytes: int | None
19
+ used_percent: float | None
20
+ @dataclass(frozen=True)
21
+ class Snapshot:
22
+ collected_at: str
23
+ schema_version: str
24
+ os: str
25
+ kernel: str
26
+ architecture: str
27
+ cpu: CpuInfo
28
+ memory: MemoryInfo
29
+ disks: list[DiskInfo] = field(default_factory=list)
30
+ uptime_seconds: float | None = None
31
+ hostname: str | None = None
32
+ username: str | None = None
33
+ extra: dict[str, Any] = field(default_factory=dict)
34
+ def to_dict(self) -> dict[str, Any]:
35
+ return asdict(self)
36
+ @dataclass(frozen=True)
37
+ class HealthCheck:
38
+ id: str
39
+ status: str
40
+ message: str
41
+ details: dict[str, Any] = field(default_factory=dict)
pcpulse/py.typed ADDED
File without changes
@@ -0,0 +1,12 @@
1
+ import json
2
+ from typing import Any
3
+ from .models import Snapshot
4
+ def to_dict(snapshot: Snapshot) -> dict[str, Any]: return snapshot.to_dict()
5
+ def to_json(snapshot: Snapshot) -> str: return json.dumps(to_dict(snapshot), indent=2)
6
+ def to_markdown(snapshot: Snapshot) -> str:
7
+ rows=[("OS",snapshot.os),("Kernel",snapshot.kernel),("Architecture",snapshot.architecture),("CPU logical",snapshot.cpu.logical_cores),("CPU physical",snapshot.cpu.physical_cores),("CPU load",snapshot.cpu.load_percent),("Memory used",snapshot.memory.used_percent),("Uptime seconds",snapshot.uptime_seconds)]
8
+ out=["# PCPulse System Snapshot","","| Metric | Value |","|---|---|"]
9
+ out += [f"| {k} | {v if v is not None else 'unavailable'} |" for k,v in rows]
10
+ out += ["","## Disks"]
11
+ out += [f"- {d.path}: {d.used_percent if d.used_percent is not None else 'unavailable'}% used" for d in snapshot.disks]
12
+ return "\n".join(out)+"\n"
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.5
2
+ Name: pcpulse
3
+ Version: 0.1.0
4
+ Summary: Privacy-first cross-platform PC diagnostics library and CLI.
5
+ Author: Matthew Para
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Matthew Para
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Keywords: cli,diagnostics,hardware,pc,privacy,system
29
+ Classifier: Development Status :: 3 - Alpha
30
+ Classifier: Operating System :: OS Independent
31
+ Classifier: Programming Language :: Python :: 3
32
+ Requires-Python: >=3.10
33
+ Requires-Dist: psutil<8,>=5.9
34
+ Requires-Dist: rich<15,>=13
35
+ Provides-Extra: dev
36
+ Requires-Dist: mypy>=1.11; extra == 'dev'
37
+ Requires-Dist: pytest>=8; extra == 'dev'
38
+ Requires-Dist: ruff>=0.6; extra == 'dev'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # PCPulse
42
+
43
+ **Local-first, privacy-conscious, cross-platform PC diagnostics for humans, scripts, support tools, and developer applications.**
44
+
45
+ PCPulse is an open-source Python library and CLI for collecting useful system diagnostics without requiring a cloud service. It turns local machine information into typed Python objects and stable machine-readable output.
46
+
47
+ ## Highlights
48
+
49
+ - Cross-platform system snapshots
50
+ - CPU, memory, disk, OS, architecture, and uptime information
51
+ - JSON and Markdown output
52
+ - Conservative local health checks with `pcpulse doctor`
53
+ - Versioned snapshot schema
54
+ - Privacy-first identity handling
55
+ - Python library API for integration into other tools
56
+ - No telemetry or mandatory network service
57
+ - MIT licensed
58
+ - Tests, linting, type checking, CI, security guidance, and contributor documentation
59
+
60
+ ## Installation
61
+
62
+ Install the published package when available:
63
+
64
+ python -m pip install pcpulse
65
+
66
+ For development:
67
+
68
+ git clone https://github.com/matthewpara567/pcpulse.git
69
+ cd pcpulse
70
+ python -m pip install -e '.[dev]'
71
+
72
+ ## Quick start
73
+
74
+ Collect a human-readable snapshot:
75
+
76
+ pcpulse snapshot
77
+
78
+ Get JSON:
79
+
80
+ pcpulse snapshot --json
81
+
82
+ Get Markdown:
83
+
84
+ pcpulse snapshot --markdown
85
+
86
+ Run health checks:
87
+
88
+ pcpulse doctor
89
+
90
+ Get machine-readable health checks:
91
+
92
+ pcpulse doctor --json
93
+
94
+ Identity information is excluded by default. Opt in explicitly:
95
+
96
+ pcpulse snapshot --include-identity
97
+
98
+ ## Python API
99
+
100
+ from pcpulse import collect_snapshot, run_checks
101
+
102
+ snapshot = collect_snapshot()
103
+ print(snapshot.to_dict())
104
+
105
+ for check in run_checks(snapshot):
106
+ print(check.status, check.message)
107
+
108
+ The library is intended to be the stable integration layer. The CLI is an interface over that library.
109
+
110
+ ## Privacy
111
+
112
+ PCPulse is designed around a local-first model:
113
+
114
+ - No telemetry is required.
115
+ - No account is required.
116
+ - No cloud backend is required.
117
+ - Hostname and username are not collected unless explicitly requested.
118
+ - Collectors are read-only diagnostics and do not intentionally modify the host.
119
+ - Applications should sanitize reports before sharing them externally.
120
+
121
+ A diagnostic report can still contain machine-specific information such as operating-system details, mount paths, hardware counts, or uptime. Review output before publishing it.
122
+
123
+ ## Snapshot schema
124
+
125
+ Snapshots carry an explicit schema version so consumers can distinguish format changes:
126
+
127
+ "schema_version": "1.0"
128
+
129
+ The canonical JSON Schema is stored at:
130
+
131
+ schemas/snapshot-1.0.schema.json
132
+
133
+ Consumers should treat schema changes as compatibility-sensitive and avoid assuming that every platform exposes every metric.
134
+
135
+ ## Health checks
136
+
137
+ `pcpulse doctor` performs conservative checks for conditions such as high memory usage and high disk utilization.
138
+
139
+ A check can report:
140
+
141
+ - `ok`
142
+ - `warning`
143
+ - `error`
144
+ - `unknown`
145
+
146
+ Unknown is preferred over inventing a value when the operating system does not expose a metric.
147
+
148
+ ## Architecture
149
+
150
+ PCPulse separates:
151
+
152
+ 1. **Collectors** — read local system information.
153
+ 2. **Models** — provide typed Python representations.
154
+ 3. **Serialization** — converts snapshots to JSON or Markdown.
155
+ 4. **Health checks** — interprets selected metrics.
156
+ 5. **CLI** — provides a user-facing command interface.
157
+
158
+ This separation makes PCPulse useful as a library without requiring applications to scrape terminal output.
159
+
160
+ ## Development
161
+
162
+ Install development dependencies:
163
+
164
+ python -m pip install -e '.[dev]'
165
+
166
+ Run the test suite:
167
+
168
+ pytest -q
169
+
170
+ Run linting:
171
+
172
+ ruff check .
173
+
174
+ Run type checking:
175
+
176
+ mypy src
177
+
178
+ ## Contributing
179
+
180
+ Bug fixes, documentation improvements, tests, platform compatibility work, and carefully scoped features are welcome.
181
+
182
+ Before opening a pull request:
183
+
184
+ - Keep the change focused.
185
+ - Add or update tests where appropriate.
186
+ - Document public API or schema changes.
187
+ - Consider privacy implications.
188
+ - Run the test, lint, and type-check commands.
189
+ - Avoid unrelated formatting churn.
190
+
191
+ See CONTRIBUTING.md for details.
192
+
193
+ ## Security
194
+
195
+ Please do not disclose vulnerabilities in public issues. Follow SECURITY.md for responsible reporting.
196
+
197
+ Security-sensitive areas include unintended network activity, unsafe parsing, dependency vulnerabilities, command execution, and accidental disclosure of local information.
198
+
199
+ ## Project structure
200
+
201
+ pcpulse/
202
+ ├── .github/
203
+ │ ├── ISSUE_TEMPLATE/
204
+ │ └── workflows/
205
+ ├── docs/
206
+ ├── examples/
207
+ ├── schemas/
208
+ ├── src/
209
+ │ └── pcpulse/
210
+ ├── tests/
211
+ ├── CHANGELOG.md
212
+ ├── CODE_OF_CONDUCT.md
213
+ ├── CONTRIBUTING.md
214
+ ├── GOVERNANCE.md
215
+ ├── LICENSE
216
+ ├── MAINTAINERS.md
217
+ ├── README.md
218
+ ├── RELEASING.md
219
+ ├── SECURITY.md
220
+ ├── SUPPORT.md
221
+ └── pyproject.toml
222
+
223
+ ## Design principles
224
+
225
+ PCPulse follows a few simple principles:
226
+
227
+ - **Local-first:** the core collector should work without a cloud service.
228
+ - **Privacy-first:** identity data is opt-in.
229
+ - **Structured:** machine-readable output is a first-class interface.
230
+ - **Best-effort:** unavailable metrics are represented as unavailable.
231
+ - **Read-only:** diagnostics should not silently modify the host.
232
+ - **Compatibility-conscious:** public API and schema changes should be deliberate.
233
+ - **Small core:** features should justify their maintenance cost.
234
+
235
+ ## Roadmap
236
+
237
+ Potential future work includes:
238
+
239
+ - More detailed platform-specific hardware metrics
240
+ - Battery, temperature, and fan information where safely available
241
+ - Pluggable collectors
242
+ - Richer diagnostic bundles
243
+ - More schema validation tooling
244
+ - Additional operating-system compatibility tests
245
+ - Release provenance and artifact-integrity improvements
246
+
247
+ Roadmap items are not promises and may change as the project evolves.
248
+
249
+ ## License
250
+
251
+ PCPulse is released under the MIT License. See LICENSE.
252
+
253
+ ## Status
254
+
255
+ PCPulse is currently an early open-source project. APIs and schema details may evolve before the first stable release.
@@ -0,0 +1,12 @@
1
+ pcpulse/__init__.py,sha256=Bl6eFKrkTPYzFIKU6cFuw_iqNEI5k-Va3jlJn8y4yjU,234
2
+ pcpulse/cli.py,sha256=maAG4THY6Se9Vm5eqaFCEuxNNu9xNHV9qLOrl6iD2qA,2257
3
+ pcpulse/collectors.py,sha256=j-YrYUmVfX-isLw8kVSBQzb7idJRfZqPsK7PF_LxrJA,1310
4
+ pcpulse/health.py,sha256=4YGIkRWMFCG3Y3gGW52enEJIh_czwC2fhOY5KxVRQwM,1130
5
+ pcpulse/models.py,sha256=lzq4ZP2GwpBFqGg56pb1OP7-M2D_1_JshDZBAqueLdU,1110
6
+ pcpulse/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ pcpulse/serialization.py,sha256=4pKiX-1V3pgodOa3ZvWE91aZHGfNIi0M5eJxVnioN2A,921
8
+ pcpulse-0.1.0.dist-info/METADATA,sha256=L2XwIy-hHqgGdWUKWNrjeUMP7DBmLXToKs5b3Gj_N6c,7866
9
+ pcpulse-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
10
+ pcpulse-0.1.0.dist-info/entry_points.txt,sha256=lZkcACK5YkP2ffT2MegqeaOV-mb6PjTnhMz7FtG1cEQ,45
11
+ pcpulse-0.1.0.dist-info/licenses/LICENSE,sha256=sTtnfqCK1QklB0VTzAXrtZK3mycLDam3SeKMd0-6IvM,1069
12
+ pcpulse-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pcpulse = pcpulse.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Para
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.