@onekeyfe/hardware-cli 1.2.0-alpha.25 → 1.2.0-alpha.27
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.d.ts +10 -0
- package/dist/cli.js +25 -79
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/package.json +6 -6
- package/src/__tests__/wallet-session.test.ts +62 -0
- package/src/cli.ts +25 -97
- package/src/session.ts +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
declare const program: Command;
|
|
3
3
|
export declare function getLegacyFirmwareConnectTimeout(transport: 'usb' | 'ble'): 90000 | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* Prepare passphrase session before SDK calls.
|
|
6
|
+
*
|
|
7
|
+
* 1. If --use-empty-passphrase or --passphrase-state provided → use as-is
|
|
8
|
+
* 2. Try keychain → preloadSessionCache → use cached session
|
|
9
|
+
* 3. Keychain miss → openWalletSession (triggers 1/2/3 prompt) → save to keychain
|
|
10
|
+
*
|
|
11
|
+
* After this, globalOpts.passphraseState is set and getCommonParams will include it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function prepareSession(sdk: typeof import('@onekeyfe/hd-common-connect-sdk').default, globalOpts: Record<string, any>): Promise<string | undefined>;
|
|
4
14
|
export declare function buildWallpaperUploadMetrics({ totalBytes, transferredBytes, startedAt, endedAt, lastProgress, }: {
|
|
5
15
|
totalBytes: number;
|
|
6
16
|
transferredBytes: number;
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.program = exports.buildWallpaperUploadMetrics = exports.getLegacyFirmwareConnectTimeout = void 0;
|
|
4
|
+
exports.program = exports.buildWallpaperUploadMetrics = exports.prepareSession = exports.getLegacyFirmwareConnectTimeout = void 0;
|
|
5
5
|
const node_fs_1 = require("node:fs");
|
|
6
6
|
const node_path_1 = require("node:path");
|
|
7
7
|
const commander_1 = require("commander");
|
|
@@ -12,30 +12,6 @@ const deviceSelection_1 = require("./deviceSelection");
|
|
|
12
12
|
const deviceStateCommands_1 = require("./deviceStateCommands");
|
|
13
13
|
const sdk_1 = require("./sdk");
|
|
14
14
|
const session_1 = require("./session");
|
|
15
|
-
function extractPassphraseSession(payload) {
|
|
16
|
-
if (typeof payload === 'string') {
|
|
17
|
-
return { passphraseState: payload };
|
|
18
|
-
}
|
|
19
|
-
if (!payload || typeof payload !== 'object') {
|
|
20
|
-
return {};
|
|
21
|
-
}
|
|
22
|
-
const statePayload = payload;
|
|
23
|
-
let passphraseState;
|
|
24
|
-
if (typeof statePayload.passphrase_state === 'string') {
|
|
25
|
-
passphraseState = statePayload.passphrase_state;
|
|
26
|
-
}
|
|
27
|
-
else if (typeof statePayload.passphraseState === 'string') {
|
|
28
|
-
passphraseState = statePayload.passphraseState;
|
|
29
|
-
}
|
|
30
|
-
let sessionId;
|
|
31
|
-
if (typeof statePayload.session_id === 'string') {
|
|
32
|
-
sessionId = statePayload.session_id;
|
|
33
|
-
}
|
|
34
|
-
else if (typeof statePayload.sessionId === 'string') {
|
|
35
|
-
sessionId = statePayload.sessionId;
|
|
36
|
-
}
|
|
37
|
-
return { passphraseState, sessionId };
|
|
38
|
-
}
|
|
39
15
|
const program = new commander_1.Command();
|
|
40
16
|
exports.program = program;
|
|
41
17
|
const { version: cliVersion } = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.resolve)(__dirname, '../package.json'), 'utf8'));
|
|
@@ -666,48 +642,30 @@ sessionCmd
|
|
|
666
642
|
process.stderr.write('[onekey-hw] Device is locked. Unlocking (PIN required)...\n');
|
|
667
643
|
await unlockWithRetry(sdk, connectId);
|
|
668
644
|
}
|
|
669
|
-
// 3.
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
useEmptyPassphrase: false,
|
|
645
|
+
// 3. Open a hidden wallet session (triggers 1/2/3 selection).
|
|
646
|
+
const sessionResult = await sdk.openWalletSession(connectId, {
|
|
647
|
+
mode: 'select-hidden',
|
|
673
648
|
});
|
|
674
|
-
if (!
|
|
675
|
-
outputResult(globalOpts,
|
|
649
|
+
if (!sessionResult.success) {
|
|
650
|
+
outputResult(globalOpts, sessionResult);
|
|
676
651
|
return;
|
|
677
652
|
}
|
|
678
|
-
|
|
679
|
-
if (!passphraseState) {
|
|
653
|
+
if (sessionResult.payload.walletType !== 'hidden') {
|
|
680
654
|
outputResult(globalOpts, {
|
|
681
655
|
success: false,
|
|
682
|
-
payload: { error: '
|
|
656
|
+
payload: { error: 'Hidden wallet selection did not return a hidden wallet session' },
|
|
683
657
|
});
|
|
684
658
|
return;
|
|
685
659
|
}
|
|
660
|
+
const { deviceId, passphraseState, sessionId } = sessionResult.payload;
|
|
686
661
|
// 4. Get address to verify + extract deviceId
|
|
687
|
-
const addrResult = await sdk.evmGetAddress(connectId,
|
|
662
|
+
const addrResult = await sdk.evmGetAddress(connectId, deviceId, {
|
|
688
663
|
path: "m/44'/60'/0'/0/0",
|
|
689
664
|
showOnOneKey: false,
|
|
690
665
|
passphraseState,
|
|
691
666
|
});
|
|
692
|
-
// 5.
|
|
693
|
-
|
|
694
|
-
// IMPORTANT: pass `passphraseState` here. Without it, the SDK's
|
|
695
|
-
// connectStateChange guard (core/index.ts) would see the payload's
|
|
696
|
-
// passphraseState flip from mnNy → undefined, clear the cached Device,
|
|
697
|
-
// and call Initialize again with no passphrase_state / no session_id.
|
|
698
|
-
// That Initialize resets the device to the standard wallet and returns
|
|
699
|
-
// a *standard-wallet* session_id — which we'd then save in the keychain
|
|
700
|
-
// paired with the hidden-wallet passphraseState. On the next CLI run
|
|
701
|
-
// the mismatch would trigger PassphraseRequest (1/2/3 again).
|
|
702
|
-
const featResult = await sdk.getFeatures(connectId, {
|
|
703
|
-
passphraseState,
|
|
704
|
-
skipPassphraseCheck: true,
|
|
705
|
-
});
|
|
706
|
-
const featPayload = featResult?.success ? featResult.payload : undefined;
|
|
707
|
-
const deviceId = featPayload?.deviceId || device.deviceId || '';
|
|
708
|
-
const sessionId = passphraseSessionId || featPayload?.sessionId || '';
|
|
709
|
-
// 6. Save to keychain
|
|
710
|
-
if (passphraseState && deviceId && sessionId) {
|
|
667
|
+
// 5. Persist only the session id returned by the explicit wallet-session API.
|
|
668
|
+
if (sessionId) {
|
|
711
669
|
await (0, session_1.saveSessionToKeychain)(deviceId, passphraseState, sessionId);
|
|
712
670
|
}
|
|
713
671
|
outputResult(globalOpts, {
|
|
@@ -804,7 +762,7 @@ async function unlockWithRetry(sdk, connectId, maxAttempts = 3) {
|
|
|
804
762
|
*
|
|
805
763
|
* 1. If --use-empty-passphrase or --passphrase-state provided → use as-is
|
|
806
764
|
* 2. Try keychain → preloadSessionCache → use cached session
|
|
807
|
-
* 3. Keychain miss →
|
|
765
|
+
* 3. Keychain miss → openWalletSession (triggers 1/2/3 prompt) → save to keychain
|
|
808
766
|
*
|
|
809
767
|
* After this, globalOpts.passphraseState is set and getCommonParams will include it.
|
|
810
768
|
*/
|
|
@@ -816,7 +774,7 @@ globalOpts) {
|
|
|
816
774
|
return globalOpts.passphraseState;
|
|
817
775
|
}
|
|
818
776
|
// Errors from the SDK calls below (PIN cancelled, transport broken,
|
|
819
|
-
//
|
|
777
|
+
// openWalletSession rejection) intentionally propagate to runCommand's
|
|
820
778
|
// catch block, which renders them as structured `{ success: false,
|
|
821
779
|
// payload: { error, code } }` output instead of silently falling through
|
|
822
780
|
// to a confusing downstream error 112 / 114.
|
|
@@ -887,38 +845,26 @@ globalOpts) {
|
|
|
887
845
|
return cached;
|
|
888
846
|
}
|
|
889
847
|
}
|
|
890
|
-
// ── Step 6: Keychain miss →
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
useEmptyPassphrase: false,
|
|
848
|
+
// ── Step 6: Keychain miss → openWalletSession (triggers 1/2/3 prompt) ──
|
|
849
|
+
const sessionResult = await sdk.openWalletSession(connectId, {
|
|
850
|
+
mode: 'select-hidden',
|
|
894
851
|
});
|
|
895
|
-
if (
|
|
896
|
-
|
|
897
|
-
if (!passphraseState) {
|
|
852
|
+
if (sessionResult.success && sessionResult.payload) {
|
|
853
|
+
if (sessionResult.payload.walletType !== 'hidden') {
|
|
898
854
|
return undefined;
|
|
899
855
|
}
|
|
856
|
+
const { deviceId: sessionDeviceId, passphraseState, sessionId } = sessionResult.payload;
|
|
857
|
+
globalOpts.deviceId = sessionDeviceId;
|
|
900
858
|
globalOpts.passphraseState = passphraseState;
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
// Initialize would be re-run without passphrase_state, resetting the
|
|
905
|
-
// device to the standard wallet and returning a mismatched session_id.
|
|
906
|
-
// See the matching comment in `session connect`.
|
|
907
|
-
if (deviceId) {
|
|
908
|
-
const featAfter = await sdk.getFeatures(connectId, {
|
|
909
|
-
passphraseState,
|
|
910
|
-
skipPassphraseCheck: true,
|
|
911
|
-
});
|
|
912
|
-
const sessionId = passphraseSessionId || (featAfter?.success ? featAfter.payload?.sessionId : undefined);
|
|
913
|
-
if (sessionId) {
|
|
914
|
-
await (0, session_1.saveSessionToKeychain)(deviceId, passphraseState, sessionId);
|
|
915
|
-
await (0, session_1.preloadSessionFromKeychain)(deviceId);
|
|
916
|
-
}
|
|
859
|
+
if (sessionId) {
|
|
860
|
+
await (0, session_1.saveSessionToKeychain)(sessionDeviceId, passphraseState, sessionId);
|
|
861
|
+
await (0, session_1.preloadSessionFromKeychain)(sessionDeviceId);
|
|
917
862
|
}
|
|
918
863
|
return passphraseState;
|
|
919
864
|
}
|
|
920
865
|
return undefined;
|
|
921
866
|
}
|
|
867
|
+
exports.prepareSession = prepareSession;
|
|
922
868
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
923
869
|
function outputResult(_globalOpts, result) {
|
|
924
870
|
console.log(JSON.stringify(result, null, 2));
|
package/dist/session.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Passphrase session management for hd-cli.
|
|
3
3
|
*
|
|
4
4
|
* Aligns with app-monorepo's CLI pattern:
|
|
5
|
-
* Login:
|
|
5
|
+
* Login: openWalletSession → passphraseState + sessionId → keychain
|
|
6
6
|
* Command: keychain → preloadSessionCache → SDK call (no passphrase prompt)
|
|
7
7
|
* Stale: error 112 → clear keychain → re-prompt → retry
|
|
8
8
|
* Logout: keychain delete
|
package/dist/session.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Passphrase session management for hd-cli.
|
|
4
4
|
*
|
|
5
5
|
* Aligns with app-monorepo's CLI pattern:
|
|
6
|
-
* Login:
|
|
6
|
+
* Login: openWalletSession → passphraseState + sessionId → keychain
|
|
7
7
|
* Command: keychain → preloadSessionCache → SDK call (no passphrase prompt)
|
|
8
8
|
* Stale: error 112 → clear keychain → re-prompt → retry
|
|
9
9
|
* Logout: keychain delete
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hardware-cli",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.27",
|
|
4
4
|
"description": "OneKey hardware wallet CLI for testing device communication",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"test": "jest"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@onekeyfe/hd-common-connect-sdk": "1.2.0-alpha.
|
|
35
|
-
"@onekeyfe/hd-core": "1.2.0-alpha.
|
|
36
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
37
|
-
"@onekeyfe/hd-transport-usb": "1.2.0-alpha.
|
|
34
|
+
"@onekeyfe/hd-common-connect-sdk": "1.2.0-alpha.27",
|
|
35
|
+
"@onekeyfe/hd-core": "1.2.0-alpha.27",
|
|
36
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.27",
|
|
37
|
+
"@onekeyfe/hd-transport-usb": "1.2.0-alpha.27",
|
|
38
38
|
"@stoprocent/noble": "2.3.16",
|
|
39
39
|
"commander": "^12.0.0"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "ea3c188364a6fb968875484805085e4466d3ef99"
|
|
42
42
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { prepareSession } from '../cli';
|
|
2
|
+
import { preloadSessionFromKeychain, saveSessionToKeychain } from '../session';
|
|
3
|
+
|
|
4
|
+
jest.mock('../session', () => ({
|
|
5
|
+
clearSessionFromKeychain: jest.fn(),
|
|
6
|
+
preloadSessionFromKeychain: jest.fn(),
|
|
7
|
+
saveSessionToKeychain: jest.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe('CLI wallet session', () => {
|
|
11
|
+
test('persists the session returned by openWalletSession without reading it from features', async () => {
|
|
12
|
+
const sdk = {
|
|
13
|
+
searchDevices: jest.fn().mockResolvedValue({
|
|
14
|
+
success: true,
|
|
15
|
+
payload: [
|
|
16
|
+
{
|
|
17
|
+
connectId: 'pro2-connect-id',
|
|
18
|
+
deviceId: 'device-id',
|
|
19
|
+
deviceType: 'pro2',
|
|
20
|
+
features: {
|
|
21
|
+
deviceId: 'device-id',
|
|
22
|
+
deviceType: 'pro2',
|
|
23
|
+
unlocked: true,
|
|
24
|
+
passphraseProtection: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
}),
|
|
29
|
+
getFeatures: jest.fn(),
|
|
30
|
+
openWalletSession: jest.fn().mockResolvedValue({
|
|
31
|
+
success: true,
|
|
32
|
+
payload: {
|
|
33
|
+
protocol: 'V2',
|
|
34
|
+
walletType: 'hidden',
|
|
35
|
+
deviceId: 'device-id',
|
|
36
|
+
passphraseState: 'wallet-state',
|
|
37
|
+
sessionId: 'wallet-session-id',
|
|
38
|
+
resumed: false,
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
const globalOpts: Record<string, unknown> = {};
|
|
43
|
+
(preloadSessionFromKeychain as jest.Mock).mockResolvedValueOnce(undefined);
|
|
44
|
+
|
|
45
|
+
await expect(prepareSession(sdk as never, globalOpts)).resolves.toBe('wallet-state');
|
|
46
|
+
|
|
47
|
+
expect(sdk.openWalletSession).toHaveBeenCalledWith('pro2-connect-id', {
|
|
48
|
+
mode: 'select-hidden',
|
|
49
|
+
});
|
|
50
|
+
expect(sdk.getFeatures).not.toHaveBeenCalled();
|
|
51
|
+
expect(saveSessionToKeychain).toHaveBeenCalledWith(
|
|
52
|
+
'device-id',
|
|
53
|
+
'wallet-state',
|
|
54
|
+
'wallet-session-id'
|
|
55
|
+
);
|
|
56
|
+
expect(globalOpts).toMatchObject({
|
|
57
|
+
connectId: 'pro2-connect-id',
|
|
58
|
+
deviceId: 'device-id',
|
|
59
|
+
passphraseState: 'wallet-state',
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
package/src/cli.ts
CHANGED
|
@@ -31,41 +31,6 @@ import type {
|
|
|
31
31
|
/** SearchDevice enriched with features fetched after discovery */
|
|
32
32
|
type EnrichedSearchDevice = SearchDevice & { features?: Features };
|
|
33
33
|
|
|
34
|
-
function extractPassphraseSession(payload: unknown): {
|
|
35
|
-
passphraseState?: string;
|
|
36
|
-
sessionId?: string;
|
|
37
|
-
} {
|
|
38
|
-
if (typeof payload === 'string') {
|
|
39
|
-
return { passphraseState: payload };
|
|
40
|
-
}
|
|
41
|
-
if (!payload || typeof payload !== 'object') {
|
|
42
|
-
return {};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const statePayload = payload as {
|
|
46
|
-
passphrase_state?: unknown;
|
|
47
|
-
passphraseState?: unknown;
|
|
48
|
-
session_id?: unknown;
|
|
49
|
-
sessionId?: unknown;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
let passphraseState: string | undefined;
|
|
53
|
-
if (typeof statePayload.passphrase_state === 'string') {
|
|
54
|
-
passphraseState = statePayload.passphrase_state;
|
|
55
|
-
} else if (typeof statePayload.passphraseState === 'string') {
|
|
56
|
-
passphraseState = statePayload.passphraseState;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let sessionId: string | undefined;
|
|
60
|
-
if (typeof statePayload.session_id === 'string') {
|
|
61
|
-
sessionId = statePayload.session_id;
|
|
62
|
-
} else if (typeof statePayload.sessionId === 'string') {
|
|
63
|
-
sessionId = statePayload.sessionId;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return { passphraseState, sessionId };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
34
|
const program = new Command();
|
|
70
35
|
const { version: cliVersion } = JSON.parse(
|
|
71
36
|
readFileSync(resolve(__dirname, '../package.json'), 'utf8')
|
|
@@ -847,7 +812,7 @@ sessionCmd
|
|
|
847
812
|
});
|
|
848
813
|
return;
|
|
849
814
|
}
|
|
850
|
-
const device
|
|
815
|
+
const device = searchResult.payload[0] as SearchDevice & { features?: Features };
|
|
851
816
|
const connectId = device.connectId || globalOpts.connectId;
|
|
852
817
|
|
|
853
818
|
// 2. Unlock if locked — getPassphraseState below talks to a live
|
|
@@ -858,53 +823,32 @@ sessionCmd
|
|
|
858
823
|
await unlockWithRetry(sdk, connectId);
|
|
859
824
|
}
|
|
860
825
|
|
|
861
|
-
// 3.
|
|
862
|
-
const
|
|
863
|
-
|
|
864
|
-
useEmptyPassphrase: false,
|
|
826
|
+
// 3. Open a hidden wallet session (triggers 1/2/3 selection).
|
|
827
|
+
const sessionResult = await sdk.openWalletSession(connectId, {
|
|
828
|
+
mode: 'select-hidden',
|
|
865
829
|
});
|
|
866
|
-
if (!
|
|
867
|
-
outputResult(globalOpts,
|
|
830
|
+
if (!sessionResult.success) {
|
|
831
|
+
outputResult(globalOpts, sessionResult);
|
|
868
832
|
return;
|
|
869
833
|
}
|
|
870
|
-
|
|
871
|
-
psResult.payload
|
|
872
|
-
);
|
|
873
|
-
if (!passphraseState) {
|
|
834
|
+
if (sessionResult.payload.walletType !== 'hidden') {
|
|
874
835
|
outputResult(globalOpts, {
|
|
875
836
|
success: false,
|
|
876
|
-
payload: { error: '
|
|
837
|
+
payload: { error: 'Hidden wallet selection did not return a hidden wallet session' },
|
|
877
838
|
});
|
|
878
839
|
return;
|
|
879
840
|
}
|
|
841
|
+
const { deviceId, passphraseState, sessionId } = sessionResult.payload;
|
|
880
842
|
|
|
881
843
|
// 4. Get address to verify + extract deviceId
|
|
882
|
-
const addrResult = await sdk.evmGetAddress(connectId,
|
|
844
|
+
const addrResult = await sdk.evmGetAddress(connectId, deviceId, {
|
|
883
845
|
path: "m/44'/60'/0'/0/0",
|
|
884
846
|
showOnOneKey: false,
|
|
885
847
|
passphraseState,
|
|
886
848
|
});
|
|
887
849
|
|
|
888
|
-
// 5.
|
|
889
|
-
|
|
890
|
-
// IMPORTANT: pass `passphraseState` here. Without it, the SDK's
|
|
891
|
-
// connectStateChange guard (core/index.ts) would see the payload's
|
|
892
|
-
// passphraseState flip from mnNy → undefined, clear the cached Device,
|
|
893
|
-
// and call Initialize again with no passphrase_state / no session_id.
|
|
894
|
-
// That Initialize resets the device to the standard wallet and returns
|
|
895
|
-
// a *standard-wallet* session_id — which we'd then save in the keychain
|
|
896
|
-
// paired with the hidden-wallet passphraseState. On the next CLI run
|
|
897
|
-
// the mismatch would trigger PassphraseRequest (1/2/3 again).
|
|
898
|
-
const featResult = await sdk.getFeatures(connectId, {
|
|
899
|
-
passphraseState,
|
|
900
|
-
skipPassphraseCheck: true,
|
|
901
|
-
});
|
|
902
|
-
const featPayload = featResult?.success ? featResult.payload : undefined;
|
|
903
|
-
const deviceId = featPayload?.deviceId || device.deviceId || '';
|
|
904
|
-
const sessionId = passphraseSessionId || featPayload?.sessionId || '';
|
|
905
|
-
|
|
906
|
-
// 6. Save to keychain
|
|
907
|
-
if (passphraseState && deviceId && sessionId) {
|
|
850
|
+
// 5. Persist only the session id returned by the explicit wallet-session API.
|
|
851
|
+
if (sessionId) {
|
|
908
852
|
await saveSessionToKeychain(deviceId, passphraseState, sessionId);
|
|
909
853
|
}
|
|
910
854
|
|
|
@@ -1023,11 +967,11 @@ async function unlockWithRetry(
|
|
|
1023
967
|
*
|
|
1024
968
|
* 1. If --use-empty-passphrase or --passphrase-state provided → use as-is
|
|
1025
969
|
* 2. Try keychain → preloadSessionCache → use cached session
|
|
1026
|
-
* 3. Keychain miss →
|
|
970
|
+
* 3. Keychain miss → openWalletSession (triggers 1/2/3 prompt) → save to keychain
|
|
1027
971
|
*
|
|
1028
972
|
* After this, globalOpts.passphraseState is set and getCommonParams will include it.
|
|
1029
973
|
*/
|
|
1030
|
-
async function prepareSession(
|
|
974
|
+
export async function prepareSession(
|
|
1031
975
|
sdk: typeof import('@onekeyfe/hd-common-connect-sdk').default,
|
|
1032
976
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1033
977
|
globalOpts: Record<string, any>
|
|
@@ -1038,7 +982,7 @@ async function prepareSession(
|
|
|
1038
982
|
}
|
|
1039
983
|
|
|
1040
984
|
// Errors from the SDK calls below (PIN cancelled, transport broken,
|
|
1041
|
-
//
|
|
985
|
+
// openWalletSession rejection) intentionally propagate to runCommand's
|
|
1042
986
|
// catch block, which renders them as structured `{ success: false,
|
|
1043
987
|
// payload: { error, code } }` output instead of silently falling through
|
|
1044
988
|
// to a confusing downstream error 112 / 114.
|
|
@@ -1147,38 +1091,22 @@ async function prepareSession(
|
|
|
1147
1091
|
}
|
|
1148
1092
|
}
|
|
1149
1093
|
|
|
1150
|
-
// ── Step 6: Keychain miss →
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
useEmptyPassphrase: false,
|
|
1094
|
+
// ── Step 6: Keychain miss → openWalletSession (triggers 1/2/3 prompt) ──
|
|
1095
|
+
const sessionResult = await sdk.openWalletSession(connectId, {
|
|
1096
|
+
mode: 'select-hidden',
|
|
1154
1097
|
});
|
|
1155
1098
|
|
|
1156
|
-
if (
|
|
1157
|
-
|
|
1158
|
-
psResult.payload
|
|
1159
|
-
);
|
|
1160
|
-
if (!passphraseState) {
|
|
1099
|
+
if (sessionResult.success && sessionResult.payload) {
|
|
1100
|
+
if (sessionResult.payload.walletType !== 'hidden') {
|
|
1161
1101
|
return undefined;
|
|
1162
1102
|
}
|
|
1103
|
+
const { deviceId: sessionDeviceId, passphraseState, sessionId } = sessionResult.payload;
|
|
1104
|
+
globalOpts.deviceId = sessionDeviceId;
|
|
1163
1105
|
globalOpts.passphraseState = passphraseState;
|
|
1164
1106
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
// Initialize would be re-run without passphrase_state, resetting the
|
|
1169
|
-
// device to the standard wallet and returning a mismatched session_id.
|
|
1170
|
-
// See the matching comment in `session connect`.
|
|
1171
|
-
if (deviceId) {
|
|
1172
|
-
const featAfter = await sdk.getFeatures(connectId, {
|
|
1173
|
-
passphraseState,
|
|
1174
|
-
skipPassphraseCheck: true,
|
|
1175
|
-
});
|
|
1176
|
-
const sessionId =
|
|
1177
|
-
passphraseSessionId || (featAfter?.success ? featAfter.payload?.sessionId : undefined);
|
|
1178
|
-
if (sessionId) {
|
|
1179
|
-
await saveSessionToKeychain(deviceId, passphraseState, sessionId);
|
|
1180
|
-
await preloadSessionFromKeychain(deviceId);
|
|
1181
|
-
}
|
|
1107
|
+
if (sessionId) {
|
|
1108
|
+
await saveSessionToKeychain(sessionDeviceId, passphraseState, sessionId);
|
|
1109
|
+
await preloadSessionFromKeychain(sessionDeviceId);
|
|
1182
1110
|
}
|
|
1183
1111
|
|
|
1184
1112
|
return passphraseState;
|
package/src/session.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Passphrase session management for hd-cli.
|
|
3
3
|
*
|
|
4
4
|
* Aligns with app-monorepo's CLI pattern:
|
|
5
|
-
* Login:
|
|
5
|
+
* Login: openWalletSession → passphraseState + sessionId → keychain
|
|
6
6
|
* Command: keychain → preloadSessionCache → SDK call (no passphrase prompt)
|
|
7
7
|
* Stale: error 112 → clear keychain → re-prompt → retry
|
|
8
8
|
* Logout: keychain delete
|