aihezu 2.1.0 → 2.3.1
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/README.md +9 -0
- package/bin/aihezu.js +49 -22
- package/commands/ccusage.js +278 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
- Claude:默认 `https://cc.aihezu.dev/api`,企业可用 `--api` / `--api-url` 指定独立域名
|
|
19
19
|
- Codex:默认 `https://cc.aihezu.dev/openai`,企业可用 `--api` / `--api-url` 指定独立域名;写入 `~/.codex/config.toml` 和 `auth.json`,使用 `AIHEZU_OAI_KEY`
|
|
20
20
|
- `ccclear`:清理 Claude Code 缓存和配置
|
|
21
|
+
- `ccusage`:查看 Claude Code API 用量统计(读取 `ANTHROPIC_BASE_URL` / `ANTHROPIC_AUTH_TOKEN`)
|
|
21
22
|
|
|
22
23
|
## 清理内容
|
|
23
24
|
|
|
@@ -84,6 +85,14 @@ npm install -g aihezu
|
|
|
84
85
|
sudo ccclear
|
|
85
86
|
```
|
|
86
87
|
|
|
88
|
+
### 查看 Claude Code 用量(ccusage)
|
|
89
|
+
|
|
90
|
+
无需管理员权限:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx aihezu ccusage
|
|
94
|
+
```
|
|
95
|
+
|
|
87
96
|
## 运行效果
|
|
88
97
|
|
|
89
98
|
```
|
package/bin/aihezu.js
CHANGED
|
@@ -6,7 +6,8 @@ const readline = require('readline');
|
|
|
6
6
|
|
|
7
7
|
const commands = {
|
|
8
8
|
install: require('../commands/install'),
|
|
9
|
-
clear: require('../commands/clear')
|
|
9
|
+
clear: require('../commands/clear'),
|
|
10
|
+
ccusage: require('../commands/ccusage')
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
const services = {
|
|
@@ -22,6 +23,7 @@ function showHelp() {
|
|
|
22
23
|
console.log('\n命令:');
|
|
23
24
|
console.log(' install <service> 配置服务 (API Key, URL, 网络设置)');
|
|
24
25
|
console.log(' clear <service> 清理缓存并刷新网络设置');
|
|
26
|
+
console.log(' ccusage 查看 Claude Code 用量统计');
|
|
25
27
|
console.log('\n服务:');
|
|
26
28
|
console.log(' claude Claude Code');
|
|
27
29
|
console.log(' codex Codex');
|
|
@@ -29,6 +31,7 @@ function showHelp() {
|
|
|
29
31
|
console.log('\n示例:');
|
|
30
32
|
console.log(' aihezu install claude');
|
|
31
33
|
console.log(' aihezu clear codex');
|
|
34
|
+
console.log(' aihezu ccusage');
|
|
32
35
|
console.log(' aihezu install (交互式安装)');
|
|
33
36
|
console.log(' aihezu help (显示帮助信息)');
|
|
34
37
|
}
|
|
@@ -38,73 +41,97 @@ async function askQuestion(rl, question) {
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
async function main() {
|
|
41
|
-
// Removed global rl creation
|
|
42
|
-
|
|
43
44
|
try {
|
|
44
|
-
if (args.length === 0 || args[0] === 'help') {
|
|
45
|
+
if (args.length === 0 || args[0] === 'help') {
|
|
45
46
|
showHelp();
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
const commandName = args[0];
|
|
50
|
-
let serviceName = args[1];
|
|
51
|
-
|
|
52
51
|
if (!commands[commandName]) {
|
|
53
|
-
console.error(`❌
|
|
52
|
+
console.error(`❌ 未知命令: ${commandName}`);
|
|
54
53
|
showHelp();
|
|
55
54
|
process.exit(1);
|
|
56
55
|
}
|
|
57
56
|
|
|
57
|
+
// Commands that don't require a service
|
|
58
|
+
if (commandName === 'ccusage') {
|
|
59
|
+
await commands[commandName](args.slice(1));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let serviceName = '';
|
|
64
|
+
let commandArgs = [];
|
|
65
|
+
|
|
66
|
+
// Check if the second argument is a service name or an option/flag
|
|
67
|
+
if (args.length > 1 && !args[1].startsWith('-')) {
|
|
68
|
+
serviceName = args[1].toLowerCase();
|
|
69
|
+
commandArgs = args.slice(2);
|
|
70
|
+
} else {
|
|
71
|
+
// No service name provided, or an option was provided instead.
|
|
72
|
+
// The command arguments will be all arguments after the command itself.
|
|
73
|
+
commandArgs = args.slice(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If service name is not provided, and command is 'install', enter interactive mode.
|
|
58
77
|
if (!serviceName) {
|
|
59
78
|
if (commandName === 'install') {
|
|
60
|
-
// Init rl ONLY here for selection
|
|
61
79
|
const rl = readline.createInterface({
|
|
62
80
|
input: process.stdin,
|
|
63
81
|
output: process.stdout
|
|
64
82
|
});
|
|
65
83
|
|
|
66
84
|
try {
|
|
67
|
-
// Interactive service selection for 'install' command
|
|
68
85
|
console.log('\n请选择要安装的服务:');
|
|
69
86
|
const serviceKeys = Object.keys(services);
|
|
87
|
+
const claudeIndex = serviceKeys.indexOf('claude');
|
|
88
|
+
|
|
70
89
|
serviceKeys.forEach((key, index) => {
|
|
71
|
-
|
|
90
|
+
const isDefault = (key === 'claude');
|
|
91
|
+
console.log(` [${index + 1}] ${services[key].displayName}${isDefault ? ' (默认)' : ''}`);
|
|
72
92
|
});
|
|
73
93
|
|
|
74
|
-
let chosenIndex;
|
|
94
|
+
let chosenIndex = claudeIndex !== -1 ? claudeIndex : 0; // Default to Claude
|
|
75
95
|
let validChoice = false;
|
|
96
|
+
|
|
76
97
|
while (!validChoice) {
|
|
77
|
-
const answer = await askQuestion(rl, '
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
serviceName = serviceKeys[chosenIndex];
|
|
81
|
-
validChoice = true;
|
|
98
|
+
const answer = await askQuestion(rl, '请输入选项编号 (直接回车使用默认值): ');
|
|
99
|
+
if (answer === '') {
|
|
100
|
+
validChoice = true; // Use the default index
|
|
82
101
|
} else {
|
|
83
|
-
|
|
102
|
+
const selectedIdx = parseInt(answer, 10) - 1;
|
|
103
|
+
if (selectedIdx >= 0 && selectedIdx < serviceKeys.length) {
|
|
104
|
+
chosenIndex = selectedIdx;
|
|
105
|
+
validChoice = true;
|
|
106
|
+
} else {
|
|
107
|
+
console.log('无效的选择,请输入列表中的数字。');
|
|
108
|
+
}
|
|
84
109
|
}
|
|
85
110
|
}
|
|
111
|
+
serviceName = serviceKeys[chosenIndex];
|
|
86
112
|
console.log(`✅ 已选择: ${services[serviceName].displayName}`);
|
|
113
|
+
|
|
87
114
|
} finally {
|
|
88
|
-
// Close rl immediately after selection and BEFORE calling the command
|
|
89
115
|
rl.close();
|
|
90
116
|
}
|
|
91
117
|
} else {
|
|
92
|
-
// For 'clear'
|
|
118
|
+
// For other commands like 'clear', service name is mandatory.
|
|
93
119
|
console.error(`❌ 请为 '${commandName}' 命令指定一个服务名称。`);
|
|
94
120
|
showHelp();
|
|
95
121
|
process.exit(1);
|
|
96
122
|
}
|
|
97
123
|
}
|
|
98
124
|
|
|
99
|
-
const service = services[serviceName
|
|
125
|
+
const service = services[serviceName];
|
|
100
126
|
if (!service) {
|
|
101
127
|
console.error(`❌ 未知服务: ${serviceName}`);
|
|
102
128
|
console.log('可用服务: ' + Object.keys(services).map(key => services[key].displayName).join(', '));
|
|
103
129
|
process.exit(1);
|
|
104
130
|
}
|
|
105
131
|
|
|
106
|
-
// Execute
|
|
107
|
-
await commands[commandName](service,
|
|
132
|
+
// Execute the command with the correct service and arguments
|
|
133
|
+
await commands[commandName](service, commandArgs);
|
|
134
|
+
|
|
108
135
|
} catch (error) {
|
|
109
136
|
console.error('❌ 发生错误:', error);
|
|
110
137
|
process.exit(1);
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const http = require('http');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
|
|
7
|
+
function normalizeHttpUrl(url) {
|
|
8
|
+
if (!url) return '';
|
|
9
|
+
const trimmed = String(url).trim();
|
|
10
|
+
if (!trimmed) return '';
|
|
11
|
+
if (/^https?:\/\//i.test(trimmed)) return trimmed;
|
|
12
|
+
return `https://${trimmed}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function readClaudeEnvFromSettingsFile() {
|
|
16
|
+
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
|
17
|
+
if (!fs.existsSync(settingsPath)) {
|
|
18
|
+
return { env: null, settingsPath, error: null };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const content = fs.readFileSync(settingsPath, 'utf8');
|
|
23
|
+
const settings = JSON.parse(content);
|
|
24
|
+
const env = settings && settings.env ? settings.env : null;
|
|
25
|
+
return { env, settingsPath, error: null };
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return { env: null, settingsPath, error };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function postJson(urlString, body, options = {}) {
|
|
32
|
+
const { timeoutMs = 15000 } = options;
|
|
33
|
+
const url = new URL(urlString);
|
|
34
|
+
const payload = JSON.stringify(body ?? {});
|
|
35
|
+
|
|
36
|
+
const transport = url.protocol === 'https:' ? https : http;
|
|
37
|
+
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const req = transport.request(
|
|
40
|
+
{
|
|
41
|
+
method: 'POST',
|
|
42
|
+
hostname: url.hostname,
|
|
43
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
44
|
+
path: `${url.pathname}${url.search || ''}`,
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
'Accept': 'application/json',
|
|
48
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
49
|
+
'User-Agent': 'aihezu-cli'
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
res => {
|
|
53
|
+
res.setEncoding('utf8');
|
|
54
|
+
let raw = '';
|
|
55
|
+
|
|
56
|
+
res.on('data', chunk => {
|
|
57
|
+
raw += chunk;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
res.on('end', () => {
|
|
61
|
+
let json = null;
|
|
62
|
+
try {
|
|
63
|
+
json = raw ? JSON.parse(raw) : null;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
// keep json as null, return raw for debugging
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
resolve({
|
|
69
|
+
statusCode: res.statusCode || 0,
|
|
70
|
+
raw,
|
|
71
|
+
json
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
req.on('error', reject);
|
|
78
|
+
req.setTimeout(timeoutMs, () => {
|
|
79
|
+
req.destroy(new Error(`Request timed out after ${timeoutMs}ms`));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
req.write(payload);
|
|
83
|
+
req.end();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function asNumber(value) {
|
|
88
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
89
|
+
if (typeof value === 'string' && value.trim() !== '' && Number.isFinite(Number(value))) {
|
|
90
|
+
return Number(value);
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function formatCost(value) {
|
|
96
|
+
const n = asNumber(value);
|
|
97
|
+
if (n === null) return '-';
|
|
98
|
+
return n.toFixed(2);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function formatPercent(current, limit) {
|
|
102
|
+
const c = asNumber(current);
|
|
103
|
+
const l = asNumber(limit);
|
|
104
|
+
if (c === null || l === null || l <= 0) return '-';
|
|
105
|
+
return `${((c / l) * 100).toFixed(1)}%`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function renderBar(current, limit, width = 18) {
|
|
109
|
+
const c = asNumber(current);
|
|
110
|
+
const l = asNumber(limit);
|
|
111
|
+
if (c === null || l === null || l <= 0) return '';
|
|
112
|
+
|
|
113
|
+
const ratio = Math.max(0, Math.min(1, c / l));
|
|
114
|
+
let filled = Math.round(ratio * width);
|
|
115
|
+
if (ratio > 0 && filled === 0) filled = 1;
|
|
116
|
+
const empty = Math.max(0, width - filled);
|
|
117
|
+
return `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function usageHint(current, limit) {
|
|
121
|
+
const c = asNumber(current);
|
|
122
|
+
const l = asNumber(limit);
|
|
123
|
+
if (c === null || l === null || l <= 0) return '';
|
|
124
|
+
const ratio = c / l;
|
|
125
|
+
if (ratio >= 1) return '⚠️ 已超出限制';
|
|
126
|
+
if (ratio >= 0.9) return '⚠️ 接近上限';
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function showUsageHelp() {
|
|
131
|
+
console.log('用法: aihezu ccusage [--json]');
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log('说明:');
|
|
134
|
+
console.log(' - 读取当前 ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN(优先环境变量,其次 ~/.claude/settings.json)');
|
|
135
|
+
console.log(' - 自动请求 /apiStats/api/get-key-id 和 /apiStats/api/user-stats 并美观输出用量');
|
|
136
|
+
console.log('');
|
|
137
|
+
console.log('示例:');
|
|
138
|
+
console.log(' npx aihezu ccusage');
|
|
139
|
+
console.log(' npx aihezu ccusage --json');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function ccusageCommand(args = []) {
|
|
143
|
+
const wantsHelp = args.includes('help') || args.includes('--help') || args.includes('-h');
|
|
144
|
+
if (wantsHelp) {
|
|
145
|
+
showUsageHelp();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const outputJson = args.includes('--json');
|
|
150
|
+
|
|
151
|
+
let baseUrl = process.env.ANTHROPIC_BASE_URL || '';
|
|
152
|
+
let authToken = process.env.ANTHROPIC_AUTH_TOKEN || '';
|
|
153
|
+
let sourceHint = '环境变量';
|
|
154
|
+
|
|
155
|
+
if (!baseUrl || !authToken) {
|
|
156
|
+
const { env, settingsPath, error } = readClaudeEnvFromSettingsFile();
|
|
157
|
+
if (error) {
|
|
158
|
+
console.log(`⚠️ 读取配置文件失败: ${settingsPath}`);
|
|
159
|
+
console.log(` 错误: ${error.message}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (env) {
|
|
163
|
+
baseUrl = baseUrl || env.ANTHROPIC_BASE_URL || '';
|
|
164
|
+
authToken = authToken || env.ANTHROPIC_AUTH_TOKEN || '';
|
|
165
|
+
sourceHint = `配置文件 (${settingsPath})`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
baseUrl = normalizeHttpUrl(baseUrl);
|
|
170
|
+
authToken = String(authToken || '').trim();
|
|
171
|
+
|
|
172
|
+
if (!baseUrl || !authToken) {
|
|
173
|
+
console.error('❌ 未找到 ANTHROPIC_BASE_URL 或 ANTHROPIC_AUTH_TOKEN。');
|
|
174
|
+
console.error(' 请先运行: npx aihezu install claude');
|
|
175
|
+
console.error(' 或设置环境变量: ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN');
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let origin = '';
|
|
180
|
+
try {
|
|
181
|
+
origin = new URL(baseUrl).origin;
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error('❌ ANTHROPIC_BASE_URL 不是合法的 URL: ' + baseUrl);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const keyIdUrl = `${origin}/apiStats/api/get-key-id`;
|
|
188
|
+
const statsUrl = `${origin}/apiStats/api/user-stats`;
|
|
189
|
+
|
|
190
|
+
console.log('');
|
|
191
|
+
console.log('📊 Claude Code 用量统计');
|
|
192
|
+
console.log(`🌐 域名: ${origin}`);
|
|
193
|
+
console.log(`🔎 配置来源: ${sourceHint}`);
|
|
194
|
+
console.log('');
|
|
195
|
+
|
|
196
|
+
const keyIdRes = await postJson(keyIdUrl, { apiKey: authToken });
|
|
197
|
+
if (keyIdRes.statusCode < 200 || keyIdRes.statusCode >= 300) {
|
|
198
|
+
console.error(`❌ 获取 API Key ID 失败 (HTTP ${keyIdRes.statusCode})`);
|
|
199
|
+
if (keyIdRes.raw) console.error(keyIdRes.raw);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const apiId =
|
|
204
|
+
keyIdRes?.json?.data?.id ||
|
|
205
|
+
keyIdRes?.json?.id ||
|
|
206
|
+
null;
|
|
207
|
+
|
|
208
|
+
if (!apiId) {
|
|
209
|
+
console.error('❌ 返回值中未找到 API Key ID。');
|
|
210
|
+
if (keyIdRes.raw) console.error(keyIdRes.raw);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const statsRes = await postJson(statsUrl, { apiId });
|
|
215
|
+
if (statsRes.statusCode < 200 || statsRes.statusCode >= 300) {
|
|
216
|
+
console.error(`❌ 获取用量失败 (HTTP ${statsRes.statusCode})`);
|
|
217
|
+
if (statsRes.raw) console.error(statsRes.raw);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!statsRes.json || typeof statsRes.json !== 'object') {
|
|
222
|
+
console.error('❌ 获取用量失败:返回内容不是合法的 JSON。');
|
|
223
|
+
if (statsRes.raw) console.error(statsRes.raw);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const stats = (statsRes && statsRes.json && statsRes.json.data) ? statsRes.json.data : (statsRes.json || {});
|
|
228
|
+
|
|
229
|
+
if (outputJson) {
|
|
230
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const limits = (stats && typeof stats === 'object' && stats.limits && typeof stats.limits === 'object')
|
|
235
|
+
? stats.limits
|
|
236
|
+
: {};
|
|
237
|
+
|
|
238
|
+
const currentDailyCost = (stats.currentDailyCost !== undefined) ? stats.currentDailyCost : limits.currentDailyCost;
|
|
239
|
+
const dailyCostLimit = (stats.dailyCostLimit !== undefined) ? stats.dailyCostLimit : limits.dailyCostLimit;
|
|
240
|
+
const currentWindowCost = (stats.currentWindowCost !== undefined) ? stats.currentWindowCost : limits.currentWindowCost;
|
|
241
|
+
const rateLimitCost = (stats.rateLimitCost !== undefined) ? stats.rateLimitCost : limits.rateLimitCost;
|
|
242
|
+
|
|
243
|
+
const dailyRemaining = (() => {
|
|
244
|
+
const c = asNumber(currentDailyCost);
|
|
245
|
+
const l = asNumber(dailyCostLimit);
|
|
246
|
+
if (c === null || l === null) return null;
|
|
247
|
+
return l - c;
|
|
248
|
+
})();
|
|
249
|
+
|
|
250
|
+
const windowRemaining = (() => {
|
|
251
|
+
const c = asNumber(currentWindowCost);
|
|
252
|
+
const l = asNumber(rateLimitCost);
|
|
253
|
+
if (c === null || l === null) return null;
|
|
254
|
+
return l - c;
|
|
255
|
+
})();
|
|
256
|
+
|
|
257
|
+
console.log('=== 关键指标 ===');
|
|
258
|
+
console.log(
|
|
259
|
+
`日额度: ${formatCost(currentDailyCost)} / ${formatCost(dailyCostLimit)} (${formatPercent(currentDailyCost, dailyCostLimit)}) ` +
|
|
260
|
+
`${renderBar(currentDailyCost, dailyCostLimit)} ${usageHint(currentDailyCost, dailyCostLimit)}`.trimEnd()
|
|
261
|
+
);
|
|
262
|
+
if (dailyRemaining !== null) {
|
|
263
|
+
console.log(`日剩余: ${formatCost(dailyRemaining)}`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
console.log('');
|
|
267
|
+
console.log(
|
|
268
|
+
`窗口: ${formatCost(currentWindowCost)} / ${formatCost(rateLimitCost)} (${formatPercent(currentWindowCost, rateLimitCost)}) ` +
|
|
269
|
+
`${renderBar(currentWindowCost, rateLimitCost)} ${usageHint(currentWindowCost, rateLimitCost)}`.trimEnd()
|
|
270
|
+
);
|
|
271
|
+
if (windowRemaining !== null) {
|
|
272
|
+
console.log(`窗口剩余: ${formatCost(windowRemaining)}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log('');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
module.exports = ccusageCommand;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aihezu",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "AI 开发环境配置工具 - 支持 Claude Code, Codex, Google Gemini 的本地化配置、代理设置与缓存清理",
|
|
5
5
|
"main": "bin/aihezu.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
"ccclear": "bin/ccclear.js",
|
|
9
9
|
"ccinstall": "bin/ccinstall.js"
|
|
10
10
|
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"commands/",
|
|
14
|
+
"services/",
|
|
15
|
+
"lib/",
|
|
16
|
+
"README.md",
|
|
17
|
+
"INSTALL_USAGE.md"
|
|
18
|
+
],
|
|
11
19
|
"scripts": {
|
|
12
20
|
"test": "node bin/aihezu.js help"
|
|
13
21
|
},
|