@88code/byebyecode 1.1.12 → 1.1.14

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.
Files changed (2) hide show
  1. package/bin/byebyecode.js +330 -35
  2. package/package.json +6 -6
package/bin/byebyecode.js CHANGED
@@ -1,57 +1,275 @@
1
1
  #!/usr/bin/env node
2
- const { spawnSync } = require('child_process');
2
+ const { spawnSync, execSync } = require('child_process');
3
3
  const path = require('path');
4
4
  const fs = require('fs');
5
5
  const os = require('os');
6
6
 
7
- // 1. Priority: Use ~/.claude/ccline/byebyecode if exists
8
- const claudePath = path.join(
9
- os.homedir(),
10
- '.claude',
11
- 'ccline',
12
- process.platform === 'win32' ? 'byebyecode.exe' : 'byebyecode'
13
- );
14
-
15
- if (fs.existsSync(claudePath)) {
16
- const result = spawnSync(claudePath, process.argv.slice(2), {
17
- stdio: 'inherit',
18
- shell: false
19
- });
20
- process.exit(result.status || 0);
7
+ // ==================== 辅助函数 ====================
8
+
9
+ /**
10
+ * 检查指定 PID 的进程是否仍在运行
11
+ */
12
+ function isProcessRunning(pid) {
13
+ try {
14
+ const platform = process.platform;
15
+
16
+ if (platform === 'win32') {
17
+ const output = execSync(`tasklist /FI "PID eq ${pid}"`, {
18
+ encoding: 'utf8',
19
+ stdio: ['ignore', 'pipe', 'ignore']
20
+ });
21
+ return output.includes(pid.toString());
22
+ } else {
23
+ // Unix-like systems
24
+ const output = execSync(`ps -p ${pid}`, {
25
+ encoding: 'utf8',
26
+ stdio: ['ignore', 'pipe', 'ignore']
27
+ });
28
+ return output.includes(pid.toString());
29
+ }
30
+ } catch (e) {
31
+ return false; // 进程不存在
32
+ }
21
33
  }
22
34
 
23
- // 2. Fallback: Use npm package binary
35
+ /**
36
+ * 清理过期或无效的锁文件
37
+ */
38
+ function cleanupStaleLock(lockFile) {
39
+ if (!fs.existsSync(lockFile)) return;
40
+
41
+ try {
42
+ const lockData = JSON.parse(fs.readFileSync(lockFile, 'utf8'));
43
+ const age = Date.now() - lockData.timestamp;
44
+ const MAX_LOCK_AGE = 5 * 60 * 1000; // 5 分钟
45
+
46
+ // 如果锁文件超过 5 分钟,或 PID 已不存在,清理锁
47
+ if (age > MAX_LOCK_AGE || !isProcessRunning(lockData.pid)) {
48
+ fs.unlinkSync(lockFile);
49
+ }
50
+ } catch (e) {
51
+ // 锁文件损坏,直接删除
52
+ try {
53
+ fs.unlinkSync(lockFile);
54
+ } catch (err) {
55
+ // Ignore
56
+ }
57
+ }
58
+ }
59
+
60
+ // ==================== 启动时检查待更新 ====================
61
+
62
+ /**
63
+ * 在执行二进制前,检查是否有待安装的更新
64
+ * 如果有,执行更新并清理标记文件
65
+ */
66
+ function checkAndInstallPendingUpdate() {
67
+ const configDir = path.join(os.homedir(), '.claude', 'byebyecode');
68
+ const pendingFile = path.join(configDir, '.update_pending');
69
+ const lockFile = path.join(configDir, '.update_lock');
70
+
71
+ // 如果没有待更新文件,直接返回
72
+ if (!fs.existsSync(pendingFile)) {
73
+ return;
74
+ }
75
+
76
+ // 清理过期锁文件
77
+ cleanupStaleLock(lockFile);
78
+
79
+ // 检查是否有其他进程正在更新
80
+ if (fs.existsSync(lockFile)) {
81
+ try {
82
+ const lockData = JSON.parse(fs.readFileSync(lockFile, 'utf8'));
83
+
84
+ // 如果锁文件对应的进程仍在运行,跳过更新
85
+ if (isProcessRunning(lockData.pid)) {
86
+ return;
87
+ }
88
+ } catch (e) {
89
+ // 锁文件损坏,继续尝试更新
90
+ }
91
+ }
92
+
93
+ // 创建锁文件
94
+ try {
95
+ fs.mkdirSync(configDir, { recursive: true });
96
+ fs.writeFileSync(lockFile, JSON.stringify({
97
+ pid: process.pid,
98
+ timestamp: Date.now()
99
+ }));
100
+ } catch (e) {
101
+ // 无法创建锁文件,跳过更新
102
+ return;
103
+ }
104
+
105
+ // 读取待更新信息
106
+ let pendingUpdate;
107
+ try {
108
+ pendingUpdate = JSON.parse(fs.readFileSync(pendingFile, 'utf8'));
109
+ } catch (e) {
110
+ // 文件损坏,清理后退出
111
+ try {
112
+ fs.unlinkSync(pendingFile);
113
+ fs.unlinkSync(lockFile);
114
+ } catch (err) {
115
+ // Ignore
116
+ }
117
+ return;
118
+ }
119
+
120
+ // 执行更新
121
+ console.error('');
122
+ console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
123
+ console.error(`🔄 正在更新 byebyecode 到 v${pendingUpdate.latestVersion}...`);
124
+ console.error(' (这可能需要几秒钟)');
125
+ console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
126
+ console.error('');
127
+
128
+ try {
129
+ execSync('npm install -g @88code/byebyecode@latest', {
130
+ stdio: 'inherit',
131
+ timeout: 120000
132
+ });
133
+
134
+ console.error('');
135
+ console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
136
+ console.error(`✓ 更新成功!已安装 v${pendingUpdate.latestVersion}`);
137
+ console.error(' 正在继续执行...');
138
+ console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
139
+ console.error('');
140
+
141
+ // 清理文件
142
+ try {
143
+ fs.unlinkSync(pendingFile);
144
+
145
+ const noticeFile = path.join(configDir, '.update_notice');
146
+ if (fs.existsSync(noticeFile)) {
147
+ fs.unlinkSync(noticeFile);
148
+ }
149
+ } catch (e) {
150
+ // Ignore cleanup errors
151
+ }
152
+ } catch (error) {
153
+ console.error('');
154
+ console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
155
+ console.error('⚠ 自动更新失败');
156
+ console.error(' 请稍后重试,或手动运行:');
157
+ console.error(' npm update -g @88code/byebyecode');
158
+ console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
159
+ console.error('');
160
+ // 不删除 pending 文件,下次启动时继续尝试
161
+ } finally {
162
+ // 清理锁文件
163
+ try {
164
+ fs.unlinkSync(lockFile);
165
+ } catch (e) {
166
+ // Ignore
167
+ }
168
+ }
169
+ }
170
+
171
+ // ==================== 后台版本检查 ====================
172
+
173
+ /**
174
+ * 检查是否有新版本可用
175
+ * 如果有,创建 .update_pending 文件,不立即更新
176
+ */
177
+ function checkVersionAndNotify() {
178
+ const configDir = path.join(os.homedir(), '.claude', 'byebyecode');
179
+ const versionCheckFile = path.join(configDir, '.last_version_check');
180
+ const pendingFile = path.join(configDir, '.update_pending');
181
+ const noticeFile = path.join(configDir, '.update_notice');
182
+
183
+ try {
184
+ // 每次都检查,但如果已有提示则显示
185
+ if (fs.existsSync(noticeFile)) {
186
+ const notice = fs.readFileSync(noticeFile, 'utf8');
187
+ console.error(notice);
188
+ }
189
+
190
+ // 记录检查时间
191
+ fs.mkdirSync(configDir, { recursive: true });
192
+ fs.writeFileSync(versionCheckFile, Date.now().toString());
193
+
194
+ // 获取当前版本
195
+ const packageJsonPath = path.join(__dirname, '..', 'package.json');
196
+ const currentVersion = require(packageJsonPath).version;
197
+
198
+ // 从 npm registry 获取最新版本
199
+ const latestVersion = execSync('npm view @88code/byebyecode version', {
200
+ encoding: 'utf8',
201
+ timeout: 5000,
202
+ stdio: ['ignore', 'pipe', 'ignore']
203
+ }).trim();
204
+
205
+ // 发现新版本
206
+ if (latestVersion && latestVersion !== currentVersion) {
207
+ // 创建待更新文件
208
+ fs.writeFileSync(pendingFile, JSON.stringify({
209
+ currentVersion,
210
+ latestVersion,
211
+ detectedAt: Date.now()
212
+ }));
213
+
214
+ // 创建提示信息
215
+ const notice = `
216
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
217
+ 📦 发现 byebyecode 新版本!
218
+ 当前版本: v${currentVersion}
219
+ 最新版本: v${latestVersion}
220
+
221
+ 💡 更新将在您下次启动 Claude Code 时自动进行
222
+ (或手动运行: npm update -g @88code/byebyecode)
223
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
224
+ `.trim();
225
+
226
+ fs.writeFileSync(noticeFile, notice);
227
+ console.error(notice);
228
+ } else {
229
+ // 没有新版本,清理旧的提示文件
230
+ if (fs.existsSync(pendingFile)) {
231
+ fs.unlinkSync(pendingFile);
232
+ }
233
+ if (fs.existsSync(noticeFile)) {
234
+ fs.unlinkSync(noticeFile);
235
+ }
236
+ }
237
+ } catch (error) {
238
+ // 静默忽略错误(网络问题等)
239
+ }
240
+ }
241
+
242
+ // ==================== 主流程 ====================
243
+
244
+ // 步骤 1: 启动时检查并安装待更新
245
+ checkAndInstallPendingUpdate();
246
+
247
+ // 步骤 2: 确定平台对应的二进制
24
248
  const platform = process.platform;
25
249
  const arch = process.arch;
26
250
 
27
- // Handle special cases
28
251
  let platformKey = `${platform}-${arch}`;
29
252
  if (platform === 'linux') {
30
- // Detect if static linking is needed based on glibc version
253
+ // 检测是否需要静态链接版本 (glibc < 2.35)
31
254
  function shouldUseStaticBinary() {
32
255
  try {
33
- const { execSync } = require('child_process');
34
- const lddOutput = execSync('ldd --version 2>/dev/null || echo ""', {
256
+ const lddOutput = execSync('ldd --version 2>/dev/null || echo ""', {
35
257
  encoding: 'utf8',
36
- timeout: 1000
258
+ timeout: 1000
37
259
  });
38
-
39
- // Parse "ldd (GNU libc) 2.35" format
260
+
40
261
  const match = lddOutput.match(/(?:GNU libc|GLIBC).*?(\d+)\.(\d+)/);
41
262
  if (match) {
42
263
  const major = parseInt(match[1]);
43
264
  const minor = parseInt(match[2]);
44
- // Use static binary if glibc < 2.35
45
265
  return major < 2 || (major === 2 && minor < 35);
46
266
  }
47
267
  } catch (e) {
48
- // If detection fails, default to dynamic binary
49
268
  return false;
50
269
  }
51
-
52
270
  return false;
53
271
  }
54
-
272
+
55
273
  if (shouldUseStaticBinary()) {
56
274
  platformKey = 'linux-x64-musl';
57
275
  }
@@ -63,31 +281,108 @@ const packageMap = {
63
281
  'linux-x64': '@88code/byebyecode-linux-x64',
64
282
  'linux-x64-musl': '@88code/byebyecode-linux-x64-musl',
65
283
  'win32-x64': '@88code/byebyecode-win32-x64',
66
- 'win32-ia32': '@88code/byebyecode-win32-x64', // Use 64-bit for 32-bit systems
284
+ 'win32-ia32': '@88code/byebyecode-win32-x64',
67
285
  };
68
286
 
69
287
  const packageName = packageMap[platformKey];
70
288
  if (!packageName) {
71
289
  console.error(`Error: Unsupported platform ${platformKey}`);
72
290
  console.error('Supported platforms: darwin (x64/arm64), linux (x64), win32 (x64)');
73
- console.error('Please visit https://github.com/Haleclipse/CCometixLine for manual installation');
291
+ console.error('Please visit https://github.com/byebye-code/byebyecode for manual installation');
74
292
  process.exit(1);
75
293
  }
76
294
 
77
295
  const binaryName = platform === 'win32' ? 'byebyecode.exe' : 'byebyecode';
78
- const binaryPath = path.join(__dirname, '..', 'node_modules', packageName, binaryName);
296
+ // 步骤 3: 确定二进制文件路径
297
+ // 优先级:
298
+ // 1. ~/.claude/byebyecode/byebyecode (由 postinstall 安装或手动安装)
299
+ // 2. node_modules 中的对应包 (支持 npm/yarn/pnpm)
300
+
301
+ const globalConfigDir = path.join(os.homedir(), '.claude', 'byebyecode');
302
+ const globalBinaryPath = path.join(globalConfigDir, binaryName);
303
+
304
+ // 查找二进制文件的辅助函数 (支持 pnpm)
305
+ const findBinaryPathInNodeModules = () => {
306
+ const possiblePaths = [
307
+ // npm/yarn: nested in node_modules
308
+ path.join(__dirname, '..', 'node_modules', packageName, binaryName),
309
+ // pnpm: try require.resolve first
310
+ (() => {
311
+ try {
312
+ const packagePath = require.resolve(packageName + '/package.json');
313
+ return path.join(path.dirname(packagePath), binaryName);
314
+ } catch {
315
+ return null;
316
+ }
317
+ })(),
318
+ // pnpm: flat structure fallback with version detection
319
+ (() => {
320
+ const currentPath = __dirname;
321
+ const pnpmMatch = currentPath.match(/(.+\.pnpm)[\\/]([^\\//]+)[\\/]/);
322
+ if (pnpmMatch) {
323
+ const pnpmRoot = pnpmMatch[1];
324
+ const packageNameEncoded = packageName.replace('/', '+');
325
+
326
+ try {
327
+ // Try to find any version of the package
328
+ const pnpmContents = fs.readdirSync(pnpmRoot);
329
+ const packagePattern = new RegExp(`^${packageNameEncoded.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}@`);
330
+ const matchingPackage = pnpmContents.find(dir => packagePattern.test(dir));
331
+
332
+ if (matchingPackage) {
333
+ return path.join(pnpmRoot, matchingPackage, 'node_modules', packageName, binaryName);
334
+ }
335
+ } catch {
336
+ // Fallback to current behavior if directory reading fails
337
+ }
338
+ }
339
+ return null;
340
+ })()
341
+ ].filter(p => p !== null);
342
+
343
+ for (const testPath of possiblePaths) {
344
+ if (fs.existsSync(testPath)) {
345
+ return testPath;
346
+ }
347
+ }
348
+ return null;
349
+ };
79
350
 
80
- if (!fs.existsSync(binaryPath)) {
81
- console.error(`Error: Binary not found at ${binaryPath}`);
82
- console.error('This might indicate a failed installation or unsupported platform.');
83
- console.error('请尝试重新安装: npm install -g @88code/byebyecode');
351
+ let binaryPath;
352
+
353
+ // 1. 检查全局配置目录
354
+ if (fs.existsSync(globalBinaryPath)) {
355
+ binaryPath = globalBinaryPath;
356
+ } else {
357
+ // 2. 检查 node_modules
358
+ binaryPath = findBinaryPathInNodeModules();
359
+ }
360
+
361
+ if (!binaryPath || !fs.existsSync(binaryPath)) {
362
+ console.error(`Error: Binary not found for platform ${platformKey}`);
84
363
  console.error(`Expected package: ${packageName}`);
364
+ console.error(`Expected binary: ${binaryName}`);
365
+ console.error('');
366
+ console.error('Troubleshooting:');
367
+ console.error('1. Try reinstalling with force:');
368
+ console.error(' npm install -g @88code/byebyecode --force');
369
+ console.error('');
370
+ console.error('2. If using pnpm, try installing with --shamefully-hoist:');
371
+ console.error(' pnpm add -g @88code/byebyecode --shamefully-hoist');
372
+ console.error('');
373
+ console.error('3. Manually download the binary from GitHub Releases and place it at:');
374
+ console.error(` ${globalBinaryPath}`);
375
+
85
376
  process.exit(1);
86
377
  }
87
378
 
379
+ // 步骤 3: 执行二进制
88
380
  const result = spawnSync(binaryPath, process.argv.slice(2), {
89
381
  stdio: 'inherit',
90
382
  shell: false
91
383
  });
92
384
 
93
- process.exit(result.status || 0);
385
+ // 步骤 4: 执行完毕后,异步检查版本
386
+ setImmediate(() => checkVersionAndNotify());
387
+
388
+ process.exitCode = result.status || 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@88code/byebyecode",
3
- "version": "1.1.12",
3
+ "version": "1.1.14",
4
4
  "description": "CCometixLine - High-performance Claude Code StatusLine tool",
5
5
  "bin": {
6
6
  "byebyecode": "./bin/byebyecode.js"
@@ -9,11 +9,11 @@
9
9
  "postinstall": "node scripts/postinstall.js"
10
10
  },
11
11
  "optionalDependencies": {
12
- "@88code/byebyecode-darwin-x64": "1.1.12",
13
- "@88code/byebyecode-darwin-arm64": "1.1.12",
14
- "@88code/byebyecode-linux-x64": "1.1.12",
15
- "@88code/byebyecode-linux-x64-musl": "1.1.12",
16
- "@88code/byebyecode-win32-x64": "1.1.12"
12
+ "@88code/byebyecode-darwin-x64": "1.1.14",
13
+ "@88code/byebyecode-darwin-arm64": "1.1.14",
14
+ "@88code/byebyecode-linux-x64": "1.1.14",
15
+ "@88code/byebyecode-linux-x64-musl": "1.1.14",
16
+ "@88code/byebyecode-win32-x64": "1.1.14"
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",