@glassly/miniapp 0.3.0-dev.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.
Files changed (312) hide show
  1. package/README.md +275 -0
  2. package/dist/background/index.d.ts +64 -0
  3. package/dist/background/index.d.ts.map +1 -0
  4. package/dist/background/index.js +26 -0
  5. package/dist/background/index.js.map +1 -0
  6. package/dist/background/register.d.ts +39 -0
  7. package/dist/background/register.d.ts.map +1 -0
  8. package/dist/background/register.js +72 -0
  9. package/dist/background/register.js.map +1 -0
  10. package/dist/dev-reload.d.ts +21 -0
  11. package/dist/dev-reload.d.ts.map +1 -0
  12. package/dist/dev-reload.js +60 -0
  13. package/dist/dev-reload.js.map +1 -0
  14. package/dist/envelope.d.ts +23 -0
  15. package/dist/envelope.d.ts.map +1 -0
  16. package/dist/envelope.js +47 -0
  17. package/dist/envelope.js.map +1 -0
  18. package/dist/globals.d.ts +54 -0
  19. package/dist/globals.d.ts.map +1 -0
  20. package/dist/globals.js +12 -0
  21. package/dist/globals.js.map +1 -0
  22. package/dist/hardware.d.ts +38 -0
  23. package/dist/hardware.d.ts.map +1 -0
  24. package/dist/hardware.js +31 -0
  25. package/dist/hardware.js.map +1 -0
  26. package/dist/index.d.ts +65 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +34 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/modules/actions.d.ts +81 -0
  31. package/dist/modules/actions.d.ts.map +1 -0
  32. package/dist/modules/actions.js +137 -0
  33. package/dist/modules/actions.js.map +1 -0
  34. package/dist/modules/ai.d.ts +211 -0
  35. package/dist/modules/ai.d.ts.map +1 -0
  36. package/dist/modules/ai.js +117 -0
  37. package/dist/modules/ai.js.map +1 -0
  38. package/dist/modules/auth.d.ts +25 -0
  39. package/dist/modules/auth.d.ts.map +1 -0
  40. package/dist/modules/auth.js +52 -0
  41. package/dist/modules/auth.js.map +1 -0
  42. package/dist/modules/base64.d.ts +16 -0
  43. package/dist/modules/base64.d.ts.map +1 -0
  44. package/dist/modules/base64.js +92 -0
  45. package/dist/modules/base64.js.map +1 -0
  46. package/dist/modules/blob.d.ts +161 -0
  47. package/dist/modules/blob.d.ts.map +1 -0
  48. package/dist/modules/blob.js +277 -0
  49. package/dist/modules/blob.js.map +1 -0
  50. package/dist/modules/camera.d.ts +183 -0
  51. package/dist/modules/camera.d.ts.map +1 -0
  52. package/dist/modules/camera.js +119 -0
  53. package/dist/modules/camera.js.map +1 -0
  54. package/dist/modules/cloud.d.ts +34 -0
  55. package/dist/modules/cloud.d.ts.map +1 -0
  56. package/dist/modules/cloud.js +70 -0
  57. package/dist/modules/cloud.js.map +1 -0
  58. package/dist/modules/dashboard.d.ts +24 -0
  59. package/dist/modules/dashboard.d.ts.map +1 -0
  60. package/dist/modules/dashboard.js +39 -0
  61. package/dist/modules/dashboard.js.map +1 -0
  62. package/dist/modules/display.d.ts +167 -0
  63. package/dist/modules/display.d.ts.map +1 -0
  64. package/dist/modules/display.js +83 -0
  65. package/dist/modules/display.js.map +1 -0
  66. package/dist/modules/events.d.ts +178 -0
  67. package/dist/modules/events.d.ts.map +1 -0
  68. package/dist/modules/events.js +168 -0
  69. package/dist/modules/events.js.map +1 -0
  70. package/dist/modules/glasses.d.ts +36 -0
  71. package/dist/modules/glasses.d.ts.map +1 -0
  72. package/dist/modules/glasses.js +52 -0
  73. package/dist/modules/glasses.js.map +1 -0
  74. package/dist/modules/heading.d.ts +16 -0
  75. package/dist/modules/heading.d.ts.map +1 -0
  76. package/dist/modules/heading.js +20 -0
  77. package/dist/modules/heading.js.map +1 -0
  78. package/dist/modules/imu.d.ts +31 -0
  79. package/dist/modules/imu.d.ts.map +1 -0
  80. package/dist/modules/imu.js +40 -0
  81. package/dist/modules/imu.js.map +1 -0
  82. package/dist/modules/input.d.ts +34 -0
  83. package/dist/modules/input.d.ts.map +1 -0
  84. package/dist/modules/input.js +50 -0
  85. package/dist/modules/input.js.map +1 -0
  86. package/dist/modules/keys.d.ts +30 -0
  87. package/dist/modules/keys.d.ts.map +1 -0
  88. package/dist/modules/keys.js +37 -0
  89. package/dist/modules/keys.js.map +1 -0
  90. package/dist/modules/languages.d.ts +42 -0
  91. package/dist/modules/languages.d.ts.map +1 -0
  92. package/dist/modules/languages.js +57 -0
  93. package/dist/modules/languages.js.map +1 -0
  94. package/dist/modules/led.d.ts +35 -0
  95. package/dist/modules/led.d.ts.map +1 -0
  96. package/dist/modules/led.js +39 -0
  97. package/dist/modules/led.js.map +1 -0
  98. package/dist/modules/location.d.ts +27 -0
  99. package/dist/modules/location.d.ts.map +1 -0
  100. package/dist/modules/location.js +35 -0
  101. package/dist/modules/location.js.map +1 -0
  102. package/dist/modules/memory.d.ts +126 -0
  103. package/dist/modules/memory.d.ts.map +1 -0
  104. package/dist/modules/memory.js +141 -0
  105. package/dist/modules/memory.js.map +1 -0
  106. package/dist/modules/mic.d.ts +61 -0
  107. package/dist/modules/mic.d.ts.map +1 -0
  108. package/dist/modules/mic.js +94 -0
  109. package/dist/modules/mic.js.map +1 -0
  110. package/dist/modules/miniapps.d.ts +82 -0
  111. package/dist/modules/miniapps.d.ts.map +1 -0
  112. package/dist/modules/miniapps.js +58 -0
  113. package/dist/modules/miniapps.js.map +1 -0
  114. package/dist/modules/navigation.d.ts +332 -0
  115. package/dist/modules/navigation.d.ts.map +1 -0
  116. package/dist/modules/navigation.js +387 -0
  117. package/dist/modules/navigation.js.map +1 -0
  118. package/dist/modules/permissions.d.ts +58 -0
  119. package/dist/modules/permissions.d.ts.map +1 -0
  120. package/dist/modules/permissions.js +66 -0
  121. package/dist/modules/permissions.js.map +1 -0
  122. package/dist/modules/phone.d.ts +95 -0
  123. package/dist/modules/phone.d.ts.map +1 -0
  124. package/dist/modules/phone.js +110 -0
  125. package/dist/modules/phone.js.map +1 -0
  126. package/dist/modules/pivots/engine.d.ts +169 -0
  127. package/dist/modules/pivots/engine.d.ts.map +1 -0
  128. package/dist/modules/pivots/engine.js +778 -0
  129. package/dist/modules/pivots/engine.js.map +1 -0
  130. package/dist/modules/pivots/geometry.d.ts +84 -0
  131. package/dist/modules/pivots/geometry.d.ts.map +1 -0
  132. package/dist/modules/pivots/geometry.js +371 -0
  133. package/dist/modules/pivots/geometry.js.map +1 -0
  134. package/dist/modules/pivots/instructions.d.ts +139 -0
  135. package/dist/modules/pivots/instructions.d.ts.map +1 -0
  136. package/dist/modules/pivots/instructions.js +281 -0
  137. package/dist/modules/pivots/instructions.js.map +1 -0
  138. package/dist/modules/speaker.d.ts +175 -0
  139. package/dist/modules/speaker.d.ts.map +1 -0
  140. package/dist/modules/speaker.js +249 -0
  141. package/dist/modules/speaker.js.map +1 -0
  142. package/dist/modules/speakers.d.ts +29 -0
  143. package/dist/modules/speakers.d.ts.map +1 -0
  144. package/dist/modules/speakers.js +38 -0
  145. package/dist/modules/speakers.js.map +1 -0
  146. package/dist/modules/storage.d.ts +50 -0
  147. package/dist/modules/storage.d.ts.map +1 -0
  148. package/dist/modules/storage.js +99 -0
  149. package/dist/modules/storage.js.map +1 -0
  150. package/dist/modules/stream.d.ts +133 -0
  151. package/dist/modules/stream.d.ts.map +1 -0
  152. package/dist/modules/stream.js +53 -0
  153. package/dist/modules/stream.js.map +1 -0
  154. package/dist/modules/system.d.ts +71 -0
  155. package/dist/modules/system.d.ts.map +1 -0
  156. package/dist/modules/system.js +65 -0
  157. package/dist/modules/system.js.map +1 -0
  158. package/dist/modules/transcription.d.ts +99 -0
  159. package/dist/modules/transcription.d.ts.map +1 -0
  160. package/dist/modules/transcription.js +126 -0
  161. package/dist/modules/transcription.js.map +1 -0
  162. package/dist/modules/translation.d.ts +65 -0
  163. package/dist/modules/translation.d.ts.map +1 -0
  164. package/dist/modules/translation.js +121 -0
  165. package/dist/modules/translation.js.map +1 -0
  166. package/dist/modules/ui.d.ts +186 -0
  167. package/dist/modules/ui.d.ts.map +1 -0
  168. package/dist/modules/ui.js +289 -0
  169. package/dist/modules/ui.js.map +1 -0
  170. package/dist/protocol.d.ts +384 -0
  171. package/dist/protocol.d.ts.map +1 -0
  172. package/dist/protocol.js +413 -0
  173. package/dist/protocol.js.map +1 -0
  174. package/dist/react/MiniappHeader.d.ts +44 -0
  175. package/dist/react/MiniappHeader.d.ts.map +1 -0
  176. package/dist/react/MiniappHeader.js +32 -0
  177. package/dist/react/MiniappHeader.js.map +1 -0
  178. package/dist/react/MiniappProvider.d.ts +33 -0
  179. package/dist/react/MiniappProvider.d.ts.map +1 -0
  180. package/dist/react/MiniappProvider.js +15 -0
  181. package/dist/react/MiniappProvider.js.map +1 -0
  182. package/dist/react/capsuleHeaderLayout.d.ts +3 -0
  183. package/dist/react/capsuleHeaderLayout.d.ts.map +1 -0
  184. package/dist/react/capsuleHeaderLayout.js +5 -0
  185. package/dist/react/capsuleHeaderLayout.js.map +1 -0
  186. package/dist/react/index.d.ts +13 -0
  187. package/dist/react/index.d.ts.map +1 -0
  188. package/dist/react/index.js +9 -0
  189. package/dist/react/index.js.map +1 -0
  190. package/dist/react/useCapabilities.d.ts +26 -0
  191. package/dist/react/useCapabilities.d.ts.map +1 -0
  192. package/dist/react/useCapabilities.js +27 -0
  193. package/dist/react/useCapabilities.js.map +1 -0
  194. package/dist/react/useCapsuleHeaderStyle.d.ts +27 -0
  195. package/dist/react/useCapsuleHeaderStyle.d.ts.map +1 -0
  196. package/dist/react/useCapsuleHeaderStyle.js +39 -0
  197. package/dist/react/useCapsuleHeaderStyle.js.map +1 -0
  198. package/dist/react/useColorScheme.d.ts +18 -0
  199. package/dist/react/useColorScheme.d.ts.map +1 -0
  200. package/dist/react/useColorScheme.js +24 -0
  201. package/dist/react/useColorScheme.js.map +1 -0
  202. package/dist/react/useConnected.d.ts +17 -0
  203. package/dist/react/useConnected.d.ts.map +1 -0
  204. package/dist/react/useConnected.js +39 -0
  205. package/dist/react/useConnected.js.map +1 -0
  206. package/dist/react/useRpc.d.ts +29 -0
  207. package/dist/react/useRpc.d.ts.map +1 -0
  208. package/dist/react/useRpc.js +82 -0
  209. package/dist/react/useRpc.js.map +1 -0
  210. package/dist/react/useSafeArea.d.ts +21 -0
  211. package/dist/react/useSafeArea.d.ts.map +1 -0
  212. package/dist/react/useSafeArea.js +22 -0
  213. package/dist/react/useSafeArea.js.map +1 -0
  214. package/dist/session.d.ts +355 -0
  215. package/dist/session.d.ts.map +1 -0
  216. package/dist/session.js +688 -0
  217. package/dist/session.js.map +1 -0
  218. package/dist/sub-path-types.test-d.d.ts +40 -0
  219. package/dist/sub-path-types.test-d.d.ts.map +1 -0
  220. package/dist/sub-path-types.test-d.js +37 -0
  221. package/dist/sub-path-types.test-d.js.map +1 -0
  222. package/dist/transport/auto.d.ts +35 -0
  223. package/dist/transport/auto.d.ts.map +1 -0
  224. package/dist/transport/auto.js +124 -0
  225. package/dist/transport/auto.js.map +1 -0
  226. package/dist/transport/dispatch.d.ts +43 -0
  227. package/dist/transport/dispatch.d.ts.map +1 -0
  228. package/dist/transport/dispatch.js +91 -0
  229. package/dist/transport/dispatch.js.map +1 -0
  230. package/dist/transport/local-socket.d.ts +28 -0
  231. package/dist/transport/local-socket.d.ts.map +1 -0
  232. package/dist/transport/local-socket.js +80 -0
  233. package/dist/transport/local-socket.js.map +1 -0
  234. package/dist/transport/mock.d.ts +54 -0
  235. package/dist/transport/mock.d.ts.map +1 -0
  236. package/dist/transport/mock.js +210 -0
  237. package/dist/transport/mock.js.map +1 -0
  238. package/dist/transport/postmessage.d.ts +36 -0
  239. package/dist/transport/postmessage.d.ts.map +1 -0
  240. package/dist/transport/postmessage.js +78 -0
  241. package/dist/transport/postmessage.js.map +1 -0
  242. package/dist/transport/types.d.ts +25 -0
  243. package/dist/transport/types.d.ts.map +1 -0
  244. package/dist/transport/types.js +11 -0
  245. package/dist/transport/types.js.map +1 -0
  246. package/dist/ui/index.d.ts +107 -0
  247. package/dist/ui/index.d.ts.map +1 -0
  248. package/dist/ui/index.js +32 -0
  249. package/dist/ui/index.js.map +1 -0
  250. package/package.json +84 -0
  251. package/src/background/index.ts +177 -0
  252. package/src/background/register.ts +89 -0
  253. package/src/dev-reload.ts +59 -0
  254. package/src/envelope.ts +54 -0
  255. package/src/globals.ts +62 -0
  256. package/src/hardware.ts +40 -0
  257. package/src/index.ts +216 -0
  258. package/src/modules/actions.ts +189 -0
  259. package/src/modules/ai.ts +281 -0
  260. package/src/modules/auth.ts +68 -0
  261. package/src/modules/base64.ts +87 -0
  262. package/src/modules/blob.ts +341 -0
  263. package/src/modules/camera.ts +258 -0
  264. package/src/modules/cloud.ts +94 -0
  265. package/src/modules/dashboard.ts +51 -0
  266. package/src/modules/display.ts +207 -0
  267. package/src/modules/events.ts +318 -0
  268. package/src/modules/glasses.ts +58 -0
  269. package/src/modules/heading.ts +23 -0
  270. package/src/modules/imu.ts +44 -0
  271. package/src/modules/input.ts +66 -0
  272. package/src/modules/keys.ts +46 -0
  273. package/src/modules/languages.ts +72 -0
  274. package/src/modules/led.ts +61 -0
  275. package/src/modules/location.ts +39 -0
  276. package/src/modules/memory.ts +224 -0
  277. package/src/modules/mic.ts +103 -0
  278. package/src/modules/miniapps.ts +103 -0
  279. package/src/modules/navigation.ts +674 -0
  280. package/src/modules/permissions.ts +83 -0
  281. package/src/modules/phone.ts +155 -0
  282. package/src/modules/pivots/engine.ts +863 -0
  283. package/src/modules/pivots/geometry.ts +409 -0
  284. package/src/modules/pivots/instructions.ts +341 -0
  285. package/src/modules/speaker.ts +347 -0
  286. package/src/modules/speakers.ts +40 -0
  287. package/src/modules/storage.ts +109 -0
  288. package/src/modules/stream.ts +168 -0
  289. package/src/modules/system.ts +113 -0
  290. package/src/modules/transcription.ts +180 -0
  291. package/src/modules/translation.ts +156 -0
  292. package/src/modules/ui.ts +481 -0
  293. package/src/protocol.ts +475 -0
  294. package/src/react/MiniappHeader.tsx +114 -0
  295. package/src/react/MiniappProvider.tsx +50 -0
  296. package/src/react/capsuleHeaderLayout.ts +11 -0
  297. package/src/react/index.ts +12 -0
  298. package/src/react/useCapabilities.ts +29 -0
  299. package/src/react/useCapsuleHeaderStyle.ts +55 -0
  300. package/src/react/useColorScheme.ts +26 -0
  301. package/src/react/useConnected.ts +47 -0
  302. package/src/react/useRpc.ts +103 -0
  303. package/src/react/useSafeArea.ts +36 -0
  304. package/src/session.ts +969 -0
  305. package/src/sub-path-types.test-d.ts +60 -0
  306. package/src/transport/auto.ts +143 -0
  307. package/src/transport/dispatch.ts +106 -0
  308. package/src/transport/local-socket.ts +91 -0
  309. package/src/transport/mock.ts +255 -0
  310. package/src/transport/postmessage.ts +95 -0
  311. package/src/transport/types.ts +26 -0
  312. package/src/ui/index.ts +129 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @fileoverview SpeakersModule — resolve enrolled-speaker ids to names.
