qapu-cli 0.78.0__tar.gz → 0.83.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/PKG-INFO +4 -2
  2. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/README.md +3 -1
  3. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/pyproject.toml +1 -1
  4. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/client.py +15 -0
  5. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/command_packages.py +19 -0
  6. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/main.py +498 -80
  7. qapu_cli-0.83.0/qapu_cli/sso_login.py +123 -0
  8. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/data.py +12 -5
  9. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/dashboard.py +4 -2
  10. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli.egg-info/PKG-INFO +4 -2
  11. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli.egg-info/SOURCES.txt +2 -0
  12. qapu_cli-0.83.0/tests/test_sso_login.py +44 -0
  13. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_tui_smoke.py +6 -6
  14. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/__init__.py +0 -0
  15. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/__init__.py +0 -0
  16. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/app.py +0 -0
  17. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/app.tcss +0 -0
  18. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/rendering.py +0 -0
  19. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/__init__.py +0 -0
  20. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/catalog.py +0 -0
  21. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/catalog_form.py +0 -0
  22. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/catalog_picker.py +0 -0
  23. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/confirm.py +0 -0
  24. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/device_authorization.py +0 -0
  25. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/device_edit.py +0 -0
  26. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/device_list.py +0 -0
  27. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/equipment_form.py +0 -0
  28. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/group_detail.py +0 -0
  29. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/group_form.py +0 -0
  30. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/group_list.py +0 -0
  31. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/screens/infrastructure.py +0 -0
  32. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/widgets/__init__.py +0 -0
  33. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/tui/widgets/device_table.py +0 -0
  34. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli/visibility.py +0 -0
  35. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli.egg-info/dependency_links.txt +0 -0
  36. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli.egg-info/entry_points.txt +0 -0
  37. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli.egg-info/requires.txt +0 -0
  38. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/qapu_cli.egg-info/top_level.txt +0 -0
  39. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/setup.cfg +0 -0
  40. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_command_packages_complete.py +0 -0
  41. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_login.py +0 -0
  42. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_logout.py +0 -0
  43. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_tui_authorization.py +0 -0
  44. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_tui_catalog.py +0 -0
  45. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_tui_device_edit.py +0 -0
  46. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_tui_electrical_parent.py +0 -0
  47. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_tui_groups.py +0 -0
  48. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_tui_infrastructure.py +0 -0
  49. {qapu_cli-0.78.0 → qapu_cli-0.83.0}/tests/test_visibility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qapu-cli
3
- Version: 0.78.0
3
+ Version: 0.83.0
4
4
  Summary: CLI client for the Qapu API - built for the Hermes agent, but usable by anyone talking to api.ovoo.com.tr from outside the Swarm.
5
5
  Author: OVOO Technology
6
6
  Classifier: Programming Language :: Python :: 3
@@ -67,7 +67,7 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
67
67
  | `qapu data` | Readings per variable for a device - latest value, last N buffered readings (--last), daily min/avg/max (--days), an explicit --start/--end range, or a live-refreshed view (--watch) (GET /hermes/data/{device_id}). |
68
68
  | `qapu energy` | One day's active/reactive energy breakdown per phase, or --days/--start/--end for a per-day breakdown plus a cumulative Total (GET /hermes/energy/{device_id}). |
69
69
  | `qapu health` | Check CLI/server version, connectivity, and server status (GET /health, no auth required). |
70
- | `qapu login` | Log in and save a Bearer JWT locally for `auth-token *` - `qapu login [username]` (operator/CLI, Username+Password, POST /auth/login/cli) is the **default**, prompting for whatever wasn't given, password always hidden, never a CLI flag. `qapu login --phone <number>` (mobile-app Phone+Pin, POST /auth/login) is an explicit opt-in, never mixed with a username. |
70
+ | `qapu login` | Log in and save a Bearer JWT locally for `auth-token *` - `qapu login [username]` (operator/CLI, Username+Password, POST /auth/login/cli) is the **default**, prompting for whatever wasn't given, password always hidden, never a CLI flag. `qapu login --phone <number>` (mobile-app Phone+Pin, POST /auth/login) is an explicit opt-in, never mixed with a username. `qapu login --browser` logs in on auth.ovoo.com.tr's own page (SSO; no password at all when the browser is already logged in), needs a browser on this machine. |
71
71
  | `qapu logout` | Clear the locally saved Bearer JWT (client-side only) - does **not** clear a saved Hermes API token, see "Auth" below. |
72
72
  | `qapu whoami` | Show the currently logged-in account, from the locally saved credentials. |
73
73
  | `qapu pipeline` | Fleet-wide pipeline throughput for a time window - ingest/data/rule/blockchain packet+event counts (GET /hermes/pipeline/stats). `--watch` live-refreshes in place. |
@@ -85,6 +85,7 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
85
85
  | `qapu device list` | List every device Qapu knows about (GET /hermes/devices). |
86
86
  | `qapu device reliability` | Analyze a device's periodic packet delivery over a time window - total/expected packets, delivery rate, average/min/max interval, estimated missed packets, longest gap (GET /hermes/reliability/{device_id}). |
