boxd 0.2.13.dev715__tar.gz → 0.2.13.dev751__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 (42) hide show
  1. {boxd-0.2.13.dev715/src/boxd.egg-info → boxd-0.2.13.dev751}/PKG-INFO +59 -1
  2. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/README.md +58 -0
  3. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/pyproject.toml +1 -1
  4. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/__init__.py +16 -0
  5. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_generated/api_pb2.py +145 -125
  6. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_generated/api_pb2_grpc.py +132 -0
  7. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_mappers.py +87 -0
  8. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_requests.py +16 -0
  9. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/models.py +113 -0
  10. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/machines.py +72 -0
  11. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/orgs.py +178 -1
  12. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751/src/boxd.egg-info}/PKG-INFO +59 -1
  13. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/tests/test_namespaces.py +104 -0
  14. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/LICENSE +0 -0
  15. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/setup.cfg +0 -0
  16. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_client.py +0 -0
  17. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_credentials.py +0 -0
  18. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_generated/__init__.py +0 -0
  19. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_streaming.py +0 -0
  20. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_transport.py +0 -0
  21. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_urls.py +0 -0
  22. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/_version_check.py +0 -0
  23. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/errors.py +0 -0
  24. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/__init__.py +0 -0
  25. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/account.py +0 -0
  26. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/credentials.py +0 -0
  27. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/disks.py +0 -0
  28. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/domains.py +0 -0
  29. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/snapshots.py +0 -0
  30. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/teams.py +0 -0
  31. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd/resources/vars.py +0 -0
  32. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd.egg-info/SOURCES.txt +0 -0
  33. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd.egg-info/dependency_links.txt +0 -0
  34. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd.egg-info/requires.txt +0 -0
  35. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/src/boxd.egg-info/top_level.txt +0 -0
  36. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/tests/test_credentials.py +0 -0
  37. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/tests/test_mappers.py +0 -0
  38. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/tests/test_requests.py +0 -0
  39. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/tests/test_streaming.py +0 -0
  40. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/tests/test_transport.py +0 -0
  41. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/tests/test_urls.py +0 -0
  42. {boxd-0.2.13.dev715 → boxd-0.2.13.dev751}/tests/test_version_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boxd
3
- Version: 0.2.13.dev715
3
+ Version: 0.2.13.dev751
4
4
  Summary: Python SDK for the boxd cloud VM platform
5
5
  Author: Azin
6
6
  License-Expression: MIT
@@ -56,6 +56,10 @@ boxd.machines.wait_until_ready(machine.id)
56
56
  result = boxd.machines.exec(machine.id, "uname -a")
57
57
  print(result.stdout)
58
58
 
59
+ # What it cost (metered every few minutes; still answers after delete):
60
+ usage = boxd.machines.usage(machine.id)
61
+ print(usage.cost_micro / 1e6, usage.currency)
62
+
59
63
  boxd.machines.delete(machine.id)
60
64
  boxd.close()
61
65
  ```
@@ -753,6 +757,60 @@ cfg = boxd.account.config()
753
757
  cfg.default_image, cfg.zone
754
758
  ```
755
759
 
