@mentra/cloud-protocol 3.3.0-dev.906 → 3.3.0-dev.910
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/package.json +1 -1
- package/src/__fixtures__/fleet/ack-invalid-reason-on-accepted.json +11 -0
- package/src/__fixtures__/fleet/ack-invalid-rejected-without-reason.json +10 -0
- package/src/__fixtures__/fleet/ack-invalid-unknown-error.json +3 -0
- package/src/__fixtures__/fleet/ack-valid-mixed.json +33 -0
- package/src/__fixtures__/fleet/ack-valid-not-installed.json +3 -0
- package/src/__fixtures__/fleet/ack-valid-unavailable.json +4 -0
- package/src/__fixtures__/fleet/report-invalid-battery-zero-when-unsupported.json +55 -0
- package/src/__fixtures__/fleet/report-invalid-email-identity.json +57 -0
- package/src/__fixtures__/fleet/report-invalid-empty-batch.json +4 -0
- package/src/__fixtures__/fleet/report-invalid-execution-without-package-name.json +32 -0
- package/src/__fixtures__/fleet/report-invalid-missing-record-id.json +55 -0
- package/src/__fixtures__/fleet/report-invalid-negative-elapsed.json +33 -0
- package/src/__fixtures__/fleet/report-invalid-offset-time.json +56 -0
- package/src/__fixtures__/fleet/report-invalid-photo-without-operation-id.json +30 -0
- package/src/__fixtures__/fleet/report-valid-batch-mixed.json +172 -0
- package/src/__fixtures__/fleet/report-valid-coverage-gap.json +27 -0
- package/src/__fixtures__/fleet/report-valid-miniapp-launch.json +28 -0
- package/src/__fixtures__/fleet/report-valid-photo-operation.json +25 -0
- package/src/__fixtures__/fleet/report-valid-snapshot-provisional.json +53 -0
- package/src/__fixtures__/fleet/report-valid-snapshot.json +56 -0
- package/src/__fixtures__/fleet/report-valid-usage-checkpoint.json +33 -0
- package/src/__fixtures__/fleet/report-valid-usage-packed.json +62 -0
- package/src/fleet.ts +245 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"results": [
|
|
4
|
+
{
|
|
5
|
+
"index": 0,
|
|
6
|
+
"recordId": "rec-mix-0001",
|
|
7
|
+
"status": "accepted"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"index": 1,
|
|
11
|
+
"recordId": "rec-mix-0002",
|
|
12
|
+
"status": "duplicate"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"index": 2,
|
|
16
|
+
"recordId": "rec-mix-0003",
|
|
17
|
+
"status": "retryable",
|
|
18
|
+
"retryAfterSeconds": 30
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"index": 3,
|
|
22
|
+
"recordId": "rec-mix-0004",
|
|
23
|
+
"status": "rejected",
|
|
24
|
+
"reasonCode": "content_mismatch"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"index": 4,
|
|
28
|
+
"recordId": "rec-mix-0005",
|
|
29
|
+
"status": "rejected",
|
|
30
|
+
"reasonCode": "outside_replay_window"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-bad-0007",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 0,
|
|
10
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "snapshot",
|
|
19
|
+
"deviceBinding": {
|
|
20
|
+
"kind": "serial",
|
|
21
|
+
"manufacturer": "Mentra",
|
|
22
|
+
"product": "mentra_live",
|
|
23
|
+
"serial": "ML2A-0042-7731"
|
|
24
|
+
},
|
|
25
|
+
"payload": {
|
|
26
|
+
"phone": {
|
|
27
|
+
"model": "Pixel 8",
|
|
28
|
+
"os": "android",
|
|
29
|
+
"osVersion": "15",
|
|
30
|
+
"appVersion": "3.4.0",
|
|
31
|
+
"appBuild": "3400",
|
|
32
|
+
"engineVersion": "1.12.0",
|
|
33
|
+
"sdkVersion": "0.9.2"
|
|
34
|
+
},
|
|
35
|
+
"glasses": {
|
|
36
|
+
"model": "mentra_live",
|
|
37
|
+
"firmware": {
|
|
38
|
+
"apk": "1.7.3",
|
|
39
|
+
"mtk": "2.1.0",
|
|
40
|
+
"bes": "0.8.5"
|
|
41
|
+
},
|
|
42
|
+
"connection": {
|
|
43
|
+
"state": "connected",
|
|
44
|
+
"observedAt": "2026-10-13T11:00:00Z"
|
|
45
|
+
},
|
|
46
|
+
"battery": {
|
|
47
|
+
"state": "unsupported",
|
|
48
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
49
|
+
"level": 0
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-bad-0002",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 0,
|
|
10
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "snapshot",
|
|
19
|
+
"deviceBinding": {
|
|
20
|
+
"kind": "serial",
|
|
21
|
+
"manufacturer": "Mentra",
|
|
22
|
+
"product": "mentra_live",
|
|
23
|
+
"serial": "ML2A-0042-7731"
|
|
24
|
+
},
|
|
25
|
+
"payload": {
|
|
26
|
+
"phone": {
|
|
27
|
+
"model": "Pixel 8",
|
|
28
|
+
"os": "android",
|
|
29
|
+
"osVersion": "15",
|
|
30
|
+
"appVersion": "3.4.0",
|
|
31
|
+
"appBuild": "3400",
|
|
32
|
+
"engineVersion": "1.12.0",
|
|
33
|
+
"sdkVersion": "0.9.2"
|
|
34
|
+
},
|
|
35
|
+
"glasses": {
|
|
36
|
+
"model": "mentra_live",
|
|
37
|
+
"firmware": {
|
|
38
|
+
"apk": "1.7.3",
|
|
39
|
+
"mtk": "2.1.0",
|
|
40
|
+
"bes": "0.8.5"
|
|
41
|
+
},
|
|
42
|
+
"connection": {
|
|
43
|
+
"state": "connected",
|
|
44
|
+
"observedAt": "2026-10-13T11:00:00Z"
|
|
45
|
+
},
|
|
46
|
+
"battery": {
|
|
47
|
+
"state": "measured",
|
|
48
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
49
|
+
"level": 76,
|
|
50
|
+
"charging": false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"email": "someone@example.com"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-bad-0005",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 1,
|
|
10
|
+
"observedAt": "2026-10-13T11:03:10Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "usage_checkpoint",
|
|
19
|
+
"payload": {
|
|
20
|
+
"sessions": [
|
|
21
|
+
{
|
|
22
|
+
"activity": "miniapp_execution",
|
|
23
|
+
"activitySessionId": "act-8d02",
|
|
24
|
+
"startedAt": "2026-10-13T11:02:10Z",
|
|
25
|
+
"elapsedMs": 60000,
|
|
26
|
+
"ended": false
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
7
|
+
"collectorSessionId": "cs-20261013-01",
|
|
8
|
+
"sequence": 1,
|
|
9
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
10
|
+
"capabilities": [
|
|
11
|
+
"glasses_connection",
|
|
12
|
+
"glasses_battery",
|
|
13
|
+
"glasses_versions",
|
|
14
|
+
"miniapp_execution",
|
|
15
|
+
"engine_active"
|
|
16
|
+
],
|
|
17
|
+
"kind": "snapshot",
|
|
18
|
+
"deviceBinding": {
|
|
19
|
+
"kind": "serial",
|
|
20
|
+
"manufacturer": "Mentra",
|
|
21
|
+
"product": "mentra_live",
|
|
22
|
+
"serial": "ML2A-0042-7731"
|
|
23
|
+
},
|
|
24
|
+
"payload": {
|
|
25
|
+
"phone": {
|
|
26
|
+
"model": "Pixel 8",
|
|
27
|
+
"os": "android",
|
|
28
|
+
"osVersion": "15",
|
|
29
|
+
"appVersion": "3.4.0",
|
|
30
|
+
"appBuild": "3400",
|
|
31
|
+
"engineVersion": "1.12.0",
|
|
32
|
+
"sdkVersion": "0.9.2"
|
|
33
|
+
},
|
|
34
|
+
"glasses": {
|
|
35
|
+
"model": "mentra_live",
|
|
36
|
+
"firmware": {
|
|
37
|
+
"apk": "1.7.3",
|
|
38
|
+
"mtk": "2.1.0",
|
|
39
|
+
"bes": "0.8.5"
|
|
40
|
+
},
|
|
41
|
+
"connection": {
|
|
42
|
+
"state": "connected",
|
|
43
|
+
"observedAt": "2026-10-13T11:00:00Z"
|
|
44
|
+
},
|
|
45
|
+
"battery": {
|
|
46
|
+
"state": "measured",
|
|
47
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
48
|
+
"level": 76,
|
|
49
|
+
"charging": false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-bad-0003",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 1,
|
|
10
|
+
"observedAt": "2026-10-13T11:03:10Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "usage_checkpoint",
|
|
19
|
+
"payload": {
|
|
20
|
+
"sessions": [
|
|
21
|
+
{
|
|
22
|
+
"activity": "miniapp_execution",
|
|
23
|
+
"activitySessionId": "act-8d02",
|
|
24
|
+
"packageName": "com.mentra.notes",
|
|
25
|
+
"startedAt": "2026-10-13T11:02:10Z",
|
|
26
|
+
"elapsedMs": -5,
|
|
27
|
+
"ended": false
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-bad-0001",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 0,
|
|
10
|
+
"observedAt": "2026-10-13T19:00:00+08:00",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "snapshot",
|
|
19
|
+
"deviceBinding": {
|
|
20
|
+
"kind": "serial",
|
|
21
|
+
"manufacturer": "Mentra",
|
|
22
|
+
"product": "mentra_live",
|
|
23
|
+
"serial": "ML2A-0042-7731"
|
|
24
|
+
},
|
|
25
|
+
"payload": {
|
|
26
|
+
"phone": {
|
|
27
|
+
"model": "Pixel 8",
|
|
28
|
+
"os": "android",
|
|
29
|
+
"osVersion": "15",
|
|
30
|
+
"appVersion": "3.4.0",
|
|
31
|
+
"appBuild": "3400",
|
|
32
|
+
"engineVersion": "1.12.0",
|
|
33
|
+
"sdkVersion": "0.9.2"
|
|
34
|
+
},
|
|
35
|
+
"glasses": {
|
|
36
|
+
"model": "mentra_live",
|
|
37
|
+
"firmware": {
|
|
38
|
+
"apk": "1.7.3",
|
|
39
|
+
"mtk": "2.1.0",
|
|
40
|
+
"bes": "0.8.5"
|
|
41
|
+
},
|
|
42
|
+
"connection": {
|
|
43
|
+
"state": "connected",
|
|
44
|
+
"observedAt": "2026-10-13T11:00:00Z"
|
|
45
|
+
},
|
|
46
|
+
"battery": {
|
|
47
|
+
"state": "measured",
|
|
48
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
49
|
+
"level": 76,
|
|
50
|
+
"charging": false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-bad-0006",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 3,
|
|
10
|
+
"observedAt": "2026-10-13T11:04:00Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "photo_operation",
|
|
19
|
+
"deviceBinding": {
|
|
20
|
+
"kind": "serial",
|
|
21
|
+
"manufacturer": "Mentra",
|
|
22
|
+
"product": "mentra_live",
|
|
23
|
+
"serial": "ML2A-0042-7731"
|
|
24
|
+
},
|
|
25
|
+
"payload": {
|
|
26
|
+
"stage": "captured"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-mix-0001",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 10,
|
|
10
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "snapshot",
|
|
19
|
+
"deviceBinding": {
|
|
20
|
+
"kind": "serial",
|
|
21
|
+
"manufacturer": "Mentra",
|
|
22
|
+
"product": "mentra_live",
|
|
23
|
+
"serial": "ML2A-0042-7731"
|
|
24
|
+
},
|
|
25
|
+
"payload": {
|
|
26
|
+
"phone": {
|
|
27
|
+
"model": "Pixel 8",
|
|
28
|
+
"os": "android",
|
|
29
|
+
"osVersion": "15",
|
|
30
|
+
"appVersion": "3.4.0",
|
|
31
|
+
"appBuild": "3400",
|
|
32
|
+
"engineVersion": "1.12.0",
|
|
33
|
+
"sdkVersion": "0.9.2"
|
|
34
|
+
},
|
|
35
|
+
"glasses": {
|
|
36
|
+
"model": "mentra_live",
|
|
37
|
+
"firmware": {
|
|
38
|
+
"apk": "1.7.3",
|
|
39
|
+
"mtk": "2.1.0",
|
|
40
|
+
"bes": "0.8.5"
|
|
41
|
+
},
|
|
42
|
+
"connection": {
|
|
43
|
+
"state": "connected",
|
|
44
|
+
"observedAt": "2026-10-13T11:00:00Z"
|
|
45
|
+
},
|
|
46
|
+
"battery": {
|
|
47
|
+
"state": "measured",
|
|
48
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
49
|
+
"level": 76,
|
|
50
|
+
"charging": false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"schemaVersion": 1,
|
|
57
|
+
"recordId": "rec-mix-0002",
|
|
58
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
59
|
+
"collectorSessionId": "cs-20261013-01",
|
|
60
|
+
"sequence": 11,
|
|
61
|
+
"observedAt": "2026-10-13T11:02:10Z",
|
|
62
|
+
"capabilities": [
|
|
63
|
+
"glasses_connection",
|
|
64
|
+
"glasses_battery",
|
|
65
|
+
"glasses_versions",
|
|
66
|
+
"miniapp_execution",
|
|
67
|
+
"engine_active"
|
|
68
|
+
],
|
|
69
|
+
"kind": "miniapp_launch",
|
|
70
|
+
"payload": {
|
|
71
|
+
"packageName": "com.mentra.notes",
|
|
72
|
+
"miniappName": "Mentra Notes",
|
|
73
|
+
"versionName": "2.0.1",
|
|
74
|
+
"start": "user",
|
|
75
|
+
"outcome": "started"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"schemaVersion": 1,
|
|
80
|
+
"recordId": "rec-mix-0003",
|
|
81
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
82
|
+
"collectorSessionId": "cs-20261013-01",
|
|
83
|
+
"sequence": 12,
|
|
84
|
+
"observedAt": "2026-10-13T11:03:10Z",
|
|
85
|
+
"capabilities": [
|
|
86
|
+
"glasses_connection",
|
|
87
|
+
"glasses_battery",
|
|
88
|
+
"glasses_versions",
|
|
89
|
+
"miniapp_execution",
|
|
90
|
+
"engine_active"
|
|
91
|
+
],
|
|
92
|
+
"kind": "usage_checkpoint",
|
|
93
|
+
"payload": {
|
|
94
|
+
"sessions": [
|
|
95
|
+
{
|
|
96
|
+
"activity": "glasses_connection",
|
|
97
|
+
"activitySessionId": "act-conn-01",
|
|
98
|
+
"deviceBinding": {
|
|
99
|
+
"kind": "serial",
|
|
100
|
+
"manufacturer": "Mentra",
|
|
101
|
+
"product": "mentra_live",
|
|
102
|
+
"serial": "ML2A-0042-7731"
|
|
103
|
+
},
|
|
104
|
+
"startedAt": "2026-10-13T10:59:00Z",
|
|
105
|
+
"elapsedMs": 250000,
|
|
106
|
+
"ended": false
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"activity": "miniapp_execution",
|
|
110
|
+
"activitySessionId": "act-8d02",
|
|
111
|
+
"packageName": "com.mentra.notes",
|
|
112
|
+
"startedAt": "2026-10-13T11:02:10Z",
|
|
113
|
+
"elapsedMs": 60000,
|
|
114
|
+
"ended": false
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"schemaVersion": 1,
|
|
121
|
+
"recordId": "rec-mix-0004",
|
|
122
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
123
|
+
"collectorSessionId": "cs-20261013-01",
|
|
124
|
+
"sequence": 13,
|
|
125
|
+
"observedAt": "2026-10-13T11:04:00Z",
|
|
126
|
+
"capabilities": [
|
|
127
|
+
"glasses_connection",
|
|
128
|
+
"glasses_battery",
|
|
129
|
+
"glasses_versions",
|
|
130
|
+
"miniapp_execution",
|
|
131
|
+
"engine_active"
|
|
132
|
+
],
|
|
133
|
+
"kind": "photo_operation",
|
|
134
|
+
"operationId": "op-photo-41c9",
|
|
135
|
+
"deviceBinding": {
|
|
136
|
+
"kind": "serial",
|
|
137
|
+
"manufacturer": "Mentra",
|
|
138
|
+
"product": "mentra_live",
|
|
139
|
+
"serial": "ML2A-0042-7731"
|
|
140
|
+
},
|
|
141
|
+
"payload": {
|
|
142
|
+
"stage": "requested"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"schemaVersion": 1,
|
|
147
|
+
"recordId": "rec-mix-0005",
|
|
148
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
149
|
+
"collectorSessionId": "cs-20261013-01",
|
|
150
|
+
"sequence": 14,
|
|
151
|
+
"observedAt": "2026-10-13T11:04:02Z",
|
|
152
|
+
"capabilities": [
|
|
153
|
+
"glasses_connection",
|
|
154
|
+
"glasses_battery",
|
|
155
|
+
"glasses_versions",
|
|
156
|
+
"miniapp_execution",
|
|
157
|
+
"engine_active"
|
|
158
|
+
],
|
|
159
|
+
"kind": "photo_operation",
|
|
160
|
+
"operationId": "op-photo-41c9",
|
|
161
|
+
"deviceBinding": {
|
|
162
|
+
"kind": "serial",
|
|
163
|
+
"manufacturer": "Mentra",
|
|
164
|
+
"product": "mentra_live",
|
|
165
|
+
"serial": "ML2A-0042-7731"
|
|
166
|
+
},
|
|
167
|
+
"payload": {
|
|
168
|
+
"stage": "captured"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-gap-0001",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 9,
|
|
10
|
+
"observedAt": "2026-10-13T12:00:00Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "coverage_gap",
|
|
19
|
+
"payload": {
|
|
20
|
+
"reason": "queue_dropped",
|
|
21
|
+
"from": "2026-10-13T11:20:00Z",
|
|
22
|
+
"to": "2026-10-13T11:55:00Z",
|
|
23
|
+
"droppedRecordCount": 31
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-launch-0001",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 1,
|
|
10
|
+
"observedAt": "2026-10-13T11:02:10Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "miniapp_launch",
|
|
19
|
+
"payload": {
|
|
20
|
+
"packageName": "com.acme.inventory",
|
|
21
|
+
"miniappName": "Acme Inventory",
|
|
22
|
+
"versionName": "2.0.1",
|
|
23
|
+
"start": "user",
|
|
24
|
+
"outcome": "started"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-photo-0001",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 3,
|
|
10
|
+
"observedAt": "2026-10-13T11:04:00Z",
|
|
11
|
+
"capabilities": ["glasses_connection", "photo_operations"],
|
|
12
|
+
"kind": "photo_operation",
|
|
13
|
+
"operationId": "op-photo-41c9",
|
|
14
|
+
"deviceBinding": {
|
|
15
|
+
"kind": "serial",
|
|
16
|
+
"manufacturer": "Mentra",
|
|
17
|
+
"product": "mentra_live",
|
|
18
|
+
"serial": "ML2A-0042-7731"
|
|
19
|
+
},
|
|
20
|
+
"payload": {
|
|
21
|
+
"stage": "captured"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-snap-0002",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 0,
|
|
10
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "snapshot",
|
|
19
|
+
"deviceBinding": {
|
|
20
|
+
"kind": "provisional",
|
|
21
|
+
"bindingId": "bind-5c21e0",
|
|
22
|
+
"product": "mentra_live"
|
|
23
|
+
},
|
|
24
|
+
"payload": {
|
|
25
|
+
"phone": {
|
|
26
|
+
"model": "Pixel 8",
|
|
27
|
+
"os": "android",
|
|
28
|
+
"osVersion": "15",
|
|
29
|
+
"appVersion": "3.4.0",
|
|
30
|
+
"appBuild": "3400",
|
|
31
|
+
"engineVersion": "1.12.0",
|
|
32
|
+
"sdkVersion": "0.9.2"
|
|
33
|
+
},
|
|
34
|
+
"glasses": {
|
|
35
|
+
"model": "mentra_live",
|
|
36
|
+
"firmware": {
|
|
37
|
+
"apk": "1.7.3",
|
|
38
|
+
"mtk": "2.1.0",
|
|
39
|
+
"bes": "0.8.5"
|
|
40
|
+
},
|
|
41
|
+
"connection": {
|
|
42
|
+
"state": "connected",
|
|
43
|
+
"observedAt": "2026-10-13T11:00:00Z"
|
|
44
|
+
},
|
|
45
|
+
"battery": {
|
|
46
|
+
"state": "unsupported",
|
|
47
|
+
"observedAt": "2026-10-13T11:00:00Z"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-snap-0001",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 0,
|
|
10
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "snapshot",
|
|
19
|
+
"deviceBinding": {
|
|
20
|
+
"kind": "serial",
|
|
21
|
+
"manufacturer": "Mentra",
|
|
22
|
+
"product": "mentra_live",
|
|
23
|
+
"serial": "ML2A-0042-7731"
|
|
24
|
+
},
|
|
25
|
+
"payload": {
|
|
26
|
+
"phone": {
|
|
27
|
+
"model": "Pixel 8",
|
|
28
|
+
"os": "android",
|
|
29
|
+
"osVersion": "15",
|
|
30
|
+
"appVersion": "3.4.0",
|
|
31
|
+
"appBuild": "3400",
|
|
32
|
+
"engineVersion": "1.12.0",
|
|
33
|
+
"sdkVersion": "0.9.2"
|
|
34
|
+
},
|
|
35
|
+
"glasses": {
|
|
36
|
+
"model": "mentra_live",
|
|
37
|
+
"firmware": {
|
|
38
|
+
"apk": "1.7.3",
|
|
39
|
+
"mtk": "2.1.0",
|
|
40
|
+
"bes": "0.8.5"
|
|
41
|
+
},
|
|
42
|
+
"connection": {
|
|
43
|
+
"state": "connected",
|
|
44
|
+
"observedAt": "2026-10-13T11:00:00Z"
|
|
45
|
+
},
|
|
46
|
+
"battery": {
|
|
47
|
+
"state": "measured",
|
|
48
|
+
"observedAt": "2026-10-13T11:00:00Z",
|
|
49
|
+
"level": 76,
|
|
50
|
+
"charging": false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-cp-0001",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 2,
|
|
10
|
+
"observedAt": "2026-10-13T11:03:10Z",
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"glasses_connection",
|
|
13
|
+
"glasses_battery",
|
|
14
|
+
"glasses_versions",
|
|
15
|
+
"miniapp_execution",
|
|
16
|
+
"engine_active"
|
|
17
|
+
],
|
|
18
|
+
"kind": "usage_checkpoint",
|
|
19
|
+
"payload": {
|
|
20
|
+
"sessions": [
|
|
21
|
+
{
|
|
22
|
+
"activity": "miniapp_execution",
|
|
23
|
+
"activitySessionId": "act-8d02",
|
|
24
|
+
"packageName": "com.mentra.notes",
|
|
25
|
+
"startedAt": "2026-10-13T11:02:10Z",
|
|
26
|
+
"elapsedMs": 60000,
|
|
27
|
+
"ended": false
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"records": [
|
|
4
|
+
{
|
|
5
|
+
"schemaVersion": 1,
|
|
6
|
+
"recordId": "rec-pack-0001",
|
|
7
|
+
"phoneDeviceId": "phone-7f3a91",
|
|
8
|
+
"collectorSessionId": "cs-20261013-01",
|
|
9
|
+
"sequence": 4,
|
|
10
|
+
"observedAt": "2026-10-13T11:03:10Z",
|
|
11
|
+
"capabilities": ["glasses_connection", "miniapp_execution", "app_foreground", "engine_active"],
|
|
12
|
+
"kind": "usage_checkpoint",
|
|
13
|
+
"payload": {
|
|
14
|
+
"sessions": [
|
|
15
|
+
{
|
|
16
|
+
"activity": "glasses_connection",
|
|
17
|
+
"activitySessionId": "act-conn-01",
|
|
18
|
+
"deviceBinding": {
|
|
19
|
+
"kind": "serial",
|
|
20
|
+
"manufacturer": "Mentra",
|
|
21
|
+
"product": "mentra_live",
|
|
22
|
+
"serial": "ML2A-0042-7731"
|
|
23
|
+
},
|
|
24
|
+
"startedAt": "2026-10-13T10:59:00Z",
|
|
25
|
+
"elapsedMs": 250000,
|
|
26
|
+
"ended": false
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"activity": "miniapp_execution",
|
|
30
|
+
"activitySessionId": "act-8d02",
|
|
31
|
+
"packageName": "com.mentra.notes",
|
|
32
|
+
"startedAt": "2026-10-13T11:02:10Z",
|
|
33
|
+
"elapsedMs": 60000,
|
|
34
|
+
"ended": false
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"activity": "miniapp_execution",
|
|
38
|
+
"activitySessionId": "act-8c91",
|
|
39
|
+
"packageName": "com.acme.inventory",
|
|
40
|
+
"startedAt": "2026-10-13T11:00:30Z",
|
|
41
|
+
"elapsedMs": 120000,
|
|
42
|
+
"ended": true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"activity": "app_foreground",
|
|
46
|
+
"activitySessionId": "act-fg-01",
|
|
47
|
+
"startedAt": "2026-10-13T10:58:30Z",
|
|
48
|
+
"elapsedMs": 280000,
|
|
49
|
+
"ended": false
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"activity": "engine_active",
|
|
53
|
+
"activitySessionId": "act-eng-01",
|
|
54
|
+
"startedAt": "2026-10-13T08:00:00Z",
|
|
55
|
+
"elapsedMs": 10990000,
|
|
56
|
+
"ended": false
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
package/src/fleet.ts
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The Fleet report and acknowledgement contract.
|
|
3
|
+
*
|
|
4
|
+
* The Mentra App reports what a phone and its glasses did to the organization's Fleet service (MFM), through the
|
|
5
|
+
* phone's own Core. The reporter (via `@mentra/cloud-client`) and MFM both parse with these schemas, so this file is
|
|
6
|
+
* the one definition of what may cross that wire.
|
|
7
|
+
*
|
|
8
|
+
* Records are validated one at a time. The envelope accepts any record shape so that one bad record is rejected on
|
|
9
|
+
* its own, and never strands the rest of its batch.
|
|
10
|
+
*/
|
|
11
|
+
import {z} from "zod"
|
|
12
|
+
|
|
13
|
+
export const FLEET_SCHEMA_VERSION = 1 as const
|
|
14
|
+
|
|
15
|
+
export const FLEET_LIMITS = {
|
|
16
|
+
maxRecordsPerBatch: 500,
|
|
17
|
+
maxBatchBytes: 1_048_576,
|
|
18
|
+
maxSessionsPerCheckpoint: 32,
|
|
19
|
+
maxSessionElapsedMs: 7 * 24 * 60 * 60 * 1000,
|
|
20
|
+
phoneQueueMaxRecords: 10_000,
|
|
21
|
+
phoneQueueMaxAgeMs: 7 * 24 * 60 * 60 * 1000,
|
|
22
|
+
replayWindowMs: 14 * 24 * 60 * 60 * 1000,
|
|
23
|
+
maxFutureSkewMs: 5 * 60 * 1000,
|
|
24
|
+
} as const
|
|
25
|
+
|
|
26
|
+
const FleetId = z
|
|
27
|
+
.string()
|
|
28
|
+
.min(1)
|
|
29
|
+
.max(128)
|
|
30
|
+
.regex(/^[A-Za-z0-9._:-]+$/)
|
|
31
|
+
const FLEET_UTC_TIME = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,3}))?Z$/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Whether `value` names a real instant. `Date.parse` alone is not enough: it normalizes a calendar-invalid date (such
|
|
35
|
+
* as `2025-02-30`) to a later instant, and accepts `24:00:00`. So the parsed instant is formatted back and must
|
|
36
|
+
* reproduce every field of the input, with a missing or short fraction read as milliseconds.
|
|
37
|
+
*/
|
|
38
|
+
function isRoundTripUtcTime(value: string): boolean {
|
|
39
|
+
const match = FLEET_UTC_TIME.exec(value)
|
|
40
|
+
if (!match) return false
|
|
41
|
+
const instant = Date.parse(value)
|
|
42
|
+
if (Number.isNaN(instant)) return false
|
|
43
|
+
return new Date(instant).toISOString() === `${match[1]}.${(match[2] ?? "").padEnd(3, "0")}Z`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** RFC 3339 UTC with a `Z`; a numeric offset, a calendar-invalid date, `24:00:00` and a leap second are refused. */
|
|
47
|
+
const FleetUtcTime = z.string().regex(FLEET_UTC_TIME).refine(isRoundTripUtcTime, "not a date")
|
|
48
|
+
const FleetText = z.string().min(1).max(128)
|
|
49
|
+
|
|
50
|
+
export const FleetDeviceBindingSchema = z.discriminatedUnion("kind", [
|
|
51
|
+
z.object({kind: z.literal("serial"), manufacturer: FleetText, product: FleetText, serial: FleetText}).strict(),
|
|
52
|
+
z.object({kind: z.literal("provisional"), bindingId: FleetId, product: FleetText}).strict(),
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
export const FLEET_CAPABILITIES = [
|
|
56
|
+
"glasses_connection",
|
|
57
|
+
"glasses_battery",
|
|
58
|
+
"glasses_versions",
|
|
59
|
+
"miniapp_execution",
|
|
60
|
+
"app_foreground",
|
|
61
|
+
"engine_active",
|
|
62
|
+
"photo_operations",
|
|
63
|
+
] as const
|
|
64
|
+
|
|
65
|
+
const Battery = z.discriminatedUnion("state", [
|
|
66
|
+
z
|
|
67
|
+
.object({
|
|
68
|
+
state: z.literal("measured"),
|
|
69
|
+
observedAt: FleetUtcTime,
|
|
70
|
+
level: z.number().int().min(0).max(100),
|
|
71
|
+
charging: z.boolean().optional(),
|
|
72
|
+
})
|
|
73
|
+
.strict(),
|
|
74
|
+
z.object({state: z.literal("unsupported"), observedAt: FleetUtcTime}).strict(),
|
|
75
|
+
z.object({state: z.literal("unknown"), observedAt: FleetUtcTime}).strict(),
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
export const FleetSnapshotPayloadSchema = z
|
|
79
|
+
.object({
|
|
80
|
+
phone: z
|
|
81
|
+
.object({
|
|
82
|
+
model: FleetText.optional(),
|
|
83
|
+
os: z.enum(["ios", "android"]),
|
|
84
|
+
osVersion: FleetText,
|
|
85
|
+
appVersion: FleetText,
|
|
86
|
+
appBuild: FleetText,
|
|
87
|
+
engineVersion: FleetText.optional(),
|
|
88
|
+
sdkVersion: FleetText.optional(),
|
|
89
|
+
})
|
|
90
|
+
.strict(),
|
|
91
|
+
glasses: z
|
|
92
|
+
.object({
|
|
93
|
+
model: FleetText,
|
|
94
|
+
firmware: z
|
|
95
|
+
.object({
|
|
96
|
+
apk: FleetText.optional(),
|
|
97
|
+
mtk: FleetText.optional(),
|
|
98
|
+
bes: FleetText.optional(),
|
|
99
|
+
os: FleetText.optional(),
|
|
100
|
+
})
|
|
101
|
+
.strict()
|
|
102
|
+
.optional(),
|
|
103
|
+
connection: z
|
|
104
|
+
.object({state: z.enum(["connected", "disconnected", "connecting", "unknown"]), observedAt: FleetUtcTime})
|
|
105
|
+
.strict(),
|
|
106
|
+
battery: Battery.optional(),
|
|
107
|
+
})
|
|
108
|
+
.strict()
|
|
109
|
+
.optional(),
|
|
110
|
+
})
|
|
111
|
+
.strict()
|
|
112
|
+
|
|
113
|
+
export const FleetLaunchPayloadSchema = z
|
|
114
|
+
.object({
|
|
115
|
+
packageName: FleetText,
|
|
116
|
+
miniappName: FleetText.optional(),
|
|
117
|
+
versionName: FleetText.optional(),
|
|
118
|
+
start: z.enum(["user", "automatic"]),
|
|
119
|
+
outcome: z.enum(["started", "failed"]),
|
|
120
|
+
})
|
|
121
|
+
.strict()
|
|
122
|
+
|
|
123
|
+
export const FLEET_ACTIVITIES = ["glasses_connection", "miniapp_execution", "app_foreground", "engine_active"] as const
|
|
124
|
+
export const FleetActivityCheckpointSchema = z
|
|
125
|
+
.object({
|
|
126
|
+
activity: z.enum(FLEET_ACTIVITIES),
|
|
127
|
+
activitySessionId: FleetId,
|
|
128
|
+
packageName: FleetText.optional(),
|
|
129
|
+
deviceBinding: FleetDeviceBindingSchema.optional(),
|
|
130
|
+
startedAt: FleetUtcTime,
|
|
131
|
+
/** Cumulative monotonic elapsed time of this session, never wall-clock subtraction. */
|
|
132
|
+
elapsedMs: z.number().int().min(0).max(FLEET_LIMITS.maxSessionElapsedMs),
|
|
133
|
+
ended: z.boolean(),
|
|
134
|
+
})
|
|
135
|
+
.strict()
|
|
136
|
+
.superRefine((value, ctx) => {
|
|
137
|
+
if ((value.activity === "miniapp_execution") !== (value.packageName !== undefined))
|
|
138
|
+
ctx.addIssue({code: "custom", message: "packageName is required for, and only for, miniapp_execution"})
|
|
139
|
+
if ((value.activity === "glasses_connection") !== (value.deviceBinding !== undefined))
|
|
140
|
+
ctx.addIssue({code: "custom", message: "deviceBinding is required for, and only for, glasses_connection"})
|
|
141
|
+
})
|
|
142
|
+
export type FleetActivity = (typeof FLEET_ACTIVITIES)[number]
|
|
143
|
+
export const FleetUsagePayloadSchema = z
|
|
144
|
+
.object({
|
|
145
|
+
sessions: z.array(FleetActivityCheckpointSchema).min(1).max(FLEET_LIMITS.maxSessionsPerCheckpoint),
|
|
146
|
+
})
|
|
147
|
+
.strict()
|
|
148
|
+
|
|
149
|
+
export const FleetPhotoPayloadSchema = z
|
|
150
|
+
.object({
|
|
151
|
+
stage: z.enum(["requested", "captured", "delivered", "failed"]),
|
|
152
|
+
failedStage: z.enum(["requested", "captured", "delivered"]).optional(),
|
|
153
|
+
})
|
|
154
|
+
.strict()
|
|
155
|
+
.refine((v) => (v.stage === "failed") === (v.failedStage !== undefined), "failedStage goes with stage failed")
|
|
156
|
+
|
|
157
|
+
export const FleetCoverageGapPayloadSchema = z
|
|
158
|
+
.object({
|
|
159
|
+
reason: z.enum(["queue_dropped", "queue_expired", "clock_untrusted", "reporter_stopped", "record_rejected"]),
|
|
160
|
+
from: FleetUtcTime,
|
|
161
|
+
to: FleetUtcTime,
|
|
162
|
+
droppedRecordCount: z.number().int().min(0).optional(),
|
|
163
|
+
})
|
|
164
|
+
.strict()
|
|
165
|
+
|
|
166
|
+
const base = {
|
|
167
|
+
schemaVersion: z.literal(FLEET_SCHEMA_VERSION),
|
|
168
|
+
recordId: FleetId,
|
|
169
|
+
phoneDeviceId: FleetId,
|
|
170
|
+
collectorSessionId: FleetId,
|
|
171
|
+
sequence: z.number().int().min(0),
|
|
172
|
+
observedAt: FleetUtcTime,
|
|
173
|
+
management: z
|
|
174
|
+
.object({associationId: FleetId, generation: z.number().int().min(0)})
|
|
175
|
+
.strict()
|
|
176
|
+
.optional(),
|
|
177
|
+
capabilities: z.array(z.enum(FLEET_CAPABILITIES)).max(FLEET_CAPABILITIES.length),
|
|
178
|
+
}
|
|
179
|
+
export const FleetRecordSchema = z.discriminatedUnion("kind", [
|
|
180
|
+
z
|
|
181
|
+
.object({
|
|
182
|
+
...base,
|
|
183
|
+
kind: z.literal("snapshot"),
|
|
184
|
+
deviceBinding: FleetDeviceBindingSchema.optional(),
|
|
185
|
+
payload: FleetSnapshotPayloadSchema,
|
|
186
|
+
})
|
|
187
|
+
.strict(),
|
|
188
|
+
z.object({...base, kind: z.literal("miniapp_launch"), payload: FleetLaunchPayloadSchema}).strict(),
|
|
189
|
+
z.object({...base, kind: z.literal("usage_checkpoint"), payload: FleetUsagePayloadSchema}).strict(),
|
|
190
|
+
z
|
|
191
|
+
.object({
|
|
192
|
+
...base,
|
|
193
|
+
kind: z.literal("photo_operation"),
|
|
194
|
+
operationId: FleetId,
|
|
195
|
+
deviceBinding: FleetDeviceBindingSchema,
|
|
196
|
+
payload: FleetPhotoPayloadSchema,
|
|
197
|
+
})
|
|
198
|
+
.strict(),
|
|
199
|
+
z.object({...base, kind: z.literal("coverage_gap"), payload: FleetCoverageGapPayloadSchema}).strict(),
|
|
200
|
+
])
|
|
201
|
+
export type FleetRecord = z.infer<typeof FleetRecordSchema>
|
|
202
|
+
export type FleetDeviceBinding = z.infer<typeof FleetDeviceBindingSchema>
|
|
203
|
+
|
|
204
|
+
/** The envelope. Records are validated one at a time, so one bad record cannot strand a batch. */
|
|
205
|
+
export const FleetReportSchema = z
|
|
206
|
+
.object({
|
|
207
|
+
schemaVersion: z.literal(FLEET_SCHEMA_VERSION),
|
|
208
|
+
records: z.array(z.unknown()).min(1).max(FLEET_LIMITS.maxRecordsPerBatch),
|
|
209
|
+
})
|
|
210
|
+
.strict()
|
|
211
|
+
export type FleetReport = {schemaVersion: 1; records: FleetRecord[]}
|
|
212
|
+
|
|
213
|
+
export const FLEET_REJECT_REASONS = [
|
|
214
|
+
"schema_invalid",
|
|
215
|
+
"unsupported_schema_version",
|
|
216
|
+
"content_mismatch",
|
|
217
|
+
"outside_replay_window",
|
|
218
|
+
"timestamp_out_of_bounds",
|
|
219
|
+
"account_deleted",
|
|
220
|
+
] as const
|
|
221
|
+
export const FleetRecordResultSchema = z
|
|
222
|
+
.object({
|
|
223
|
+
index: z.number().int().min(0),
|
|
224
|
+
recordId: FleetId.optional(),
|
|
225
|
+
status: z.enum(["accepted", "duplicate", "retryable", "rejected"]),
|
|
226
|
+
reasonCode: z.enum(FLEET_REJECT_REASONS).optional(),
|
|
227
|
+
retryAfterSeconds: z.number().int().min(0).optional(),
|
|
228
|
+
})
|
|
229
|
+
.strict()
|
|
230
|
+
.superRefine((value, ctx) => {
|
|
231
|
+
if ((value.status === "rejected") !== (value.reasonCode !== undefined))
|
|
232
|
+
ctx.addIssue({code: "custom", message: "reasonCode is required for, and only for, status rejected"})
|
|
233
|
+
})
|
|
234
|
+
export const FleetReportAckSchema = z
|
|
235
|
+
.object({
|
|
236
|
+
schemaVersion: z.literal(FLEET_SCHEMA_VERSION),
|
|
237
|
+
results: z.array(FleetRecordResultSchema),
|
|
238
|
+
})
|
|
239
|
+
.strict()
|
|
240
|
+
export type FleetRecordResult = z.infer<typeof FleetRecordResultSchema>
|
|
241
|
+
export type FleetReportAck = z.infer<typeof FleetReportAckSchema>
|
|
242
|
+
|
|
243
|
+
/** Whole-response error codes Core and MFM answer with. */
|
|
244
|
+
export const FLEET_NOT_INSTALLED = "fleet_not_installed"
|
|
245
|
+
export const FLEET_UNAVAILABLE = "fleet_unavailable"
|
package/src/index.ts
CHANGED