pethost 0.2.2__tar.gz → 0.2.4__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 (30) hide show
  1. {pethost-0.2.2 → pethost-0.2.4}/PKG-INFO +7 -6
  2. {pethost-0.2.2 → pethost-0.2.4}/README.md +6 -5
  3. {pethost-0.2.2 → pethost-0.2.4}/pethost/__init__.py +32 -0
  4. {pethost-0.2.2 → pethost-0.2.4}/pethost/_async_client.py +41 -1
  5. {pethost-0.2.2 → pethost-0.2.4}/pethost/_client.py +41 -1
  6. {pethost-0.2.2 → pethost-0.2.4}/pethost/_convert.py +416 -2
  7. {pethost-0.2.2 → pethost-0.2.4}/pethost/_errors.py +4 -4
  8. {pethost-0.2.2 → pethost-0.2.4}/pethost/_types.py +738 -0
  9. {pethost-0.2.2 → pethost-0.2.4}/pethost/_version.py +1 -1
  10. {pethost-0.2.2 → pethost-0.2.4}/pethost/_wire/v1/panel_connect.py +187 -1
  11. {pethost-0.2.2 → pethost-0.2.4}/pethost/_wire/v1/panel_pb.py +1363 -179
  12. {pethost-0.2.2 → pethost-0.2.4}/pyproject.toml +1 -1
  13. {pethost-0.2.2 → pethost-0.2.4}/tests/api.py +6 -0
  14. {pethost-0.2.2 → pethost-0.2.4}/.gitignore +0 -0
  15. {pethost-0.2.2 → pethost-0.2.4}/LICENSE +0 -0
  16. {pethost-0.2.2 → pethost-0.2.4}/pethost/_runtime.py +0 -0
  17. {pethost-0.2.2 → pethost-0.2.4}/pethost/_wire/__init__.py +0 -0
  18. {pethost-0.2.2 → pethost-0.2.4}/pethost/_wire/v1/__init__.py +0 -0
  19. {pethost-0.2.2 → pethost-0.2.4}/pethost/py.typed +0 -0
  20. {pethost-0.2.2 → pethost-0.2.4}/tests/__init__.py +0 -0
  21. {pethost-0.2.2 → pethost-0.2.4}/tests/fake.py +0 -0
  22. {pethost-0.2.2 → pethost-0.2.4}/tests/harness.py +0 -0
  23. {pethost-0.2.2 → pethost-0.2.4}/tests/samples.py +0 -0
  24. {pethost-0.2.2 → pethost-0.2.4}/tests/test_clients.py +0 -0
  25. {pethost-0.2.2 → pethost-0.2.4}/tests/test_declarations.py +0 -0
  26. {pethost-0.2.2 → pethost-0.2.4}/tests/test_errors.py +0 -0
  27. {pethost-0.2.2 → pethost-0.2.4}/tests/test_json.py +0 -0
  28. {pethost-0.2.2 → pethost-0.2.4}/tests/test_messages.py +0 -0
  29. {pethost-0.2.2 → pethost-0.2.4}/tests/test_runtime.py +0 -0
  30. {pethost-0.2.2 → pethost-0.2.4}/tests/test_streams.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pethost
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: The Python SDK for Pethost, hosting for Docker Compose projects: deploy, read logs and requests, run commands and move files through its API.
5
5
  Project-URL: Homepage, https://pethost.dev
6
6
  Project-URL: Documentation, https://pethost.dev/docs/api/
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
30
30
  # Pethost for Python
31
31
 
