@adversity/coding-tool-x 2.4.0 → 2.4.2

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.
@@ -396,27 +396,44 @@ function deleteWorkspace(id, removeFiles = false) {
396
396
 
397
397
  const workspace = data.workspaces[index];
398
398
 
399
- // 如果需要删除物理文件
400
- if (removeFiles && fs.existsSync(workspace.path)) {
401
- try {
402
- // 清理 worktrees
403
- for (const proj of workspace.projects) {
404
- if (proj.worktrees && proj.worktrees.length > 0) {
405
- for (const wt of proj.worktrees) {
406
- if (fs.existsSync(wt.path)) {
407
- try {
408
- execSync(`git worktree remove "${wt.path}" --force`, {
409
- cwd: proj.sourcePath,
410
- stdio: 'pipe'
411
- });
412
- } catch (error) {
413
- console.error(`删除 worktree 失败: ${wt.path}`, error.message);
399
+ // 清理 worktrees (无论是否删除工作区目录,都应该清理 worktree)
400
+ for (const proj of workspace.projects) {
401
+ if (proj.isGitRepo && proj.sourcePath && fs.existsSync(proj.sourcePath)) {
402
+ try {
403
+ // 重新扫描实际的 worktrees,确保获取最新状态
404
+ const actualWorktrees = getGitWorktrees(proj.sourcePath);
405
+ for (const wt of actualWorktrees) {
406
+ // 只删除属于这个工作区的 worktree (通过 -ws- 标识符识别)
407
+ if (wt.path && wt.path.includes('-ws-')) {
408
+ try {
409
+ console.log(`清理 worktree: ${wt.path}`);
410
+ execSync(`git worktree remove "${wt.path}" --force`, {
411
+ cwd: proj.sourcePath,
412
+ stdio: 'pipe'
413
+ });
414
+ } catch (error) {
415
+ console.error(`删除 worktree 失败: ${wt.path}`, error.message);
416
+ // 如果 git worktree remove 失败,尝试手动删除目录
417
+ if (fs.existsSync(wt.path)) {
418
+ try {
419
+ fs.rmSync(wt.path, { recursive: true, force: true });
420
+ console.log(`手动删除 worktree 目录: ${wt.path}`);
421
+ } catch (rmError) {
422
+ console.error(`手动删除 worktree 目录失败: ${wt.path}`, rmError.message);
423
+ }
414
424
  }
415
425
  }
416
426
  }
417
427
  }
428
+ } catch (error) {
429
+ console.error(`扫描 worktree 失败: ${proj.sourcePath}`, error.message);
418
430
  }
431
+ }
432
+ }
419
433
 
434
+ // 如果需要删除物理文件
435
+ if (removeFiles && fs.existsSync(workspace.path)) {
436
+ try {
420
437
  // 删除工作区目录
421
438
  fs.rmSync(workspace.path, { recursive: true, force: true });
422
439
  } catch (error) {
@@ -594,50 +611,72 @@ function removeProjectFromWorkspace(workspaceId, projectName, removeWorktrees =
594
611
 
595
612
  /**
596
613
  * 获取所有渠道(Claude/Codex/Gemini)的项目并集
597
- * @returns {Array} 去重后的项目列表
614
+ * @returns {Promise<Array>} 去重后的项目列表
598
615
  */
599
- function getAllAvailableProjects() {
600
- const { NATIVE_PATHS } = require('../../config/paths');
616
+ async function getAllAvailableProjects() {
617
+ const { loadConfig } = require('../../config/loader');
601
618
  const sessionsService = require('./sessions');
619
+ const codexSessionsService = require('./codex-sessions');
620
+ const geminiSessionsService = require('./gemini-sessions');
621
+ const { isCodexInstalled } = require('./codex-config');
622
+ const { isGeminiInstalled } = require('./gemini-config');
602
623
 
603
624
  const allProjects = [];
604
- const seenPaths = new Set();
625
+ const seenKeys = new Set();
626
+
627
+ function addProject(channel, project) {
628
+ if (!project || !project.name) return;
629
+
630
+ const projectPath = project.fullPath || project.path || null;
631
+ const keyBase = projectPath || project.name;
632
+ const projectKey = `${channel}:${keyBase}`;
633
+ if (seenKeys.has(projectKey)) return;
634
+ seenKeys.add(projectKey);
635
+
636
+ const displayName = project.displayName || (projectPath ? path.basename(projectPath) : project.name);
637
+ const lastUsedValue = project.lastUsed || project.lastUpdated || null;
638
+ const lastUsed = typeof lastUsedValue === 'string'
639
+ ? new Date(lastUsedValue).getTime()
640
+ : (lastUsedValue || 0);
641
+
642
+ allProjects.push({
643
+ name: project.name,
644
+ displayName,
645
+ fullPath: projectPath || project.name,
646
+ channel,
647
+ sessionCount: project.sessionCount || 0,
648
+ lastUsed,
649
+ isGitRepo: projectPath ? isGitRepo(projectPath) : false
650
+ });
651
+ }
605
652
 
606
- // 定义渠道配置
607
- const channels = [
608
- { name: 'claude', projectsDir: NATIVE_PATHS.claude.projects },
609
- { name: 'codex', projectsDir: NATIVE_PATHS.codex.sessions },
610
- { name: 'gemini', projectsDir: NATIVE_PATHS.gemini.tmp }
611
- ];
653
+ try {
654
+ const config = loadConfig();
655
+ const claudeProjects = await sessionsService.getProjectsWithStats(config, { force: true });
656
+ const list = Array.isArray(claudeProjects) ? claudeProjects : [];
657
+ list.forEach(project => addProject('claude', project));
658
+ } catch (error) {
659
+ console.error('获取 claude 项目失败:', error.message);
660
+ }
612
661
 
613
- for (const channel of channels) {
614
- try {
615
- if (!fs.existsSync(channel.projectsDir)) {
616
- continue;
617
- }
662
+ try {
663
+ if (isCodexInstalled()) {
664
+ const codexProjects = codexSessionsService.getProjects();
665
+ const list = Array.isArray(codexProjects) ? codexProjects : [];
666
+ list.forEach(project => addProject('codex', project));
667
+ }
668
+ } catch (error) {
669
+ console.error('获取 codex 项目失败:', error.message);
670
+ }
618
671
 
619
- const config = { projectsDir: channel.projectsDir };
620
- const projects = sessionsService.getProjectsWithStats(config, { force: true });
621
-
622
- for (const proj of projects) {
623
- // 使用 fullPath 去重
624
- const projectPath = proj.fullPath;
625
- if (projectPath && !seenPaths.has(projectPath)) {
626
- seenPaths.add(projectPath);
627
- allProjects.push({
628
- name: proj.name,
629
- displayName: proj.displayName,
630
- fullPath: projectPath,
631
- channel: channel.name,
632
- sessionCount: proj.sessionCount || 0,
633
- lastUsed: proj.lastUsed,
634
- isGitRepo: isGitRepo(projectPath)
635
- });
636
- }
637
- }
638
- } catch (error) {
639
- console.error(`获取 ${channel.name} 项目失败:`, error.message);
672
+ try {
673
+ if (isGeminiInstalled()) {
674
+ const geminiProjects = geminiSessionsService.getProjects();
675
+ const list = Array.isArray(geminiProjects) ? geminiProjects : [];
676
+ list.forEach(project => addProject('gemini', project));
640
677
  }
678
+ } catch (error) {
679
+ console.error('获取 gemini 项目失败:', error.message);
641
680
  }
642
681
 
643
682
  // 按最后使用时间排序
@@ -175,7 +175,10 @@ function startWebSocketServer(httpServer) {
175
175
  console.log(`✅ WebSocket server started on ws://127.0.0.1:${port}/ws`);
176
176
  }
177
177
 
178
- wss.on('connection', (ws) => {
178
+ wss.on('connection', (ws, req) => {
179
+ const clientIp = req.socket.remoteAddress;
180
+ console.log(`[WebSocket] New connection from ${clientIp}`);
181
+
179
182
  wsClients.add(ws);
180
183
 
181
184
  // 标记客户端存活