Skip to content

Commit 8c8b439

Browse files
committed
feat: freeze CLI automation contract
1 parent 2fe635a commit 8c8b439

5 files changed

Lines changed: 64 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Changelog
22

3+
- Added: the headless CLI and localhost bridge now publish a versioned v1 JSON contract with a forward-compatible schema, stable exit codes, documented selectors/routes, and release-packaged schema fixtures.
34
- Added: release builds now emit an offline-verifiable manifest with payload and ZIP SHA-256 hashes, source commit, runtime metadata, and a CycloneDX SBOM. Tagged CI builds publish GitHub artifact provenance; releases remain intentionally unsigned.
45
- Added: an opt-in, notify-only update check under **System > Overview**. It queries the GitHub releases API with an `ETag` cache, compares the release tag with the installed version, and offers dismissible links to the release page without downloading or executing anything; failures stay silent beyond diagnostics.
56
- Added: the UI now selects the Windows display language by default, resolves static, runtime, tray, dialog, and automation-accessible text through invariant resource keys with English fallback, and formats displayed values with the selected culture. A `-Culture` override and expanded/accented `qps-ploc` pseudo-locale exercise localization without requiring a translated build.

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ best-effort rollback from the GUI transaction path. JSON mode emits exactly one
192192
{"SchemaVersion":1,"Command":"get","Success":true,"ExitCode":0,"Data":{"Identity":"winrt:0123456789abcdef","Vcp":"0x10","Current":50,"Maximum":100,"Type":0},"Error":null}
193193
```
194194

195+
The envelope contract is versioned in [`schemas/monitorcontrol-cli-v1.schema.json`](schemas/monitorcontrol-cli-v1.schema.json)
196+
and is also packaged as `monitorcontrol-cli-v1.schema.json` in release ZIPs. Readers must use
197+
`SchemaVersion` and tolerate unknown fields; a breaking change requires a new schema version.
198+
Error objects always contain a stable `Code` and a human-readable `Message`, and the JSON output
199+
never includes bridge keys or other credentials.
200+
195201
| Exit code | Meaning |
196202
|---:|---|
197203
| `0` | Success, including an `-IfNeeded` no-op |
@@ -202,6 +208,14 @@ best-effort rollback from the GUI transaction path. JSON mode emits exactly one
202208
| `6` | Risky write denied by the shared per-identity safety policy |
203209
| `10` | Unexpected internal or worker-start failure |
204210

211+
The localhost bridge shares the same stable monitor selectors and uses these routes: an exact,
212+
body-free `GET /api/health` returns `{ "ok": true }` without authentication; authenticated
213+
`GET /api/monitors`, `/api/profiles`, and `/api/brightness` read state; authenticated `POST
214+
/api/brightness` accepts a percentage `value` and optional `monitor`; authenticated `POST
215+
/api/profile` accepts a profile `name`. Credentials are accepted only through
216+
`X-MonitorControl-Key` or `Authorization: Bearer`, never query strings or JSON bodies. Unknown
217+
routes and malformed requests return deterministic 4xx JSON responses.
218+
205219
### Build a Release ZIP
206220
```powershell
207221
.\tools\build-release.ps1
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://github.com/SysAdminDoc/MonitorControl/schemas/monitorcontrol-cli-v1.schema.json",
4+
"title": "MonitorControl Pro CLI JSON envelope v1",
5+
"description": "Stable JSON output for the portable headless CLI. Unknown fields are intentionally allowed for forward-compatible readers.",
6+
"type": "object",
7+
"additionalProperties": true,
8+
"required": ["SchemaVersion", "Command", "Success", "ExitCode", "Data", "Error"],
9+
"properties": {
10+
"SchemaVersion": { "const": 1 },
11+
"Command": {
12+
"type": "string",
13+
"enum": ["list", "get", "set", "profile", "diagnostics", "b", "s"]
14+
},
15+
"Success": { "type": "boolean" },
16+
"ExitCode": { "type": "integer", "enum": [0, 2, 3, 4, 5, 6, 10] },
17+
"Data": {},
18+
"Error": {
19+
"anyOf": [
20+
{ "type": "null" },
21+
{
22+
"type": "object",
23+
"additionalProperties": true,
24+
"required": ["Code", "Message"],
25+
"properties": {
26+
"Code": { "type": "string", "minLength": 1 },
27+
"Message": { "type": "string" }
28+
}
29+
}
30+
]
31+
}
32+
}
33+
}

