@bridgething/lib 0.1.2

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/dist/bindings/client.d.ts +1904 -0
  4. package/dist/bindings/client.d.ts.map +1 -0
  5. package/dist/bindings/client.js +2 -0
  6. package/dist/bindings/client.js.map +1 -0
  7. package/dist/bindings/gateway.d.ts +1508 -0
  8. package/dist/bindings/gateway.d.ts.map +1 -0
  9. package/dist/bindings/gateway.js +2 -0
  10. package/dist/bindings/gateway.js.map +1 -0
  11. package/dist/bindings/shared.d.ts +1485 -0
  12. package/dist/bindings/shared.d.ts.map +1 -0
  13. package/dist/bindings/shared.js +3 -0
  14. package/dist/bindings/shared.js.map +1 -0
  15. package/dist/bindings/stock.d.ts +14 -0
  16. package/dist/bindings/stock.d.ts.map +1 -0
  17. package/dist/bindings/stock.js +3 -0
  18. package/dist/bindings/stock.js.map +1 -0
  19. package/dist/bindings/wire.d.ts +47 -0
  20. package/dist/bindings/wire.d.ts.map +1 -0
  21. package/dist/bindings/wire.js +3 -0
  22. package/dist/bindings/wire.js.map +1 -0
  23. package/dist/codec.d.ts +66 -0
  24. package/dist/codec.d.ts.map +1 -0
  25. package/dist/codec.js +180 -0
  26. package/dist/codec.js.map +1 -0
  27. package/dist/framing.d.ts +34 -0
  28. package/dist/framing.d.ts.map +1 -0
  29. package/dist/framing.js +69 -0
  30. package/dist/framing.js.map +1 -0
  31. package/dist/index.d.ts +19 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +20 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/logger.d.ts +21 -0
  36. package/dist/logger.d.ts.map +1 -0
  37. package/dist/logger.js +23 -0
  38. package/dist/logger.js.map +1 -0
  39. package/dist/uuid-fields.generated.d.ts +7 -0
  40. package/dist/uuid-fields.generated.d.ts.map +1 -0
  41. package/dist/uuid-fields.generated.js +15 -0
  42. package/dist/uuid-fields.generated.js.map +1 -0
  43. package/dist/uuid.d.ts +6 -0
  44. package/dist/uuid.d.ts.map +1 -0
  45. package/dist/uuid.js +20 -0
  46. package/dist/uuid.js.map +1 -0
  47. package/dist/version.d.ts +2 -0
  48. package/dist/version.d.ts.map +1 -0
  49. package/dist/version.js +3 -0
  50. package/dist/version.js.map +1 -0
  51. package/package.json +87 -0
