lockwire 0.2.3__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 (81) hide show
  1. {lockwire-0.2.3 → lockwire-0.2.4}/PKG-INFO +11 -7
  2. {lockwire-0.2.3 → lockwire-0.2.4}/README.md +7 -5
  3. {lockwire-0.2.3 → lockwire-0.2.4}/pyproject.toml +5 -3
  4. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/__init__.py +1 -1
  5. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/api.py +20 -0
  6. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/checks.py +329 -22
  7. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/cli.py +192 -24
  8. lockwire-0.2.4/src/lockwire/confine.py +31 -0
  9. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/copy.py +11 -3
  10. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/edit.py +18 -10
  11. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/arm/contract.yaml +7 -0
  12. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/humanoid/contract.yaml +92 -0
  13. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/quadruped/contract.yaml +13 -0
  14. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/rover/contract.yaml +5 -0
  15. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/mcp_server.py +28 -6
  16. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/mechanism.py +25 -8
  17. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/mesh_dae.py +2 -1
  18. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/page.py +27 -4
  19. lockwire-0.2.4/src/lockwire/parts_list.py +164 -0
  20. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/report.py +14 -2
  21. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/scene_urdf.py +21 -5
  22. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/schema.py +55 -8
  23. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/serve.py +28 -29
  24. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/start_operation.py +20 -10
  25. lockwire-0.2.4/src/lockwire/statics.py +284 -0
  26. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/urdf.py +88 -9
  27. lockwire-0.2.3/src/lockwire/statics.py +0 -77
  28. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/__main__.py +0 -0
  29. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/_filelock.py +0 -0
  30. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/advice.py +0 -0
  31. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/api_payload.py +0 -0
  32. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/bench.py +0 -0
  33. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/blame.py +0 -0
  34. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/catalog.py +0 -0
  35. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/catalog_seed.yaml +0 -0
  36. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/cli_line.py +0 -0
  37. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/constraints.py +0 -0
  38. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/describe.py +0 -0
  39. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/emit_ts.py +0 -0
  40. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/arm/LICENSE +0 -0
  41. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/arm/NOTICE +0 -0
  42. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/arm/plan.yaml +0 -0
  43. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/arm/scene.glb +0 -0
  44. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/arm/starter.json +0 -0
  45. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/drone/contract.yaml +0 -0
  46. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/drone/plan.yaml +0 -0
  47. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/drone/scene.glb +0 -0
  48. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/drone/starter.json +0 -0
  49. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/humanoid/NOTICE +0 -0
  50. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/humanoid/plan.yaml +0 -0
  51. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/humanoid/scene.glb +0 -0
  52. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/humanoid/starter.json +0 -0
  53. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/quadruped/NOTICE +0 -0
  54. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/quadruped/plan.yaml +0 -0
  55. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/quadruped/scene.glb +0 -0
  56. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/quadruped/starter.json +0 -0
  57. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/rover/NOTICE +0 -0
  58. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/rover/plan.yaml +0 -0
  59. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/rover/scene.glb +0 -0
  60. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/rover/starter.json +0 -0
  61. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/examples/servo-bench.yaml +0 -0
  62. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/export_mhs.py +0 -0
  63. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/factdiff.py +0 -0
  64. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/forces.py +0 -0
  65. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/gitref.py +0 -0
  66. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/hw.py +0 -0
  67. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/mesh_simple.py +0 -0
  68. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/plan.py +0 -0
  69. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/plan_payload.py +0 -0
  70. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/plugins.py +0 -0
  71. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/report_html.py +0 -0
  72. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/report_json.py +0 -0
  73. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/review_draft.py +0 -0
  74. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/scan.py +0 -0
  75. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/scene.py +0 -0
  76. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/scene_import.py +0 -0
  77. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/scene_view.py +0 -0
  78. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/sdf.py +0 -0
  79. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/units.py +0 -0
  80. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/vcs.py +0 -0
  81. {lockwire-0.2.3 → lockwire-0.2.4}/src/lockwire/whatif.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lockwire
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: The physical-assumption contract for robots: checks that a robot's declared parts (power, weight, bus, torque) add up
5
5
  Keywords: robotics,urdf,ros,preflight,lockfile,ci,hardware,drone
