@onekeyfe/hardware-cli 1.1.26-alpha.106 → 1.1.26-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +4 -0
- package/dist/chains.d.ts +6 -0
- package/dist/chains.js +191 -87
- package/dist/cli.js +615 -496
- package/dist/index.d.ts +16 -89
- package/dist/index.js +1 -2
- package/dist/sdk.d.ts +15 -5
- package/dist/sdk.js +237 -131
- package/dist/session.d.ts +22 -0
- package/dist/session.js +83 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.js +5 -0
- package/dist/storage/process-utils.d.ts +2 -0
- package/dist/storage/process-utils.js +44 -0
- package/dist/storage/secure-storage.linux.d.ts +11 -0
- package/dist/storage/secure-storage.linux.js +59 -0
- package/dist/storage/secure-storage.macos.d.ts +11 -0
- package/dist/storage/secure-storage.macos.js +65 -0
- package/dist/storage/storage-factory.d.ts +3 -0
- package/dist/storage/storage-factory.js +14 -0
- package/dist/storage/types.d.ts +18 -0
- package/dist/storage/types.js +2 -0
- package/package.json +15 -13
- package/src/chains.ts +229 -85
- package/src/cli.ts +620 -297
- package/src/sdk.ts +244 -125
- package/src/session.ts +89 -0
- package/src/storage/index.ts +2 -0
- package/src/storage/process-utils.ts +50 -0
- package/src/storage/secure-storage.linux.ts +68 -0
- package/src/storage/secure-storage.macos.ts +68 -0
- package/src/storage/storage-factory.ts +13 -0
- package/src/storage/types.ts +17 -0
- package/tsconfig.json +5 -7
- package/.claude-plugin/plugin.json +0 -14
- package/AGENTS.md +0 -40
- package/CLAUDE.md +0 -40
- package/README.md +0 -112
- package/evals/cases.json +0 -373
- package/evals/run-evals.sh +0 -136
- package/rollup.config.js +0 -28
package/dist/index.d.ts
CHANGED
|
@@ -1,95 +1,22 @@
|
|
|
1
|
-
import * as _onekeyfe_hd_core from '@onekeyfe/hd-core';
|
|
2
|
-
import { CoreApi } from '@onekeyfe/hd-core';
|
|
3
|
-
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
6
|
-
* for CLI usage with the appropriate transport.
|
|
2
|
+
* @onekeyfe/hardware-cli
|
|
7
3
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* OneKey hardware wallet CLI for AI agent integration.
|
|
5
|
+
* Provides device management, multi-chain signing, firmware updates,
|
|
6
|
+
* and security management capabilities.
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
passphraseState?: string;
|
|
16
|
-
useEmptyPassphrase?: boolean;
|
|
17
|
-
}
|
|
18
|
-
declare function createSDK(opts: SDKOptions): Promise<_onekeyfe_hd_core.CoreApi>;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Chain Resolver — maps chain identifiers to the correct SDK API calls.
|
|
22
|
-
* Handles derivation path defaults and chain-specific parameter transformations.
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx @onekeyfe/hardware-cli search
|
|
10
|
+
* npx @onekeyfe/hardware-cli get-address --chain evm
|
|
11
|
+
* npx @onekeyfe/hardware-cli sign-transaction --chain evm --tx '{...}'
|
|
23
12
|
*
|
|
24
|
-
*
|
|
25
|
-
* content/en/hardware-sdk/chains/<chain>/<method>.mdx
|
|
26
|
-
* content/en/hardware-sdk/core-api-guide.mdx (HD path section)
|
|
13
|
+
* All output is structured JSON for AI agent consumption.
|
|
27
14
|
*
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Common params passed to all SDK methods.
|
|
33
|
-
* Reference: packages/core/src/types/api/export.ts (CommonParams)
|
|
15
|
+
* IMPORTANT: All signing operations require physical confirmation on the
|
|
16
|
+
* hardware device. The CLI handles PIN/Passphrase prompts via stdin for
|
|
17
|
+
* interactive use, or via SDK event system for programmatic use.
|
|
34
18
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
useEmptyPassphrase?: boolean;
|
|
40
|
-
}
|
|
41
|
-
interface GetAddressParams extends CommonCLIParams {
|
|
42
|
-
chain: string;
|
|
43
|
-
path?: string;
|
|
44
|
-
showOnDevice?: boolean;
|
|
45
|
-
}
|
|
46
|
-
declare function resolveGetAddress(sdk: CoreApi, params: GetAddressParams): Promise<{
|
|
47
|
-
success: boolean;
|
|
48
|
-
error: string;
|
|
49
|
-
chain: string;
|
|
50
|
-
path: string;
|
|
51
|
-
} | {
|
|
52
|
-
chain: string;
|
|
53
|
-
path: string;
|
|
54
|
-
success?: undefined;
|
|
55
|
-
error?: undefined;
|
|
56
|
-
}>;
|
|
57
|
-
interface GetPublicKeyParams extends CommonCLIParams {
|
|
58
|
-
chain: string;
|
|
59
|
-
path?: string;
|
|
60
|
-
}
|
|
61
|
-
declare function resolveGetPublicKey(sdk: CoreApi, params: GetPublicKeyParams): Promise<{
|
|
62
|
-
chain: string;
|
|
63
|
-
path: string;
|
|
64
|
-
}>;
|
|
65
|
-
interface SignTransactionParams extends CommonCLIParams {
|
|
66
|
-
chain: string;
|
|
67
|
-
path?: string;
|
|
68
|
-
transaction: Record<string, unknown>;
|
|
69
|
-
}
|
|
70
|
-
declare function resolveSignTransaction(sdk: CoreApi, params: SignTransactionParams): Promise<{
|
|
71
|
-
chain: string;
|
|
72
|
-
path: string;
|
|
73
|
-
}>;
|
|
74
|
-
interface SignMessageParams extends CommonCLIParams {
|
|
75
|
-
chain: string;
|
|
76
|
-
path?: string;
|
|
77
|
-
message: string;
|
|
78
|
-
}
|
|
79
|
-
declare function resolveSignMessage(sdk: CoreApi, params: SignMessageParams): Promise<{
|
|
80
|
-
chain: string;
|
|
81
|
-
path: string;
|
|
82
|
-
}>;
|
|
83
|
-
interface BatchGetAddressParams extends CommonCLIParams {
|
|
84
|
-
bundle: Array<{
|
|
85
|
-
chain: string;
|
|
86
|
-
path?: string;
|
|
87
|
-
showOnDevice?: boolean;
|
|
88
|
-
}>;
|
|
89
|
-
}
|
|
90
|
-
declare function resolveBatchGetAddress(sdk: CoreApi, params: BatchGetAddressParams): Promise<{
|
|
91
|
-
success: boolean;
|
|
92
|
-
addresses: Record<string, unknown>[];
|
|
93
|
-
}>;
|
|
94
|
-
|
|
95
|
-
export { BatchGetAddressParams, CommonCLIParams, GetAddressParams, GetPublicKeyParams, SDKOptions, SignMessageParams, SignTransactionParams, createSDK, resolveBatchGetAddress, resolveGetAddress, resolveGetPublicKey, resolveSignMessage, resolveSignTransaction };
|
|
19
|
+
export { createSDK } from './sdk';
|
|
20
|
+
export type { SDKOptions } from './sdk';
|
|
21
|
+
export { resolveGetAddress, resolveGetPublicKey, resolveSignTransaction, resolveSignMessage, resolveBatchGetAddress, } from './chains';
|
|
22
|
+
export type { CommonCLIParams, GetAddressParams, GetPublicKeyParams, SignTransactionParams, SignMessageParams, BatchGetAddressParams, } from './chains';
|
package/dist/index.js
CHANGED
package/dist/sdk.d.ts
CHANGED
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
* SDK Factory — creates and initializes the hardware SDK instance
|
|
3
3
|
* for CLI usage with the appropriate transport.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Passphrase flow aligns with app-monorepo CLI:
|
|
6
|
+
* - Standard wallet: --use-empty-passphrase, auto-respond
|
|
7
|
+
* - Hidden wallet: interactive 1/2/3 selection (standard / pinentry / on-device)
|
|
8
|
+
* - Session caching: passphraseState + sessionId stored in OS keychain,
|
|
9
|
+
* preloaded via preloadSessionCache on next invocation
|
|
9
10
|
*/
|
|
11
|
+
import HardwareSDK from '@onekeyfe/hd-common-connect-sdk';
|
|
10
12
|
export interface SDKOptions {
|
|
11
13
|
connectId?: string;
|
|
12
14
|
passphraseState?: string;
|
|
13
15
|
useEmptyPassphrase?: boolean;
|
|
14
16
|
}
|
|
15
|
-
export declare function createSDK(opts: SDKOptions): Promise<
|
|
17
|
+
export declare function createSDK(opts: SDKOptions): Promise<typeof HardwareSDK>;
|
|
18
|
+
/**
|
|
19
|
+
* Release the SDK and clear the singleton. Must be called before the CLI
|
|
20
|
+
* process exits — the USB transport holds open handles (event listeners,
|
|
21
|
+
* polling timers) that otherwise keep Node.js alive for ~26s.
|
|
22
|
+
*
|
|
23
|
+
* Safe to call multiple times (no-op after the first successful call).
|
|
24
|
+
*/
|
|
25
|
+
export declare function disposeSDK(): Promise<void>;
|
package/dist/sdk.js
CHANGED
|
@@ -1,189 +1,295 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
3
2
|
/**
|
|
4
3
|
* SDK Factory — creates and initializes the hardware SDK instance
|
|
5
4
|
* for CLI usage with the appropriate transport.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Passphrase flow aligns with app-monorepo CLI:
|
|
7
|
+
* - Standard wallet: --use-empty-passphrase, auto-respond
|
|
8
|
+
* - Hidden wallet: interactive 1/2/3 selection (standard / pinentry / on-device)
|
|
9
|
+
* - Session caching: passphraseState + sessionId stored in OS keychain,
|
|
10
|
+
* preloaded via preloadSessionCache on next invocation
|
|
11
11
|
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
12
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.createSDK = void 0;
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const hd_common_connect_sdk_1 =
|
|
39
|
+
exports.disposeSDK = exports.createSDK = void 0;
|
|
40
|
+
const node_child_process_1 = require("node:child_process");
|
|
41
|
+
const readline = __importStar(require("node:readline"));
|
|
42
|
+
const hd_common_connect_sdk_1 = __importDefault(require("@onekeyfe/hd-common-connect-sdk"));
|
|
17
43
|
const hd_core_1 = require("@onekeyfe/hd-core");
|
|
18
|
-
const hd_transport_usb_1 = require("@onekeyfe/hd-transport-usb");
|
|
19
|
-
const readline = tslib_1.__importStar(require("readline"));
|
|
20
44
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
45
|
+
* Current per-invocation CLI options. Event handlers read from this object
|
|
46
|
+
* so that invoking createSDK() with different opts never results in stale
|
|
47
|
+
* closure captures — the SDK is a singleton, but opts can change per call.
|
|
48
|
+
*/
|
|
49
|
+
let currentOpts = {};
|
|
50
|
+
/**
|
|
51
|
+
* Promise-singleton that resolves to the initialised SDK. Set on first call,
|
|
52
|
+
* reused by all subsequent (and concurrent) callers.
|
|
53
|
+
*
|
|
54
|
+
* Using a Promise instead of a boolean flag eliminates the race where two
|
|
55
|
+
* concurrent createSDK() calls could both pass the "initialised?" check
|
|
56
|
+
* before the flag was set, then both run init() — each call replaces the
|
|
57
|
+
* internal Core/Connector at the hd-core layer and leaks USB handles.
|
|
58
|
+
*
|
|
59
|
+
* Mirrors app-monorepo's apps/cli/src/commands/device/hardware-sdk.ts
|
|
60
|
+
* (sdkReadyPromise). Cleared by disposeSDK() so REPL/test harnesses can
|
|
61
|
+
* re-init cleanly after an explicit tear-down.
|
|
23
62
|
*/
|
|
24
|
-
|
|
63
|
+
let sdkReadyPromise = null;
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Pinentry — secure passphrase input via native OS dialog
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
const PINENTRY_PROGRAMS = ['pinentry-mac', 'pinentry', 'pinentry-gnome3', 'pinentry-qt'];
|
|
68
|
+
function findPinentry() {
|
|
69
|
+
for (const prog of PINENTRY_PROGRAMS) {
|
|
70
|
+
try {
|
|
71
|
+
const result = (0, node_child_process_1.execFileSync)('which', [prog], {
|
|
72
|
+
encoding: 'utf-8',
|
|
73
|
+
timeout: 2000,
|
|
74
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
75
|
+
});
|
|
76
|
+
if (result.trim())
|
|
77
|
+
return prog;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// not found
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
function promptPassphraseViaPinentry() {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
const pinentryBin = findPinentry();
|
|
88
|
+
if (!pinentryBin) {
|
|
89
|
+
process.stderr.write('[onekey-hw] No pinentry found, falling back to on-device entry.\n');
|
|
90
|
+
resolve({ value: '', passphraseOnDevice: true });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const commands = [
|
|
94
|
+
'SETDESC OneKey Hardware Wallet',
|
|
95
|
+
'SETPROMPT Enter passphrase',
|
|
96
|
+
'GETPIN',
|
|
97
|
+
'BYE',
|
|
98
|
+
].join('\n');
|
|
99
|
+
const child = (0, node_child_process_1.execFile)(pinentryBin, [], { timeout: 120000, encoding: 'utf-8' }, (error, stdout) => {
|
|
100
|
+
if (error) {
|
|
101
|
+
if (error.killed || (stdout && stdout.includes('ERR 83886179'))) {
|
|
102
|
+
process.stderr.write('[onekey-hw] Passphrase entry cancelled, falling back to on-device.\n');
|
|
103
|
+
resolve({ value: '', passphraseOnDevice: true });
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
reject(error);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const dataLine = stdout.split('\n').find(l => l.startsWith('D '));
|
|
110
|
+
if (dataLine) {
|
|
111
|
+
resolve({ value: dataLine.slice(2), passphraseOnDevice: false });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (stdout.includes('ERR 83886179') || stdout.includes('Operation cancelled')) {
|
|
115
|
+
process.stderr.write('[onekey-hw] Passphrase entry cancelled, falling back to on-device.\n');
|
|
116
|
+
resolve({ value: '', passphraseOnDevice: true });
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// Empty passphrase — on-device
|
|
120
|
+
resolve({ value: '', passphraseOnDevice: true });
|
|
121
|
+
});
|
|
122
|
+
child.stdin?.write(commands);
|
|
123
|
+
child.stdin?.end();
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Interactive prompts
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
/**
|
|
130
|
+
* Prompt user to select wallet type (aligns with app-monorepo flow):
|
|
131
|
+
* 1. Standard wallet (no passphrase)
|
|
132
|
+
* 2. Hidden wallet — enter passphrase via pinentry (secure OS dialog)
|
|
133
|
+
* 3. Hidden wallet — enter passphrase on device screen
|
|
134
|
+
*/
|
|
135
|
+
function resolvePassphraseByChoice(choice) {
|
|
136
|
+
if (choice === '1')
|
|
137
|
+
return Promise.resolve({ value: '', passphraseOnDevice: false });
|
|
138
|
+
if (choice === '2')
|
|
139
|
+
return promptPassphraseViaPinentry();
|
|
140
|
+
return Promise.resolve({ value: '', passphraseOnDevice: true });
|
|
141
|
+
}
|
|
142
|
+
function promptPassphraseMode() {
|
|
25
143
|
if (!process.stdin.isTTY) {
|
|
26
|
-
|
|
27
|
-
return Promise.resolve('');
|
|
144
|
+
return Promise.resolve({ value: '', passphraseOnDevice: true });
|
|
28
145
|
}
|
|
29
146
|
return new Promise(resolve => {
|
|
30
147
|
const rl = readline.createInterface({
|
|
31
148
|
input: process.stdin,
|
|
32
|
-
output: process.stderr,
|
|
149
|
+
output: process.stderr,
|
|
150
|
+
terminal: true,
|
|
33
151
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
if (stdin.setRawMode)
|
|
46
|
-
stdin.setRawMode(wasRaw ?? false);
|
|
47
|
-
stdin.removeListener('data', onData);
|
|
48
|
-
process.stderr.write('\n');
|
|
152
|
+
const prompt = () => {
|
|
153
|
+
process.stderr.write([
|
|
154
|
+
'[onekey-hw] Select wallet type:',
|
|
155
|
+
' 1. Standard wallet (no passphrase)',
|
|
156
|
+
' 2. Hidden wallet — enter passphrase on this computer (pinentry)',
|
|
157
|
+
' 3. Hidden wallet — enter passphrase on device screen',
|
|
158
|
+
'',
|
|
159
|
+
].join('\n'));
|
|
160
|
+
rl.question('Enter selection [1/2/3]: ', answer => {
|
|
161
|
+
const n = answer.trim();
|
|
162
|
+
if (n === '1' || n === '2' || n === '3') {
|
|
49
163
|
rl.close();
|
|
50
|
-
|
|
164
|
+
resolvePassphraseByChoice(n).then(resolve);
|
|
165
|
+
return;
|
|
51
166
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
else if (c === '\u007F' || c === '\b') {
|
|
57
|
-
// Backspace
|
|
58
|
-
input = input.slice(0, -1);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
input += c;
|
|
62
|
-
process.stderr.write('*');
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
stdin.on('data', onData);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
rl.question(question, answer => {
|
|
69
|
-
rl.close();
|
|
70
|
-
resolve(answer);
|
|
167
|
+
process.stderr.write('Invalid selection. Enter 1, 2, or 3.\n');
|
|
168
|
+
prompt();
|
|
71
169
|
});
|
|
72
|
-
}
|
|
170
|
+
};
|
|
171
|
+
prompt();
|
|
73
172
|
});
|
|
74
173
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* - PIN entry (entered on device screen for Touch/Pro, or via matrix for Classic)
|
|
80
|
-
* - Passphrase input (for hidden wallets)
|
|
81
|
-
* - Button confirmation (user must physically press on device)
|
|
82
|
-
*
|
|
83
|
-
* Reference: packages/core/src/core/index.ts lines 315-330, 1021-1098
|
|
84
|
-
*/
|
|
85
|
-
function registerEventHandlers(sdk, opts) {
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Event handlers
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
function registerEventHandlers(sdk) {
|
|
86
178
|
sdk.on(hd_core_1.UI_EVENT, (message) => {
|
|
87
|
-
// PIN Request
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
179
|
+
// PIN Request — always on-device for CLI security (no terminal echo).
|
|
180
|
+
// The sentinel '@@ONEKEY_INPUT_PIN_IN_DEVICE' makes DeviceCommands send
|
|
181
|
+
// `BixinPinInputOnDevice` so the device switches to on-device PIN entry.
|
|
182
|
+
// Sending an empty string would be treated as a wrong (empty) PIN and
|
|
183
|
+
// would consume a PIN retry on Classic/1S/Mini/Pure.
|
|
184
|
+
// Touch/Pro never emit PinMatrixRequest — they handle PIN on touchscreen
|
|
185
|
+
// before this code path runs, so the sentinel is harmless there.
|
|
91
186
|
if (message.type === hd_core_1.UI_REQUEST.REQUEST_PIN) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
// Classic devices: PIN entry via matrix
|
|
100
|
-
// In CLI mode, prompt user or let agent handle
|
|
101
|
-
process.stderr.write('[onekey-hw] PIN required. Please enter PIN on your device.\n');
|
|
102
|
-
promptUser('PIN (on-device numpad mapping): ', true).then(pin => {
|
|
103
|
-
sdk.uiResponse({
|
|
104
|
-
type: hd_core_1.UI_RESPONSE.RECEIVE_PIN,
|
|
105
|
-
payload: pin,
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
}
|
|
187
|
+
process.stderr.write('[onekey-hw] Please enter PIN on your device screen...\n');
|
|
188
|
+
sdk.uiResponse({
|
|
189
|
+
type: hd_core_1.UI_RESPONSE.RECEIVE_PIN,
|
|
190
|
+
payload: '@@ONEKEY_INPUT_PIN_IN_DEVICE',
|
|
191
|
+
});
|
|
109
192
|
}
|
|
110
193
|
// Passphrase Request
|
|
111
|
-
// User must provide passphrase for hidden wallet access.
|
|
112
|
-
// Passphrase can be entered on-device (Touch/Pro) or via host.
|
|
113
194
|
if (message.type === hd_core_1.UI_REQUEST.REQUEST_PASSPHRASE) {
|
|
114
|
-
|
|
115
|
-
|
|
195
|
+
// 1. Explicit --use-empty-passphrase: auto-respond
|
|
196
|
+
if (currentOpts.useEmptyPassphrase) {
|
|
197
|
+
sdk.uiResponse({
|
|
198
|
+
type: hd_core_1.UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
199
|
+
payload: { value: '', passphraseOnDevice: false, save: false },
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
// 2. Interactive: 1/2/3 selection
|
|
204
|
+
promptPassphraseMode()
|
|
205
|
+
.then(result => {
|
|
116
206
|
sdk.uiResponse({
|
|
117
207
|
type: hd_core_1.UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
118
208
|
payload: {
|
|
119
|
-
value:
|
|
120
|
-
passphraseOnDevice:
|
|
209
|
+
value: result.value,
|
|
210
|
+
passphraseOnDevice: result.passphraseOnDevice,
|
|
121
211
|
save: false,
|
|
122
212
|
},
|
|
123
213
|
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
process.stderr.write('[onekey-hw] Passphrase
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
sdk.uiResponse({
|
|
131
|
-
type: hd_core_1.UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
132
|
-
payload: {
|
|
133
|
-
value: '',
|
|
134
|
-
passphraseOnDevice: true,
|
|
135
|
-
save: false,
|
|
136
|
-
},
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
sdk.uiResponse({
|
|
141
|
-
type: hd_core_1.UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
142
|
-
payload: {
|
|
143
|
-
value: passphrase,
|
|
144
|
-
passphraseOnDevice: false,
|
|
145
|
-
save: false,
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
}
|
|
214
|
+
})
|
|
215
|
+
.catch(() => {
|
|
216
|
+
process.stderr.write('[onekey-hw] Passphrase prompt failed, falling back to on-device.\n');
|
|
217
|
+
sdk.uiResponse({
|
|
218
|
+
type: hd_core_1.UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
219
|
+
payload: { value: '', passphraseOnDevice: true, save: false },
|
|
149
220
|
});
|
|
150
|
-
}
|
|
221
|
+
});
|
|
151
222
|
}
|
|
152
223
|
// Passphrase On Device
|
|
153
224
|
if (message.type === hd_core_1.UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE) {
|
|
154
225
|
process.stderr.write('[onekey-hw] Please enter passphrase on your device screen...\n');
|
|
155
226
|
}
|
|
156
227
|
// Button Confirmation
|
|
157
|
-
// User must physically press confirm/reject on the device.
|
|
158
228
|
if (message.type === hd_core_1.UI_REQUEST.REQUEST_BUTTON) {
|
|
159
229
|
process.stderr.write('[onekey-hw] Please confirm the action on your device...\n');
|
|
160
230
|
}
|
|
161
231
|
});
|
|
162
|
-
// Device connection events — only show when device has a known name
|
|
163
232
|
sdk.on(hd_core_1.DEVICE.CONNECT, (device) => {
|
|
164
233
|
const name = device?.label || device?.name;
|
|
165
|
-
if (name)
|
|
234
|
+
if (name)
|
|
166
235
|
process.stderr.write(`[onekey-hw] Device connected: ${name}\n`);
|
|
167
|
-
}
|
|
168
236
|
});
|
|
169
237
|
sdk.on(hd_core_1.DEVICE.DISCONNECT, (device) => {
|
|
170
238
|
const name = device?.label || device?.name;
|
|
171
|
-
if (name)
|
|
239
|
+
if (name)
|
|
172
240
|
process.stderr.write(`[onekey-hw] Device disconnected: ${name}\n`);
|
|
173
|
-
}
|
|
174
241
|
});
|
|
175
242
|
}
|
|
176
|
-
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// SDK Factory
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
async function initSDK() {
|
|
177
247
|
const settings = {
|
|
178
248
|
debug: false,
|
|
179
249
|
fetchConfig: true,
|
|
250
|
+
env: 'node-usb',
|
|
180
251
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
|
|
252
|
+
await hd_common_connect_sdk_1.default.init(settings);
|
|
253
|
+
// Defensive: strip any stale listeners (e.g. left over from a previous
|
|
254
|
+
// dispose/init cycle in a long-running process) before wiring ours.
|
|
255
|
+
// Mirrors app-monorepo's cleanupHardwareSDKInstance() which removes
|
|
256
|
+
// listeners for ALL events at once.
|
|
257
|
+
// @ts-expect-error CoreApi types require an event name; passing none
|
|
258
|
+
// removes listeners for ALL events (Node.js EventEmitter semantics).
|
|
259
|
+
hd_common_connect_sdk_1.default.removeAllListeners();
|
|
260
|
+
registerEventHandlers(hd_common_connect_sdk_1.default);
|
|
187
261
|
return hd_common_connect_sdk_1.default;
|
|
188
262
|
}
|
|
263
|
+
async function createSDK(opts) {
|
|
264
|
+
// Always refresh opts — event handlers read from the module-level ref,
|
|
265
|
+
// so subsequent createSDK() calls pick up new opts without re-registering.
|
|
266
|
+
currentOpts = opts;
|
|
267
|
+
if (!sdkReadyPromise) {
|
|
268
|
+
sdkReadyPromise = initSDK();
|
|
269
|
+
}
|
|
270
|
+
return sdkReadyPromise;
|
|
271
|
+
}
|
|
189
272
|
exports.createSDK = createSDK;
|
|
273
|
+
/**
|
|
274
|
+
* Release the SDK and clear the singleton. Must be called before the CLI
|
|
275
|
+
* process exits — the USB transport holds open handles (event listeners,
|
|
276
|
+
* polling timers) that otherwise keep Node.js alive for ~26s.
|
|
277
|
+
*
|
|
278
|
+
* Safe to call multiple times (no-op after the first successful call).
|
|
279
|
+
*/
|
|
280
|
+
async function disposeSDK() {
|
|
281
|
+
if (!sdkReadyPromise)
|
|
282
|
+
return;
|
|
283
|
+
try {
|
|
284
|
+
const sdk = await sdkReadyPromise;
|
|
285
|
+
sdk.dispose();
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// ignore errors during cleanup
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
sdkReadyPromise = null;
|
|
292
|
+
currentOpts = {};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
exports.disposeSDK = disposeSDK;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passphrase session management for hd-cli.
|
|
3
|
+
*
|
|
4
|
+
* Aligns with app-monorepo's CLI pattern:
|
|
5
|
+
* Login: getPassphraseState → passphraseState + sessionId → keychain
|
|
6
|
+
* Command: keychain → preloadSessionCache → SDK call (no passphrase prompt)
|
|
7
|
+
* Stale: error 112 → clear keychain → re-prompt → retry
|
|
8
|
+
* Logout: keychain delete
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Load passphraseState + sessionId from keychain and call preloadSessionCache.
|
|
12
|
+
* Non-fatal: returns the loaded passphraseState or undefined.
|
|
13
|
+
*/
|
|
14
|
+
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
|
+
/**
|
|
20
|
+
* Clear cached session from keychain.
|
|
21
|
+
*/
|
|
22
|
+
export declare function clearSessionFromKeychain(deviceId?: string): Promise<void>;
|