@@ -0,0 +1,1485 @@
1
+ /**
2
+ * Direction the accessory wants iOS to take when answering an incoming
3
+ * call while another call is active.
4
+ */
5
+ export type AcceptCallAction = 'accept' | 'endAndAccept';
6
+ /**
7
+ * An album reference.
8
+ */
9
+ export type Album = {
10
+ id: string;
11
+ name: string;
12
+ /**
13
+ * Opaque artwork asset id (`asset.get`), when known.
14
+ */
15
+ artwork_id: string | null;
16
+ };
17
+ /**
18
+ * Daemon-observed state of the ANCS GATT-client session against the
19
+ * connected iPhone. iOS-only; emitted on transitions so the companion
20
+ * app can confirm the LE-pair + ANCS authorization handshake completed.
21
+ *
22
+ * State machine:
23
+ * - `Unknown`: pre-boot only (no iAP2 has ever attached this session).
24
+ * - `Probing`: a session task is running but no determination yet,
25
+ * OR iAP2 just detached and we expect to re-probe on reconnect.
26
+ * - `Authorized`: ANCS attribute fetches are succeeding.
27
+ * - `Unauthorized`: ANCS service hidden, or auth-gate detected.
28
+ */
29
+ export type AncsAuthState = 'unknown' | 'probing' | 'authorized' | 'unauthorized';
30
+ /**
31
+ * Art render sizes a webapp declares so the companion warms exactly the
32
+ * pixels it renders: hero (now-playing / detail views) and thumb (queue /
33
+ * grid). Omitted in a manifest falls back to the canonical `{248, 96}`,
34
+ * which is also the stock webapp's profile.
35
+ */
36
+ export type ArtProfile = {
37
+ heroPx: number;
38
+ thumbPx: number;
39
+ };
40
+ /**
41
+ * An artist reference.
42
+ */
43
+ export type Artist = {
44
+ id: string;
45
+ name: string;
46
+ /**
47
+ * Opaque artwork asset id (`asset.get`), when known.
48
+ */
49
+ artwork_id: string | null;
50
+ };
51
+ export type AssetRetention = {
52
+ type: 'lru';
53
+ } | {
54
+ type: 'pinned';
55
+ } | {
56
+ type: 'ttl';
57
+ data: TtlRetention;
58
+ } | {
59
+ type: 'persistent';
60
+ };
61
+ /**
62
+ * What the gateway-side audio backend supports. `earcons` are short
63
+ * named sounds the companion can play; `voices` are TTS voices.
64
+ */
65
+ export type AudioCapabilities = {
66
+ earcons: Array<string>;
67
+ voices: Array<VoiceDescriptor>;
68
+ };
69
+ export type BoolField = {
70
+ key: string;
71
+ label: string;
72
+ default: boolean | null;
73
+ };
74
+ /**
75
+ * Bridge-side identity announce. Daemon sends one of these to every
76
+ * gateway on connect so the companion knows what daemon it's talking to
77
+ * and can opt out of unsupported surfaces.
78
+ */
79
+ export type BridgeThingMeta = {
80
+ bridgethingVersion: string;
81
+ libbridgethingVersion: string;
82
+ appName: string;
83
+ /**
84
+ * User-set display name for this device. None when the user hasn't
85
+ * set one yet; consumers fall back to `model_name` / `serial_number`.
86
+ * Set via the gateway-side `system.device.setNickname` surface.
87
+ */
88
+ nickname: string | null;
89
+ /**
90
+ * Daemon semver (no leading `v`), e.g. `0.8.4`. Compared directly to
91
+ * the manifest's daemon-component version for OTA hot-swap decisions.
92
+ */
93
+ appVersion: string;
94
+ osName: string;
95
+ osVersion: string;
96
+ osDescription: string;
97
+ btMac: string;
98
+ serialNumber: string;
99
+ fccId: string;
100
+ icId: string;
101
+ modelName: string;
102
+ /**
103
+ * OTA channel the running image was cut on, e.g. `stable` or `dev`.
104
+ * The companion's poll loop only auto-pushes when its configured
105
+ * channel matches; a mismatch surfaces a "channel switch needs full
106
+ * flash" event rather than swapping channels in-band.
107
+ */
108
+ channel: string;
109
+ /**
110
+ * Image variant the running image was cut as, e.g. `prod` or `dev`.
111
+ * Maps to the yocto image recipe name `bridgething-<variant>-image`,
112
+ * which is what the companion uses to construct the OTA artifact URL
113
+ * `images/<channel>/<image_version>/bridgething-<variant>-image.{swu,zck}`.
114
+ */
115
+ imageVariant: string;
116
+ /**
117
+ * Canonical image version (CalVer, e.g. `2026.05.0`). What the
118
+ * companion compares to the manifest's image-component version.
119
+ */
120
+ imageVersion: string;
121
+ imageBuildId: string;
122
+ imageBuildDate: string;
123
+ imageDistro: string;
124
+ imageMachine: string;
125
+ discord: string;
126
+ credits: string;
127
+ };
128
+ export type BrightnessMode = 'auto' | 'manual';
129
+ /**
130
+ * Backlight state. `level` is the user-set value (only respected in
131
+ * `Manual`); `effective_level` is what's actually on the panel - equal
132
+ * to `level` in `Manual`, ALS-derived in `Auto`.
133
+ */
134
+ export type BrightnessState = {
135
+ mode: BrightnessMode;
136
+ level: number;
137
+ effectiveLevel: number;
138
+ };
139
+ /**
140
+ * One row in a `BrowseResult`: either a folder (drilldown) or a leaf
141
+ * item the user can play / queue / favorite.
142
+ */
143
+ export type BrowseEntry = {
144
+ type: 'folder';
145
+ data: BrowseFolder;
146
+ } | {
147
+ type: 'item';
148
+ data: LibraryItem;
149
+ };
150
+ /**
151
+ * A drilldown node. `node_id` is opaque and gateway-defined; webapps
152
+ * pass it back as the next `browse({ node_id })` to descend. `total` is
153
+ * the count of children behind this folder when the gateway can cheaply
154
+ * expose it. `preview_children` is an inline first-N slice of those
155
+ * children so home-shelf shapes don't need a separate drill round-trip;
156
+ * gateways populate it when cheap (Spotify Web API home shelves include
157
+ * previews; Apple Music curated rails do too) and leave it `None`
158
+ * otherwise.
159
+ */
160
+ export type BrowseFolder = {
161
+ nodeId: string;
162
+ title: string;
163
+ subtitle: string | null;
164
+ artworkId: string | null;
165
+ total: number | null;
166
+ previewChildren: Array<BrowseEntry> | null;
167
+ };
168
+ /**
169
+ * Page of browse results. `total` is the count of items in the
170
+ * underlying collection when the gateway can cheaply expose it (None
171
+ * means indeterminate). `has_more` is the authoritative end-of-data
172
+ * signal - webapps paginate by raising `offset` until `has_more` is
173
+ * false rather than relying on `total`.
174
+ */
175
+ export type BrowseResult = {
176
+ entries: Array<BrowseEntry>;
177
+ total: number | null;
178
+ hasMore: boolean;
179
+ };
180
+ /**
181
+ * Why a call ended, surfaced on `onPhoneCallEnded`. `Failed` carries a
182
+ * platform-defined reason (network, busy, etc.).
183
+ */
184
+ export type CallEndReason = {
185
+ type: 'local';
186
+ } | {
187
+ type: 'remote';
188
+ } | {
189
+ type: 'missed';
190
+ } | {
191
+ type: 'declined';
192
+ } | {
193
+ type: 'failed';
194
+ data: {
195
+ reason: string;
196
+ };
197
+ };
198
+ /**
199
+ * What the daemon advertises to webapps. `gateway: None` means no
200
+ * companion is connected; webapps that depend on companion-routed
201
+ * surfaces (Library, Net, Notifications, etc.) should branch on this.
202
+ * `authority` is the live set of scopes the companion currently claims.
203
+ */
204
+ export type Capabilities = {
205
+ gateway: GatewayInfo | null;
206
+ available: SurfaceAvailability;
207
+ authority: Array<CompanionAuthorityScope>;
208
+ uriSchemes: Array<string>;
209
+ network: NetworkInfo;
210
+ audio: AudioCapabilities;
211
+ musicProvider: MusicProvider;
212
+ };
213
+ /**
214
+ * What call-control verbs are currently legal. Webapps must gate UI on
215
+ * these flags; sending an unavailable verb is a protocol violation, not
216
+ * a no-op. All `None` = no signal received yet, treat as conservatively
217
+ * unavailable.
218
+ */
219
+ export type CommunicationsState = {
220
+ signalStrength: number | null;
221
+ registrationStatus: RegistrationStatus | null;
222
+ airplaneMode: boolean | null;
223
+ carrierName: string | null;
224
+ cellularSupported: boolean | null;
225
+ telephonyEnabled: boolean | null;
226
+ faceTimeAudioEnabled: boolean | null;
227
+ faceTimeVideoEnabled: boolean | null;
228
+ muteStatus: boolean | null;
229
+ currentCallCount: number | null;
230
+ newVoicemailCount: number | null;
231
+ initiateCallAvailable: boolean | null;
232
+ endAndAcceptAvailable: boolean | null;
233
+ holdAndAcceptAvailable: boolean | null;
234
+ swapAvailable: boolean | null;
235
+ mergeAvailable: boolean | null;
236
+ holdAvailable: boolean | null;
237
+ };
238
+ /**
239
+ * One axis the companion can declare authority over. Each scope has a
240
+ * fallback source the daemon merges with when no claim is active or
241
+ * the claim has gone stale. Unknown scopes arriving at an older daemon
242
+ * are stored opaquely and ignored.
243
+ */
244
+ export type CompanionAuthorityScope = 'nowPlayingMetadata' | 'nowPlayingPlayback' | 'volume';
245
+ /**
246
+ * One key/value pair as exposed by config read APIs. `value` is always a
247
+ * string; consumers parse per the field's declared kind (number -> parseFloat,
248
+ * boolean -> "true"/"false", string/enum/secret -> as-is).
249
+ */
250
+ export type ConfigEntry = {
251
+ key: string;
252
+ value: string;
253
+ };
254
+ /**
255
+ * One declared user-tunable setting. Adjacent-tagged on the wire to
256
+ * stay typeshare-compatible: `{"type":"string","data":{"key":"zip",...}}`.
257
+ */
258
+ export type ConfigField = {
259
+ type: 'string';
260
+ data: StringField;
261
+ } | {
262
+ type: 'number';
263
+ data: NumberField;
264
+ } | {
265
+ type: 'boolean';
266
+ data: BoolField;
267
+ } | {
268
+ type: 'enum';
269
+ data: EnumField;
270
+ } | {
271
+ type: 'secret';
272
+ data: StringField;
273
+ };
274
+ /**
275
+ * The foreground app driving playback, surfaced on iOS over iAP2.
276
+ */
277
+ export type CurrentlyActiveApplication = {
278
+ /**
279
+ * Bundle identifier, e.g. `com.spotify.client`.
280
+ */
281
+ id: string;
282
+ name: string;
283
+ };
284
+ export type Device = {
285
+ name: string;
286
+ type: DeviceType;
287
+ mac: string;
288
+ default: boolean;
289
+ };
290
+ export type DeviceType = 'android' | 'iOS' | 'windows' | 'macOS' | 'linux' | 'unknown';
291
+ /**
292
+ * Daemon health snapshot. `load_avg` is the unix 1/5/15-minute load.
293
+ * `soc_temp_c` may be `None` on builds where the SoC thermal probe is
294
+ * not exposed by the kernel.
295
+ */
296
+ export type Diagnostics = {
297
+ diskUsedBytes: number;
298
+ diskFreeBytes: number;
299
+ memUsedBytes: number;
300
+ memAvailBytes: number;
301
+ uptimeS: number;
302
+ socTempC: number | null;
303
+ loadAvg: [number, number, number];
304
+ daemonVersion: string;
305
+ kernelVersion: string;
306
+ bootId: string;
307
+ };
308
+ /**
309
+ * Why a notification went away. `Acted` covers both positive and
310
+ * negative invokes; gateways that distinguish dismiss-vs-acted may
311
+ * surface both as `Acted`.
312
+ */
313
+ export type DismissReason = 'userDismissed' | 'acted' | 'remoteDismissed';
314
+ /**
315
+ * DTMF tones the accessory can play during an active call.
316
+ */
317
+ export type DtmfTone = 'd0' | 'd1' | 'd2' | 'd3' | 'd4' | 'd5' | 'd6' | 'd7' | 'd8' | 'd9' | 'star' | 'hash';
318
+ /**
319
+ * Direction the accessory wants iOS to take when ending a call.
320
+ */
321
+ export type EndCallAction = 'end' | 'endAll';
322
+ export type EnumField = {
323
+ key: string;
324
+ label: string;
325
+ choices: Array<string>;
326
+ default: string | null;
327
+ };
328
+ /**
329
+ * Page of the user's favorited / liked / saved library items. Mixed-kind
330
+ * because most platforms expose one "Saved" surface across kinds.
331
+ */
332
+ export type FavoritesPage = {
333
+ items: Array<LibraryItem>;
334
+ total: number | null;
335
+ hasMore: boolean;
336
+ };
337
+ export type ForwardMessage = {
338
+ encoding: 'text';
339
+ data: string;
340
+ } | {
341
+ encoding: 'json';
342
+ data: unknown;
343
+ } | {
344
+ encoding: 'binary';
345
+ data: Uint8Array;
346
+ };
347
+ /**
348
+ * What a connected companion advertises about itself. Sent on every
349
+ * session-up via `GatewayToBridgeCapabilitiesMsg::Announce`, and re-sent
350
+ * on any change. The daemon caches the latest snapshot per peer and
351
+ * derives the webapp-facing `Capabilities` from it.
352
+ */
353
+ export type GatewayCapabilities = {
354
+ gateway: GatewayInfo;
355
+ uriSchemes: Array<string>;
356
+ network: NetworkInfo;
357
+ available: SurfaceAvailability;
358
+ audio: AudioCapabilities;
359
+ musicProvider: MusicProvider;
360
+ };
361
+ /**
362
+ * Identity payload describing the companion peer the daemon is talking
363
+ * to. Address is the BT MAC the companion advertises; on transports without
364
+ * a stable MAC (the network gateway's `0xfe:fe:...` synthetic addrs) it
365
+ * is the synthetic address as a string.
366
+ */
367
+ export type GatewayInfo = {
368
+ address: string;
369
+ name: string;
370
+ osName: string;
371
+ appName: string;
372
+ appVersion: string;
373
+ adapterVersion: string;
374
+ libVersion: string;
375
+ libbridgethingVersion: string;
376
+ };
377
+ /**
378
+ * Subscriber's accuracy preference. `Coarse` opts into the lower-power
379
+ * city-block-grade fix on platforms that distinguish (iOS reduced
380
+ * accuracy, Android `PRIORITY_BALANCED_POWER_ACCURACY`). The daemon
381
+ * aggregates across subscribers and forwards the most-demanding to the
382
+ * companion.
383
+ */
384
+ export type GeoAccuracy = 'coarse' | 'fine';
385
+ export type GeoError = 'permissionDenied' | 'unavailable' | 'unknownToken';
386
+ export type HardwareError = 'levelOutOfRange' | 'modeMismatch';
387
+ /**
388
+ * Snapshot of the device's hardware-controlled surfaces. Sent on
389
+ * `hardware.state.get` and re-broadcast on any change. `ambient_level`
390
+ * is the 0-255 ambient light reading.
391
+ */
392
+ export type HardwareState = {
393
+ brightness: BrightnessState;
394
+ ambientLevel: number;
395
+ };
396
+ /**
397
+ * One header on an HTTP request or response. Key order is preserved
398
+ * across serialize/deserialize.
399
+ */
400
+ export type HttpHeader = {
401
+ name: string;
402
+ value: string;
403
+ };
404
+ export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
405
+ /**
406
+ * An image delivered either as an opaque asset id (fetch via `asset.get`)
407
+ * or as inline bytes.
408
+ */
409
+ export type Image = {
410
+ type: 'id';
411
+ data: string;
412
+ } | {
413
+ type: 'bytes';
414
+ data: Uint8Array;
415
+ };
416
+ /**
417
+ * What kind of outbound call the accessory wants placed.
418
+ */
419
+ export type InitiateCallType = 'destination' | 'voicemail' | 'redial';
420
+ /**
421
+ * Coarse type tag a webapp uses to filter or branch. Mirrors the variant
422
+ * names of `LibraryItem`; kept separate so search/recommendations can
423
+ * constrain by kind without having to construct a sample item.
424
+ */
425
+ export type ItemKind = 'track' | 'album' | 'playlist' | 'podcastEpisode' | 'show' | 'artist' | 'station';
426
+ /**
427
+ * Stable URI + kind a webapp passes back to act on a library item
428
+ * (e.g. `player.play({ uri })`, `library.favorites.toggle({ item })`).
429
+ * `persistent_id` is the platform-stable id when the gateway has one;
430
+ * webapps treat it as opaque.
431
+ */
432
+ export type ItemRef = {
433
+ uri: string;
434
+ kind: ItemKind;
435
+ persistentId: string | null;
436
+ };
437
+ export type LibraryError = {
438
+ type: 'notFound';
439
+ data: {
440
+ uri: string;
441
+ };
442
+ } | {
443
+ type: 'notSupported';
444
+ data: {
445
+ reason: string;
446
+ };
447
+ } | {
448
+ type: 'unauthorized';
449
+ } | {
450
+ type: 'noGateway';
451
+ };
452
+ /**
453
+ * One playable / browsable item from the library. Lean per-variant
454
+ * payload - gateways translate platform-specific extras down to these
455
+ * fields, rare per-platform fields just don't surface. Forward-compat:
456
+ * adding new variants or fields is an additive change webapps can
457
+ * branch on.
458
+ */
459
+ export type LibraryItem = {
460
+ type: 'track';
461
+ data: Track;
462
+ } | {
463
+ type: 'album';
464
+ data: Album;
465
+ } | {
466
+ type: 'playlist';
467
+ data: Playlist;
468
+ } | {
469
+ type: 'podcastEpisode';
470
+ data: PodcastEpisode;
471
+ } | {
472
+ type: 'show';
473
+ data: Show;
474
+ } | {
475
+ type: 'artist';
476
+ data: Artist;
477
+ } | {
478
+ type: 'station';
479
+ data: Station;
480
+ };
481
+ /**
482
+ * One log record. `ts_unix_s` is unix-epoch seconds. `target` is the
483
+ * tracing target / unit name; `message` is the rendered single-line
484
+ * body. Pre-filtered at subscription time so wire-bloating trace
485
+ * events don't reach webapps.
486
+ */
487
+ export type LogEntry = {
488
+ tsUnixS: number;
489
+ level: LogLevel;
490
+ target: string;
491
+ message: string;
492
+ };
493
+ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
494
+ /**
495
+ * What stream of log records a subscription pulls from. `Daemon` is the
496
+ * bridgething tracing subscriber; `System` is the `journald` view; `All`
497
+ * merges both in arrival order.
498
+ */
499
+ export type LogSource = 'daemon' | 'system' | 'all';
500
+ /**
501
+ * Currently-playing track, populated to the extent the gateway/iAP2
502
+ * stream has surfaced. All fields are optional because each one arrives
503
+ * as a separate attribute fetch on iAP2; the daemon accumulates and the
504
+ * snapshot reflects whatever's known so far.
505
+ */
506
+ export type MediaItem = {
507
+ uri: string | null;
508
+ persistentId: string | null;
509
+ title: string | null;
510
+ album: string | null;
511
+ albumUri: string | null;
512
+ albumArtist: string | null;
513
+ artist: string | null;
514
+ artistUri: string | null;
515
+ liked: boolean | null;
516
+ artworkId: string | null;
517
+ durationMs: number | null;
518
+ mediaTypes: Array<MediaType> | null;
519
+ trackNumber: number | null;
520
+ trackCount: number | null;
521
+ isLikeSupported: boolean | null;
522
+ isBanSupported: boolean | null;
523
+ isBanned: boolean | null;
524
+ chapterCount: number | null;
525
+ };
526
+ /**
527
+ * Per-track attributes that vary per song. `persistent_id` is a stable
528
+ * per-platform identifier (iAP2 sends u64; we hex-encode it on the
529
+ * wire). `artwork_id` is an opaque asset id - webapps pass this value
530
+ * to `ClientToBridgeAssetMsg::Get` to retrieve the bytes. The id namespace
531
+ * is producer-defined: iAP2 emits `iap2/art/<persistent_hex>/<n>`, the
532
+ * companion picks whatever shape it wants (e.g. `spotify/track/<id>/image`).
533
+ * Webapps treat the value as opaque.
534
+ */
535
+ export type MediaItemUpdate = {
536
+ persistentId: string | null;
537
+ title: string | null;
538
+ album: string | null;
539
+ albumUri: string | null;
540
+ albumArtist: string | null;
541
+ artist: string | null;
542
+ artistUri: string | null;
543
+ liked: boolean | null;
544
+ artworkId: string | null;
545
+ durationMs: number | null;
546
+ mediaTypes: Array<MediaType> | null;
547
+ trackNumber: number | null;
548
+ trackCount: number | null;
549
+ isLikeSupported: boolean | null;
550
+ isBanSupported: boolean | null;
551
+ isBanned: boolean | null;
552
+ isResidentOnDevice: boolean | null;
553
+ chapterCount: number | null;
554
+ };
555
+ /**
556
+ * The kind of media currently playing. Multi-typed: an item can be
557
+ * e.g. both `Podcast` and `AudioBook` (rare). Drives webapp UI choices
558
+ * like skip-15s-vs-skip-track and chapter UI.
559
+ */
560
+ export type MediaType = 'music' | 'podcast' | 'audioBook';
561
+ /**
562
+ * Which music service the companion is currently logged into and
563
+ * driving on behalf of the user. `None` when no glue is attached.
564
+ */
565
+ export type MusicProvider = 'none' | 'spotify' | 'appleMusic' | 'tidal';
566
+ export type NetError = {
567
+ type: 'requestFailed';
568
+ data: {
569
+ reason: string;
570
+ };
571
+ } | {
572
+ type: 'timeout';
573
+ } | {
574
+ type: 'unavailable';
575
+ } | {
576
+ type: 'noGateway';
577
+ };
578
+ export type NetFetchRequest = {
579
+ url: string;
580
+ method: HttpMethod;
581
+ headers: Array<HttpHeader>;
582
+ body?: Uint8Array | null;
583
+ timeoutMs: number | null;
584
+ redirect: RedirectPolicy;
585
+ };
586
+ export type NetFetchResponse = {
587
+ status: number;
588
+ headers: Array<HttpHeader>;
589
+ body: Uint8Array;
590
+ };
591
+ /**
592
+ * What kind of network the companion's host is currently using. `metered`
593
+ * is the OS-reported metered flag; webapps should treat it as a hint
594
+ * (e.g. defer non-essential fetches) rather than a hard ban.
595
+ */
596
+ export type NetworkInfo = {
597
+ kind: NetworkKind;
598
+ metered: boolean;
599
+ };
600
+ export type NetworkKind = 'unknown' | 'wifi' | 'cellular' | 'ethernet';
601
+ /**
602
+ * One alternate interpretation the LLM surfaced alongside the primary.
603
+ * Populated when the LLM returns `ambiguous_alternates` in its
604
+ * json_schema output; consumed by the companion's CLARIFY UI so the
605
+ * user can pick.
606
+ */
607
+ export type NluAlternate = {
608
+ intent: string;
609
+ slots: NluSlots | null;
610
+ };
611
+ /**
612
+ * Confidence the companion-side NLU pipeline attaches to a resolved
613
+ * intent: a coarse "low" | "medium" | "high" level per channel, one for
614
+ * the intent match and one for the extracted slots.
615
+ */
616
+ export type NluConfidence = {
617
+ /**
618
+ * "low" | "medium" | "high". The LLM emits one of these per channel.
619
+ */
620
+ intent: string;
621
+ slots: string | null;
622
+ };
623
+ /**
624
+ * What the companion-side NLU resolved an utterance to, sent across
625
+ * the gateway link for the daemon to dispatch. This is the bridgething-
626
+ * native shape; the daemon's stock-compat layer wraps it into the
627
+ * SLIMO `NluMessage` envelope when the active webapp is stock.
628
+ *
629
+ * `transcript` is the ASR output the NLU ran on; carried so the daemon
630
+ * can echo it for telemetry and so SHOW+UNKNOWN+query="DJ"-style
631
+ * stock-compat fallbacks have the raw query available.
632
+ */
633
+ export type NluResolvedIntent = {
634
+ intent: string;
635
+ slots: NluSlots;
636
+ transcript: string;
637
+ confidence: NluConfidence | null;
638
+ alternates: Array<NluAlternate> | null;
639
+ };
640
+ /**
641
+ * Slot catalog. Every intent projects through this flat shape;
642
+ * per-intent slot allowlists are enforced by the json_schema grammar at
643
+ * decode time, not by this struct. The wire payload omits absent slots,
644
+ * so a PLAY-with-artist row is just `{ "artist": "..." }` on the wire.
645
+ *
646
+ * String values are passed through verbatim from the user's transcript
647
+ * (no normalization at this layer); the SpotifyResolver may decorate
648
+ * the slots with a `uri` after catalog lookup.
649
+ */
650
+ export type NluSlots = {
651
+ artist: string | null;
652
+ track: string | null;
653
+ album: string | null;
654
+ playlist: string | null;
655
+ podcast: string | null;
656
+ episode: string | null;
657
+ mood: string | null;
658
+ genre: string | null;
659
+ era: string | null;
660
+ popularityFilter: string | null;
661
+ entityType: string | null;
662
+ query: string | null;
663
+ /**
664
+ * WEBAPP_INTENT only: the filler-stripped natural-language command
665
+ * the active webapp's voice grammar handler will parse.
666
+ */
667
+ rawQuery: string | null;
668
+ /**
669
+ * WEBAPP_INTENT / OPEN_WEBAPP only.
670
+ */
671
+ webappId: string | null;
672
+ webappName: string | null;
673
+ /**
674
+ * PLAY_PRESET / SAVE_TO_PRESET only. String because users say "two"
675
+ * but stock SLIMO expects an Array<string> envelope - the stock
676
+ * translation wraps as needed.
677
+ */
678
+ preset: string | null;
679
+ /**
680
+ * VOLUME_UP / VOLUME_DOWN. "small" | "large" | numeric step.
681
+ */
682
+ amount: string | null;
683
+ /**
684
+ * VOLUME_ABSOLUTE. 0-100.
685
+ */
686
+ level: number | null;
687
+ /**
688
+ * Post-resolution Spotify URI. Populated by the companion's
689
+ * SpotifyResolver after the NLU stage; daemon dispatches directly
690
+ * to playback when set.
691
+ */
692
+ uri: string | null;
693
+ };
694
+ /**
695
+ * One notification surfaced from the connected companion's notification
696
+ * center. `id` is companion-stable for the lifetime of the notification
697
+ * - webapps pass it to `invokePositive`/`invokeNegative` and listen for
698
+ * `onNotificationRemoved`. Bodies (`title`/`subtitle`/`message`) are all
699
+ * optional because ANCS treats them as separate attribute fetches.
700
+ */
701
+ export type Notification = {
702
+ id: string;
703
+ app: NotificationApp;
704
+ category: NotificationCategory;
705
+ title: string | null;
706
+ subtitle: string | null;
707
+ message: string | null;
708
+ timestampUnixS: number | null;
709
+ flags: NotificationFlags;
710
+ positiveAction: NotificationAction | null;
711
+ negativeAction: NotificationAction | null;
712
+ };
713
+ /**
714
+ * One ANCS-style action slot. `label` is the gateway-localized prompt
715
+ * the webapp renders on the action button.
716
+ */
717
+ export type NotificationAction = {
718
+ label: string;
719
+ };
720
+ /**
721
+ * Originating app metadata. `bundle_id` is platform-stable
722
+ * (`com.apple.MobileSMS`, `com.spotify.client`, etc.); `display_name`
723
+ * and `icon_asset_id` are best-effort and may be missing on Android
724
+ * gateways that don't surface them cheaply.
725
+ */
726
+ export type NotificationApp = {
727
+ bundleId: string;
728
+ displayName: string | null;
729
+ iconAssetId: string | null;
730
+ };
731
+ export type NotificationCategory = 'other' | 'incomingCall' | 'missedCall' | 'voicemail' | 'social' | 'schedule' | 'email' | 'news' | 'healthAndFitness' | 'businessAndFinance' | 'location' | 'entertainment';
732
+ /**
733
+ * ANCS-shaped flags. `silent` mirrors the iOS "do not surface
734
+ * audibly" hint, `important` is the high-importance flag, and
735
+ * `pre_existing` is true for notifications that arrived before the
736
+ * daemon connected (replayed by the companion on first sync).
737
+ */
738
+ export type NotificationFlags = {
739
+ silent: boolean;
740
+ important: boolean;
741
+ preExisting: boolean;
742
+ };
743
+ /**
744
+ * Delta event the companion or iAP2 stream emits whenever a player
745
+ * attribute changes. Every field is optional: producers send partial
746
+ * updates populating only what changed, and fields left unset keep
747
+ * their prior value.
748
+ */
749
+ export type NowPlayingUpdate = {
750
+ mediaItem: MediaItemUpdate | null;
751
+ playback: PlaybackUpdate | null;
752
+ };
753
+ export type NumberField = {
754
+ key: string;
755
+ label: string;
756
+ min: number | null;
757
+ max: number | null;
758
+ step: number | null;
759
+ default: number | null;
760
+ };
761
+ export type OtaError = {
762
+ code: OtaErrorCode;
763
+ msg: string;
764
+ };
765
+ /**
766
+ * Terminal error from the OTA orchestrator. After an `OtaError` the
767
+ * orchestrator is back to idle and a fresh `OtaBegin` may be sent.
768
+ */
769
+ export type OtaErrorCode = 'unknownUpdate' | 'offsetMismatch' | 'hashMismatch' | 'sizeMismatch' | 'cancelled' | 'writeFailed' | 'confirmFailed' | 'internal';
770
+ /**
771
+ * What the streamed bytes are going to be applied as.
772
+ *
773
+ * `Image` streams a `.swu` through libswupdate + slot flip + reboot.
774
+ * `Daemon` streams a fresh aarch64 daemon binary, atomic-rotates on
775
+ * the bandaid bind-mount, restarts the service. `BuiltinWebapp`
776
+ * streams a zip bundle of hub or stock, validates the manifest id is
777
+ * one of the reserved built-ins, atomic-rotates the bundle dir on the
778
+ * bandaid bind-mount, restarts the service. `InstalledWebapp` streams
779
+ * a zip bundle of a third-party (non-reserved) webapp and installs it
780
+ * into the writable registry; it neither stages on the bandaid nor
781
+ * restarts, and is never part of an `OtaActivate` batch.
782
+ *
783
+ * Companions key reboot expectations off this: image means the device
784
+ * power-cycles; daemon and builtin-webapp mean the daemon process
785
+ * restarts and the gateway link drops and reconnects; installed-webapp
786
+ * applies in place with no restart, and the terminal signal is the
787
+ * `WebappInstalled` event (or an `OtaError`).
788
+ */
789
+ export type OtaKind = 'image' | 'daemon' | 'builtinWebapp' | 'installedWebapp';
790
+ /**
791
+ * Stage of the OTA orchestrator. The phase set is shared between
792
+ * kinds, with non-image kinds emitting a subset.
793
+ *
794
+ * Image: `Streaming` -> `Verifying` -> `Writing` (libswupdate to slot)
795
+ * -> `Confirming` (try-counter reset) -> `Reboot`.
796
+ *
797
+ * Daemon and BuiltinWebapp: `Streaming` -> `Verifying` -> `Writing`,
798
+ * where `Writing`/100 means the piece is validated and staged on the
799
+ * bandaid (not yet live). The atomic rotate and the single `systemctl
800
+ * restart` happen later, on `OtaActivate`, which emits the terminal
801
+ * `Reboot` for the whole batch. `Confirming` is image-only.
802
+ *
803
+ * InstalledWebapp: `Streaming` -> `Verifying` -> `Writing`/0 while the
804
+ * bundle installs into the writable registry. There is no `Writing`/100,
805
+ * no `Confirming`, and no `Reboot`; the terminal signal is the
806
+ * `WebappInstalled` event (or an `OtaError`).
807
+ */
808
+ export type OtaPhase = 'streaming' | 'verifying' | 'writing' | 'confirming' | 'reboot';
809
+ /**
810
+ * Per-phase progress tick. `percent` is 0-100 within the current
811
+ * phase, not the overall flow. `eta_ms` is best-effort remaining time
812
+ * for the phase when the orchestrator can compute it.
813
+ */
814
+ export type OtaProgress = {
815
+ phase: OtaPhase;
816
+ percent: number;
817
+ etaMs: number | null;
818
+ };
819
+ export type Peer = {
820
+ device: Device;
821
+ paired: boolean;
822
+ iap2: PeerIap2Status;
823
+ companion: PeerCompanionStatus;
824
+ displayName: string | null;
825
+ language: string | null;
826
+ uuid: string | null;
827
+ };
828
+ export type PeerCompanionStatus = {
829
+ type: 'none';
830
+ } | {
831
+ type: 'pending';
832
+ } | {
833
+ type: 'connected';
834
+ data: GatewayInfo;
835
+ };
836
+ export type PeerIap2Status = 'none' | 'linkUp' | 'authenticated' | 'identified';
837
+ /**
838
+ * One telephony call. `call_id` is companion-stable for the call's
839
+ * lifetime; webapps pass it back to `answer`/`decline`/`end`/`hold`.
840
+ * `remote_id` is the raw E.164 (or platform raw); `display_name` is the
841
+ * gateway's resolved contact name when available.
842
+ */
843
+ export type PhoneCall = {
844
+ callId: string;
845
+ remoteId: string;
846
+ displayName: string;
847
+ status: PhoneCallStatus;
848
+ direction: PhoneCallDirection;
849
+ startedAtUnixS: number | null;
850
+ label: string | null;
851
+ addressBookId: string | null;
852
+ service: PhoneCallService | null;
853
+ isConferenced: boolean | null;
854
+ conferenceGroup: number | null;
855
+ };
856
+ export type PhoneCallDirection = 'incoming' | 'outgoing';
857
+ /**
858
+ * Call bearer / service kind. iAP2's `CallStateUpdateService` enum
859
+ * values, projected to our wire surface. Companion gateways that don't
860
+ * distinguish bearers project all calls to `Telephony`.
861
+ */
862
+ export type PhoneCallService = 'unknown' | 'telephony' | 'faceTimeAudio' | 'faceTimeVideo';
863
+ export type PhoneCallStatus = 'disconnected' | 'sending' | 'ringing' | 'connecting' | 'active' | 'held' | 'disconnecting';
864
+ export type PhoneError = {
865
+ type: 'callNotFound';
866
+ data: {
867
+ call_id: string;
868
+ };
869
+ } | {
870
+ type: 'actionRejected';
871
+ data: {
872
+ reason: string;
873
+ };
874
+ } | {
875
+ type: 'noTarget';
876
+ } | {
877
+ type: 'unavailable';
878
+ data: {
879
+ verb: string;
880
+ };
881
+ };
882
+ /**
883
+ * Snapshot of every active call known to the gateway. Multi-call is
884
+ * possible (call-waiting, conference) - webapps rendering only one
885
+ * active call typically pick the first non-Held entry.
886
+ */
887
+ export type PhoneState = {
888
+ activeCalls: Array<PhoneCall>;
889
+ };
890
+ /**
891
+ * Optional context for `play({ uri })`. `context_uri` is the album /
892
+ * playlist / show URI the track is being played from; gateways with
893
+ * playlist support honor it for skip-next semantics. `position` is the
894
+ * 0-based index inside the context.
895
+ */
896
+ export type PlayContext = {
897
+ contextUri: string;
898
+ position: number | null;
899
+ };
900
+ /**
901
+ * Per-session playback snapshot: where in the song we are, what mode is
902
+ * engaged. `position_ms` is the live playhead at snapshot time; webapps
903
+ * extrapolate forward locally while `state == Playing`.
904
+ *
905
+ * `set_elapsed_time_available` gates scrub UI: when false, the foreground
906
+ * app refuses absolute-position seeks and webapps must disable the scrub
907
+ * thumb. `None` means unknown (no signal received yet); webapps treat
908
+ * unknown as "available" for backward compatibility with older gateways.
909
+ */
910
+ export type Playback = {
911
+ state: PlaybackState;
912
+ positionMs: number;
913
+ positionAgeMs: number | null;
914
+ shuffle: boolean;
915
+ shuffleMode: ShuffleMode | null;
916
+ repeat: RepeatMode;
917
+ queueIndex: number | null;
918
+ queueCount: number | null;
919
+ queueChapterIndex: number | null;
920
+ setElapsedTimeAvailable: boolean | null;
921
+ queueListAvail: boolean | null;
922
+ appleMusicRadioAd: boolean | null;
923
+ };
924
+ /**
925
+ * What the current track is playing from: the playlist / album / show /
926
+ * artist context. Webapps render "playing from <name>"; `uri` lets them
927
+ * drill into it. `name` is `None` until the companion resolves it.
928
+ */
929
+ export type PlaybackContext = {
930
+ uri: string;
931
+ name: string | null;
932
+ };
933
+ /**
934
+ * Repeat and shuffle toggle state.
935
+ */
936
+ export type PlaybackOptions = {
937
+ repeat: RepeatMode;
938
+ shuffle: boolean;
939
+ };
940
+ /**
941
+ * A resolved queue: the current track flanked by upcoming and previous tracks.
942
+ */
943
+ export type PlaybackQueue = {
944
+ next: Array<Track>;
945
+ current: Track;
946
+ previous: Array<Track>;
947
+ };
948
+ /**
949
+ * Which player controls the current source supports; webapps disable the
950
+ * UI for any capability that is `false`.
951
+ */
952
+ export type PlaybackRestrictions = {
953
+ can_repeat_context: boolean;
954
+ can_repeat_track: boolean;
955
+ can_seek: boolean;
956
+ can_skip_next: boolean;
957
+ can_skip_prev: boolean;
958
+ can_toggle_shuffle: boolean;
959
+ can_like: boolean;
960
+ can_change_volume: boolean;
961
+ can_set_output: boolean;
962
+ };
963
+ /**
964
+ * Three-state playback. `Stopped` is the no-track-loaded resting state;
965
+ * `Paused` is "track is loaded, position is held"; `Playing` is the
966
+ * progressing state.
967
+ */
968
+ export type PlaybackState = 'stopped' | 'paused' | 'playing';
969
+ /**
970
+ * Per-playback-session attributes that vary regardless of track:
971
+ * playing/paused, position, shuffle/repeat, and the iOS bundle
972
+ * identifier of the app currently driving playback (e.g.
973
+ * `"com.spotify.client"`). `app_bundle` is null on the Android path
974
+ * since it isn't a meaningful surface there.
975
+ *
976
+ * `set_elapsed_time_available` is the gate webapps must honor for
977
+ * scrub UI: when false, scrubbing is unsupported by the foreground
978
+ * app and the seek button must be disabled.
979
+ */
980
+ export type PlaybackUpdate = {
981
+ playing: boolean | null;
982
+ positionMs: number | null;
983
+ shuffle: boolean | null;
984
+ shuffleMode: ShuffleMode | null;
985
+ repeat: RepeatMode | null;
986
+ appBundle: string | null;
987
+ appDisplayName: string | null;
988
+ queueIndex: number | null;
989
+ queueCount: number | null;
990
+ queueChapterIndex: number | null;
991
+ playbackSpeed: number | null;
992
+ setElapsedTimeAvailable: boolean | null;
993
+ queueListAvail: boolean | null;
994
+ appleMusicRadioAd: boolean | null;
995
+ appleMusicRadioStationName: string | null;
996
+ };
997
+ export type PlayerError = {
998
+ type: 'schemeUnclaimed';
999
+ data: {
1000
+ scheme: string;
1001
+ };
1002
+ } | {
1003
+ type: 'playFailed';
1004
+ data: {
1005
+ reason: string;
1006
+ };
1007
+ } | {
1008
+ type: 'noGateway';
1009
+ } | {
1010
+ type: 'notInQueue';
1011
+ data: {
1012
+ index: number;
1013
+ };
1014
+ };
1015
+ /**
1016
+ * User-tunable knobs that are not "currently playing" state.
1017
+ * `crossfade_ms = None` is "crossfade off"; `Some(0)` is also off but
1018
+ * distinguishes "user explicitly set zero" from "feature unsupported by
1019
+ * gateway".
1020
+ */
1021
+ export type PlayerOptions = {
1022
+ speed: number;
1023
+ crossfade_ms: number | null;
1024
+ };
1025
+ /**
1026
+ * Full player snapshot the daemon broadcasts to webapps. Initial value
1027
+ * arrives on `BridgeToClientPlayerMsg::StateChange` at connect time;
1028
+ * subsequent changes flow as `NowPlayingUpdate` deltas the client SDK
1029
+ * merges into the cached snapshot.
1030
+ */
1031
+ export type PlayerState = {
1032
+ track: MediaItem | null;
1033
+ playback: Playback;
1034
+ queue: Array<QueueItem>;
1035
+ options: PlayerOptions;
1036
+ context: PlaybackContext | null;
1037
+ };
1038
+ /**
1039
+ * Lean cross-platform shape for a playlist. `uri` is what `player.play`
1040
+ * would route on; `track_count` is best-effort (some sources don't expose
1041
+ * it cheaply); `owner_name` is whatever the source surfaces (Spotify
1042
+ * owner, Apple Music curator, etc.).
1043
+ */
1044
+ export type Playlist = {
1045
+ uri: string;
1046
+ name: string;
1047
+ ownerName: string | null;
1048
+ trackCount: number | null;
1049
+ artworkId: string | null;
1050
+ };
1051
+ /**
1052
+ * One episode of a podcast. `show_name` mirrors what the gateway exposes
1053
+ * at episode-level so a webapp can render show + episode without a
1054
+ * separate fetch. `published_at_ms` is best-effort; not every gateway
1055
+ * surfaces it.
1056
+ */
1057
+ export type PodcastEpisode = {
1058
+ uri: string;
1059
+ name: string;
1060
+ showName: string | null;
1061
+ durationMs: number | null;
1062
+ publishedAtUnixS: number | null;
1063
+ artworkId: string | null;
1064
+ };
1065
+ /**
1066
+ * One position fix from the gateway. `accuracy_m` is the 1-sigma
1067
+ * horizontal radius. `speed_mps` and `heading_deg` are populated when
1068
+ * the underlying source provides them (CLLocation on iOS does for moving
1069
+ * fixes; Android's FusedLocationProvider similar). `ts_ms` is the
1070
+ * gateway-provided fix timestamp, not the wire-arrival time.
1071
+ */
1072
+ export type Position = {
1073
+ lat: number;
1074
+ lon: number;
1075
+ altM: number | null;
1076
+ accuracyM: number;
1077
+ speedMps: number | null;
1078
+ headingDeg: number | null;
1079
+ tsUnixS: number;
1080
+ };
1081
+ export type Priority = 'normal' | 'bulk' | 'background';
1082
+ /**
1083
+ * One row in the player queue. Lean cross-platform shape - gateways
1084
+ * that have richer per-track data still surface what fields they have.
1085
+ * `uri` is required because every queued item must be addressable for
1086
+ * `skipToIndex`. `persistent_id` is the platform-stable id when
1087
+ * available; webapps treat it as opaque.
1088
+ */
1089
+ export type QueueItem = {
1090
+ uri: string;
1091
+ title: string | null;
1092
+ artist: string | null;
1093
+ artistUri: string | null;
1094
+ album: string | null;
1095
+ albumUri: string | null;
1096
+ artworkId: string | null;
1097
+ durationMs: number | null;
1098
+ persistentId: string | null;
1099
+ queued: boolean;
1100
+ };
1101
+ /**
1102
+ * Where in the queue a `queue({ uri })` should land. `Append` (default)
1103
+ * goes at the end; `Next` is play-next; `Index` is an explicit position.
1104
+ */
1105
+ export type QueuePosition = {
1106
+ type: 'append';
1107
+ } | {
1108
+ type: 'next';
1109
+ } | {
1110
+ type: 'index';
1111
+ data: number;
1112
+ };
1113
+ /**
1114
+ * Resolved range the companion is about to serve. `start` and `length`
1115
+ * echo the corresponding `RangeSpec`; the bytes follow in the reply's
1116
+ * `TransferBody`.
1117
+ */
1118
+ export type RangePart = {
1119
+ start: number;
1120
+ length: number;
1121
+ };
1122
+ /**
1123
+ * Half-open byte range the daemon's range proxy asks the companion
1124
+ * to serve. Mirrors HTTP `Range: bytes=start-end` semantics: `start`
1125
+ * inclusive, `length` bytes. The proxy caps the multi-range count at
1126
+ * the daemon edge (loopback) - companions see whatever swupdate's
1127
+ * delta downloader emits. Offsets are u32 because OTA artifacts are
1128
+ * bounded at 4 GiB end-to-end (matches `OtaBegin.expected_size`).
1129
+ */
1130
+ export type RangeSpec = {
1131
+ start: number;
1132
+ length: number;
1133
+ };
1134
+ /**
1135
+ * Page of recommendation results. Gateway decides how seed + kind
1136
+ * interact (Spotify uses radio-style seeding, Apple Music uses curated
1137
+ * rails) - the daemon doesn't prescribe.
1138
+ */
1139
+ export type RecommendationsResult = {
1140
+ items: Array<LibraryItem>;
1141
+ total: number | null;
1142
+ hasMore: boolean;
1143
+ };
1144
+ export type RedirectPolicy = 'follow' | 'manual' | 'error';
1145
+ /**
1146
+ * Cellular registration state - populated from iAP2 `CommunicationsUpdate`
1147
+ * or the companion's equivalent.
1148
+ */
1149
+ export type RegistrationStatus = 'unknown' | 'notRegistered' | 'searching' | 'denied' | 'registeredHome' | 'registeredRoaming' | 'emergencyCallsOnly';
1150
+ /**
1151
+ * `repeat` is a typed enum (Off/All/One) shared across the player
1152
+ * surface and the iAP2 NowPlaying CSM / MediaSession backends, which
1153
+ * all expose three repeat states.
1154
+ */
1155
+ export type RepeatMode = 'off' | 'all' | 'one';
1156
+ /**
1157
+ * Page of search results. `kinds` is the constrained kinds the search
1158
+ * honored (echoed back so webapps can detect ignored constraints); items
1159
+ * are ranked best-first.
1160
+ */
1161
+ export type SearchResult = {
1162
+ items: Array<LibraryItem>;
1163
+ kinds: Array<ItemKind>;
1164
+ total: number | null;
1165
+ hasMore: boolean;
1166
+ };
1167
+ /**
1168
+ * One podcast show (parent of `PodcastEpisode`). `episode_count` is
1169
+ * best-effort.
1170
+ */
1171
+ export type Show = {
1172
+ uri: string;
1173
+ name: string;
1174
+ publisher: string | null;
1175
+ episodeCount: number | null;
1176
+ artworkId: string | null;
1177
+ };
1178
+ /**
1179
+ * Three-state shuffle. iAP2 and Apple Music distinguish track-level
1180
+ * from album-level shuffle; companion gateways without that distinction
1181
+ * project to `Songs` when on. Webapps that just need an on/off signal
1182
+ * read `shuffle_on` (None when the underlying mode is unknown).
1183
+ */
1184
+ export type ShuffleMode = 'off' | 'songs' | 'albums';
1185
+ /**
1186
+ * Algorithmic / radio station. `seed` is the URI the station was seeded
1187
+ * from when known (artist, track, etc.).
1188
+ */
1189
+ export type Station = {
1190
+ uri: string;
1191
+ name: string;
1192
+ seed: string | null;
1193
+ artworkId: string | null;
1194
+ };
1195
+ /**
1196
+ * First event of an open stream. Carries the response status, headers,
1197
+ * and (when known) total payload size so the consumer can preallocate
1198
+ * or display progress. Subsequent `StreamChunk` and `StreamEnd` events
1199
+ * for the same `stream_id` follow.
1200
+ */
1201
+ export type StreamBegin = {
1202
+ streamId: string;
1203
+ status: number;
1204
+ headers: Array<HttpHeader>;
1205
+ totalSize: number | null;
1206
+ };
1207
+ /**
1208
+ * One body chunk. Chunks arrive in order; `offset` is the byte
1209
+ * position of `bytes[0]` within the full body.
1210
+ */
1211
+ export type StreamChunk = {
1212
+ streamId: string;
1213
+ offset: number;
1214
+ bytes: Uint8Array;
1215
+ };
1216
+ /**
1217
+ * Terminates a stream. After `End` no further chunks for `stream_id`
1218
+ * are valid and the daemon clears its routing entry.
1219
+ */
1220
+ export type StreamEnd = {
1221
+ streamId: string;
1222
+ };
1223
+ /**
1224
+ * Stream failed mid-flight (or before the first byte). Terminal - the
1225
+ * daemon clears its routing entry. The `error` shape is shared with
1226
+ * `fetch` since the failure modes are identical.
1227
+ */
1228
+ export type StreamError = {
1229
+ streamId: string;
1230
+ error: NetError;
1231
+ };
1232
+ export type StringField = {
1233
+ key: string;
1234
+ label: string;
1235
+ pattern: string | null;
1236
+ minLength: number | null;
1237
+ maxLength: number | null;
1238
+ default: string | null;
1239
+ };
1240
+ /**
1241
+ * Bool feature flags the daemon exposes to webapps. Each is true when the
1242
+ * surface has both a backing implementation and (where applicable) a
1243
+ * connected companion claiming to provide it. False = the surface will
1244
+ * respond `Unsupported` or `Unimplemented` to verbs.
1245
+ */
1246
+ export type SurfaceAvailability = {
1247
+ geo: boolean;
1248
+ notifications: boolean;
1249
+ netFetch: boolean;
1250
+ netWs: boolean;
1251
+ audioTts: boolean;
1252
+ lyrics: boolean;
1253
+ };
1254
+ /**
1255
+ * Wall clock + locale snapshot. `wall_clock_unix_s` is the gateway's
1256
+ * (or iAP2 device's) claimed "now" in unix-epoch seconds - webapps
1257
+ * reading time should use the device clock if any but use this as the
1258
+ * trust anchor on first arrival.
1259
+ *
1260
+ * Two zone-identification paths coexist: companion gateways send
1261
+ * `tz_iana` (an IANA zone identifier like `America/Denver`) while iAP2
1262
+ * `DeviceTimeUpdate` only exposes numeric `utc_offset_minutes` plus a
1263
+ * separate `dst_offset_minutes`. Webapps prefer `tz_iana` when present
1264
+ * and fall back to the offset pair.
1265
+ */
1266
+ export type TimeInfo = {
1267
+ tzIana: string | null;
1268
+ locale: string | null;
1269
+ wallClockUnixS: number | null;
1270
+ utcOffsetMinutes: number | null;
1271
+ dstOffsetMinutes: number | null;
1272
+ };
1273
+ /**
1274
+ * A track with resolved album/artist metadata, used by library search
1275
+ * and browse results. For live now-playing state see `MediaItem`.
1276
+ */
1277
+ export type Track = {
1278
+ id: string;
1279
+ name: string;
1280
+ album: Album;
1281
+ /**
1282
+ * Primary credited artist.
1283
+ */
1284
+ artist: Artist;
1285
+ /**
1286
+ * All credited artists, in order.
1287
+ */
1288
+ artists: Array<Artist>;
1289
+ duration_ms: number;
1290
+ /**
1291
+ * Opaque artwork asset id; pass to `asset.get` for the bytes.
1292
+ */
1293
+ image_id: string;
1294
+ /**
1295
+ * Whether the track is saved in the user's library.
1296
+ */
1297
+ saved: boolean;
1298
+ };
1299
+ export type TtlRetention = {
1300
+ seconds: number;
1301
+ };
1302
+ export type TunnelClosed = {
1303
+ tunnelId: string;
1304
+ reason: string | null;
1305
+ };
1306
+ export type TunnelData = {
1307
+ tunnelId: string;
1308
+ bytes: Uint8Array;
1309
+ };
1310
+ export type TunnelError = {
1311
+ type: 'connectFailed';
1312
+ data: {
1313
+ reason: string;
1314
+ };
1315
+ } | {
1316
+ type: 'permissionDenied';
1317
+ } | {
1318
+ type: 'unavailable';
1319
+ };
1320
+ /**
1321
+ * One TTS voice the companion's audio backend can speak as. `id` is
1322
+ * platform-opaque (Apple/Android voice id); `locale` is BCP-47.
1323
+ */
1324
+ export type VoiceDescriptor = {
1325
+ id: string;
1326
+ name: string;
1327
+ locale: string;
1328
+ };
1329
+ /**
1330
+ * Why dispatch declined to act on a `VoiceDispatch`. The companion
1331
+ * surfaces these to the user (toast / UI hint); the daemon does not
1332
+ * otherwise retain state about the failure.
1333
+ */
1334
+ export type VoiceDispatchErrorCode = 'webappNotInstalled' | 'webappNotActive' | 'webappRefused' | 'notDispatchable' | 'playbackFailed' | 'internal';
1335
+ /**
1336
+ * Where the daemon actually routed a successful dispatch. Carried back
1337
+ * to the companion so it can render the right confirmation UI.
1338
+ */
1339
+ export type VoiceDispatchTarget = 'stockPlayback' | 'activeWebapp' | 'webappSwitch';
1340
+ /**
1341
+ * Domain errors emitted by any webapp surface (gateway- or client-side).
1342
+ * Single catalog: both protocols speak the same variant set.
1343
+ */
1344
+ export type WebappError = {
1345
+ type: 'webappNotFound';
1346
+ data: {
1347
+ id: string;
1348
+ };
1349
+ } | {
1350
+ type: 'cannotUninstallBuiltin';
1351
+ data: {
1352
+ id: string;
1353
+ };
1354
+ } | {
1355
+ type: 'idReserved';
1356
+ data: {
1357
+ id: string;
1358
+ };
1359
+ } | {
1360
+ type: 'extractedTooLarge';
1361
+ data: {
1362
+ max_bytes: number;
1363
+ };
1364
+ } | {
1365
+ type: 'zipMalformed';
1366
+ data: {
1367
+ reason: string;
1368
+ };
1369
+ } | {
1370
+ type: 'missingIndexHtml';
1371
+ } | {
1372
+ type: 'invalidManifest';
1373
+ data: {
1374
+ reason: string;
1375
+ };
1376
+ } | {
1377
+ type: 'iconNotAvailable';
1378
+ data: {
1379
+ id: string;
1380
+ };
1381
+ } | {
1382
+ type: 'unknownConfigKey';
1383
+ data: {
1384
+ key: string;
1385
+ };
1386
+ } | {
1387
+ type: 'invalidConfigValue';
1388
+ data: {
1389
+ key: string;
1390
+ reason: string;
1391
+ };
1392
+ } | {
1393
+ type: 'internal';
1394
+ data: {
1395
+ reason: string;
1396
+ };
1397
+ };
1398
+ export type WebappInfo = {
1399
+ id: string;
1400
+ name: string;
1401
+ source: WebappSource;
1402
+ role: WebappRole;
1403
+ version: string;
1404
+ description: string | null;
1405
+ iconAvailable: boolean;
1406
+ iconMime: string | null;
1407
+ /**
1408
+ * icon bytes, inlined on the gateway list so the companion never round-trips
1409
+ * a separate fetch per app. omitted on the on-device client list.
1410
+ */
1411
+ icon?: Uint8Array | null;
1412
+ config: Array<ConfigField>;
1413
+ permissions: Array<string>;
1414
+ /**
1415
+ * Plain-English description of the voice intents the webapp wants
1416
+ * WEBAPP_INTENT routing for. Companion-side NLU folds this into the
1417
+ * "currently active extensions" section of the system prompt at
1418
+ * inference, which is what makes WEBAPP_INTENT emission context-aware.
1419
+ * `None` opts the webapp out of voice integration.
1420
+ */
1421
+ voiceGrammar: string | null;
1422
+ /**
1423
+ * Declared art render sizes; the companion warms exactly these. `None`
1424
+ * means the canonical `{248, 96}` default applies.
1425
+ */
1426
+ art: ArtProfile | null;
1427
+ };
1428
+ /**
1429
+ * On-disk `manifest.json` shape. Read from the bundle at install time
1430
+ * and validated; the resulting metadata projects to `WebappInfo` for
1431
+ * the wire.
1432
+ */
1433
+ export type WebappManifest = {
1434
+ id: string;
1435
+ name: string;
1436
+ version: string;
1437
+ description: string | null;
1438
+ icon: string | null;
1439
+ role: WebappRole;
1440
+ config: Array<ConfigField>;
1441
+ permissions: Array<string>;
1442
+ /**
1443
+ * Optional plain-English description of the voice commands this
1444
+ * webapp wants WEBAPP_INTENT routing for. The companion's NLU folds
1445
+ * the grammars of all installed-and-active webapps into the system
1446
+ * prompt at inference. Webapps that don't declare a grammar opt out
1447
+ * of voice integration.
1448
+ */
1449
+ voiceGrammar: string | null;
1450
+ /**
1451
+ * Declared art render sizes. Omitted falls back to `{248, 96}`.
1452
+ */
1453
+ art?: ArtProfile | null;
1454
+ };
1455
+ /**
1456
+ * A webapp's launcher visibility. `Standard` shows up in user-facing
1457
+ * listings (the hub grid, etc); `Launcher` is itself a launcher and is
1458
+ * hidden from those listings. The daemon filters `Launcher` bundles
1459
+ * out of `client.webapp.list`; the gateway list keeps everything.
1460
+ */
1461
+ export type WebappRole = 'standard' | 'launcher';
1462
+ export type WebappSource = 'builtin' | 'installed';
1463
+ export type WsError = {
1464
+ type: 'connectFailed';
1465
+ data: {
1466
+ reason: string;
1467
+ };
1468
+ } | {
1469
+ type: 'frameTooLarge';
1470
+ } | {
1471
+ type: 'gatewayDisconnected';
1472
+ } | {
1473
+ type: 'protocolError';
1474
+ data: {
1475
+ reason: string;
1476
+ };
1477
+ };
1478
+ export type WsFrame = {
1479
+ type: 'text';
1480
+ data: string;
1481
+ } | {
1482
+ type: 'binary';
1483
+ data: Uint8Array;
1484
+ };
1485
+ //# sourceMappingURL=shared.d.ts.map