6
6
  Author: Shivansh
@@ -19,7 +19,9 @@ Requires-Dist: pydantic>=2.13.4
19
19
  Requires-Dist: rich>=15.0.0
20
20
  Requires-Dist: ruamel-yaml>=0.19.1
21
21
  Requires-Dist: typer>=0.26.8
22
- Requires-Dist: mcp>=2.1.1 ; extra == 'mcp'
22
+ Requires-Dist: xacro>=2.1.1
23
+ Requires-Dist: mcp>=2.2.0 ; extra == 'mcp'
24
+ Requires-Dist: pyjwt>=2.15 ; extra == 'mcp'
23
25
  Requires-Dist: fastapi>=0.141.1 ; extra == 'serve'
24
26
  Requires-Dist: uvicorn>=0.52.4 ; extra == 'serve'
25
27
  Requires-Python: >=3.12
@@ -198,9 +200,9 @@ VIOLATION budgets/bud.power.pm02-5v — observed 35.20 W (draw mode: nominal), e
198
200
  action: shed load on rail 'rail.pm02-5v' or raise its capacity with provenance
199
201
  VIOLATION budgets/bud.power.pm02-5v.7w-mode — observed 17.20 W (draw mode: 7W), expected <= 15.60 W (Δ +1.60 W (+10%))
