@onekeyfe/hardware-cli 1.1.28 → 1.2.0-alpha.1
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 +53 -15
- package/jest.config.js +1 -0
- package/package.json +6 -6
- package/src/cli.ts +73 -18
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,32 @@ const commander_1 = require("commander");
|
|
|
5
5
|
const sdk_1 = require("./sdk");
|
|
6
6
|
const session_1 = require("./session");
|
|
7
7
|
const chains_1 = require("./chains");
|
|
8
|
+
const hd_shared_1 = require("@onekeyfe/hd-shared");
|
|
9
|
+
const hd_core_1 = require("@onekeyfe/hd-core");
|
|
10
|
+
function extractPassphraseSession(payload) {
|
|
11
|
+
if (typeof payload === 'string') {
|
|
12
|
+
return { passphraseState: payload };
|
|
13
|
+
}
|
|
14
|
+
if (!payload || typeof payload !== 'object') {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
const statePayload = payload;
|
|
18
|
+
let passphraseState;
|
|
19
|
+
if (typeof statePayload.passphrase_state === 'string') {
|
|
20
|
+
passphraseState = statePayload.passphrase_state;
|
|
21
|
+
}
|
|
22
|
+
else if (typeof statePayload.passphraseState === 'string') {
|
|
23
|
+
passphraseState = statePayload.passphraseState;
|
|
24
|
+
}
|
|
25
|
+
let sessionId;
|
|
26
|
+
if (typeof statePayload.session_id === 'string') {
|
|
27
|
+
sessionId = statePayload.session_id;
|
|
28
|
+
}
|
|
29
|
+
else if (typeof statePayload.sessionId === 'string') {
|
|
30
|
+
sessionId = statePayload.sessionId;
|
|
31
|
+
}
|
|
32
|
+
return { passphraseState, sessionId };
|
|
33
|
+
}
|
|
8
34
|
const program = new commander_1.Command();
|
|
9
35
|
program
|
|
10
36
|
.name('onekey-hw')
|
|
@@ -33,8 +59,8 @@ program
|
|
|
33
59
|
const features = await sdk.getFeatures(device.connectId);
|
|
34
60
|
if (features?.success && features.payload) {
|
|
35
61
|
device.features = features.payload;
|
|
36
|
-
device.name = features.payload.label || features.payload.
|
|
37
|
-
const devType = features.payload.
|
|
62
|
+
device.name = features.payload.label || features.payload.bleName || device.name;
|
|
63
|
+
const devType = features.payload.deviceType?.toLowerCase();
|
|
38
64
|
if (devType) {
|
|
39
65
|
device.deviceType = devType;
|
|
40
66
|
}
|
|
@@ -537,7 +563,14 @@ sessionCmd
|
|
|
537
563
|
outputResult(globalOpts, psResult);
|
|
538
564
|
return;
|
|
539
565
|
}
|
|
540
|
-
const passphraseState = psResult.payload;
|
|
566
|
+
const { passphraseState, sessionId: passphraseSessionId } = extractPassphraseSession(psResult.payload);
|
|
567
|
+
if (!passphraseState) {
|
|
568
|
+
outputResult(globalOpts, {
|
|
569
|
+
success: false,
|
|
570
|
+
payload: { error: 'getPassphraseState did not return passphraseState' },
|
|
571
|
+
});
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
541
574
|
// 4. Get address to verify + extract deviceId
|
|
542
575
|
const addrResult = await sdk.evmGetAddress(connectId, device.deviceId || '', {
|
|
543
576
|
path: "m/44'/60'/0'/0/0",
|
|
@@ -559,8 +592,8 @@ sessionCmd
|
|
|
559
592
|
skipPassphraseCheck: true,
|
|
560
593
|
});
|
|
561
594
|
const featPayload = featResult?.success ? featResult.payload : undefined;
|
|
562
|
-
const deviceId = featPayload?.
|
|
563
|
-
const sessionId = featPayload?.
|
|
595
|
+
const deviceId = featPayload?.deviceId || device.deviceId || '';
|
|
596
|
+
const sessionId = passphraseSessionId || featPayload?.sessionId || '';
|
|
564
597
|
// 6. Save to keychain
|
|
565
598
|
if (passphraseState && deviceId && sessionId) {
|
|
566
599
|
await (0, session_1.saveSessionToKeychain)(deviceId, passphraseState, sessionId);
|
|
@@ -582,7 +615,7 @@ sessionCmd
|
|
|
582
615
|
const searchResult = await sdk.searchDevices();
|
|
583
616
|
const device = // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
584
617
|
searchResult?.payload?.[0];
|
|
585
|
-
const deviceId = device?.
|
|
618
|
+
const deviceId = device?.deviceId || device?.features?.device_id;
|
|
586
619
|
if (deviceId) {
|
|
587
620
|
await (0, session_1.clearSessionFromKeychain)(deviceId);
|
|
588
621
|
}
|
|
@@ -690,16 +723,18 @@ globalOpts) {
|
|
|
690
723
|
// ── Step 2: Get features if searchDevices didn't populate them ──
|
|
691
724
|
// getFeatures failures here are non-fatal — we fall through to Step 3
|
|
692
725
|
// which will fail with a clearer error if the device is truly unreachable.
|
|
693
|
-
let deviceId = device.features?.
|
|
726
|
+
let deviceId = device.features?.deviceId || device.deviceId || '';
|
|
727
|
+
let deviceType = (0, hd_core_1.getDeviceType)(device.features);
|
|
694
728
|
let unlocked = device.features?.unlocked;
|
|
695
|
-
let passphraseProtection = device.features?.
|
|
729
|
+
let passphraseProtection = device.features?.passphraseProtection;
|
|
696
730
|
if (!deviceId || unlocked == null || passphraseProtection == null) {
|
|
697
731
|
try {
|
|
698
732
|
const featResult = await sdk.getFeatures(connectId);
|
|
699
733
|
if (featResult?.success && featResult.payload) {
|
|
700
|
-
deviceId = featResult.payload.
|
|
734
|
+
deviceId = featResult.payload.deviceId || deviceId;
|
|
735
|
+
deviceType = (0, hd_core_1.getDeviceType)(featResult.payload) || deviceType;
|
|
701
736
|
unlocked = featResult.payload.unlocked;
|
|
702
|
-
passphraseProtection = featResult.payload.
|
|
737
|
+
passphraseProtection = featResult.payload.passphraseProtection;
|
|
703
738
|
}
|
|
704
739
|
}
|
|
705
740
|
catch {
|
|
@@ -715,15 +750,15 @@ globalOpts) {
|
|
|
715
750
|
if (wasLocked) {
|
|
716
751
|
process.stderr.write('[onekey-hw] Device is locked. Unlocking (PIN required)...\n');
|
|
717
752
|
const { payload: feat } = await unlockWithRetry(sdk, connectId);
|
|
718
|
-
deviceId = feat.
|
|
753
|
+
deviceId = feat.deviceId || deviceId;
|
|
719
754
|
unlocked = feat.unlocked;
|
|
720
|
-
passphraseProtection = feat.
|
|
755
|
+
passphraseProtection = feat.passphraseProtection;
|
|
721
756
|
}
|
|
722
757
|
if (!globalOpts.deviceId && deviceId) {
|
|
723
758
|
globalOpts.deviceId = deviceId;
|
|
724
759
|
}
|
|
725
760
|
// ── Step 4: Check passphrase protection ──────────────────────────
|
|
726
|
-
if (passphraseProtection === false) {
|
|
761
|
+
if (passphraseProtection === false && deviceType !== hd_shared_1.EDeviceType.Pro2) {
|
|
727
762
|
return undefined;
|
|
728
763
|
}
|
|
729
764
|
// ── Step 5: Try keychain session reuse ───────────────────────────
|
|
@@ -742,7 +777,10 @@ globalOpts) {
|
|
|
742
777
|
useEmptyPassphrase: false,
|
|
743
778
|
});
|
|
744
779
|
if (psResult.success && psResult.payload) {
|
|
745
|
-
const passphraseState = psResult.payload;
|
|
780
|
+
const { passphraseState, sessionId: passphraseSessionId } = extractPassphraseSession(psResult.payload);
|
|
781
|
+
if (!passphraseState) {
|
|
782
|
+
return undefined;
|
|
783
|
+
}
|
|
746
784
|
globalOpts.passphraseState = passphraseState;
|
|
747
785
|
// Save session to keychain for next invocation.
|
|
748
786
|
//
|
|
@@ -755,7 +793,7 @@ globalOpts) {
|
|
|
755
793
|
passphraseState,
|
|
756
794
|
skipPassphraseCheck: true,
|
|
757
795
|
});
|
|
758
|
-
const sessionId = featAfter?.success ? featAfter.payload?.
|
|
796
|
+
const sessionId = passphraseSessionId || (featAfter?.success ? featAfter.payload?.sessionId : undefined);
|
|
759
797
|
if (sessionId) {
|
|
760
798
|
await (0, session_1.saveSessionToKeychain)(deviceId, passphraseState, sessionId);
|
|
761
799
|
await (0, session_1.preloadSessionFromKeychain)(deviceId);
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hardware-cli",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.0-alpha.1",
|
|
4
4
|
"description": "OneKey hardware wallet CLI for testing device communication",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"test": "jest"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@onekeyfe/hd-common-connect-sdk": "1.1
|
|
34
|
-
"@onekeyfe/hd-core": "1.1
|
|
35
|
-
"@onekeyfe/hd-shared": "1.1
|
|
36
|
-
"@onekeyfe/hd-transport-usb": "1.1
|
|
33
|
+
"@onekeyfe/hd-common-connect-sdk": "1.2.0-alpha.1",
|
|
34
|
+
"@onekeyfe/hd-core": "1.2.0-alpha.1",
|
|
35
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.1",
|
|
36
|
+
"@onekeyfe/hd-transport-usb": "1.2.0-alpha.1",
|
|
37
37
|
"commander": "^12.0.0"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "4f4aae3a47be7bd54b4db4a10c400056784e01bd"
|
|
40
40
|
}
|
package/src/cli.ts
CHANGED
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
resolveSignTransaction,
|
|
15
15
|
} from './chains';
|
|
16
16
|
|
|
17
|
+
import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
18
|
+
import { getDeviceType } from '@onekeyfe/hd-core';
|
|
17
19
|
import type {
|
|
18
20
|
EthereumSignTypedDataMessage,
|
|
19
21
|
EthereumSignTypedDataTypes,
|
|
@@ -25,6 +27,41 @@ import type {
|
|
|
25
27
|
/** SearchDevice enriched with features fetched after discovery */
|
|
26
28
|
type EnrichedSearchDevice = SearchDevice & { features?: Features };
|
|
27
29
|
|
|
30
|
+
function extractPassphraseSession(payload: unknown): {
|
|
31
|
+
passphraseState?: string;
|
|
32
|
+
sessionId?: string;
|
|
33
|
+
} {
|
|
34
|
+
if (typeof payload === 'string') {
|
|
35
|
+
return { passphraseState: payload };
|
|
36
|
+
}
|
|
37
|
+
if (!payload || typeof payload !== 'object') {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const statePayload = payload as {
|
|
42
|
+
passphrase_state?: unknown;
|
|
43
|
+
passphraseState?: unknown;
|
|
44
|
+
session_id?: unknown;
|
|
45
|
+
sessionId?: unknown;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let passphraseState: string | undefined;
|
|
49
|
+
if (typeof statePayload.passphrase_state === 'string') {
|
|
50
|
+
passphraseState = statePayload.passphrase_state;
|
|
51
|
+
} else if (typeof statePayload.passphraseState === 'string') {
|
|
52
|
+
passphraseState = statePayload.passphraseState;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let sessionId: string | undefined;
|
|
56
|
+
if (typeof statePayload.session_id === 'string') {
|
|
57
|
+
sessionId = statePayload.session_id;
|
|
58
|
+
} else if (typeof statePayload.sessionId === 'string') {
|
|
59
|
+
sessionId = statePayload.sessionId;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { passphraseState, sessionId };
|
|
63
|
+
}
|
|
64
|
+
|
|
28
65
|
const program = new Command();
|
|
29
66
|
|
|
30
67
|
program
|
|
@@ -63,8 +100,8 @@ program
|
|
|
63
100
|
const features = await sdk.getFeatures(device.connectId);
|
|
64
101
|
if (features?.success && features.payload) {
|
|
65
102
|
device.features = features.payload;
|
|
66
|
-
device.name = features.payload.label || features.payload.
|
|
67
|
-
const devType = features.payload.
|
|
103
|
+
device.name = features.payload.label || features.payload.bleName || device.name;
|
|
104
|
+
const devType = features.payload.deviceType?.toLowerCase();
|
|
68
105
|
if (devType) {
|
|
69
106
|
device.deviceType = devType as IDeviceType;
|
|
70
107
|
}
|
|
@@ -687,7 +724,16 @@ sessionCmd
|
|
|
687
724
|
outputResult(globalOpts, psResult);
|
|
688
725
|
return;
|
|
689
726
|
}
|
|
690
|
-
const passphraseState =
|
|
727
|
+
const { passphraseState, sessionId: passphraseSessionId } = extractPassphraseSession(
|
|
728
|
+
psResult.payload
|
|
729
|
+
);
|
|
730
|
+
if (!passphraseState) {
|
|
731
|
+
outputResult(globalOpts, {
|
|
732
|
+
success: false,
|
|
733
|
+
payload: { error: 'getPassphraseState did not return passphraseState' },
|
|
734
|
+
});
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
691
737
|
|
|
692
738
|
// 4. Get address to verify + extract deviceId
|
|
693
739
|
const addrResult = await sdk.evmGetAddress(connectId, device.deviceId || '', {
|
|
@@ -711,8 +757,8 @@ sessionCmd
|
|
|
711
757
|
skipPassphraseCheck: true,
|
|
712
758
|
});
|
|
713
759
|
const featPayload = featResult?.success ? featResult.payload : undefined;
|
|
714
|
-
const deviceId = featPayload?.
|
|
715
|
-
const sessionId = featPayload?.
|
|
760
|
+
const deviceId = featPayload?.deviceId || device.deviceId || '';
|
|
761
|
+
const sessionId = passphraseSessionId || featPayload?.sessionId || '';
|
|
716
762
|
|
|
717
763
|
// 6. Save to keychain
|
|
718
764
|
if (passphraseState && deviceId && sessionId) {
|
|
@@ -739,7 +785,7 @@ sessionCmd
|
|
|
739
785
|
const searchResult = await sdk.searchDevices();
|
|
740
786
|
const device = // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
741
787
|
(searchResult?.payload as any)?.[0];
|
|
742
|
-
const deviceId = device?.
|
|
788
|
+
const deviceId = device?.deviceId || device?.features?.device_id;
|
|
743
789
|
if (deviceId) {
|
|
744
790
|
await clearSessionFromKeychain(deviceId);
|
|
745
791
|
}
|
|
@@ -868,9 +914,10 @@ async function prepareSession(
|
|
|
868
914
|
connectId?: string;
|
|
869
915
|
deviceId?: string;
|
|
870
916
|
features?: {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
917
|
+
deviceId?: string | null;
|
|
918
|
+
deviceType?: string;
|
|
919
|
+
sessionId?: string | null;
|
|
920
|
+
passphraseProtection?: boolean | null;
|
|
874
921
|
unlocked?: boolean | null;
|
|
875
922
|
};
|
|
876
923
|
};
|
|
@@ -882,17 +929,19 @@ async function prepareSession(
|
|
|
882
929
|
// ── Step 2: Get features if searchDevices didn't populate them ──
|
|
883
930
|
// getFeatures failures here are non-fatal — we fall through to Step 3
|
|
884
931
|
// which will fail with a clearer error if the device is truly unreachable.
|
|
885
|
-
let deviceId = device.features?.
|
|
932
|
+
let deviceId = device.features?.deviceId || device.deviceId || '';
|
|
933
|
+
let deviceType = getDeviceType(device.features as Features | undefined);
|
|
886
934
|
let unlocked = device.features?.unlocked;
|
|
887
|
-
let passphraseProtection = device.features?.
|
|
935
|
+
let passphraseProtection = device.features?.passphraseProtection;
|
|
888
936
|
|
|
889
937
|
if (!deviceId || unlocked == null || passphraseProtection == null) {
|
|
890
938
|
try {
|
|
891
939
|
const featResult = await sdk.getFeatures(connectId);
|
|
892
940
|
if (featResult?.success && featResult.payload) {
|
|
893
|
-
deviceId = featResult.payload.
|
|
941
|
+
deviceId = featResult.payload.deviceId || deviceId;
|
|
942
|
+
deviceType = getDeviceType(featResult.payload) || deviceType;
|
|
894
943
|
unlocked = featResult.payload.unlocked;
|
|
895
|
-
passphraseProtection = featResult.payload.
|
|
944
|
+
passphraseProtection = featResult.payload.passphraseProtection;
|
|
896
945
|
}
|
|
897
946
|
} catch {
|
|
898
947
|
/* non-fatal — Step 3 will surface a clear error if device is gone */
|
|
@@ -908,9 +957,9 @@ async function prepareSession(
|
|
|
908
957
|
if (wasLocked) {
|
|
909
958
|
process.stderr.write('[onekey-hw] Device is locked. Unlocking (PIN required)...\n');
|
|
910
959
|
const { payload: feat } = await unlockWithRetry(sdk, connectId);
|
|
911
|
-
deviceId = feat.
|
|
960
|
+
deviceId = feat.deviceId || deviceId;
|
|
912
961
|
unlocked = feat.unlocked;
|
|
913
|
-
passphraseProtection = feat.
|
|
962
|
+
passphraseProtection = feat.passphraseProtection;
|
|
914
963
|
}
|
|
915
964
|
|
|
916
965
|
if (!globalOpts.deviceId && deviceId) {
|
|
@@ -918,7 +967,7 @@ async function prepareSession(
|
|
|
918
967
|
}
|
|
919
968
|
|
|
920
969
|
// ── Step 4: Check passphrase protection ──────────────────────────
|
|
921
|
-
if (passphraseProtection === false) {
|
|
970
|
+
if (passphraseProtection === false && deviceType !== EDeviceType.Pro2) {
|
|
922
971
|
return undefined;
|
|
923
972
|
}
|
|
924
973
|
|
|
@@ -940,7 +989,12 @@ async function prepareSession(
|
|
|
940
989
|
});
|
|
941
990
|
|
|
942
991
|
if (psResult.success && psResult.payload) {
|
|
943
|
-
const passphraseState =
|
|
992
|
+
const { passphraseState, sessionId: passphraseSessionId } = extractPassphraseSession(
|
|
993
|
+
psResult.payload
|
|
994
|
+
);
|
|
995
|
+
if (!passphraseState) {
|
|
996
|
+
return undefined;
|
|
997
|
+
}
|
|
944
998
|
globalOpts.passphraseState = passphraseState;
|
|
945
999
|
|
|
946
1000
|
// Save session to keychain for next invocation.
|
|
@@ -954,7 +1008,8 @@ async function prepareSession(
|
|
|
954
1008
|
passphraseState,
|
|
955
1009
|
skipPassphraseCheck: true,
|
|
956
1010
|
});
|
|
957
|
-
const sessionId =
|
|
1011
|
+
const sessionId =
|
|
1012
|
+
passphraseSessionId || (featAfter?.success ? featAfter.payload?.sessionId : undefined);
|
|
958
1013
|
if (sessionId) {
|
|
959
1014
|
await saveSessionToKeychain(deviceId, passphraseState, sessionId);
|
|
960
1015
|
await preloadSessionFromKeychain(deviceId);
|