@mhome/app-facade-protocol 1.83.0 → 1.85.0

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.
@@ -75,11 +75,17 @@ Fallback characteristic UUIDs: the standard endpoints as for pods, `host-info`
75
75
  "productId": "camera",
76
76
  "firmwareVersion": "0.9.0",
77
77
  "fingerprint": "<hex SHA-256 of the bootstrap public key>",
78
- "wifiConfigured": false
78
+ "wifiConfigured": false,
79
+ "onboardingRequired": true,
80
+ "networkOnline": false
79
81
  }
80
82
  ```
81
83
 
82
- `productId` is optional.
84
+ `productId` is optional. `onboardingRequired` reports whether this Host still
85
+ awaits its first claim. `networkOnline` reports current LAN readiness. Both
86
+ are optional for older Hosts. A securely paired Host with `networkOnline: true`
87
+ and `onboardingRequired: true` can finish without resubmitting Wi-Fi. `finish` issues a fresh first-claim token after checking authoritative
88
+ Host ownership. Already claimed Hosts never issue a new first-claim token.
83
89
  - `{"op":"finish"}` → `{"ok":true,"claimToken":"…"}`. After a successful join
84
90
  during first provisioning the response carries the claim token: 32 random
85
91
  bytes as 43 unpadded base64url characters. It is created when the join
@@ -120,17 +126,26 @@ Candidates come from the shared discovery (`/local/pod/discovery/*`) with
120
126
  | `/local/host/provision/wifi` | `{"sessionId","ssid","password"?}` | session snapshot |
121
127
  | `/local/host/provision/wifi/scan` | `{"sessionId"}` | session snapshot |
122
128
  | `/local/host/provision/status` | `{}` | `{"session": snapshot or null}` |
129
+ | `/local/host/provision/cancel` | `{"sessionId"}` | session snapshot |
123
130
 
124
131
  Event `/local/host/provision/changed` carries a session snapshot. One session
125
132
  per native Client, and none while a pod session is active; discovery scanning
126
133
  pauses during either. Rejected requests use the error envelope and the stable
127
134
  `details.reason` values of `pod-commissioning-v1.md` section 7. The session is
128
- owned by the native core until success, failure or the session deadline.
129
- There is no cancellation or renewal endpoint; leaving the UI does not end it.
135
+ owned by the native core until success, failure or the session deadline. Host
136
+ setup is bound to the signed-in native account context, including before a Space
137
+ exists. An account change retires the session and its claim token; status and
138
+ queued events cannot transfer an old handoff to the new login.
139
+ Cancellation is explicit. It transitions through `cancelling` and reaches
140
+ `cancelled` only after the link and radio are released. Repeated cancellation
141
+ returns the same session; a different session ID is rejected. Cancellation during
142
+ `joining_wifi` is rejected as busy: applying Wi-Fi and handing off the first-claim
143
+ token form a bounded critical section. Leaving the UI does not cancel a session.
144
+ There is no renewal endpoint.
130
145
 
131
146
  Session states, in order: `connecting`, `awaiting_code`, `securing`,
132
147
  `reading_info`, `awaiting_wifi`, `joining_wifi`, `completed`; terminal `failed`,
133
- `timed_out`. `completed` carries `host`, the reported `addresses`
148
+ `timed_out`, `cancelled`; transitional `cancelling`. `completed` carries `host`, the reported `addresses`
134
149
  (IPv4 only, possibly empty) and `claimToken` from the `finish` response; the
135
150
  commissioner sends `finish` (up to 3 times while the secure session lasts)
136
151
  before reporting it. `claimToken` is absent when `finish` could not be
@@ -158,6 +173,10 @@ The schema is `schema/host-provision.v1.schema.json`.
158
173
  ## 5. Host-side entry points
159
174
 
160
175
  - First provisioning starts BLE at boot when the Host has no known network.
176
+ BLE-capable Hosts whose authoritative local claim is still absent also restore
177
+ setup after restart, keeping saved Wi-Fi. Network readiness is not ownership.
178
+ An existing local claim, including one awaiting cloud confirmation, must never
179
+ reopen first-claim authority. An unreadable ownership store refuses recovery.
161
180
  - Re-provision window: opened from the Host's local interface (display settings
162
181
  or `meowhostd provision open`). Mutating local provisioning commands (`open`,
163
182
  `close`, `factory-reset`) require root or membership in the `meow-admin`
@@ -0,0 +1,46 @@
1
+ # Plugin installation
2
+
3
+ These platform-owned App operations run on the selected local Space's Hub. They
4
+ are App-only, require a personal User with Space management access, and retain
5
+ Host-owned authorization and identity pinning whenever Host management is needed.
6
+ A verified available service can be commissioned without Host software-management
7
+ permission or a Host installation receipt; it retains the existing Node admission
8
+ and Space management checks. Agent/CLI/MCP do not acquire a new
9
+ installation entrypoint. The shared schema describes business input and data;
10
+ requests and responses still use the canonical App Facade envelopes.
11
+
12
+ - `/app/plugin/install/prepare` accepts `{nodeType}`. It inspects Hosts reachable
13
+ from this Hub. It immediately returns a `preparing` session; status eventually
14
+ returns `prepared` with immutable per-Host actions or `failed` with an error.
15
+ Cardinality is unresolved (`null`) while preparing and selection is disabled.
16
+ `single` permits one selected Host; `multiple` permits one or more. An existing
17
+ single binding prevents another add. Unsupported or unknown observations are
18
+ ineligible, not treated as missing software. Compatible installed services are
19
+ reused without an implicit update or repair. Starting a stopped service is
20
+ explicit in the action shown to the User. Prepare never starts service work.
21
+ - `/app/plugin/install/start` accepts `{sessionId,hostIds}` and submits once.
22
+ Repeated identical submission returns the existing session, including failure;
23
+ a different selection for a submitted ID is rejected. Plans expire; no silent
24
+ replanning occurs. Each Host independently installs if necessary, waits for the
25
+ exact service and release, then commissions into this Space. Failure stops that
26
+ Host's dependent stages. Other selected Hosts continue. Commissioning waits for
27
+ the existing Space mutation lock before one attempt.
28
+ - `/app/plugin/install/status` accepts `{sessionId}` and reads the originating
29
+ User/tenant/Space session. It never executes or retries a stage. `running` is
30
+ acceptance/progress, not success. All selected targets must succeed for
31
+ `succeeded`; mixed success/failure is `partiallySucceeded`, zero success is
32
+ `failed`, and any unconfirmed target makes the overall state `unknown`.
33
+
34
+ Each Host retains separate installation, readiness and commissioning outcomes,
35
+ with errors and submitted task/session references. `notRequired` means the path
36
+ omits a stage; `notRun` means its prerequisite failed or execution was interrupted.
37
+ A service installed before commissioning failed remains installed. No rollback,
38
+ automatic retry, retry endpoint, or automatic resumption is provided. Polling an
39
+ existing task after a lost response is observation, never a repeated write. A Core
40
+ restart records interrupted work and preserves completed facts; it never restarts
41
+ installation/commissioning. Existing provisional-credential cleanup is retained.
42
+ Host tasks already accepted may continue independently; interruption is not proof
43
+ that installation failed. Warnings are preserved even when targets succeed.
44
+
45
+ Results omit credentials, signing evidence and private execution plans. Completion
46
+ of this operation does not force device/account/model setup.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.app-facade.v1",
3
- "contractVersion": "1.83.0",
3
+ "contractVersion": "1.85.0",
4
4
  "callSchema": "schema/facade-call.v1.schema.json",
5
5
  "routing": "manifest/routing.v1.json",
6
6
  "routingSchema": "schema/routing.v1.schema.json",
@@ -54,13 +54,18 @@
54
54
  "/app/plugin/enable/start",
55
55
  "/app/plugin/enable/status",
56
56
  "/app/plugin/disable",
57
- "/app/plugin/profile/update"
57
+ "/app/plugin/profile/update",
58
+ "/app/plugin/install/prepare",
59
+ "/app/plugin/install/start",
60
+ "/app/plugin/install/status"
58
61
  ],
59
62
  "eventTargets": [
60
63
  "/app/plugin/settings/changed"
61
64
  ],
62
65
  "extensionFamily": "/app/plugin/{nodeType}/{route...}",
63
- "extensionPriority": "after_exact"
66
+ "extensionPriority": "after_exact",
67
+ "installSchema": "schema/plugin-install.v1.schema.json",
68
+ "installContract": "contract/plugin-install-v1.md"
64
69
  },
65
70
  "runtime": {
66
71
  "contract": "mhome.runtime.status.v1",
@@ -1,5 +1,5 @@
1
1
  {
2
- "contractVersion": "1.83.0",
2
+ "contractVersion": "1.85.0",
3
3
  "operations": {
4
4
  "/app/agent/context/get": "spaceRead",
5
5
  "/app/agent/credit/usage": "spaceRead",
@@ -196,6 +196,9 @@
196
196
  "/app/plugin/disable": "spaceManage",
197
197
  "/app/plugin/enable/start": "spaceManage",
198
198
  "/app/plugin/enable/status": "spaceManage",
199
+ "/app/plugin/install/prepare": "spaceManage",
200
+ "/app/plugin/install/start": "spaceManage",
201
+ "/app/plugin/install/status": "spaceManage",
199
202
  "/app/plugin/enabled/list": "spaceRead",
200
203
  "/app/plugin/profile/update": "spaceManage",
201
204
  "/app/pod/enrollment/get": "userRead",
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "manifest": "mhome.host-provision.targets.v1",
3
- "contractVersion": "1.83.0",
3
+ "contractVersion": "1.85.0",
4
4
  "contract": "contract/host-provisioning-v1.md",
5
5
  "localTargets": [
6
6
  "/local/host/provision/start",
7
7
  "/local/host/provision/code",
8
8
  "/local/host/provision/wifi",
9
9
  "/local/host/provision/wifi/scan",
10
- "/local/host/provision/status"
10
+ "/local/host/provision/status",
11
+ "/local/host/provision/cancel"
11
12
  ],
12
13
  "eventTargets": [
13
14
  "/local/host/provision/changed"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.hub.targets.v1",
3
- "contractVersion": "1.83.0",
3
+ "contractVersion": "1.85.0",
4
4
  "appTargets": [
5
5
  "/app/hub/get",
6
6
  "/app/hub/remove",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.pod.targets.v1",
3
- "contractVersion": "1.83.0",
3
+ "contractVersion": "1.85.0",
4
4
  "contract": "contract/pod-commissioning-v1.md",
5
5
  "localTargets": [
6
6
  "/local/pod/discovery/start",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.app-facade.routing.v1",
3
- "contractVersion": "1.83.0",
3
+ "contractVersion": "1.85.0",
4
4
  "targetPrefix": "/app/",
5
5
  "default": {
6
6
  "execution": "scopeMode"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.messaging.targets.v1",
3
- "contractVersion": "1.83.0",
3
+ "contractVersion": "1.85.0",
4
4
  "requestTargets": [
5
5
  "/app/messaging/provider/list",
6
6
  "/app/messaging/provider-account/list",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhome/app-facade-protocol",
3
- "version": "1.83.0",
3
+ "version": "1.85.0",
4
4
  "description": "Build-time protocol artifacts for mhome.app-facade",
5
5
  "license": "MIT",
6
6
  "repository": {
package/protocol.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "protocol": "mhome.app-facade",
3
- "contractVersion": "1.83.0",
3
+ "contractVersion": "1.85.0",
4
4
  "manifest": "manifest/app-facade.v1.json",
5
5
  "schemas": {
6
+ "pluginInstall": "schema/plugin-install.v1.schema.json",
6
7
  "hostAuthorization": "schema/host-authorization.v1.schema.json",
7
8
  "hostAccess": "schema/host-access.v1.schema.json",
8
9
  "agentLlm": "schema/agent-llm.v1.schema.json",
@@ -16,6 +16,8 @@
16
16
  "reading_info",
17
17
  "awaiting_wifi",
18
18
  "joining_wifi",
19
+ "cancelling",
20
+ "cancelled",
19
21
  "completed",
20
22
  "failed",
21
23
  "timed_out"
@@ -0,0 +1,281 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.mhome.ai/app-facade/plugin-install.v1.schema.json",
4
+ "$defs": {
5
+ "prepare": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "nodeType"
10
+ ],
11
+ "properties": {
12
+ "nodeType": {
13
+ "type": "string",
14
+ "minLength": 1
15
+ }
16
+ }
17
+ },
18
+ "start": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "required": [
22
+ "sessionId",
23
+ "hostIds"
24
+ ],
25
+ "properties": {
26
+ "sessionId": {
27
+ "type": "string",
28
+ "minLength": 1
29
+ },
30
+ "hostIds": {
31
+ "type": "array",
32
+ "items": {
33
+ "type": "string",
34
+ "minLength": 1
35
+ },
36
+ "minItems": 1,
37
+ "maxItems": 32,
38
+ "uniqueItems": true
39
+ }
40
+ }
41
+ },
42
+ "status": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "required": [
46
+ "sessionId"
47
+ ],
48
+ "properties": {
49
+ "sessionId": {
50
+ "type": "string",
51
+ "minLength": 1
52
+ }
53
+ }
54
+ },
55
+ "stage": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": [
59
+ "state"
60
+ ],
61
+ "properties": {
62
+ "state": {
63
+ "enum": [
64
+ "pending",
65
+ "running",
66
+ "succeeded",
67
+ "failed",
68
+ "notRequired",
69
+ "notRun",
70
+ "unknown"
71
+ ]
72
+ },
73
+ "error": {
74
+ "type": "string"
75
+ }
76
+ }
77
+ },
78
+ "target": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "required": [
82
+ "hostId",
83
+ "hostName",
84
+ "action",
85
+ "eligible",
86
+ "reason",
87
+ "selected",
88
+ "state",
89
+ "installed",
90
+ "expectedVersion",
91
+ "installation",
92
+ "readiness",
93
+ "commissioning",
94
+ "operationId",
95
+ "enableSessionId",
96
+ "nodeId",
97
+ "warnings"
98
+ ],
99
+ "properties": {
100
+ "hostId": {
101
+ "type": "string",
102
+ "minLength": 1
103
+ },
104
+ "hostName": {
105
+ "type": "string",
106
+ "minLength": 1
107
+ },
108
+ "action": {
109
+ "enum": [
110
+ "unavailable",
111
+ "alreadyAdded",
112
+ "add",
113
+ "startAndAdd",
114
+ "installAndAdd"
115
+ ]
116
+ },
117
+ "eligible": {
118
+ "type": "boolean"
119
+ },
120
+ "reason": {
121
+ "type": [
122
+ "string",
123
+ "null"
124
+ ]
125
+ },
126
+ "selected": {
127
+ "type": "boolean"
128
+ },
129
+ "state": {
130
+ "enum": [
131
+ "pending",
132
+ "running",
133
+ "succeeded",
134
+ "failed",
135
+ "unknown"
136
+ ]
137
+ },
138
+ "installed": {
139
+ "type": "boolean"
140
+ },
141
+ "expectedVersion": {
142
+ "type": [
143
+ "string",
144
+ "null"
145
+ ]
146
+ },
147
+ "installation": {
148
+ "$ref": "#/$defs/stage"
149
+ },
150
+ "readiness": {
151
+ "$ref": "#/$defs/stage"
152
+ },
153
+ "commissioning": {
154
+ "$ref": "#/$defs/stage"
155
+ },
156
+ "operationId": {
157
+ "type": [
158
+ "string",
159
+ "null"
160
+ ]
161
+ },
162
+ "enableSessionId": {
163
+ "type": [
164
+ "string",
165
+ "null"
166
+ ]
167
+ },
168
+ "nodeId": {
169
+ "type": [
170
+ "string",
171
+ "null"
172
+ ]
173
+ },
174
+ "warnings": {
175
+ "type": "array",
176
+ "items": {
177
+ "type": "string",
178
+ "minLength": 1
179
+ }
180
+ }
181
+ }
182
+ },
183
+ "response": {
184
+ "type": "object",
185
+ "additionalProperties": false,
186
+ "required": [
187
+ "contract",
188
+ "sessionId",
189
+ "spaceId",
190
+ "nodeType",
191
+ "pluginName",
192
+ "bindingCardinality",
193
+ "state",
194
+ "revision",
195
+ "expiresAtMs",
196
+ "hosts",
197
+ "warnings",
198
+ "error"
199
+ ],
200
+ "properties": {
201
+ "contract": {
202
+ "const": "mhome.plugin.install.v1"
203
+ },
204
+ "sessionId": {
205
+ "type": "string",
206
+ "minLength": 1
207
+ },
208
+ "spaceId": {
209
+ "type": "string",
210
+ "minLength": 1
211
+ },
212
+ "nodeType": {
213
+ "type": "string",
214
+ "minLength": 1
215
+ },
216
+ "pluginName": {
217
+ "type": "string",
218
+ "minLength": 1
219
+ },
220
+ "bindingCardinality": {
221
+ "enum": [
222
+ "single",
223
+ "multiple",
224
+ null
225
+ ]
226
+ },
227
+ "state": {
228
+ "enum": [
229
+ "preparing",
230
+ "prepared",
231
+ "running",
232
+ "succeeded",
233
+ "partiallySucceeded",
234
+ "failed",
235
+ "unknown"
236
+ ]
237
+ },
238
+ "revision": {
239
+ "type": "integer",
240
+ "minimum": 1
241
+ },
242
+ "expiresAtMs": {
243
+ "type": "integer"
244
+ },
245
+ "hosts": {
246
+ "type": "array",
247
+ "items": {
248
+ "$ref": "#/$defs/target"
249
+ }
250
+ },
251
+ "warnings": {
252
+ "type": "array",
253
+ "items": {
254
+ "type": "string",
255
+ "minLength": 1
256
+ }
257
+ },
258
+ "error": {
259
+ "type": [
260
+ "string",
261
+ "null"
262
+ ]
263
+ }
264
+ }
265
+ }
266
+ },
267
+ "oneOf": [
268
+ {
269
+ "$ref": "#/$defs/prepare"
270
+ },
271
+ {
272
+ "$ref": "#/$defs/start"
273
+ },
274
+ {
275
+ "$ref": "#/$defs/status"
276
+ },
277
+ {
278
+ "$ref": "#/$defs/response"
279
+ }
280
+ ]
281
+ }