@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/session.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Passphrase session management for hd-cli.
|
|
4
|
+
*
|
|
5
|
+
* Aligns with app-monorepo's CLI pattern:
|
|
6
|
+
* Login: getPassphraseState → passphraseState + sessionId → keychain
|
|
7
|
+
* Command: keychain → preloadSessionCache → SDK call (no passphrase prompt)
|
|
8
|
+
* Stale: error 112 → clear keychain → re-prompt → retry
|
|
9
|
+
* Logout: keychain delete
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.clearSessionFromKeychain = exports.saveSessionToKeychain = exports.preloadSessionFromKeychain = void 0;
|
|
13
|
+
const hd_core_1 = require("@onekeyfe/hd-core");
|
|
14
|
+
const storage_1 = require("./storage");
|
|
15
|
+
// Keychain key format: scoped by deviceId for multi-device support
|
|
16
|
+
function psKey(deviceId) {
|
|
17
|
+
return `onekey-hw:${deviceId}/passphrase-state`;
|
|
18
|
+
}
|
|
19
|
+
function sidKey(deviceId) {
|
|
20
|
+
return `onekey-hw:${deviceId}/session-id`;
|
|
21
|
+
}
|
|
22
|
+
let storageInstance = null;
|
|
23
|
+
function getStorage() {
|
|
24
|
+
if (!storageInstance) {
|
|
25
|
+
storageInstance = (0, storage_1.createSecureStorage)();
|
|
26
|
+
}
|
|
27
|
+
return storageInstance;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load passphraseState + sessionId from keychain and call preloadSessionCache.
|
|
31
|
+
* Non-fatal: returns the loaded passphraseState or undefined.
|
|
32
|
+
*/
|
|
33
|
+
async function preloadSessionFromKeychain(deviceId) {
|
|
34
|
+
try {
|
|
35
|
+
const storage = getStorage();
|
|
36
|
+
const [psBuf, sidBuf] = await Promise.all([
|
|
37
|
+
storage.get(psKey(deviceId)),
|
|
38
|
+
storage.get(sidKey(deviceId)),
|
|
39
|
+
]);
|
|
40
|
+
if (psBuf && sidBuf) {
|
|
41
|
+
const passphraseState = psBuf.toString('utf-8');
|
|
42
|
+
const sessionId = sidBuf.toString('utf-8');
|
|
43
|
+
(0, hd_core_1.preloadSessionCache)(deviceId, passphraseState, sessionId);
|
|
44
|
+
return passphraseState;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Non-fatal — fall through to passphrase prompt
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
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
|
+
/**
|
|
70
|
+
* Clear cached session from keychain.
|
|
71
|
+
*/
|
|
72
|
+
async function clearSessionFromKeychain(deviceId) {
|
|
73
|
+
try {
|
|
74
|
+
const storage = getStorage();
|
|
75
|
+
if (deviceId) {
|
|
76
|
+
await Promise.allSettled([storage.delete(psKey(deviceId)), storage.delete(sidKey(deviceId))]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Non-fatal
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.clearSessionFromKeychain = clearSessionFromKeychain;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSecureStorage = void 0;
|
|
4
|
+
var storage_factory_1 = require("./storage-factory");
|
|
5
|
+
Object.defineProperty(exports, "createSecureStorage", { enumerable: true, get: function () { return storage_factory_1.createSecureStorage; } });
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultProcessRunner = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
exports.defaultProcessRunner = {
|
|
6
|
+
execFileAsync(cmd, args) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
(0, node_child_process_1.execFile)(cmd, args, (error, stdout, stderr) => {
|
|
9
|
+
if (error) {
|
|
10
|
+
error.stderr = stderr;
|
|
11
|
+
reject(error);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
resolve({ stdout, stderr });
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
spawnWithStdin(cmd, args, input) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const child = (0, node_child_process_1.spawn)(cmd, args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
21
|
+
let stdout = '';
|
|
22
|
+
let stderr = '';
|
|
23
|
+
child.stdout.on('data', (data) => {
|
|
24
|
+
stdout += data.toString();
|
|
25
|
+
});
|
|
26
|
+
child.stderr.on('data', (data) => {
|
|
27
|
+
stderr += data.toString();
|
|
28
|
+
});
|
|
29
|
+
child.on('error', reject);
|
|
30
|
+
child.on('close', code => {
|
|
31
|
+
if (code !== 0) {
|
|
32
|
+
const error = new Error(`Process exited with code ${code}`);
|
|
33
|
+
error.code = code ?? 1;
|
|
34
|
+
error.stderr = stderr;
|
|
35
|
+
reject(error);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
resolve({ stdout, stderr });
|
|
39
|
+
});
|
|
40
|
+
child.stdin.write(`${input}\n`);
|
|
41
|
+
child.stdin.end();
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { IProcessRunner, ISecureStorage, SecureStorageBackend } from './types';
|
|
3
|
+
export declare class LinuxSecureStorage implements ISecureStorage {
|
|
4
|
+
private readonly runner;
|
|
5
|
+
constructor(runner?: IProcessRunner);
|
|
6
|
+
getBackendType(): SecureStorageBackend;
|
|
7
|
+
set(key: string, value: Buffer): Promise<void>;
|
|
8
|
+
get(key: string): Promise<Buffer | null>;
|
|
9
|
+
delete(key: string): Promise<void>;
|
|
10
|
+
private isItemNotFound;
|
|
11
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LinuxSecureStorage = void 0;
|
|
4
|
+
const process_utils_1 = require("./process-utils");
|
|
5
|
+
const SERVICE_NAME = 'onekey-hw-cli';
|
|
6
|
+
const SECRET_LABEL = 'OneKey HW CLI Secret';
|
|
7
|
+
class LinuxSecureStorage {
|
|
8
|
+
constructor(runner = process_utils_1.defaultProcessRunner) {
|
|
9
|
+
this.runner = runner;
|
|
10
|
+
}
|
|
11
|
+
getBackendType() {
|
|
12
|
+
return 'linux-secret-service';
|
|
13
|
+
}
|
|
14
|
+
async set(key, value) {
|
|
15
|
+
await this.runner.spawnWithStdin('secret-tool', ['store', '--label', SECRET_LABEL, 'service', SERVICE_NAME, 'account', key], value.toString('hex'));
|
|
16
|
+
}
|
|
17
|
+
async get(key) {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await this.runner.execFileAsync('secret-tool', [
|
|
20
|
+
'lookup',
|
|
21
|
+
'service',
|
|
22
|
+
SERVICE_NAME,
|
|
23
|
+
'account',
|
|
24
|
+
key,
|
|
25
|
+
]);
|
|
26
|
+
const hex = stdout.trim();
|
|
27
|
+
return hex ? Buffer.from(hex, 'hex') : null;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (this.isItemNotFound(error))
|
|
31
|
+
return null;
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async delete(key) {
|
|
36
|
+
try {
|
|
37
|
+
await this.runner.execFileAsync('secret-tool', [
|
|
38
|
+
'clear',
|
|
39
|
+
'service',
|
|
40
|
+
SERVICE_NAME,
|
|
41
|
+
'account',
|
|
42
|
+
key,
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (this.isItemNotFound(error))
|
|
47
|
+
return;
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
isItemNotFound(error) {
|
|
52
|
+
const err = error;
|
|
53
|
+
const stderr = err.stderr ?? err.message ?? '';
|
|
54
|
+
return (stderr.includes('No such secret item') ||
|
|
55
|
+
stderr.includes('Object does not exist') ||
|
|
56
|
+
stderr.includes('could not find'));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.LinuxSecureStorage = LinuxSecureStorage;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { IProcessRunner, ISecureStorage, SecureStorageBackend } from './types';
|
|
3
|
+
export declare class MacOSSecureStorage implements ISecureStorage {
|
|
4
|
+
private readonly runner;
|
|
5
|
+
constructor(runner?: IProcessRunner);
|
|
6
|
+
getBackendType(): SecureStorageBackend;
|
|
7
|
+
set(key: string, value: Buffer): Promise<void>;
|
|
8
|
+
get(key: string): Promise<Buffer | null>;
|
|
9
|
+
delete(key: string): Promise<void>;
|
|
10
|
+
private isItemNotFound;
|
|
11
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MacOSSecureStorage = void 0;
|
|
4
|
+
const process_utils_1 = require("./process-utils");
|
|
5
|
+
const SERVICE_NAME = 'onekey-hw-cli';
|
|
6
|
+
class MacOSSecureStorage {
|
|
7
|
+
constructor(runner = process_utils_1.defaultProcessRunner) {
|
|
8
|
+
this.runner = runner;
|
|
9
|
+
}
|
|
10
|
+
getBackendType() {
|
|
11
|
+
return 'macos-keychain';
|
|
12
|
+
}
|
|
13
|
+
async set(key, value) {
|
|
14
|
+
const hex = value.toString('hex');
|
|
15
|
+
// Use `security -i` (interactive/batch mode): the tool parses commands
|
|
16
|
+
// from stdin internally instead of re-spawning a sub-process per command,
|
|
17
|
+
// so the password argument never appears in /proc or `ps aux` output.
|
|
18
|
+
//
|
|
19
|
+
// Keys are of the form `onekey-hw:<deviceId>/<slot>` and hex values only
|
|
20
|
+
// contain 0-9a-f, so neither contains shell metacharacters that would
|
|
21
|
+
// break the simple quoting security's parser expects.
|
|
22
|
+
const cmd = `add-generic-password -s "${SERVICE_NAME}" -a "${key}" -w "${hex}" -U`;
|
|
23
|
+
await this.runner.spawnWithStdin('security', ['-i'], cmd);
|
|
24
|
+
}
|
|
25
|
+
async get(key) {
|
|
26
|
+
try {
|
|
27
|
+
const { stdout } = await this.runner.execFileAsync('security', [
|
|
28
|
+
'find-generic-password',
|
|
29
|
+
'-s',
|
|
30
|
+
SERVICE_NAME,
|
|
31
|
+
'-a',
|
|
32
|
+
key,
|
|
33
|
+
'-w',
|
|
34
|
+
]);
|
|
35
|
+
const hex = stdout.trim();
|
|
36
|
+
return hex ? Buffer.from(hex, 'hex') : null;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (this.isItemNotFound(error))
|
|
40
|
+
return null;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async delete(key) {
|
|
45
|
+
try {
|
|
46
|
+
await this.runner.execFileAsync('security', [
|
|
47
|
+
'delete-generic-password',
|
|
48
|
+
'-s',
|
|
49
|
+
SERVICE_NAME,
|
|
50
|
+
'-a',
|
|
51
|
+
key,
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (this.isItemNotFound(error))
|
|
56
|
+
return;
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
isItemNotFound(error) {
|
|
61
|
+
const err = error;
|
|
62
|
+
return err.code === 44 || err.stderr?.includes('could not be found') === true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.MacOSSecureStorage = MacOSSecureStorage;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSecureStorage = void 0;
|
|
4
|
+
const secure_storage_linux_1 = require("./secure-storage.linux");
|
|
5
|
+
const secure_storage_macos_1 = require("./secure-storage.macos");
|
|
6
|
+
function createSecureStorage(platform = process.platform) {
|
|
7
|
+
if (platform === 'darwin')
|
|
8
|
+
return new secure_storage_macos_1.MacOSSecureStorage();
|
|
9
|
+
if (platform === 'linux')
|
|
10
|
+
return new secure_storage_linux_1.LinuxSecureStorage();
|
|
11
|
+
throw new Error(`Secure storage is not supported on platform "${platform}". ` +
|
|
12
|
+
'Use macOS Keychain or Linux Secret Service.');
|
|
13
|
+
}
|
|
14
|
+
exports.createSecureStorage = createSecureStorage;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export type SecureStorageBackend = 'macos-keychain' | 'linux-secret-service';
|
|
3
|
+
export interface ISecureStorage {
|
|
4
|
+
getBackendType(): SecureStorageBackend;
|
|
5
|
+
get(key: string): Promise<Buffer | null>;
|
|
6
|
+
set(key: string, value: Buffer): Promise<void>;
|
|
7
|
+
delete(key: string): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export interface IProcessRunner {
|
|
10
|
+
execFileAsync(cmd: string, args: string[]): Promise<{
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
}>;
|
|
14
|
+
spawnWithStdin(cmd: string, args: string[], input: string): Promise<{
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hardware-cli",
|
|
3
|
-
"version": "1.1.26-alpha.
|
|
4
|
-
"description": "OneKey hardware wallet CLI for
|
|
3
|
+
"version": "1.1.26-alpha.4",
|
|
4
|
+
"description": "OneKey hardware wallet CLI for testing device communication",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -18,20 +18,22 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
21
|
+
"build": "tsc && node -e \"const f='dist/cli.js';const c=require('fs').readFileSync(f,'utf8');if(!c.startsWith('#!'))require('fs').writeFileSync(f,'#!/usr/bin/env node\\n'+c)\"",
|
|
22
|
+
"prestart": "tsc",
|
|
23
|
+
"start": "node dist/cli.js",
|
|
24
|
+
"search": "node dist/cli.js search",
|
|
25
|
+
"get-features": "node dist/cli.js get-features",
|
|
26
|
+
"get-address": "node dist/cli.js get-address",
|
|
27
|
+
"ping": "node dist/cli.js ping",
|
|
23
28
|
"lint": "eslint .",
|
|
24
29
|
"lint:fix": "eslint . --fix"
|
|
25
30
|
},
|
|
26
31
|
"dependencies": {
|
|
27
|
-
"@onekeyfe/hd-common-connect-sdk": "1.1.26-alpha.
|
|
28
|
-
"@onekeyfe/hd-core": "1.1.26-alpha.
|
|
29
|
-
"@onekeyfe/hd-shared": "1.1.26-alpha.
|
|
30
|
-
"@onekeyfe/hd-transport-usb": "1.1.26-alpha.
|
|
31
|
-
"commander": "^12.0.0"
|
|
32
|
-
"eventemitter2": "^6.4.9",
|
|
33
|
-
"lodash": "^4.17.21",
|
|
34
|
-
"tslib": "^2.6.0"
|
|
32
|
+
"@onekeyfe/hd-common-connect-sdk": "1.1.26-alpha.4",
|
|
33
|
+
"@onekeyfe/hd-core": "1.1.26-alpha.4",
|
|
34
|
+
"@onekeyfe/hd-shared": "1.1.26-alpha.4",
|
|
35
|
+
"@onekeyfe/hd-transport-usb": "1.1.26-alpha.4",
|
|
36
|
+
"commander": "^12.0.0"
|
|
35
37
|
},
|
|
36
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "c03e3a11a9871dc22b711589ad4962399381c92e"
|
|
37
39
|
}
|