@onekeyfe/hd-core 1.2.0-alpha.46 → 1.2.0-alpha.48
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__/check-all-firmware-release-protocol-v2.test.ts +107 -12
- package/__tests__/protocol-v2-resources.test.ts +125 -45
- package/__tests__/protocol-v2.test.ts +168 -34
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +2 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -0
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/index.d.ts +21 -3
- package/dist/index.js +228 -111
- package/dist/protocols/protocol-v2/resources.d.ts +4 -4
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +2 -1
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/settings.d.ts +10 -0
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +9 -10
- package/src/api/FirmwareUpdateV4.ts +77 -6
- package/src/data/messages/messages-protocol-v2.json +0 -43
- package/src/data-manager/DataManager.ts +4 -0
- package/src/protocols/protocol-v2/resources.ts +183 -57
- package/src/types/api/firmwareUpdate.ts +3 -0
- package/src/types/settings.ts +16 -0
|
@@ -450,8 +450,6 @@
|
|
|
450
450
|
"MessageType_DeviceInfo": 60601,
|
|
451
451
|
"MessageType_DeviceStatusGet": 60602,
|
|
452
452
|
"MessageType_DeviceStatus": 60603,
|
|
453
|
-
"MessageType_ResourceInventoryGet": 60604,
|
|
454
|
-
"MessageType_ResourceInventory": 60605,
|
|
455
453
|
"MessageType_FilesystemPermissionFix": 60800,
|
|
456
454
|
"MessageType_FilesystemPathInfo": 60801,
|
|
457
455
|
"MessageType_FilesystemPathInfoQuery": 60802,
|
|
@@ -11996,16 +11994,6 @@
|
|
|
11996
11994
|
"APP": 85
|
|
11997
11995
|
}
|
|
11998
11996
|
},
|
|
11999
|
-
"ResourceBundleType": {
|
|
12000
|
-
"values": {
|
|
12001
|
-
"IMAGES": 0,
|
|
12002
|
-
"ANIMATION": 1,
|
|
12003
|
-
"WALLPAPER": 2,
|
|
12004
|
-
"TRANSLATIONS": 3,
|
|
12005
|
-
"ROOBERT": 4,
|
|
12006
|
-
"NOTO": 5
|
|
12007
|
-
}
|
|
12008
|
-
},
|
|
12009
11997
|
"DeviceFirmwareImageInfo": {
|
|
12010
11998
|
"fields": {
|
|
12011
11999
|
"version": {
|
|
@@ -12203,37 +12191,6 @@
|
|
|
12203
12191
|
}
|
|
12204
12192
|
}
|
|
12205
12193
|
},
|
|
12206
|
-
"ResourceInventoryGet": {
|
|
12207
|
-
"fields": {}
|
|
12208
|
-
},
|
|
12209
|
-
"ResourceInventoryItem": {
|
|
12210
|
-
"fields": {
|
|
12211
|
-
"type": {
|
|
12212
|
-
"rule": "required",
|
|
12213
|
-
"type": "ResourceBundleType",
|
|
12214
|
-
"id": 1
|
|
12215
|
-
},
|
|
12216
|
-
"size": {
|
|
12217
|
-
"rule": "required",
|
|
12218
|
-
"type": "uint32",
|
|
12219
|
-
"id": 2
|
|
12220
|
-
},
|
|
12221
|
-
"header_hash": {
|
|
12222
|
-
"rule": "required",
|
|
12223
|
-
"type": "bytes",
|
|
12224
|
-
"id": 3
|
|
12225
|
-
}
|
|
12226
|
-
}
|
|
12227
|
-
},
|
|
12228
|
-
"ResourceInventory": {
|
|
12229
|
-
"fields": {
|
|
12230
|
-
"items": {
|
|
12231
|
-
"rule": "repeated",
|
|
12232
|
-
"type": "ResourceInventoryItem",
|
|
12233
|
-
"id": 1
|
|
12234
|
-
}
|
|
12235
|
-
}
|
|
12236
|
-
},
|
|
12237
12194
|
"DeviceSessionErrorCode": {
|
|
12238
12195
|
"values": {
|
|
12239
12196
|
"DeviceSessionError_None": 0,
|
|
@@ -509,6 +509,10 @@ export default class DataManager {
|
|
|
509
509
|
return this.deviceMap[EDeviceType.Pro2]?.resources?.stable;
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
+
static getProtocolV2BootResources() {
|
|
513
|
+
return this.deviceMap[EDeviceType.Pro2]?.resources?.boot;
|
|
514
|
+
}
|
|
515
|
+
|
|
512
516
|
static getProtobufMessages(schema: ProtobufMessageSchema = 'v1CurrentSchema'): JSON {
|
|
513
517
|
return this.messages[schema];
|
|
514
518
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { sha256 } from '@noble/hashes/sha256';
|
|
2
|
+
import { PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE } from '@onekeyfe/hd-transport';
|
|
2
3
|
|
|
3
4
|
import type {
|
|
5
|
+
IProtocolV2BootResources,
|
|
4
6
|
IProtocolV2Resource,
|
|
5
7
|
IProtocolV2ResourceType,
|
|
6
8
|
IProtocolV2Resources,
|
|
7
9
|
} from '../../types';
|
|
8
10
|
import type { DeviceCommands } from '../../device/DeviceCommands';
|
|
9
|
-
import type { ResourceInventory } from '@onekeyfe/hd-transport';
|
|
10
11
|
|
|
11
12
|
export const PROTOCOL_V2_RESOURCE_TYPES = [
|
|
12
13
|
'images',
|
|
@@ -30,23 +31,16 @@ export const PROTOCOL_V2_RESOURCE_DEVICE_PATHS: Readonly<Record<IProtocolV2Resou
|
|
|
30
31
|
const RESOURCE_TYPE_SET = new Set<string>(PROTOCOL_V2_RESOURCE_TYPES);
|
|
31
32
|
const SHA256_HEX_LENGTH = 64;
|
|
32
33
|
const SHA3_512_HEX_LENGTH = 128;
|
|
34
|
+
const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
35
|
+
const PROTOCOL_V2_OKPP_TYPE_OFFSET = 0x08;
|
|
36
|
+
const PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET = 0x0c;
|
|
37
|
+
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
38
|
+
const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
39
|
+
const PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE =
|
|
40
|
+
PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET + PROTOCOL_V2_OKPP_HASH_SIZE;
|
|
41
|
+
const PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE = 64;
|
|
33
42
|
export const PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS = 5 * 1000;
|
|
34
43
|
|
|
35
|
-
const RESOURCE_TYPE_BY_DEVICE_VALUE: Readonly<Record<string, IProtocolV2ResourceType>> = {
|
|
36
|
-
'0': 'images',
|
|
37
|
-
IMAGES: 'images',
|
|
38
|
-
'1': 'animation',
|
|
39
|
-
ANIMATION: 'animation',
|
|
40
|
-
'2': 'wallpaper',
|
|
41
|
-
WALLPAPER: 'wallpaper',
|
|
42
|
-
'3': 'translations',
|
|
43
|
-
TRANSLATIONS: 'translations',
|
|
44
|
-
'4': 'roobert',
|
|
45
|
-
ROOBERT: 'roobert',
|
|
46
|
-
'5': 'noto',
|
|
47
|
-
NOTO: 'noto',
|
|
48
|
-
};
|
|
49
|
-
|
|
50
44
|
export type ProtocolV2ResourceInventoryItem = {
|
|
51
45
|
type: IProtocolV2ResourceType;
|
|
52
46
|
size: number;
|
|
@@ -60,57 +54,153 @@ export type ProtocolV2ResourceUpdatePlan = {
|
|
|
60
54
|
resources: IProtocolV2Resource[];
|
|
61
55
|
};
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
value
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (!Array.isArray(items)) {
|
|
69
|
-
throw new Error('Invalid Pro2 resource inventory: items must be an array');
|
|
57
|
+
function toFiniteNumber(value: unknown): number | undefined {
|
|
58
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
59
|
+
if (typeof value === 'string') {
|
|
60
|
+
const numeric = Number(value);
|
|
61
|
+
return Number.isFinite(numeric) ? numeric : undefined;
|
|
70
62
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
63
|
+
if (value && typeof value === 'object') {
|
|
64
|
+
const longLike = value as { toNumber?: () => number };
|
|
65
|
+
if (typeof longLike.toNumber === 'function') {
|
|
66
|
+
const numeric = longLike.toNumber();
|
|
67
|
+
return Number.isFinite(numeric) ? numeric : undefined;
|
|
75
68
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function toUint8Array(value: unknown): Uint8Array {
|
|
74
|
+
if (value instanceof Uint8Array) return value;
|
|
75
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
76
|
+
if (ArrayBuffer.isView(value)) {
|
|
77
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
78
|
+
}
|
|
79
|
+
if (typeof value === 'string') {
|
|
80
|
+
const hex = value.replace(/^0x/i, '');
|
|
81
|
+
if (!hex || hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
|
|
82
|
+
return new Uint8Array(0);
|
|
80
83
|
}
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
85
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
86
|
+
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
83
87
|
}
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
};
|
|
89
|
-
});
|
|
88
|
+
return bytes;
|
|
89
|
+
}
|
|
90
|
+
return new Uint8Array(0);
|
|
91
|
+
}
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
function readAscii(bytes: Uint8Array, offset: number, length: number): string {
|
|
94
|
+
return Array.from(bytes.slice(offset, offset + length), byte => String.fromCharCode(byte)).join(
|
|
95
|
+
''
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function parseProtocolV2ResourceHeaderHash(bytes: Uint8Array): string | undefined {
|
|
100
|
+
if (bytes.byteLength < PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE) return undefined;
|
|
101
|
+
if (readAscii(bytes, 0, 4) !== 'OKPP') return undefined;
|
|
102
|
+
if (readAscii(bytes, PROTOCOL_V2_OKPP_TYPE_OFFSET, 4) !== 'RESC') return undefined;
|
|
103
|
+
|
|
104
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
105
|
+
if (
|
|
106
|
+
view.getUint32(PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET, true) !== PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
107
|
+
) {
|
|
108
|
+
return undefined;
|
|
93
109
|
}
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
110
|
+
return bytesToHex(
|
|
111
|
+
bytes.slice(
|
|
112
|
+
PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET,
|
|
113
|
+
PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET + PROTOCOL_V2_OKPP_HASH_SIZE
|
|
114
|
+
)
|
|
115
|
+
);
|
|
98
116
|
}
|
|
99
117
|
|
|
100
|
-
|
|
118
|
+
async function readProtocolV2ResourceIdentity({
|
|
101
119
|
commands,
|
|
120
|
+
resource,
|
|
121
|
+
chunkSize,
|
|
102
122
|
timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS,
|
|
103
123
|
}: {
|
|
104
|
-
commands: DeviceCommands
|
|
124
|
+
commands: Pick<DeviceCommands, 'typedCall'>;
|
|
125
|
+
resource: IProtocolV2Resource;
|
|
126
|
+
chunkSize: number;
|
|
105
127
|
timeoutMs?: number;
|
|
106
|
-
}): Promise<ProtocolV2ResourceInventoryItem
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
'
|
|
110
|
-
|
|
128
|
+
}): Promise<ProtocolV2ResourceInventoryItem | undefined> {
|
|
129
|
+
const path = PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type];
|
|
130
|
+
const pathInfo = await commands.typedCall(
|
|
131
|
+
'FilesystemPathInfoQuery',
|
|
132
|
+
'FilesystemPathInfo',
|
|
133
|
+
{ path },
|
|
111
134
|
{ timeoutMs }
|
|
112
135
|
);
|
|
113
|
-
|
|
136
|
+
const size = toFiniteNumber(pathInfo.message?.size);
|
|
137
|
+
if (
|
|
138
|
+
!pathInfo.message?.exist ||
|
|
139
|
+
pathInfo.message?.directory ||
|
|
140
|
+
!Number.isSafeInteger(size) ||
|
|
141
|
+
size !== resource.size ||
|
|
142
|
+
size < PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
143
|
+
) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const header = new Uint8Array(PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE);
|
|
148
|
+
let offset = 0;
|
|
149
|
+
while (offset < header.byteLength) {
|
|
150
|
+
const readLength = Math.min(chunkSize, header.byteLength - offset);
|
|
151
|
+
const response = await commands.typedCall(
|
|
152
|
+
'FilesystemFileRead',
|
|
153
|
+
'FilesystemFile',
|
|
154
|
+
{
|
|
155
|
+
file: { path, offset, total_size: 0 },
|
|
156
|
+
chunk_len: readLength,
|
|
157
|
+
},
|
|
158
|
+
{ timeoutMs }
|
|
159
|
+
);
|
|
160
|
+
const data = toUint8Array(response.message?.data);
|
|
161
|
+
if (data.byteLength === 0) return undefined;
|
|
162
|
+
const copied = Math.min(data.byteLength, header.byteLength - offset);
|
|
163
|
+
header.set(data.subarray(0, copied), offset);
|
|
164
|
+
offset += copied;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const headerHash = parseProtocolV2ResourceHeaderHash(header);
|
|
168
|
+
return headerHash ? { type: resource.type, size, headerHash } : undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 使用已发布 Pro2 固件支持的文件系统消息构建资源清单。
|
|
173
|
+
* 缺失、无法读取或格式错误的文件不会进入清单,因此会被选中重写。
|
|
174
|
+
*/
|
|
175
|
+
export async function readProtocolV2ResourceInventory({
|
|
176
|
+
commands,
|
|
177
|
+
resources,
|
|
178
|
+
chunkSize = PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE,
|
|
179
|
+
timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS,
|
|
180
|
+
}: {
|
|
181
|
+
commands: Pick<DeviceCommands, 'typedCall'>;
|
|
182
|
+
resources: readonly IProtocolV2Resource[];
|
|
183
|
+
chunkSize?: number;
|
|
184
|
+
timeoutMs?: number;
|
|
185
|
+
}): Promise<ProtocolV2ResourceInventoryItem[]> {
|
|
186
|
+
const normalizedChunkSize = Number.isFinite(chunkSize)
|
|
187
|
+
? Math.max(Math.floor(chunkSize), PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE)
|
|
188
|
+
: PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
189
|
+
const inventory: ProtocolV2ResourceInventoryItem[] = [];
|
|
190
|
+
for (const resource of resources) {
|
|
191
|
+
try {
|
|
192
|
+
const item = await readProtocolV2ResourceIdentity({
|
|
193
|
+
commands,
|
|
194
|
+
resource,
|
|
195
|
+
chunkSize: normalizedChunkSize,
|
|
196
|
+
timeoutMs,
|
|
197
|
+
});
|
|
198
|
+
if (item) inventory.push(item);
|
|
199
|
+
} catch {
|
|
200
|
+
// 单个资源无法读取时按缺失处理,不阻断其余资源的增量检查。
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return inventory;
|
|
114
204
|
}
|
|
115
205
|
|
|
116
206
|
function normalizeHex(value: unknown, expectedLength: number, field: string): string {
|
|
@@ -149,6 +239,34 @@ function validateResource(value: unknown, index: number): IProtocolV2Resource {
|
|
|
149
239
|
};
|
|
150
240
|
}
|
|
151
241
|
|
|
242
|
+
function validateBootResources(value: unknown): IProtocolV2BootResources {
|
|
243
|
+
if (!value || typeof value !== 'object') {
|
|
244
|
+
throw new Error('Invalid Pro2 boot resources config');
|
|
245
|
+
}
|
|
246
|
+
const resource = value as Partial<IProtocolV2BootResources>;
|
|
247
|
+
if (resource.required !== false) {
|
|
248
|
+
throw new Error('Invalid Pro2 boot resources required flag: expected false');
|
|
249
|
+
}
|
|
250
|
+
if (resource.target !== 'CRATE') {
|
|
251
|
+
throw new Error('Invalid Pro2 boot resources target: expected CRATE');
|
|
252
|
+
}
|
|
253
|
+
if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
|
|
254
|
+
throw new Error('Invalid Pro2 boot resources url');
|
|
255
|
+
}
|
|
256
|
+
if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
|
|
257
|
+
throw new Error('Invalid Pro2 boot resources size');
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
required: false,
|
|
261
|
+
target: 'CRATE',
|
|
262
|
+
url: resource.url,
|
|
263
|
+
size: Number(resource.size),
|
|
264
|
+
fileHash: normalizeHex(resource.fileHash, SHA256_HEX_LENGTH, 'boot fileHash'),
|
|
265
|
+
payloadHash: normalizeHex(resource.payloadHash, SHA3_512_HEX_LENGTH, 'boot payloadHash'),
|
|
266
|
+
headerHash: normalizeHex(resource.headerHash, SHA3_512_HEX_LENGTH, 'boot headerHash'),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
152
270
|
/** Validate a complete Pro2 stable resource set from remote configuration. */
|
|
153
271
|
export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources | undefined {
|
|
154
272
|
if (value === undefined) return undefined;
|
|
@@ -160,7 +278,8 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
|
|
|
160
278
|
throw new Error('Invalid Pro2 resources config: stable must be an array');
|
|
161
279
|
}
|
|
162
280
|
|
|
163
|
-
const
|
|
281
|
+
const config = value as { stable: unknown[]; boot?: unknown };
|
|
282
|
+
const stable = config.stable.map(validateResource);
|
|
164
283
|
const types = new Set(stable.map(resource => resource.type));
|
|
165
284
|
if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
|
|
166
285
|
throw new Error('Invalid Pro2 resources config: stable must contain six unique resource types');
|
|
@@ -170,6 +289,7 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
|
|
|
170
289
|
throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
|
|
171
290
|
}
|
|
172
291
|
}
|
|
292
|
+
const boot = config.boot === undefined ? undefined : validateBootResources(config.boot);
|
|
173
293
|
return {
|
|
174
294
|
stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
|
|
175
295
|
const resource = stable.find(item => item.type === type);
|
|
@@ -178,10 +298,11 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
|
|
|
178
298
|
}
|
|
179
299
|
return resource;
|
|
180
300
|
}),
|
|
301
|
+
...(boot ? { boot } : undefined),
|
|
181
302
|
};
|
|
182
303
|
}
|
|
183
304
|
|
|
184
|
-
/**
|
|
305
|
+
/** 比较文件系统资源清单;恢复模式无法取得清单时回退到全量更新。 */
|
|
185
306
|
export function buildProtocolV2ResourceUpdatePlan({
|
|
186
307
|
resources,
|
|
187
308
|
inventory,
|
|
@@ -193,14 +314,19 @@ export function buildProtocolV2ResourceUpdatePlan({
|
|
|
193
314
|
mode: ProtocolV2ResourceUpdateMode;
|
|
194
315
|
forced?: boolean;
|
|
195
316
|
}): ProtocolV2ResourceUpdatePlan {
|
|
196
|
-
if (
|
|
317
|
+
if (forced) {
|
|
197
318
|
return {
|
|
198
319
|
status: resources.length > 0 ? 'outdated' : 'valid',
|
|
199
320
|
resources: [...resources],
|
|
200
321
|
};
|
|
201
322
|
}
|
|
202
323
|
if (!inventory) {
|
|
203
|
-
return
|
|
324
|
+
return mode === 'bootloader-recovery'
|
|
325
|
+
? {
|
|
326
|
+
status: resources.length > 0 ? 'outdated' : 'valid',
|
|
327
|
+
resources: [...resources],
|
|
328
|
+
}
|
|
329
|
+
: { status: 'unknown', resources: [] };
|
|
204
330
|
}
|
|
205
331
|
|
|
206
332
|
const inventoryByType = new Map(inventory.map(item => [item.type, item]));
|
|
@@ -63,6 +63,7 @@ export interface FirmwareUpdateV3Params {
|
|
|
63
63
|
*/
|
|
64
64
|
export type FirmwareUpdateV4Target =
|
|
65
65
|
| 'boot'
|
|
66
|
+
| 'boot_resources'
|
|
66
67
|
| 'app_v1'
|
|
67
68
|
| 'app_v2'
|
|
68
69
|
| 'coprocessor'
|
|
@@ -82,6 +83,8 @@ export interface FirmwareUpdateV4Params {
|
|
|
82
83
|
romloaderBinary?: ArrayBuffer;
|
|
83
84
|
/** FW_MGMT_TARGET_BOOTLOADER = 3 */
|
|
84
85
|
bootloaderBinary?: ArrayBuffer;
|
|
86
|
+
/** FW_MGMT_TARGET_CRATE = 1; optional Pro2 startup resources. */
|
|
87
|
+
bootResourcesBinary?: ArrayBuffer;
|
|
85
88
|
/** FW_MGMT_TARGET_APPLICATION_P1 = 4 */
|
|
86
89
|
applicationP1Binary?: ArrayBuffer;
|
|
87
90
|
/** FW_MGMT_TARGET_APPLICATION_P2 = 5 */
|
package/src/types/settings.ts
CHANGED
|
@@ -81,8 +81,24 @@ export type IProtocolV2Resource = {
|
|
|
81
81
|
headerHash: string;
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
+
/** Optional Pro2 startup-resource CRATE installed by the bootloader updater. */
|
|
85
|
+
export type IProtocolV2BootResources = {
|
|
86
|
+
required: false;
|
|
87
|
+
target: 'CRATE';
|
|
88
|
+
url: string;
|
|
89
|
+
/** Complete CRATE file size in bytes. */
|
|
90
|
+
size: number;
|
|
91
|
+
/** SHA-256 of the complete CRATE file. */
|
|
92
|
+
fileHash: string;
|
|
93
|
+
/** SHA3-512 payload_hash from the signed CRATE header. */
|
|
94
|
+
payloadHash: string;
|
|
95
|
+
/** SHA3-512 header_hash from the signed CRATE header. */
|
|
96
|
+
headerHash: string;
|
|
97
|
+
};
|
|
98
|
+
|
|
84
99
|
export type IProtocolV2Resources = {
|
|
85
100
|
stable: IProtocolV2Resource[];
|
|
101
|
+
boot?: IProtocolV2BootResources;
|
|
86
102
|
};
|
|
87
103
|
|
|
88
104
|
/** Pro2 RESC bundle okpkg descriptor for incremental FileWrite synchronization. */
|