@onekeyfe/hardware-cli 1.1.25-alpha.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/.claude-plugin/plugin.json +14 -0
- package/dist/chains.d.ts +74 -0
- package/dist/chains.js +307 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +824 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +30 -0
- package/dist/sdk.d.ts +16 -0
- package/dist/sdk.js +187 -0
- package/evals/cases.json +564 -0
- package/evals/run-evals.sh +136 -0
- package/package.json +34 -0
- package/rollup.config.js +28 -0
- package/skills/device/SKILL.md +191 -0
- package/skills/firmware/SKILL.md +163 -0
- package/skills/security/SKILL.md +328 -0
- package/skills/signing/SKILL.md +478 -0
- package/src/chains.ts +387 -0
- package/src/cli.ts +855 -0
- package/src/index.ts +38 -0
- package/src/sdk.ts +198 -0
- package/tsconfig.json +18 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @onekeyfe/hardware-cli
|
|
3
|
+
*
|
|
4
|
+
* OneKey hardware wallet CLI for AI agent integration.
|
|
5
|
+
* Provides device management, multi-chain signing, firmware updates,
|
|
6
|
+
* and security management capabilities.
|
|
7
|
+
*
|
|
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 '{...}'
|
|
12
|
+
*
|
|
13
|
+
* For AI agent integration, use --json flag for structured output.
|
|
14
|
+
*
|
|
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.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export { createSDK } from './sdk';
|
|
21
|
+
export type { SDKOptions } from './sdk';
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
resolveGetAddress,
|
|
25
|
+
resolveGetPublicKey,
|
|
26
|
+
resolveSignTransaction,
|
|
27
|
+
resolveSignMessage,
|
|
28
|
+
resolveBatchGetAddress,
|
|
29
|
+
} from './chains';
|
|
30
|
+
|
|
31
|
+
export type {
|
|
32
|
+
CommonCLIParams,
|
|
33
|
+
GetAddressParams,
|
|
34
|
+
GetPublicKeyParams,
|
|
35
|
+
SignTransactionParams,
|
|
36
|
+
SignMessageParams,
|
|
37
|
+
BatchGetAddressParams,
|
|
38
|
+
} from './chains';
|
package/src/sdk.ts
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK Factory — creates and initializes the hardware SDK instance
|
|
3
|
+
* for CLI usage with the appropriate transport.
|
|
4
|
+
*
|
|
5
|
+
* CRITICAL: Must register UI event handlers for PIN, Passphrase, and Button
|
|
6
|
+
* confirmation. Without these, the SDK will hang waiting for responses.
|
|
7
|
+
*
|
|
8
|
+
* Reference: packages/core/src/core/index.ts (event registration pattern)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// @ts-ignore - hd-common-connect-sdk may not have type declarations
|
|
12
|
+
import HardwareSDK from '@onekeyfe/hd-common-connect-sdk';
|
|
13
|
+
import { DEVICE, UI_EVENT, UI_REQUEST, UI_RESPONSE } from '@onekeyfe/hd-core';
|
|
14
|
+
import { UsbPlugin } from '@onekeyfe/hd-transport-usb';
|
|
15
|
+
import * as readline from 'readline';
|
|
16
|
+
|
|
17
|
+
import type { ConnectSettings } from '@onekeyfe/hd-core';
|
|
18
|
+
|
|
19
|
+
export interface SDKOptions {
|
|
20
|
+
json?: boolean;
|
|
21
|
+
connectId?: string;
|
|
22
|
+
passphraseState?: string;
|
|
23
|
+
useEmptyPassphrase?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Prompt user for input in the terminal (hidden for PIN).
|
|
28
|
+
* Falls back to empty string in non-TTY (piped) mode.
|
|
29
|
+
*/
|
|
30
|
+
function promptUser(question: string, hidden = false): Promise<string> {
|
|
31
|
+
if (!process.stdin.isTTY) {
|
|
32
|
+
// Non-interactive mode: return empty (agent should handle via uiResponse)
|
|
33
|
+
return Promise.resolve('');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return new Promise(resolve => {
|
|
37
|
+
const rl = readline.createInterface({
|
|
38
|
+
input: process.stdin,
|
|
39
|
+
output: process.stderr, // Use stderr so JSON stdout stays clean
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (hidden) {
|
|
43
|
+
// Mute output for PIN entry
|
|
44
|
+
process.stderr.write(question);
|
|
45
|
+
const { stdin } = process;
|
|
46
|
+
const wasRaw = stdin.isRaw;
|
|
47
|
+
if (stdin.setRawMode) stdin.setRawMode(true);
|
|
48
|
+
|
|
49
|
+
let input = '';
|
|
50
|
+
const onData = (char: Buffer) => {
|
|
51
|
+
const c = char.toString('utf8');
|
|
52
|
+
if (c === '\n' || c === '\r' || c === '\u0004') {
|
|
53
|
+
if (stdin.setRawMode) stdin.setRawMode(wasRaw ?? false);
|
|
54
|
+
stdin.removeListener('data', onData);
|
|
55
|
+
process.stderr.write('\n');
|
|
56
|
+
rl.close();
|
|
57
|
+
resolve(input);
|
|
58
|
+
} else if (c === '\u0003') {
|
|
59
|
+
// Ctrl+C
|
|
60
|
+
process.exit(1);
|
|
61
|
+
} else if (c === '\u007F' || c === '\b') {
|
|
62
|
+
// Backspace
|
|
63
|
+
input = input.slice(0, -1);
|
|
64
|
+
} else {
|
|
65
|
+
input += c;
|
|
66
|
+
process.stderr.write('*');
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
stdin.on('data', onData);
|
|
70
|
+
} else {
|
|
71
|
+
rl.question(question, answer => {
|
|
72
|
+
rl.close();
|
|
73
|
+
resolve(answer);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Register UI event handlers for interactive device operations.
|
|
81
|
+
*
|
|
82
|
+
* The SDK emits events when the device needs user interaction:
|
|
83
|
+
* - PIN entry (entered on device screen for Touch/Pro, or via matrix for Classic)
|
|
84
|
+
* - Passphrase input (for hidden wallets)
|
|
85
|
+
* - Button confirmation (user must physically press on device)
|
|
86
|
+
*
|
|
87
|
+
* Reference: packages/core/src/core/index.ts lines 315-330, 1021-1098
|
|
88
|
+
*/
|
|
89
|
+
function registerEventHandlers(sdk: typeof HardwareSDK, opts: SDKOptions): void {
|
|
90
|
+
sdk.on(UI_EVENT, (message: any) => {
|
|
91
|
+
// PIN Request
|
|
92
|
+
// For Touch/Pro devices, PIN is entered on-device (device screen shows numpad).
|
|
93
|
+
// For Classic devices, PIN uses a matrix mapping.
|
|
94
|
+
// In CLI context, we auto-acknowledge since PIN entry happens on-device.
|
|
95
|
+
if (message.type === UI_REQUEST.REQUEST_PIN) {
|
|
96
|
+
const pinType = message.payload?.type;
|
|
97
|
+
|
|
98
|
+
if (pinType === 'ButtonRequest_PinEntry' || pinType === 'ButtonRequest_AttachPin') {
|
|
99
|
+
// PIN is entered directly on device screen (Touch/Pro)
|
|
100
|
+
process.stderr.write('[onekey-hw] Please enter PIN on your device screen...\n');
|
|
101
|
+
// No uiResponse needed — device handles PIN input internally
|
|
102
|
+
} else {
|
|
103
|
+
// Classic devices: PIN entry via matrix
|
|
104
|
+
// In CLI mode, prompt user or let agent handle
|
|
105
|
+
process.stderr.write('[onekey-hw] PIN required. Please enter PIN on your device.\n');
|
|
106
|
+
promptUser('PIN (on-device numpad mapping): ', true).then(pin => {
|
|
107
|
+
sdk.uiResponse({
|
|
108
|
+
type: UI_RESPONSE.RECEIVE_PIN,
|
|
109
|
+
payload: pin,
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Passphrase Request
|
|
116
|
+
// User must provide passphrase for hidden wallet access.
|
|
117
|
+
// Passphrase can be entered on-device (Touch/Pro) or via host.
|
|
118
|
+
if (message.type === UI_REQUEST.REQUEST_PASSPHRASE) {
|
|
119
|
+
if (opts.useEmptyPassphrase) {
|
|
120
|
+
// Standard wallet (no passphrase)
|
|
121
|
+
sdk.uiResponse({
|
|
122
|
+
type: UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
123
|
+
payload: {
|
|
124
|
+
value: '',
|
|
125
|
+
passphraseOnDevice: false,
|
|
126
|
+
save: false,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
} else {
|
|
130
|
+
process.stderr.write('[onekey-hw] Passphrase required for hidden wallet.\n');
|
|
131
|
+
promptUser('Enter passphrase (or press Enter for on-device entry): ').then(passphrase => {
|
|
132
|
+
if (passphrase === '') {
|
|
133
|
+
// Enter on device
|
|
134
|
+
sdk.uiResponse({
|
|
135
|
+
type: UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
136
|
+
payload: {
|
|
137
|
+
value: '',
|
|
138
|
+
passphraseOnDevice: true,
|
|
139
|
+
save: false,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
sdk.uiResponse({
|
|
144
|
+
type: UI_RESPONSE.RECEIVE_PASSPHRASE,
|
|
145
|
+
payload: {
|
|
146
|
+
value: passphrase,
|
|
147
|
+
passphraseOnDevice: false,
|
|
148
|
+
save: false,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Passphrase On Device
|
|
157
|
+
if (message.type === UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE) {
|
|
158
|
+
process.stderr.write('[onekey-hw] Please enter passphrase on your device screen...\n');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Button Confirmation
|
|
162
|
+
// User must physically press confirm/reject on the device.
|
|
163
|
+
if (message.type === UI_REQUEST.REQUEST_BUTTON) {
|
|
164
|
+
process.stderr.write('[onekey-hw] Please confirm the action on your device...\n');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Device connection events
|
|
169
|
+
sdk.on(DEVICE.CONNECT, (device: any) => {
|
|
170
|
+
if (!opts.json) {
|
|
171
|
+
process.stderr.write(`[onekey-hw] Device connected: ${device?.name || 'Unknown'}\n`);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
sdk.on(DEVICE.DISCONNECT, (device: any) => {
|
|
176
|
+
if (!opts.json) {
|
|
177
|
+
process.stderr.write(`[onekey-hw] Device disconnected: ${device?.name || 'Unknown'}\n`);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export async function createSDK(opts: SDKOptions) {
|
|
183
|
+
const settings: Partial<ConnectSettings> = {
|
|
184
|
+
debug: false,
|
|
185
|
+
fetchConfig: true,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Direct USB via libusb — works on macOS, Linux, Windows
|
|
189
|
+
settings.env = 'lowlevel';
|
|
190
|
+
const plugin = UsbPlugin;
|
|
191
|
+
|
|
192
|
+
await HardwareSDK.init(settings, undefined, plugin);
|
|
193
|
+
|
|
194
|
+
// Register event handlers AFTER init
|
|
195
|
+
registerEventHandlers(HardwareSDK, opts);
|
|
196
|
+
|
|
197
|
+
return HardwareSDK;
|
|
198
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noImplicitAny": false,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"outDir": "./dist",
|
|
14
|
+
"rootDir": "./src"
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "dist", "evals"]
|
|
18
|
+
}
|