@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.
- package/contract/host-provisioning-v1.md +24 -5
- package/contract/plugin-install-v1.md +46 -0
- package/manifest/app-facade.v1.json +8 -3
- package/manifest/authorization.v1.json +4 -1
- package/manifest/host-provision-targets.v1.json +3 -2
- package/manifest/hub-targets.v1.json +1 -1
- package/manifest/pod-targets.v1.json +1 -1
- package/manifest/routing.v1.json +1 -1
- package/manifest/targets.v1.json +1 -1
- package/package.json +1 -1
- package/protocol.json +2 -1
- package/schema/host-provision.v1.schema.json +2 -0
- package/schema/plugin-install.v1.schema.json +281 -0
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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"
|
package/manifest/routing.v1.json
CHANGED
package/manifest/targets.v1.json
CHANGED
package/package.json
CHANGED
package/protocol.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocol": "mhome.app-facade",
|
|
3
|
-
"contractVersion": "1.
|
|
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",
|
|
@@ -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
|
+
}
|