coding-tool-x 3.8.0 → 3.8.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +4 -6
  3. package/dist/web/assets/{Analytics-8dRv1aYW.js → Analytics-COEUXmd4.js} +1 -1
  4. package/dist/web/assets/{ConfigTemplates-BQRXSICO.js → ConfigTemplates-DLLkS53H.js} +1 -1
  5. package/dist/web/assets/{Home-BtzWhKIa.js → Home-DCEyLzwX.js} +1 -1
  6. package/dist/web/assets/{PluginManager-BTa-Ygf6.js → PluginManager-Oe1ZHoMO.js} +1 -1
  7. package/dist/web/assets/{ProjectList-F_cB8ldA.js → ProjectList-CWjuAFof.js} +1 -1
  8. package/dist/web/assets/{SessionList-LOrbPQvu.js → SessionList-v3ehGg9U.js} +1 -1
  9. package/dist/web/assets/{SkillManager-BzI_t-yV.js → SkillManager-DUF_qP5S.js} +1 -1
  10. package/dist/web/assets/{WorkspaceManager-Db9RGiz7.js → WorkspaceManager-CjAWBosO.js} +1 -1
  11. package/dist/web/assets/index-D5hyHPj7.js +2 -0
  12. package/dist/web/index.html +1 -1
  13. package/docs/superpowers/plans/2026-08-10-codex-conversion-compatibility.md +251 -0
  14. package/docs/superpowers/plans/2026-08-11-windows-command-window-hiding.md +90 -0
  15. package/docs/superpowers/specs/2026-08-10-codex-conversion-compatibility-design.md +61 -0
  16. package/docs/superpowers/specs/2026-08-11-windows-command-window-hiding-design.md +44 -0
  17. package/package.json +2 -2
  18. package/src/commands/update.js +1 -1
  19. package/src/config/paths.js +1 -0
  20. package/src/server/api/codex-sessions.js +14 -14
  21. package/src/server/api/gemini-sessions.js +52 -14
  22. package/src/server/api/omp-projects.js +2 -2
  23. package/src/server/api/omp-sessions.js +61 -23
  24. package/src/server/api/sessions.js +44 -2
  25. package/src/server/opencode-proxy-server.js +55 -1037
  26. package/src/server/services/claude-wire.js +694 -0
  27. package/src/server/services/codex-sessions.js +41 -233
  28. package/src/server/services/codex-wire.js +219 -0
  29. package/src/server/services/dashboard-snapshot-worker.js +5 -5
  30. package/src/server/services/gemini-sessions.js +67 -157
  31. package/src/server/services/gemini-wire.js +759 -0
  32. package/src/server/services/model-detector.js +49 -131
  33. package/src/server/services/omp-channels.js +23 -4
  34. package/src/server/services/omp-gateway.js +29 -1
  35. package/src/server/services/omp-sessions.js +102 -105
  36. package/src/server/services/omp-settings-manager.js +27 -3
  37. package/src/server/services/opencode-gateway-adapters.js +22 -1147
  38. package/src/server/services/opencode-normalization.js +546 -0
  39. package/src/server/services/opencode-sessions.js +339 -546
  40. package/src/server/services/session-converter.js +4 -4
  41. package/src/server/services/session-history-adapters/claude.js +235 -0
  42. package/src/server/services/session-history-adapters/codex.js +158 -0
  43. package/src/server/services/session-history-adapters/gemini.js +292 -0
  44. package/src/server/services/session-history-adapters/index.js +20 -0
  45. package/src/server/services/session-history-adapters/omp.js +256 -0
  46. package/src/server/services/session-history-index.js +930 -0
  47. package/src/server/services/session-history-worker.js +99 -0
  48. package/src/server/services/session-snapshot-worker.js +7 -7
  49. package/src/server/services/sessions.js +167 -775
  50. package/src/server/services/speed-test.js +85 -303
  51. package/src/server/services/sqlite-connection.js +78 -0
  52. package/src/server/services/workspace-service.js +3 -3
  53. package/dist/web/assets/index-DIWJmyL6.js +0 -2
@@ -4,6 +4,7 @@ const crypto = require('crypto');
4
4
  const { HOME_DIR } = require('../../config/paths');
5
5
  const { getGeminiDir } = require('./gemini-config');
6
6
  const { resolveModelPricing } = require('../utils/pricing');
7
+ const { listProjects: idxListProjects, listSessions: idxListSessions, getSessionStatus: idxGetSessionStatus, getRecentSessions: idxGetRecent, searchSessions: idxSearch } = require('./session-history-index');
7
8
 
8
9
  const HASH_RE = /^[a-f0-9]{64}$/;
9
10
  const SESSION_FILE_RE = /^session-(.*)-([a-f0-9]+)\.(json|jsonl)$/;
