@onekeyfe/hd-core 1.2.0-alpha.98 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +20 -4
- package/__tests__/DeviceCommands.test.ts +203 -27
- package/__tests__/base64Data.test.ts +69 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +89 -38
- package/__tests__/check-firmware-release-protocol-v2.test.ts +83 -14
- package/__tests__/device-connector-protocol.test.ts +21 -0
- package/__tests__/device-lifecycle-events.test.ts +427 -7
- package/__tests__/device-pool-state.test.ts +25 -0
- package/__tests__/device-settings.test.ts +237 -9
- package/__tests__/device-state-events.test.ts +2 -2
- package/__tests__/device-state-mapper.test.ts +61 -5
- package/__tests__/device-utils.test.ts +15 -1
- package/__tests__/device-wallet-session-store.test.ts +147 -21
- package/__tests__/deviceSettings.test.ts +7 -3
- package/__tests__/deviceUploadNft.test.ts +36 -4
- package/__tests__/firmware-update/device-update-bootloader.test.ts +14 -3
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +61 -9
- package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +19 -9
- package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +21 -0
- package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +128 -2
- package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +80 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +114 -50
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +19 -5
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +116 -0
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +678 -0
- package/__tests__/firmware-update/firmware-update-v4-parameters.test.ts +85 -0
- package/__tests__/get-device-state.test.ts +60 -21
- package/__tests__/logBlockEvent.test.ts +13 -1
- package/__tests__/open-wallet-session.test.ts +127 -1
- package/__tests__/protocol-v2-firmware-targets.test.ts +39 -0
- package/__tests__/protocol-v2-resources.test.ts +185 -88
- package/__tests__/protocol-v2-unlock-policy.test.ts +235 -2
- package/__tests__/protocol-v2.test.ts +2274 -466
- package/__tests__/refresh-device-state.test.ts +3 -1
- package/__tests__/resourceBase64Boundary.test.ts +48 -0
- package/__tests__/search-devices.test.ts +79 -32
- package/__tests__/ton-sign-message.test.ts +84 -0
- package/dist/api/BaseMethod.d.ts +2 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +22 -7
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/device/DeviceVerify.d.ts +1 -0
- package/dist/api/device/DeviceVerify.d.ts.map +1 -1
- package/dist/api/device/DeviceWipe.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +5 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -1
- package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -15
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +1 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +3 -2
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
- package/dist/api/helpers/base64Data.d.ts +16 -0
- package/dist/api/helpers/base64Data.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +3 -1
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceReboot.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +2 -3
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +2 -3
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
- package/dist/core/index.d.ts +6 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +4 -3
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/TransportManager.d.ts.map +1 -1
- package/dist/device/Device.d.ts +16 -3
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +4 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +3 -0
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +54 -84
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2333 -1515
- package/dist/protocols/protocol-v2/features.d.ts +13 -5
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +12 -7
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +3 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +1 -1
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/settings.d.ts +3 -31
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts +1 -0
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +7 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -1
- package/package.json +6 -4
- package/src/api/BaseMethod.ts +8 -1
- package/src/api/CheckAllFirmwareRelease.ts +8 -5
- package/src/api/CheckBLEFirmwareRelease.ts +2 -2
- package/src/api/CheckBootloaderRelease.ts +3 -2
- package/src/api/CheckFirmwareRelease.ts +2 -2
- package/src/api/FirmwareUpdateV2.ts +130 -69
- package/src/api/FirmwareUpdateV3.ts +29 -19
- package/src/api/FirmwareUpdateV4.ts +966 -743
- package/src/api/OpenWalletSession.ts +39 -9
- package/src/api/PromptWebDeviceAccess.ts +2 -2
- package/src/api/SearchDevices.ts +6 -3
- package/src/api/UploadPortfolio.ts +9 -2
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +2 -1
- package/src/api/device/DeviceChangePin.ts +4 -1
- package/src/api/device/DeviceRebootToBoardloader.ts +3 -1
- package/src/api/device/DeviceRebootToBootloader.ts +3 -1
- package/src/api/device/DeviceSettings.ts +34 -20
- package/src/api/device/DeviceVerify.ts +25 -0
- package/src/api/device/DeviceWipe.ts +4 -1
- package/src/api/firmware/FirmwareArtifactSource.ts +41 -14
- package/src/api/firmware/FirmwareHostBinding.ts +10 -0
- package/src/api/firmware/FirmwarePreparedResourceArchive.ts +16 -27
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +131 -76
- package/src/api/firmware/FirmwareUpdatePlan.ts +55 -82
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +32 -15
- package/src/api/firmware/protocolV2Release.ts +7 -2
- package/src/api/helpers/base64Data.ts +85 -0
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
- package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +6 -28
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
- package/src/api/protocol-v2/DeviceInfoGet.ts +3 -3
- package/src/api/protocol-v2/DeviceReboot.ts +5 -0
- package/src/api/protocol-v2/DeviceUploadNft.ts +61 -9
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +53 -19
- package/src/api/protocol-v2/helpers.ts +8 -0
- package/src/api/ton/TonSignMessage.ts +5 -3
- package/src/core/index.ts +264 -55
- package/src/data/messages/messages-protocol-v2.json +140 -13
- package/src/data-manager/DataManager.ts +46 -39
- package/src/data-manager/TransportManager.ts +3 -1
- package/src/device/Device.ts +210 -44
- package/src/device/DeviceCommands.ts +27 -18
- package/src/device/DeviceConnector.ts +17 -3
- package/src/device/DevicePool.ts +9 -2
- package/src/device/DeviceStateMapper.ts +30 -24
- package/src/deviceProfile/buildDeviceFeatures.ts +10 -5
- package/src/events/logBlockEvent.ts +14 -1
- package/src/events/ui-request.ts +3 -0
- package/src/index.ts +0 -10
- package/src/protocols/protocol-v2/features.ts +22 -5
- package/src/protocols/protocol-v2/index.ts +2 -0
- package/src/protocols/protocol-v2/resources.ts +110 -147
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +13 -1
- package/src/protocols/protocol-v2/walletSession.ts +65 -22
- package/src/types/api/firmwareUpdate.ts +1 -1
- package/src/types/api/firmwareUpdatePlan.ts +2 -2
- package/src/types/api/protocolV2.ts +1 -1
- package/src/types/device.ts +2 -1
- package/src/types/settings.ts +6 -34
- package/src/utils/deviceFeaturesCompat.ts +9 -4
- package/src/utils/deviceFeaturesUtils.ts +4 -0
- package/src/utils/deviceInfoUtils.ts +9 -2
- package/src/utils/deviceSettings.ts +11 -4
- package/src/utils/index.ts +1 -0
- package/src/utils/pro2Nft.ts +31 -8
- package/__tests__/firmware-memory-host.test.ts +0 -112
- package/dist/api/firmware/FirmwareMemoryHost.d.ts +0 -22
- package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +0 -1
- package/src/api/firmware/FirmwareMemoryHost.ts +0 -142
|
@@ -29,6 +29,12 @@ const wasResumed = (session: unknown) =>
|
|
|
29
29
|
'resumed' in session &&
|
|
30
30
|
(session as { resumed?: unknown }).resumed === true;
|
|
31
31
|
|
|
32
|
+
const wasWalletStatusRefreshed = (session: unknown) =>
|
|
33
|
+
!!session &&
|
|
34
|
+
typeof session === 'object' &&
|
|
35
|
+
'walletStatusRefreshed' in session &&
|
|
36
|
+
(session as { walletStatusRefreshed?: unknown }).walletStatusRefreshed === true;
|
|
37
|
+
|
|
32
38
|
const requireHiddenWalletResponse = (session: { passphraseState?: string }) => {
|
|
33
39
|
if (!session.passphraseState) {
|
|
34
40
|
throw ERRORS.TypedError(
|
|
@@ -87,12 +93,20 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
87
93
|
this.unlockPolicy = 'unlock-before-run';
|
|
88
94
|
this.skipForceUpdateCheck = true;
|
|
89
95
|
this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
|
|
96
|
+
this.protocolV2PreUnlockPinType =
|
|
97
|
+
this.params.mode === OpenWalletSessionMode.SelectHidden
|
|
98
|
+
? DeviceSessionPinType.Any
|
|
99
|
+
: DeviceSessionPinType.Main;
|
|
90
100
|
this.payload.useEmptyPassphrase = this.params.mode === OpenWalletSessionMode.Standard;
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
async run(): Promise<OpenWalletSessionPayload> {
|
|
94
104
|
const isProtocolV2 = this.device.isProtocolV2();
|
|
95
|
-
|
|
105
|
+
const reusePreflightStatus =
|
|
106
|
+
isProtocolV2 && this.protocolV2UnlockContext?.preflightStatusRefreshed === true;
|
|
107
|
+
let state = reusePreflightStatus
|
|
108
|
+
? await this.device.getDeviceState()
|
|
109
|
+
: await this.device.getDeviceState({ refreshSections: ['status'] });
|
|
96
110
|
let currentDeviceId = state.identity.deviceId;
|
|
97
111
|
const hasAuthoritativeProtocolV2WalletStatus = (candidate: typeof state) =>
|
|
98
112
|
candidate.status.unlocked === true &&
|
|
@@ -115,14 +129,25 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
115
129
|
currentDeviceId = state.identity.deviceId;
|
|
116
130
|
return state;
|
|
117
131
|
};
|
|
132
|
+
const resolveProtocolV2DeviceStateAfterSession = async (session: unknown) => {
|
|
133
|
+
if (!wasWalletStatusRefreshed(session)) {
|
|
134
|
+
return refreshProtocolV2DeviceState();
|
|
135
|
+
}
|
|
136
|
+
state = await this.device.getDeviceState();
|
|
137
|
+
currentDeviceId = state.identity.deviceId;
|
|
138
|
+
return state;
|
|
139
|
+
};
|
|
118
140
|
const ensureProtocolV2WalletStatus = async () => {
|
|
119
141
|
if (isProtocolV2 && !hasAuthoritativeProtocolV2WalletStatus(state)) {
|
|
120
|
-
await this.device.unlockDevice(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
142
|
+
await this.device.unlockDevice(
|
|
143
|
+
this.protocolV2PreUnlockPinType ?? DeviceSessionPinType.Main,
|
|
144
|
+
{
|
|
145
|
+
source: 'unlock-coordinator',
|
|
146
|
+
reason: 'device-locked',
|
|
147
|
+
deviceOnly: true,
|
|
148
|
+
method: 'openWalletSession',
|
|
149
|
+
}
|
|
150
|
+
);
|
|
126
151
|
requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState());
|
|
127
152
|
}
|
|
128
153
|
return state;
|
|
@@ -139,13 +164,16 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
139
164
|
selectMainWalletBeforeRestore:
|
|
140
165
|
!hasAuthoritativeProtocolV2WalletStatus(state) ||
|
|
141
166
|
state.status.unlockedAttachPin === true,
|
|
167
|
+
mainPinSelected: this.protocolV2UnlockContext?.preflightMainPinSelected,
|
|
142
168
|
})
|
|
143
169
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, {
|
|
144
170
|
onlyMainPin: true,
|
|
145
171
|
initSession: this.payload.initSession,
|
|
146
172
|
});
|
|
147
173
|
const refreshedState = isProtocolV2
|
|
148
|
-
? requireAuthoritativeProtocolV2WalletStatus(
|
|
174
|
+
? requireAuthoritativeProtocolV2WalletStatus(
|
|
175
|
+
await resolveProtocolV2DeviceStateAfterSession(session)
|
|
176
|
+
)
|
|
149
177
|
: state;
|
|
150
178
|
const deviceId = requireDeviceId();
|
|
151
179
|
if (
|
|
@@ -233,7 +261,9 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
233
261
|
})
|
|
234
262
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
|
|
235
263
|
const refreshedState = isProtocolV2
|
|
236
|
-
? requireAuthoritativeProtocolV2WalletStatus(
|
|
264
|
+
? requireAuthoritativeProtocolV2WalletStatus(
|
|
265
|
+
await resolveProtocolV2DeviceStateAfterSession(session)
|
|
266
|
+
)
|
|
237
267
|
: state;
|
|
238
268
|
const deviceId = requireDeviceId();
|
|
239
269
|
if (isProtocolV2 && refreshedState.status.passphraseProtection !== true) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { ERRORS, HardwareErrorCode, resolveOneKeyUsbDevicePath } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import { BaseMethod } from './BaseMethod';
|
|
4
4
|
import TransportManager from '../data-manager/TransportManager';
|
|
@@ -52,7 +52,7 @@ export default class PromptWebDeviceAccess extends BaseMethod {
|
|
|
52
52
|
|
|
53
53
|
if (isWebUsbEnv) {
|
|
54
54
|
const usbDevice = device as USBDevice;
|
|
55
|
-
const path = usbDevice
|
|
55
|
+
const path = resolveOneKeyUsbDevicePath(usbDevice);
|
|
56
56
|
if (!path) {
|
|
57
57
|
throw ERRORS.TypedError(HardwareErrorCode.WebDevicePromptAccessError);
|
|
58
58
|
}
|
package/src/api/SearchDevices.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { canonicalizePro2BleAdvertisementName } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
1
3
|
import { BaseMethod } from './BaseMethod';
|
|
2
4
|
import TransportManager from '../data-manager/TransportManager';
|
|
3
5
|
import { DataManager } from '../data-manager';
|
|
@@ -36,14 +38,15 @@ export default class SearchDevices extends BaseMethod {
|
|
|
36
38
|
const lowerId = device.id?.toLowerCase();
|
|
37
39
|
if (!seenIds.has(lowerId)) {
|
|
38
40
|
seenIds.add(lowerId);
|
|
39
|
-
const
|
|
41
|
+
const rawBleName =
|
|
40
42
|
device.name ?? (device as unknown as { localName?: string }).localName ?? '';
|
|
43
|
+
const bleName = canonicalizePro2BleAdvertisementName(rawBleName);
|
|
41
44
|
devices.push({
|
|
42
45
|
...device,
|
|
43
46
|
connectId: device.id,
|
|
44
47
|
serialNo: null,
|
|
45
|
-
//
|
|
46
|
-
uuid:
|
|
48
|
+
// BLE discovery cannot provide a physical-device identity before initialization.
|
|
49
|
+
uuid: '',
|
|
47
50
|
deviceId: null,
|
|
48
51
|
name: bleName || device.name,
|
|
49
52
|
deviceType: getDeviceTypeByBleName(bleName),
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { createDeviceNotSupportMethodError } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import { supportsProtocolV2Message } from '../protocols/protocol-v2/features';
|
|
4
|
+
import { decodeCanonicalBase64 } from './helpers/base64Data';
|
|
4
5
|
import FileWrite from './FileWrite';
|
|
5
6
|
|
|
6
7
|
export type UploadPortfolioParams = {
|
|
7
|
-
|
|
8
|
+
packageBase64: string;
|
|
8
9
|
timeoutMs?: number | string;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
|
|
12
13
|
const PORTFOLIO_CHUNK_SIZE = 2048;
|
|
14
|
+
const PORTFOLIO_PACKAGE_MAX_BYTES = 128 * 1024;
|
|
13
15
|
const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
|
|
14
16
|
const PORTFOLIO_UPDATE_MESSAGE_TYPE = 61400;
|
|
15
17
|
|
|
16
18
|
export default class UploadPortfolio extends FileWrite {
|
|
17
19
|
init() {
|
|
18
|
-
const {
|
|
20
|
+
const { packageBase64, timeoutMs } = this.payload as UploadPortfolioParams;
|
|
21
|
+
const packageBytes = decodeCanonicalBase64({
|
|
22
|
+
value: packageBase64,
|
|
23
|
+
parameterName: 'packageBase64',
|
|
24
|
+
maxBytes: PORTFOLIO_PACKAGE_MAX_BYTES,
|
|
25
|
+
});
|
|
19
26
|
this.payload = {
|
|
20
27
|
...this.payload,
|
|
21
28
|
path: PORTFOLIO_PENDING_PATH,
|
|
@@ -400,7 +400,8 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
|
|
|
400
400
|
this.payload.passphraseState,
|
|
401
401
|
useEmptyPassphrase,
|
|
402
402
|
this.payload.skipPassphraseCheck,
|
|
403
|
-
deriveCardano
|
|
403
|
+
deriveCardano,
|
|
404
|
+
this.protocolV2UnlockContext?.preflightMainPinSelected
|
|
404
405
|
);
|
|
405
406
|
if (!passphraseStateSafety) {
|
|
406
407
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
1
|
+
import { DeviceSessionPinType, DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
2
2
|
|
|
3
3
|
import { BaseMethod } from '../BaseMethod';
|
|
4
4
|
import { validateParams } from '../helpers/paramsValidator';
|
|
@@ -13,6 +13,9 @@ export default class DeviceChangePin extends BaseMethod<ChangePin> {
|
|
|
13
13
|
|
|
14
14
|
init() {
|
|
15
15
|
this.unlockPolicy = 'unlock-before-run';
|
|
16
|
+
// Protocol V2 device-management actions are not wallet-scoped, so either
|
|
17
|
+
// the main PIN or an Attach PIN may authorize them.
|
|
18
|
+
this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
|
|
16
19
|
this.useDevicePassphraseState = false;
|
|
17
20
|
|
|
18
21
|
// check payload
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeviceRebootType } from '@onekeyfe/hd-transport';
|
|
1
|
+
import { DeviceRebootType, DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
2
2
|
|
|
3
3
|
import { BaseMethod } from '../BaseMethod';
|
|
4
4
|
|
|
@@ -13,6 +13,8 @@ export default class DeviceRebootToBoardloader extends BaseMethod<RebootToBoardl
|
|
|
13
13
|
init() {
|
|
14
14
|
this.useDevicePassphraseState = false;
|
|
15
15
|
this.skipForceUpdateCheck = true;
|
|
16
|
+
this.unlockPolicy = 'unlock-before-run';
|
|
17
|
+
this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
getVersionRange() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeviceRebootType } from '@onekeyfe/hd-transport';
|
|
1
|
+
import { DeviceRebootType, DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
2
2
|
|
|
3
3
|
import { BaseMethod } from '../BaseMethod';
|
|
4
4
|
|
|
@@ -13,6 +13,8 @@ export default class DeviceRebootToBootloader extends BaseMethod<RebootToBootloa
|
|
|
13
13
|
init() {
|
|
14
14
|
this.useDevicePassphraseState = false;
|
|
15
15
|
this.skipForceUpdateCheck = true;
|
|
16
|
+
this.unlockPolicy = 'unlock-before-run';
|
|
17
|
+
this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
getVersionRange() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HardwareErrorCode, TypedError } from '@onekeyfe/hd-shared';
|
|
2
|
-
import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
2
|
+
import { DeviceSessionPinType, DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
4
|
import { BaseMethod } from '../BaseMethod';
|
|
5
5
|
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '../../device/DeviceStateMapper';
|
|
11
11
|
import { getProtocolV2SettingsBehavior } from '../../protocols/protocol-v2/settingsUnlockPolicy';
|
|
12
12
|
import {
|
|
13
|
+
DEVICE_SETTINGS_NEVER_TIMEOUT_MS,
|
|
13
14
|
DEVICE_SETTINGS_V1_ONLY_FIELDS,
|
|
14
15
|
DEVICE_SETTINGS_V2_ONLY_FIELDS,
|
|
15
16
|
LANGUAGE_LABELS,
|
|
@@ -30,6 +31,9 @@ const assertSettingsSupported = (
|
|
|
30
31
|
}
|
|
31
32
|
};
|
|
32
33
|
|
|
34
|
+
const normalizeProtocolV1DelayMs = (value: number | undefined) =>
|
|
35
|
+
value === 0 ? DEVICE_SETTINGS_NEVER_TIMEOUT_MS : value;
|
|
36
|
+
|
|
33
37
|
const assertProtocolV2SettingValues = (
|
|
34
38
|
payload: DeviceSettingsParams,
|
|
35
39
|
capabilities: ReturnType<typeof getDeviceSettingsCapabilities>
|
|
@@ -145,6 +149,8 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
|
|
|
145
149
|
})
|
|
146
150
|
: getProtocolV2SettingsBehavior({ kind: 'page', page });
|
|
147
151
|
this.unlockPolicy = behavior.unlockPolicy;
|
|
152
|
+
this.protocolV2PreUnlockPinType =
|
|
153
|
+
behavior.unlockPolicy === 'unlock-before-run' ? DeviceSessionPinType.Any : undefined;
|
|
148
154
|
this.protocolV2UiInteraction = behavior.uiInteraction;
|
|
149
155
|
}
|
|
150
156
|
|
|
@@ -162,8 +168,7 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
|
|
|
162
168
|
async run() {
|
|
163
169
|
try {
|
|
164
170
|
if (this.device.isProtocolV2()) {
|
|
165
|
-
const refreshStatusAndSettings = () =>
|
|
166
|
-
this.device.getDeviceState({ refreshSections: ['status', 'settings'] });
|
|
171
|
+
const refreshStatusAndSettings = () => this.device.refreshProtocolV2SettingsAfterMutation();
|
|
167
172
|
assertSettingsSupported(this.payload, DEVICE_SETTINGS_V1_ONLY_FIELDS, 'Protocol V2');
|
|
168
173
|
const capabilities = getDeviceSettingsCapabilities(
|
|
169
174
|
this.device.getCurrentDeviceType(),
|
|
@@ -195,20 +200,20 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
|
|
|
195
200
|
const res = await this.device.commands.typedCall('DeviceSettingsPageShow', 'Success', {
|
|
196
201
|
page: DeviceSettingsPage.DevicePassphrase,
|
|
197
202
|
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
203
|
+
// A successful page response confirms the device-side toggle. When an Attach PIN
|
|
204
|
+
// session disables passphrase protection, the firmware locks immediately and omits
|
|
205
|
+
// private passphrase fields from subsequent reads, so persist the confirmed value
|
|
206
|
+
// before refreshing the remaining device state.
|
|
207
|
+
this.device.updateState(
|
|
208
|
+
{
|
|
209
|
+
status: {
|
|
210
|
+
passphraseProtection: requestedPassphrase,
|
|
211
|
+
...(requestedPassphrase === false ? { unlockedAttachPin: false } : undefined),
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
'settings-write'
|
|
215
|
+
);
|
|
216
|
+
await refreshStatusAndSettings();
|
|
212
217
|
return res.message;
|
|
213
218
|
}
|
|
214
219
|
if (requestedAirgap !== undefined) {
|
|
@@ -253,10 +258,19 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
|
|
|
253
258
|
);
|
|
254
259
|
}
|
|
255
260
|
|
|
256
|
-
|
|
261
|
+
// Early SDK 1.2 alphas exposed 0 as Protocol V1 "Never". Normalize at the
|
|
262
|
+
// protocol boundary so existing consumers send the firmware-supported value.
|
|
263
|
+
const protocolV1Params = {
|
|
257
264
|
...this.params,
|
|
258
|
-
|
|
259
|
-
|
|
265
|
+
auto_lock_delay_ms: normalizeProtocolV1DelayMs(this.params.auto_lock_delay_ms),
|
|
266
|
+
auto_shutdown_delay_ms: normalizeProtocolV1DelayMs(this.params.auto_shutdown_delay_ms),
|
|
267
|
+
};
|
|
268
|
+
const res = await this.device.commands.typedCall(
|
|
269
|
+
'ApplySettings',
|
|
270
|
+
'Success',
|
|
271
|
+
protocolV1Params
|
|
272
|
+
);
|
|
273
|
+
this.device.updateState(mapApplySettingsToState(protocolV1Params), 'settings-write');
|
|
260
274
|
return res.message;
|
|
261
275
|
} catch (error) {
|
|
262
276
|
if (error.message?.toLowerCase().includes('no setting provided')) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { sha256 } from '@noble/hashes/sha256';
|
|
2
2
|
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
|
+
import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
3
4
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
4
5
|
|
|
5
6
|
import { formatAnyHex } from '../helpers/hexUtils';
|
|
@@ -11,7 +12,15 @@ import type { BixinVerifyDeviceRequest } from '@onekeyfe/hd-transport';
|
|
|
11
12
|
import type { DeviceVerifySignature } from '../../types';
|
|
12
13
|
|
|
13
14
|
export default class DeviceVerify extends BaseMethod<BixinVerifyDeviceRequest> {
|
|
15
|
+
getSupportedProtocols() {
|
|
16
|
+
return ['V1', 'V2'] as const;
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
init() {
|
|
20
|
+
this.unlockPolicy = 'unlock-before-run';
|
|
21
|
+
// Protocol V2 device-management actions are not wallet-scoped, so either
|
|
22
|
+
// the main PIN or an Attach PIN may authorize them.
|
|
23
|
+
this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
|
|
15
24
|
this.useDevicePassphraseState = false;
|
|
16
25
|
|
|
17
26
|
// check payload
|
|
@@ -38,6 +47,22 @@ export default class DeviceVerify extends BaseMethod<BixinVerifyDeviceRequest> {
|
|
|
38
47
|
}
|
|
39
48
|
);
|
|
40
49
|
response = res.message;
|
|
50
|
+
} else if (this.device.isProtocolV2()) {
|
|
51
|
+
const signatureRes = await this.device.commands.typedCall(
|
|
52
|
+
'DeviceCertificateSign',
|
|
53
|
+
'DeviceCertificateSignature',
|
|
54
|
+
{
|
|
55
|
+
data: this.params.data,
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
const certRes = await this.device.commands.typedCall(
|
|
59
|
+
'DeviceCertificateRead',
|
|
60
|
+
'DeviceCertificate'
|
|
61
|
+
);
|
|
62
|
+
response = {
|
|
63
|
+
cert: certRes.message.cert_and_pubkey,
|
|
64
|
+
signature: signatureRes.message.data,
|
|
65
|
+
};
|
|
41
66
|
} else {
|
|
42
67
|
const signatureRes = await this.device.commands.typedCall(
|
|
43
68
|
'SESignMessage',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
1
|
+
import { DeviceSessionPinType, DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
2
2
|
|
|
3
3
|
import { BaseMethod } from '../BaseMethod';
|
|
4
4
|
|
|
@@ -11,6 +11,9 @@ export default class DeviceWipe extends BaseMethod<WipeDevice> {
|
|
|
11
11
|
|
|
12
12
|
init() {
|
|
13
13
|
this.unlockPolicy = 'unlock-before-run';
|
|
14
|
+
// Protocol V2 device-management actions are not wallet-scoped, so either
|
|
15
|
+
// the main PIN or an Attach PIN may authorize them.
|
|
16
|
+
this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
|
|
14
17
|
this.useDevicePassphraseState = false;
|
|
15
18
|
this.protocolV2UiInteraction = {
|
|
16
19
|
request: 'button',
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
3
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
2
4
|
|
|
3
5
|
import type {
|
|
4
6
|
FirmwareArtifactReader,
|
|
@@ -188,6 +190,43 @@ class ReaderFirmwareByteSource implements FirmwareByteSource {
|
|
|
188
190
|
}
|
|
189
191
|
}
|
|
190
192
|
|
|
193
|
+
export const readFirmwareByteSourceFully = async (
|
|
194
|
+
source: FirmwareByteSource
|
|
195
|
+
): Promise<ArrayBuffer> => {
|
|
196
|
+
const output = new Uint8Array(source.size);
|
|
197
|
+
let offset = 0;
|
|
198
|
+
while (offset < source.size) {
|
|
199
|
+
const length = Math.min(MAX_FIRMWARE_ARTIFACT_READ_BYTES, source.size - offset);
|
|
200
|
+
output.set(new Uint8Array(await source.readAt(offset, length)), offset);
|
|
201
|
+
offset += length;
|
|
202
|
+
}
|
|
203
|
+
return output.buffer;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export const readVerifiedFirmwareArtifact = async ({
|
|
207
|
+
artifact,
|
|
208
|
+
reader,
|
|
209
|
+
}: {
|
|
210
|
+
artifact: FirmwareArtifactReference;
|
|
211
|
+
reader: FirmwareArtifactReader;
|
|
212
|
+
}): Promise<ArrayBuffer> => {
|
|
213
|
+
assertFirmwareArtifactReference(artifact);
|
|
214
|
+
const source = await ReaderFirmwareByteSource.open(artifact, reader);
|
|
215
|
+
try {
|
|
216
|
+
const binary = await readFirmwareByteSourceFully(source);
|
|
217
|
+
const actualSha256 = bytesToHex(sha256(new Uint8Array(binary)));
|
|
218
|
+
if (actualSha256 !== artifact.sha256.toLowerCase()) {
|
|
219
|
+
return artifactError(
|
|
220
|
+
'FirmwareArtifactReceiptMismatch',
|
|
221
|
+
'Firmware artifact bytes do not match the approved SHA-256 receipt'
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return binary;
|
|
225
|
+
} finally {
|
|
226
|
+
await source.close().catch(() => undefined);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
191
230
|
export const openFirmwareByteSource = async ({
|
|
192
231
|
binary,
|
|
193
232
|
artifact,
|
|
@@ -216,20 +255,8 @@ export const openFirmwareByteSource = async ({
|
|
|
216
255
|
'Firmware artifact reader is required for artifactRef input'
|
|
217
256
|
);
|
|
218
257
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
export const readFirmwareByteSourceFully = async (
|
|
223
|
-
source: FirmwareByteSource
|
|
224
|
-
): Promise<ArrayBuffer> => {
|
|
225
|
-
const output = new Uint8Array(source.size);
|
|
226
|
-
let offset = 0;
|
|
227
|
-
while (offset < source.size) {
|
|
228
|
-
const length = Math.min(MAX_FIRMWARE_ARTIFACT_READ_BYTES, source.size - offset);
|
|
229
|
-
output.set(new Uint8Array(await source.readAt(offset, length)), offset);
|
|
230
|
-
offset += length;
|
|
231
|
-
}
|
|
232
|
-
return output.buffer;
|
|
258
|
+
const verifiedBinary = await readVerifiedFirmwareArtifact({ artifact, reader });
|
|
259
|
+
return createMemoryFirmwareByteSource(verifiedBinary);
|
|
233
260
|
};
|
|
234
261
|
|
|
235
262
|
export const writeFirmwareByteSource = async ({
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
|
+
import { clearFirmwareUpdatePreparedPlanDeviceIdentityPin } from './FirmwareUpdatePreparedPlan';
|
|
4
|
+
|
|
3
5
|
import type { FirmwareUpdateHostBinding } from '../../types/api/firmwareUpdate';
|
|
4
6
|
|
|
5
7
|
const bindingError = (message: string): never => {
|
|
@@ -24,8 +26,12 @@ class FirmwareHostBindingRegistry {
|
|
|
24
26
|
) {
|
|
25
27
|
return bindingError('Firmware host binding is invalid');
|
|
26
28
|
}
|
|
29
|
+
const replacedPreparedPlanDigest = this.binding?.preparedPlanDigest;
|
|
27
30
|
this.generation += 1;
|
|
28
31
|
this.binding = binding;
|
|
32
|
+
if (replacedPreparedPlanDigest) {
|
|
33
|
+
clearFirmwareUpdatePreparedPlanDeviceIdentityPin(replacedPreparedPlanDigest);
|
|
34
|
+
}
|
|
29
35
|
return this.generation;
|
|
30
36
|
}
|
|
31
37
|
|
|
@@ -33,8 +39,12 @@ class FirmwareHostBindingRegistry {
|
|
|
33
39
|
if (generation !== undefined && generation !== this.generation) {
|
|
34
40
|
return false;
|
|
35
41
|
}
|
|
42
|
+
const releasedPreparedPlanDigest = this.binding?.preparedPlanDigest;
|
|
36
43
|
this.generation += 1;
|
|
37
44
|
this.binding = undefined;
|
|
45
|
+
if (releasedPreparedPlanDigest) {
|
|
46
|
+
clearFirmwareUpdatePreparedPlanDeviceIdentityPin(releasedPreparedPlanDigest);
|
|
47
|
+
}
|
|
38
48
|
return true;
|
|
39
49
|
}
|
|
40
50
|
|
|
@@ -3,7 +3,7 @@ import { bytesToHex } from '@noble/hashes/utils';
|
|
|
3
3
|
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
4
4
|
import JSZip from 'jszip';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { readVerifiedFirmwareArtifact } from './FirmwareArtifactSource';
|
|
7
7
|
import { getFirmwareUpdateResourceName } from './FirmwareUpdatePreparedPlan';
|
|
8
8
|
|
|
9
9
|
import type { FirmwareArtifactReader } from '../../types/api/firmwareUpdate';
|
|
@@ -26,7 +26,10 @@ export type VerifiedPreparedResourceEntry = {
|
|
|
26
26
|
|
|
27
27
|
const resourceArchiveError = (
|
|
28
28
|
message: string,
|
|
29
|
-
firmwareUpdateCode:
|
|
29
|
+
firmwareUpdateCode:
|
|
30
|
+
| 'FirmwareArtifactsNotPrepared'
|
|
31
|
+
| 'FirmwareArtifactReceiptMismatch'
|
|
32
|
+
| 'FirmwareArtifactReaderInvalid'
|
|
30
33
|
): never => {
|
|
31
34
|
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, message, { firmwareUpdateCode });
|
|
32
35
|
};
|
|
@@ -73,6 +76,12 @@ export const readVerifiedPreparedResourceArchive = async ({
|
|
|
73
76
|
}
|
|
74
77
|
return [];
|
|
75
78
|
}
|
|
79
|
+
if (!reader) {
|
|
80
|
+
return resourceArchiveError(
|
|
81
|
+
'Firmware artifact reader is required for prepared resource archives',
|
|
82
|
+
'FirmwareArtifactReaderInvalid'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
76
85
|
if (resourceArtifacts.length !== 1) {
|
|
77
86
|
return resourceArchiveError(
|
|
78
87
|
'Firmware prepared plan must contain exactly one materialized resource ZIP',
|
|
@@ -92,37 +101,17 @@ export const readVerifiedPreparedResourceArchive = async ({
|
|
|
92
101
|
'FirmwareArtifactsNotPrepared'
|
|
93
102
|
);
|
|
94
103
|
}
|
|
95
|
-
|
|
96
|
-
artifact: archiveArtifact.artifact,
|
|
97
|
-
reader,
|
|
98
|
-
});
|
|
99
|
-
if (!archiveSource) {
|
|
100
|
-
return resourceArchiveError(
|
|
101
|
-
'Firmware prepared resource ZIP is unavailable',
|
|
102
|
-
'FirmwareArtifactsNotPrepared'
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
if (archiveSource.size > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES) {
|
|
106
|
-
await archiveSource.close().catch(() => undefined);
|
|
104
|
+
if (archiveArtifact.artifact.size > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES) {
|
|
107
105
|
return resourceArchiveError(
|
|
108
106
|
'Firmware prepared resource ZIP exceeds the archive size limit',
|
|
109
107
|
'FirmwareArtifactsNotPrepared'
|
|
110
108
|
);
|
|
111
109
|
}
|
|
112
110
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
await archiveSource.close().catch(() => undefined);
|
|
118
|
-
}
|
|
119
|
-
const archiveDigest = bytesToHex(sha256(new Uint8Array(archiveBinary)));
|
|
120
|
-
if (archiveDigest !== archiveArtifact.artifact.sha256.toLowerCase()) {
|
|
121
|
-
return resourceArchiveError(
|
|
122
|
-
'Firmware prepared resource ZIP does not match its approved receipt',
|
|
123
|
-
'FirmwareArtifactReceiptMismatch'
|
|
124
|
-
);
|
|
125
|
-
}
|
|
111
|
+
const archiveBinary = await readVerifiedFirmwareArtifact({
|
|
112
|
+
artifact: archiveArtifact.artifact,
|
|
113
|
+
reader,
|
|
114
|
+
});
|
|
126
115
|
|
|
127
116
|
let zip: JSZip;
|
|
128
117
|
try {
|