87
87
  | `qapu score list` | List recent Cinga Score snapshot rows, optionally filtered by device/window type (GET /hermes/scores). |
88
+ | `qapu score fleet-summary --device-ids A,B,C [--window-type ...] [--json]` | Latest four cards per device, worst card first, with the change since the window start (GET /hermes/scores/fleet-summary). |
88
89
  | `qapu score get` | Single snapshot detail (GET /hermes/scores/{score_id}). |
89
90
  | `qapu score bundle` | The AI-facing structured packet for one snapshot (GET /hermes/scores/{score_id}/bundle). |
90
91
  | `qapu score revisions` | Every revision (ACTIVE/SUPERSEDED) of the same logical period (GET /hermes/scores/{score_id}/revisions). |
@@ -99,6 +100,7 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
99
100
  |---|---|
100
101
  | `qapu variable get` | Get one variable's detail (GET /hermes/variables/{variable_id}). |
101
102
  | `qapu variable list` | List the variable catalog (GET /hermes/variables). |
103
+ | `qapu variable stats` | Stored readings per variable - typed table, reading/device counts, first/last time, last 24 h; `--segment`, `--stored-only` (GET /hermes/variables/stats, recomputed hourly by services/windows). |
102
104
 
103
105
  #### `group` - device groups
104
106
 
@@ -50,7 +50,7 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
50
50
  | `qapu data` | Readings per variable for a device - latest value, last N buffered readings (--last), daily min/avg/max (--days), an explicit --start/--end range, or a live-refreshed view (--watch) (GET /hermes/data/{device_id}). |
51
51
  | `qapu energy` | One day's active/reactive energy breakdown per phase, or --days/--start/--end for a per-day breakdown plus a cumulative Total (GET /hermes/energy/{device_id}). |
52
52
  | `qapu health` | Check CLI/server version, connectivity, and server status (GET /health, no auth required). |
53
- | `qapu login` | Log in and save a Bearer JWT locally for `auth-token *` - `qapu login [username]` (operator/CLI, Username+Password, POST /auth/login/cli) is the **default**, prompting for whatever wasn't given, password always hidden, never a CLI flag. `qapu login --phone <number>` (mobile-app Phone+Pin, POST /auth/login) is an explicit opt-in, never mixed with a username. |
53
+ | `qapu login` | Log in and save a Bearer JWT locally for `auth-token *` - `qapu login [username]` (operator/CLI, Username+Password, POST /auth/login/cli) is the **default**, prompting for whatever wasn't given, password always hidden, never a CLI flag. `qapu login --phone <number>` (mobile-app Phone+Pin, POST /auth/login) is an explicit opt-in, never mixed with a username. `qapu login --browser` logs in on auth.ovoo.com.tr's own page (SSO; no password at all when the browser is already logged in), needs a browser on this machine. |
54
54
  | `qapu logout` | Clear the locally saved Bearer JWT (client-side only) - does **not** clear a saved Hermes API token, see "Auth" below. |
55
55
  | `qapu whoami` | Show the currently logged-in account, from the locally saved credentials. |
56
56
  | `qapu pipeline` | Fleet-wide pipeline throughput for a time window - ingest/data/rule/blockchain packet+event counts (GET /hermes/pipeline/stats). `--watch` live-refreshes in place. |
@@ -68,6 +68,7 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
68
68
  | `qapu device list` | List every device Qapu knows about (GET /hermes/devices). |
69
69
  | `qapu device reliability` | Analyze a device's periodic packet delivery over a time window - total/expected packets, delivery rate, average/min/max interval, estimated missed packets, longest gap (GET /hermes/reliability/{device_id}). |
70
70
  | `qapu score list` | List recent Cinga Score snapshot rows, optionally filtered by device/window type (GET /hermes/scores). |
71
+ | `qapu score fleet-summary --device-ids A,B,C [--window-type ...] [--json]` | Latest four cards per device, worst card first, with the change since the window start (GET /hermes/scores/fleet-summary). |
71
72
  | `qapu score get` | Single snapshot detail (GET /hermes/scores/{score_id}). |
72
73
  | `qapu score bundle` | The AI-facing structured packet for one snapshot (GET /hermes/scores/{score_id}/bundle). |
73
74
  | `qapu score revisions` | Every revision (ACTIVE/SUPERSEDED) of the same logical period (GET /hermes/scores/{score_id}/revisions). |
@@ -82,6 +83,7 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
82
83
  |---|---|
83
84
  | `qapu variable get` | Get one variable's detail (GET /hermes/variables/{variable_id}). |
84
85
  | `qapu variable list` | List the variable catalog (GET /hermes/variables). |
86
+ | `qapu variable stats` | Stored readings per variable - typed table, reading/device counts, first/last time, last 24 h; `--segment`, `--stored-only` (GET /hermes/variables/stats, recomputed hourly by services/windows). |
85
87
 
86
88
  #### `group` - device groups