760
+ ## Usage and cost
761
+
762
+ What each machine used and cost, and what was actually charged. Org owners
763
+ and admins only; an API key issued for the org works too.
764
+
765
+ ```python
766
+ from datetime import datetime, timezone
767
+
768
+ # One machine over its whole life, or pass since=/until= (datetime or unix
769
+ # seconds). A deleted machine is found by its id or name (org= says where to look).
770
+ u = boxd.machines.usage("my-machine")
771
+ u.seconds.running, u.seconds.standby, u.seconds.hibernated, u.seconds.stopped
772
+ u.cost_micro / 1e6 # estimated cost, in u.currency ("eur" | "usd")
773
+ u.complete # True once all its usage is metered (see below)
774
+ u.period.start, u.period.end
775
+ u.vcpu_hours, u.ram_gib_hours, u.disk_gib_hours # what the cost is priced on
776
+ u.rates # ...at these rates, per unit-hour
777
+
778
+ # Every machine in an org, most expensive first. Default: the last 28 days.
779
+ r = boxd.orgs.usage(since=datetime(2026, 9, 1, tzinfo=timezone.utc))
780
+ r.total_cost_micro, r.currency
781
+ for m in r.machines:
782
+ print(m.name, m.status, m.cost_micro / 1e6)
783
+
784
+ # The credit ledger: what was actually charged, newest first.
785
+ for e in boxd.orgs.ledger(since=datetime(2026, 9, 1, tzinfo=timezone.utc)):
786
+ print(e.created_at, e.kind, e.amount_micro / 1e6) # charges are negative
787
+ ```
788
+
789
+ To record what a machine cost after deleting it, wait for `complete`:
790
+ metering lags by up to ~25 minutes, so the figure read right after `delete()`
791
+ is still growing.
792
+
793
+ ```python
794
+ boxd.machines.delete(machine_id)
795
+ # ...a little later, or from a periodic job:
796
+ u = boxd.machines.usage(machine_id)
797
+ if u.complete:
798
+ save(machine_id, u.cost_micro, u.currency) # otherwise try again later
799
+ ```
800
+
801
+ `orgs.usage()` carries the same flag on every machine, so one periodic call can
802
+ settle all of them. A machine that still exists is never complete in a window
803
+ that runs to now.
804
+
805
+ Money is in micro-units (1e-6) of the org's currency. Usage is metered in
806
+ 15-minute buckets and priced at the org's current rates, so it is an estimate
807
+ that tracks the ledger closely; the ledger is what was charged (one
808
+ `usage_charge` per 15 minutes for the whole org, plus top-ups and grants).
809
+ Machines deleted since still show up, with status `"deleted"`. A window spans
810
+ at most 366 days. `orgs.ledger_page()` returns one page with its
811
+ `next_page_token` when you want to page by hand. The async client has the
812
+ same methods (`async for e in boxd.orgs.ledger(): ...`).
813
+
756
814
  ## Errors
757
815
 
758
816
  ```python
@@ -24,6 +24,10 @@ boxd.machines.wait_until_ready(machine.id)
24
24
  result = boxd.machines.exec(machine.id, "uname -a")
25
25
  print(result.stdout)
26
26
 
27
+ # What it cost (metered every few minutes; still answers after delete):
28
+ usage = boxd.machines.usage(machine.id)
29
+ print(usage.cost_micro / 1e6, usage.currency)
30
+
27
31
  boxd.machines.delete(machine.id)
28
32
  boxd.close()
29
33
  ```
@@ -721,6 +725,60 @@ cfg = boxd.account.config()
721
725
  cfg.default_image, cfg.zone
722
726
  ```
723
727
 
728
+ ## Usage and cost
729
+
730
+ What each machine used and cost, and what was actually charged. Org owners
731
+ and admins only; an API key issued for the org works too.
732
+
733
+ ```python
734
+ from datetime import datetime, timezone
735
+
736
+ # One machine over its whole life, or pass since=/until= (datetime or unix
737
+ # seconds). A deleted machine is found by its id or name (org= says where to look).
738
+ u = boxd.machines.usage("my-machine")
739
+ u.seconds.running, u.seconds.standby, u.seconds.hibernated, u.seconds.stopped
740
+ u.cost_micro / 1e6 # estimated cost, in u.currency ("eur" | "usd")
741
+ u.complete # True once all its usage is metered (see below)
742
+ u.period.start, u.period.end
743
+ u.vcpu_hours, u.ram_gib_hours, u.disk_gib_hours # what the cost is priced on
744
+ u.rates # ...at these rates, per unit-hour
745
+
746
+ # Every machine in an org, most expensive first. Default: the last 28 days.
747
+ r = boxd.orgs.usage(since=datetime(2026, 9, 1, tzinfo=timezone.utc))
748
+ r.total_cost_micro, r.currency
749
+ for m in r.machines:
750
+ print(m.name, m.status, m.cost_micro / 1e6)
751
+
752
+ # The credit ledger: what was actually charged, newest first.
753
+ for e in boxd.orgs.ledger(since=datetime(2026, 9, 1, tzinfo=timezone.utc)):
754
+ print(e.created_at, e.kind, e.amount_micro / 1e6) # charges are negative
755
+ ```
756
+
757
+ To record what a machine cost after deleting it, wait for `complete`:
758
+ metering lags by up to ~25 minutes, so the figure read right after `delete()`
759
+ is still growing.
760
+
761
+ ```python
762
+ boxd.machines.delete(machine_id)
763
+ # ...a little later, or from a periodic job:
764
+ u = boxd.machines.usage(machine_id)
765
+ if u.complete:
766
+ save(machine_id, u.cost_micro, u.currency) # otherwise try again later
767
+ ```
768
+
769
+ `orgs.usage()` carries the same flag on every machine, so one periodic call can
770
+ settle all of them. A machine that still exists is never complete in a window
771
+ that runs to now.
772
+
773
+ Money is in micro-units (1e-6) of the org's currency. Usage is metered in
774
+ 15-minute buckets and priced at the org's current rates, so it is an estimate
775
+ that tracks the ledger closely; the ledger is what was charged (one
776
+ `usage_charge` per 15 minutes for the whole org, plus top-ups and grants).
777
+ Machines deleted since still show up, with status `"deleted"`. A window spans
778
+ at most 366 days. `orgs.ledger_page()` returns one page with its
779
+ `next_page_token` when you want to page by hand. The async client has the
780
+ same methods (`async for e in boxd.orgs.ledger(): ...`).
781
+
724
782
  ## Errors
725
783
 
726
784
  ```python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "boxd"
