rvis-aiui-kit 1.0.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.
Files changed (44) hide show
  1. package/README.md +22 -0
  2. package/components/image/index.ink +44 -0
  3. package/components/list/index.ink +198 -0
  4. package/components/list/readme.md +71 -0
  5. package/components/markdown/index.ink +109 -0
  6. package/components/markdown/parser.js +149 -0
  7. package/components/markdown/readme.md +50 -0
  8. package/components/model-list/index.ink +181 -0
  9. package/components/model-list/readme.md +111 -0
  10. package/components/paragraph/index.ink +61 -0
  11. package/components/paragraph/readme.md +17 -0
  12. package/components/table/index.ink +272 -0
  13. package/components/table/readme.md +83 -0
  14. package/package.json +35 -0
  15. package/sdk/README.md +1013 -0
  16. package/sdk/api-map.js +25 -0
  17. package/sdk/core/client.js +641 -0
  18. package/sdk/core/constants.js +10 -0
  19. package/sdk/core/transport.js +55 -0
  20. package/sdk/index.js +19 -0
  21. package/sdk/modules/audio/index.js +177 -0
  22. package/sdk/modules/audio/readme.md +136 -0
  23. package/sdk/modules/camera/index.js +380 -0
  24. package/sdk/modules/camera/readme.md +302 -0
  25. package/sdk/modules/device-context/index.js +206 -0
  26. package/sdk/modules/device-context/readme.md +243 -0
  27. package/sdk/modules/face/index.js +108 -0
  28. package/sdk/modules/face/readme.md +196 -0
  29. package/sdk/modules/motion/index.js +116 -0
  30. package/sdk/modules/motion/readme.md +148 -0
  31. package/sdk/modules/notification/index.js +192 -0
  32. package/sdk/modules/notification/readme.md +175 -0
  33. package/sdk/modules/offline-command/index.js +265 -0
  34. package/sdk/modules/offline-command/readme.md +143 -0
  35. package/sdk/modules/screen/index.js +64 -0
  36. package/sdk/modules/screen/readme.md +110 -0
  37. package/sdk/modules/tts/index.js +75 -0
  38. package/sdk/modules/tts/readme.md +162 -0
  39. package/sdk/utils/api-builder.js +16 -0
  40. package/sdk/utils/case.js +19 -0
  41. package/sdk/utils/errors.js +32 -0
  42. package/sdk/utils/events.js +22 -0
  43. package/sdk/utils/message.js +63 -0
  44. package/sdk/utils/request-id.js +18 -0