200
202
  … (same rail, Jetson in its 7 W mode: the radio's transmit peak still tips it over)
201
- not scored devices/dev.camera.oak-d-lite/physical/mass — observed mass not declared, expected a positive mass with units
203
+ not scored budgets/bud.mass.payload — observed mass unknown for dev.companion.jetson-orin-nano, dev.camera.oak-d-lite, dev.magnetometer.ist8310; total not scored, expected every device and link in this budget to declare a mass
202
204
  … (more not-scored lines: things the lockfile does not declare yet, listed, never hidden)
203
- scored 10: 8 add up, 2 do not · 9 not scored
205
+ scored 9: 7 add up, 2 do not · 10 not scored
204
206
 
205
207
  NO-GO — 2 violation(s), 0 warning(s)
206
208
  $ echo $?
@@ -217,7 +219,7 @@ integration of published parts, and the two vendors' own numbers still do not ad
217
219
  Nobody printed "this build is broken". Nobody printed "5 A" either: that is 25 W / 5 V, a
218
220
  quotient of figures published on different pages. The records disagree with each other, and
219
221
  you find out before you apply power. Give the Jetson its own regulator
220
- (`tests/fixtures/x500/contract_corrected.yaml`) and the same check passes.
222
+ (`tests/fixtures/x500/contract_corrected.yaml`) and the same check passes: `GO — records agree (10 scored); 10 check(s) not scored`.
221
223
 
222
224
  ## Your own robot
223
225
 
@@ -241,7 +243,9 @@ Any field you hand-edited (including `robot.name`) is silently overwritten back
241
243
  | `start` | the contract was written (or an existing one kept); no page is written | the URDF/SDF could not be parsed, or the contract could not be read or written | — | — |
242
244
 
243
245
  `NOT SCORED` (exit 3) means no comparison had both operands: it is not a pass and not a failure.
246
+
244
247
  The `scored N: …` line above the verdict is where a script should look.
248
+ `lockwire check --strict` makes a `GO with notes` run exit 1 (the `NO-GO` code); the table does not change.
245
249
 
246
250
  ## In CI
247
251
 
@@ -252,7 +256,7 @@ The `scored N: …` line above the verdict is where a script should look.
252
256
  contract: lockwire.yaml
253
257
  ```
254
258
 
255
- Inputs: `contract` (default `lockwire.yaml`), `version` (pin a PyPI release; default installs the action's own code), `python-version` (default 3.12).
259
+ Inputs: `contract` (default `lockwire.yaml`), `version` (pin a PyPI release; default installs the action's own code), `strict` (default `false`; `true` passes `--strict`), `python-version` (default 3.12).
256
260
  Or just `pipx install lockwire && lockwire check lockwire.yaml`.
257
261
 
258
262
  ## Pre-commit
@@ -260,7 +264,7 @@ Or just `pipx install lockwire && lockwire check lockwire.yaml`.
260
264
  ```yaml
261
265
  repos:
262
266
  - repo: https://github.com/Shivansh2703/lockwire
263
- rev: v0.2.0
267
+ rev: v0.2.3
264
268
  hooks:
265
269
  - id: lockwire-check
266
270
  ```
@@ -166,9 +166,9 @@ VIOLATION budgets/bud.power.pm02-5v — observed 35.20 W (draw mode: nominal), e
166
166
  action: shed load on rail 'rail.pm02-5v' or raise its capacity with provenance
167
167
  VIOLATION budgets/bud.power.pm02-5v.7w-mode — observed 17.20 W (draw mode: 7W), expected <= 15.60 W (Δ +1.60 W (+10%))
168
168
  … (same rail, Jetson in its 7 W mode: the radio's transmit peak still tips it over)
169
- not scored devices/dev.camera.oak-d-lite/physical/mass — observed mass not declared, expected a positive mass with units
169
+ not scored budgets/bud.mass.payload — observed mass unknown for dev.companion.jetson-orin-nano, dev.camera.oak-d-lite, dev.magnetometer.ist8310; total not scored, expected every device and link in this budget to declare a mass
170
170
  … (more not-scored lines: things the lockfile does not declare yet, listed, never hidden)
171
- scored 10: 8 add up, 2 do not · 9 not scored
171
+ scored 9: 7 add up, 2 do not · 10 not scored
172
172
 
173
173
  NO-GO — 2 violation(s), 0 warning(s)
174
174
  $ echo $?
@@ -185,7 +185,7 @@ integration of published parts, and the two vendors' own numbers still do not ad
185
185
  Nobody printed "this build is broken". Nobody printed "5 A" either: that is 25 W / 5 V, a
186
186
  quotient of figures published on different pages. The records disagree with each other, and
187
187
  you find out before you apply power. Give the Jetson its own regulator
188
- (`tests/fixtures/x500/contract_corrected.yaml`) and the same check passes.
188
+ (`tests/fixtures/x500/contract_corrected.yaml`) and the same check passes: `GO — records agree (10 scored); 10 check(s) not scored`.
189
189
 
190
190
  ## Your own robot
191
191
 
@@ -209,7 +209,9 @@ Any field you hand-edited (including `robot.name`) is silently overwritten back
209
209
  | `start` | the contract was written (or an existing one kept); no page is written | the URDF/SDF could not be parsed, or the contract could not be read or written | — | — |
210
210
 
211
211
  `NOT SCORED` (exit 3) means no comparison had both operands: it is not a pass and not a failure.
212
+
212
213
  The `scored N: …` line above the verdict is where a script should look.
214
+ `lockwire check --strict` makes a `GO with notes` run exit 1 (the `NO-GO` code); the table does not change.
213
215
 
214
216
  ## In CI
215
217
 
@@ -220,7 +222,7 @@ The `scored N: …` line above the verdict is where a script should look.
220
222
  contract: lockwire.yaml
221
223
  ```
222
224
 
223
- Inputs: `contract` (default `lockwire.yaml`), `version` (pin a PyPI release; default installs the action's own code), `python-version` (default 3.12).
225
+ Inputs: `contract` (default `lockwire.yaml`), `version` (pin a PyPI release; default installs the action's own code), `strict` (default `false`; `true` passes `--strict`), `python-version` (default 3.12).
224
226
  Or just `pipx install lockwire && lockwire check lockwire.yaml`.
225
227
 
226
228
  ## Pre-commit
@@ -228,7 +230,7 @@ Or just `pipx install lockwire && lockwire check lockwire.yaml`.
228
230
  ```yaml
229
231
  repos:
230
232
  - repo: https://github.com/Shivansh2703/lockwire
231
- rev: v0.2.0
233
+ rev: v0.2.3
232
234
  hooks:
233
235
  - id: lockwire-check
234
236
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lockwire"
3
- version = "0.2.3"
3
+ version = "0.2.4"
4
4
  description = "The physical-assumption contract for robots: checks that a robot's declared parts (power, weight, bus, torque) add up"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -26,13 +26,15 @@ dependencies = [
26
26
  "rich>=15.0.0",
27
27
  "ruamel-yaml>=0.19.1",
28
28
  "typer>=0.26.8",
29
+ "xacro>=2.1.1",
29
30
  ]
30
31
 
31
32
  [project.optional-dependencies]
32
33
  # Serving lockwire over MCP to an agent. Optional for the same reason as `ros2bag`:
33
34
  # a plain install must stay lean, and the SDK is only needed by `lockwire mcp`.
34
35
  mcp = [
35
- "mcp>=2.1.1",
36
+ "mcp>=2.2.0",
37
+ "pyjwt>=2.15", # direct floor: a fresh lock must not resolve below the fixed release
36
38
  ]
37
39
  # The Electron app's sidecar: `lockwire serve` over local HTTP. Optional for the
38
40
  # same reason as `desktop`/`ros2bag`/`mcp` — a plain CLI install stays lean; only the
@@ -59,7 +61,7 @@ build-backend = "uv_build"
59
61
 
60
62
  [dependency-groups]
61
63
  dev = [
62
- "mcp>=2.1.1",
64
+ "mcp>=2.2.0",
63
65
  # tests/test_serve.py needs the sidecar (`lockwire serve`) importable and
64
66
  # runnable as a subprocess. Same reasoning as `mcp`/`mcap` above: CI's
65
67
  # `test` job runs a plain `uv sync --dev`, so without these here that
@@ -1,6 +1,6 @@
1
1
  """Lockwire — the physical-assumption contract for robots, enforced in CI and at preflight."""
2
2
 
3
- __version__ = "0.2.3"
3
+ __version__ = "0.2.4"
4
4
 
5
5
 
6
6
  def main() -> None:
@@ -105,6 +105,24 @@ def start(
105
105
  return start_contract(urdf, overwrite=overwrite, expected_revision=expected_revision)
106
106
 
107
107
 
108
+ def from_parts(
109
+ text: str, *, out: Path | str | None = None, catalog: Path | str | None = None
110
+ ) -> dict[str, Any]:
111
+ """Match a pasted parts list to the catalog; with ``out``, also write a draft lockfile.
112
+
113
+ Information, not a gate: a mismatch is listed, never refused. ``parts_list.match_parts``.
114
+ """
115
+ from lockwire.parts_list import match_parts, write_draft
116
+
117
+ if not isinstance(text, str): # same plain ValueError the neighbouring functions raise
118
+ raise ValueError("from_parts needs the parts list as text")
119
+ document = match_parts(text, catalog)
120
+ if out is not None and not Path(out).exists():
121
+ write_draft(document, Path(out), catalog)
122
+ document["written"] = str(out)
123
+ return document
124
+
125
+
108
126
  def api_schema(out: Path | str) -> None:
109
127
  """Write the payload union's JSON schema — ``api_payload.export_openapi``.
110
128
 
@@ -249,6 +267,7 @@ __all__ = [
249
267
  "FUNCTIONS",
250
268
  "add",
251
269
  "api_schema",
270
+ "from_parts",
252
271
  "api_types",
253
272
  "blame",
254
273
  "what_if",
@@ -262,6 +281,7 @@ __all__ = [
262
281
  "mount_shape",
263
282
  "move",
264
283
  "log",
284
+ "place",
265
285
  "plan",
266
286
  "reparent",
267
287
  "review",