@onekeyfe/hd-core 1.2.0-alpha.51 → 1.2.0-alpha.52
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/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +294 -0
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +3 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
- package/dist/index.js +29 -5
- package/dist/utils/deviceFeaturesCompat.d.ts +1 -0
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV2.ts +4 -1
- package/src/api/firmware/FirmwareUpdatePlan.ts +31 -4
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +24 -1
- package/src/utils/deviceFeaturesCompat.ts +6 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
|
|
4
|
+
import {
|
|
5
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
6
|
+
prepareFirmwareUpdatePlan,
|
|
7
|
+
} from '../../src/api/firmware/FirmwareUpdatePreparedPlan';
|
|
8
|
+
|
|
9
|
+
import type { Features } from '../../src/types';
|
|
10
|
+
|
|
11
|
+
const FIRMWARE_SHA256 = 'a'.repeat(64);
|
|
12
|
+
|
|
13
|
+
const BLE_SHA256 = 'b'.repeat(64);
|
|
14
|
+
|
|
15
|
+
const buildDesktopPlan = (
|
|
16
|
+
features: Partial<Features>,
|
|
17
|
+
options?: {
|
|
18
|
+
ble?: { status: string; release?: Record<string, unknown> };
|
|
19
|
+
firmwareStatus?: string;
|
|
20
|
+
}
|
|
21
|
+
) =>
|
|
22
|
+
buildFirmwareUpdatePlan({
|
|
23
|
+
features: features as Features,
|
|
24
|
+
firmwareType: EFirmwareType.Universal,
|
|
25
|
+
platform: 'desktop',
|
|
26
|
+
firmware: {
|
|
27
|
+
status: options?.firmwareStatus ?? 'none',
|
|
28
|
+
release: {
|
|
29
|
+
url: 'https://firmware.onekey.so/classic/firmware.bin',
|
|
30
|
+
expectedSize: 4,
|
|
31
|
+
fingerprint: FIRMWARE_SHA256,
|
|
32
|
+
version: [3, 20, 0],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
ble: options?.ble ?? {
|
|
36
|
+
status: 'valid',
|
|
37
|
+
release: undefined,
|
|
38
|
+
},
|
|
39
|
+
bootloader: {
|
|
40
|
+
status: 'valid',
|
|
41
|
+
release: undefined,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const bleNoneWithRelease = {
|
|
46
|
+
status: 'none',
|
|
47
|
+
release: {
|
|
48
|
+
url: 'https://firmware.onekey.so/classic/ble.bin',
|
|
49
|
+
webUpdate: 'https://firmware.onekey.so/classic/ble.bin',
|
|
50
|
+
expectedSize: 8,
|
|
51
|
+
fingerprint: BLE_SHA256,
|
|
52
|
+
version: [1, 5, 6],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// A classic1s that lost its firmware reports no serial in bootloader mode; the
|
|
57
|
+
// recovery flow must still be able to build (and later execute) a prepared plan.
|
|
58
|
+
const bootloaderClassic1sFeatures: Partial<Features> = {
|
|
59
|
+
deviceType: EDeviceType.Classic1s,
|
|
60
|
+
bootloaderMode: true,
|
|
61
|
+
firmwareVersion: '0.0.0',
|
|
62
|
+
bootloaderVersion: '2.1.2',
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
describe('firmware update plan identity in bootloader mode', () => {
|
|
66
|
+
test('builds a degraded-identity plan for a serial-less bootloader classic1s on desktop', () => {
|
|
67
|
+
const plan = buildDesktopPlan(bootloaderClassic1sFeatures);
|
|
68
|
+
expect(plan.deviceIdentity).toBe('unavailable');
|
|
69
|
+
expect(plan.executor).not.toBe('v4');
|
|
70
|
+
expect(plan.targetsToUpdate).toContain('firmware');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('bootloader mode with intact firmware (status unknown) still gets the firmware artifact', () => {
|
|
74
|
+
const plan = buildDesktopPlan(
|
|
75
|
+
{
|
|
76
|
+
deviceType: EDeviceType.Classic1s,
|
|
77
|
+
bootloaderMode: true,
|
|
78
|
+
firmwareVersion: '2.1.2',
|
|
79
|
+
bootloaderVersion: '2.1.2',
|
|
80
|
+
},
|
|
81
|
+
{ firmwareStatus: 'unknown' }
|
|
82
|
+
);
|
|
83
|
+
expect(plan.targetsToUpdate).toContain('firmware');
|
|
84
|
+
expect(plan.deviceIdentity).toBe('unavailable');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('normal-mode status none with a release available still adds no artifacts', () => {
|
|
88
|
+
const plan = buildDesktopPlan({
|
|
89
|
+
deviceType: EDeviceType.Classic1s,
|
|
90
|
+
serialNo: 'CLA45F0023',
|
|
91
|
+
firmwareVersion: '3.19.0',
|
|
92
|
+
bootloaderVersion: '2.1.2',
|
|
93
|
+
});
|
|
94
|
+
expect(plan.targetsToUpdate).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('recovery plan carries the BLE artifact when the BLE version is unreadable in bootloader', () => {
|
|
98
|
+
const plan = buildDesktopPlan(bootloaderClassic1sFeatures, { ble: bleNoneWithRelease });
|
|
99
|
+
expect(plan.targetsToUpdate).toEqual(expect.arrayContaining(['firmware', 'ble']));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('a normal-mode device without BLE hardware (status none) gets no BLE artifact', () => {
|
|
103
|
+
const plan = buildFirmwareUpdatePlan({
|
|
104
|
+
features: {
|
|
105
|
+
deviceType: EDeviceType.Mini,
|
|
106
|
+
serialNo: 'mini-device-id',
|
|
107
|
+
firmwareVersion: '3.9.0',
|
|
108
|
+
bootloaderVersion: '2.0.0',
|
|
109
|
+
} as Features,
|
|
110
|
+
firmwareType: EFirmwareType.Universal,
|
|
111
|
+
platform: 'desktop',
|
|
112
|
+
firmware: {
|
|
113
|
+
status: 'outdated',
|
|
114
|
+
release: {
|
|
115
|
+
url: 'https://firmware.onekey.so/mini/firmware.bin',
|
|
116
|
+
expectedSize: 4,
|
|
117
|
+
fingerprint: FIRMWARE_SHA256,
|
|
118
|
+
version: [3, 10, 0],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
ble: { status: 'none', release: undefined },
|
|
122
|
+
bootloader: { status: 'valid', release: undefined },
|
|
123
|
+
});
|
|
124
|
+
expect(plan.targetsToUpdate).toEqual(['firmware']);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('still rejects a serial-less normal-mode device on desktop', () => {
|
|
128
|
+
expect(() =>
|
|
129
|
+
buildDesktopPlan(
|
|
130
|
+
{
|
|
131
|
+
deviceType: EDeviceType.Classic1s,
|
|
132
|
+
firmwareVersion: '3.19.0',
|
|
133
|
+
bootloaderVersion: '2.1.2',
|
|
134
|
+
},
|
|
135
|
+
{ firmwareStatus: 'outdated' }
|
|
136
|
+
)
|
|
137
|
+
).toThrow(/stable device identity/);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('keeps rejecting degraded identity for serial-less Pro (v3) in bootloader', () => {
|
|
141
|
+
expect(() =>
|
|
142
|
+
buildDesktopPlan(
|
|
143
|
+
{
|
|
144
|
+
deviceType: EDeviceType.Pro,
|
|
145
|
+
bootloaderMode: true,
|
|
146
|
+
firmwareVersion: '0.0.0',
|
|
147
|
+
bootloaderVersion: '2.8.0',
|
|
148
|
+
},
|
|
149
|
+
{ firmwareStatus: 'none' }
|
|
150
|
+
)
|
|
151
|
+
).toThrow(/stable device identity/);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('degraded plans are v2-only', () => {
|
|
155
|
+
const plan = buildDesktopPlan(bootloaderClassic1sFeatures);
|
|
156
|
+
expect(plan.executor).toBe('v2');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('prepared plan device identity assertion', () => {
|
|
160
|
+
const prepareDegradedPlan = () => {
|
|
161
|
+
const plan = buildDesktopPlan(bootloaderClassic1sFeatures);
|
|
162
|
+
return prepareFirmwareUpdatePlan({
|
|
163
|
+
plan,
|
|
164
|
+
leaseRef: 'fwlease:00000000-0000-4000-8000-000000000001',
|
|
165
|
+
artifacts: [
|
|
166
|
+
{
|
|
167
|
+
artifactId: 'firmware',
|
|
168
|
+
artifact: {
|
|
169
|
+
artifactRef: `fw:${FIRMWARE_SHA256}`,
|
|
170
|
+
size: 4,
|
|
171
|
+
sha256: FIRMWARE_SHA256,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
test('accepts a degraded plan when the live device is also identity-less in bootloader mode', () => {
|
|
179
|
+
const preparedPlan = prepareDegradedPlan();
|
|
180
|
+
expect(() =>
|
|
181
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
182
|
+
preparedPlan,
|
|
183
|
+
deviceIdentity: undefined,
|
|
184
|
+
bootloaderMode: true,
|
|
185
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
186
|
+
})
|
|
187
|
+
).not.toThrow();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('rejects a degraded plan outside bootloader mode', () => {
|
|
191
|
+
const preparedPlan = prepareDegradedPlan();
|
|
192
|
+
expect(() =>
|
|
193
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
194
|
+
preparedPlan,
|
|
195
|
+
deviceIdentity: undefined,
|
|
196
|
+
bootloaderMode: false,
|
|
197
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
198
|
+
})
|
|
199
|
+
).toThrow();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('rejects a degraded plan when bootloader mode is not stated', () => {
|
|
203
|
+
const preparedPlan = prepareDegradedPlan();
|
|
204
|
+
expect(() =>
|
|
205
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
206
|
+
preparedPlan,
|
|
207
|
+
deviceIdentity: undefined,
|
|
208
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
209
|
+
})
|
|
210
|
+
).toThrow();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('rejects a degraded plan when the live device reports a real identity', () => {
|
|
214
|
+
const preparedPlan = prepareDegradedPlan();
|
|
215
|
+
expect(() =>
|
|
216
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
217
|
+
preparedPlan,
|
|
218
|
+
deviceIdentity: 'CLA45F0023',
|
|
219
|
+
bootloaderMode: true,
|
|
220
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
221
|
+
})
|
|
222
|
+
).toThrow();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('rejects a degraded plan on a different device model', () => {
|
|
226
|
+
const preparedPlan = prepareDegradedPlan();
|
|
227
|
+
expect(() =>
|
|
228
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
229
|
+
preparedPlan,
|
|
230
|
+
deviceIdentity: undefined,
|
|
231
|
+
bootloaderMode: true,
|
|
232
|
+
deviceModel: String(EDeviceType.Mini),
|
|
233
|
+
})
|
|
234
|
+
).toThrow();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('rejects a degraded plan when the live model is unstated', () => {
|
|
238
|
+
const preparedPlan = prepareDegradedPlan();
|
|
239
|
+
expect(() =>
|
|
240
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
241
|
+
preparedPlan,
|
|
242
|
+
deviceIdentity: undefined,
|
|
243
|
+
bootloaderMode: true,
|
|
244
|
+
})
|
|
245
|
+
).toThrow();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('a live serial equal to the reserved sentinel never matches a degraded plan', () => {
|
|
249
|
+
const preparedPlan = prepareDegradedPlan();
|
|
250
|
+
expect(() =>
|
|
251
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
252
|
+
preparedPlan,
|
|
253
|
+
deviceIdentity: 'unavailable',
|
|
254
|
+
bootloaderMode: false,
|
|
255
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
256
|
+
})
|
|
257
|
+
).toThrow();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('rejects a real-identity plan when the live device is identity-less in bootloader mode', () => {
|
|
261
|
+
const plan = buildDesktopPlan(
|
|
262
|
+
{
|
|
263
|
+
deviceType: EDeviceType.Classic1s,
|
|
264
|
+
serialNo: 'CLA45F0023',
|
|
265
|
+
firmwareVersion: '3.19.0',
|
|
266
|
+
bootloaderVersion: '2.1.2',
|
|
267
|
+
},
|
|
268
|
+
{ firmwareStatus: 'outdated' }
|
|
269
|
+
);
|
|
270
|
+
const preparedPlan = prepareFirmwareUpdatePlan({
|
|
271
|
+
plan,
|
|
272
|
+
leaseRef: 'fwlease:00000000-0000-4000-8000-000000000002',
|
|
273
|
+
artifacts: [
|
|
274
|
+
{
|
|
275
|
+
artifactId: 'firmware',
|
|
276
|
+
artifact: {
|
|
277
|
+
artifactRef: `fw:${FIRMWARE_SHA256}`,
|
|
278
|
+
size: 4,
|
|
279
|
+
sha256: FIRMWARE_SHA256,
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
});
|
|
284
|
+
expect(() =>
|
|
285
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
286
|
+
preparedPlan,
|
|
287
|
+
deviceIdentity: undefined,
|
|
288
|
+
bootloaderMode: true,
|
|
289
|
+
deviceModel: String(EDeviceType.Classic1s),
|
|
290
|
+
})
|
|
291
|
+
).toThrow();
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdateV2.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV2.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAKnB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"FirmwareUpdateV2.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV2.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAKnB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAyB1C,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,UAAU,CAAC;AAEtD,OAAO,KAAK,EACV,sBAAsB,EACtB,yBAAyB,EAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AAE1F,KAAK,MAAM,GAAG;IACZ,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IAC7C,eAAe,CAAC,EAAE,KAAK,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,yBAAyB,CAAC;KACrC,CAAC,CAAC;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,EAAE,0BAA0B,CAAC,UAAU,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,UAAU,GAAG,KAAK,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC;AAkFF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,UAAU,CAAC,MAAM,CAAC;IAC9D,YAAY,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAQ;IAE1C,IAAI;IA6EJ,cAAc,YAAa,MAAM,UAS/B;YAEY,qCAAqC;IAkBnD,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS;YA6FvC,4BAA4B;IAuB1C,qBAAqB;IAUrB,uBAAuB,CAAC,UAAU,EAAE,MAAM;IAc1C,gCAAgC,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAAE,YAAY,EAAE,aAAa;IAwBtF,GAAG;CA2QV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdatePlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePlan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,aAAa,EAA6B,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"FirmwareUpdatePlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePlan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAU5F,OAAO,KAAK,EACV,kBAAkB,EAElB,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,sBAAsB,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,WAAW,CAAC;AAyBjF,KAAK,gBAAgB,GAAG;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA0GF,eAAO,MAAM,4BAA4B,UAAW,OAAO,KAAG,MAG3D,CAAC;AAkBJ,eAAO,MAAM,sCAAsC,UAC1C,OAAO,KACb,6BAA6B,EAa/B,CAAC;AAuBF,eAAO,MAAM,4BAA4B,+BAavC,CAAC;AAEH,eAAO,MAAM,0BAA0B,aAOrC,CAAC;AAYH,eAAO,MAAM,wBAAwB,UAAW,OAAO,KAAG,kBAyHzD,CAAC;AA4NF,eAAO,MAAM,uBAAuB;cASxB,QAAQ;kBACJ,aAAa;cACjB,sBAAsB;cACtB,gBAAgB;SACrB,gBAAgB;;;MAGnB,kBAsKH,CAAC"}
|
|
@@ -8,9 +8,11 @@ export declare const prepareFirmwareUpdatePlan: ({ plan: value, leaseRef, artifa
|
|
|
8
8
|
artifacts: FirmwareUpdatePreparedArtifactInput[];
|
|
9
9
|
}) => FirmwareUpdatePreparedPlan;
|
|
10
10
|
export declare const validateFirmwareUpdatePreparedPlan: (value: unknown) => FirmwareUpdatePreparedPlan;
|
|
11
|
-
export declare const assertFirmwareUpdatePreparedPlanDeviceIdentity: ({ preparedPlan: value, deviceIdentity, }: {
|
|
11
|
+
export declare const assertFirmwareUpdatePreparedPlanDeviceIdentity: ({ preparedPlan: value, deviceIdentity: reportedIdentity, bootloaderMode, deviceModel, }: {
|
|
12
12
|
preparedPlan: unknown;
|
|
13
13
|
deviceIdentity: string | undefined;
|
|
14
|
+
bootloaderMode?: boolean | undefined;
|
|
15
|
+
deviceModel?: string | undefined;
|
|
14
16
|
}) => FirmwareUpdatePreparedPlan;
|
|
15
17
|
type FirmwareUpdatePreparedBinding = {
|
|
16
18
|
target: FirmwareUpdatePlan['targetsToUpdate'][number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdatePreparedPlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePreparedPlan.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,mCAAmC,EAEnC,0BAA0B,EAC3B,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAwB7E,eAAO,MAAM,6BAA6B,UAAW,OAAO,KAAG,MA8B9D,CAAC;AAgGF,eAAO,MAAM,yBAAyB;UAK9B,kBAAkB;cACd,MAAM;eACL,mCAAmC,EAAE;MAC9C,0BAmBH,CAAC;AAEF,eAAO,MAAM,kCAAkC,UAAW,OAAO,KAAG,0BAmGnE,CAAC;AAEF,eAAO,MAAM,8CAA8C;
|
|
1
|
+
{"version":3,"file":"FirmwareUpdatePreparedPlan.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareUpdatePreparedPlan.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,mCAAmC,EAEnC,0BAA0B,EAC3B,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAwB7E,eAAO,MAAM,6BAA6B,UAAW,OAAO,KAAG,MA8B9D,CAAC;AAgGF,eAAO,MAAM,yBAAyB;UAK9B,kBAAkB;cACd,MAAM;eACL,mCAAmC,EAAE;MAC9C,0BAmBH,CAAC;AAEF,eAAO,MAAM,kCAAkC,UAAW,OAAO,KAAG,0BAmGnE,CAAC;AAEF,eAAO,MAAM,8CAA8C;kBAM3C,OAAO;oBACL,MAAM,GAAG,SAAS;;;MAKhC,0BAuBH,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,MAAM,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;IACtD,QAAQ,EAAE,yBAAyB,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAUF,eAAO,MAAM,uCAAuC;kBAQpC,OAAO;cACX,0BAA0B,CAAC,UAAU,CAAC;;;kBAGlC,0BAA0B,CAAC,iBAAiB,CAAC;cACjD,6BAA6B,EAAE;MACvC,0BAmDH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -317,6 +317,13 @@ const resolveDeviceSerialNo = (features) => {
|
|
|
317
317
|
const compatible = asCompatibleFeatures(features);
|
|
318
318
|
return ((_a = firstNonEmptyString(compatible.serialNo, compatible.onekey_serial_no, compatible.onekey_serial, compatible.serial_no)) !== null && _a !== void 0 ? _a : '');
|
|
319
319
|
};
|
|
320
|
+
const resolveDeviceBootloaderMode = (features) => {
|
|
321
|
+
var _a;
|
|
322
|
+
if (!features)
|
|
323
|
+
return false;
|
|
324
|
+
const compatible = asCompatibleFeatures(features);
|
|
325
|
+
return ((_a = compatible.bootloaderMode) !== null && _a !== void 0 ? _a : compatible.bootloader_mode) === true;
|
|
326
|
+
};
|
|
320
327
|
const resolveDeviceType = (features) => {
|
|
321
328
|
var _a, _b, _c, _d;
|
|
322
329
|
if (!features || typeof features !== 'object') {
|
|
@@ -721,6 +728,7 @@ const assertFirmwareUpdatePlan = (value) => {
|
|
|
721
728
|
plan.targetsToUpdate.some(target => legacyOnlyTargets.has(target))) ||
|
|
722
729
|
(plan.artifacts.length > 0 &&
|
|
723
730
|
(plan.platform === 'native' || plan.platform === 'desktop') &&
|
|
731
|
+
plan.executor !== 'v2' &&
|
|
724
732
|
plan.deviceIdentity === 'unavailable')) {
|
|
725
733
|
return planError('Firmware update plan executor contract is invalid');
|
|
726
734
|
}
|
|
@@ -855,7 +863,11 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
|
|
|
855
863
|
const firmwareRelease = asRelease(firmware);
|
|
856
864
|
const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
|
|
857
865
|
const forcedTargets = new Set(validatedForceTargets);
|
|
858
|
-
const
|
|
866
|
+
const bootloaderMode = resolveDeviceBootloaderMode(features);
|
|
867
|
+
const isRecoveryInstall = (release) => bootloaderMode &&
|
|
868
|
+
((release === null || release === void 0 ? void 0 : release.status) === 'none' || (release === null || release === void 0 ? void 0 : release.status) === 'unknown') &&
|
|
869
|
+
!!asRelease(release);
|
|
870
|
+
const shouldUpdateFirmware = isUpgrade(firmware) || isRecoveryInstall(firmware) || forcedTargets.has('firmware');
|
|
859
871
|
const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
|
|
860
872
|
if (executor === 'v4' &&
|
|
861
873
|
validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
|
|
@@ -912,7 +924,7 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
|
|
|
912
924
|
}
|
|
913
925
|
}
|
|
914
926
|
}
|
|
915
|
-
if (isUpgrade(ble) || forcedTargets.has('ble')) {
|
|
927
|
+
if (isUpgrade(ble) || isRecoveryInstall(ble) || forcedTargets.has('ble')) {
|
|
916
928
|
const bleRelease = asRelease(ble);
|
|
917
929
|
artifacts.push(Object.assign(Object.assign({ artifactId: 'ble', role: 'ble', target: 'ble', url: assertArtifactUrl((_f = bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.webUpdate) !== null && _f !== void 0 ? _f : bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.url, 'BLE release'), container: 'raw' }, asIntegrity({
|
|
918
930
|
size: bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.expectedSize,
|
|
@@ -929,10 +941,12 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
|
|
|
929
941
|
artifacts,
|
|
930
942
|
targetsToUpdate,
|
|
931
943
|
});
|
|
932
|
-
const
|
|
944
|
+
const reportedIdentity = getDeviceUUID(features);
|
|
945
|
+
const deviceIdentity = reportedIdentity === 'unavailable' ? '' : reportedIdentity;
|
|
933
946
|
if (artifacts.length > 0 &&
|
|
934
947
|
(platform === 'native' || platform === 'desktop') &&
|
|
935
|
-
!deviceIdentity
|
|
948
|
+
!deviceIdentity &&
|
|
949
|
+
!(bootloaderMode && executor === 'v2')) {
|
|
936
950
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware update requires a stable device identity', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
|
|
937
951
|
}
|
|
938
952
|
const planWithoutDigest = {
|
|
@@ -1160,8 +1174,16 @@ const validateFirmwareUpdatePreparedPlan = (value) => {
|
|
|
1160
1174
|
}
|
|
1161
1175
|
return preparedPlan;
|
|
1162
1176
|
};
|
|
1163
|
-
const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({ preparedPlan: value, deviceIdentity, }) => {
|
|
1177
|
+
const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({ preparedPlan: value, deviceIdentity: reportedIdentity, bootloaderMode, deviceModel, }) => {
|
|
1164
1178
|
const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
|
|
1179
|
+
const deviceIdentity = reportedIdentity === 'unavailable' ? undefined : reportedIdentity;
|
|
1180
|
+
if (preparedPlan.deviceIdentity === 'unavailable' &&
|
|
1181
|
+
!deviceIdentity &&
|
|
1182
|
+
bootloaderMode === true &&
|
|
1183
|
+
deviceModel !== undefined &&
|
|
1184
|
+
preparedPlan.deviceModel === deviceModel) {
|
|
1185
|
+
return preparedPlan;
|
|
1186
|
+
}
|
|
1165
1187
|
if (!deviceIdentity || preparedPlan.deviceIdentity !== deviceIdentity) {
|
|
1166
1188
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
1167
1189
|
}
|
|
@@ -49193,6 +49215,8 @@ class FirmwareUpdateV2 extends BaseMethod {
|
|
|
49193
49215
|
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
49194
49216
|
preparedPlan: params.preparedPlan,
|
|
49195
49217
|
deviceIdentity: getDeviceUUID(features) || undefined,
|
|
49218
|
+
bootloaderMode: resolveDeviceBootloaderMode(features),
|
|
49219
|
+
deviceModel: String(getDeviceType(features)),
|
|
49196
49220
|
});
|
|
49197
49221
|
}
|
|
49198
49222
|
const deviceFirmwareType = device.getCurrentFirmwareType();
|
|
@@ -3,6 +3,7 @@ import type { PROTO } from '../constants';
|
|
|
3
3
|
import type { Features, IDeviceType } from '../types';
|
|
4
4
|
export type DeviceFeaturesInput = Features | PROTO.Features;
|
|
5
5
|
export declare const resolveDeviceSerialNo: (features?: DeviceFeaturesInput) => string;
|
|
6
|
+
export declare const resolveDeviceBootloaderMode: (features?: DeviceFeaturesInput) => boolean;
|
|
6
7
|
export declare const resolveDeviceType: (features?: DeviceFeaturesInput) => IDeviceType;
|
|
7
8
|
export declare const resolveDeviceFirmwareType: (features?: DeviceFeaturesInput) => EFirmwareType;
|
|
8
9
|
export declare const resolveDeviceBleName: (features?: DeviceFeaturesInput) => string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceFeaturesCompat.d.ts","sourceRoot":"","sources":["../../src/utils/deviceFeaturesCompat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;AAoB5D,eAAO,MAAM,qBAAqB,cAAe,mBAAmB,KAAG,MAWtE,CAAC;AAEF,eAAO,MAAM,iBAAiB,cAAe,mBAAmB,KAAG,WAgDlE,CAAC;AAEF,eAAO,MAAM,yBAAyB,cAAe,mBAAmB,KAAG,aAsB1E,CAAC;AAEF,eAAO,MAAM,oBAAoB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAM9E,CAAC;AAEF,eAAO,MAAM,4BAA4B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAgBtF,CAAC;AAEF,eAAO,MAAM,8BAA8B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAiBxF,CAAC;AAEF,eAAO,MAAM,yBAAyB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAUnF,CAAC;AAEF,eAAO,MAAM,+BAA+B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IASzF,CAAC"}
|
|
1
|
+
{"version":3,"file":"deviceFeaturesCompat.d.ts","sourceRoot":"","sources":["../../src/utils/deviceFeaturesCompat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;AAoB5D,eAAO,MAAM,qBAAqB,cAAe,mBAAmB,KAAG,MAWtE,CAAC;AAEF,eAAO,MAAM,2BAA2B,cAAe,mBAAmB,KAAG,OAI5E,CAAC;AAEF,eAAO,MAAM,iBAAiB,cAAe,mBAAmB,KAAG,WAgDlE,CAAC;AAEF,eAAO,MAAM,yBAAyB,cAAe,mBAAmB,KAAG,aAsB1E,CAAC;AAEF,eAAO,MAAM,oBAAoB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAM9E,CAAC;AAEF,eAAO,MAAM,4BAA4B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAgBtF,CAAC;AAEF,eAAO,MAAM,8BAA8B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAiBxF,CAAC;AAEF,eAAO,MAAM,yBAAyB,cAAe,mBAAmB,KAAG,MAAM,GAAG,IAUnF,CAAC;AAEF,eAAO,MAAM,+BAA+B,cAAe,mBAAmB,KAAG,MAAM,GAAG,IASzF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.52",
|
|
4
4
|
"description": "Core processes and APIs for communicating with OneKey hardware devices.",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.52",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.52",
|
|
30
30
|
"axios": "1.15.2",
|
|
31
31
|
"bignumber.js": "^9.0.2",
|
|
32
32
|
"bytebuffer": "^5.0.1",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"@types/w3c-web-usb": "^1.0.10",
|
|
45
45
|
"@types/web-bluetooth": "^0.0.21"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "d8fd8f3d30c4ff3653d0379d5c3e1c100414b61e"
|
|
48
48
|
}
|
|
@@ -21,7 +21,8 @@ import {
|
|
|
21
21
|
uploadFirmwareFromSource,
|
|
22
22
|
} from './firmware/uploadFirmware';
|
|
23
23
|
import { BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS } from './firmware/FirmwareUpdateBaseMethod';
|
|
24
|
-
import { LoggerNames, getDeviceUUID, getLogger, wait } from '../utils';
|
|
24
|
+
import { LoggerNames, getDeviceType, getDeviceUUID, getLogger, wait } from '../utils';
|
|
25
|
+
import { resolveDeviceBootloaderMode } from '../utils/deviceFeaturesCompat';
|
|
25
26
|
import { FirmwareUpdateTipMessage, createUiMessage } from '../events/ui-request';
|
|
26
27
|
import { DeviceModelToTypes } from '../types';
|
|
27
28
|
import { DataManager } from '../data-manager';
|
|
@@ -429,6 +430,8 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
|
|
|
429
430
|
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
430
431
|
preparedPlan: params.preparedPlan,
|
|
431
432
|
deviceIdentity: getDeviceUUID(features) || undefined,
|
|
433
|
+
bootloaderMode: resolveDeviceBootloaderMode(features),
|
|
434
|
+
deviceModel: String(getDeviceType(features)),
|
|
432
435
|
});
|
|
433
436
|
}
|
|
434
437
|
|
|
@@ -4,6 +4,7 @@ import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe
|
|
|
4
4
|
import semver from 'semver';
|
|
5
5
|
|
|
6
6
|
import { getDeviceType, getDeviceUUID } from '../../utils/deviceInfoUtils';
|
|
7
|
+
import { resolveDeviceBootloaderMode } from '../../utils/deviceFeaturesCompat';
|
|
7
8
|
import {
|
|
8
9
|
getDeviceBootloaderVersion,
|
|
9
10
|
getDeviceFirmwareVersion,
|
|
@@ -350,8 +351,13 @@ export const assertFirmwareUpdatePlan = (value: unknown): FirmwareUpdatePlan =>
|
|
|
350
351
|
plan.targetsToUpdate.some(target =>
|
|
351
352
|
legacyOnlyTargets.has(target as FirmwareUpdatePlanTarget)
|
|
352
353
|
)) ||
|
|
354
|
+
// Only executor v2 services identity-less bootloader recovery (classic family);
|
|
355
|
+
// Pro (v3) and Pro2 (v4) report a serial even in bootloader mode, so their plans
|
|
356
|
+
// must always carry a real identity. Degraded v2 plans are bound to the live
|
|
357
|
+
// device at install time instead.
|
|
353
358
|
(plan.artifacts.length > 0 &&
|
|
354
359
|
(plan.platform === 'native' || plan.platform === 'desktop') &&
|
|
360
|
+
plan.executor !== 'v2' &&
|
|
355
361
|
plan.deviceIdentity === 'unavailable')
|
|
356
362
|
) {
|
|
357
363
|
return planError('Firmware update plan executor contract is invalid');
|
|
@@ -604,7 +610,19 @@ export const buildFirmwareUpdatePlan = ({
|
|
|
604
610
|
const firmwareRelease = asRelease(firmware);
|
|
605
611
|
const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
|
|
606
612
|
const forcedTargets = new Set(validatedForceTargets);
|
|
607
|
-
const
|
|
613
|
+
const bootloaderMode = resolveDeviceBootloaderMode(features);
|
|
614
|
+
// Bootloader recovery: an erased device reports firmware status 'none', a device
|
|
615
|
+
// with intact firmware in bootloader mode reports 'unknown' (version fields carry
|
|
616
|
+
// the bootloader version), and neither can report its BLE version — yet the app
|
|
617
|
+
// treats bootloader mode as a mandatory reinstall. Only bootloader mode may treat
|
|
618
|
+
// these statuses as install targets: in normal mode 'none' also describes devices
|
|
619
|
+
// without the component at all (e.g. Mini has no BLE) and must stay excluded.
|
|
620
|
+
const isRecoveryInstall = (release: ReleaseSelection | undefined): boolean =>
|
|
621
|
+
bootloaderMode &&
|
|
622
|
+
(release?.status === 'none' || release?.status === 'unknown') &&
|
|
623
|
+
!!asRelease(release);
|
|
624
|
+
const shouldUpdateFirmware =
|
|
625
|
+
isUpgrade(firmware) || isRecoveryInstall(firmware) || forcedTargets.has('firmware');
|
|
608
626
|
const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
|
|
609
627
|
|
|
610
628
|
if (
|
|
@@ -689,7 +707,7 @@ export const buildFirmwareUpdatePlan = ({
|
|
|
689
707
|
}
|
|
690
708
|
}
|
|
691
709
|
}
|
|
692
|
-
if (isUpgrade(ble) || forcedTargets.has('ble')) {
|
|
710
|
+
if (isUpgrade(ble) || isRecoveryInstall(ble) || forcedTargets.has('ble')) {
|
|
693
711
|
const bleRelease = asRelease(ble);
|
|
694
712
|
artifacts.push({
|
|
695
713
|
artifactId: 'ble',
|
|
@@ -716,11 +734,20 @@ export const buildFirmwareUpdatePlan = ({
|
|
|
716
734
|
targetsToUpdate,
|
|
717
735
|
});
|
|
718
736
|
|
|
719
|
-
|
|
737
|
+
// 'unavailable' is the reserved degraded-identity sentinel; a device-reported
|
|
738
|
+
// serial must never be able to collide with it.
|
|
739
|
+
const reportedIdentity = getDeviceUUID(features);
|
|
740
|
+
const deviceIdentity = reportedIdentity === 'unavailable' ? '' : reportedIdentity;
|
|
741
|
+
// Bootloader-mode classic-family devices (executor v2, e.g. classic1s) cannot report
|
|
742
|
+
// a serial number, so their recovery plans fall back to the degraded 'unavailable'
|
|
743
|
+
// identity. Binding is then enforced against the live device at install time instead
|
|
744
|
+
// (assertFirmwareUpdatePreparedPlanDeviceIdentity), and plan integrity by the digest.
|
|
745
|
+
// Pro (v3) and Pro2 (v4) report a serial even in bootloader mode and stay strict.
|
|
720
746
|
if (
|
|
721
747
|
artifacts.length > 0 &&
|
|
722
748
|
(platform === 'native' || platform === 'desktop') &&
|
|
723
|
-
!deviceIdentity
|
|
749
|
+
!deviceIdentity &&
|
|
750
|
+
!(bootloaderMode && executor === 'v2')
|
|
724
751
|
) {
|
|
725
752
|
throw ERRORS.TypedError(
|
|
726
753
|
HardwareErrorCode.RuntimeError,
|
|
@@ -297,12 +297,35 @@ export const validateFirmwareUpdatePreparedPlan = (value: unknown): FirmwareUpda
|
|
|
297
297
|
|
|
298
298
|
export const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({
|
|
299
299
|
preparedPlan: value,
|
|
300
|
-
deviceIdentity,
|
|
300
|
+
deviceIdentity: reportedIdentity,
|
|
301
|
+
bootloaderMode,
|
|
302
|
+
deviceModel,
|
|
301
303
|
}: {
|
|
302
304
|
preparedPlan: unknown;
|
|
303
305
|
deviceIdentity: string | undefined;
|
|
306
|
+
/** Live device mode; only bootloader mode may accept a degraded-identity plan. */
|
|
307
|
+
bootloaderMode?: boolean;
|
|
308
|
+
/** Live device model; a degraded plan must target the same model. */
|
|
309
|
+
deviceModel?: string;
|
|
304
310
|
}): FirmwareUpdatePreparedPlan => {
|
|
305
311
|
const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
|
|
312
|
+
// 'unavailable' is the reserved degraded sentinel — a device-reported serial equal
|
|
313
|
+
// to it must never satisfy the exact-match branch below.
|
|
314
|
+
const deviceIdentity = reportedIdentity === 'unavailable' ? undefined : reportedIdentity;
|
|
315
|
+
// Bootloader recovery: classic-family devices report no serial in bootloader mode,
|
|
316
|
+
// so a degraded plan is only acceptable when the live device is equally
|
|
317
|
+
// identity-less AND targets the same device model (identity equality implies the
|
|
318
|
+
// model match in the strict branch; the degraded branch must substitute it).
|
|
319
|
+
// A device that DOES report an identity must match the plan exactly.
|
|
320
|
+
if (
|
|
321
|
+
preparedPlan.deviceIdentity === 'unavailable' &&
|
|
322
|
+
!deviceIdentity &&
|
|
323
|
+
bootloaderMode === true &&
|
|
324
|
+
deviceModel !== undefined &&
|
|
325
|
+
preparedPlan.deviceModel === deviceModel
|
|
326
|
+
) {
|
|
327
|
+
return preparedPlan;
|
|
328
|
+
}
|
|
306
329
|
if (!deviceIdentity || preparedPlan.deviceIdentity !== deviceIdentity) {
|
|
307
330
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
308
331
|
}
|
|
@@ -37,6 +37,12 @@ export const resolveDeviceSerialNo = (features?: DeviceFeaturesInput): string =>
|
|
|
37
37
|
);
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
export const resolveDeviceBootloaderMode = (features?: DeviceFeaturesInput): boolean => {
|
|
41
|
+
if (!features) return false;
|
|
42
|
+
const compatible = asCompatibleFeatures(features);
|
|
43
|
+
return (compatible.bootloaderMode ?? compatible.bootloader_mode) === true;
|
|
44
|
+
};
|
|
45
|
+
|
|
40
46
|
export const resolveDeviceType = (features?: DeviceFeaturesInput): IDeviceType => {
|
|
41
47
|
if (!features || typeof features !== 'object') {
|
|
42
48
|
return EDeviceType.Unknown;
|