@@ -0,0 +1,206 @@
1
+ import {
2
+ COMPLETION_EVENT,
3
+ COMPLETION_READY_EVENT_STREAM
4
+ } from '../../core/constants.js';
5
+ import { Glass3Error } from '../../utils/errors.js';
6
+ import { stripSensitiveFields } from '../../utils/message.js';
7
+
8
+ const DEVICE_CONTEXT_DOMAINS = [
9
+ 'identity',
10
+ 'software',
11
+ 'account',
12
+ 'environment',
13
+ 'power',
14
+ 'network',
15
+ 'interaction'
16
+ ];
17
+
18
+ function createParameterError(message) {
19
+ return new Glass3Error(message, {
20
+ code: 'INVALID_PARAMS',
21
+ stage: 'params',
22
+ namespace: 'rokid.tools',
23
+ method: 'invoke'
24
+ });
25
+ }
26
+
27
+ function buildContextArgs(args, methodName) {
28
+ if (
29
+ args !== undefined &&
30
+ args !== null &&
31
+ (typeof args !== 'object' || Array.isArray(args))
32
+ ) {
33
+ throw createParameterError(
34
+ `deviceContext.${methodName} parameters must be an object`
35
+ );
36
+ }
37
+
38
+ const input = args || {};
39
+ const fields = Object.keys(input);
40
+ fields.forEach(name => {
41
+ if (name !== 'domains') {
42
+ throw createParameterError(
43
+ `deviceContext.${methodName} does not support ${name}`
44
+ );
45
+ }
46
+ });
47
+
48
+ if (input.domains === undefined) return {};
49
+
50
+ if (!Array.isArray(input.domains) || input.domains.length === 0) {
51
+ throw createParameterError(
52
+ `deviceContext.${methodName} domains must be a non-empty array`
53
+ );
54
+ }
55
+
56
+ input.domains.forEach((domain, index) => {
57
+ if (typeof domain !== 'string') {
58
+ throw createParameterError(
59
+ `deviceContext.${methodName} domains[${index}] must be a string`
60
+ );
61
+ }
62
+
63
+ if (DEVICE_CONTEXT_DOMAINS.indexOf(domain) === -1) {
64
+ throw createParameterError(
65
+ `deviceContext.${methodName} does not support domain ${domain}`
66
+ );
67
+ }
68
+ });
69
+
70
+ return { domains: input.domains.slice() };
71
+ }
72
+
73
+ function buildGetArgs(args) {
74
+ return buildContextArgs(args, 'get');
75
+ }
76
+
77
+ function buildObserveArgs(args) {
78
+ return buildContextArgs(args, 'observe');
79
+ }
80
+
81
+ function buildStopObserveArgs(args) {
82
+ if (
83
+ !args ||
84
+ typeof args !== 'object' ||
85
+ Array.isArray(args) ||
86
+ typeof args.requestId !== 'string' ||
87
+ !args.requestId.trim()
88
+ ) {
89
+ throw createParameterError(
90
+ 'deviceContext.stopObserve requires a requestId'
91
+ );
92
+ }
93
+
94
+ const fields = Object.keys(args);
95
+ if (fields.length !== 1 || fields[0] !== 'requestId') {
96
+ throw createParameterError(
97
+ 'deviceContext.stopObserve only supports requestId'
98
+ );
99
+ }
100
+
101
+ return { targetRequestId: args.requestId };
102
+ }
103
+
104
+ function isStartedResponse(result, requestId) {
105
+ return Boolean(
106
+ result &&
107
+ result.accepted === true &&
108
+ result.requestId === requestId &&
109
+ result.toolName === 'observeDeviceContext' &&
110
+ result.toolAction === 'start' &&
111
+ result.state === 'started'
112
+ );
113
+ }
114
+
115
+ function selectContextEventResult(eventData) {
116
+ return Object.assign(
117
+ { requestId: eventData.requestId },
118
+ stripSensitiveFields(eventData.result)
119
+ );
120
+ }
121
+
122
+ const deviceContextGet = {
123
+ publicModule: 'deviceContext',
124
+ publicMethod: 'get',
125
+
126
+ namespace: 'rokid.tools',
127
+ method: 'invoke',
128
+ toolName: 'getDeviceContext',
129
+ toolAction: 'start',
130
+
131
+ completion: COMPLETION_EVENT,
132
+ eventNamespace: 'rokid.tools',
133
+ eventName: 'toolResult',
134
+ buildArgs: buildGetArgs,
135
+ transformEventResult: stripSensitiveFields,
136
+ isTerminalResult(result) {
137
+ return Boolean(result && result.eventType === 'snapshot');
138
+ },
139
+ selectEventResult: selectContextEventResult
140
+ };
141
+
142
+ const deviceContextObserve = {
143
+ publicModule: 'deviceContext',
144
+ publicMethod: 'observe',
145
+
146
+ namespace: 'rokid.tools',
147
+ method: 'invoke',
148
+ toolName: 'observeDeviceContext',
149
+ toolAction: 'start',
150
+
151
+ completion: COMPLETION_READY_EVENT_STREAM,
152
+ eventNamespace: 'rokid.tools',
153
+ eventName: 'toolResult',
154
+ responseCompletionStates: ['started'],
155
+ buildArgs: buildObserveArgs,
156
+ transformEventResult: stripSensitiveFields,
157
+ isAcceptedResponseResult: isStartedResponse,
158
+ isErrorResult(result) {
159
+ return Boolean(result && result.ok === false);
160
+ },
161
+ isStreamTerminalResult(result) {
162
+ return Boolean(
163
+ result &&
164
+ (
165
+ result.eventType === 'invalidated' ||
166
+ result.state === 'canceled'
167
+ )
168
+ );
169
+ },
170
+ selectReadyResult(requestId, result) {
171
+ return {
172
+ requestId,
173
+ ok: result.ok,
174
+ state: result.state
175
+ };
176
+ }
177
+ };
178
+
179
+ const deviceContextStopObserve = {
180
+ publicModule: 'deviceContext',
181
+ publicMethod: 'stopObserve',
182
+
183
+ namespace: 'rokid.tools',
184
+ method: 'invoke',
185
+ toolName: 'observeDeviceContext',
186
+ toolAction: 'stop',
187
+
188
+ completion: COMPLETION_EVENT,
189
+ eventNamespace: 'rokid.tools',
190
+ eventName: 'toolResult',
191
+ terminalStates: ['stopped'],
192
+ buildArgs: buildStopObserveArgs,
193
+ transformEventResult: stripSensitiveFields,
194
+ releaseTargetInvocation: {
195
+ publicModule: 'deviceContext',
196
+ publicMethod: 'observe'
197
+ },
198
+ selectEventResult: selectContextEventResult
199
+ };
200
+
201
+ export { DEVICE_CONTEXT_DOMAINS };
202
+ export default [
203
+ deviceContextGet,
204
+ deviceContextObserve,
205
+ deviceContextStopObserve
206
+ ];
@@ -0,0 +1,243 @@
1
+ # Device Context API
2
+
3
+ `deviceContext` 模块通过 `glass3.deviceContext` 提供设备上下文查询和订阅能力,覆盖
4
+ 设备、软件、账号、运行环境、电量、网络与交互状态。
5
+
6
+ 页面必须在 `onMessage` 中调用 `glass3.handleMessage(messageEvent)`,查询结果和订阅
7
+ 事件才能被关联到对应请求。订阅结束前应保存 start requestId,并在页面卸载时调用
8
+ `stopObserve`。
9
+
10
+ ## API
11
+
12
+ | API | 说明 |
13
+ | --- | --- |
14
+ | `deviceContext.get(options?)` | 一次性获取原子上下文快照。 |
15
+ | `deviceContext.observe(options?, callOptions?)` | 订阅初始快照与后续状态变化。 |
16
+ | `deviceContext.stopObserve({ requestId })` | 停止指定订阅。 |
17
+
18
+ ## Domain
19
+
20
+ `get` 和 `observe` 接受相同的可选参数:
21
+
22
+ ```js
23
+ {
24
+ domains: ['identity', 'software', 'account', 'environment']
25
+ }
26
+ ```
27
+
28
+ 可选 domain:
29
+
30
+ ```text
31
+ identity, software, account, environment, power, network, interaction
32
+ ```
33
+
34
+ - 省略整个参数、传 `{}` 或传 `{ domains: undefined }` 表示全部 domain;SDK 不会
35
+ 将其展开为固定列表。
36
+ - 显式传入 `domains` 时必须是非空数组,每个元素必须是支持的字符串。
37
+ - 协议未禁止重复 domain,SDK 不主动去重或拒绝。
38
+ - 参数对象是闭合结构,不接受其他字段。
39
+
40
+ 参数不合法时抛出 `code: "INVALID_PARAMS"` 的 `Glass3Error`。
41
+
42
+ ## `deviceContext.get`
43
+
44
+ ```js
45
+ const result = await glass3.deviceContext.get({
46
+ domains: ['environment', 'power', 'network', 'interaction']
47
+ });
48
+
49
+ console.log(JSON.stringify(result.snapshot));
50
+ ```
51
+
52
+ accepted response 只表示 Native 已受理。Promise 等到唯一的
53
+ `eventType: "snapshot"` 业务事件后完成:
54
+
55
+ ```js
56
+ {
57
+ requestId: 'get request UUID',
58
+ ok: true,
59
+ eventType: 'snapshot',
60
+ snapshot: {
61
+ schemaVersion: '1.0',
62
+ epoch: 'system-server-process-uuid',
63
+ revision: 12,
64
+ capturedAtElapsedMs: 368219,
65
+ environment: {
66
+ envType: 'custom',
67
+ customBaseUrl: 'https://private.example.com/base',
68
+ endpoints: {
69
+ apiBaseUrl: 'https://private.example.com/base',
70
+ h5BaseUrl: 'https://private.example.com/base',
71
+ inspectH5BaseUrl: 'https://private.example.com/base'
72
+ },
73
+ environmentRevision: 2
74
+ },
75
+ power: {
76
+ available: true,
77
+ levelPercent: 76,
78
+ charging: false,
79
+ plugType: 'none'
80
+ },
81
+ network: {
82
+ wifiEnabled: true,
83
+ localConnected: true,
84
+ localValidated: true,
85
+ metered: false,
86
+ transport: 'wifi',
87
+ rssi: -54,
88
+ effectiveRoute: 'glass_direct',
89
+ online: true
90
+ },
91
+ interaction: {
92
+ screenState: 'on'
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## `deviceContext.observe`
99
+
100
+ ```js
101
+ const observer = await glass3.deviceContext.observe(
102
+ {
103
+ domains: ['account', 'environment', 'power', 'network']
104
+ },
105
+ {
106
+ onEvent(event) {
107
+ switch (event.eventType) {
108
+ case 'snapshot':
109
+ initializeContext(event.snapshot);
110
+ break;
111
+ case 'changed':
112
+ applyContextPatch(event);
113
+ break;
114
+ case 'invalidated':
115
+ clearAccountContext();
116
+ reloadScene();
117
+ break;
118
+ }
119
+ }
120
+ }
121
+ );
122
+ ```
123
+
124
+ Promise 在合法的 started response 到达后完成:
125
+
126
+ ```js
127
+ {
128
+ requestId: 'observe request UUID',
129
+ ok: true,
130
+ state: 'started'
131
+ }
132
+ ```
133
+
134
+ 要接收初始快照与后续通知,必须提供 `callOptions.onEvent`。首个业务事件固定为
135
+ `eventType: "snapshot"`,结构与 `get` 的 snapshot 事件相同。
136
+
137
+ 实际状态发生边沿变化后,Native 发送增量事件:
138
+
139
+ ```js
140
+ {
141
+ ok: true,
142
+ eventType: 'changed',
143
+ schemaVersion: '1.0',
144
+ epoch: 'system-server-process-uuid',
145
+ baseRevision: 12,
146
+ revision: 13,
147
+ changedAtElapsedMs: 369100,
148
+ changedDomains: ['power'],
149
+ reason: 'battery_changed',
150
+ patch: {
151
+ power: {
152
+ available: true,
153
+ levelPercent: 75,
154
+ charging: false,
155
+ plugType: 'none'
156
+ }
157
+ }
158
+ }
159
+ ```
160
+
161
+ SDK 不缓存快照,也不自动合并 patch。调用方合并前必须验证:
162
+
163
+ ```js
164
+ event.epoch === currentSnapshot.epoch &&
165
+ event.baseRevision === currentSnapshot.revision
166
+ ```
167
+
168
+ epoch 不同或 revision 不连续时,不应继续使用旧上下文;应重新查询或重载 Scene。
169
+
170
+ 账号或环境发生隔离边界变化时,Native 发送终态:
171
+
172
+ ```js
173
+ {
174
+ ok: true,
175
+ eventType: 'invalidated',
176
+ state: 'canceled',
177
+ reason: 'context_invalidated',
178
+ invalidatedDomains: ['account', 'environment'],
179
+ requiresReload: true,
180
+ epoch: 'system-server-process-uuid',
181
+ revision: 14
182
+ }
183
+ ```
184
+
185
+ 该事件会先进入 `onEvent`,随后 SDK 释放订阅监听。调用方必须停止使用旧上下文、
186
+ 清除账号相关内存状态并重载 Scene;重载后重新订阅。SDK 不自动执行导航或重载。
187
+
188
+ ## `deviceContext.stopObserve`
189
+
190
+ ```js
191
+ const result = await glass3.deviceContext.stopObserve({
192
+ requestId: observer.requestId
193
+ });
194
+ ```
195
+
196
+ SDK 将公共参数转换为:
197
+
198
+ ```js
199
+ { targetRequestId: observer.requestId }
200
+ ```
201
+
202
+ stop 使用独立 requestId,并等待归属于 stop 调用的 stopped event:
203
+
204
+ ```js
205
+ {
206
+ requestId: 'stop request UUID',
207
+ ok: true,
208
+ state: 'stopped'
209
+ }
210
+ ```
211
+
212
+ 完成后 SDK 释放目标 observe 的本地监听。协议不会再为原 observe requestId 补发
213
+ 第二个终态。
214
+
215
+ ## 可选字段和枚举值
216
+
217
+ SDK 保留协议字段名和枚举值,不为缺失字段补默认值:
218
+
219
+ - 电量不可读时没有 `levelPercent`。
220
+ - 非 Wi-Fi 网络没有 `rssi`。
221
+ - 非 custom 环境没有 `customBaseUrl`。
222
+ - `effectiveRoute: "phone_relay"` 时,手机在线状态未同步,首版没有 `online`;不能
223
+ 将眼镜本地网络状态推断为手机中继可用性。
224
+ - `logged_in`、`glass_direct`、`phone_relay` 等枚举字符串不会转成 camelCase。
225
+
226
+ 需要区分 `false` 与“未提供”时,应检查字段是否存在。
227
+
228
+ ## 敏感信息
229
+
230
+ 协议不允许 token、Authorization、Cookie、secretKey 或任何 token 字段进入返回值、
231
+ 事件、Schema 和日志。SDK 对业务结果和日志额外执行递归清理;`identity.customerProfile`
232
+ 当前不属于公开结构。
233
+
234
+ ## Native 映射
235
+
236
+ | SDK | `toolName` | `toolAction` |
237
+ | --- | --- | --- |
238
+ | `deviceContext.get` | `getDeviceContext` | `start` |
239
+ | `deviceContext.observe` | `observeDeviceContext` | `start` |
240
+ | `deviceContext.stopObserve` | `observeDeviceContext` | `stop` |
241
+
242
+ 全部调用使用 RPC `version: "2.0.0"`、`namespace: "rokid.tools"`、
243
+ `method: "invoke"`。
@@ -0,0 +1,108 @@
1
+ import {
2
+ COMPLETION_EVENT,
3
+ COMPLETION_READY_EVENT_STREAM
4
+ } from '../../core/constants.js';
5
+ import { snakeToCamelCase } from '../../utils/case.js';
6
+ import { Glass3Error } from '../../utils/errors.js';
7
+
8
+ function createParameterError(message) {
9
+ return new Glass3Error(message, {
10
+ code: 'INVALID_PARAMS',
11
+ stage: 'params',
12
+ namespace: 'rokid.tools',
13
+ method: 'invoke'
14
+ });
15
+ }
16
+
17
+ function buildStartRecognizeArgs(args) {
18
+ if (args === undefined || args === null) return {};
19
+
20
+ if (
21
+ typeof args !== 'object' ||
22
+ Array.isArray(args) ||
23
+ Object.keys(args).length !== 0
24
+ ) {
25
+ throw createParameterError('face.startRecognize does not accept parameters');
26
+ }
27
+
28
+ return {};
29
+ }
30
+
31
+ function buildStopRecognizeArgs(args) {
32
+ if (
33
+ !args ||
34
+ typeof args !== 'object' ||
35
+ Array.isArray(args) ||
36
+ typeof args.requestId !== 'string' ||
37
+ !args.requestId
38
+ ) {
39
+ throw createParameterError('face.stopRecognize requires a requestId');
40
+ }
41
+
42
+ const inputFields = Object.keys(args);
43
+ if (inputFields.length !== 1 || inputFields[0] !== 'requestId') {
44
+ throw createParameterError('face.stopRecognize only supports requestId');
45
+ }
46
+
47
+ return {
48
+ targetRequestId: args.requestId
49
+ };
50
+ }
51
+
52
+ const faceStartRecognize = {
53
+ publicModule: 'face',
54
+ publicMethod: 'startRecognize',
55
+
56
+ namespace: 'rokid.tools',
57
+ method: 'invoke',
58
+ toolName: 'faceRecognize',
59
+ toolAction: 'start',
60
+
61
+ completion: COMPLETION_READY_EVENT_STREAM,
62
+ eventNamespace: 'rokid.tools',
63
+ eventName: 'toolResult',
64
+ responseCompletionStates: ['started'],
65
+ buildArgs: buildStartRecognizeArgs,
66
+ transformEventResult: snakeToCamelCase,
67
+ isErrorResult(result) {
68
+ return Boolean(result && result.ok === false);
69
+ },
70
+ isReadyResult(result) {
71
+ return Boolean(result && result.state === 'started');
72
+ },
73
+ isStreamTerminalResult(result) {
74
+ return Boolean(result && result.state === 'stopped');
75
+ },
76
+ selectReadyResult(requestId, result) {
77
+ return {
78
+ requestId,
79
+ ok: result.ok
80
+ };
81
+ }
82
+ };
83
+
84
+ const faceStopRecognize = {
85
+ publicModule: 'face',
86
+ publicMethod: 'stopRecognize',
87
+
88
+ namespace: 'rokid.tools',
89
+ method: 'invoke',
90
+ toolName: 'faceRecognize',
91
+ toolAction: 'stop',
92
+
93
+ completion: COMPLETION_EVENT,
94
+ eventNamespace: 'rokid.tools',
95
+ eventName: 'toolResult',
96
+ terminalStates: ['stopped'],
97
+ responseCompletionStates: ['stopped'],
98
+ buildArgs: buildStopRecognizeArgs,
99
+ transformEventResult: snakeToCamelCase,
100
+ selectEventResult(eventData) {
101
+ return Object.assign(
102
+ { requestId: eventData.requestId },
103
+ snakeToCamelCase(eventData.result)
104
+ );
105
+ }
106
+ };
107
+
108
+ export default [faceStartRecognize, faceStopRecognize];