32
32
  The Python SDK for [Pethost](https://pethost.dev), hosting for Docker Compose projects on a
33
- machine of your own. It has the 13 calls of Pethost's API, the ones an agent has through its
33
+ machine of your own. It has the 14 calls of Pethost's API, the ones an agent has through its
34
34
  MCP server: read the machine and its projects, deploy, read logs and HTTP requests, run
35
35
  commands, move files. It has the API's streams too, which follow an operation, the logs
36
36
  and the requests as they happen. Everything is typed, there is a synchronous client and an
@@ -110,6 +110,7 @@ asyncio.run(main())
110
110
  | `deploy_project(project_id, ...)` | Deploy a project | destructive |
111
111
  | `list_commits(project_id, ...)` | List a project's commits | read-only |
112
112
  | `run_project_action(project_id, ...)` | Run a project action | destructive |
113
+ | `set_project_alerts(project_id, ...)` | Set a project's alerts | |
113
114
  | `get_operation(project_id, ...)` | Get an operation | read-only |
114
115
  | `query_http_traffic(project_id, ...)` | Query HTTP traffic | read-only |
115
116
  | `query_container_logs(project_id, ...)` | Query container logs | read-only |
@@ -188,7 +189,7 @@ around it.
188
189
  A failed call raises a `PethostError`, and its class is the code:
189
190
  `CanceledError`, `UnknownError`, `InvalidArgumentError`, `DeadlineExceededError`, `NotFoundError`, `AlreadyExistsError`, `PermissionDeniedError`, `ResourceExhaustedError`, `FailedPreconditionError`, `AbortedError`, `OutOfRangeError`, `UnimplementedError`, `InternalError`, `UnavailableError`, `DataLossError`, `UnauthenticatedError`.
190
191
  `error.message` is prose for people. What a program branches on, beside the class, is
191
- `error.detail`: `ProjectBusy`, `ProjectChanged`, `MachineUnreachable`, `NoMachine`, or `None`.
192
+ `error.detail`: `ProjectBusy`, `ProjectChanged`, `MachineUnreachable`, `MachineUpdatePending`, `NoMachine`, or `None`.
192
193
 
193
194
  ```python
194
195
  from pethost import NotFoundError, Pethost, PethostError
@@ -263,9 +264,9 @@ agent or from a web panel: both show the same projects, logs and requests.
263
264
  it, never through our panel.
264
265
 
265
266
  **Pricing.** Starter is €9.99 a month for 2 vCPU, 3 GB of memory and 30 GB of SSD. Grow is
266
- €17.99 for 4 vCPU, 6 GB and 60 GB. Prices are without VAT. As many projects, containers and
267
- databases as fit: nothing is metered, and nothing is billed on top. There is no free tier.
268
- [See the plans](https://pethost.dev/#pricing).
267
+ €17.99 for 4 vCPU, 6 GB and 60 GB. Scale is €59 for 8 vCPU, 30 GB and 200 GB. Prices are without
268
+ VAT. As many projects, containers and databases as fit: nothing is metered, and nothing is billed
269
+ on top. There is no free tier. [See the plans](https://pethost.dev/#pricing).
269
270
 
270
271
  **Who it is for.** Solo developers and early startups who host several projects, bots or small
271
272
  apps and want one fixed monthly bill, and people who build with an AI agent and want it to
@@ -1,7 +1,7 @@
1
1
  # Pethost for Python
2
2
 
3
3
  The Python SDK for [Pethost](https://pethost.dev), hosting for Docker Compose projects on a
4
- machine of your own. It has the 13 calls of Pethost's API, the ones an agent has through its
4
+ machine of your own. It has the 14 calls of Pethost's API, the ones an agent has through its
5
5
  MCP server: read the machine and its projects, deploy, read logs and HTTP requests, run
6
6
  commands, move files. It has the API's streams too, which follow an operation, the logs
7
7
  and the requests as they happen. Everything is typed, there is a synchronous client and an
@@ -81,6 +81,7 @@ asyncio.run(main())
81
81
  | `deploy_project(project_id, ...)` | Deploy a project | destructive |
82
82
  | `list_commits(project_id, ...)` | List a project's commits | read-only |
83
83
  | `run_project_action(project_id, ...)` | Run a project action | destructive |
84
+ | `set_project_alerts(project_id, ...)` | Set a project's alerts | |
84
85
  | `get_operation(project_id, ...)` | Get an operation | read-only |
85
86
  | `query_http_traffic(project_id, ...)` | Query HTTP traffic | read-only |
86
87
  | `query_container_logs(project_id, ...)` | Query container logs | read-only |
@@ -159,7 +160,7 @@ around it.
159
160
  A failed call raises a `PethostError`, and its class is the code:
160
161
  `CanceledError`, `UnknownError`, `InvalidArgumentError`, `DeadlineExceededError`, `NotFoundError`, `AlreadyExistsError`, `PermissionDeniedError`, `ResourceExhaustedError`, `FailedPreconditionError`, `AbortedError`, `OutOfRangeError`, `UnimplementedError`, `InternalError`, `UnavailableError`, `DataLossError`, `UnauthenticatedError`.
161
162
  `error.message` is prose for people. What a program branches on, beside the class, is
162
- `error.detail`: `ProjectBusy`, `ProjectChanged`, `MachineUnreachable`, `NoMachine`, or `None`.
163
+ `error.detail`: `ProjectBusy`, `ProjectChanged`, `MachineUnreachable`, `MachineUpdatePending`, `NoMachine`, or `None`.
163
164
 
164
165
  ```python
165
166
  from pethost import NotFoundError, Pethost, PethostError
@@ -234,9 +235,9 @@ agent or from a web panel: both show the same projects, logs and requests.
234
235
  it, never through our panel.
235
236
 
236
237
  **Pricing.** Starter is €9.99 a month for 2 vCPU, 3 GB of memory and 30 GB of SSD. Grow is
237
- €17.99 for 4 vCPU, 6 GB and 60 GB. Prices are without VAT. As many projects, containers and
238
- databases as fit: nothing is metered, and nothing is billed on top. There is no free tier.
239
- [See the plans](https://pethost.dev/#pricing).
238
+ €17.99 for 4 vCPU, 6 GB and 60 GB. Scale is €59 for 8 vCPU, 30 GB and 200 GB. Prices are without
239
+ VAT. As many projects, containers and databases as fit: nothing is metered, and nothing is billed
240
+ on top. There is no free tier. [See the plans](https://pethost.dev/#pricing).
240
241
 
241
242
  **Who it is for.** Solo developers and early startups who host several projects, bots or small
242
243
  apps and want one fixed monthly bill, and people who build with an AI agent and want it to
@@ -99,6 +99,9 @@ from ._errors import (
99
99
  UnknownError,
100
100
  )
101
101
  from ._types import (
102
+ AlertChannelKind,
103
+ AlertDestination,
104
+ AlertStatus,
102
105
  ArchiveUpload,
103
106
  BackUpAction,
104
107
  BranchCommit,
@@ -133,12 +136,16 @@ from ._types import (
133
136
  HttpTrafficBucket,
134
137
  HttpTrafficFilter,
135
138
  HttpTrafficSummary,
139
+ Incident,
140
+ IncidentEvidence,
141
+ IncidentQuietReason,
136
142
  ListCommitsResponse,
137
143
  LogLine,
138
144
  Machine,
139
145
  MachineSession,
140
146
  MachineSessionKind,
141
147
  MachineUnreachable,
148
+ MachineUpdatePending,
142
149
  MountVolume,
143
150
  NoMachine,
144
151
  NoMachineReason,
@@ -149,6 +156,7 @@ from ._types import (
149
156
  OutputStream,
150
157
  PathDownload,
151
158
  Project,
159
+ ProjectAlerts,
152
160
  ProjectBusy,
153
161
  ProjectChanged,
154
162
  ProjectExtension,
@@ -175,11 +183,19 @@ from ._types import (
175
183
  ServiceSummary,
176
184
  ServicesAction,
177
185
  ServicesActionKind,
186
+ SetProjectAlertsResponse,
187
+ SiteCheck,
188
+ SiteCheckResult,
189
+ SiteCheckState,
178
190
  Snapshot,
179
191
  SpecViolation,
180
192
  SshKey,
181
193
  TailContainerLogsResponse,
182
194
  TailHttpTrafficResponse,
195
+ TrafficMetric,
196
+ TrafficRule,
197
+ TrafficRuleState,
198
+ UptimeDay,
183
199
  Volume,
184
200
  VolumeMount,
185
201
  VolumeMountedBy,
@@ -188,6 +204,9 @@ from ._types import (
188
204
 
189
205
  __all__ = [
190
206
  "AbortedError",
207
+ "AlertChannelKind",
208
+ "AlertDestination",
209
+ "AlertStatus",
191
210
  "AlreadyExistsError",
192
211
  "ArchiveUpload",
193
212
  "AsyncPethost",
@@ -229,6 +248,9 @@ __all__ = [
229
248
  "HttpTrafficBucket",
230
249
  "HttpTrafficFilter",
231
250
  "HttpTrafficSummary",
251
+ "Incident",
252
+ "IncidentEvidence",
253
+ "IncidentQuietReason",
232
254
  "InternalError",
233
255
  "InvalidArgumentError",
234
256
  "ListCommitsResponse",
@@ -237,6 +259,7 @@ __all__ = [
237
259
  "MachineSession",
238
260
  "MachineSessionKind",
239
261
  "MachineUnreachable",
262
+ "MachineUpdatePending",
240
263
  "MountVolume",
241
264
  "NoMachine",
242
265
  "NoMachineReason",
@@ -252,6 +275,7 @@ __all__ = [
252
275
  "Pethost",
253
276
  "PethostError",
254
277
  "Project",
278
+ "ProjectAlerts",
255
279
  "ProjectBusy",
256
280
  "ProjectChanged",
257
281
  "ProjectExtension",
@@ -279,15 +303,23 @@ __all__ = [
279
303
  "ServiceSummary",
280
304
  "ServicesAction",
281
305
  "ServicesActionKind",
306
+ "SetProjectAlertsResponse",
307
+ "SiteCheck",
308
+ "SiteCheckResult",
309
+ "SiteCheckState",
282
310
  "Snapshot",
283
311
  "SpecViolation",
284
312
  "SshKey",
285
313
  "TailContainerLogsResponse",
286
314
  "TailHttpTrafficResponse",
315
+ "TrafficMetric",
316
+ "TrafficRule",
317
+ "TrafficRuleState",
287
318
  "UnauthenticatedError",
288
319
  "UnavailableError",
289
320
  "UnimplementedError",
290
321
  "UnknownError",
322
+ "UptimeDay",
291
323
  "Volume",
292
324
  "VolumeMount",
293
325
  "VolumeMountedBy",
@@ -42,9 +42,12 @@ from pethost._types import (
42
42
  RunProjectActionResponse,
43
43
  RunServiceCommandResponse,
44
44
  ServicesAction,
45
+ SetProjectAlertsResponse,
46
+ SiteCheck,
45
47
  SshKey,
46
48
  TailContainerLogsResponse,
47
49
  TailHttpTrafficResponse,
50
+ TrafficRule,
48
51
  WatchOperationResponse,
49
52
  )
50
53
  from pethost._wire.v1.panel_connect import PanelServiceClient as _Wire
@@ -342,6 +345,9 @@ class AsyncPethost:
342
345
  include_secret_values: bool = False,
343
346
  snapshots_before: datetime | None = None,
344
347
  snapshots_volume: str = "",
348
+ incidents_before: datetime | None = None,
349
+ uptime_day_count: int = 0,
350
+ incidents_before_id: str = "",
345
351
  ) -> GetProjectResponse:
346
352
  """Get a project.
347
353
 
@@ -356,8 +362,11 @@ class AsyncPethost:
356
362
  include_secret_values: True = include env-file values (EnvironmentVariable.secret). To edit .env: read_path /.env.
357
363
  snapshots_before: Only older snapshots: the oldest create_time seen, to page back. Absent = the newest.
358
364
  snapshots_volume: Only snapshots holding this volume (Volume.snapshot_count). Empty = all.
365
+ incidents_before: Only incidents that opened before it: the oldest open_time seen, to page back. Absent = the newest.
366
+ uptime_day_count: ≤90: Project.uptime_days. 0 = none.
367
+ incidents_before_id: With incidents_before: the incident_id of the oldest seen, so that those that opened at the same time are not skipped.
359
368
  """
360
- return _convert.get_project_response_from_wire(await self._call(self._wire.get_project, _convert.get_project_request(project_id=project_id, include_secret_values=include_secret_values, snapshots_before=snapshots_before, snapshots_volume=snapshots_volume)))
369
+ return _convert.get_project_response_from_wire(await self._call(self._wire.get_project, _convert.get_project_request(project_id=project_id, include_secret_values=include_secret_values, snapshots_before=snapshots_before, snapshots_volume=snapshots_volume, incidents_before=incidents_before, uptime_day_count=uptime_day_count, incidents_before_id=incidents_before_id)))
361
370
 
362
371
  async def create_project(
363
372
  self,
@@ -1409,6 +1418,37 @@ class AsyncPethost:
1409
1418
  """
1410
1419
  return _convert.run_project_action_response_from_wire(await self._call(self._wire.run_project_action, _convert.run_project_action_request(project_id=project_id, start_services=start_services, stop_services=stop_services, restart_services=restart_services, recreate_service=recreate_service, back_up=back_up, restore_snapshot=restore_snapshot, cancel_operation=cancel_operation, delete_project=delete_project, set_source=set_source, delete_volume=delete_volume, operation_id=operation_id, wait_seconds=wait_seconds)))
1411
1420
 
1421
+ async def set_project_alerts(
1422
+ self,
1423
+ project_id: str,
1424
+ *,
1425
+ checks: Sequence[SiteCheck] = (),
1426
+ remove_checks: bool = False,
1427
+ traffic_rules: Sequence[TrafficRule] = (),
1428
+ remove_traffic_rules: bool = False,
1429
+ off: bool | None = None,
1430
+ mute_minutes: int | None = None,
1431
+ ) -> SetProjectAlertsResponse:
1432
+ """Set a project's alerts.
1433
+
1434
+ Changes what Pethost watches on a project and when it tells the person: site checks (an
1435
+ address the machine opens every minute) and traffic rules (5xx or p95 over a window), off,
1436
+ mute. A list given replaces the project's (send Project.alerts' with your change); remove_*
1437
+ empties it; a scalar left out stays. Service crashes and failed backups are always watched.
1438
+ Answers with Project.alerts. Where alerts go is the person's choice in the panel's Settings →
1439
+ Alerts (Machine.alert_channels): no tool sets it. FailedPreconditionError + MachineUpdatePending
1440
+ = the machine's software predates alerts.
1441
+
1442
+ Args:
1443
+ checks: ≤10. Given = the whole list.
1444
+ remove_checks: True = none, not even the default check.
1445
+ traffic_rules: ≤10. Given = the whole list.
1446
+ remove_traffic_rules: True = none.
1447
+ off: True = incidents are recorded, nothing is sent. Absent = stays.
1448
+ mute_minutes: ≤10080; 0 = unmute. Incidents opened meanwhile are not sent. Absent = stays.
1449
+ """
1450
+ return _convert.set_project_alerts_response_from_wire(await self._call(self._wire.set_project_alerts, _convert.set_project_alerts_request(project_id=project_id, checks=checks, remove_checks=remove_checks, traffic_rules=traffic_rules, remove_traffic_rules=remove_traffic_rules, off=off, mute_minutes=mute_minutes)))
1451
+
1412
1452
  async def get_operation(
1413
1453
  self,
1414
1454
  project_id: str,
@@ -41,9 +41,12 @@ from pethost._types import (
41
41
  RunProjectActionResponse,
42
42
  RunServiceCommandResponse,
43
43
  ServicesAction,
44
+ SetProjectAlertsResponse,
45
+ SiteCheck,
44
46
  SshKey,
45
47
  TailContainerLogsResponse,
46
48
  TailHttpTrafficResponse,
49
+ TrafficRule,
47
50
  WatchOperationResponse,
48
51
  )
49
52
  from pethost._wire.v1.panel_connect import PanelServiceClientSync as _Wire
@@ -338,6 +341,9 @@ class Pethost:
338
341
  include_secret_values: bool = False,
339
342
  snapshots_before: datetime | None = None,
340
343
  snapshots_volume: str = "",
344
+ incidents_before: datetime | None = None,
345
+ uptime_day_count: int = 0,
346
+ incidents_before_id: str = "",
341
347
  ) -> GetProjectResponse:
342
348
  """Get a project.
343
349
 
@@ -352,8 +358,11 @@ class Pethost:
352
358
  include_secret_values: True = include env-file values (EnvironmentVariable.secret). To edit .env: read_path /.env.
353
359
  snapshots_before: Only older snapshots: the oldest create_time seen, to page back. Absent = the newest.
354
360
  snapshots_volume: Only snapshots holding this volume (Volume.snapshot_count). Empty = all.
361
+ incidents_before: Only incidents that opened before it: the oldest open_time seen, to page back. Absent = the newest.
362
+ uptime_day_count: ≤90: Project.uptime_days. 0 = none.
363
+ incidents_before_id: With incidents_before: the incident_id of the oldest seen, so that those that opened at the same time are not skipped.
355
364
  """
356
- return _convert.get_project_response_from_wire(self._call(self._wire.get_project, _convert.get_project_request(project_id=project_id, include_secret_values=include_secret_values, snapshots_before=snapshots_before, snapshots_volume=snapshots_volume)))
365
+ return _convert.get_project_response_from_wire(self._call(self._wire.get_project, _convert.get_project_request(project_id=project_id, include_secret_values=include_secret_values, snapshots_before=snapshots_before, snapshots_volume=snapshots_volume, incidents_before=incidents_before, uptime_day_count=uptime_day_count, incidents_before_id=incidents_before_id)))
357
366
 
358
367
  def create_project(
359
368
  self,
@@ -1405,6 +1414,37 @@ class Pethost:
1405
1414
  """
1406
1415
  return _convert.run_project_action_response_from_wire(self._call(self._wire.run_project_action, _convert.run_project_action_request(project_id=project_id, start_services=start_services, stop_services=stop_services, restart_services=restart_services, recreate_service=recreate_service, back_up=back_up, restore_snapshot=restore_snapshot, cancel_operation=cancel_operation, delete_project=delete_project, set_source=set_source, delete_volume=delete_volume, operation_id=operation_id, wait_seconds=wait_seconds)))
1407
1416
 
1417
+ def set_project_alerts(
1418
+ self,
1419
+ project_id: str,
1420
+ *,
1421
+ checks: Sequence[SiteCheck] = (),
1422
+ remove_checks: bool = False,
1423
+ traffic_rules: Sequence[TrafficRule] = (),
1424
+ remove_traffic_rules: bool = False,
1425
+ off: bool | None = None,
1426
+ mute_minutes: int | None = None,
1427
+ ) -> SetProjectAlertsResponse:
1428
+ """Set a project's alerts.
1429
+
1430
+ Changes what Pethost watches on a project and when it tells the person: site checks (an
1431
+ address the machine opens every minute) and traffic rules (5xx or p95 over a window), off,
1432
+ mute. A list given replaces the project's (send Project.alerts' with your change); remove_*
1433
+ empties it; a scalar left out stays. Service crashes and failed backups are always watched.
1434
+ Answers with Project.alerts. Where alerts go is the person's choice in the panel's Settings →
1435
+ Alerts (Machine.alert_channels): no tool sets it. FailedPreconditionError + MachineUpdatePending
1436
+ = the machine's software predates alerts.
1437
+
1438
+ Args:
1439
+ checks: ≤10. Given = the whole list.
1440
+ remove_checks: True = none, not even the default check.
1441
+ traffic_rules: ≤10. Given = the whole list.
1442
+ remove_traffic_rules: True = none.
1443
+ off: True = incidents are recorded, nothing is sent. Absent = stays.
1444
+ mute_minutes: ≤10080; 0 = unmute. Incidents opened meanwhile are not sent. Absent = stays.
1445
+ """
1446
+ return _convert.set_project_alerts_response_from_wire(self._call(self._wire.set_project_alerts, _convert.set_project_alerts_request(project_id=project_id, checks=checks, remove_checks=remove_checks, traffic_rules=traffic_rules, remove_traffic_rules=remove_traffic_rules, off=off, mute_minutes=mute_minutes)))
1447
+
1408
1448
  def get_operation(
1409
1449
  self,
1410
1450
  project_id: str,