@@ -628,59 +629,20 @@ function getProjectPath(projectHash, options = {}) {
628
629
  * 聚合项目列表
629
630
  * @returns {Array} 项目对象数组
630
631
  */
631
- function getProjects(options = {}) {
632
- if (options.force) {
633
- pathMappingCache = null;
634
- pathMappingCacheTime = 0;
635
- }
636
- const sessions = getAllSessions();
637
- const projectMap = new Map();
638
-
639
- sessions.forEach(session => {
640
- const projectHash = session.projectHash;
641
-
642
- if (!projectMap.has(projectHash)) {
643
- const projectPath = session.projectRoot || getProjectPath(projectHash);
644
-
645
- // 如果找到了真实路径,使用目录名作为显示名称
646
- let displayName;
647
- if (projectPath) {
648
- displayName = path.basename(projectPath);
649
- // 如果是 home 目录,显示 ~
650
- if (projectPath === HOME_DIR) {
651
- displayName = '~';
652
- }
653
- } else {
654
- // 未找到路径,使用 hash 前 8 位
655
- displayName = `Project ${projectHash.substring(0, 8)}`;
656
- }
657
-
658
- projectMap.set(projectHash, {
659
- name: projectHash,
660
- displayName,
661
- path: projectPath,
662
- fullPath: projectPath,
663
- storageName: session.storageName,
664
- sessionCount: 0,
665
- lastUpdated: session.lastUpdated,
666
- source: 'gemini'
667
- });
668
- }
669
-
670
- const project = projectMap.get(projectHash);
671
- project.sessionCount++;
672
-
673
- // 更新最后活动时间
674
- if (new Date(session.lastUpdated) > new Date(project.lastUpdated)) {
675
- project.lastUpdated = session.lastUpdated;
676
- }
632
+ async function getProjects(options = {}) {
633
+ const projects = await idxListProjects('gemini', options);
634
+ const paths = buildPathMapping();
635
+ return projects.map(project => {
636
+ const fullPath = project.fullPath || paths.get(project.name) || null;
637
+ return {
638
+ ...project,
639
+ displayName: fullPath ? path.basename(fullPath) : project.displayName,
640
+ path: fullPath,
641
+ fullPath,
642
+ storageName: fullPath ? path.basename(fullPath) : project.name,
643
+ lastUpdated: new Date(project.lastUsed).toISOString()
644
+ };
677
645
  });
678
-
679
- // 转换为数组并排序(按最后活动时间)
680
- const projects = Array.from(projectMap.values());
681
- projects.sort((a, b) => new Date(b.lastUpdated) - new Date(a.lastUpdated));
682
-
683
- return projects;
684
646
  }
685
647
 
686
648
  /**
@@ -688,15 +650,25 @@ function getProjects(options = {}) {
688
650
  * @param {string} projectHash - 项目 hash
689
651
  * @returns {Array} 会话对象数组
690
652
  */
691
- function getProjectSessions(projectHash, options = {}) {
692
- if (options.force) {
693
- pathMappingCache = null;
694
- pathMappingCacheTime = 0;
695
- }
696
- const allSessions = getAllSessions();
697
- return allSessions
698
- .filter(session => session.projectHash === projectHash)
699
- .map(normalizeSession);
653
+ async function getProjectSessions(projectHash, options = {}) {
654
+ const indexed = await idxListSessions('gemini', projectHash, options);
655
+ return indexed.map(session => ({
656
+ sessionId: session.sessionId,
657
+ mtime: new Date(session.mtime).toISOString(),
658
+ size: session.size,
659
+ filePath: session.filePath,
660
+ gitBranch: null,
661
+ firstMessage: session.firstMessage,
662
+ forkedFrom: session.extra?.forkedFrom || null,
663
+ source: 'gemini',
664
+ tokens: session.tokens?.total ?? session.tokens ?? 0,
665
+ cost: session.extra?.cost || 0,
666
+ model: session.model || 'gemini-2.5-pro',
667
+ projectHash: session.projectName,
668
+ projectName: session.projectName,
669
+ storageName: session.extra?.storageName || session.projectName,
670
+ projectRoot: session.projectFullPath || null
671
+ }));
700
672
  }
701
673
 
702
674
  /**
@@ -704,15 +676,9 @@ function getProjectSessions(projectHash, options = {}) {
704
676
  * @param {string} sessionId - 会话 ID
705
677
  * @returns {Object|null} 完整会话数据
706
678
  */
707
- function getSession(sessionId) {
708
- const allSessions = getAllSessions();
709
- const session = allSessions.find(s => s.sessionId === sessionId);
710
-
711
- if (!session) {
712
- return null;
713
- }
714
-
715
- return readSessionFull(session.filePath);
679
+ async function getSession(sessionId) {
680
+ const status = await idxGetSessionStatus('gemini', sessionId);
681
+ return status ? readSessionFull(status.filePath) : null;
716
682
  }
717
683
 
718
684
  /**
@@ -773,11 +739,13 @@ function saveProjectOrder(order) {
773
739
  * @param {number} limit - 限制数量
774
740
  * @returns {Array} 会话对象数组
775
741
  */
776
- function getRecentSessions(limit = 5) {
777
- const allSessions = getAllSessions();
778
- return allSessions
779
- .slice(0, limit)
780
- .map(normalizeSession);
742
+ async function getRecentSessions(limit = 5) {
743
+ const sessions = await idxGetRecent('gemini', limit);
744
+ return sessions.map(session => ({
745
+ ...session,
746
+ mtime: new Date(session.mtime).toISOString(),
747
+ projectHash: session.projectName
748
+ }));
781
749
  }
782
750
 
783
751
  /**
@@ -785,26 +753,16 @@ function getRecentSessions(limit = 5) {
785
753
  * @param {string} sessionId - 会话 ID
786
754
  * @returns {Object|null} 完整会话数据
787
755
  */
788
- function getSessionById(sessionId) {
789
- const allSessions = getAllSessions();
790
- const sessionMeta = allSessions.find(s => s.sessionId === sessionId);
791
-
792
- if (!sessionMeta) {
793
- return null;
794
- }
795
-
796
- const fullSession = readSessionFull(sessionMeta.filePath);
797
-
798
- if (!fullSession) {
799
- return null;
800
- }
801
-
802
- // 合并元数据
756
+ async function getSessionById(sessionId) {
757
+ const status = await idxGetSessionStatus('gemini', sessionId);
758
+ if (!status) return null;
759
+ const fullSession = readSessionFull(status.filePath);
760
+ if (!fullSession) return null;
803
761
  return {
804
762
  ...fullSession,
805
- filePath: sessionMeta.filePath,
806
- size: sessionMeta.size,
807
- mtime: sessionMeta.mtime,
763
+ filePath: status.filePath,
764
+ size: status.size,
765
+ mtime: new Date(status.lastModified).toISOString(),
808
766
  source: 'gemini'
809
767
  };
810
768
  }
@@ -815,70 +773,22 @@ function getSessionById(sessionId) {
815
773
  * @param {number} contextLength - 上下文长度(可选)
816
774
  * @returns {Array} 搜索结果数组
817
775
  */
818
- function searchSessions(keyword, contextLength = 35) {
819
- const allSessions = getAllSessions();
820
- const results = [];
821
-
822
- allSessions.forEach(sessionMeta => {
823
- const fullSession = readSessionFull(sessionMeta.filePath);
824
-
825
- if (!fullSession || !fullSession.messages) {
826
- return;
827
- }
828
-
829
- const matches = [];
830
-
831
- fullSession.messages.forEach((msg, index) => {
832
- let content = '';
833
-
834
- // 提取消息内容
835
- if (msg.type === 'user' || msg.type === 'assistant' || msg.type === 'gemini') {
836
- content = extractContentText(msg.content);
837
- }
838
-
839
- // 搜索关键词
840
- const lowerContent = content.toLowerCase();
841
- const lowerKeyword = keyword.toLowerCase();
842
-
843
- if (lowerContent.includes(lowerKeyword)) {
844
- const keywordIndex = lowerContent.indexOf(lowerKeyword);
845
-
846
- // 提取上下文
847
- const start = Math.max(0, keywordIndex - contextLength);
848
- const end = Math.min(content.length, keywordIndex + keyword.length + contextLength);
849
-
850
- let context = content.substring(start, end);
851
-
852
- // 添加省略号
853
- if (start > 0) context = '...' + context;
854
- if (end < content.length) context = context + '...';
855
-
856
- matches.push({
857
- messageIndex: index,
858
- role: msg.type,
859
- context,
860
- timestamp: msg.timestamp
861
- });
862
- }
863
- });
864
-
865
- if (matches.length > 0) {
866
- results.push({
867
- sessionId: sessionMeta.sessionId,
868
- projectHash: sessionMeta.projectHash,
869
- firstMessage: sessionMeta.firstMessage,
870
- lastUpdated: sessionMeta.lastUpdated,
871
- matches,
872
- matchCount: matches.length,
873
- source: 'gemini'
874
- });
875
- }
876
- });
877
-
878
- // 按匹配数量排序
879
- results.sort((a, b) => b.matchCount - a.matchCount);
880
-
881
- return results;
776
+ async function searchSessions(keyword, contextLength = 35) {
777
+ const results = await idxSearch('gemini', keyword, { contextLength });
778
+ return results.map(result => ({
779
+ sessionId: result.sessionId,
780
+ projectHash: result.projectName,
781
+ firstMessage: result.firstMessage,
782
+ lastUpdated: result.updatedAt || null,
783
+ matches: result.matches.map(match => ({
784
+ messageIndex: match.ordinal,
785
+ role: match.type === 'gemini' ? 'gemini' : match.role,
786
+ context: match.context,
787
+ timestamp: match.timestamp
788
+ })),
789
+ matchCount: result.matchCount,
790
+ source: 'gemini'
791
+ }));
882
792
  }
883
793
 
884
794
  /**