3
+ *
4
+ * On-device voice recognition stamps transcription events with stable opaque
5
+ * ids (`TranscriptionData.speakerId` = `spk_…`) when a voice matches the
6
+ * profile the user enrolled in Settings > Voice Recognition (owner-only:
7
+ * `TranscriptionData.isUser` is the primary signal). Display names are
8
+ * on-device PII and never ride along with events; a miniapp that wants the
9
+ * display name must declare the SPEAKERS manifest permission and resolve ids
10
+ * through this module.
11
+ *
12
+ * All calls require the SPEAKERS manifest permission; without it the host
13
+ * rejects with PERMISSION_NOT_DECLARED. Ids that don't correspond to an
14
+ * enrolled profile (anonymous diarization labels, deleted profiles) resolve
15
+ * to null — treat null as "no name available", not an error.
16
+ */
17
+
18
+ import {MiniappRequestType} from "../protocol"
19
+ import {MiniappSession} from "../session"
20
+
21
+ export class SpeakersModule {
22
+ constructor(private readonly session: MiniappSession) {}
23
+
24
+ /** True iff this miniapp's manifest declares the SPEAKERS permission. */
25
+ get hasPermission(): boolean {
26
+ return this.session._hasManifestPermission("SPEAKERS")
27
+ }
28
+
29
+ /**
30
+ * Resolve an enrolled-speaker id to its display name, or null when the id
31
+ * is not an enrolled profile (anonymous label, deleted profile).
32
+ */
33
+ async resolveName(id: string): Promise<string | null> {
34
+ const result = await this.session.sendRequest<{name: string | null}>({
35
+ type: MiniappRequestType.SPEAKERS_RESOLVE_NAME,
36
+ id,
37
+ })
38
+ return result?.name ?? null
39
+ }
40
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @fileoverview SimpleStorage — phone-local key-value store scoped to
3
+ * (userId, packageName). Mirrors cloud SDK v3's StorageManager surface.
4
+ *
5
+ * All operations round-trip to LocalMiniappRuntime, which reads/writes
6
+ * the phone's storage with a namespaced key format:
7
+ * localstorage_{userId}_{packageName}_{key}
8
+ *
9
+ * Values are plain strings. Callers serialize structured data with
10
+ * JSON.stringify themselves (matching cloud's SimpleStorage shape).
11
+ */
12
+
13
+ import {MiniappRequestType} from "../protocol"
14
+ import {MiniappSession} from "../session"
15
+
16
+ export class SimpleStorage {
17
+ constructor(private readonly session: MiniappSession) {}
18
+
19
+ /** Get a value by key. Resolves to null when the key is unset. */
20
+ async get(key: string): Promise<string | null> {
21
+ const result = await this.session.sendRequest<{value: string | null}>({
22
+ type: MiniappRequestType.STORAGE_GET,
23
+ key,
24
+ })
25
+ return result?.value ?? null
26
+ }
27
+
28
+ /** Set a single key/value pair. Overwrites silently. */
29
+ async set(key: string, value: string): Promise<void> {
30
+ await this.session.sendRequest<void>({
31
+ type: MiniappRequestType.STORAGE_SET,
32
+ key,
33
+ value,
34
+ })
35
+ }
36
+
37
+ /** Delete one key. No-op if the key is unset. */
38
+ async delete(key: string): Promise<void> {
39
+ await this.session.sendRequest<void>({
40
+ type: MiniappRequestType.STORAGE_DELETE,
41
+ key,
42
+ })
43
+ }
44
+
45
+ /** List every key in this miniapp's namespace. */
46
+ async keys(): Promise<string[]> {
47
+ const result = await this.session.sendRequest<{keys: string[]}>({
48
+ type: MiniappRequestType.STORAGE_LIST,
49
+ })
50
+ return result?.keys ?? []
51
+ }
52
+
53
+ /**
54
+ * @deprecated Renamed to `keys()` for cloud SDK parity. This alias will be
55
+ * removed in a future release.
56
+ */
57
+ async list(): Promise<string[]> {
58
+ return this.keys()
59
+ }
60
+
61
+ /** Drop every key in this miniapp's namespace. */
62
+ async clear(): Promise<void> {
63
+ await this.session.sendRequest<void>({
64
+ type: MiniappRequestType.STORAGE_CLEAR,
65
+ })
66
+ }
67
+
68
+ /** True iff `key` is set in this miniapp's namespace. */
69
+ async has(key: string): Promise<boolean> {
70
+ const result = await this.session.sendRequest<{has: boolean}>({
71
+ type: MiniappRequestType.STORAGE_HAS,
72
+ key,
73
+ })
74
+ return !!result?.has
75
+ }
76
+
77
+ /**
78
+ * Read every key/value pair in this miniapp's namespace. Useful for
79
+ * hydrating state on bootstrap. Bounded by the underlying namespace
80
+ * size — keep total stored payload small (typical < 1 MB) to avoid
81
+ * stalling the JSContext when this resolves.
82
+ */
83
+ async getAll(): Promise<Record<string, string>> {
84
+ const result = await this.session.sendRequest<{values: Record<string, string>}>({
85
+ type: MiniappRequestType.STORAGE_GET_ALL,
86
+ })
87
+ return result?.values ?? {}
88
+ }
89
+
90
+ /** Write many key/value pairs in one round-trip. */
91
+ async setMultiple(values: Record<string, string>): Promise<void> {
92
+ await this.session.sendRequest<void>({
93
+ type: MiniappRequestType.STORAGE_SET_MULTIPLE,
94
+ values,
95
+ })
96
+ }
97
+
98
+ /**
99
+ * Force any pending in-flight writes to disk. No-op today because the
100
+ * host writes through immediately, but reserved so a future debounced
101
+ * backend can honor "flush before I quit" calls without breaking the
102
+ * API.
103
+ */
104
+ async flush(): Promise<void> {
105
+ await this.session.sendRequest<void>({
106
+ type: MiniappRequestType.STORAGE_FLUSH,
107
+ })
108
+ }
109
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * @fileoverview StreamModule -- video streaming from glasses.
3
+ *
4
+ * Wires to cloud streaming extensions via the __phone__ subscriber path.
5
+ *
6
+ * One entry point: `startStream`. By default it provisions a Glassly-hosted
7
+ * stream and returns playback URLs (managed). Pass `direct` to publish straight
8
+ * to your own ingest URL with no relay.
9
+ */
10
+
11
+ import {MiniappRequestType} from "../protocol"
12
+ import {MiniappSession} from "../session"
13
+
14
+ export interface StreamVideoConfig {
15
+ width?: number
16
+ height?: number
17
+ bitrate?: number
18
+ fps?: number
19
+ }
20
+
21
+ export interface StreamAudioConfig {
22
+ bitrate?: number
23
+ sampleRate?: number
24
+ echoCancellation?: boolean
25
+ noiseSuppression?: boolean
26
+ }
27
+
28
+ export interface StreamResolvedConfig {
29
+ transport?: "rtmp" | "srt" | "whip"
30
+ video?: {
31
+ width: number
32
+ height: number
33
+ captureWidth?: number
34
+ captureHeight?: number
35
+ bitrate: number
36
+ fps: number
37
+ }
38
+ audio?: {
39
+ bitrate?: number
40
+ sampleRate?: number
41
+ echoCancellation?: boolean
42
+ noiseSuppression?: boolean
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Restream destination. The full stream key is part of the URL (e.g.
48
+ * `rtmp://yt.com/live/STREAM-KEY`). `name` is a human label that surfaces
49
+ * in dashboards but doesn't affect routing.
50
+ */
51
+ export interface RestreamDestination {
52
+ url: string
53
+ name?: string
54
+ }
55
+
56
+ export interface StartStreamOptions {
57
+ /**
58
+ * Publish straight to your own ingest URL (rtmp/rtmps/srt/whip). The glasses
59
+ * connect to it directly with no Glassly relay, so no playback URLs come back.
60
+ * Omit this to use the managed relay (the default, best for most apps).
61
+ */
62
+ direct?: string
63
+ /**
64
+ * Restream destinations the managed relay fans out to (YouTube, Twitch, …).
65
+ * Bare URL strings or `{url, name?}` objects; mix freely. Managed streams
66
+ * only — ignored when `direct` is set.
67
+ */
68
+ destinations?: Array<string | RestreamDestination>
69
+ video?: StreamVideoConfig
70
+ audio?: StreamAudioConfig
71
+ /** Play glasses-side stream start/stop sounds. Defaults to true. */
72
+ sound?: boolean
73
+ /**
74
+ * Managed ingest protocol, a latency/durability trade. Ignored when `direct`
75
+ * is set.
76
+ * - "srt" (default): ~10-20s latency via LL-HLS, shareable HLS/DASH URLs,
77
+ * and automatic recording.
78
+ * - "whip": sub-second WebRTC playback (use `webrtcUrl`/WHEP), but no
79
+ * HLS/DASH and no recording.
80
+ * - "rtmp": RTMPS ingest over TCP 443 → HLS playback. Use when WHIP/SRT
81
+ * UDP is blocked (then play `hlsUrl`, not WHEP).
82
+ */
83
+ ingest?: "srt" | "whip" | "rtmp"
84
+ /** Optional Bearer token for direct WHIP Authorization (custom authenticated endpoints). */
85
+ authToken?: string
86
+ }
87
+
88
+ export interface StreamResult {
89
+ streamId: string
90
+ status: string
91
+ resolvedConfig?: StreamResolvedConfig
92
+ /** Managed streams only: the live-input UID for building hosted-player URLs. */
93
+ liveInputId?: string
94
+ /** Managed streams only: which playback this stream serves. In "webrtc" mode
95
+ * `hlsUrl` never serves content. */
96
+ mode?: "hls" | "webrtc"
97
+ /** Managed streams only: HLS playback URL. */
98
+ hlsUrl?: string
99
+ /** Managed streams only: DASH playback URL. */
100
+ dashUrl?: string
101
+ /** Managed streams only: WHEP playback URL (present in "webrtc" mode). */
102
+ webrtcUrl?: string
103
+ }
104
+
105
+ /** Live encoder and device telemetry emitted periodically by supported glasses firmware. */
106
+ export interface StreamLiveStats {
107
+ /** Current encoded video bitrate in bits per second. */
108
+ bitrate?: number
109
+ /** Current encode frame rate. */
110
+ fps?: number
111
+ droppedFrames?: number
112
+ /** Seconds since the stream started. */
113
+ duration?: number
114
+ /** Device temperature in °C, if the hardware reports it. */
115
+ temperatureC?: number
116
+ }
117
+
118
+ export interface StreamStatus {
119
+ streamId: string
120
+ status: string
121
+ errorDetails?: string
122
+ /** Negotiated encode config — forwarded once, on the first status ack. */
123
+ resolvedConfig?: StreamResolvedConfig
124
+ stats?: StreamLiveStats
125
+ }
126
+
127
+ export class StreamModule {
128
+ constructor(private readonly session: MiniappSession) {}
129
+
130
+ /**
131
+ * Start a live video stream from the glasses camera.
132
+ *
133
+ * Managed (default) -- Glassly hosts the stream and the result carries
134
+ * `hlsUrl`/`dashUrl`/`webrtcUrl` for playback. Optionally fan out to your own
135
+ * `destinations`.
136
+ *
137
+ * Direct -- pass `direct` with your own ingest URL; the glasses publish to it
138
+ * and no playback URLs are returned.
139
+ */
140
+ async startStream(options: StartStreamOptions = {}): Promise<StreamResult> {
141
+ if (options.direct !== undefined) {
142
+ return this.session.sendRequest<StreamResult>({
143
+ type: MiniappRequestType.STREAM_START,
144
+ streamUrl: options.direct,
145
+ video: options.video,
146
+ audio: options.audio,
147
+ sound: options.sound ?? true,
148
+ ...(options.authToken ? {authToken: options.authToken} : {}),
149
+ })
150
+ }
151
+ return this.session.sendRequest<StreamResult>({
152
+ type: MiniappRequestType.MANAGED_STREAM_START,
153
+ restreamDestinations: options.destinations,
154
+ video: options.video,
155
+ audio: options.audio,
156
+ sound: options.sound ?? true,
157
+ ingest: options.ingest,
158
+ })
159
+ }
160
+
161
+ /** Stop the active stream, or a specific one by `streamId`. */
162
+ async stop(streamId?: string): Promise<void> {
163
+ await this.session.sendRequest<void>({
164
+ type: MiniappRequestType.STREAM_STOP,
165
+ streamId,
166
+ })
167
+ }
168
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @fileoverview SystemModule — OS-level utilities (share, open URL, clipboard, download, QR scan).
3
+ *
4
+ * These bridge to native phone capabilities via LocalMiniappRuntime.
5
+ */
6
+
7
+ import {MiniappRequestType} from "../protocol"
8
+ import {MiniappSession} from "../session"
9
+
10
+ export interface ShareOptions {
11
+ text?: string
12
+ url?: string
13
+ title?: string
14
+ /** Base64-encoded file data for file sharing. */
15
+ base64?: string
16
+ /** MIME type when sharing base64 data. */
17
+ mimeType?: string
18
+ /** Filename when sharing base64 data. */
19
+ filename?: string
20
+ }
21
+
22
+ export interface ShareResult {
23
+ success: boolean
24
+ cancelled?: boolean
25
+ }
26
+
27
+ export interface DownloadOptions {
28
+ /** URL to download from, OR base64 data. */
29
+ url?: string
30
+ base64?: string
31
+ filename?: string
32
+ mimeType?: string
33
+ }
34
+
35
+ export interface DownloadResult {
36
+ success: boolean
37
+ cancelled?: boolean
38
+ }
39
+
40
+ export interface ScanQrOptions {
41
+ /** Header title on the phone scanner overlay. */
42
+ title?: string
43
+ /** Hint shown over the camera viewfinder. */
44
+ hint?: string
45
+ }
46
+
47
+ export type ScanQrResult = {data: string; cancelled?: false} | {cancelled: true; data?: undefined}
48
+
49
+ export class SystemModule {
50
+ constructor(private readonly session: MiniappSession) {}
51
+
52
+ /** Open the OS share sheet with the given content. */
53
+ async share(options: ShareOptions): Promise<ShareResult> {
54
+ const result = await this.session.sendRequest<ShareResult>({
55
+ type: MiniappRequestType.SHARE,
56
+ ...options,
57
+ })
58
+ return result ?? {success: false}
59
+ }
60
+
61
+ /** Open a URL in the system browser. Blocks dangerous schemes (javascript:, file:). */
62
+ openUrl(url: string): void {
63
+ this.session.sendOneShot({
64
+ type: MiniappRequestType.OPEN_URL,
65
+ url,
66
+ })
67
+ }
68
+
69
+ /** Copy text to the system clipboard. */
70
+ async copyToClipboard(text: string): Promise<void> {
71
+ await this.session.sendRequest<void>({
72
+ type: MiniappRequestType.COPY_CLIPBOARD,
73
+ text,
74
+ })
75
+ }
76
+
77
+ /** Download a file. Opens the OS share sheet so user can choose save location. */
78
+ async download(options: DownloadOptions): Promise<DownloadResult> {
79
+ const result = await this.session.sendRequest<DownloadResult>({
80
+ type: MiniappRequestType.DOWNLOAD,
81
+ ...options,
82
+ })
83
+ return result ?? {success: false}
84
+ }
85
+
86
+ /**
87
+ * Ask the host to stop this miniapp. Use after finishing one-shot work —
88
+ * e.g. an action handler that woke the app headlessly and has nothing more
89
+ * to do (return the action's result first; exit tears the context down).
90
+ * Fire-and-forget: the JS context dies shortly after the host accepts.
91
+ */
92
+ exit(): void {
93
+ this.session.sendOneShot({
94
+ type: MiniappRequestType.SYSTEM_EXIT,
95
+ })
96
+ }
97
+
98
+ /**
99
+ * Open the phone camera QR scanner as a host overlay. Does not unmount the
100
+ * miniapp (so live sessions survive the scan). Resolves with the raw QR
101
+ * payload, or `{cancelled: true}` if the user dismisses the scanner.
102
+ */
103
+ async scanQr(options: ScanQrOptions = {}): Promise<ScanQrResult> {
104
+ const result = await this.session.sendRequest<ScanQrResult>(
105
+ {
106
+ type: MiniappRequestType.SCAN_QR,
107
+ ...options,
108
+ },
109
+ {timeoutMs: 0},
110
+ )
111
+ return result ?? { cancelled: true }
112
+ }
113
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * @fileoverview TranscriptionModule — top-level transcription API.
3
+ *
4
+ * Mirrors cloud SDK v3's TranscriptionManager:
5
+ *
6
+ * session.transcription.on(handler) // auto-detect
7
+ * session.transcription.forLanguage("en-US", handler) // single language
8
+ * session.transcription.forLanguage(["en-US", "es-ES"], handler) // multi-language
9
+ * session.transcription.configure({languageHints, vocabulary, diarization})
10
+ * session.transcription.stop() // tear down all
11
+ *
12
+ * Hoisted to top-level (vs. nested under `session.mic`) because transcription
13
+ * is a domain in its own right, not a microphone-input event. The `mic`
14
+ * module still exposes lower-level audio chunks + VAD; this module is for
15
+ * "give me text from speech."
16
+ *
17
+ * MICROPHONE permission must be declared in miniapp.json. The phone runtime
18
+ * rejects with PERMISSION_NOT_DECLARED otherwise.
19
+ */
20
+
21
+ import {MiniappRequestType, MiniappStreamType} from "../protocol"
22
+ import {MiniappSession} from "../session"
23
+ import type {TranscriptionData, UnsubscribeFn} from "./events"
24
+ import {requireLanguageHint, requireTranscriptionLanguage} from "./languages"
25
+ import type {LanguageHint, TranscriptionLanguage} from "./languages"
26
+
27
+ /** Configuration for cloud-side transcription behavior. */
28
+ export interface TranscriptionConfig {
29
+ /**
30
+ * Language hints to improve auto-detection accuracy. Bare ISO 639-1 codes
31
+ * from the registry (e.g. ["en", "ja"]); registry BCP-47 tags are accepted
32
+ * and normalized ("en-US" -> "en"). Anything else throws
33
+ * `MiniappValidationError`.
34
+ */
35
+ languageHints?: LanguageHint[]
36
+ /** Custom vocabulary / boosted terms (e.g. ["Glassly", "HIPAA"]). */
37
+ vocabulary?: string[]
38
+ /**
39
+ * Enable speaker diarisation. Currently always on for cloud transcription
40
+ * (the platform's provider runs diarization unconditionally and events
41
+ * carry `speakerId` when detected); the flag is accepted for
42
+ * forward-compatibility with providers where it is optional.
43
+ */
44
+ diarization?: boolean
45
+ }
46
+
47
+ export interface TranscriptionOptions {
48
+ /**
49
+ * Use only on-device STT for this listener; never deliver cloud transcripts.
50
+ * Other listeners on the same stream keep their own routing choice. Requires
51
+ * a downloaded local STT model.
52
+ */
53
+ forceLocal?: boolean
54
+ }
55
+
56
+ export class TranscriptionModule {
57
+ private readonly unsubs = new Set<UnsubscribeFn>()
58
+ private currentConfig: TranscriptionConfig | null = null
59
+
60
+ constructor(private readonly session: MiniappSession) {}
61
+
62
+ /**
63
+ * Subscribe to all transcription events (auto-detect language). The
64
+ * detected language is in `data.language`.
65
+ *
66
+ * Wire-level: subscribes to `transcription:auto`. Today's wildcard fan-out
67
+ * (handlers on `transcription:auto` receive any `transcription:<lang>`
68
+ * event) preserves existing semantics.
69
+ */
70
+ on(handler: (data: TranscriptionData) => void, options: TranscriptionOptions = {}): UnsubscribeFn {
71
+ return this.track(
72
+ this.session._subscribe(`${MiniappStreamType.TRANSCRIPTION}:auto`, handler as (data: unknown) => void, options),
73
+ )
74
+ }
75
+
76
+ /**
77
+ * Subscribe to transcription for one or more specific languages. Each call
78
+ * is independent; multiple can be active simultaneously.
79
+ *
80
+ * Languages are validated against the platform registry
81
+ * (`SUPPORTED_TRANSCRIPTION_LANGUAGES`): registry BCP-47 tags pass through,
82
+ * bare registry codes canonicalize ("fr" -> "fr-FR"), anything else throws
83
+ * `MiniappValidationError` immediately — an invalid language must never
84
+ * become a subscription that silently transcribes the wrong thing
85
+ * (OS-1746 / issue 021).
86
+ *
87
+ * @param language - Registry tag(s), e.g. `"en-US"` or `["en-US", "es-ES"]`.
88
+ * @param handler - Called for every event in any of the listed languages.
89
+ */
90
+ forLanguage(
91
+ language: TranscriptionLanguage | TranscriptionLanguage[],
92
+ handler: (data: TranscriptionData) => void,
93
+ options: TranscriptionOptions = {},
94
+ ): UnsubscribeFn {
95
+ const langs = (Array.isArray(language) ? language : [language]).map((lang) =>
96
+ requireTranscriptionLanguage(lang, "transcription.forLanguage(language)"),
97
+ )
98
+ if (langs.length === 0) return () => {}
99
+
100
+ const unsubs: UnsubscribeFn[] = []
101
+ for (const lang of langs) {
102
+ unsubs.push(
103
+ this.session._subscribe(
104
+ `${MiniappStreamType.TRANSCRIPTION}:${lang}`,
105
+ handler as (data: unknown) => void,
106
+ options,
107
+ ),
108
+ )
109
+ }
110
+ const combined: UnsubscribeFn = () => {
111
+ for (const u of unsubs) {
112
+ try {
113
+ u()
114
+ } catch {
115
+ /* ignore */
116
+ }
117
+ }
118
+ }
119
+ return this.track(combined)
120
+ }
121
+
122
+ /**
123
+ * Apply transcription configuration (language hints, custom vocabulary,
124
+ * diarisation toggle). Hints are validated against the registry and
125
+ * normalized to bare codes ("en-US" -> "en"); unknown values throw
126
+ * `MiniappValidationError` synchronously.
127
+ *
128
+ * Sent to the phone runtime as a REQUEST (not a one-shot) so the returned
129
+ * promise rejects if the runtime cannot apply it — a config that the
130
+ * platform ignores must be loud, not silent (issue 021 S3). Cached locally
131
+ * so the runtime can re-apply on reconnect.
132
+ */
133
+ configure(config: TranscriptionConfig): Promise<void> {
134
+ const normalized: TranscriptionConfig = {
135
+ ...config,
136
+ languageHints: config.languageHints?.map((hint) =>
137
+ requireLanguageHint(hint, "transcription.configure(languageHints)"),
138
+ ),
139
+ }
140
+ this.currentConfig = {...normalized}
141
+ return this.session
142
+ .sendRequest<void>({
143
+ type: MiniappRequestType.TRANSCRIPTION_CONFIG,
144
+ config: {...normalized},
145
+ })
146
+ .then(() => undefined)
147
+ }
148
+
149
+ /** Tear down every transcription subscription this module owns. */
150
+ stop(): void {
151
+ for (const u of this.unsubs) {
152
+ try {
153
+ u()
154
+ } catch {
155
+ /* ignore */
156
+ }
157
+ }
158
+ this.unsubs.clear()
159
+ }
160
+
161
+ /** True iff `MICROPHONE` is declared in the miniapp's manifest. */
162
+ get hasPermission(): boolean {
163
+ return this.session._hasManifestPermission("MICROPHONE")
164
+ }
165
+
166
+ /** @internal — current config, read by tests / future reconnect logic. */
167
+ _getConfig(): TranscriptionConfig | null {
168
+ return this.currentConfig ? {...this.currentConfig} : null
169
+ }
170
+
171
+ // ------------------------------------------------------------------------
172
+
173
+ private track(unsub: UnsubscribeFn): UnsubscribeFn {
174
+ this.unsubs.add(unsub)
175
+ return () => {
176
+ this.unsubs.delete(unsub)
177
+ unsub()
178
+ }
179
+ }
180
+ }