diff --git a/docs/CONTRACT-STABILITY.md b/docs/CONTRACT-STABILITY.md index c2cdcb62..ec7d7a31 100644 --- a/docs/CONTRACT-STABILITY.md +++ b/docs/CONTRACT-STABILITY.md @@ -47,6 +47,7 @@ Recorded in the comment block in `shopdb/__init__.py`: | 0.25.0 | Added `caller_holds(permission)`, which answers whether the current caller holds a permission WITHOUT refusing the request - the complement to `require_permission` for a route that serves everyone but should only offer what the caller can act on. The endpoints services panel is the case: it emitted Start/Stop row actions to every reader, because the emission never consulted the caller. Clicking them was always refused server-side, so nothing was exploitable, but offering a read-only viewer controls over a shop-floor PC is the wrong signal, and it becomes a real problem once a site grants that panel to guests. Guests are included in the answer, since a signed-out visitor holds whatever the guest role was granted | additive surface (minor) | | 0.26.0 | Dashboard cards may declare `shortname`. The dashboard merged its per-source cards into one worst-first queue, and a card's `title` is written as a heading ("Endpoints with crashes or shutdowns") which as a row badge ran wider than the hostname it qualified. Optional, defaults to the title, so a plugin that does not declare one is unaffected | additive field (minor) | | 0.27.0 | Added `Script` so a plugin can read the ShopDB-hosted script library that backs endpoint `run_script` delivery without importing core model internals. The endpoints plugin resolves a delivered script by name and serves its body by digest to the agent | additive surface (minor) | +| 0.29.0 | Added an optional `group` to `get_asset_panels`, naming which detail-page tab a panel belongs to: `details` (the default) / `software` / `metrics` / `actions` / `activity`. The detail pages are tabbed, and a page that placed panels by knowing their ids would defeat the hook. Additive: an absent or unrecognised group resolves to `details` server-side, so every existing plugin keeps working with no change | additive surface (minor) | | 0.28.0 | Added `client_ip` and `strip_port` so a plugin does not hand-roll caller-IP extraction. Two plugins had, and both read the first `X-Forwarded-For` hop, which the caller supplies - so a rate limit keyed on it was defeated by rotating the header. The helper resolves from `remote_addr`, which ProxyFix has already filled from the header when `TRUSTED_PROXY_COUNT` says a proxy is really in front | additive surface (minor) | The source comment block documents 0.3.0, 0.4.0, 0.6.0, 0.7.0, 0.9.0, 0.10.0, diff --git a/docs/PROJECT-MAP.md b/docs/PROJECT-MAP.md index 3775fef3..54f2ccdf 100644 --- a/docs/PROJECT-MAP.md +++ b/docs/PROJECT-MAP.md @@ -12,7 +12,7 @@ never by editing this file. | series | value | governed by | |---|---|---| | product `__version__` | `0.12.0` | ADR-007 | -| plugin contract `__contract_version__` | `0.28.0` | ADR-002 | +| plugin contract `__contract_version__` | `0.29.0` | ADR-002 | They move independently. A contract bump is not a release. @@ -90,6 +90,6 @@ Manifest-less directories under `plugins/` are core frontend surface and always ## Size -- test functions defined: **1575** (parametrised cases collect higher) +- test functions defined: **1579** (parametrised cases collect higher) - documented API paths: **346** (`docs/openapi.json`, regenerate with `scripts/gen_openapi.py`) diff --git a/frontend/src/components/AssetDetailTabs.vue b/frontend/src/components/AssetDetailTabs.vue new file mode 100644 index 00000000..a4c49778 --- /dev/null +++ b/frontend/src/components/AssetDetailTabs.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/frontend/src/components/PluginAssetPanels.vue b/frontend/src/components/PluginAssetPanels.vue index d5b8ab92..abe4b013 100644 --- a/frontend/src/components/PluginAssetPanels.vue +++ b/frontend/src/components/PluginAssetPanels.vue @@ -2,7 +2,7 @@ -