ms-vite-plugin 1.1.2 → 1.1.4

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 (94) hide show
  1. package/dist/build.js +6 -0
  2. package/dist/cli.js +91 -1
  3. package/dist/mcp/device-config.d.ts +55 -0
  4. package/dist/mcp/device-config.js +183 -0
  5. package/dist/mcp/docs-service.d.ts +65 -0
  6. package/dist/mcp/docs-service.js +168 -0
  7. package/dist/mcp/project.d.ts +16 -0
  8. package/dist/mcp/project.js +74 -0
  9. package/dist/mcp/tools.d.ts +18 -0
  10. package/dist/mcp/tools.js +825 -0
  11. package/dist/mcp/types.d.ts +32 -0
  12. package/dist/mcp/types.js +11 -0
  13. package/dist/mcp-server.d.ts +2 -0
  14. package/dist/mcp-server.js +86 -0
  15. package/dist/project.d.ts +89 -0
  16. package/dist/project.js +306 -0
  17. package/dist/version.d.ts +12 -0
  18. package/dist/version.js +63 -0
  19. package/docs/api/action.md +922 -0
  20. package/docs/api/appleocr.md +229 -0
  21. package/docs/api/config.md +122 -0
  22. package/docs/api/cryptoUtils.md +232 -0
  23. package/docs/api/device.md +374 -0
  24. package/docs/api/file.md +516 -0
  25. package/docs/api/global.md +617 -0
  26. package/docs/api/hid.md +1032 -0
  27. package/docs/api/hotUpdate.md +166 -0
  28. package/docs/api/http.md +548 -0
  29. package/docs/api/image.md +907 -0
  30. package/docs/api/ime.md +290 -0
  31. package/docs/api/logger.md +324 -0
  32. package/docs/api/media.md +248 -0
  33. package/docs/api/mysql.md +441 -0
  34. package/docs/api/netCard.md +200 -0
  35. package/docs/api/node.md +353 -0
  36. package/docs/api/paddleocr.md +246 -0
  37. package/docs/api/pip.md +242 -0
  38. package/docs/api/system.md +572 -0
  39. package/docs/api/thread.md +269 -0
  40. package/docs/api/tomatoocr.md +425 -0
  41. package/docs/api/tts.md +334 -0
  42. package/docs/api/ui.md +947 -0
  43. package/docs/api/utils.md +265 -0
  44. package/docs/api/yolo.md +310 -0
  45. package/docs/apicn/action.md +919 -0
  46. package/docs/apicn/appleocr.md +233 -0
  47. package/docs/apicn/config.md +120 -0
  48. package/docs/apicn/device.md +385 -0
  49. package/docs/apicn/file.md +511 -0
  50. package/docs/apicn/global.md +613 -0
  51. package/docs/apicn/hid.md +1033 -0
  52. package/docs/apicn/hotUpdate.md +170 -0
  53. package/docs/apicn/http.md +672 -0
  54. package/docs/apicn/image.md +924 -0
  55. package/docs/apicn/ime.md +290 -0
  56. package/docs/apicn/logger.md +332 -0
  57. package/docs/apicn/media.md +252 -0
  58. package/docs/apicn/mysql.md +445 -0
  59. package/docs/apicn/netCard.md +200 -0
  60. package/docs/apicn/node.md +362 -0
  61. package/docs/apicn/paddleocr.md +255 -0
  62. package/docs/apicn/pip.md +242 -0
  63. package/docs/apicn/system.md +575 -0
  64. package/docs/apicn/thread.md +269 -0
  65. package/docs/apicn/tts.md +338 -0
  66. package/docs/apicn/ui.md +933 -0
  67. package/docs/apicn/utils.md +265 -0
  68. package/docs/apicn/yolo.md +314 -0
  69. package/docs/apipython/action.md +901 -0
  70. package/docs/apipython/appleocr.md +226 -0
  71. package/docs/apipython/config.md +126 -0
  72. package/docs/apipython/cryptoUtils.md +246 -0
  73. package/docs/apipython/device.md +365 -0
  74. package/docs/apipython/file.md +476 -0
  75. package/docs/apipython/g.md +154 -0
  76. package/docs/apipython/hid.md +1059 -0
  77. package/docs/apipython/hotUpdate.md +154 -0
  78. package/docs/apipython/image.md +938 -0
  79. package/docs/apipython/ime.md +306 -0
  80. package/docs/apipython/logger.md +330 -0
  81. package/docs/apipython/media.md +221 -0
  82. package/docs/apipython/mysql.md +432 -0
  83. package/docs/apipython/netCard.md +219 -0
  84. package/docs/apipython/node.md +331 -0
  85. package/docs/apipython/overview.md +66 -0
  86. package/docs/apipython/paddleocr.md +211 -0
  87. package/docs/apipython/pip.md +231 -0
  88. package/docs/apipython/system.md +458 -0
  89. package/docs/apipython/tomatoocr.md +444 -0
  90. package/docs/apipython/tts.md +331 -0
  91. package/docs/apipython/ui.md +949 -0
  92. package/docs/apipython/utils.md +284 -0
  93. package/docs/apipython/yolo.md +281 -0
  94. package/package.json +8 -4