3
- version = "0.2.13.dev715"
3
+ version = "0.2.13.dev751"
4
4
  description = "Python SDK for the boxd cloud VM platform"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -53,6 +53,8 @@ from .models import (
53
53
  EnvVar,
54
54
  ExecResult,
55
55
  ExposedPort,
56
+ LedgerEntry,
57
+ LedgerPage,
56
58
  Machine,
57
59
  MachineAccess,
58
60
  MachineIdle,
@@ -61,6 +63,8 @@ from .models import (
61
63
  MachineSource,
62
64
  MachineSourceKind,
63
65
  MachineStatus,
66
+ MachineUsage,
67
+ MachineUsageReport,
64
68
  Org,
65
69
  OrgDomain,
66
70
  OrgInvite,
@@ -69,6 +73,7 @@ from .models import (
69
73
  OrgMembers,
70
74
  OrgRef,
71
75
  OrgRole,
76
+ OrgUsage,
72
77
  PauseResult,
73
78
  Proxy,
74
79
  ProxyEntry,
@@ -77,8 +82,11 @@ from .models import (
77
82
  ResumeResult,
78
83
  Secret,
79
84
  Snapshot,
85
+ StateSeconds,
80
86
  Team,
81
87
  TeamMember,
88
+ UsagePeriod,
89
+ UsageRates,
82
90
  VarScope,
83
91
  VolumeMount,
84
92
  )
@@ -122,6 +130,8 @@ __all__ = [
122
130
  "EnvVar",
123
131
  "ExecResult",
124
132
  "ExposedPort",
133
+ "LedgerEntry",
134
+ "LedgerPage",
125
135
  "Machine",
126
136
  "MachineAccess",
127
137
  "MachineIdle",
@@ -130,6 +140,8 @@ __all__ = [
130
140
  "MachineSource",
131
141
  "MachineSourceKind",
132
142
  "MachineStatus",
143
+ "MachineUsage",
144
+ "MachineUsageReport",
133
145
  "Org",
134
146
  "OrgDomain",
135
147
  "OrgInvite",
@@ -138,6 +150,7 @@ __all__ = [
138
150
  "OrgMembers",
139
151
  "OrgRef",
140
152
  "OrgRole",
153
+ "OrgUsage",
141
154
  "PauseResult",
142
155
  "Proxy",
143
156
  "ProxyEntry",
@@ -146,8 +159,11 @@ __all__ = [
146
159
  "ResumeResult",
147
160
  "Secret",
148
161
  "Snapshot",
162
+ "StateSeconds",
149
163
  "Team",
150
164
  "TeamMember",
165
+ "UsagePeriod",
166
+ "UsageRates",
151
167
  "VarScope",
152
168
  "VolumeMount",
153
169
  # Errors