@onekeyfe/hardware-cli 1.2.0-alpha.2 → 1.2.0-alpha.4
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/dist/cli.js +293 -9
- package/dist/sdk.d.ts +2 -0
- package/dist/sdk.js +5 -3
- package/dist/transports/nobleBlePlugin.d.ts +2 -0
- package/dist/transports/nobleBlePlugin.js +328 -0
- package/package.json +7 -6
- package/src/cli.ts +423 -15
- package/src/sdk.ts +11 -3
- package/src/transports/nobleBlePlugin.ts +415 -0
package/src/cli.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { Command } from 'commander';
|
|
3
|
+
import { UI_EVENT, UI_REQUEST, getDeviceType } from '@onekeyfe/hd-core';
|
|
4
|
+
import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
2
5
|
|
|
3
|
-
import { createSDK, disposeSDK } from './sdk';
|
|
4
|
-
import {
|
|
5
|
-
clearSessionFromKeychain,
|
|
6
|
-
preloadSessionFromKeychain,
|
|
7
|
-
saveSessionToKeychain,
|
|
8
|
-
} from './session';
|
|
9
6
|
import {
|
|
10
7
|
resolveBatchGetAddress,
|
|
11
8
|
resolveGetAddress,
|
|
@@ -13,9 +10,13 @@ import {
|
|
|
13
10
|
resolveSignMessage,
|
|
14
11
|
resolveSignTransaction,
|
|
15
12
|
} from './chains';
|
|
13
|
+
import { createSDK, disposeSDK } from './sdk';
|
|
14
|
+
import {
|
|
15
|
+
clearSessionFromKeychain,
|
|
16
|
+
preloadSessionFromKeychain,
|
|
17
|
+
saveSessionToKeychain,
|
|
18
|
+
} from './session';
|
|
16
19
|
|
|
17
|
-
import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
18
|
-
import { getDeviceType } from '@onekeyfe/hd-core';
|
|
19
20
|
import type {
|
|
20
21
|
EthereumSignTypedDataMessage,
|
|
21
22
|
EthereumSignTypedDataTypes,
|
|
@@ -78,8 +79,10 @@ program.option(
|
|
|
78
79
|
'--device-id <id>',
|
|
79
80
|
'Persistent device ID from getFeatures (changes when seed changes)'
|
|
80
81
|
);
|
|
82
|
+
program.option('--transport <transport>', 'Transport to use: usb or ble', 'usb');
|
|
81
83
|
program.option('--passphrase-state <state>', 'Passphrase state for hidden wallet access');
|
|
82
84
|
program.option('--use-empty-passphrase', 'Use standard wallet (skip passphrase prompt)');
|
|
85
|
+
program.option('--debug', 'Enable SDK debug logs');
|
|
83
86
|
|
|
84
87
|
// ============================================================
|
|
85
88
|
// Device Commands
|
|
@@ -92,8 +95,8 @@ program
|
|
|
92
95
|
runCommand({}, async ({ sdk, globalOpts }) => {
|
|
93
96
|
const result = await sdk.searchDevices();
|
|
94
97
|
|
|
95
|
-
//
|
|
96
|
-
if (result?.success && Array.isArray(result.payload)) {
|
|
98
|
+
// USB 下自动读取 features 成本低;BLE 搜索阶段只做枚举,避免批量连接导致超时。
|
|
99
|
+
if (globalOpts.transport !== 'ble' && result?.success && Array.isArray(result.payload)) {
|
|
97
100
|
for (const device of result.payload as EnrichedSearchDevice[]) {
|
|
98
101
|
if (device.connectId) {
|
|
99
102
|
try {
|
|
@@ -101,7 +104,7 @@ program
|
|
|
101
104
|
if (features?.success && features.payload) {
|
|
102
105
|
device.features = features.payload;
|
|
103
106
|
device.name = features.payload.label || features.payload.bleName || device.name;
|
|
104
|
-
const devType = features.payload.deviceType;
|
|
107
|
+
const devType = features.payload.deviceType?.toLowerCase();
|
|
105
108
|
if (devType) {
|
|
106
109
|
device.deviceType = devType as IDeviceType;
|
|
107
110
|
}
|
|
@@ -540,18 +543,47 @@ program
|
|
|
540
543
|
|
|
541
544
|
program
|
|
542
545
|
.command('firmware-update-ble')
|
|
543
|
-
.description('
|
|
546
|
+
.description('Run Protocol V2 firmware update over BLE')
|
|
544
547
|
.action(() =>
|
|
545
548
|
respondAndExit({
|
|
546
549
|
success: false,
|
|
547
550
|
payload: {
|
|
548
551
|
error:
|
|
549
|
-
'
|
|
550
|
-
code: '
|
|
552
|
+
'Use `onekey-hw --transport ble firmware-update-v4-debug` for BLE Protocol V2 firmware update debugging.',
|
|
553
|
+
code: 'USE_FIRMWARE_UPDATE_V4_DEBUG',
|
|
551
554
|
},
|
|
552
555
|
})
|
|
553
556
|
);
|
|
554
557
|
|
|
558
|
+
program
|
|
559
|
+
.command('firmware-update-v4-debug')
|
|
560
|
+
.description('Debug Protocol V2 firmware update through sdk.firmwareUpdateV4')
|
|
561
|
+
.option('--chunk-size <bytes>', 'Transfer chunk size in bytes')
|
|
562
|
+
.option('--resource <path>', 'FW_MGMT_TARGET_CRATE resource package path')
|
|
563
|
+
.option('--romloader <path>', 'FW_MGMT_TARGET_ROMLOADER binary path')
|
|
564
|
+
.option('--bootloader <path>', 'FW_MGMT_TARGET_BOOTLOADER binary path')
|
|
565
|
+
.option('--application-p1 <path>', 'FW_MGMT_TARGET_APPLICATION_P1 binary path')
|
|
566
|
+
.option('--application-p2 <path>', 'FW_MGMT_TARGET_APPLICATION_P2 binary path')
|
|
567
|
+
.option('--coprocessor <path>', 'FW_MGMT_TARGET_COPROCESSOR binary path')
|
|
568
|
+
.option('--se01 <path>', 'FW_MGMT_TARGET_SE01 binary path')
|
|
569
|
+
.option('--se02 <path>', 'FW_MGMT_TARGET_SE02 binary path')
|
|
570
|
+
.option('--se03 <path>', 'FW_MGMT_TARGET_SE03 binary path')
|
|
571
|
+
.option('--se04 <path>', 'FW_MGMT_TARGET_SE04 binary path')
|
|
572
|
+
.option('--forced-update-res', 'Force resource update')
|
|
573
|
+
.option('--retries <count>', 'Retry count for transient Protocol V2 USB probe failures')
|
|
574
|
+
.action(opts =>
|
|
575
|
+
runCommand({}, async ({ sdk, globalOpts }) => {
|
|
576
|
+
const params = buildFirmwareUpdateV4DebugParams(opts);
|
|
577
|
+
const result = await runFirmwareUpdateV4DebugWithRetry({
|
|
578
|
+
sdk,
|
|
579
|
+
globalOpts,
|
|
580
|
+
params,
|
|
581
|
+
retries: opts.retries ? safeParseInt(opts.retries, '--retries') : undefined,
|
|
582
|
+
});
|
|
583
|
+
outputResult(globalOpts, result);
|
|
584
|
+
})
|
|
585
|
+
);
|
|
586
|
+
|
|
555
587
|
program
|
|
556
588
|
.command('bootloader-check')
|
|
557
589
|
.description('Check bootloader version and status')
|
|
@@ -910,7 +942,17 @@ async function prepareSession(
|
|
|
910
942
|
return undefined;
|
|
911
943
|
}
|
|
912
944
|
|
|
913
|
-
const device = searchResult.payload[0] as
|
|
945
|
+
const device = searchResult.payload[0] as {
|
|
946
|
+
connectId?: string;
|
|
947
|
+
deviceId?: string;
|
|
948
|
+
features?: {
|
|
949
|
+
deviceId?: string | null;
|
|
950
|
+
deviceType?: string;
|
|
951
|
+
sessionId?: string | null;
|
|
952
|
+
passphraseProtection?: boolean | null;
|
|
953
|
+
unlocked?: boolean | null;
|
|
954
|
+
};
|
|
955
|
+
};
|
|
914
956
|
const connectId = device.connectId || globalOpts.connectId || '';
|
|
915
957
|
if (!globalOpts.connectId && connectId) {
|
|
916
958
|
globalOpts.connectId = connectId;
|
|
@@ -1061,6 +1103,9 @@ async function runCommand(
|
|
|
1061
1103
|
): Promise<void> {
|
|
1062
1104
|
const globalOpts = program.opts();
|
|
1063
1105
|
try {
|
|
1106
|
+
if (globalOpts.transport !== 'usb' && globalOpts.transport !== 'ble') {
|
|
1107
|
+
throw new Error(`Unsupported transport: ${globalOpts.transport}. Use "usb" or "ble".`);
|
|
1108
|
+
}
|
|
1064
1109
|
const sdk = await createSDK(globalOpts);
|
|
1065
1110
|
if (options.needsSession) {
|
|
1066
1111
|
await prepareSession(sdk, globalOpts);
|
|
@@ -1109,6 +1154,369 @@ function safeJsonParse(input: string, label: string): unknown {
|
|
|
1109
1154
|
}
|
|
1110
1155
|
}
|
|
1111
1156
|
|
|
1157
|
+
function readBinaryParam(path: string): ArrayBuffer {
|
|
1158
|
+
const buffer = readFileSync(path);
|
|
1159
|
+
return new Uint8Array(buffer).buffer;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
function getFirmwareUpdateV4DebugTotalBytes(
|
|
1163
|
+
params: ReturnType<typeof buildFirmwareUpdateV4DebugParams>
|
|
1164
|
+
) {
|
|
1165
|
+
return [
|
|
1166
|
+
params.resourceBinary,
|
|
1167
|
+
params.bootloaderBinary,
|
|
1168
|
+
params.applicationP1Binary,
|
|
1169
|
+
params.applicationP2Binary,
|
|
1170
|
+
params.coprocessorBinary,
|
|
1171
|
+
params.se01Binary,
|
|
1172
|
+
params.se02Binary,
|
|
1173
|
+
params.se03Binary,
|
|
1174
|
+
params.se04Binary,
|
|
1175
|
+
].reduce((total, binary) => total + (binary?.byteLength ?? 0), 0);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function getFirmwareUpdateV4DebugErrorText(result: unknown) {
|
|
1179
|
+
if (!result || typeof result !== 'object') return '';
|
|
1180
|
+
const payload = (result as { payload?: unknown }).payload;
|
|
1181
|
+
if (!payload || typeof payload !== 'object') return '';
|
|
1182
|
+
const error = (payload as { error?: unknown }).error;
|
|
1183
|
+
return typeof error === 'string' ? error : '';
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function isProtocolV2UsbProbeTransientResult(result: unknown) {
|
|
1187
|
+
const error = getFirmwareUpdateV4DebugErrorText(result);
|
|
1188
|
+
return (
|
|
1189
|
+
error.includes('Device protocol mismatch') &&
|
|
1190
|
+
error.includes('expected V2') &&
|
|
1191
|
+
error.includes('did not respond to expected protocol')
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function isSuccessResult(result: unknown) {
|
|
1196
|
+
return (
|
|
1197
|
+
!!result && typeof result === 'object' && (result as { success?: boolean }).success === true
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
function getFirmwareDebugPayload(message: unknown) {
|
|
1202
|
+
if (!message || typeof message !== 'object') return undefined;
|
|
1203
|
+
return (message as { payload?: Record<string, unknown> }).payload;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function formatFirmwareDebugProgress(progress: number) {
|
|
1207
|
+
if (!Number.isFinite(progress)) return '0%';
|
|
1208
|
+
return `${Math.min(Math.max(Math.round(progress), 0), 100)}%`;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function formatFirmwareDebugBytes(bytes: number) {
|
|
1212
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return '';
|
|
1213
|
+
return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
function maybePrintFirmwareDebugProgress({
|
|
1217
|
+
progressType,
|
|
1218
|
+
progress,
|
|
1219
|
+
payload,
|
|
1220
|
+
lastPrintedProgress,
|
|
1221
|
+
}: {
|
|
1222
|
+
progressType: string;
|
|
1223
|
+
progress: number;
|
|
1224
|
+
payload: Record<string, unknown>;
|
|
1225
|
+
lastPrintedProgress: number;
|
|
1226
|
+
}) {
|
|
1227
|
+
const printableProgress = Math.floor(progress / 10) * 10;
|
|
1228
|
+
if (printableProgress <= lastPrintedProgress && progress < 100) {
|
|
1229
|
+
return lastPrintedProgress;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
const transferredBytes = Number(payload.transferredBytes);
|
|
1233
|
+
const totalBytes = Number(payload.totalBytes);
|
|
1234
|
+
const rateBytesPerSecond = Number(payload.rateBytesPerSecond);
|
|
1235
|
+
const sizeText =
|
|
1236
|
+
Number.isFinite(transferredBytes) && Number.isFinite(totalBytes) && totalBytes > 0
|
|
1237
|
+
? ` ${formatFirmwareDebugBytes(transferredBytes)}/${formatFirmwareDebugBytes(totalBytes)}`
|
|
1238
|
+
: '';
|
|
1239
|
+
const speedText =
|
|
1240
|
+
Number.isFinite(rateBytesPerSecond) && rateBytesPerSecond > 0
|
|
1241
|
+
? ` ${(rateBytesPerSecond / 1024).toFixed(2)} KiB/s`
|
|
1242
|
+
: '';
|
|
1243
|
+
|
|
1244
|
+
process.stderr.write(
|
|
1245
|
+
`[onekey-hw] Firmware ${progressType}: ${formatFirmwareDebugProgress(
|
|
1246
|
+
progress
|
|
1247
|
+
)}${sizeText}${speedText}\n`
|
|
1248
|
+
);
|
|
1249
|
+
return progress >= 100 ? 100 : printableProgress;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
function buildFirmwareUpdateV4DebugMetrics({
|
|
1253
|
+
attempt,
|
|
1254
|
+
maxAttempts,
|
|
1255
|
+
totalBytes,
|
|
1256
|
+
totalStartedAt,
|
|
1257
|
+
transferStartedAt,
|
|
1258
|
+
transferEndedAt,
|
|
1259
|
+
installStartedAt,
|
|
1260
|
+
installEndedAt,
|
|
1261
|
+
progressEvents,
|
|
1262
|
+
lastProgress,
|
|
1263
|
+
installProgressEvents,
|
|
1264
|
+
lastInstallProgress,
|
|
1265
|
+
retried,
|
|
1266
|
+
}: {
|
|
1267
|
+
attempt: number;
|
|
1268
|
+
maxAttempts: number;
|
|
1269
|
+
totalBytes: number;
|
|
1270
|
+
totalStartedAt: number;
|
|
1271
|
+
transferStartedAt?: number;
|
|
1272
|
+
transferEndedAt?: number;
|
|
1273
|
+
installStartedAt?: number;
|
|
1274
|
+
installEndedAt?: number;
|
|
1275
|
+
progressEvents: number;
|
|
1276
|
+
lastProgress: number;
|
|
1277
|
+
installProgressEvents: number;
|
|
1278
|
+
lastInstallProgress: number;
|
|
1279
|
+
retried: boolean;
|
|
1280
|
+
}) {
|
|
1281
|
+
const totalElapsedMs = Date.now() - totalStartedAt;
|
|
1282
|
+
const transferElapsedMs =
|
|
1283
|
+
transferStartedAt !== undefined && transferEndedAt !== undefined
|
|
1284
|
+
? transferEndedAt - transferStartedAt
|
|
1285
|
+
: undefined;
|
|
1286
|
+
const installElapsedMs =
|
|
1287
|
+
installStartedAt !== undefined && installEndedAt !== undefined
|
|
1288
|
+
? installEndedAt - installStartedAt
|
|
1289
|
+
: undefined;
|
|
1290
|
+
|
|
1291
|
+
return {
|
|
1292
|
+
attempt,
|
|
1293
|
+
maxAttempts,
|
|
1294
|
+
retried,
|
|
1295
|
+
totalBytes,
|
|
1296
|
+
progressEvents,
|
|
1297
|
+
lastProgress,
|
|
1298
|
+
installProgressEvents,
|
|
1299
|
+
lastInstallProgress,
|
|
1300
|
+
transferSeconds:
|
|
1301
|
+
transferElapsedMs !== undefined ? Number((transferElapsedMs / 1000).toFixed(2)) : null,
|
|
1302
|
+
transferKiBPerSecond:
|
|
1303
|
+
transferElapsedMs !== undefined && transferElapsedMs > 0
|
|
1304
|
+
? Number((totalBytes / 1024 / (transferElapsedMs / 1000)).toFixed(2))
|
|
1305
|
+
: null,
|
|
1306
|
+
installSeconds:
|
|
1307
|
+
installElapsedMs !== undefined ? Number((installElapsedMs / 1000).toFixed(2)) : null,
|
|
1308
|
+
totalSeconds: Number((totalElapsedMs / 1000).toFixed(2)),
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
async function runFirmwareUpdateV4DebugWithRetry({
|
|
1313
|
+
sdk,
|
|
1314
|
+
globalOpts,
|
|
1315
|
+
params,
|
|
1316
|
+
retries,
|
|
1317
|
+
}: {
|
|
1318
|
+
sdk: AnySdk;
|
|
1319
|
+
globalOpts: Record<string, any>;
|
|
1320
|
+
params: ReturnType<typeof buildFirmwareUpdateV4DebugParams>;
|
|
1321
|
+
retries?: number;
|
|
1322
|
+
}) {
|
|
1323
|
+
const totalBytes = getFirmwareUpdateV4DebugTotalBytes(params);
|
|
1324
|
+
const maxAttempts = Math.max((retries ?? 2) + 1, 1);
|
|
1325
|
+
let currentSdk = sdk;
|
|
1326
|
+
let lastResult: unknown;
|
|
1327
|
+
let retried = false;
|
|
1328
|
+
|
|
1329
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1330
|
+
let progressEvents = 0;
|
|
1331
|
+
let lastProgress = -1;
|
|
1332
|
+
let transferStartedAt: number | undefined;
|
|
1333
|
+
let transferEndedAt: number | undefined;
|
|
1334
|
+
let installProgressEvents = 0;
|
|
1335
|
+
let lastInstallProgress = -1;
|
|
1336
|
+
let installStartedAt: number | undefined;
|
|
1337
|
+
let installEndedAt: number | undefined;
|
|
1338
|
+
let lastPrintedTransferProgress = -10;
|
|
1339
|
+
let lastPrintedInstallProgress = -10;
|
|
1340
|
+
const totalStartedAt = Date.now();
|
|
1341
|
+
const connectId =
|
|
1342
|
+
retried && globalOpts.transport === 'usb' && globalOpts.connectId
|
|
1343
|
+
? undefined
|
|
1344
|
+
: globalOpts.connectId;
|
|
1345
|
+
|
|
1346
|
+
const onUiEvent = (message: unknown) => {
|
|
1347
|
+
if (!message || typeof message !== 'object') return;
|
|
1348
|
+
const messageType = (message as { type?: string }).type;
|
|
1349
|
+
const payload = getFirmwareDebugPayload(message);
|
|
1350
|
+
|
|
1351
|
+
if (messageType === UI_REQUEST.FIRMWARE_TIP) {
|
|
1352
|
+
const tipMessage = (payload?.data as { message?: unknown } | undefined)?.message;
|
|
1353
|
+
if (typeof tipMessage === 'string') {
|
|
1354
|
+
process.stderr.write(`[onekey-hw] Firmware: ${tipMessage}\n`);
|
|
1355
|
+
}
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
if (messageType === UI_REQUEST.REQUEST_BUTTON) {
|
|
1360
|
+
const code = typeof payload?.code === 'string' ? ` (${payload.code})` : '';
|
|
1361
|
+
process.stderr.write(
|
|
1362
|
+
`[onekey-hw] Please confirm the firmware update on your device${code}.\n`
|
|
1363
|
+
);
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
if (messageType !== UI_REQUEST.FIRMWARE_PROGRESS || !payload) return;
|
|
1368
|
+
const progress = Number(payload.progress);
|
|
1369
|
+
if (!Number.isFinite(progress)) return;
|
|
1370
|
+
|
|
1371
|
+
if (payload.progressType === 'transferData') {
|
|
1372
|
+
progressEvents += 1;
|
|
1373
|
+
lastProgress = Math.max(lastProgress, progress);
|
|
1374
|
+
transferStartedAt ??= Date.now();
|
|
1375
|
+
lastPrintedTransferProgress = maybePrintFirmwareDebugProgress({
|
|
1376
|
+
progressType: 'transfer',
|
|
1377
|
+
progress,
|
|
1378
|
+
payload,
|
|
1379
|
+
lastPrintedProgress: lastPrintedTransferProgress,
|
|
1380
|
+
});
|
|
1381
|
+
if (progress >= 100) {
|
|
1382
|
+
transferEndedAt ??= Date.now();
|
|
1383
|
+
}
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
if (payload.progressType === 'installingFirmware') {
|
|
1388
|
+
installProgressEvents += 1;
|
|
1389
|
+
lastInstallProgress = Math.max(lastInstallProgress, progress);
|
|
1390
|
+
installStartedAt ??= Date.now();
|
|
1391
|
+
lastPrintedInstallProgress = maybePrintFirmwareDebugProgress({
|
|
1392
|
+
progressType: 'install',
|
|
1393
|
+
progress,
|
|
1394
|
+
payload,
|
|
1395
|
+
lastPrintedProgress: lastPrintedInstallProgress,
|
|
1396
|
+
});
|
|
1397
|
+
if (progress >= 100) {
|
|
1398
|
+
installEndedAt ??= Date.now();
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
currentSdk.on(UI_EVENT, onUiEvent);
|
|
1404
|
+
try {
|
|
1405
|
+
lastResult = await currentSdk.firmwareUpdateV4(connectId, params);
|
|
1406
|
+
} finally {
|
|
1407
|
+
currentSdk.off?.(UI_EVENT, onUiEvent);
|
|
1408
|
+
}
|
|
1409
|
+
if (installStartedAt !== undefined && installEndedAt === undefined) {
|
|
1410
|
+
installEndedAt = Date.now();
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
const debugMetrics = buildFirmwareUpdateV4DebugMetrics({
|
|
1414
|
+
attempt,
|
|
1415
|
+
maxAttempts,
|
|
1416
|
+
totalBytes,
|
|
1417
|
+
totalStartedAt,
|
|
1418
|
+
transferStartedAt,
|
|
1419
|
+
transferEndedAt,
|
|
1420
|
+
installStartedAt,
|
|
1421
|
+
installEndedAt,
|
|
1422
|
+
progressEvents,
|
|
1423
|
+
lastProgress,
|
|
1424
|
+
installProgressEvents,
|
|
1425
|
+
lastInstallProgress,
|
|
1426
|
+
retried,
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
if (lastResult && typeof lastResult === 'object') {
|
|
1430
|
+
const payload = ((lastResult as { payload?: unknown }).payload ?? {}) as Record<
|
|
1431
|
+
string,
|
|
1432
|
+
unknown
|
|
1433
|
+
>;
|
|
1434
|
+
lastResult = {
|
|
1435
|
+
...(lastResult as Record<string, unknown>),
|
|
1436
|
+
payload: {
|
|
1437
|
+
...payload,
|
|
1438
|
+
_debug: debugMetrics,
|
|
1439
|
+
},
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (isSuccessResult(lastResult)) {
|
|
1444
|
+
return lastResult;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
if (
|
|
1448
|
+
attempt >= maxAttempts ||
|
|
1449
|
+
globalOpts.transport !== 'usb' ||
|
|
1450
|
+
!isProtocolV2UsbProbeTransientResult(lastResult)
|
|
1451
|
+
) {
|
|
1452
|
+
return lastResult;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
retried = true;
|
|
1456
|
+
process.stderr.write(
|
|
1457
|
+
`[onekey-hw] Protocol V2 USB probe was transient; retrying firmwareUpdateV4 (${attempt}/${maxAttempts})...\n`
|
|
1458
|
+
);
|
|
1459
|
+
await disposeSDK();
|
|
1460
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
1461
|
+
currentSdk = await createSDK(globalOpts);
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
return lastResult;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
function buildFirmwareUpdateV4DebugParams(opts: {
|
|
1468
|
+
chunkSize?: string;
|
|
1469
|
+
resource?: string;
|
|
1470
|
+
romloader?: string;
|
|
1471
|
+
bootloader?: string;
|
|
1472
|
+
applicationP1?: string;
|
|
1473
|
+
applicationP2?: string;
|
|
1474
|
+
coprocessor?: string;
|
|
1475
|
+
se01?: string;
|
|
1476
|
+
se02?: string;
|
|
1477
|
+
se03?: string;
|
|
1478
|
+
se04?: string;
|
|
1479
|
+
forcedUpdateRes?: boolean;
|
|
1480
|
+
}) {
|
|
1481
|
+
const params = {
|
|
1482
|
+
platform: 'desktop' as const,
|
|
1483
|
+
connectProtocol: 'V2' as const,
|
|
1484
|
+
chunkSize: opts.chunkSize ? safeParseInt(opts.chunkSize, '--chunk-size') : undefined,
|
|
1485
|
+
forcedUpdateRes: opts.forcedUpdateRes,
|
|
1486
|
+
resourceBinary: opts.resource ? readBinaryParam(opts.resource) : undefined,
|
|
1487
|
+
romloaderBinary: opts.romloader ? readBinaryParam(opts.romloader) : undefined,
|
|
1488
|
+
bootloaderBinary: opts.bootloader ? readBinaryParam(opts.bootloader) : undefined,
|
|
1489
|
+
applicationP1Binary: opts.applicationP1 ? readBinaryParam(opts.applicationP1) : undefined,
|
|
1490
|
+
applicationP2Binary: opts.applicationP2 ? readBinaryParam(opts.applicationP2) : undefined,
|
|
1491
|
+
coprocessorBinary: opts.coprocessor ? readBinaryParam(opts.coprocessor) : undefined,
|
|
1492
|
+
se01Binary: opts.se01 ? readBinaryParam(opts.se01) : undefined,
|
|
1493
|
+
se02Binary: opts.se02 ? readBinaryParam(opts.se02) : undefined,
|
|
1494
|
+
se03Binary: opts.se03 ? readBinaryParam(opts.se03) : undefined,
|
|
1495
|
+
se04Binary: opts.se04 ? readBinaryParam(opts.se04) : undefined,
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
const hasPayload = [
|
|
1499
|
+
params.resourceBinary,
|
|
1500
|
+
params.romloaderBinary,
|
|
1501
|
+
params.bootloaderBinary,
|
|
1502
|
+
params.applicationP1Binary,
|
|
1503
|
+
params.applicationP2Binary,
|
|
1504
|
+
params.coprocessorBinary,
|
|
1505
|
+
params.se01Binary,
|
|
1506
|
+
params.se02Binary,
|
|
1507
|
+
params.se03Binary,
|
|
1508
|
+
params.se04Binary,
|
|
1509
|
+
].some(Boolean);
|
|
1510
|
+
|
|
1511
|
+
if (!hasPayload) {
|
|
1512
|
+
const err = new Error('firmware-update-v4-debug requires at least one binary path');
|
|
1513
|
+
(err as Error & { code?: string }).code = 'MISSING_FIRMWARE_BINARY';
|
|
1514
|
+
throw err;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
return params;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1112
1520
|
/**
|
|
1113
1521
|
* #9 FIX: Safe parseInt with NaN check
|
|
1114
1522
|
*/
|
package/src/sdk.ts
CHANGED
|
@@ -14,6 +14,7 @@ import HardwareSDK from '@onekeyfe/hd-common-connect-sdk';
|
|
|
14
14
|
import { DEVICE, UI_EVENT, UI_REQUEST, UI_RESPONSE } from '@onekeyfe/hd-core';
|
|
15
15
|
|
|
16
16
|
import { promptPassphraseViaPinentry } from './pinentry';
|
|
17
|
+
import { createNobleBlePlugin } from './transports/nobleBlePlugin';
|
|
17
18
|
|
|
18
19
|
import type { ConnectSettings } from '@onekeyfe/hd-core';
|
|
19
20
|
import type { PinentryResult } from './pinentry';
|
|
@@ -22,6 +23,8 @@ export interface SDKOptions {
|
|
|
22
23
|
connectId?: string;
|
|
23
24
|
passphraseState?: string;
|
|
24
25
|
useEmptyPassphrase?: boolean;
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
transport?: 'usb' | 'ble';
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
/**
|
|
@@ -182,12 +185,17 @@ function registerEventHandlers(sdk: typeof HardwareSDK): void {
|
|
|
182
185
|
// ---------------------------------------------------------------------------
|
|
183
186
|
|
|
184
187
|
async function initSDK(): Promise<typeof HardwareSDK> {
|
|
188
|
+
const transport = currentOpts.transport ?? 'usb';
|
|
185
189
|
const settings: Partial<ConnectSettings> = {
|
|
186
|
-
debug: false,
|
|
190
|
+
debug: currentOpts.debug ?? false,
|
|
187
191
|
fetchConfig: true,
|
|
188
|
-
env: 'node-usb',
|
|
192
|
+
env: transport === 'ble' ? 'lowlevel' : 'node-usb',
|
|
189
193
|
};
|
|
190
|
-
await HardwareSDK.init(
|
|
194
|
+
await HardwareSDK.init(
|
|
195
|
+
settings,
|
|
196
|
+
undefined,
|
|
197
|
+
transport === 'ble' ? createNobleBlePlugin() : undefined
|
|
198
|
+
);
|
|
191
199
|
|
|
192
200
|
// Defensive: strip any stale listeners (e.g. left over from a previous
|
|
193
201
|
// dispose/init cycle in a long-running process) before wiring ours.
|