@@ -0,0 +1,476 @@
1
+ # 文件模块 (File)
2
+
3
+ 文件模块提供完整的文件与目录操作功能,包括目录管理、文件基础操作、文件读写、按行操作、文件信息等。
4
+
5
+ ## 功能概览
6
+
7
+ - 目录管理:获取应用目录、创建目录、列出目录内容
8
+ - 文件操作:创建、删除、复制、检查存在性
9
+ - 文件读写:支持整文件与按行读写
10
+ - 文件信息:获取文件 MD5 值
11
+
12
+ ## 注意事项
13
+
14
+ - **文件路径**: 所有文件路径均为绝对路径,不支持相对路径。
15
+ - **目录查看**: `documents`目录可以通过`文件`->`我的iPhone`->`快点JS`查看,其他目录不可直接查看
16
+
17
+ ## API 参考
18
+
19
+ ### 目录操作
20
+
21
+ #### getInternalDir - 获取应用内部目录路径
22
+
23
+ ```python
24
+ from typing import Literal
25
+
26
+ def getInternalDir(type: Literal["documents","library","temp","libraryCaches"]) -> str
27
+ ```
28
+
29
+ **参数:**
30
+
31
+ | 参数名 | 类型 | 是否必填 | 描述 |
32
+ | ------ | ------ | -------- | ------------------------------------------------------------------------------------- |
33
+ | `type` | string | 是 | 目录类型:`"documents"` 文档、`"library"` 库、`"temp"` 临时、`"libraryCaches"` 库缓存 |
34
+
35
+ **返回值:**
36
+
37
+ | 类型 | 描述 |
38
+ | ----- | ---------------- |
39
+ | `str` | 应用内部目录路径 |
40
+
41
+ **示例:**
42
+
43
+ ```python
44
+ from kuaijs import file
45
+ documents_dir = file.getInternalDir("documents")
46
+ temp_dir = file.getInternalDir("temp")
47
+ print(documents_dir)
48
+ print(temp_dir)
49
+ ```
50
+
51
+ #### getDataDir - 获取应用数据目录路径
52
+
53
+ ```python
54
+ def getDataDir() -> str
55
+ ```
56
+
57
+ **返回值:**
58
+
59
+ | 类型 | 描述 |
60
+ | ----- | ---------------- |
61
+ | `str` | 应用数据目录路径 |
62
+
63
+ **示例:**
64
+
65
+ ```python
66
+ from kuaijs import file
67
+ data_dir = file.getDataDir()
68
+ print(data_dir)
69
+ ```
70
+
71
+ #### getDataFile - 获取应用数据文件的完整路径
72
+
73
+ ```python
74
+ def getDataFile(file: str) -> str
75
+ ```
76
+
77
+ **参数:**
78
+
79
+ | 参数名 | 类型 | 是否必填 | 描述 |
80
+ | ------ | ---- | -------- | ------------ |
81
+ | `file` | str | 是 | 文件名或路径 |
82
+
83
+ **返回值:**
84
+
85
+ | 类型 | 描述 |
86
+ | ----- | ------------ |
87
+ | `str` | 文件完整路径 |
88
+
89
+ **示例:**
90
+
91
+ ```python
92
+ from kuaijs import file
93
+ config_path = file.getDataFile("config.json")
94
+ print(config_path)
95
+ ```
96
+
97
+ #### mkdirs - 创建目录(支持递归创建)
98
+
99
+ ```python
100
+ def mkdirs(path: str) -> bool
101
+ ```
102
+
103
+ **参数:**
104
+
105
+ | 参数名 | 类型 | 是否必填 | 描述 |
106
+ | ------ | ---- | -------- | -------- |
107
+ | `path` | str | 是 | 目录路径 |
108
+
109
+ **返回值:**
110
+
111
+ | 类型 | 描述 |
112
+ | ------ | ------------ |
113
+ | `bool` | 是否创建成功 |
114
+
115
+ **示例:**
116
+
117
+ ```python
118
+ from kuaijs import file
119
+ ok = file.mkdirs("/path/to/nested/directory")
120
+ print(ok)
121
+ ```
122
+
123
+ #### listDir - 列出目录下的所有文件和子目录
124
+
125
+ ```python
126
+ from typing import List
127
+
128
+ def listDir(path: str, recursion: bool) -> List[str]
129
+ ```
130
+
131
+ **参数:**
132
+
133
+ | 参数名 | 类型 | 是否必填 | 描述 |
134
+ | ----------- | ---- | -------- | ---------------------- |
135
+ | `path` | str | 是 | 目录路径 |
136
+ | `recursion` | bool | 是 | 是否递归列出子目录内容 |
137
+
138
+ **返回值:**
139
+
140
+ | 类型 | 描述 |
141
+ | ----------- | ------------------ |
142
+ | `List[str]` | 文件和目录路径数组 |
143
+
144
+ **示例:**
145
+
146
+ ```python
147
+ from kuaijs import file
148
+ files = file.listDir("/path/to/directory", False)
149
+ all_files = file.listDir("/path/to/directory", True)
150
+ print(files)
151
+ print(all_files)
152
+ ```
153
+
154
+ ### 文件基础操作
155
+
156
+ #### create - 创建新文件
157
+
158
+ ```python
159
+ def create(path: str) -> bool
160
+ ```
161
+
162
+ **参数:**
163
+
164
+ | 参数名 | 类型 | 是否必填 | 描述 |
165
+ | ------ | ---- | -------- | -------- |
166
+ | `path` | str | 是 | 文件路径 |
167
+
168
+ **返回值:**
169
+
170
+ | 类型 | 描述 |
171
+ | ------ | ------------ |
172
+ | `bool` | 是否创建成功 |
173
+
174
+ **示例:**
175
+
176
+ ```python
177
+ from kuaijs import file
178
+ created = file.create("/path/to/newfile.txt")
179
+ print(created)
180
+ ```
181
+
182
+ #### exists - 检查文件或目录是否存在
183
+
184
+ ```python
185
+ def exists(path: str) -> bool
186
+ ```
187
+
188
+ **参数:**
189
+
190
+ | 参数名 | 类型 | 是否必填 | 描述 |
191
+ | ------ | ---- | -------- | -------------- |
192
+ | `path` | str | 是 | 文件或目录路径 |
193
+
194
+ **返回值:**
195
+
196
+ | 类型 | 描述 |
197
+ | ------ | -------- |
198
+ | `bool` | 是否存在 |
199
+
200
+ **示例:**
201
+
202
+ ```python
203
+ from kuaijs import file
204
+ print(file.exists("/path/to/file.txt"))
205
+ ```
206
+
207
+ #### copy - 复制文件
208
+
209
+ ```python
210
+ def copy(src: str, dest: str) -> bool
211
+ ```
212
+
213
+ **参数:**
214
+
215
+ | 参数名 | 类型 | 是否必填 | 描述 |
216
+ | ------ | ---- | -------- | ------------ |
217
+ | `src` | str | 是 | 源文件路径 |
218
+ | `dest` | str | 是 | 目标文件路径 |
219
+
220
+ **返回值:**
221
+
222
+ | 类型 | 描述 |
223
+ | ------ | ------------ |
224
+ | `bool` | 是否复制成功 |
225
+
226
+ **示例:**
227
+
228
+ ```python
229
+ from kuaijs import file
230
+ copied = file.copy("/path/to/source.txt", "/path/to/destination.txt")
231
+ print(copied)
232
+ ```
233
+
234
+ #### deleteAllFile - 删除文件或目录
235
+
236
+ ```python
237
+ def deleteAllFile(path: str) -> bool
238
+ ```
239
+
240
+ **参数:**
241
+
242
+ | 参数名 | 类型 | 是否必填 | 描述 |
243
+ | ------ | ---- | -------- | -------------- |
244
+ | `path` | str | 是 | 文件或目录路径 |
245
+
246
+ **返回值:**
247
+
248
+ | 类型 | 描述 |
249
+ | ------ | ------------ |
250
+ | `bool` | 是否删除成功 |
251
+
252
+ **示例:**
253
+
254
+ ```python
255
+ from kuaijs import file
256
+ deleted = file.deleteAllFile("/path/to/file.txt")
257
+ print(deleted)
258
+ ```
259
+
260
+ ### 文件读写操作
261
+
262
+ #### readFile - 读取整个文件内容
263
+
264
+ ```python
265
+ def readFile(path: str) -> Optional[str]
266
+ ```
267
+
268
+ **参数:**
269
+
270
+ | 参数名 | 类型 | 是否必填 | 描述 |
271
+ | ------ | ---- | -------- | -------- |
272
+ | `path` | str | 是 | 文件路径 |
273
+
274
+ **返回值:**
275
+
276
+ | 类型 | 描述 |
277
+ | --------------- | ------------------------------- |
278
+ | `str \| None` | 文件内容;失败或不存在返回 None |
279
+
280
+ **示例:**
281
+
282
+ ```python
283
+ from kuaijs import file
284
+ p = file.getDataFile("read.txt")
285
+ file.writeFile(p, "line1\nline2")
286
+ print(file.readFile(p))
287
+ ```
288
+
289
+ #### readResFile - 读取 res 目录中的文本文件内容
290
+
291
+ 注意:该函数仅适用于读取脚本 res 目录中的文本文件;res 目录文件只读,不能修改。
292
+
293
+ ```python
294
+ def readResFile(fileName: str) -> Optional[str]
295
+ ```
296
+
297
+ **参数:**
298
+
299
+ | 参数名 | 类型 | 是否必填 | 描述 |
300
+ | ---------- | ---- | -------- | ------------ |
301
+ | `fileName` | str | 是 | res 目录路径 |
302
+
303
+ **返回值:**
304
+
305
+ | 类型 | 描述 |
306
+ | ------------- | ----------------------- |
307
+ | `str \| None` | 文件内容;失败返回 None |
308
+
309
+ **示例:**
310
+
311
+ ```python
312
+ from kuaijs import file
313
+ config = file.readResFile("config.json")
314
+ if config:
315
+ print(config)
316
+ ```
317
+
318
+ #### writeFile - 写入文件内容(覆盖)
319
+
320
+ ```python
321
+ def writeFile(path: str, data: str) -> bool
322
+ ```
323
+
324
+ **参数:**
325
+
326
+ | 参数名 | 类型 | 是否必填 | 描述 |
327
+ | ------ | ---- | -------- | ------------ |
328
+ | `path` | str | 是 | 文件路径 |
329
+ | `data` | str | 是 | 要写入的内容 |
330
+
331
+ **返回值:**
332
+
333
+ | 类型 | 描述 |
334
+ | ------ | ------------ |
335
+ | `bool` | 是否写入成功 |
336
+
337
+ #### appendLine - 向文件追加内容
338
+
339
+ ```python
340
+ def appendLine(path: str, content: str) -> bool:
341
+ ```
342
+
343
+ **参数:**
344
+
345
+ | 参数名 | 类型 | 是否必填 | 描述 |
346
+ | --------- | ---- | -------- | ------------ |
347
+ | `path` | str | 是 | 文件路径 |
348
+ | `content` | str | 是 | 要追加的内容 |
349
+
350
+ **返回值:**
351
+
352
+ | 类型 | 描述 |
353
+ | ------ | ------------ |
354
+ | `bool` | 是否追加成功 |
355
+
356
+ **示例:**
357
+
358
+ ```python
359
+ from kuaijs import file
360
+ appended = file.appendLine("/path/to/log.txt", "新的日志条目")
361
+ print(appended)
362
+ ```
363
+
364
+ ### 按行操作
365
+
366
+ #### readLine - 读取文件指定行的内容
367
+
368
+ ```python
369
+ def readLine(path: str, lineNo: int) -> Optional[str]
370
+ ```
371
+
372
+ **参数:**
373
+
374
+ | 参数名 | 类型 | 是否必填 | 描述 |
375
+ | -------- | ---- | -------- | ----------------- |
376
+ | `path` | str | 是 | 文件路径 |
377
+ | `lineNo` | int | 是 | 行号(从 1 开始) |
378
+
379
+ **返回值:**
380
+
381
+ | 类型 | 描述 |
382
+ | ------------- | ----------------------------- |
383
+ | `str \| None` | 行内容;失败或不存在返回 None |
384
+
385
+ **示例:**
386
+
387
+ ```python
388
+ from kuaijs import file
389
+ first_line = file.readLine("/path/to/file.txt", 1)
390
+ print(first_line)
391
+ ```
392
+
393
+ #### readAllLines - 读取文件所有行内容
394
+
395
+ ```python
396
+ from typing import List, Optional
397
+
398
+ def readAllLines(path: str) -> Optional[List[str]]
399
+ ```
400
+
401
+ **参数:**
402
+
403
+ | 参数名 | 类型 | 是否必填 | 描述 |
404
+ | ------ | ---- | -------- | -------- |
405
+ | `path` | str | 是 | 文件路径 |
406
+
407
+ **返回值:**
408
+
409
+ | 类型 | 描述 |
410
+ | ------------------- | ----------------------------- |
411
+ | `List[str] \| None` | 所有行内容数组;失败返回 None |
412
+
413
+ **示例:**
414
+
415
+ ```python
416
+ from kuaijs import file
417
+ lines = file.readAllLines("/path/to/file.txt")
418
+ print(lines)
419
+ ```
420
+
421
+ #### deleteLine - 删除文件中符合条件的行
422
+
423
+ ```python
424
+ def deleteLine(path: str, line: int, contains: Optional[str]) -> bool
425
+ ```
426
+
427
+ **参数:**
428
+
429
+ | 参数名 | 类型 | 是否必填 | 描述 |
430
+ | ---------- | ----------- | -------- | ----------------------------------- |
431
+ | `path` | str | 是 | 文件路径 |
432
+ | `line` | int | 是 | 行号(-1 表示不按行号删除) |
433
+ | `contains` | str \| None | 否 | 包含的内容(None 表示不按内容删除) |
434
+
435
+ **返回值:**
436
+
437
+ | 类型 | 描述 |
438
+ | ------ | ------------ |
439
+ | `bool` | 是否删除成功 |
440
+
441
+ **示例:**
442
+
443
+ ```python
444
+ from kuaijs import file
445
+ file.deleteLine("/path/to/file.txt", 3, None)
446
+ file.deleteLine("/path/to/file.txt", -1, "error")
447
+ file.deleteLine("/path/to/file.txt", 5, "debug")
448
+ ```
449
+
450
+ ### 文件信息
451
+
452
+ #### fileMD5 - 获取文件的 MD5 哈希值
453
+
454
+ ```python
455
+ def fileMD5(path: str) -> Optional[str]
456
+ ```
457
+
458
+ **参数:**
459
+
460
+ | 参数名 | 类型 | 是否必填 | 描述 |
461
+ | ------ | ---- | -------- | -------- |
462
+ | `path` | str | 是 | 文件路径 |
463
+
464
+ **返回值:**
465
+
466
+ | 类型 | 描述 |
467
+ | ------------- | -------------------------- |
468
+ | `str \| None` | 文件 MD5 值;失败返回 None |
469
+
470
+ **示例:**
471
+
472
+ ```python
473
+ from kuaijs import file
474
+ md5 = file.fileMD5("/path/to/file.txt")
475
+ print(md5)
476
+ ```
@@ -0,0 +1,154 @@
1
+ # 全局模块 (Global)
2
+
3
+ - 提供应用信息常量与前台切换能力
4
+
5
+ ## 功能概览
6
+
7
+ - 应用信息常量:版本号/构建号/名称/包名
8
+ - 前台切换:将宿主应用切到前台
9
+
10
+ ## 常量
11
+
12
+ - `runMode: str` 运行模式,"agent" 或 "hid"
13
+ - `isSupportBuiltAgent: bool` 是否使用内置Agent
14
+ - `platform: str` 平台信息("ios" 或 "android")
15
+ - `appVersion: str` 应用版本号
16
+ - `appBuildNumber: int` 应用构建号
17
+ - `appName: str` 应用名称
18
+ - `appBundleId: str` 应用包名
19
+ - `isDebug: bool` 是否调试模式(True/False)
20
+ - `packageJson: dict` 应用的 `package.json` 内容(对象)
21
+
22
+ ## 示例
23
+
24
+ ```python
25
+ from kuaijs import g
26
+ print(g.appName, g.appVersion)
27
+
28
+ # 读取 package.json 内容
29
+ pkg = g.packageJson
30
+ print(pkg.get("appVersion"))
31
+
32
+ # 调试模式判断
33
+ print("Debug Mode" if g.isDebug else "Release Mode")
34
+ ```
35
+
36
+ ## API 参考
37
+
38
+ ### 方法
39
+
40
+ #### `takeMeToFront` - 将宿主应用切入前台
41
+
42
+ ```python
43
+ def takeMeToFront() -> bool
44
+ ```
45
+
46
+ **返回值:**
47
+
48
+ - `bool` 是否成功切入前台
49
+
50
+ **示例:**
51
+
52
+ ```python
53
+ from kuaijs import g
54
+ g.takeMeToFront()
55
+ ```
56
+
57
+ #### `restartScript` - 重启脚本
58
+
59
+ ```python
60
+ def restartScript(delay_ms: int = 2000) -> None
61
+ ```
62
+
63
+ **参数:**
64
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
65
+ | ---- | ---- | ---- | ------ | ---- |
66
+ | `delay_ms` | int | 否 | 2000 | 重启延迟毫秒数(至少 2000ms) |
67
+
68
+ **示例:**
69
+
70
+ ```python
71
+ from kuaijs import g
72
+ g.restartScript()
73
+ ```
74
+
75
+ #### `loadDex` - 加载指定的 dex、apk 文件
76
+
77
+ **注意:**
78
+
79
+ - 仅在 Android 平台可用
80
+
81
+ ```python
82
+ def loadDex(dexPath: str) -> bool
83
+ ```
84
+
85
+ **参数:**
86
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
87
+ | ---- | ---- | ---- | ------ | ---- |
88
+ | `dexPath` | str | 是 | - | Dex 或 APK 文件的路径 |
89
+
90
+ **返回值:**
91
+
92
+ **返回值:**
93
+
94
+ | 类型 | 描述 |
95
+ | ------ | --------------------- |
96
+ | `bool` | 是否成功加载 Dex 文件 |
97
+
98
+ **示例:**
99
+
100
+ ```python
101
+ from kuaijs import g
102
+ g.loadDex("/sdcard/classes.dex")
103
+ ```
104
+
105
+ #### `getContext` - 获取安卓上下文
106
+
107
+ **注意:**
108
+
109
+ - 仅在 Android 平台可用
110
+
111
+ ```python
112
+ def getContext() -> dict
113
+ ```
114
+
115
+ **返回值:**
116
+
117
+ - `dict` 当前安卓上下文对象
118
+
119
+ **示例:**
120
+
121
+ ```python
122
+ from kuaijs import g
123
+ ctx = g.getContext()
124
+ print(ctx)
125
+ ```
126
+
127
+ #### `importClass` - 导入类对象
128
+
129
+ **注意:**
130
+
131
+ - 仅在 Android 平台可用
132
+
133
+ ```python
134
+ def importClass(className: str) -> any
135
+ ```
136
+
137
+ **参数:**
138
+
139
+ | 参数 | 类型 | 必需 | 默认值 | 描述 |
140
+ | ----------- | ---- | ---- | ------ | ---------------------- |
141
+ | `className` | str | 是 | - | 类名(包含完整包路径) |
142
+
143
+ **返回值:**
144
+
145
+ | 类型 | 描述 |
146
+ | ----- | ------ |
147
+ | `any` | 类对象 |
148
+
149
+ **示例:**
150
+
151
+ ```python
152
+ from kuaijs import g
153
+ Activity = g.importClass("android.app.Activity")
154
+ ```