@huynle/hub 0.10.1 → 0.11.1
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/CHANGELOG.md +80 -0
- package/README.md +36 -8
- package/package.json +1 -1
- package/packages/claude-code/package.json +1 -1
- package/packages/claude-code/src/sessions.ts +4 -1
- package/packages/cli/dist/hub-cli.js +851 -466
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/cli.ts +12 -7
- package/packages/cli/src/decisions.ts +52 -4
- package/packages/cli/src/format.ts +9 -0
- package/packages/cli/src/grants-mcp.ts +65 -0
- package/packages/cli/src/mcp-operations.ts +49 -0
- package/packages/cli/src/mcp.ts +26 -4
- package/packages/cli/src/subscriptions-mcp.ts +76 -0
- package/packages/cli/src/team-intake-mcp.ts +2 -1
- package/packages/cli/src/teams-mcp.ts +9 -3
- package/packages/codex/package.json +1 -1
- package/packages/codex/src/sessions.ts +14 -3
- package/packages/node/package.json +1 -1
- package/packages/preset/package.json +1 -1
- package/packages/protocol/package.json +1 -1
- package/packages/protocol/schema/api/v1/_root.json +119 -0
- package/packages/protocol/schema/api/v1/account.json +419 -0
- package/packages/protocol/schema/api/v1/decisions.json +635 -0
- package/packages/protocol/schema/api/v1/discovery.json +110 -0
- package/packages/protocol/schema/api/v1/events.json +679 -0
- package/packages/protocol/schema/api/v1/session-actions.json +660 -0
- package/packages/protocol/schema/api/v1/sessions.json +2055 -0
- package/packages/protocol/schema/api/v1/speech.json +492 -0
- package/packages/protocol/schema/api/v1/unspecified.json +282 -0
- package/packages/protocol/schema/app-delivery/v1.schema.json +67 -5
- package/packages/protocol/schema/app-front/v1.schema.json +7 -3
- package/packages/protocol/src/api-v1.ts +8 -0
- package/packages/protocol/src/index.ts +2 -0
- package/packages/protocol/src/teams.ts +16 -0
- package/packages/protocol/src/voice-recording.ts +39 -2
- package/packages/runtime-openshell/image/entry.sh +3 -1
- package/packages/runtime-openshell/image/start.sh +3 -1
- package/packages/runtime-openshell/package.json +1 -1
- package/packages/runtime-sdk/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/packages/sdk/src/app-front.ts +2 -2
- package/packages/sdk/src/ask-fields.ts +76 -0
- package/packages/sdk/src/client.ts +24 -2
- package/packages/sdk/src/envelope.ts +3 -2
- package/packages/sdk/src/index.ts +1 -0
- package/packages/sdk/src/team-intake.ts +6 -0
- package/packages/sdk/src/teams.ts +4 -0
- package/packages/sdk/src/types.ts +36 -4
- package/packages/sdk/src/wake.ts +9 -4
- package/packages/spoke-sdk/package.json +1 -1
- package/src/central/agent/agent-limits.ts +185 -0
- package/src/central/agent/app-decisions.ts +6 -5
- package/src/central/agent/decisions.ts +136 -9
- package/src/central/agent/delivery.ts +164 -22
- package/src/central/agent/errors.ts +2 -1
- package/src/central/agent/events.ts +20 -2
- package/src/central/agent/grant-routes.ts +14 -1
- package/src/central/agent/grants.ts +23 -0
- package/src/central/agent/launch-engine.ts +9 -6
- package/src/central/agent/message-limits.ts +6 -0
- package/src/central/agent/message-routes.ts +33 -6
- package/src/central/agent/messages.ts +7 -0
- package/src/central/agent/placement.ts +4 -2
- package/src/central/agent/read-routes.ts +5 -0
- package/src/central/agent/routes.ts +13 -0
- package/src/central/agent/subscriptions.ts +15 -4
- package/src/central/api-v1.ts +115 -0
- package/src/central/artifacts/store.ts +4 -2
- package/src/central/concierge/board-results.ts +2 -1
- package/src/central/concierge/notifier.ts +4 -2
- package/src/central/config.ts +24 -3
- package/src/central/health/classify.ts +209 -0
- package/src/central/http.ts +380 -36
- package/src/central/recording/export.ts +57 -3
- package/src/central/recording/index.ts +236 -17
- package/src/central/recording/ingest.ts +11 -2
- package/src/central/recording/routes.ts +61 -7
- package/src/central/recording/schema.ts +68 -3
- package/src/central/recording/settings.ts +19 -5
- package/src/central/recording/store.ts +19 -0
- package/src/central/recording/taps.ts +45 -1
- package/src/central/registry.ts +34 -3
- package/src/central/speech/openai.ts +4 -4
- package/src/central/speech/openrouter.ts +4 -4
- package/src/central/speech/provider.ts +32 -1
- package/src/central/speech/service.ts +6 -5
- package/src/central/teams/budgets.ts +144 -0
- package/src/central/teams/hop-stalls.ts +80 -0
- package/src/central/teams/intake.ts +38 -6
- package/src/central/teams/involvement.ts +95 -0
- package/src/central/teams/item-decisions.ts +60 -0
- package/src/central/teams/item-eligibility.ts +35 -0
- package/src/central/teams/item-stalls.ts +87 -0
- package/src/central/teams/loops.ts +40 -0
- package/src/central/teams/policy.ts +10 -0
- package/src/central/teams/progress.ts +154 -0
- package/src/central/teams/routes.ts +2 -0
- package/src/central/teams/seats.ts +3 -3
- package/src/central/teams/staffing.ts +5 -0
- package/src/central/teams/store.ts +20 -1
- package/src/central/teams/validation.ts +16 -2
- package/src/central/workspaces/coordinator.ts +3 -1
- package/src/spoke/hub-tool.ts +26 -11
- package/web/dist/assets/{ArtifactPage-CsPjam8D.js → ArtifactPage-TbFJi59n.js} +1 -1
- package/web/dist/assets/{ArtifactPane-B9weO8L6.js → ArtifactPane-7D-M-Ich.js} +1 -1
- package/web/dist/assets/{ArtifactWorkbench-DzKhMs3l.js → ArtifactWorkbench-DlCLEjut.js} +1 -1
- package/web/dist/assets/{Diagram-CuRIffka.js → Diagram-PvOPetOI.js} +1 -1
- package/web/dist/assets/{Library-rv5DGtrm.js → Library-Cdi83pOV.js} +2 -2
- package/web/dist/assets/{ProfilesDestination-DUuQ9jmd.js → ProfilesDestination-cryC7P6j.js} +1 -1
- package/web/dist/assets/TeamsPage-B4xrlGhp.js +1 -0
- package/web/dist/assets/Watchtower-60B1JX_J.js +2 -0
- package/web/dist/assets/{artifacts-But87Jfo.js → artifacts-DlQEYmDl.js} +1 -1
- package/web/dist/assets/index-DA0gKIzv.js +33 -0
- package/web/dist/assets/{index-rW6LKQj1.css → index-Dyq-jBeE.css} +1 -1
- package/web/dist/assets/live-B2IfKm37.js +5 -0
- package/web/dist/assets/{live-BUofsQpz.css → live-De-kd3eW.css} +1 -1
- package/web/dist/assets/{pane-layout-BW0RdX5a.js → pane-layout-BAuu_pA3.js} +1 -1
- package/web/dist/assets/{renderer2d-DyANQ1WS.js → renderer2d-CsZnF3_o.js} +1 -1
- package/web/dist/assets/{renderer3d-vYYKFAJq.js → renderer3d-BIMA0wFn.js} +2 -2
- package/web/dist/assets/{safe-area-B2Bk__FK.js → safe-area-dtrnw9NF.js} +1 -1
- package/web/dist/assets/{silero-worker-e802lB5T.js → silero-worker-CC2etKS3.js} +1 -1
- package/web/dist/assets/{source-C-De8L3s.js → source-GYayxUxS.js} +1 -1
- package/web/dist/assets/{toast-CXTwTvDw.js → toast-q818mjUF.js} +1 -1
- package/web/dist/index.html +5 -5
- package/web/dist/assets/TeamsPage-D_17MkvN.js +0 -1
- package/web/dist/assets/Watchtower-DlrNOJ7X.js +0 -2
- package/web/dist/assets/index-bX8B6NLo.js +0 -33
- package/web/dist/assets/live-BGLA-peO.js +0 -5
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.1.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Hub API",
|
|
5
|
+
"version": "1",
|
|
6
|
+
"description": "The versioned HTTP API of a Hub: every route of the console and agent tables under /api/v1. v1 changes only by addition: clients ignore fields they do not know, treat a value outside an `x-hub-known` list as unknown, and skip a variant of an `x-hub-union` they do not know. Every error is one envelope: {error: {code, message, hint?}}."
|
|
7
|
+
},
|
|
8
|
+
"servers": [
|
|
9
|
+
{
|
|
10
|
+
"url": "https://hub.example",
|
|
11
|
+
"description": "A placeholder: the address of your hub."
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"security": [
|
|
15
|
+
{
|
|
16
|
+
"deviceToken": []
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"components": {
|
|
20
|
+
"securitySchemes": {
|
|
21
|
+
"deviceToken": {
|
|
22
|
+
"type": "http",
|
|
23
|
+
"scheme": "bearer",
|
|
24
|
+
"description": "A device token: what a native app gets when it redeems a link code (POST /api/v1/devices/redeem). A request with a bearer token needs no Origin."
|
|
25
|
+
},
|
|
26
|
+
"deviceCookie": {
|
|
27
|
+
"type": "apiKey",
|
|
28
|
+
"in": "cookie",
|
|
29
|
+
"name": "voice_device",
|
|
30
|
+
"description": "The browser's credential: the same secret as the device token, as an HttpOnly cookie set when a browser pairs or signs in. A write with the cookie must be same-origin. Send the cookie or a bearer token, never both. Declared here for readers and never listed as a requirement: a generated client sends the bearer token only."
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"responses": {
|
|
34
|
+
"Error": {
|
|
35
|
+
"description": "Any error (status 400 and up): the one envelope of /api/v1.",
|
|
36
|
+
"content": {
|
|
37
|
+
"application/json": {
|
|
38
|
+
"schema": {
|
|
39
|
+
"$ref": "#/components/schemas/ErrorBody"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"schemas": {
|
|
46
|
+
"ErrorBody": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"required": ["error"],
|
|
49
|
+
"properties": {
|
|
50
|
+
"error": {
|
|
51
|
+
"$ref": "#/components/schemas/ErrorDetail"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"ErrorDetail": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"description": "What went wrong. A code may come with more fields (for example retryAfter, currentRevision, candidates, feature, limit).",
|
|
58
|
+
"required": ["code", "message"],
|
|
59
|
+
"properties": {
|
|
60
|
+
"code": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "Stable and machine-readable. A route that names no code of its own gets one from the status (400 invalid, 401 unauthorized, 403 forbidden, 404 not_found, 408 and 504 timeout, 409 conflict, 410 gone, 413 too_large, 422 unsupported, 429 rate_limited, 500 internal, 502 upstream_failed, 503 unavailable).",
|
|
63
|
+
"x-hub-known": [
|
|
64
|
+
"ambiguous",
|
|
65
|
+
"bad_answer",
|
|
66
|
+
"bad_bundle",
|
|
67
|
+
"busy",
|
|
68
|
+
"changed",
|
|
69
|
+
"conflict",
|
|
70
|
+
"developer_off",
|
|
71
|
+
"dimensions",
|
|
72
|
+
"expired",
|
|
73
|
+
"forbidden",
|
|
74
|
+
"gone",
|
|
75
|
+
"hidden",
|
|
76
|
+
"integrity",
|
|
77
|
+
"internal",
|
|
78
|
+
"invalid",
|
|
79
|
+
"invalid_cursor",
|
|
80
|
+
"limit",
|
|
81
|
+
"name_taken",
|
|
82
|
+
"needs_user",
|
|
83
|
+
"not_exposed",
|
|
84
|
+
"not_found",
|
|
85
|
+
"offline",
|
|
86
|
+
"path",
|
|
87
|
+
"paused",
|
|
88
|
+
"plugin_outdated",
|
|
89
|
+
"rate_limited",
|
|
90
|
+
"recording_ended",
|
|
91
|
+
"recording_off",
|
|
92
|
+
"refused",
|
|
93
|
+
"timeout",
|
|
94
|
+
"token_expired",
|
|
95
|
+
"too_large",
|
|
96
|
+
"too_old",
|
|
97
|
+
"type",
|
|
98
|
+
"unauthorized",
|
|
99
|
+
"unavailable",
|
|
100
|
+
"unsupported",
|
|
101
|
+
"upstream_failed"
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
"message": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"description": "For people."
|
|
107
|
+
},
|
|
108
|
+
"hint": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"description": "What to do about it, when the hub knows."
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"JsonValue": {
|
|
115
|
+
"description": "Any JSON value: an object, an array, a string, a number, a boolean or null. For data whose shape the hub does not define (a tool's input, a harness's own metadata). Clients map it to their JSON element type."
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
{
|
|
2
|
+
"paths": {
|
|
3
|
+
"/api/v1/auth": {
|
|
4
|
+
"get": {
|
|
5
|
+
"operationId": "getAuth",
|
|
6
|
+
"tags": ["account"],
|
|
7
|
+
"x-hub-access": "public",
|
|
8
|
+
"security": [],
|
|
9
|
+
"summary": "How this hub signs people in",
|
|
10
|
+
"description": "What a sign-in screen needs before anyone is signed in.",
|
|
11
|
+
"responses": {
|
|
12
|
+
"200": {
|
|
13
|
+
"description": "The hub's sign-in mode.",
|
|
14
|
+
"content": {
|
|
15
|
+
"application/json": {
|
|
16
|
+
"schema": {
|
|
17
|
+
"$ref": "#/components/schemas/AuthInfo"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"default": {
|
|
23
|
+
"$ref": "#/components/responses/Error"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"/api/v1/pair": {
|
|
29
|
+
"post": {
|
|
30
|
+
"operationId": "pairDevice",
|
|
31
|
+
"tags": ["account"],
|
|
32
|
+
"x-hub-access": "public",
|
|
33
|
+
"security": [],
|
|
34
|
+
"summary": "Pair a browser with a pairing code",
|
|
35
|
+
"description": "Trades a pairing code for a new device, whose secret comes back only as the HttpOnly device cookie (Set-Cookie). For browsers: the request must be same-origin. A native app redeems a link code instead (POST /api/v1/devices/redeem). Fails with 401 unauthorized for an unknown, used or expired code (codes last 5 minutes), 400 invalid for a device name longer than 64 characters, and 429 rate_limited after 10 failed attempts in a minute.",
|
|
36
|
+
"requestBody": {
|
|
37
|
+
"required": true,
|
|
38
|
+
"content": {
|
|
39
|
+
"application/json": {
|
|
40
|
+
"schema": {
|
|
41
|
+
"$ref": "#/components/schemas/PairRequest"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"responses": {
|
|
47
|
+
"200": {
|
|
48
|
+
"description": "The new device. Its secret is in the Set-Cookie header, not in the body.",
|
|
49
|
+
"content": {
|
|
50
|
+
"application/json": {
|
|
51
|
+
"schema": {
|
|
52
|
+
"$ref": "#/components/schemas/PairedDevice"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"default": {
|
|
58
|
+
"$ref": "#/components/responses/Error"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"/api/v1/devices/redeem": {
|
|
64
|
+
"post": {
|
|
65
|
+
"operationId": "redeemDeviceLink",
|
|
66
|
+
"tags": ["account"],
|
|
67
|
+
"x-hub-access": "public",
|
|
68
|
+
"security": [],
|
|
69
|
+
"summary": "Trade a link code for a device token",
|
|
70
|
+
"description": "How a native app signs in: it sends the single-use code of the console's \"Add the Hub app\" link and gets a device token, which it keeps and sends as `Authorization: Bearer`. The request carries no cookie, no bearer token and no Origin; the code is the credential. No cookie is set. Fails with 401 unauthorized for an unknown, used or expired code (codes last 5 minutes) and 400 invalid for a malformed public key (the code stays valid).",
|
|
71
|
+
"requestBody": {
|
|
72
|
+
"required": true,
|
|
73
|
+
"content": {
|
|
74
|
+
"application/json": {
|
|
75
|
+
"schema": {
|
|
76
|
+
"$ref": "#/components/schemas/RedeemLinkRequest"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"responses": {
|
|
82
|
+
"200": {
|
|
83
|
+
"description": "The new device and its token. The token is shown this once.",
|
|
84
|
+
"content": {
|
|
85
|
+
"application/json": {
|
|
86
|
+
"schema": {
|
|
87
|
+
"$ref": "#/components/schemas/LinkedDevice"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"default": {
|
|
93
|
+
"$ref": "#/components/responses/Error"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"/api/v1/me": {
|
|
99
|
+
"get": {
|
|
100
|
+
"operationId": "getMe",
|
|
101
|
+
"tags": ["account"],
|
|
102
|
+
"x-hub-access": "user",
|
|
103
|
+
"summary": "The signed-in device, its user and the hub",
|
|
104
|
+
"responses": {
|
|
105
|
+
"200": {
|
|
106
|
+
"description": "Who is asking, and which hub is answering.",
|
|
107
|
+
"content": {
|
|
108
|
+
"application/json": {
|
|
109
|
+
"schema": {
|
|
110
|
+
"$ref": "#/components/schemas/Me"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"default": {
|
|
116
|
+
"$ref": "#/components/responses/Error"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"/api/v1/devices": {
|
|
122
|
+
"get": {
|
|
123
|
+
"operationId": "listDevices",
|
|
124
|
+
"tags": ["account"],
|
|
125
|
+
"x-hub-access": "user",
|
|
126
|
+
"summary": "The user's devices",
|
|
127
|
+
"responses": {
|
|
128
|
+
"200": {
|
|
129
|
+
"description": "Every device of the user: paired browsers and linked apps.",
|
|
130
|
+
"content": {
|
|
131
|
+
"application/json": {
|
|
132
|
+
"schema": {
|
|
133
|
+
"$ref": "#/components/schemas/DeviceList"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"default": {
|
|
139
|
+
"$ref": "#/components/responses/Error"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"/api/v1/devices/link": {
|
|
145
|
+
"post": {
|
|
146
|
+
"operationId": "createDeviceLink",
|
|
147
|
+
"tags": ["account"],
|
|
148
|
+
"x-hub-access": "user",
|
|
149
|
+
"summary": "Issue a link code for a native app",
|
|
150
|
+
"description": "A single-use code that links a new device to the user, shown as a QR code or deep link. The request has no body. The code is redeemed at POST /api/v1/devices/redeem.",
|
|
151
|
+
"responses": {
|
|
152
|
+
"200": {
|
|
153
|
+
"description": "The code and where to redeem it.",
|
|
154
|
+
"content": {
|
|
155
|
+
"application/json": {
|
|
156
|
+
"schema": {
|
|
157
|
+
"$ref": "#/components/schemas/DeviceLink"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"default": {
|
|
163
|
+
"$ref": "#/components/responses/Error"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"/api/v1/devices/{id}": {
|
|
169
|
+
"delete": {
|
|
170
|
+
"operationId": "revokeDevice",
|
|
171
|
+
"tags": ["account"],
|
|
172
|
+
"x-hub-access": "user",
|
|
173
|
+
"summary": "Revoke one of the user's devices",
|
|
174
|
+
"description": "The device's cookie and token stop working. Fails with 404 not_found for an unknown device or another user's.",
|
|
175
|
+
"parameters": [
|
|
176
|
+
{
|
|
177
|
+
"name": "id",
|
|
178
|
+
"in": "path",
|
|
179
|
+
"required": true,
|
|
180
|
+
"description": "The device's ID.",
|
|
181
|
+
"schema": {
|
|
182
|
+
"type": "string"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
"responses": {
|
|
187
|
+
"204": {
|
|
188
|
+
"description": "Revoked."
|
|
189
|
+
},
|
|
190
|
+
"default": {
|
|
191
|
+
"$ref": "#/components/responses/Error"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"/api/v1/logout": {
|
|
197
|
+
"post": {
|
|
198
|
+
"operationId": "logout",
|
|
199
|
+
"tags": ["account"],
|
|
200
|
+
"x-hub-access": "user",
|
|
201
|
+
"summary": "Sign this device out",
|
|
202
|
+
"description": "Removes the calling device: its cookie and token stop working, and the cookie is cleared (Set-Cookie). The request has no body.",
|
|
203
|
+
"responses": {
|
|
204
|
+
"204": {
|
|
205
|
+
"description": "Signed out."
|
|
206
|
+
},
|
|
207
|
+
"default": {
|
|
208
|
+
"$ref": "#/components/responses/Error"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
"components": {
|
|
215
|
+
"schemas": {
|
|
216
|
+
"AuthInfo": {
|
|
217
|
+
"type": "object",
|
|
218
|
+
"required": ["mode", "signIn"],
|
|
219
|
+
"properties": {
|
|
220
|
+
"mode": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"description": "pairing: one owner, devices join with a pairing code. oidc: people sign in with the hub's identity provider.",
|
|
223
|
+
"x-hub-known": ["pairing", "oidc"]
|
|
224
|
+
},
|
|
225
|
+
"signIn": {
|
|
226
|
+
"type": ["string", "null"],
|
|
227
|
+
"description": "The path on this hub that starts a browser sign-in (oidc), or null (pairing)."
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"PairRequest": {
|
|
232
|
+
"type": "object",
|
|
233
|
+
"required": ["code"],
|
|
234
|
+
"properties": {
|
|
235
|
+
"code": {
|
|
236
|
+
"type": "string",
|
|
237
|
+
"description": "The 6-digit pairing code. Spaces around it are ignored."
|
|
238
|
+
},
|
|
239
|
+
"deviceName": {
|
|
240
|
+
"type": "string",
|
|
241
|
+
"description": "A name for the device, 1 to 64 characters after trimming. \"Device\" when absent or empty."
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"PairedDevice": {
|
|
246
|
+
"type": "object",
|
|
247
|
+
"required": ["device"],
|
|
248
|
+
"properties": {
|
|
249
|
+
"device": {
|
|
250
|
+
"$ref": "#/components/schemas/Device"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"RedeemLinkRequest": {
|
|
255
|
+
"type": "object",
|
|
256
|
+
"required": ["code"],
|
|
257
|
+
"properties": {
|
|
258
|
+
"code": {
|
|
259
|
+
"type": "string",
|
|
260
|
+
"description": "The link code, from the `code` parameter of the hubapp:// link."
|
|
261
|
+
},
|
|
262
|
+
"deviceName": {
|
|
263
|
+
"type": "string",
|
|
264
|
+
"description": "A name for the device. Control characters are dropped and it is cut to 64 characters. \"App\" when absent or empty."
|
|
265
|
+
},
|
|
266
|
+
"publicKey": {
|
|
267
|
+
"type": ["string", "null"],
|
|
268
|
+
"maxLength": 4096,
|
|
269
|
+
"description": "The public half of the device's signing key, PEM (BEGIN PUBLIC KEY) or its base64 body. Kept with the device for later strong approvals."
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
"LinkedDevice": {
|
|
274
|
+
"type": "object",
|
|
275
|
+
"required": ["device", "token"],
|
|
276
|
+
"properties": {
|
|
277
|
+
"device": {
|
|
278
|
+
"$ref": "#/components/schemas/Device"
|
|
279
|
+
},
|
|
280
|
+
"token": {
|
|
281
|
+
"type": "string",
|
|
282
|
+
"description": "The device token: the app's credential, sent as `Authorization: Bearer`."
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"Device": {
|
|
287
|
+
"type": "object",
|
|
288
|
+
"required": ["id", "name", "created", "lastSeen"],
|
|
289
|
+
"properties": {
|
|
290
|
+
"id": {
|
|
291
|
+
"type": "string"
|
|
292
|
+
},
|
|
293
|
+
"name": {
|
|
294
|
+
"type": "string"
|
|
295
|
+
},
|
|
296
|
+
"created": {
|
|
297
|
+
"type": "integer",
|
|
298
|
+
"format": "int64",
|
|
299
|
+
"description": "When it was paired or linked, in epoch milliseconds."
|
|
300
|
+
},
|
|
301
|
+
"lastSeen": {
|
|
302
|
+
"type": "integer",
|
|
303
|
+
"format": "int64",
|
|
304
|
+
"description": "When the hub last saw it, in epoch milliseconds."
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
"User": {
|
|
309
|
+
"type": "object",
|
|
310
|
+
"required": ["id", "role"],
|
|
311
|
+
"properties": {
|
|
312
|
+
"id": {
|
|
313
|
+
"type": "string",
|
|
314
|
+
"description": "\"local\" for the owner of a pairing-mode hub."
|
|
315
|
+
},
|
|
316
|
+
"email": {
|
|
317
|
+
"type": "string",
|
|
318
|
+
"description": "Absent for the owner of a pairing-mode hub."
|
|
319
|
+
},
|
|
320
|
+
"name": {
|
|
321
|
+
"type": "string",
|
|
322
|
+
"description": "Absent when the identity provider gave none."
|
|
323
|
+
},
|
|
324
|
+
"role": {
|
|
325
|
+
"type": "string",
|
|
326
|
+
"x-hub-known": ["admin", "user"]
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
"Me": {
|
|
331
|
+
"type": "object",
|
|
332
|
+
"required": ["device", "user", "auth", "publicURL", "version"],
|
|
333
|
+
"properties": {
|
|
334
|
+
"device": {
|
|
335
|
+
"$ref": "#/components/schemas/Device"
|
|
336
|
+
},
|
|
337
|
+
"user": {
|
|
338
|
+
"$ref": "#/components/schemas/User"
|
|
339
|
+
},
|
|
340
|
+
"auth": {
|
|
341
|
+
"type": "string",
|
|
342
|
+
"description": "The hub's sign-in mode, as in AuthInfo.mode.",
|
|
343
|
+
"x-hub-known": ["pairing", "oidc"]
|
|
344
|
+
},
|
|
345
|
+
"publicURL": {
|
|
346
|
+
"type": "string",
|
|
347
|
+
"description": "The address the hub is reached at, without a trailing slash."
|
|
348
|
+
},
|
|
349
|
+
"version": {
|
|
350
|
+
"type": "string",
|
|
351
|
+
"description": "The hub's version."
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
"ListedDevice": {
|
|
356
|
+
"type": "object",
|
|
357
|
+
"description": "A device in the user's list: a Device, and whether it is the one asking.",
|
|
358
|
+
"required": ["id", "name", "created", "lastSeen", "current"],
|
|
359
|
+
"properties": {
|
|
360
|
+
"id": {
|
|
361
|
+
"type": "string"
|
|
362
|
+
},
|
|
363
|
+
"name": {
|
|
364
|
+
"type": "string"
|
|
365
|
+
},
|
|
366
|
+
"created": {
|
|
367
|
+
"type": "integer",
|
|
368
|
+
"format": "int64",
|
|
369
|
+
"description": "When it was paired or linked, in epoch milliseconds."
|
|
370
|
+
},
|
|
371
|
+
"lastSeen": {
|
|
372
|
+
"type": "integer",
|
|
373
|
+
"format": "int64",
|
|
374
|
+
"description": "When the hub last saw it, in epoch milliseconds."
|
|
375
|
+
},
|
|
376
|
+
"current": {
|
|
377
|
+
"type": "boolean",
|
|
378
|
+
"description": "True for the device that made this request."
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
"DeviceList": {
|
|
383
|
+
"type": "object",
|
|
384
|
+
"required": ["devices"],
|
|
385
|
+
"properties": {
|
|
386
|
+
"devices": {
|
|
387
|
+
"type": "array",
|
|
388
|
+
"items": {
|
|
389
|
+
"$ref": "#/components/schemas/ListedDevice"
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"DeviceLink": {
|
|
395
|
+
"type": "object",
|
|
396
|
+
"required": ["code", "expiresAt", "hub", "url"],
|
|
397
|
+
"properties": {
|
|
398
|
+
"code": {
|
|
399
|
+
"type": "string",
|
|
400
|
+
"description": "The single-use link code."
|
|
401
|
+
},
|
|
402
|
+
"expiresAt": {
|
|
403
|
+
"type": "integer",
|
|
404
|
+
"format": "int64",
|
|
405
|
+
"description": "When the code stops working, in epoch milliseconds (5 minutes after it was issued)."
|
|
406
|
+
},
|
|
407
|
+
"hub": {
|
|
408
|
+
"type": "string",
|
|
409
|
+
"description": "The hub's public address, where the app redeems the code."
|
|
410
|
+
},
|
|
411
|
+
"url": {
|
|
412
|
+
"type": "string",
|
|
413
|
+
"description": "The deep link to show as a QR code: hubapp://link?hub=<hub>&code=<code>."
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|