@condorinst/mobile-bridge 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +133 -34
- package/CHANGELOG.md +175 -0
- package/README.md +30 -481
- package/contract/bridge-contract.json +32 -21
- package/dist/cli.cjs +126 -29
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +126 -29
- package/dist/cli.js.map +1 -1
- package/dist/dev-harness.global.js +388 -55
- package/dist/dev.cjs +389 -55
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.d.cts +239 -9
- package/dist/dev.d.ts +239 -9
- package/dist/dev.js +388 -55
- package/dist/dev.js.map +1 -1
- package/dist/index.cjs +30 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -26
- package/dist/index.d.ts +154 -26
- package/dist/index.js +29 -8
- package/dist/index.js.map +1 -1
- package/docs/agent-guide.md +9 -265
- package/docs/bridge-contract.md +313 -26
- package/docs/client-guide.md +34 -400
- package/docs/device-testing.md +234 -0
- package/docs/pilot-onboarding.md +41 -115
- package/docs/shell-integration.md +292 -0
- package/docs/troubleshooting.md +19 -11
- package/examples/minimal-home/README.md +2 -2
- package/examples/minimal-home/package.json +1 -1
- package/examples/minimal-home/src/main.ts +23 -3
- package/llms-full.txt +764 -865
- package/llms.txt +13 -15
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -196,6 +196,10 @@ interface BridgeOutgoingEvent {
|
|
|
196
196
|
* There is no hard semver validation — it is **always** `true`, so it carries
|
|
197
197
|
* no information. Never branch on it.
|
|
198
198
|
*/
|
|
199
|
+
/**
|
|
200
|
+
* Ack of the legacy {@link Bridge} (`createBridge`). With `BridgeClient`, the
|
|
201
|
+
* ack is {@link BridgeClientHandshakeAck}, whose `nativeVersion` may be `null`.
|
|
202
|
+
*/
|
|
199
203
|
interface HandshakeAck {
|
|
200
204
|
nativeVersion: string;
|
|
201
205
|
accepted: boolean;
|
|
@@ -332,16 +336,35 @@ interface DeviceUserConfigItem {
|
|
|
332
336
|
* — but only when that `serialNo` appears once in `devicesConfig`. A device may
|
|
333
337
|
* have no telemetry yet: render it with empty readings, never drop it.
|
|
334
338
|
*
|
|
335
|
-
* **
|
|
336
|
-
*
|
|
339
|
+
* **Below `nativeVersion` 0.18.0 it is not guaranteed to answer**: a request
|
|
340
|
+
* sent while the app's background service starts is lost, and a failed lookup
|
|
341
|
+
* gets no reply. From 0.18.0 the app re-sends the lost request itself and
|
|
342
|
+
* answers a failure with empty arrays and `error`. Either way, use
|
|
343
|
+
* `BridgeClient.requestUserDevices()` instead of `send('userDevice')`; see
|
|
344
|
+
* `docs/troubleshooting.md`.
|
|
337
345
|
*
|
|
338
346
|
* The app enforces no device cap. A commercial rule currently limits a user to
|
|
339
347
|
* two linked devices, but that can change with no signal in this contract — read
|
|
340
348
|
* the length of the array rather than hard-coding it.
|
|
341
349
|
*/
|
|
350
|
+
/**
|
|
351
|
+
* Why the device list could not be loaded (`UserDevicePayload.error`, from
|
|
352
|
+
* `nativeVersion` 0.18.0):
|
|
353
|
+
* - `NO_DEVICES` — the server says the account has no linked device;
|
|
354
|
+
* - `NETWORK` — no answer from the server (offline, timeout) or a 5xx;
|
|
355
|
+
* - `SESSION` — no session, or the server ended it;
|
|
356
|
+
* - `UNKNOWN` — anything else. Treat an unrecognised value like `UNKNOWN`.
|
|
357
|
+
*/
|
|
358
|
+
type UserDeviceErrorCode = 'NO_DEVICES' | 'NETWORK' | 'SESSION' | 'UNKNOWN' | (string & {});
|
|
342
359
|
interface UserDevicePayload {
|
|
343
360
|
/** Epoch milliseconds of the snapshot. */
|
|
344
361
|
lastUpdate: number;
|
|
362
|
+
/**
|
|
363
|
+
* Present only when the list could not be loaded; the three arrays are then
|
|
364
|
+
* empty. From `nativeVersion` 0.18.0 — older apps send no reply at all in
|
|
365
|
+
* that case. `BridgeClient.requestUserDevices()` turns it into a status.
|
|
366
|
+
*/
|
|
367
|
+
error?: UserDeviceErrorCode;
|
|
345
368
|
devicesConfig: DeviceConfigItem[];
|
|
346
369
|
devicesTelemetry: DeviceTelemetryItem[];
|
|
347
370
|
devicesUserConfig: DeviceUserConfigItem[];
|
|
@@ -349,6 +372,8 @@ interface UserDevicePayload {
|
|
|
349
372
|
/**
|
|
350
373
|
* Raw light-sensor channels, under the names the backend uses in
|
|
351
374
|
* `user_cmd=log` — the same vocabulary for history and for real time.
|
|
375
|
+
* Relative values with no physical unit (as are the `wavelength*` channels);
|
|
376
|
+
* only `photopic` / `melanopic` (and `lux_*` in the history) are lux.
|
|
352
377
|
*
|
|
353
378
|
* Present from **nativeVersion 0.16.0**. A bundle compiled against this SDK can
|
|
354
379
|
* run on an older native app that does not emit them: check `nativeVersion`
|
|
@@ -387,6 +412,7 @@ type LightSensorsRawChannels = {
|
|
|
387
412
|
* change between hardware generations.
|
|
388
413
|
*/
|
|
389
414
|
type LightSensorsActlumus1Data = LightSensorsRawChannels & {
|
|
415
|
+
/** When the phone received the reading: epoch ms (UTC), phone clock. */
|
|
390
416
|
time: number;
|
|
391
417
|
wavelength390: number;
|
|
392
418
|
wavelength415: number;
|
|
@@ -419,6 +445,7 @@ type LightSensorsActlumus1Data = LightSensorsRawChannels & {
|
|
|
419
445
|
* Y in the green, X in the red.
|
|
420
446
|
*/
|
|
421
447
|
type LightSensorsActlumus2Data = LightSensorsRawChannels & {
|
|
448
|
+
/** When the phone received the reading: epoch ms (UTC), phone clock. */
|
|
422
449
|
time: number;
|
|
423
450
|
wavelength395: number;
|
|
424
451
|
wavelength426: number;
|
|
@@ -503,9 +530,12 @@ interface DeviceConnectionPayload {
|
|
|
503
530
|
isConnect: boolean;
|
|
504
531
|
}
|
|
505
532
|
/**
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
533
|
+
* One step of a device sync (BLE session). Mirrors `DevicesNotification.toMap()`.
|
|
534
|
+
* Branch on `messageCode` (`pipeline_done_notification` = the sync succeeded,
|
|
535
|
+
* `*_error` = a stage failed); `message` is the app's English log line, not
|
|
536
|
+
* localized and not stable. `isConnect` is `!isError` on sync steps — read
|
|
537
|
+
* the connection from `deviceConnection`. See `docs/bridge-contract.md`,
|
|
538
|
+
* "Sync progress".
|
|
509
539
|
*/
|
|
510
540
|
interface DeviceNotificationPayload {
|
|
511
541
|
serialNo: number;
|
|
@@ -536,8 +566,8 @@ interface SensorLogQueryPayload {
|
|
|
536
566
|
/** Echoed back in the reply — correlates concurrent requests. */
|
|
537
567
|
requestId: string;
|
|
538
568
|
serialNo: number;
|
|
539
|
-
/**
|
|
540
|
-
bodyPart?:
|
|
569
|
+
/** Omit for no filter. */
|
|
570
|
+
bodyPart?: 'wrist' | 'necklace';
|
|
541
571
|
/** 1..1000 (backend default is 10). Outside the range: `INVALID_LIMIT`. */
|
|
542
572
|
limit?: number;
|
|
543
573
|
startingAfter?: string;
|
|
@@ -562,6 +592,11 @@ interface SensorLogItem {
|
|
|
562
592
|
/** `'YYYY-MM-DD HH:MM:SS'` in **device-local time**, not UTC. */
|
|
563
593
|
log_time: string;
|
|
564
594
|
log_type: number;
|
|
595
|
+
/**
|
|
596
|
+
* Where the device was set to be worn (its `deviceBodyPart`) when this log was
|
|
597
|
+
* uploaded — a setting, not a detection. Whether it was actually worn is
|
|
598
|
+
* `off_wrist`.
|
|
599
|
+
*/
|
|
565
600
|
body_part: 'wrist' | 'necklace' | null;
|
|
566
601
|
/** Degrees Celsius, already converted by the backend. */
|
|
567
602
|
temperature: number | null;
|
|
@@ -594,6 +629,11 @@ interface SensorLogItem {
|
|
|
594
629
|
act_data_pim?: number | null;
|
|
595
630
|
act_data_tat?: number | null;
|
|
596
631
|
act_data_zcm?: number | null;
|
|
632
|
+
/**
|
|
633
|
+
* The device's raw wear flag, and **the name misleads**: `1` = worn, `0` = not
|
|
634
|
+
* worn (on its dock). Treat any value other than `1` as not worn, and `null`
|
|
635
|
+
* as unknown.
|
|
636
|
+
*/
|
|
597
637
|
off_wrist?: number | null;
|
|
598
638
|
cap_sensor_1?: number | null;
|
|
599
639
|
cap_sensor_2?: number | null;
|
|
@@ -648,6 +688,12 @@ interface SystemStatusPayload {
|
|
|
648
688
|
internet: boolean;
|
|
649
689
|
permission: boolean;
|
|
650
690
|
hasDevices: boolean;
|
|
691
|
+
/**
|
|
692
|
+
* `true` when `hasDevices` is the server's answer; `false` when the app could
|
|
693
|
+
* not ask (no internet, session) — then `hasDevices: false` does **not** mean
|
|
694
|
+
* "no device". Absent below `nativeVersion` 0.18.0: treat absent as unknown.
|
|
695
|
+
*/
|
|
696
|
+
hasDevicesKnown?: boolean;
|
|
651
697
|
/**
|
|
652
698
|
* Optional permissions not yet granted, as `AppPermission` names
|
|
653
699
|
* (e.g. `["notification", "backgroundLocation"]`). Additive and
|
|
@@ -688,6 +734,54 @@ interface RequestPermissionPayload {
|
|
|
688
734
|
/** Name of the permission to request, e.g. `'bluetoothScan'`. */
|
|
689
735
|
permission: string;
|
|
690
736
|
}
|
|
737
|
+
/**
|
|
738
|
+
* Which device a command is about. `serialNo` is an **integer** and always
|
|
739
|
+
* goes; add `deviceId` (from `devicesConfig[].deviceId`) when
|
|
740
|
+
* `nativeVersion` ≥ 0.17.0 — it is the only way to address one of two devices
|
|
741
|
+
* that share a `serialNo`. A string `serialNo` is dropped by the app in silence.
|
|
742
|
+
*/
|
|
743
|
+
interface DeviceAddress {
|
|
744
|
+
serialNo: number;
|
|
745
|
+
deviceId?: number;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Arguments of every command the client may send, by name. `BridgeClient.send`
|
|
749
|
+
* checks them at compile time: a command that is not here (or `dfu`, which the
|
|
750
|
+
* shell denies) has no typed arguments and is refused at run time.
|
|
751
|
+
*/
|
|
752
|
+
interface CommandArgs {
|
|
753
|
+
ping: Record<string, never> | undefined;
|
|
754
|
+
userDevice: {
|
|
755
|
+
getFromServer?: boolean;
|
|
756
|
+
} | undefined;
|
|
757
|
+
realTime: DeviceAddress & {
|
|
758
|
+
value: boolean;
|
|
759
|
+
};
|
|
760
|
+
deviceConnection: DeviceAddress & {
|
|
761
|
+
type: 'add' | 'remove';
|
|
762
|
+
};
|
|
763
|
+
sensorLog: SensorLogQueryPayload;
|
|
764
|
+
openSettings: Record<string, never> | undefined;
|
|
765
|
+
requestPermission: RequestPermissionPayload;
|
|
766
|
+
setStatusBarColor: {
|
|
767
|
+
color: string;
|
|
768
|
+
};
|
|
769
|
+
navigationState: NavigationStatePayload;
|
|
770
|
+
language: Record<string, never> | undefined;
|
|
771
|
+
jsError: {
|
|
772
|
+
message: string;
|
|
773
|
+
stack?: string;
|
|
774
|
+
source?: string;
|
|
775
|
+
lineno?: number;
|
|
776
|
+
colno?: number;
|
|
777
|
+
};
|
|
778
|
+
syncTime: DeviceAddress;
|
|
779
|
+
deleteMemory: DeviceAddress;
|
|
780
|
+
logout: Record<string, never> | undefined;
|
|
781
|
+
}
|
|
782
|
+
type CommandName = keyof CommandArgs;
|
|
783
|
+
/** The rest-parameters of `send(command, …)`: none, optional or required. */
|
|
784
|
+
type CommandArgsTuple<C extends CommandName> = undefined extends CommandArgs[C] ? [args?: Exclude<CommandArgs[C], undefined>] : [args: CommandArgs[C]];
|
|
691
785
|
/**
|
|
692
786
|
* Payload of the `navigationBack` event (native → client). Empty — the request
|
|
693
787
|
* carries no data; react by going back exactly one internal page.
|
|
@@ -906,6 +1000,16 @@ declare const Bridge: CondorBridge;
|
|
|
906
1000
|
* native layer.
|
|
907
1001
|
*/
|
|
908
1002
|
|
|
1003
|
+
/**
|
|
1004
|
+
* A unique `requestId` for `sensorLog`. Built on `crypto.getRandomValues`,
|
|
1005
|
+
* which works in any context — unlike `crypto.randomUUID`, which exists only in
|
|
1006
|
+
* a secure context, and the app's sandboxed iframe is probably not one.
|
|
1007
|
+
*/
|
|
1008
|
+
declare function createRequestId(): string;
|
|
1009
|
+
/** A `sensorLog` query for `fetchSensorLog`: `requestId` is filled in if absent. */
|
|
1010
|
+
type SensorLogFetchQuery = Omit<SensorLogQueryPayload, 'requestId'> & {
|
|
1011
|
+
requestId?: string;
|
|
1012
|
+
};
|
|
909
1013
|
interface BridgeClientHandshakeAck {
|
|
910
1014
|
shellVersion: string;
|
|
911
1015
|
/**
|
|
@@ -916,7 +1020,7 @@ interface BridgeClientHandshakeAck {
|
|
|
916
1020
|
* version negotiation, and a sandboxed client reaches the native side by no
|
|
917
1021
|
* other path. For example, the raw light channels (`light_sensor_*` in
|
|
918
1022
|
* `deviceLightSensors`) exist from `0.16.0`. Always keep a working branch
|
|
919
|
-
* for `null`. The dev harness reports `0.
|
|
1023
|
+
* for `null`. The dev harness reports `0.18.0` unless a scenario says
|
|
920
1024
|
* otherwise.
|
|
921
1025
|
*/
|
|
922
1026
|
nativeVersion: string | null;
|
|
@@ -927,6 +1031,31 @@ interface BridgeClientHandshakeAck {
|
|
|
927
1031
|
*/
|
|
928
1032
|
accepted: boolean;
|
|
929
1033
|
}
|
|
1034
|
+
/** What {@link CondorBridgeClient.requestUserDevices} resolves with. */
|
|
1035
|
+
type UserDevicesResult =
|
|
1036
|
+
/** The list arrived. It may be empty. */
|
|
1037
|
+
{
|
|
1038
|
+
status: 'ok';
|
|
1039
|
+
devices: UserDevicePayload;
|
|
1040
|
+
}
|
|
1041
|
+
/** The server says the account has no linked device (nativeVersion 0.18.0+). */
|
|
1042
|
+
| {
|
|
1043
|
+
status: 'no-devices';
|
|
1044
|
+
}
|
|
1045
|
+
/** The app could not load the list: `NETWORK`, `SESSION`, `UNKNOWN` (0.18.0+). */
|
|
1046
|
+
| {
|
|
1047
|
+
status: 'error';
|
|
1048
|
+
code: string;
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* No answer before the deadline — the only failure an app below 0.18.0 can
|
|
1052
|
+
* produce. `hasDevices` is the last trustworthy `systemStatus.hasDevices`,
|
|
1053
|
+
* or `null`.
|
|
1054
|
+
*/
|
|
1055
|
+
| {
|
|
1056
|
+
status: 'timeout';
|
|
1057
|
+
hasDevices: boolean | null;
|
|
1058
|
+
};
|
|
930
1059
|
interface CondorBridgeClient {
|
|
931
1060
|
/** Version of the client-side postMessage protocol. */
|
|
932
1061
|
readonly protocolVersion: string;
|
|
@@ -950,7 +1079,7 @@ interface CondorBridgeClient {
|
|
|
950
1079
|
* Replies, when a command has one, arrive on the corresponding key — see
|
|
951
1080
|
* `docs/bridge-contract.md`.
|
|
952
1081
|
*/
|
|
953
|
-
send(command:
|
|
1082
|
+
send<C extends CommandName>(command: C, ...args: CommandArgsTuple<C>): void;
|
|
954
1083
|
/** Opens the native Settings screen. Equivalent to `send('openSettings')`. */
|
|
955
1084
|
openSettings(): void;
|
|
956
1085
|
/**
|
|
@@ -962,9 +1091,10 @@ interface CondorBridgeClient {
|
|
|
962
1091
|
* Saves you from keeping a `requestId → resolve` map by hand. To sweep a
|
|
963
1092
|
* period, chain calls using the returned `startingAfter` until `hasMore`
|
|
964
1093
|
* becomes `false`: the cursors are dependent, so pages **cannot** be fetched
|
|
965
|
-
* in parallel.
|
|
1094
|
+
* in parallel. Leave `requestId` out: one is created for you
|
|
1095
|
+
* ({@link createRequestId}).
|
|
966
1096
|
*/
|
|
967
|
-
fetchSensorLog(query:
|
|
1097
|
+
fetchSensorLog(query: SensorLogFetchQuery, opts?: {
|
|
968
1098
|
timeoutMs?: number;
|
|
969
1099
|
}): Promise<SensorLogPayload>;
|
|
970
1100
|
/**
|
|
@@ -998,19 +1128,23 @@ interface CondorBridgeClient {
|
|
|
998
1128
|
}>;
|
|
999
1129
|
/**
|
|
1000
1130
|
* Requests the user's devices and **keeps asking** until they arrive. Use
|
|
1001
|
-
* this instead of `send('userDevice')`, which may never be answered
|
|
1131
|
+
* this instead of `send('userDevice')`, which may never be answered on an app
|
|
1132
|
+
* below `nativeVersion` 0.18.0:
|
|
1002
1133
|
*
|
|
1003
1134
|
* - on a cold start the first request is lost while the background service
|
|
1004
1135
|
* comes up;
|
|
1005
1136
|
* - an account with no linked device gets no reply at all.
|
|
1006
1137
|
*
|
|
1007
1138
|
* Resends with backoff (1.5 s, 3 s, 6 s, then every 10 s) and on every
|
|
1008
|
-
* `systemStatus` while pending. Never rejects. Resolves
|
|
1009
|
-
*
|
|
1010
|
-
* `
|
|
1011
|
-
* `
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1139
|
+
* `systemStatus` while pending. Never rejects. Resolves a
|
|
1140
|
+
* {@link UserDevicesResult}:
|
|
1141
|
+
* - `ok` — render `devices`;
|
|
1142
|
+
* - `no-devices` — the server confirmed there is none (0.18.0+);
|
|
1143
|
+
* - `error` — the list could not load (`NETWORK`, `SESSION`, `UNKNOWN`;
|
|
1144
|
+
* 0.18.0+): show it with a retry;
|
|
1145
|
+
* - `timeout` — no answer in `timeoutMs` (default 30 s). On an older app this
|
|
1146
|
+
* is how both failures look; `hasDevices === false` then **suggests** an
|
|
1147
|
+
* account with no device, worded so it is true either way.
|
|
1014
1148
|
*
|
|
1015
1149
|
* `getFromServer: true` asks the app to refresh from the server; leave it
|
|
1016
1150
|
* `false` (default) at boot, where the cached list is enough.
|
|
@@ -1018,13 +1152,7 @@ interface CondorBridgeClient {
|
|
|
1018
1152
|
requestUserDevices(opts?: {
|
|
1019
1153
|
timeoutMs?: number;
|
|
1020
1154
|
getFromServer?: boolean;
|
|
1021
|
-
}): Promise<
|
|
1022
|
-
status: 'ok';
|
|
1023
|
-
devices: UserDevicePayload;
|
|
1024
|
-
} | {
|
|
1025
|
-
status: 'timeout';
|
|
1026
|
-
hasDevices: boolean | null;
|
|
1027
|
-
}>;
|
|
1155
|
+
}): Promise<UserDevicesResult>;
|
|
1028
1156
|
/**
|
|
1029
1157
|
* Handshakes with the shell. Resolves with
|
|
1030
1158
|
* `{ shellVersion, nativeVersion, accepted }` — read `nativeVersion` to learn
|
|
@@ -1318,4 +1446,4 @@ declare class BridgeShell {
|
|
|
1318
1446
|
*/
|
|
1319
1447
|
declare function versionAtLeast(version: string | null | undefined, min: string): boolean;
|
|
1320
1448
|
|
|
1321
|
-
export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, type ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConfigItem, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type DeviceSyncErrorItem, type DeviceTelemetryItem, type DeviceUserConfigItem, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, NATIVE_CHANNEL_NAME, type NavigationBackPayload, type NavigationStatePayload, type OutgoingStamp, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogItem, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, type ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload, versionAtLeast };
|
|
1449
|
+
export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, type ClientToShellMessage, type CommandArgs, type CommandArgsTuple, type CommandName, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceAddress, type DeviceConfigItem, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type DeviceSyncErrorItem, type DeviceTelemetryItem, type DeviceUserConfigItem, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, NATIVE_CHANNEL_NAME, type NavigationBackPayload, type NavigationStatePayload, type OutgoingStamp, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogFetchQuery, type SensorLogItem, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, type ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDeviceErrorCode, type UserDevicePayload, type UserDevicesResult, createRequestId, versionAtLeast };
|
package/dist/index.d.ts
CHANGED
|
@@ -196,6 +196,10 @@ interface BridgeOutgoingEvent {
|
|
|
196
196
|
* There is no hard semver validation — it is **always** `true`, so it carries
|
|
197
197
|
* no information. Never branch on it.
|
|
198
198
|
*/
|
|
199
|
+
/**
|
|
200
|
+
* Ack of the legacy {@link Bridge} (`createBridge`). With `BridgeClient`, the
|
|
201
|
+
* ack is {@link BridgeClientHandshakeAck}, whose `nativeVersion` may be `null`.
|
|
202
|
+
*/
|
|
199
203
|
interface HandshakeAck {
|
|
200
204
|
nativeVersion: string;
|
|
201
205
|
accepted: boolean;
|
|
@@ -332,16 +336,35 @@ interface DeviceUserConfigItem {
|
|
|
332
336
|
* — but only when that `serialNo` appears once in `devicesConfig`. A device may
|
|
333
337
|
* have no telemetry yet: render it with empty readings, never drop it.
|
|
334
338
|
*
|
|
335
|
-
* **
|
|
336
|
-
*
|
|
339
|
+
* **Below `nativeVersion` 0.18.0 it is not guaranteed to answer**: a request
|
|
340
|
+
* sent while the app's background service starts is lost, and a failed lookup
|
|
341
|
+
* gets no reply. From 0.18.0 the app re-sends the lost request itself and
|
|
342
|
+
* answers a failure with empty arrays and `error`. Either way, use
|
|
343
|
+
* `BridgeClient.requestUserDevices()` instead of `send('userDevice')`; see
|
|
344
|
+
* `docs/troubleshooting.md`.
|
|
337
345
|
*
|
|
338
346
|
* The app enforces no device cap. A commercial rule currently limits a user to
|
|
339
347
|
* two linked devices, but that can change with no signal in this contract — read
|
|
340
348
|
* the length of the array rather than hard-coding it.
|
|
341
349
|
*/
|
|
350
|
+
/**
|
|
351
|
+
* Why the device list could not be loaded (`UserDevicePayload.error`, from
|
|
352
|
+
* `nativeVersion` 0.18.0):
|
|
353
|
+
* - `NO_DEVICES` — the server says the account has no linked device;
|
|
354
|
+
* - `NETWORK` — no answer from the server (offline, timeout) or a 5xx;
|
|
355
|
+
* - `SESSION` — no session, or the server ended it;
|
|
356
|
+
* - `UNKNOWN` — anything else. Treat an unrecognised value like `UNKNOWN`.
|
|
357
|
+
*/
|
|
358
|
+
type UserDeviceErrorCode = 'NO_DEVICES' | 'NETWORK' | 'SESSION' | 'UNKNOWN' | (string & {});
|
|
342
359
|
interface UserDevicePayload {
|
|
343
360
|
/** Epoch milliseconds of the snapshot. */
|
|
344
361
|
lastUpdate: number;
|
|
362
|
+
/**
|
|
363
|
+
* Present only when the list could not be loaded; the three arrays are then
|
|
364
|
+
* empty. From `nativeVersion` 0.18.0 — older apps send no reply at all in
|
|
365
|
+
* that case. `BridgeClient.requestUserDevices()` turns it into a status.
|
|
366
|
+
*/
|
|
367
|
+
error?: UserDeviceErrorCode;
|
|
345
368
|
devicesConfig: DeviceConfigItem[];
|
|
346
369
|
devicesTelemetry: DeviceTelemetryItem[];
|
|
347
370
|
devicesUserConfig: DeviceUserConfigItem[];
|
|
@@ -349,6 +372,8 @@ interface UserDevicePayload {
|
|
|
349
372
|
/**
|
|
350
373
|
* Raw light-sensor channels, under the names the backend uses in
|
|
351
374
|
* `user_cmd=log` — the same vocabulary for history and for real time.
|
|
375
|
+
* Relative values with no physical unit (as are the `wavelength*` channels);
|
|
376
|
+
* only `photopic` / `melanopic` (and `lux_*` in the history) are lux.
|
|
352
377
|
*
|
|
353
378
|
* Present from **nativeVersion 0.16.0**. A bundle compiled against this SDK can
|
|
354
379
|
* run on an older native app that does not emit them: check `nativeVersion`
|
|
@@ -387,6 +412,7 @@ type LightSensorsRawChannels = {
|
|
|
387
412
|
* change between hardware generations.
|
|
388
413
|
*/
|
|
389
414
|
type LightSensorsActlumus1Data = LightSensorsRawChannels & {
|
|
415
|
+
/** When the phone received the reading: epoch ms (UTC), phone clock. */
|
|
390
416
|
time: number;
|
|
391
417
|
wavelength390: number;
|
|
392
418
|
wavelength415: number;
|
|
@@ -419,6 +445,7 @@ type LightSensorsActlumus1Data = LightSensorsRawChannels & {
|
|
|
419
445
|
* Y in the green, X in the red.
|
|
420
446
|
*/
|
|
421
447
|
type LightSensorsActlumus2Data = LightSensorsRawChannels & {
|
|
448
|
+
/** When the phone received the reading: epoch ms (UTC), phone clock. */
|
|
422
449
|
time: number;
|
|
423
450
|
wavelength395: number;
|
|
424
451
|
wavelength426: number;
|
|
@@ -503,9 +530,12 @@ interface DeviceConnectionPayload {
|
|
|
503
530
|
isConnect: boolean;
|
|
504
531
|
}
|
|
505
532
|
/**
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
533
|
+
* One step of a device sync (BLE session). Mirrors `DevicesNotification.toMap()`.
|
|
534
|
+
* Branch on `messageCode` (`pipeline_done_notification` = the sync succeeded,
|
|
535
|
+
* `*_error` = a stage failed); `message` is the app's English log line, not
|
|
536
|
+
* localized and not stable. `isConnect` is `!isError` on sync steps — read
|
|
537
|
+
* the connection from `deviceConnection`. See `docs/bridge-contract.md`,
|
|
538
|
+
* "Sync progress".
|
|
509
539
|
*/
|
|
510
540
|
interface DeviceNotificationPayload {
|
|
511
541
|
serialNo: number;
|
|
@@ -536,8 +566,8 @@ interface SensorLogQueryPayload {
|
|
|
536
566
|
/** Echoed back in the reply — correlates concurrent requests. */
|
|
537
567
|
requestId: string;
|
|
538
568
|
serialNo: number;
|
|
539
|
-
/**
|
|
540
|
-
bodyPart?:
|
|
569
|
+
/** Omit for no filter. */
|
|
570
|
+
bodyPart?: 'wrist' | 'necklace';
|
|
541
571
|
/** 1..1000 (backend default is 10). Outside the range: `INVALID_LIMIT`. */
|
|
542
572
|
limit?: number;
|
|
543
573
|
startingAfter?: string;
|
|
@@ -562,6 +592,11 @@ interface SensorLogItem {
|
|
|
562
592
|
/** `'YYYY-MM-DD HH:MM:SS'` in **device-local time**, not UTC. */
|
|
563
593
|
log_time: string;
|
|
564
594
|
log_type: number;
|
|
595
|
+
/**
|
|
596
|
+
* Where the device was set to be worn (its `deviceBodyPart`) when this log was
|
|
597
|
+
* uploaded — a setting, not a detection. Whether it was actually worn is
|
|
598
|
+
* `off_wrist`.
|
|
599
|
+
*/
|
|
565
600
|
body_part: 'wrist' | 'necklace' | null;
|
|
566
601
|
/** Degrees Celsius, already converted by the backend. */
|
|
567
602
|
temperature: number | null;
|
|
@@ -594,6 +629,11 @@ interface SensorLogItem {
|
|
|
594
629
|
act_data_pim?: number | null;
|
|
595
630
|
act_data_tat?: number | null;
|
|
596
631
|
act_data_zcm?: number | null;
|
|
632
|
+
/**
|
|
633
|
+
* The device's raw wear flag, and **the name misleads**: `1` = worn, `0` = not
|
|
634
|
+
* worn (on its dock). Treat any value other than `1` as not worn, and `null`
|
|
635
|
+
* as unknown.
|
|
636
|
+
*/
|
|
597
637
|
off_wrist?: number | null;
|
|
598
638
|
cap_sensor_1?: number | null;
|
|
599
639
|
cap_sensor_2?: number | null;
|
|
@@ -648,6 +688,12 @@ interface SystemStatusPayload {
|
|
|
648
688
|
internet: boolean;
|
|
649
689
|
permission: boolean;
|
|
650
690
|
hasDevices: boolean;
|
|
691
|
+
/**
|
|
692
|
+
* `true` when `hasDevices` is the server's answer; `false` when the app could
|
|
693
|
+
* not ask (no internet, session) — then `hasDevices: false` does **not** mean
|
|
694
|
+
* "no device". Absent below `nativeVersion` 0.18.0: treat absent as unknown.
|
|
695
|
+
*/
|
|
696
|
+
hasDevicesKnown?: boolean;
|
|
651
697
|
/**
|
|
652
698
|
* Optional permissions not yet granted, as `AppPermission` names
|
|
653
699
|
* (e.g. `["notification", "backgroundLocation"]`). Additive and
|
|
@@ -688,6 +734,54 @@ interface RequestPermissionPayload {
|
|
|
688
734
|
/** Name of the permission to request, e.g. `'bluetoothScan'`. */
|
|
689
735
|
permission: string;
|
|
690
736
|
}
|
|
737
|
+
/**
|
|
738
|
+
* Which device a command is about. `serialNo` is an **integer** and always
|
|
739
|
+
* goes; add `deviceId` (from `devicesConfig[].deviceId`) when
|
|
740
|
+
* `nativeVersion` ≥ 0.17.0 — it is the only way to address one of two devices
|
|
741
|
+
* that share a `serialNo`. A string `serialNo` is dropped by the app in silence.
|
|
742
|
+
*/
|
|
743
|
+
interface DeviceAddress {
|
|
744
|
+
serialNo: number;
|
|
745
|
+
deviceId?: number;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Arguments of every command the client may send, by name. `BridgeClient.send`
|
|
749
|
+
* checks them at compile time: a command that is not here (or `dfu`, which the
|
|
750
|
+
* shell denies) has no typed arguments and is refused at run time.
|
|
751
|
+
*/
|
|
752
|
+
interface CommandArgs {
|
|
753
|
+
ping: Record<string, never> | undefined;
|
|
754
|
+
userDevice: {
|
|
755
|
+
getFromServer?: boolean;
|
|
756
|
+
} | undefined;
|
|
757
|
+
realTime: DeviceAddress & {
|
|
758
|
+
value: boolean;
|
|
759
|
+
};
|
|
760
|
+
deviceConnection: DeviceAddress & {
|
|
761
|
+
type: 'add' | 'remove';
|
|
762
|
+
};
|
|
763
|
+
sensorLog: SensorLogQueryPayload;
|
|
764
|
+
openSettings: Record<string, never> | undefined;
|
|
765
|
+
requestPermission: RequestPermissionPayload;
|
|
766
|
+
setStatusBarColor: {
|
|
767
|
+
color: string;
|
|
768
|
+
};
|
|
769
|
+
navigationState: NavigationStatePayload;
|
|
770
|
+
language: Record<string, never> | undefined;
|
|
771
|
+
jsError: {
|
|
772
|
+
message: string;
|
|
773
|
+
stack?: string;
|
|
774
|
+
source?: string;
|
|
775
|
+
lineno?: number;
|
|
776
|
+
colno?: number;
|
|
777
|
+
};
|
|
778
|
+
syncTime: DeviceAddress;
|
|
779
|
+
deleteMemory: DeviceAddress;
|
|
780
|
+
logout: Record<string, never> | undefined;
|
|
781
|
+
}
|
|
782
|
+
type CommandName = keyof CommandArgs;
|
|
783
|
+
/** The rest-parameters of `send(command, …)`: none, optional or required. */
|
|
784
|
+
type CommandArgsTuple<C extends CommandName> = undefined extends CommandArgs[C] ? [args?: Exclude<CommandArgs[C], undefined>] : [args: CommandArgs[C]];
|
|
691
785
|
/**
|
|
692
786
|
* Payload of the `navigationBack` event (native → client). Empty — the request
|
|
693
787
|
* carries no data; react by going back exactly one internal page.
|
|
@@ -906,6 +1000,16 @@ declare const Bridge: CondorBridge;
|
|
|
906
1000
|
* native layer.
|
|
907
1001
|
*/
|
|
908
1002
|
|
|
1003
|
+
/**
|
|
1004
|
+
* A unique `requestId` for `sensorLog`. Built on `crypto.getRandomValues`,
|
|
1005
|
+
* which works in any context — unlike `crypto.randomUUID`, which exists only in
|
|
1006
|
+
* a secure context, and the app's sandboxed iframe is probably not one.
|
|
1007
|
+
*/
|
|
1008
|
+
declare function createRequestId(): string;
|
|
1009
|
+
/** A `sensorLog` query for `fetchSensorLog`: `requestId` is filled in if absent. */
|
|
1010
|
+
type SensorLogFetchQuery = Omit<SensorLogQueryPayload, 'requestId'> & {
|
|
1011
|
+
requestId?: string;
|
|
1012
|
+
};
|
|
909
1013
|
interface BridgeClientHandshakeAck {
|
|
910
1014
|
shellVersion: string;
|
|
911
1015
|
/**
|
|
@@ -916,7 +1020,7 @@ interface BridgeClientHandshakeAck {
|
|
|
916
1020
|
* version negotiation, and a sandboxed client reaches the native side by no
|
|
917
1021
|
* other path. For example, the raw light channels (`light_sensor_*` in
|
|
918
1022
|
* `deviceLightSensors`) exist from `0.16.0`. Always keep a working branch
|
|
919
|
-
* for `null`. The dev harness reports `0.
|
|
1023
|
+
* for `null`. The dev harness reports `0.18.0` unless a scenario says
|
|
920
1024
|
* otherwise.
|
|
921
1025
|
*/
|
|
922
1026
|
nativeVersion: string | null;
|
|
@@ -927,6 +1031,31 @@ interface BridgeClientHandshakeAck {
|
|
|
927
1031
|
*/
|
|
928
1032
|
accepted: boolean;
|
|
929
1033
|
}
|
|
1034
|
+
/** What {@link CondorBridgeClient.requestUserDevices} resolves with. */
|
|
1035
|
+
type UserDevicesResult =
|
|
1036
|
+
/** The list arrived. It may be empty. */
|
|
1037
|
+
{
|
|
1038
|
+
status: 'ok';
|
|
1039
|
+
devices: UserDevicePayload;
|
|
1040
|
+
}
|
|
1041
|
+
/** The server says the account has no linked device (nativeVersion 0.18.0+). */
|
|
1042
|
+
| {
|
|
1043
|
+
status: 'no-devices';
|
|
1044
|
+
}
|
|
1045
|
+
/** The app could not load the list: `NETWORK`, `SESSION`, `UNKNOWN` (0.18.0+). */
|
|
1046
|
+
| {
|
|
1047
|
+
status: 'error';
|
|
1048
|
+
code: string;
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* No answer before the deadline — the only failure an app below 0.18.0 can
|
|
1052
|
+
* produce. `hasDevices` is the last trustworthy `systemStatus.hasDevices`,
|
|
1053
|
+
* or `null`.
|
|
1054
|
+
*/
|
|
1055
|
+
| {
|
|
1056
|
+
status: 'timeout';
|
|
1057
|
+
hasDevices: boolean | null;
|
|
1058
|
+
};
|
|
930
1059
|
interface CondorBridgeClient {
|
|
931
1060
|
/** Version of the client-side postMessage protocol. */
|
|
932
1061
|
readonly protocolVersion: string;
|
|
@@ -950,7 +1079,7 @@ interface CondorBridgeClient {
|
|
|
950
1079
|
* Replies, when a command has one, arrive on the corresponding key — see
|
|
951
1080
|
* `docs/bridge-contract.md`.
|
|
952
1081
|
*/
|
|
953
|
-
send(command:
|
|
1082
|
+
send<C extends CommandName>(command: C, ...args: CommandArgsTuple<C>): void;
|
|
954
1083
|
/** Opens the native Settings screen. Equivalent to `send('openSettings')`. */
|
|
955
1084
|
openSettings(): void;
|
|
956
1085
|
/**
|
|
@@ -962,9 +1091,10 @@ interface CondorBridgeClient {
|
|
|
962
1091
|
* Saves you from keeping a `requestId → resolve` map by hand. To sweep a
|
|
963
1092
|
* period, chain calls using the returned `startingAfter` until `hasMore`
|
|
964
1093
|
* becomes `false`: the cursors are dependent, so pages **cannot** be fetched
|
|
965
|
-
* in parallel.
|
|
1094
|
+
* in parallel. Leave `requestId` out: one is created for you
|
|
1095
|
+
* ({@link createRequestId}).
|
|
966
1096
|
*/
|
|
967
|
-
fetchSensorLog(query:
|
|
1097
|
+
fetchSensorLog(query: SensorLogFetchQuery, opts?: {
|
|
968
1098
|
timeoutMs?: number;
|
|
969
1099
|
}): Promise<SensorLogPayload>;
|
|
970
1100
|
/**
|
|
@@ -998,19 +1128,23 @@ interface CondorBridgeClient {
|
|
|
998
1128
|
}>;
|
|
999
1129
|
/**
|
|
1000
1130
|
* Requests the user's devices and **keeps asking** until they arrive. Use
|
|
1001
|
-
* this instead of `send('userDevice')`, which may never be answered
|
|
1131
|
+
* this instead of `send('userDevice')`, which may never be answered on an app
|
|
1132
|
+
* below `nativeVersion` 0.18.0:
|
|
1002
1133
|
*
|
|
1003
1134
|
* - on a cold start the first request is lost while the background service
|
|
1004
1135
|
* comes up;
|
|
1005
1136
|
* - an account with no linked device gets no reply at all.
|
|
1006
1137
|
*
|
|
1007
1138
|
* Resends with backoff (1.5 s, 3 s, 6 s, then every 10 s) and on every
|
|
1008
|
-
* `systemStatus` while pending. Never rejects. Resolves
|
|
1009
|
-
*
|
|
1010
|
-
* `
|
|
1011
|
-
* `
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1139
|
+
* `systemStatus` while pending. Never rejects. Resolves a
|
|
1140
|
+
* {@link UserDevicesResult}:
|
|
1141
|
+
* - `ok` — render `devices`;
|
|
1142
|
+
* - `no-devices` — the server confirmed there is none (0.18.0+);
|
|
1143
|
+
* - `error` — the list could not load (`NETWORK`, `SESSION`, `UNKNOWN`;
|
|
1144
|
+
* 0.18.0+): show it with a retry;
|
|
1145
|
+
* - `timeout` — no answer in `timeoutMs` (default 30 s). On an older app this
|
|
1146
|
+
* is how both failures look; `hasDevices === false` then **suggests** an
|
|
1147
|
+
* account with no device, worded so it is true either way.
|
|
1014
1148
|
*
|
|
1015
1149
|
* `getFromServer: true` asks the app to refresh from the server; leave it
|
|
1016
1150
|
* `false` (default) at boot, where the cached list is enough.
|
|
@@ -1018,13 +1152,7 @@ interface CondorBridgeClient {
|
|
|
1018
1152
|
requestUserDevices(opts?: {
|
|
1019
1153
|
timeoutMs?: number;
|
|
1020
1154
|
getFromServer?: boolean;
|
|
1021
|
-
}): Promise<
|
|
1022
|
-
status: 'ok';
|
|
1023
|
-
devices: UserDevicePayload;
|
|
1024
|
-
} | {
|
|
1025
|
-
status: 'timeout';
|
|
1026
|
-
hasDevices: boolean | null;
|
|
1027
|
-
}>;
|
|
1155
|
+
}): Promise<UserDevicesResult>;
|
|
1028
1156
|
/**
|
|
1029
1157
|
* Handshakes with the shell. Resolves with
|
|
1030
1158
|
* `{ shellVersion, nativeVersion, accepted }` — read `nativeVersion` to learn
|
|
@@ -1318,4 +1446,4 @@ declare class BridgeShell {
|
|
|
1318
1446
|
*/
|
|
1319
1447
|
declare function versionAtLeast(version: string | null | undefined, min: string): boolean;
|
|
1320
1448
|
|
|
1321
|
-
export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, type ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConfigItem, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type DeviceSyncErrorItem, type DeviceTelemetryItem, type DeviceUserConfigItem, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, NATIVE_CHANNEL_NAME, type NavigationBackPayload, type NavigationStatePayload, type OutgoingStamp, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogItem, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, type ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload, versionAtLeast };
|
|
1449
|
+
export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, type ClientToShellMessage, type CommandArgs, type CommandArgsTuple, type CommandName, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceAddress, type DeviceConfigItem, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type DeviceSyncErrorItem, type DeviceTelemetryItem, type DeviceUserConfigItem, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, NATIVE_CHANNEL_NAME, type NavigationBackPayload, type NavigationStatePayload, type OutgoingStamp, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogFetchQuery, type SensorLogItem, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, type ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDeviceErrorCode, type UserDevicePayload, type UserDevicesResult, createRequestId, versionAtLeast };
|