@onekeyfe/hardware-cli 1.2.0-alpha.1 → 1.2.0-alpha.100
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 +83 -1
- package/dist/cli.js +501 -142
- package/dist/deviceSelection.d.ts +3 -0
- package/dist/deviceSelection.js +11 -0
- package/dist/deviceStateCommands.d.ts +19 -0
- package/dist/deviceStateCommands.js +62 -0
- package/dist/pinentry.d.ts +1 -0
- package/dist/sdk.d.ts +12 -2
- package/dist/sdk.js +22 -11
- package/dist/session.d.ts +2 -9
- package/dist/session.js +3 -22
- package/dist/transports/nobleBlePlugin.d.ts +2 -0
- package/dist/transports/nobleBlePlugin.js +371 -0
- package/package.json +8 -6
- package/src/__tests__/cli-version.test.ts +8 -0
- package/src/__tests__/device-selection.test.ts +29 -0
- package/src/__tests__/device-state-commands.test.ts +118 -0
- package/src/__tests__/firmware-update-legacy-command.test.ts +18 -0
- package/src/__tests__/firmware-update-v4-command.test.ts +101 -0
- package/src/__tests__/noble-ble-plugin.test.ts +370 -0
- package/src/__tests__/wallet-session.test.ts +64 -0
- package/src/__tests__/wallpaper-upload-command.test.ts +46 -0
- package/src/cli.ts +687 -173
- package/src/deviceSelection.ts +13 -0
- package/src/deviceStateCommands.ts +71 -0
- package/src/pinentry.ts +1 -0
- package/src/sdk.ts +29 -13
- package/src/session.ts +2 -24
- package/src/transports/nobleBlePlugin.ts +487 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.selectSearchDevice = void 0;
|
|
4
|
+
function selectSearchDevice(devices, preferredConnectId) {
|
|
5
|
+
if (preferredConnectId) {
|
|
6
|
+
return (devices.find(device => device.connectId === preferredConnectId) ??
|
|
7
|
+
{ connectId: preferredConnectId });
|
|
8
|
+
}
|
|
9
|
+
return devices[0];
|
|
10
|
+
}
|
|
11
|
+
exports.selectSearchDevice = selectSearchDevice;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CoreApi, DeviceStateScope } from '@onekeyfe/hd-core';
|
|
2
|
+
type DeviceStateSdk = Pick<CoreApi, 'searchDevices' | 'getDeviceState' | 'getFeatures'>;
|
|
3
|
+
/**
|
|
4
|
+
* Unified state entry for the new CLI. Resolve the user-facing connectId through
|
|
5
|
+
* discovery so V1 serial IDs map to the process-local USB path.
|
|
6
|
+
*/
|
|
7
|
+
export declare const getCanonicalDeviceState: (sdk: DeviceStateSdk, connectId: string | undefined, scope: DeviceStateScope) => Promise<import("@onekeyfe/hd-core").Unsuccessful | {
|
|
8
|
+
success: true;
|
|
9
|
+
payload: import("@onekeyfe/hd-core").DeviceState;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Legacy CLI only: retain getFeatures for V1 and reuse the discovery projection for V2.
|
|
13
|
+
* Public SDK.getFeatures remains V1-only.
|
|
14
|
+
*/
|
|
15
|
+
export declare const getCompatibleFeatures: (sdk: DeviceStateSdk, connectId?: string) => Promise<import("@onekeyfe/hd-core").Unsuccessful | {
|
|
16
|
+
success: true;
|
|
17
|
+
payload: import("@onekeyfe/hd-core").Features;
|
|
18
|
+
}>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCompatibleFeatures = exports.getCanonicalDeviceState = void 0;
|
|
4
|
+
const createDeviceNotFoundResult = (connectId) => ({
|
|
5
|
+
success: false,
|
|
6
|
+
payload: {
|
|
7
|
+
code: 'DEVICE_NOT_FOUND',
|
|
8
|
+
error: connectId ? `Device not found: ${connectId}` : 'No device found',
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
const resolveSearchDevice = async (sdk, connectId) => {
|
|
12
|
+
const searchResult = await sdk.searchDevices();
|
|
13
|
+
if (!searchResult.success)
|
|
14
|
+
return searchResult;
|
|
15
|
+
const devices = searchResult.payload;
|
|
16
|
+
const device = connectId ? devices.find(item => item.connectId === connectId) : devices[0];
|
|
17
|
+
if (!device?.connectId)
|
|
18
|
+
return createDeviceNotFoundResult(connectId);
|
|
19
|
+
return { success: true, payload: device };
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Unified state entry for the new CLI. Resolve the user-facing connectId through
|
|
23
|
+
* discovery so V1 serial IDs map to the process-local USB path.
|
|
24
|
+
*/
|
|
25
|
+
const getCanonicalDeviceState = async (sdk, connectId, scope) => {
|
|
26
|
+
const deviceResult = await resolveSearchDevice(sdk, connectId);
|
|
27
|
+
if (!deviceResult.success)
|
|
28
|
+
return deviceResult;
|
|
29
|
+
if (scope === 'runtime' && deviceResult.payload.state) {
|
|
30
|
+
return { success: true, payload: deviceResult.payload.state };
|
|
31
|
+
}
|
|
32
|
+
const resolvedConnectId = deviceResult.payload.connectId ?? undefined;
|
|
33
|
+
if (!resolvedConnectId)
|
|
34
|
+
return createDeviceNotFoundResult();
|
|
35
|
+
return sdk.getDeviceState(resolvedConnectId, { scope });
|
|
36
|
+
};
|
|
37
|
+
exports.getCanonicalDeviceState = getCanonicalDeviceState;
|
|
38
|
+
/**
|
|
39
|
+
* Legacy CLI only: retain getFeatures for V1 and reuse the discovery projection for V2.
|
|
40
|
+
* Public SDK.getFeatures remains V1-only.
|
|
41
|
+
*/
|
|
42
|
+
const getCompatibleFeatures = async (sdk, connectId) => {
|
|
43
|
+
const deviceResult = await resolveSearchDevice(sdk, connectId);
|
|
44
|
+
if (!deviceResult.success)
|
|
45
|
+
return deviceResult;
|
|
46
|
+
const device = deviceResult.payload;
|
|
47
|
+
const protocol = device.state?.protocol ?? device.features?.protocol;
|
|
48
|
+
if (protocol === 'V2') {
|
|
49
|
+
if (!device.features) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
payload: {
|
|
53
|
+
code: 'DEVICE_FEATURES_UNAVAILABLE',
|
|
54
|
+
error: 'Protocol V2 compatibility features are unavailable',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return { success: true, payload: device.features };
|
|
59
|
+
}
|
|
60
|
+
return sdk.getFeatures(device.connectId ?? '');
|
|
61
|
+
};
|
|
62
|
+
exports.getCompatibleFeatures = getCompatibleFeatures;
|
package/dist/pinentry.d.ts
CHANGED
package/dist/sdk.d.ts
CHANGED
|
@@ -5,15 +5,25 @@
|
|
|
5
5
|
* Passphrase flow aligns with app-monorepo CLI:
|
|
6
6
|
* - Standard wallet: --use-empty-passphrase, auto-respond
|
|
7
7
|
* - Hidden wallet: interactive 1/2/3 selection (standard / pinentry / on-device)
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
8
|
+
* - Legacy session caching: existing OS keychain entries may still be preloaded,
|
|
9
|
+
* but public SDK responses never expose new device session ids
|
|
10
10
|
*/
|
|
11
11
|
import HardwareSDK from '@onekeyfe/hd-common-connect-sdk';
|
|
12
|
+
import type { PinentryResult } from './pinentry';
|
|
12
13
|
export interface SDKOptions {
|
|
13
14
|
connectId?: string;
|
|
14
15
|
passphraseState?: string;
|
|
15
16
|
useEmptyPassphrase?: boolean;
|
|
17
|
+
debug?: boolean;
|
|
18
|
+
transport?: 'usb' | 'ble';
|
|
16
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Prompt user to select wallet type (aligns with app-monorepo flow):
|
|
22
|
+
* 1. Standard wallet (no passphrase)
|
|
23
|
+
* 2. Hidden wallet — enter passphrase via pinentry (secure OS dialog)
|
|
24
|
+
* 3. Hidden wallet — enter passphrase on device screen
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolvePassphraseByChoice(choice: '1' | '2' | '3' | '4'): Promise<PinentryResult>;
|
|
17
27
|
export declare function createSDK(opts: SDKOptions): Promise<typeof HardwareSDK>;
|
|
18
28
|
/**
|
|
19
29
|
* Release the SDK and clear the singleton. Must be called before the CLI
|
package/dist/sdk.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* Passphrase flow aligns with app-monorepo CLI:
|
|
7
7
|
* - Standard wallet: --use-empty-passphrase, auto-respond
|
|
8
8
|
* - Hidden wallet: interactive 1/2/3 selection (standard / pinentry / on-device)
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
9
|
+
* - Legacy session caching: existing OS keychain entries may still be preloaded,
|
|
10
|
+
* but public SDK responses never expose new device session ids
|
|
11
11
|
*/
|
|
12
12
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
13
|
if (k2 === undefined) k2 = k;
|
|
@@ -36,11 +36,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.disposeSDK = exports.createSDK = void 0;
|
|
39
|
+
exports.disposeSDK = exports.createSDK = exports.resolvePassphraseByChoice = void 0;
|
|
40
40
|
const readline = __importStar(require("node:readline"));
|
|
41
41
|
const hd_common_connect_sdk_1 = __importDefault(require("@onekeyfe/hd-common-connect-sdk"));
|
|
42
42
|
const hd_core_1 = require("@onekeyfe/hd-core");
|
|
43
43
|
const pinentry_1 = require("./pinentry");
|
|
44
|
+
const nobleBlePlugin_1 = require("./transports/nobleBlePlugin");
|
|
44
45
|
/**
|
|
45
46
|
* Current per-invocation CLI options. Event handlers read from this object
|
|
46
47
|
* so that invoking createSDK() with different opts never results in stale
|
|
@@ -75,8 +76,15 @@ function resolvePassphraseByChoice(choice) {
|
|
|
75
76
|
return Promise.resolve({ value: '', passphraseOnDevice: false });
|
|
76
77
|
if (choice === '2')
|
|
77
78
|
return (0, pinentry_1.promptPassphraseViaPinentry)();
|
|
79
|
+
if (choice === '4')
|
|
80
|
+
return Promise.resolve({
|
|
81
|
+
value: '',
|
|
82
|
+
passphraseOnDevice: false,
|
|
83
|
+
attachPinOnDevice: true,
|
|
84
|
+
});
|
|
78
85
|
return Promise.resolve({ value: '', passphraseOnDevice: true });
|
|
79
86
|
}
|
|
87
|
+
exports.resolvePassphraseByChoice = resolvePassphraseByChoice;
|
|
80
88
|
function promptPassphraseMode() {
|
|
81
89
|
if (!process.stdin.isTTY) {
|
|
82
90
|
return Promise.resolve({ value: '', passphraseOnDevice: true });
|
|
@@ -93,16 +101,17 @@ function promptPassphraseMode() {
|
|
|
93
101
|
' 1. Standard wallet (no passphrase)',
|
|
94
102
|
' 2. Hidden wallet — enter passphrase on this computer (pinentry)',
|
|
95
103
|
' 3. Hidden wallet — enter passphrase on device screen',
|
|
104
|
+
' 4. Attach PIN wallet — enter Attach PIN on device screen',
|
|
96
105
|
'',
|
|
97
106
|
].join('\n'));
|
|
98
107
|
rl.question('Enter selection [1/2/3]: ', answer => {
|
|
99
108
|
const n = answer.trim();
|
|
100
|
-
if (n === '1' || n === '2' || n === '3') {
|
|
109
|
+
if (n === '1' || n === '2' || n === '3' || n === '4') {
|
|
101
110
|
rl.close();
|
|
102
111
|
resolvePassphraseByChoice(n).then(resolve);
|
|
103
112
|
return;
|
|
104
113
|
}
|
|
105
|
-
process.stderr.write('Invalid selection. Enter 1, 2, or
|
|
114
|
+
process.stderr.write('Invalid selection. Enter 1, 2, 3, or 4.\n');
|
|
106
115
|
prompt();
|
|
107
116
|
});
|
|
108
117
|
};
|
|
@@ -146,6 +155,7 @@ function registerEventHandlers(sdk) {
|
|
|
146
155
|
payload: {
|
|
147
156
|
value: result.value,
|
|
148
157
|
passphraseOnDevice: result.passphraseOnDevice,
|
|
158
|
+
attachPinOnDevice: result.attachPinOnDevice,
|
|
149
159
|
save: false,
|
|
150
160
|
},
|
|
151
161
|
});
|
|
@@ -167,12 +177,12 @@ function registerEventHandlers(sdk) {
|
|
|
167
177
|
process.stderr.write('[onekey-hw] Please confirm the action on your device...\n');
|
|
168
178
|
}
|
|
169
179
|
});
|
|
170
|
-
sdk.on(hd_core_1.DEVICE.CONNECT, (device) => {
|
|
180
|
+
sdk.on(hd_core_1.DEVICE.CONNECT, ({ device }) => {
|
|
171
181
|
const name = device?.label || device?.name;
|
|
172
182
|
if (name)
|
|
173
183
|
process.stderr.write(`[onekey-hw] Device connected: ${name}\n`);
|
|
174
184
|
});
|
|
175
|
-
sdk.on(hd_core_1.DEVICE.DISCONNECT, (device) => {
|
|
185
|
+
sdk.on(hd_core_1.DEVICE.DISCONNECT, ({ device }) => {
|
|
176
186
|
const name = device?.label || device?.name;
|
|
177
187
|
if (name)
|
|
178
188
|
process.stderr.write(`[onekey-hw] Device disconnected: ${name}\n`);
|
|
@@ -182,12 +192,13 @@ function registerEventHandlers(sdk) {
|
|
|
182
192
|
// SDK Factory
|
|
183
193
|
// ---------------------------------------------------------------------------
|
|
184
194
|
async function initSDK() {
|
|
195
|
+
const transport = currentOpts.transport ?? 'usb';
|
|
185
196
|
const settings = {
|
|
186
|
-
debug: false,
|
|
197
|
+
debug: currentOpts.debug ?? false,
|
|
187
198
|
fetchConfig: true,
|
|
188
|
-
env: 'node-usb',
|
|
199
|
+
env: transport === 'ble' ? 'lowlevel' : 'node-usb',
|
|
189
200
|
};
|
|
190
|
-
await hd_common_connect_sdk_1.default.init(settings);
|
|
201
|
+
await hd_common_connect_sdk_1.default.init(settings, undefined, transport === 'ble' ? (0, nobleBlePlugin_1.createNobleBlePlugin)() : undefined);
|
|
191
202
|
// Defensive: strip any stale listeners (e.g. left over from a previous
|
|
192
203
|
// dispose/init cycle in a long-running process) before wiring ours.
|
|
193
204
|
// Mirrors app-monorepo's cleanupHardwareSDKInstance() which removes
|
|
@@ -220,7 +231,7 @@ async function disposeSDK() {
|
|
|
220
231
|
return;
|
|
221
232
|
try {
|
|
222
233
|
const sdk = await sdkReadyPromise;
|
|
223
|
-
sdk.dispose();
|
|
234
|
+
await Promise.resolve(sdk.dispose());
|
|
224
235
|
}
|
|
225
236
|
catch {
|
|
226
237
|
// ignore errors during cleanup
|
package/dist/session.d.ts
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Passphrase session management for hd-cli.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Command: keychain → preloadSessionCache → SDK call (no passphrase prompt)
|
|
7
|
-
* Stale: error 112 → clear keychain → re-prompt → retry
|
|
8
|
-
* Logout: keychain delete
|
|
4
|
+
* Existing keychain entries remain readable for compatibility, but the public
|
|
5
|
+
* SDK no longer exposes new device session ids for persistence.
|
|
9
6
|
*/
|
|
10
7
|
/**
|
|
11
8
|
* Load passphraseState + sessionId from keychain and call preloadSessionCache.
|
|
12
9
|
* Non-fatal: returns the loaded passphraseState or undefined.
|
|
13
10
|
*/
|
|
14
11
|
export declare function preloadSessionFromKeychain(deviceId: string): Promise<string | undefined>;
|
|
15
|
-
/**
|
|
16
|
-
* Save passphraseState + sessionId to keychain for next CLI invocation.
|
|
17
|
-
*/
|
|
18
|
-
export declare function saveSessionToKeychain(deviceId: string, passphraseState: string, sessionId: string): Promise<void>;
|
|
19
12
|
/**
|
|
20
13
|
* Clear cached session from keychain.
|
|
21
14
|
*/
|
package/dist/session.js
CHANGED
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Passphrase session management for hd-cli.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Command: keychain → preloadSessionCache → SDK call (no passphrase prompt)
|
|
8
|
-
* Stale: error 112 → clear keychain → re-prompt → retry
|
|
9
|
-
* Logout: keychain delete
|
|
5
|
+
* Existing keychain entries remain readable for compatibility, but the public
|
|
6
|
+
* SDK no longer exposes new device session ids for persistence.
|
|
10
7
|
*/
|
|
11
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.clearSessionFromKeychain = exports.
|
|
9
|
+
exports.clearSessionFromKeychain = exports.preloadSessionFromKeychain = void 0;
|
|
13
10
|
const hd_core_1 = require("@onekeyfe/hd-core");
|
|
14
11
|
const storage_1 = require("./storage");
|
|
15
12
|
// Keychain key format: scoped by deviceId for multi-device support
|
|
@@ -50,22 +47,6 @@ async function preloadSessionFromKeychain(deviceId) {
|
|
|
50
47
|
return undefined;
|
|
51
48
|
}
|
|
52
49
|
exports.preloadSessionFromKeychain = preloadSessionFromKeychain;
|
|
53
|
-
/**
|
|
54
|
-
* Save passphraseState + sessionId to keychain for next CLI invocation.
|
|
55
|
-
*/
|
|
56
|
-
async function saveSessionToKeychain(deviceId, passphraseState, sessionId) {
|
|
57
|
-
try {
|
|
58
|
-
const storage = getStorage();
|
|
59
|
-
await Promise.all([
|
|
60
|
-
storage.set(psKey(deviceId), Buffer.from(passphraseState, 'utf-8')),
|
|
61
|
-
storage.set(sidKey(deviceId), Buffer.from(sessionId, 'utf-8')),
|
|
62
|
-
]);
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// Non-fatal — session still works in-memory for this invocation
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
exports.saveSessionToKeychain = saveSessionToKeychain;
|
|
69
50
|
/**
|
|
70
51
|
* Clear cached session from keychain.
|
|
71
52
|
*/
|