tests/MonitorControl.NoHardware.Tests.ps1

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5436,6 +5436,19 @@ Describe "Headless CLI contract" {
54365436
(($result.Envelope | ConvertTo-Json -Depth 10 -Compress) | ConvertFrom-Json).Error | Should -BeNullOrEmpty
54375437
}
54385438

5439+
It "ships a forward-compatible CLI envelope schema" {
5440+
$schemaPath = Join-Path $script:RepoRoot "schemas\monitorcontrol-cli-v1.schema.json"
5441+
Test-Path -LiteralPath $schemaPath -PathType Leaf | Should -BeTrue
5442+
$schema = Get-Content -LiteralPath $schemaPath -Raw | ConvertFrom-Json
5443+
$schema.properties.SchemaVersion.const | Should -Be 1
5444+
@($schema.required) | Should -Contain "SchemaVersion"
5445+
@($schema.required) | Should -Contain "Error"
5446+
$schema.additionalProperties | Should -BeTrue
5447+
@($schema.properties.ExitCode.enum) | Should -Contain 10
5448+
@($schema.properties.Command.enum) | Should -Contain "diagnostics"
5449+
$schema.properties.Error.anyOf[1].additionalProperties | Should -BeTrue
5450+
}
5451+
54395452
It "requires a stable monitor selector when more than one target is usable" {
54405453
$monitors = @((New-CliTestMonitor), (New-CliTestMonitor -Identity "winrt:stable-b" -Label "Side display" -Handle ([IntPtr]0x7002)))
54415454
$result = Invoke-MonitorControlCli -Command get -Argument 0x10 -MonitorData $monitors -ReadAction { throw "must not read" }

tools/build-release.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ $readmePath = Join-Path $repoRoot "README.md"
1717
$licensePath = Join-Path $repoRoot "LICENSE"
1818
$iconPath = Join-Path $repoRoot "icon.ico"
1919
$screenshotPath = Join-Path $repoRoot "screenshot.png"
20+
$cliSchemaPath = Join-Path $repoRoot "schemas\monitorcontrol-cli-v1.schema.json"
2021

21-
foreach ($required in @($metadataPath, $scriptPath, $compilePath, $readmePath, $licensePath, $iconPath, $screenshotPath)) {
22+
foreach ($required in @($metadataPath, $scriptPath, $compilePath, $readmePath, $licensePath, $iconPath, $screenshotPath, $cliSchemaPath)) {
2223
if (-not (Test-Path -LiteralPath $required -PathType Leaf)) { throw "Missing release input: $required" }
2324
}
2425

@@ -247,6 +248,7 @@ Copy-Item -LiteralPath $readmePath -Destination (Join-Path $stageRoot "README.md
247248
Copy-Item -LiteralPath $licensePath -Destination (Join-Path $stageRoot "LICENSE") -Force
248249
Copy-Item -LiteralPath $iconPath -Destination (Join-Path $stageRoot "icon.ico") -Force
249250
Copy-Item -LiteralPath $screenshotPath -Destination (Join-Path $stageRoot "screenshot.png") -Force
251+
Copy-Item -LiteralPath $cliSchemaPath -Destination (Join-Path $stageRoot "monitorcontrol-cli-v1.schema.json") -Force
250252

251253
# Scoop resolves `shortcuts` against a file it can execute. A bare .ps1 is not one, and a GUI
252254
# app should not get a `bin` shim either, so the ZIP carries a launcher that starts the script

0 commit comments

Comments
 (0)