87
89
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "qapu-cli"
3
- version = "0.78.0"
3
+ version = "0.83.0"
4
4
  description = "CLI client for the Qapu API - built for the Hermes agent, but usable by anyone talking to api.ovoo.com.tr from outside the Swarm."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -261,6 +261,21 @@ def _request(method: str, path: str, params: dict[str, Any] | None = None, json_
261
261
  raise ClientError(_report_request_error(e, method, url))
262
262
 
263
263
 
264
+ # Upload one File as multipart/form-data (2026-10-26, Firmware Files) - a
265
+ # Longer Write/Read Timeout Than JSON Calls: the Server Also Relays it to FTP
266
+ def post_file(path: str, file_path: str, field: str = "file", data: dict[str, Any] | None = None) -> Any:
267
+ url = f"{_base_url()}{path}"
268
+ try:
269
+ with open(file_path, "rb") as handle:
270
+ response = httpx.post(url, headers=_headers(), files={field: (os.path.basename(file_path), handle, "application/octet-stream")}, data=data or {}, timeout=httpx.Timeout(connect=5.0, read=120.0, write=120.0, pool=5.0))
271
+ response.raise_for_status()
272
+ return response.json()
273
+ except httpx.HTTPStatusError as e:
274
+ raise ClientError(_report_http_status_error(e, "POST", url))
275
+ except httpx.RequestError as e:
276
+ raise ClientError(_report_request_error(e, "POST", url))
277
+
278
+
264
279
  def post(path: str, params: dict[str, Any] | None = None, json_body: dict[str, Any] | None = None) -> Any:
265
280
 
266
281
  # Issue a POST Request
@@ -125,7 +125,12 @@ COMMAND_REQUIRED_PACKAGES: dict[str, frozenset[str]] = {
125
125
  "fleet_data": frozenset({"telemetry:read"}),
126
126
  "fleet_score": frozenset({"scores:read"}),
127
127
  "fleet_status": frozenset({"devices:read", "infrastructure:read", "platform-config:read"}),
128
+ "fota_approvals": frozenset({"hardware-catalog:read"}),
129
+ "fota_approve": frozenset({"hardware-catalog:write"}),
130
+ "fota_confirmations": frozenset({"hardware-catalog:read"}),
131
+ "fota_reports": frozenset({"hardware-catalog:read"}),
128
132
  "fota_ftp": frozenset({"device-actuation:write", "hardware-catalog:read"}),
133
+ "fota_revoke": frozenset({"hardware-catalog:write"}),
129
134
  "fota_http": frozenset({"device-actuation:write", "hardware-catalog:read"}),
130
135
  "fota_upload": frozenset({"hardware-catalog:read", "hardware-catalog:write"}),
131
136
  "group_add": frozenset({"infrastructure:write"}),
@@ -165,8 +170,11 @@ COMMAND_REQUIRED_PACKAGES: dict[str, frozenset[str]] = {
165
170
  "model_get": frozenset({"hardware-catalog:read"}),
166
171
  "model_list": frozenset({"hardware-catalog:read"}),
167
172
  "model_update": frozenset({"hardware-catalog:write"}),
173
+ "modem_add": frozenset({"hardware-catalog:write"}),
174
+ "modem_delete": frozenset({"hardware-catalog:write"}),
168
175
  "modem_get": frozenset({"hardware-catalog:read"}),
169
176
  "modem_list": frozenset({"hardware-catalog:read"}),
177
+ "modem_update": frozenset({"hardware-catalog:write"}),
170
178
  "mqtt_provision": frozenset({"device-actuation:write"}),
171
179
  "pipeline_stats": frozenset({"platform-config:read"}),
172
180
  "project_add": frozenset({"platform-config:write"}),
@@ -193,6 +201,7 @@ COMMAND_REQUIRED_PACKAGES: dict[str, frozenset[str]] = {
193
201
  "score_bundle": frozenset({"scores:read"}),
194
202
  "score_calculate": frozenset({"scores:write"}),
195
203
  "score_delete": frozenset({"scores:write"}),
204
+ "score_fleet_summary": frozenset({"telemetry:read"}),
196
205
  "score_get": frozenset({"scores:read"}),
197
206
  "score_list": frozenset({"scores:read"}),
198
207
  "score_narrative": frozenset({"scores:write"}),
@@ -246,6 +255,16 @@ COMMAND_REQUIRED_PACKAGES: dict[str, frozenset[str]] = {
246
255
  "user_update": frozenset({"users:write"}),
247
256
  "variable_get": frozenset({"platform-config:read"}),
248
257
  "variable_list": frozenset({"platform-config:read"}),
258
+ "variable_stats": frozenset({"platform-config:read"}),
259
+ # Notifications (2026-11-11) - hermes_notifications.py routes (not in the hermes.py route table the
260
+ # derivation reads): always visible, Hermes enforces devices:read / users:* / notifications:*
261
+ "notification_channel_set": frozenset(),
262
+ "notification_channels": frozenset(),
263
+ "notification_log": frozenset(),
264
+ "notification_set": frozenset(),
265
+ "notification_settings": frozenset(),
266
+ "notification_type_update": frozenset(),
267
+ "notification_types": frozenset(),
249
268
  "whoami": frozenset(),
250
269
  }
251
270