hop-claude 1.0.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/CHANGELOG.md +316 -0
- package/README.md +574 -0
- package/SECURITY.md +280 -0
- package/bin/cli.js +6 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +147 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/config-manager.d.ts +88 -0
- package/dist/config/config-manager.d.ts.map +1 -0
- package/dist/config/config-manager.js +334 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/encryption-v2.d.ts +51 -0
- package/dist/config/encryption-v2.d.ts.map +1 -0
- package/dist/config/encryption-v2.js +93 -0
- package/dist/config/encryption-v2.js.map +1 -0
- package/dist/config/encryption.d.ts +36 -0
- package/dist/config/encryption.d.ts.map +1 -0
- package/dist/config/encryption.js +72 -0
- package/dist/config/encryption.js.map +1 -0
- package/dist/config/keychain.d.ts +56 -0
- package/dist/config/keychain.d.ts.map +1 -0
- package/dist/config/keychain.js +112 -0
- package/dist/config/keychain.js.map +1 -0
- package/dist/config/storage.d.ts +32 -0
- package/dist/config/storage.d.ts.map +1 -0
- package/dist/config/storage.js +87 -0
- package/dist/config/storage.js.map +1 -0
- package/dist/config/validator.d.ts +9 -0
- package/dist/config/validator.d.ts.map +1 -0
- package/dist/config/validator.js +53 -0
- package/dist/config/validator.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +38 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/display.d.ts +25 -0
- package/dist/ui/display.d.ts.map +1 -0
- package/dist/ui/display.js +39 -0
- package/dist/ui/display.js.map +1 -0
- package/dist/ui/prompts.d.ts +52 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +339 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/utils/backup.d.ts +14 -0
- package/dist/utils/backup.d.ts.map +1 -0
- package/dist/utils/backup.js +27 -0
- package/dist/utils/backup.js.map +1 -0
- package/dist/utils/claude-launcher.d.ts +8 -0
- package/dist/utils/claude-launcher.d.ts.map +1 -0
- package/dist/utils/claude-launcher.js +68 -0
- package/dist/utils/claude-launcher.js.map +1 -0
- package/dist/utils/migration.d.ts +18 -0
- package/dist/utils/migration.d.ts.map +1 -0
- package/dist/utils/migration.js +176 -0
- package/dist/utils/migration.js.map +1 -0
- package/dist/utils/platform.d.ts +19 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +91 -0
- package/dist/utils/platform.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import { KeychainManager } from '../config/keychain.js';
|
|
3
|
+
import { displaySuccess, displayError, displayMessage, displayWarning } from '../ui/display.js';
|
|
4
|
+
/**
|
|
5
|
+
* 加密模式迁移工具
|
|
6
|
+
* 帮助用户从 legacy 模式迁移到 keychain 或 passphrase 模式
|
|
7
|
+
*/
|
|
8
|
+
export class EncryptionMigration {
|
|
9
|
+
configManager;
|
|
10
|
+
constructor(configManager) {
|
|
11
|
+
this.configManager = configManager;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 执行加密模式迁移
|
|
15
|
+
*/
|
|
16
|
+
async migrate() {
|
|
17
|
+
displayMessage('\n=== Encryption Mode Migration ===\n');
|
|
18
|
+
// 获取当前加密模式
|
|
19
|
+
const currentMode = await this.configManager.getEncryptionMode();
|
|
20
|
+
displayMessage(`Current encryption mode: ${currentMode}`);
|
|
21
|
+
if (currentMode === 'legacy') {
|
|
22
|
+
displayWarning('\nLegacy mode uses machine-bound encryption.');
|
|
23
|
+
displayWarning('Your exported config cannot be restored on different machines.');
|
|
24
|
+
displayMessage('\nRecommended: Migrate to a more secure and portable mode.\n');
|
|
25
|
+
}
|
|
26
|
+
// 检查 keychain 是否可用
|
|
27
|
+
const keychainAvailable = await KeychainManager.isAvailable();
|
|
28
|
+
// 选择新的加密模式
|
|
29
|
+
const choices = [];
|
|
30
|
+
if (keychainAvailable) {
|
|
31
|
+
choices.push({
|
|
32
|
+
title: 'Keychain (Recommended)',
|
|
33
|
+
value: 'keychain',
|
|
34
|
+
description: 'Store keys in OS keychain - most secure, no passwords needed',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
displayWarning('Note: OS Keychain not available on this system.');
|
|
39
|
+
}
|
|
40
|
+
choices.push({
|
|
41
|
+
title: 'Passphrase',
|
|
42
|
+
value: 'passphrase',
|
|
43
|
+
description: 'Encrypt with password - portable across machines, password required',
|
|
44
|
+
});
|
|
45
|
+
if (currentMode !== 'legacy') {
|
|
46
|
+
choices.push({
|
|
47
|
+
title: 'Legacy (Not recommended)',
|
|
48
|
+
value: 'legacy',
|
|
49
|
+
description: 'Machine-bound encryption - cannot be restored on different machines',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const { newMode } = await prompts({
|
|
53
|
+
type: 'select',
|
|
54
|
+
name: 'newMode',
|
|
55
|
+
message: 'Choose new encryption mode:',
|
|
56
|
+
choices,
|
|
57
|
+
});
|
|
58
|
+
if (!newMode) {
|
|
59
|
+
displayWarning('Migration cancelled');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (newMode === currentMode) {
|
|
63
|
+
displayMessage('Already using this mode. No migration needed.');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// 如果选择 passphrase 模式,需要输入密码
|
|
67
|
+
let passphrase;
|
|
68
|
+
if (newMode === 'passphrase') {
|
|
69
|
+
const { pwd1 } = await prompts({
|
|
70
|
+
type: 'password',
|
|
71
|
+
name: 'pwd1',
|
|
72
|
+
message: 'Enter new passphrase for encryption:',
|
|
73
|
+
validate: (value) => value.length >= 8 ? true : 'Passphrase must be at least 8 characters',
|
|
74
|
+
});
|
|
75
|
+
if (!pwd1) {
|
|
76
|
+
displayWarning('Migration cancelled');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const { pwd2 } = await prompts({
|
|
80
|
+
type: 'password',
|
|
81
|
+
name: 'pwd2',
|
|
82
|
+
message: 'Confirm passphrase:',
|
|
83
|
+
});
|
|
84
|
+
if (pwd1 !== pwd2) {
|
|
85
|
+
displayError('Passphrases do not match');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
passphrase = pwd1;
|
|
89
|
+
}
|
|
90
|
+
// 确认迁移
|
|
91
|
+
const { confirm } = await prompts({
|
|
92
|
+
type: 'confirm',
|
|
93
|
+
name: 'confirm',
|
|
94
|
+
message: `Migrate from ${currentMode} to ${newMode} mode?`,
|
|
95
|
+
initial: false,
|
|
96
|
+
});
|
|
97
|
+
if (!confirm) {
|
|
98
|
+
displayWarning('Migration cancelled');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
displayMessage('\nBacking up current configuration...');
|
|
103
|
+
// 创建备份
|
|
104
|
+
const backupPath = `${this.configManager.getConfigPath()}.backup-${Date.now()}`;
|
|
105
|
+
const configData = await this.configManager.exportConfig();
|
|
106
|
+
const fs = await import('fs/promises');
|
|
107
|
+
await fs.writeFile(backupPath, configData, 'utf8');
|
|
108
|
+
displaySuccess(`Backup created: ${backupPath}`);
|
|
109
|
+
displayMessage('\nMigrating encryption mode...');
|
|
110
|
+
// 执行迁移
|
|
111
|
+
await this.configManager.switchEncryptionMode(newMode, passphrase);
|
|
112
|
+
displaySuccess(`\nMigration completed!`);
|
|
113
|
+
displayMessage(`\nEncryption mode changed: ${currentMode} → ${newMode}`);
|
|
114
|
+
if (newMode === 'keychain') {
|
|
115
|
+
displayMessage('\nYour API keys are now stored in the system keychain.');
|
|
116
|
+
displayMessage('The config file only contains non-sensitive data.');
|
|
117
|
+
}
|
|
118
|
+
else if (newMode === 'passphrase') {
|
|
119
|
+
displayWarning('\nImportant: Remember your passphrase!');
|
|
120
|
+
displayWarning('You will need it every time you use hop-claude.');
|
|
121
|
+
if (passphrase) {
|
|
122
|
+
this.configManager.setSessionPassphrase(passphrase);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
displayMessage(`\nBackup file: ${backupPath}`);
|
|
126
|
+
displayMessage('You can delete it after verifying the migration works correctly.\n');
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
130
|
+
displayError(`Migration failed: ${err.message}`);
|
|
131
|
+
if (process.env.DEBUG) {
|
|
132
|
+
console.error('\nStack trace:');
|
|
133
|
+
console.error(err.stack);
|
|
134
|
+
}
|
|
135
|
+
displayWarning('\nYour configuration has been backed up.');
|
|
136
|
+
displayWarning('Please restore from backup if needed.');
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 显示当前加密模式信息
|
|
142
|
+
*/
|
|
143
|
+
async showEncryptionInfo() {
|
|
144
|
+
const mode = await this.configManager.getEncryptionMode();
|
|
145
|
+
const keychainAvailable = await KeychainManager.isAvailable();
|
|
146
|
+
displayMessage('\n=== Encryption Mode Information ===\n');
|
|
147
|
+
displayMessage(`Current mode: ${mode}`);
|
|
148
|
+
switch (mode) {
|
|
149
|
+
case 'legacy':
|
|
150
|
+
displayMessage('\nLegacy mode:');
|
|
151
|
+
displayMessage(' - Uses machine-bound encryption (hostname + username)');
|
|
152
|
+
displayMessage(' - Keys are encrypted but tied to this machine');
|
|
153
|
+
displayMessage(' - Cannot restore exported configs on different machines');
|
|
154
|
+
displayWarning('\n Warning: This mode is deprecated and less secure.');
|
|
155
|
+
displayMessage(' Consider migrating to keychain or passphrase mode.');
|
|
156
|
+
break;
|
|
157
|
+
case 'keychain':
|
|
158
|
+
displayMessage('\nKeychain mode:');
|
|
159
|
+
displayMessage(' - Keys stored in OS keychain (most secure)');
|
|
160
|
+
displayMessage(' - No passwords needed for daily use');
|
|
161
|
+
displayMessage(' - Keys never written to disk');
|
|
162
|
+
displayWarning('\n Note: Cannot export/import keys across machines.');
|
|
163
|
+
break;
|
|
164
|
+
case 'passphrase':
|
|
165
|
+
displayMessage('\nPassphrase mode:');
|
|
166
|
+
displayMessage(' - Keys encrypted with your password');
|
|
167
|
+
displayMessage(' - Fully portable across machines');
|
|
168
|
+
displayMessage(' - Password required for each operation');
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
displayMessage(`\nOS Keychain available: ${keychainAvailable ? 'Yes' : 'No'}`);
|
|
172
|
+
displayMessage('\nTo migrate to a different mode, run:');
|
|
173
|
+
displayMessage(' hop-claude --migrate-encryption\n');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.js","sourceRoot":"","sources":["../../src/utils/migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取配置目录路径(跨平台)
|
|
3
|
+
* - macOS/Linux: ~/.hop-claude-config/
|
|
4
|
+
* - Windows: %APPDATA%/hop-claude-config/
|
|
5
|
+
*/
|
|
6
|
+
export declare function getConfigDir(): string;
|
|
7
|
+
/**
|
|
8
|
+
* 确保目录存在并设置正确的权限
|
|
9
|
+
* - Unix: 0o700 (仅所有者可读写执行)
|
|
10
|
+
* - Windows: 尽力而为(使用 icacls 限制权限)
|
|
11
|
+
*/
|
|
12
|
+
export declare function ensureSecureDirectory(dirPath: string): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* 设置文件权限(仅所有者可读写)
|
|
15
|
+
* - Unix: 0o600
|
|
16
|
+
* - Windows: 尽力而为
|
|
17
|
+
*/
|
|
18
|
+
export declare function setSecureFilePermissions(filePath: string): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/utils/platform.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAerC;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmD1E;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9E"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
/**
|
|
6
|
+
* 获取配置目录路径(跨平台)
|
|
7
|
+
* - macOS/Linux: ~/.hop-claude-config/
|
|
8
|
+
* - Windows: %APPDATA%/hop-claude-config/
|
|
9
|
+
*/
|
|
10
|
+
export function getConfigDir() {
|
|
11
|
+
const platform = os.platform();
|
|
12
|
+
if (platform === 'win32') {
|
|
13
|
+
// Windows: 使用 APPDATA
|
|
14
|
+
const appData = process.env.APPDATA;
|
|
15
|
+
if (!appData) {
|
|
16
|
+
throw new Error('APPDATA environment variable not found');
|
|
17
|
+
}
|
|
18
|
+
return path.join(appData, 'hop-claude-config');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
// macOS/Linux: 使用 home 目录
|
|
22
|
+
const home = os.homedir();
|
|
23
|
+
return path.join(home, '.hop-claude-config');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 确保目录存在并设置正确的权限
|
|
28
|
+
* - Unix: 0o700 (仅所有者可读写执行)
|
|
29
|
+
* - Windows: 尽力而为(使用 icacls 限制权限)
|
|
30
|
+
*/
|
|
31
|
+
export async function ensureSecureDirectory(dirPath) {
|
|
32
|
+
try {
|
|
33
|
+
await fs.access(dirPath);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// 目录不存在,创建它
|
|
37
|
+
await fs.mkdir(dirPath, { recursive: true, mode: 0o700 });
|
|
38
|
+
}
|
|
39
|
+
// 在 Unix 系统上设置权限
|
|
40
|
+
if (os.platform() !== 'win32') {
|
|
41
|
+
await fs.chmod(dirPath, 0o700);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Windows: 使用 icacls 限制权限到当前用户
|
|
45
|
+
// 这是可选的,如果失败会警告用户但不会阻止操作
|
|
46
|
+
try {
|
|
47
|
+
const username = os.userInfo().username;
|
|
48
|
+
const success = await new Promise((resolve) => {
|
|
49
|
+
const icacls = spawn('icacls', [
|
|
50
|
+
dirPath,
|
|
51
|
+
'/inheritance:r',
|
|
52
|
+
`/grant:r`,
|
|
53
|
+
`${username}:(OI)(CI)F`
|
|
54
|
+
], { shell: true });
|
|
55
|
+
let stderr = '';
|
|
56
|
+
icacls.stderr?.on('data', (data) => {
|
|
57
|
+
stderr += data.toString();
|
|
58
|
+
});
|
|
59
|
+
icacls.on('exit', (code) => {
|
|
60
|
+
resolve(code === 0);
|
|
61
|
+
});
|
|
62
|
+
icacls.on('error', () => {
|
|
63
|
+
resolve(false);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
if (!success) {
|
|
67
|
+
console.warn(`\nWarning: Failed to set secure permissions for ${dirPath}`);
|
|
68
|
+
console.warn('The directory may be accessible to other users on this system.');
|
|
69
|
+
console.warn('To manually secure it, run:');
|
|
70
|
+
console.warn(` icacls "${dirPath}" /inheritance:r /grant:r "${username}:(OI)(CI)F"\n`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
75
|
+
console.warn(`\nWarning: Could not set Windows ACL for ${dirPath}: ${err.message}`);
|
|
76
|
+
console.warn('Your configuration files may not be properly secured.\n');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 设置文件权限(仅所有者可读写)
|
|
82
|
+
* - Unix: 0o600
|
|
83
|
+
* - Windows: 尽力而为
|
|
84
|
+
*/
|
|
85
|
+
export async function setSecureFilePermissions(filePath) {
|
|
86
|
+
if (os.platform() !== 'win32') {
|
|
87
|
+
await fs.chmod(filePath, 0o600);
|
|
88
|
+
}
|
|
89
|
+
// Windows 文件权限继承自目录,已在 ensureSecureDirectory 中设置
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/utils/platform.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hop-claude",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code configuration manager and launcher",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude",
|
|
7
|
+
"anthropic",
|
|
8
|
+
"cli",
|
|
9
|
+
"proxy",
|
|
10
|
+
"config"
|
|
11
|
+
],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"bin": {
|
|
17
|
+
"hop-claude": "bin/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"bin",
|
|
22
|
+
"README.md",
|
|
23
|
+
"SECURITY.md",
|
|
24
|
+
"CHANGELOG.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"dev": "bun run src/index.ts",
|
|
30
|
+
"prepublishOnly": "npm run build",
|
|
31
|
+
"release": "changeset publish"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"chalk": "^5.6.2",
|
|
38
|
+
"commander": "^14.0.2",
|
|
39
|
+
"keytar": "^7.9.0",
|
|
40
|
+
"prompts": "^2.4.2",
|
|
41
|
+
"proper-lockfile": "^4.1.2",
|
|
42
|
+
"which": "^6.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@changesets/cli": "^2.29.8",
|
|
46
|
+
"@types/bun": "latest",
|
|
47
|
+
"@types/node": "^25.0.3",
|
|
48
|
+
"@types/prompts": "^2.4.9",
|
|
49
|
+
"@types/proper-lockfile": "^4.1.4",
|
|
50
|
+
"@types/which": "^3.0.4",
|
|
51
|
+
"bun-types": "^1.3.5",
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
|
+
}
|
|
54
|
+
}
|