benchweave-sdk 0.0.1__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.
- benchweave_sdk-0.0.1/.gitignore +17 -0
- benchweave_sdk-0.0.1/LICENSE +21 -0
- benchweave_sdk-0.0.1/PKG-INFO +192 -0
- benchweave_sdk-0.0.1/README.md +174 -0
- benchweave_sdk-0.0.1/hatch_build.py +74 -0
- benchweave_sdk-0.0.1/pyproject.toml +55 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/__init__.py +8 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/cli.py +361 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/conformance.py +63 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/console.py +57 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/fixtures.py +289 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/interfaces.py +51 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/packaging.py +61 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/presentation.py +404 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/preview_assets/inventory.json +21 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/preview_assets/site/assets/index-BqP_aR5c.css +1 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/preview_assets/site/assets/index-D0qT06Ho.js +28 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/preview_assets/site/index.html +14 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/preview_models.py +103 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/preview_server.py +305 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/preview_tui.py +69 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/py.typed +1 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/scaffold.py +430 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/_GENERATED.txt +12 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/bench.schema.json +476 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/commissioning.schema.json +277 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/examples/bench.json +158 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/examples/commissioning.json +158 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/examples/procedure.json +101 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/examples/run-binding.json +38 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/examples/run-record.json +25 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/examples/safety-policy.json +110 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/procedure.schema.json +498 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/run-binding.schema.json +176 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/run-record.schema.json +321 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/execution/execution-v1.0.0/safety-policy.schema.json +511 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/interface/_GENERATED.txt +6 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/interface/interface-v1.1.1/examples/mcp-start-exchange.json +63 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/interface/interface-v1.1.1/examples/operation-vectors.json +393 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/interface/interface-v1.1.1/interface.schema.json +1974 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/interface/interface-v1.1.1/mcp-tools.json +25151 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/interface/interface-v1.1.1/openapi.json +3584 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/interface/interface-v1.1.1/operation-catalog.json +1448 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/_GENERATED.txt +24 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/device-profile-catalog.json +4798 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/device-profile-catalog.schema.json +110 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-action-vectors.json +2678 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-daq.json +177 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-dc_psu.json +159 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-dmm.json +144 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-electronic_load.json +159 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-embedded_controller.json +186 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-function_generator.json +155 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-logic_analyser.json +188 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-oscilloscope.json +177 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-smu.json +214 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-spectrum_analyser.json +177 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-switch_matrix.json +154 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/class-vna.json +177 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/measurement-vectors.json +1089 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/reference-can.json +108 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/reference-capture.json +114 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/reference-controller.json +150 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/reference-psu.json +187 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/examples/reference-vectors.json +259 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/otdp-device-descriptor.schema.json +3003 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/otdp-measurement.schema.json +787 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/otdp/otdp-v0.3.0/otdp-runtime.schema.json +1238 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui/_GENERATED.txt +5 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui/contracts.py +573 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui/plugin-ui-v0.1.0/binding-catalogue.schema.json +6 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui/plugin-ui-v0.1.0/configuration-preset.schema.json +6 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui/plugin-ui-v0.1.0/presentation-envelope.schema.json +6 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui/plugin-ui-v0.1.0/ui-manifest.schema.json +69 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui-preview/_GENERATED.txt +2 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui-preview/plugin-ui-preview-v1/fixture.schema.json +94 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/plugin-ui-preview/plugin-ui-preview-v1/preview-document.schema.json +209 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/registry/_GENERATED.txt +6 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/registry/registry-v1.0.0/examples/package-lock.json +32 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/registry/registry-v1.0.0/examples/release-manifest.json +170 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/registry/registry-v1.0.0/examples/release-status.json +18 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/registry/registry-v1.0.0/package-lock.schema.json +116 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/registry/registry-v1.0.0/release-manifest.schema.json +721 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards/registry/registry-v1.0.0/release-status.schema.json +190 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/standards_sync.py +270 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/testing.py +106 -0
- benchweave_sdk-0.0.1/src/benchweave_sdk/validation.py +102 -0
- benchweave_sdk-0.0.1/standards-lock.json +1 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
.gortex/
|
|
3
|
+
venv/
|
|
4
|
+
uv.lock
|
|
5
|
+
docs/superpowers/
|
|
6
|
+
|
|
7
|
+
# Memory-proposal ledger: session findings queued for the vault (see .claude/memory-protocol.md)
|
|
8
|
+
.claude/memory-proposals.jsonl
|
|
9
|
+
.claude/memory-proposals.jsonl.tmp
|
|
10
|
+
.claude/memory-proposals.drained.jsonl
|
|
11
|
+
.claude/memory-proposals.deadletter.jsonl
|
|
12
|
+
.claude/memory-proposals.lock
|
|
13
|
+
.claude/memory-drain-receipt.json
|
|
14
|
+
.claude/memory-drain-receipt.json.tmp
|
|
15
|
+
.claude/memory-drain-receipts.jsonl
|
|
16
|
+
.claude/memory-drain-receipts.jsonl.tmp
|
|
17
|
+
.claude/settings.local.json
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 madeinoz67
|
|
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.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: benchweave-sdk
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Offline authoring and conformance tools for BenchWeave OTDP device plugins.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Requires-Dist: click<9,>=8.3
|
|
9
|
+
Requires-Dist: jsonschema>=4.23
|
|
10
|
+
Requires-Dist: referencing>=0.35
|
|
11
|
+
Requires-Dist: rfc3339-validator>=0.1.4
|
|
12
|
+
Requires-Dist: rfc3987>=1.3.8
|
|
13
|
+
Requires-Dist: rich<15,>=14
|
|
14
|
+
Requires-Dist: textual<9,>=8.2
|
|
15
|
+
Provides-Extra: test
|
|
16
|
+
Requires-Dist: pytest>=8.0; extra == 'test'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
# BenchWeave plugin developer SDK
|
|
22
|
+
|
|
23
|
+
Build an external device plugin without importing gateway internals. Python 3.13+, SDK 0.0.1, OTDP 0.3.0 and adapter API 1.1 are the initial baseline. This package is a separate wheel built alongside BenchWeave; published to PyPI as benchweave-sdk.
|
|
24
|
+
|
|
25
|
+
## Sister repository
|
|
26
|
+
|
|
27
|
+
This is the SDK. The BenchWeave gateway and the canonical architecture and contract standards live in the main repository: [madeinoz67/benchweave](https://github.com/madeinoz67/benchweave). This SDK is mounted there at `packages/sdk` as a git submodule and has its own CI and release cycle.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
Stable releases are on PyPI:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pip install benchweave-sdk
|
|
35
|
+
# or as an isolated CLI tool
|
|
36
|
+
uv tool install benchweave-sdk
|
|
37
|
+
# or via Homebrew (macOS and Linux)
|
|
38
|
+
brew install madeinoz67/tap/benchweave-sdk
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For bleeding-edge work before a release, install straight from the default branch:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
pip install git+https://github.com/madeinoz67/benchweave-sdk.git
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
To hack on the SDK itself, clone the repository and `uv sync --extra test`.
|
|
48
|
+
|
|
49
|
+
## Five steps
|
|
50
|
+
|
|
51
|
+
1. Install the SDK from PyPI (`uv pip install benchweave-sdk`, or see [Installation](#installation)).
|
|
52
|
+
2. Run `benchweave-sdk new plugins/acme/model100 --package benchweave_acme_model100`. Replace `acme/model100` with your manufacturer/device name; the independent project contains `src/benchweave_acme_model100/` and `tests/`.
|
|
53
|
+
3. Change into the generated project (`cd plugins/acme/model100`). Replace the explicitly synthetic protocol with verified device behaviour, then update its descriptor. The generated AI-GUIDE.md describes the design, build, test, review and release steps.
|
|
54
|
+
4. Install the plugin with test dependencies, run its tests, and run `benchweave-sdk check src/benchweave_acme_model100/descriptor.json`. Record all applicable S01–S18, C01–C12 and M01–M14 obligations and evidence; basic SDK checks do not cover all of them.
|
|
55
|
+
5. Build with `uv build`, prepare registry metadata and reviewed evidence, and approve the release before publication or hardware qualification. `benchweave-sdk inventory` helps generate hashes, not a complete registry manifest.
|
|
56
|
+
|
|
57
|
+
The generated runtime has no dependency on this SDK. The plugin test extra pins the SDK version from PyPI. Generate and retain a plugin dependency lock in its repository. A template is not qualified firmware or a real instrument driver.
|
|
58
|
+
|
|
59
|
+
## Directory structure
|
|
60
|
+
|
|
61
|
+
In a plugin collection, each device plugin is an independent project at `plugins/<manufacturer>/<name>/`. The SDK itself stays in `packages/sdk/`. An external plugin repository can use that device project as its repository root; it does not need the enclosing `plugins/<manufacturer>/` directories. The project directory and Python import package have different roles: `acme/model100` organises the collection, while `benchweave_acme_model100` is the import name selected by `--package`.
|
|
62
|
+
|
|
63
|
+
Create a project from the collection root:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
benchweave-sdk new plugins/acme/model100 --package benchweave_acme_model100 --with-ui
|
|
67
|
+
cd plugins/acme/model100
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The destination must not already exist. Omit `--with-ui` for a plugin without presentation metadata.
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
plugins/acme/model100/ # independent plugin project
|
|
74
|
+
├── pyproject.toml # build configuration and test dependencies
|
|
75
|
+
├── README.md
|
|
76
|
+
├── AI-GUIDE.md # generated development workflow
|
|
77
|
+
├── UI-GUIDE.md # generated only with --with-ui
|
|
78
|
+
├── docs/ # author-supplied device documentation
|
|
79
|
+
│ ├── compatibility.md # supported models/firmware and limitations
|
|
80
|
+
│ └── qualification.md # supervised hardware test plan/evidence
|
|
81
|
+
├── firmware/ # optional author-supplied firmware material
|
|
82
|
+
│ ├── README.md # official sources, versions and checksums
|
|
83
|
+
│ └── release-notes/ # relevant vendor changes and upgrade constraints
|
|
84
|
+
├── src/
|
|
85
|
+
│ └── benchweave_acme_model100/ # import package; included in the wheel
|
|
86
|
+
│ ├── __init__.py
|
|
87
|
+
│ ├── adapter.py # SDK-facing adapter and create_plugin
|
|
88
|
+
│ ├── protocol.py # device protocol implementation
|
|
89
|
+
│ ├── descriptor.json # OTDP device/operation contract
|
|
90
|
+
│ ├── protocol.md # protocol evidence, initially synthetic
|
|
91
|
+
│ ├── vectors.json # exact exchanges, initially synthetic
|
|
92
|
+
│ ├── config/ # optional configuration for a headless plugin
|
|
93
|
+
│ │ ├── settings.schema.json # author-supplied complete-settings schema
|
|
94
|
+
│ │ └── presets/
|
|
95
|
+
│ │ └── default.json # author-supplied configuration preset
|
|
96
|
+
│ ├── presentation.json # --with-ui: envelope and resource root
|
|
97
|
+
│ ├── binding-catalogue.json # --with-ui: descriptor-aligned targets
|
|
98
|
+
│ └── ui/ # --with-ui: presentation resource root
|
|
99
|
+
│ ├── manifest.json # generated readings page; no plot required
|
|
100
|
+
│ ├── fixtures/ # generated synthetic preview examples
|
|
101
|
+
│ │ ├── normal.json
|
|
102
|
+
│ │ └── warning.json
|
|
103
|
+
│ ├── settings/ # author-supplied, when configuration exists
|
|
104
|
+
│ │ └── settings.schema.json
|
|
105
|
+
│ ├── presets/ # author-supplied complete configurations
|
|
106
|
+
│ │ └── default.json
|
|
107
|
+
│ └── assets/ # author-supplied declared static resources
|
|
108
|
+
└── tests/
|
|
109
|
+
├── test_plugin.py # generated mock/conformance tests
|
|
110
|
+
├── test_presentation_preview.py # generated offline preview conformance test
|
|
111
|
+
├── test_configuration.py # author-supplied schema/preset checks
|
|
112
|
+
├── test_presentation.py # author-supplied UI binding/asset checks
|
|
113
|
+
└── fixtures/ # author-supplied exchanges by model/firmware
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The SDK generates `ui/manifest.json`, schema-valid synthetic examples under `ui/fixtures/`, and `tests/test_presentation_preview.py`. The `docs/`, `firmware/`, `config/`, optional UI asset directories and additional test files remain author-supplied extensions. Add only the features the device plugin supports. `default.json` is an example filename, not an automatically selected or applied configuration.
|
|
117
|
+
|
|
118
|
+
| Optional feature | Recommended location | Behaviour and ownership |
|
|
119
|
+
| --- | --- | --- |
|
|
120
|
+
| Device configuration without UI | `src/<package>/config/settings.schema.json` and `config/presets/` | Validate complete settings offline with `check-preset`; application requires an approved device procedure. |
|
|
121
|
+
| Device configuration shown in UI | `src/<package>/ui/settings/` and `ui/presets/` | Keep one authoritative copy inside the UI resource root and declare it in the manifest; do not duplicate it under `config/`. |
|
|
122
|
+
| Specialised pages and optional graphs | `src/<package>/ui/manifest.json` and `ui/assets/` | Declare bindings, plot metadata and supported panel IDs. A page need not have a graph. Browser rendering remains separate work. |
|
|
123
|
+
| Data collection | Descriptor action/measurement contracts, adapter code and `tests/fixtures/` | Declare supported acquisition and dataset bindings. The gateway owns execution and retained data; live datasets are not packaged here. |
|
|
124
|
+
| Firmware compatibility | `docs/compatibility.md`, descriptor firmware constraints and firmware-specific test fixtures | State supported firmware versions and retain evidence. The descriptor remains the runtime compatibility contract. |
|
|
125
|
+
| Firmware reference material | `firmware/README.md` and `firmware/release-notes/` | Record vendor sources, exact version/checksum information and upgrade constraints. This directory has no SDK discovery or flashing behaviour. Redistribute vendor images only when permitted and explicitly required by the release. |
|
|
126
|
+
| Hardware qualification | `docs/qualification.md` | Record the supervised test plan, tested versions and evidence. Synthetic tests do not establish hardware qualification. |
|
|
127
|
+
|
|
128
|
+
A preset contains complete settings and compatibility/provenance metadata, not collected measurements. A plugin can have configuration without UI, UI without configuration, and firmware compatibility declarations without shipping firmware images.
|
|
129
|
+
|
|
130
|
+
Keep distributable resources inside `src/<package>/` so the generated Hatch wheel configuration includes them. Generate and retain `uv.lock` for development dependencies, and supply the appropriate licence and release evidence before distribution; these are not scaffolded. Build outputs belong in `dist/`. Gateway configuration, credentials, live readings and retained datasets belong to the deployment/runtime stores, outside the plugin source package.
|
|
131
|
+
|
|
132
|
+
### Path resolution and validation
|
|
133
|
+
|
|
134
|
+
Run these commands from the plugin project root after installing its development dependencies:
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
benchweave-sdk check src/benchweave_acme_model100/descriptor.json
|
|
138
|
+
benchweave-sdk check-ui src/benchweave_acme_model100/presentation.json \
|
|
139
|
+
--descriptor src/benchweave_acme_model100/descriptor.json \
|
|
140
|
+
--resources src/benchweave_acme_model100 \
|
|
141
|
+
--catalogue src/benchweave_acme_model100/binding-catalogue.json \
|
|
142
|
+
--firmware 1.0.0
|
|
143
|
+
pytest
|
|
144
|
+
uv build
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Here `--resources` points to the **package root**. The generated envelope's `resource_root: "ui"` selects its `ui/` subdirectory. Manifest asset paths such as `settings/settings.schema.json` and `presets/default.json` are relative to that UI root. Resource paths must remain inside the root and cannot traverse symlinks; use canonical local paths. On macOS, use `/private/tmp/...` rather than the `/tmp` symlink for temporary preview projects. Keep descriptor, manifest and asset byte hashes current after editing resources. The binding catalogue must be aligned with the descriptor and verified by the host during admission; a packaged candidate catalogue does not grant device capabilities.
|
|
148
|
+
|
|
149
|
+
## Optional plugin pages and presets
|
|
150
|
+
|
|
151
|
+
Add `--with-ui` to `benchweave-sdk new` to generate a declarative readings page, presentation envelope and binding catalogue. The default scaffold stays unchanged. Keep presentation assets under the import package's `ui/` directory so wheels carry them; store complete configuration presets alongside their settings schema. Plugins can declare configuration, readings, dataset and registered panel pages, with plots only when appropriate.
|
|
152
|
+
|
|
153
|
+
Use `benchweave-sdk check-ui` and `benchweave-sdk check-preset` for offline validation before packaging. Validation neither admits a plugin nor approves applying settings. The [plugin presentation guide](https://github.com/madeinoz67/benchweave/blob/main/docs/plugin-ui-v0.1.0/README.md) (main repository) covers the directory structure, preconfigured settings, optional graphs, data bindings and CLI examples.
|
|
154
|
+
|
|
155
|
+
### Local UI preview
|
|
156
|
+
|
|
157
|
+
The SDK includes the version-matched React renderer and nine deterministic baseline scenarios. Preview author fixtures without importing plugin Python, opening a device transport or contacting a gateway:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
benchweave-sdk preview-ui src/benchweave_acme_model100/presentation.json \
|
|
161
|
+
--descriptor src/benchweave_acme_model100/descriptor.json \
|
|
162
|
+
--resources src/benchweave_acme_model100 \
|
|
163
|
+
--catalogue src/benchweave_acme_model100/binding-catalogue.json \
|
|
164
|
+
--fixtures src/benchweave_acme_model100/ui/fixtures
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Use `--no-open` for CI or a terminal-only readiness check. The default listener is an ephemeral port on `127.0.0.1`; wildcard listeners are rejected. A non-loopback host requires `--allow-network` and remains unsuitable for shared or production deployment. UI contributors can point at a compatible Vite renderer with `--renderer-url`; the CLI adds the preview server URL as the renderer's `apiBase` query parameter and permits cross-origin API responses only for that renderer's exact origin. Both renderers require preview API version 1.
|
|
168
|
+
|
|
169
|
+
The command suite uses Click for stable parsing and exit codes and Rich for readable non-interactive output. In an interactive terminal, `preview-ui` uses a Textual status screen: press `o` to reopen the browser and `q` to stop the preview. Textual is deliberately bypassed for `--no-open` and non-terminal output so CI, pipes and SDK tests remain deterministic.
|
|
170
|
+
|
|
171
|
+
Every preview is labelled `SIMULATED PRESENTATION DATA`. Control interactions create only in-memory simulated receipts and never update observed readings optimistically. Preview success is not admission, hardware qualification or permission to operate equipment.
|
|
172
|
+
|
|
173
|
+
## Public surfaces
|
|
174
|
+
|
|
175
|
+
- `interfaces`: structural async `Adapter`, `HostServices`, `OperationContext` and optional `CaptureServices` definitions. No SDK superclass is required.
|
|
176
|
+
- `testing`: deterministic `MockContext` and `MockHost`, exact scripted transfers, dispatch markers, cancellation and a manually advanced clock. These are test doubles, not qualified host services.
|
|
177
|
+
- `validation`: pinned local schemas, strict finite JSON, format validation, runtime correlation and basic descriptor S01/S02 checks. Unresolved schema references fail without network retrieval.
|
|
178
|
+
- `conformance`: reusable operation and quiet lifecycle checks, with configurable wall-clock timeouts for cooperative async calls. Authors must add device-specific failure, profile and measurement tests. Use process isolation for blocking code or code that suppresses cancellation.
|
|
179
|
+
- `presentation`: bounded offline validation of presentation resources and complete configuration presets, using the same validator bytes as the gateway.
|
|
180
|
+
- `packaging`: inventory and integrity checks for a prepared bundle, including duplicate/path/symlink rejection. No installation, signing, publication or dependency execution.
|
|
181
|
+
|
|
182
|
+
## Compatibility and limits
|
|
183
|
+
|
|
184
|
+
The gateway has an explicit OTDP bridge and loader for identify, scalar read and scalar write. Profile actions, capture and streaming are not implemented by that bridge. Package-relative and standard-library imports are supported; arbitrary third-party runtime dependencies need further integration. Existing simulator interfaces remain private. Release CI builds an SDK and external plugin outside the checkout and exercises that plugin through the gateway bridge using mock transport. Unsupported operations must fail explicitly. No live install endpoint, physical backend, container device permissions or hardware qualification is supplied by this SDK.
|
|
185
|
+
|
|
186
|
+
The SDK sdist and wheel include the canonical OTDP, registry and plugin presentation contract sets. Build from the repository with `uv build packages/sdk`; the build hook includes contract resources and a wheel rebuilt from the sdist remains self-contained. The release smoke compares installed contract bytes with the canonical repository copies. SDK and gateway versions are independently named; each release must record the exact pair tested before expanding compatibility claims.
|
|
187
|
+
|
|
188
|
+
Pure Python plugins still need declared dependencies and compatible runtimes. Native dependencies and physical transport mappings require a separately qualified gateway deployment. The SDK does not install dependencies into a running gateway.
|
|
189
|
+
|
|
190
|
+
## Distribution rights
|
|
191
|
+
|
|
192
|
+
The SDK package is distributed under the [MIT licence](LICENSE). The vendored OTDP, registry and presentation contract sets and the generated templates are part of this package and carry the same grant. Plugin authors choose their own licence; generated examples contain no licence grant and make no claim on plugin code written with them.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# BenchWeave plugin developer SDK
|
|
4
|
+
|
|
5
|
+
Build an external device plugin without importing gateway internals. Python 3.13+, SDK 0.0.1, OTDP 0.3.0 and adapter API 1.1 are the initial baseline. This package is a separate wheel built alongside BenchWeave; published to PyPI as benchweave-sdk.
|
|
6
|
+
|
|
7
|
+
## Sister repository
|
|
8
|
+
|
|
9
|
+
This is the SDK. The BenchWeave gateway and the canonical architecture and contract standards live in the main repository: [madeinoz67/benchweave](https://github.com/madeinoz67/benchweave). This SDK is mounted there at `packages/sdk` as a git submodule and has its own CI and release cycle.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Stable releases are on PyPI:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pip install benchweave-sdk
|
|
17
|
+
# or as an isolated CLI tool
|
|
18
|
+
uv tool install benchweave-sdk
|
|
19
|
+
# or via Homebrew (macOS and Linux)
|
|
20
|
+
brew install madeinoz67/tap/benchweave-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For bleeding-edge work before a release, install straight from the default branch:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pip install git+https://github.com/madeinoz67/benchweave-sdk.git
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To hack on the SDK itself, clone the repository and `uv sync --extra test`.
|
|
30
|
+
|
|
31
|
+
## Five steps
|
|
32
|
+
|
|
33
|
+
1. Install the SDK from PyPI (`uv pip install benchweave-sdk`, or see [Installation](#installation)).
|
|
34
|
+
2. Run `benchweave-sdk new plugins/acme/model100 --package benchweave_acme_model100`. Replace `acme/model100` with your manufacturer/device name; the independent project contains `src/benchweave_acme_model100/` and `tests/`.
|
|
35
|
+
3. Change into the generated project (`cd plugins/acme/model100`). Replace the explicitly synthetic protocol with verified device behaviour, then update its descriptor. The generated AI-GUIDE.md describes the design, build, test, review and release steps.
|
|
36
|
+
4. Install the plugin with test dependencies, run its tests, and run `benchweave-sdk check src/benchweave_acme_model100/descriptor.json`. Record all applicable S01–S18, C01–C12 and M01–M14 obligations and evidence; basic SDK checks do not cover all of them.
|
|
37
|
+
5. Build with `uv build`, prepare registry metadata and reviewed evidence, and approve the release before publication or hardware qualification. `benchweave-sdk inventory` helps generate hashes, not a complete registry manifest.
|
|
38
|
+
|
|
39
|
+
The generated runtime has no dependency on this SDK. The plugin test extra pins the SDK version from PyPI. Generate and retain a plugin dependency lock in its repository. A template is not qualified firmware or a real instrument driver.
|
|
40
|
+
|
|
41
|
+
## Directory structure
|
|
42
|
+
|
|
43
|
+
In a plugin collection, each device plugin is an independent project at `plugins/<manufacturer>/<name>/`. The SDK itself stays in `packages/sdk/`. An external plugin repository can use that device project as its repository root; it does not need the enclosing `plugins/<manufacturer>/` directories. The project directory and Python import package have different roles: `acme/model100` organises the collection, while `benchweave_acme_model100` is the import name selected by `--package`.
|
|
44
|
+
|
|
45
|
+
Create a project from the collection root:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
benchweave-sdk new plugins/acme/model100 --package benchweave_acme_model100 --with-ui
|
|
49
|
+
cd plugins/acme/model100
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The destination must not already exist. Omit `--with-ui` for a plugin without presentation metadata.
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
plugins/acme/model100/ # independent plugin project
|
|
56
|
+
├── pyproject.toml # build configuration and test dependencies
|
|
57
|
+
├── README.md
|
|
58
|
+
├── AI-GUIDE.md # generated development workflow
|
|
59
|
+
├── UI-GUIDE.md # generated only with --with-ui
|
|
60
|
+
├── docs/ # author-supplied device documentation
|
|
61
|
+
│ ├── compatibility.md # supported models/firmware and limitations
|
|
62
|
+
│ └── qualification.md # supervised hardware test plan/evidence
|
|
63
|
+
├── firmware/ # optional author-supplied firmware material
|
|
64
|
+
│ ├── README.md # official sources, versions and checksums
|
|
65
|
+
│ └── release-notes/ # relevant vendor changes and upgrade constraints
|
|
66
|
+
├── src/
|
|
67
|
+
│ └── benchweave_acme_model100/ # import package; included in the wheel
|
|
68
|
+
│ ├── __init__.py
|
|
69
|
+
│ ├── adapter.py # SDK-facing adapter and create_plugin
|
|
70
|
+
│ ├── protocol.py # device protocol implementation
|
|
71
|
+
│ ├── descriptor.json # OTDP device/operation contract
|
|
72
|
+
│ ├── protocol.md # protocol evidence, initially synthetic
|
|
73
|
+
│ ├── vectors.json # exact exchanges, initially synthetic
|
|
74
|
+
│ ├── config/ # optional configuration for a headless plugin
|
|
75
|
+
│ │ ├── settings.schema.json # author-supplied complete-settings schema
|
|
76
|
+
│ │ └── presets/
|
|
77
|
+
│ │ └── default.json # author-supplied configuration preset
|
|
78
|
+
│ ├── presentation.json # --with-ui: envelope and resource root
|
|
79
|
+
│ ├── binding-catalogue.json # --with-ui: descriptor-aligned targets
|
|
80
|
+
│ └── ui/ # --with-ui: presentation resource root
|
|
81
|
+
│ ├── manifest.json # generated readings page; no plot required
|
|
82
|
+
│ ├── fixtures/ # generated synthetic preview examples
|
|
83
|
+
│ │ ├── normal.json
|
|
84
|
+
│ │ └── warning.json
|
|
85
|
+
│ ├── settings/ # author-supplied, when configuration exists
|
|
86
|
+
│ │ └── settings.schema.json
|
|
87
|
+
│ ├── presets/ # author-supplied complete configurations
|
|
88
|
+
│ │ └── default.json
|
|
89
|
+
│ └── assets/ # author-supplied declared static resources
|
|
90
|
+
└── tests/
|
|
91
|
+
├── test_plugin.py # generated mock/conformance tests
|
|
92
|
+
├── test_presentation_preview.py # generated offline preview conformance test
|
|
93
|
+
├── test_configuration.py # author-supplied schema/preset checks
|
|
94
|
+
├── test_presentation.py # author-supplied UI binding/asset checks
|
|
95
|
+
└── fixtures/ # author-supplied exchanges by model/firmware
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The SDK generates `ui/manifest.json`, schema-valid synthetic examples under `ui/fixtures/`, and `tests/test_presentation_preview.py`. The `docs/`, `firmware/`, `config/`, optional UI asset directories and additional test files remain author-supplied extensions. Add only the features the device plugin supports. `default.json` is an example filename, not an automatically selected or applied configuration.
|
|
99
|
+
|
|
100
|
+
| Optional feature | Recommended location | Behaviour and ownership |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| Device configuration without UI | `src/<package>/config/settings.schema.json` and `config/presets/` | Validate complete settings offline with `check-preset`; application requires an approved device procedure. |
|
|
103
|
+
| Device configuration shown in UI | `src/<package>/ui/settings/` and `ui/presets/` | Keep one authoritative copy inside the UI resource root and declare it in the manifest; do not duplicate it under `config/`. |
|
|
104
|
+
| Specialised pages and optional graphs | `src/<package>/ui/manifest.json` and `ui/assets/` | Declare bindings, plot metadata and supported panel IDs. A page need not have a graph. Browser rendering remains separate work. |
|
|
105
|
+
| Data collection | Descriptor action/measurement contracts, adapter code and `tests/fixtures/` | Declare supported acquisition and dataset bindings. The gateway owns execution and retained data; live datasets are not packaged here. |
|
|
106
|
+
| Firmware compatibility | `docs/compatibility.md`, descriptor firmware constraints and firmware-specific test fixtures | State supported firmware versions and retain evidence. The descriptor remains the runtime compatibility contract. |
|
|
107
|
+
| Firmware reference material | `firmware/README.md` and `firmware/release-notes/` | Record vendor sources, exact version/checksum information and upgrade constraints. This directory has no SDK discovery or flashing behaviour. Redistribute vendor images only when permitted and explicitly required by the release. |
|
|
108
|
+
| Hardware qualification | `docs/qualification.md` | Record the supervised test plan, tested versions and evidence. Synthetic tests do not establish hardware qualification. |
|
|
109
|
+
|
|
110
|
+
A preset contains complete settings and compatibility/provenance metadata, not collected measurements. A plugin can have configuration without UI, UI without configuration, and firmware compatibility declarations without shipping firmware images.
|
|
111
|
+
|
|
112
|
+
Keep distributable resources inside `src/<package>/` so the generated Hatch wheel configuration includes them. Generate and retain `uv.lock` for development dependencies, and supply the appropriate licence and release evidence before distribution; these are not scaffolded. Build outputs belong in `dist/`. Gateway configuration, credentials, live readings and retained datasets belong to the deployment/runtime stores, outside the plugin source package.
|
|
113
|
+
|
|
114
|
+
### Path resolution and validation
|
|
115
|
+
|
|
116
|
+
Run these commands from the plugin project root after installing its development dependencies:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
benchweave-sdk check src/benchweave_acme_model100/descriptor.json
|
|
120
|
+
benchweave-sdk check-ui src/benchweave_acme_model100/presentation.json \
|
|
121
|
+
--descriptor src/benchweave_acme_model100/descriptor.json \
|
|
122
|
+
--resources src/benchweave_acme_model100 \
|
|
123
|
+
--catalogue src/benchweave_acme_model100/binding-catalogue.json \
|
|
124
|
+
--firmware 1.0.0
|
|
125
|
+
pytest
|
|
126
|
+
uv build
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Here `--resources` points to the **package root**. The generated envelope's `resource_root: "ui"` selects its `ui/` subdirectory. Manifest asset paths such as `settings/settings.schema.json` and `presets/default.json` are relative to that UI root. Resource paths must remain inside the root and cannot traverse symlinks; use canonical local paths. On macOS, use `/private/tmp/...` rather than the `/tmp` symlink for temporary preview projects. Keep descriptor, manifest and asset byte hashes current after editing resources. The binding catalogue must be aligned with the descriptor and verified by the host during admission; a packaged candidate catalogue does not grant device capabilities.
|
|
130
|
+
|
|
131
|
+
## Optional plugin pages and presets
|
|
132
|
+
|
|
133
|
+
Add `--with-ui` to `benchweave-sdk new` to generate a declarative readings page, presentation envelope and binding catalogue. The default scaffold stays unchanged. Keep presentation assets under the import package's `ui/` directory so wheels carry them; store complete configuration presets alongside their settings schema. Plugins can declare configuration, readings, dataset and registered panel pages, with plots only when appropriate.
|
|
134
|
+
|
|
135
|
+
Use `benchweave-sdk check-ui` and `benchweave-sdk check-preset` for offline validation before packaging. Validation neither admits a plugin nor approves applying settings. The [plugin presentation guide](https://github.com/madeinoz67/benchweave/blob/main/docs/plugin-ui-v0.1.0/README.md) (main repository) covers the directory structure, preconfigured settings, optional graphs, data bindings and CLI examples.
|
|
136
|
+
|
|
137
|
+
### Local UI preview
|
|
138
|
+
|
|
139
|
+
The SDK includes the version-matched React renderer and nine deterministic baseline scenarios. Preview author fixtures without importing plugin Python, opening a device transport or contacting a gateway:
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
benchweave-sdk preview-ui src/benchweave_acme_model100/presentation.json \
|
|
143
|
+
--descriptor src/benchweave_acme_model100/descriptor.json \
|
|
144
|
+
--resources src/benchweave_acme_model100 \
|
|
145
|
+
--catalogue src/benchweave_acme_model100/binding-catalogue.json \
|
|
146
|
+
--fixtures src/benchweave_acme_model100/ui/fixtures
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Use `--no-open` for CI or a terminal-only readiness check. The default listener is an ephemeral port on `127.0.0.1`; wildcard listeners are rejected. A non-loopback host requires `--allow-network` and remains unsuitable for shared or production deployment. UI contributors can point at a compatible Vite renderer with `--renderer-url`; the CLI adds the preview server URL as the renderer's `apiBase` query parameter and permits cross-origin API responses only for that renderer's exact origin. Both renderers require preview API version 1.
|
|
150
|
+
|
|
151
|
+
The command suite uses Click for stable parsing and exit codes and Rich for readable non-interactive output. In an interactive terminal, `preview-ui` uses a Textual status screen: press `o` to reopen the browser and `q` to stop the preview. Textual is deliberately bypassed for `--no-open` and non-terminal output so CI, pipes and SDK tests remain deterministic.
|
|
152
|
+
|
|
153
|
+
Every preview is labelled `SIMULATED PRESENTATION DATA`. Control interactions create only in-memory simulated receipts and never update observed readings optimistically. Preview success is not admission, hardware qualification or permission to operate equipment.
|
|
154
|
+
|
|
155
|
+
## Public surfaces
|
|
156
|
+
|
|
157
|
+
- `interfaces`: structural async `Adapter`, `HostServices`, `OperationContext` and optional `CaptureServices` definitions. No SDK superclass is required.
|
|
158
|
+
- `testing`: deterministic `MockContext` and `MockHost`, exact scripted transfers, dispatch markers, cancellation and a manually advanced clock. These are test doubles, not qualified host services.
|
|
159
|
+
- `validation`: pinned local schemas, strict finite JSON, format validation, runtime correlation and basic descriptor S01/S02 checks. Unresolved schema references fail without network retrieval.
|
|
160
|
+
- `conformance`: reusable operation and quiet lifecycle checks, with configurable wall-clock timeouts for cooperative async calls. Authors must add device-specific failure, profile and measurement tests. Use process isolation for blocking code or code that suppresses cancellation.
|
|
161
|
+
- `presentation`: bounded offline validation of presentation resources and complete configuration presets, using the same validator bytes as the gateway.
|
|
162
|
+
- `packaging`: inventory and integrity checks for a prepared bundle, including duplicate/path/symlink rejection. No installation, signing, publication or dependency execution.
|
|
163
|
+
|
|
164
|
+
## Compatibility and limits
|
|
165
|
+
|
|
166
|
+
The gateway has an explicit OTDP bridge and loader for identify, scalar read and scalar write. Profile actions, capture and streaming are not implemented by that bridge. Package-relative and standard-library imports are supported; arbitrary third-party runtime dependencies need further integration. Existing simulator interfaces remain private. Release CI builds an SDK and external plugin outside the checkout and exercises that plugin through the gateway bridge using mock transport. Unsupported operations must fail explicitly. No live install endpoint, physical backend, container device permissions or hardware qualification is supplied by this SDK.
|
|
167
|
+
|
|
168
|
+
The SDK sdist and wheel include the canonical OTDP, registry and plugin presentation contract sets. Build from the repository with `uv build packages/sdk`; the build hook includes contract resources and a wheel rebuilt from the sdist remains self-contained. The release smoke compares installed contract bytes with the canonical repository copies. SDK and gateway versions are independently named; each release must record the exact pair tested before expanding compatibility claims.
|
|
169
|
+
|
|
170
|
+
Pure Python plugins still need declared dependencies and compatible runtimes. Native dependencies and physical transport mappings require a separately qualified gateway deployment. The SDK does not install dependencies into a running gateway.
|
|
171
|
+
|
|
172
|
+
## Distribution rights
|
|
173
|
+
|
|
174
|
+
The SDK package is distributed under the [MIT licence](LICENSE). The vendored OTDP, registry and presentation contract sets and the generated templates are part of this package and carry the same grant. Plugin authors choose their own licence; generated examples contain no licence grant and make no claim on plugin code written with them.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Verify the vendored standards tree against its lock before packaging."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
8
|
+
|
|
9
|
+
LOCK_NAME = "standards-lock.json"
|
|
10
|
+
VENDORED = "src/benchweave_sdk/standards"
|
|
11
|
+
STAMP_NAME = "_GENERATED.txt"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _validate_preview_assets(package: Path) -> None:
|
|
15
|
+
root = package / "preview_assets"
|
|
16
|
+
inventory_path = root / "inventory.json"
|
|
17
|
+
if not inventory_path.is_file():
|
|
18
|
+
raise RuntimeError("Bundled preview inventory missing; run npm run build:preview from ui/")
|
|
19
|
+
try:
|
|
20
|
+
inventory = json.loads(inventory_path.read_bytes())
|
|
21
|
+
assets = inventory["assets"]
|
|
22
|
+
except (json.JSONDecodeError, KeyError, TypeError) as exc:
|
|
23
|
+
raise RuntimeError("Bundled preview inventory is invalid") from exc
|
|
24
|
+
if inventory.get("api_version") != 1 or not isinstance(assets, list) or not assets:
|
|
25
|
+
raise RuntimeError("Bundled preview inventory is incompatible or empty")
|
|
26
|
+
for asset in assets:
|
|
27
|
+
relative = Path(asset["path"])
|
|
28
|
+
if relative.is_absolute() or ".." in relative.parts:
|
|
29
|
+
raise RuntimeError(f"Unsafe preview asset path: {relative}")
|
|
30
|
+
content = (root / relative).read_bytes()
|
|
31
|
+
if len(content) != asset["size"] or hashlib.sha256(content).hexdigest() != asset["sha256"]:
|
|
32
|
+
raise RuntimeError(f"Bundled preview asset is stale or corrupt: {relative}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _verify_vendored_file(tree: Path, relative: str, digest: str) -> None:
|
|
36
|
+
path = Path(relative)
|
|
37
|
+
if path.is_absolute() or ".." in path.parts:
|
|
38
|
+
raise RuntimeError(f"Unsafe vendored standards path: {relative}")
|
|
39
|
+
target = tree / path
|
|
40
|
+
if not target.is_file():
|
|
41
|
+
raise RuntimeError(f"Vendored standards file missing: {relative}")
|
|
42
|
+
if hashlib.sha256(target.read_bytes()).hexdigest() != digest:
|
|
43
|
+
raise RuntimeError(f"Vendored standards file is stale or corrupt: {relative}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _validate_vendored_standards(root: Path) -> None:
|
|
47
|
+
tree = root / VENDORED
|
|
48
|
+
lock_path = root / LOCK_NAME
|
|
49
|
+
if not tree.is_dir() or not lock_path.is_file():
|
|
50
|
+
raise RuntimeError(
|
|
51
|
+
"Vendored standards tree or lock missing; "
|
|
52
|
+
"run benchweave-sdk sync-standards <bundle> from packages/sdk"
|
|
53
|
+
)
|
|
54
|
+
try:
|
|
55
|
+
lock = json.loads(lock_path.read_bytes())
|
|
56
|
+
standards = lock["standards"]
|
|
57
|
+
if lock.get("lock_version") != 1 or not isinstance(standards, list) or not standards:
|
|
58
|
+
raise RuntimeError("Bundled standards lock is incompatible or empty")
|
|
59
|
+
for standard in standards:
|
|
60
|
+
identifier = standard["id"]
|
|
61
|
+
if not (tree / identifier / STAMP_NAME).is_file():
|
|
62
|
+
raise RuntimeError(f"Vendored standard stamp missing: {identifier}/{STAMP_NAME}")
|
|
63
|
+
for file in standard["files"]:
|
|
64
|
+
_verify_vendored_file(tree, file["path"], file["sha256"])
|
|
65
|
+
except (json.JSONDecodeError, KeyError, TypeError) as exc:
|
|
66
|
+
raise RuntimeError("Bundled standards lock is invalid") from exc
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CustomBuildHook(BuildHookInterface):
|
|
70
|
+
def initialize(self, version, build_data):
|
|
71
|
+
root = Path(self.root)
|
|
72
|
+
package = root / "src/benchweave_sdk"
|
|
73
|
+
_validate_preview_assets(package)
|
|
74
|
+
_validate_vendored_standards(root)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.26"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "benchweave-sdk"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Offline authoring and conformance tools for BenchWeave OTDP device plugins."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"click>=8.3,<9",
|
|
15
|
+
"jsonschema>=4.23",
|
|
16
|
+
"referencing>=0.35",
|
|
17
|
+
"rfc3339-validator>=0.1.4",
|
|
18
|
+
"rfc3987>=1.3.8",
|
|
19
|
+
"rich>=14,<15",
|
|
20
|
+
"textual>=8.2,<9",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
test = ["pytest>=8.0"]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
benchweave-sdk = "benchweave_sdk.cli:main"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.wheel]
|
|
30
|
+
packages = ["src/benchweave_sdk"]
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build.targets.sdist]
|
|
33
|
+
include = ["src", "pyproject.toml", "README.md", "hatch_build.py", "standards-lock.json"]
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.hooks.custom]
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
dev = [
|
|
39
|
+
"mypy>=1.11",
|
|
40
|
+
"ruff>=0.6",
|
|
41
|
+
"types-jsonschema>=4.26.0.20260518",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.ruff]
|
|
45
|
+
line-length = 100
|
|
46
|
+
target-version = "py313"
|
|
47
|
+
extend-exclude = ["docs"]
|
|
48
|
+
|
|
49
|
+
[tool.ruff.lint]
|
|
50
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
51
|
+
|
|
52
|
+
[tool.mypy]
|
|
53
|
+
python_version = "3.13"
|
|
54
|
+
strict = true
|
|
55
|
+
files = ["src"]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Public authoring surface for OTDP 0.3.0 / adapter API 1.1."""
|
|
2
|
+
|
|
3
|
+
from .interfaces import Adapter, HostServices, OperationContext
|
|
4
|
+
|
|
5
|
+
__version__ = "0.1.0"
|
|
6
|
+
OTDP_VERSION = "0.3.0"
|
|
7
|
+
ADAPTER_API_VERSION = "1.1"
|
|
8
|
+
__all__ = ["Adapter", "HostServices", "OperationContext"]
|