cli-link 0.0.5 → 0.0.7

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 (58) hide show
  1. package/bin/agentpilot.js +1 -0
  2. package/dist/client/assets/History-D2xDopni.js +4 -0
  3. package/dist/client/assets/ImageViewer-DuegU_fC.js +1 -0
  4. package/dist/client/assets/MarkdownRenderer-CsyizEL3.js +1 -0
  5. package/dist/client/assets/{PageTopBar-C8j-5s_3.js → PageTopBar-CQwjO6Af.js} +1 -1
  6. package/dist/client/assets/Session-B0s5zBGg.js +7 -0
  7. package/dist/client/assets/Settings-CfHFmJdV.js +1 -0
  8. package/dist/client/assets/Workspace-Cfl0mbNE.js +4 -0
  9. package/dist/client/assets/WorkspaceLinkedText-DCVYd9x-.js +2 -0
  10. package/dist/client/assets/c-BIGW1oBm.js +1 -0
  11. package/dist/client/assets/cpp-DIPi6g--.js +1 -0
  12. package/dist/client/assets/csharp-DSvCPggb.js +1 -0
  13. package/dist/client/assets/dart-bE4Kk8sk.js +1 -0
  14. package/dist/client/assets/dotenv-Da5cRb03.js +1 -0
  15. package/dist/client/assets/go-C27-OAKa.js +1 -0
  16. package/dist/client/assets/graphql-pNE0_Gx8.js +1 -0
  17. package/dist/client/assets/groovy-gcz8RCvz.js +1 -0
  18. package/dist/client/assets/index-BCg3ymV3.css +1 -0
  19. package/dist/client/assets/index-CrJqHlc8.js +2 -0
  20. package/dist/client/assets/java-VnEXKtx_.js +148 -0
  21. package/dist/client/assets/jsx-g9-lgVsj.js +1 -0
  22. package/dist/client/assets/kotlin-BdnUsdx6.js +1 -0
  23. package/dist/client/assets/less-B1dDrJ26.js +1 -0
  24. package/dist/client/assets/lua-BaeVxFsk.js +1 -0
  25. package/dist/client/assets/makefile-CHLpvVh8.js +1 -0
  26. package/dist/client/assets/php-BcCyJq-p.js +1 -0
  27. package/dist/client/assets/properties-DTPjHERo.js +1 -0
  28. package/dist/client/assets/ruby-BwImf3Ka.js +1 -0
  29. package/dist/client/assets/rust-B1yitclQ.js +1 -0
  30. package/dist/client/assets/scss-lMagJa-5.js +1 -0
  31. package/dist/client/assets/sql-CRqJ_cUM.js +1 -0
  32. package/dist/client/assets/svelte-B4a9v_or.js +1 -0
  33. package/dist/client/assets/swift-D82vCrfD.js +1 -0
  34. package/dist/client/assets/toml-vGWfd6FD.js +1 -0
  35. package/dist/client/assets/{vendor-icons-CNN4EKVi.js → vendor-icons-CMXJHDEv.js} +125 -65
  36. package/dist/client/assets/vendor-markdown--d-T3AbU.js +37 -0
  37. package/dist/client/assets/{vendor-motion-n6Lx6G4a.js → vendor-motion-D0ZmPdi9.js} +1 -1
  38. package/dist/client/assets/{vendor-react-DSV5aFEg.js → vendor-react-CcDXZHn_.js} +1 -1
  39. package/dist/client/assets/{vendor-virtual-CcftJrIC.js → vendor-virtual-DJI7OicV.js} +1 -1
  40. package/dist/client/assets/vue-DBXACu8K.js +1 -0
  41. package/dist/client/assets/workspace-return-FrQUv7g3.js +1 -0
  42. package/dist/client/index.html +18 -4
  43. package/dist/server/cli-manager.js +151 -26
  44. package/dist/server/codex-history.js +119 -17
  45. package/dist/server/index.js +1051 -65
  46. package/dist/server/store.js +369 -27
  47. package/dist/server/terminal-qr.js +17 -314
  48. package/package.json +5 -3
  49. package/dist/client/assets/History-BxJVDFpN.js +0 -3
  50. package/dist/client/assets/MarkdownRenderer-BO-KS_L1.js +0 -1
  51. package/dist/client/assets/Session-CQFXA2Sr.js +0 -11
  52. package/dist/client/assets/Settings-DYmjRmoN.js +0 -1
  53. package/dist/client/assets/Workspace-D8kv9euM.js +0 -8
  54. package/dist/client/assets/WorkspaceLinkedText-DQyPLk-X.js +0 -2
  55. package/dist/client/assets/code-highlight-CEcsuMpw.js +0 -1
  56. package/dist/client/assets/index-BXT2BylN.css +0 -1
  57. package/dist/client/assets/index-DOgH1Kf3.js +0 -2
  58. package/dist/client/assets/vendor-markdown-BDwu-Ux6.js +0 -35
@@ -33,6 +33,17 @@ function buildHistoryTitle(messages) {
33
33
  const userMessage = messages.find(m => m.type === 'user_message' || m.type === 'user');
34
34
  return userMessage ? cleanHistoryTitle(userMessage.content) : '会话记录';
35
35
  }
36
+ function normalizePromptHistoryText(content) {
37
+ if (typeof content !== 'string')
38
+ return null;
39
+ const text = content.trim();
40
+ if (!text)
41
+ return null;
42
+ const key = text.replace(/\s+/g, ' ').toLowerCase();
43
+ if (!key)
44
+ return null;
45
+ return { text, key };
46
+ }
36
47
  function normalizeWorkDir(workDir) {
37
48
  if (typeof workDir !== 'string')
38
49
  return null;
@@ -124,7 +135,20 @@ export function initDb(dbPath) {
124
135
  created_at INTEGER NOT NULL
125
136
  );
126
137
 
138
+ CREATE TABLE IF NOT EXISTS prompt_history (
139
+ id TEXT PRIMARY KEY,
140
+ work_dir TEXT,
141
+ text TEXT NOT NULL,
142
+ normalized_text TEXT NOT NULL,
143
+ session_id TEXT,
144
+ message_id TEXT,
145
+ use_count INTEGER NOT NULL DEFAULT 1,
146
+ last_used_at INTEGER NOT NULL,
147
+ created_at INTEGER NOT NULL
148
+ );
149
+
127
150
  CREATE INDEX IF NOT EXISTS idx_messages_session_seq ON messages(session_id, seq);
151
+ CREATE INDEX IF NOT EXISTS idx_messages_session_id ON messages(session_id, id);
128
152
  `);
129
153
  // Migrate: add 'details' column if it doesn't exist (for existing databases)
130
154
  const columns = db.prepare(`PRAGMA table_info(messages)`).all();
@@ -147,7 +171,11 @@ export function initDb(dbPath) {
147
171
  }
148
172
  db.exec(`CREATE INDEX IF NOT EXISTS idx_history_tasks_work_dir_created ON history_tasks(work_dir, created_at DESC)`);
149
173
  db.exec(`CREATE INDEX IF NOT EXISTS idx_workdir_history_last_used ON workdir_history(last_used_at DESC)`);
174
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_prompt_history_work_dir_last_used ON prompt_history(work_dir, last_used_at DESC)`);
175
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_prompt_history_last_used ON prompt_history(last_used_at DESC)`);
176
+ db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_prompt_history_work_dir_norm ON prompt_history(work_dir, normalized_text)`);
150
177
  backfillHistoryTaskSummaries();
178
+ backfillPromptHistoryFromMessages();
151
179
  console.log(`[Store] Database initialized at ${path}`);
152
180
  }
153
181
  function backfillHistoryTaskSummaries() {
@@ -475,6 +503,105 @@ export function getRecentWorkDirs(limit = 12) {
475
503
  }
476
504
  return existing;
477
505
  }
506
+ // --- Prompt History ---
507
+ export function recordPromptHistory(input) {
508
+ const normalizedText = normalizePromptHistoryText(input.text);
509
+ if (!normalizedText)
510
+ return null;
511
+ const workDir = normalizeWorkDir(input.workDir);
512
+ const now = Date.now();
513
+ const existing = workDir
514
+ ? db.prepare(`SELECT * FROM prompt_history WHERE work_dir = ? AND normalized_text = ?`)
515
+ .get(workDir, normalizedText.key)
516
+ : db.prepare(`SELECT * FROM prompt_history WHERE work_dir IS NULL AND normalized_text = ?`)
517
+ .get(normalizedText.key);
518
+ if (existing) {
519
+ db.prepare(`UPDATE prompt_history
520
+ SET text = ?, session_id = ?, message_id = ?, use_count = use_count + 1, last_used_at = ?
521
+ WHERE id = ?`).run(normalizedText.text, input.sessionId || null, input.messageId || null, now, existing.id);
522
+ return {
523
+ ...existing,
524
+ text: normalizedText.text,
525
+ session_id: input.sessionId || null,
526
+ message_id: input.messageId || null,
527
+ use_count: existing.use_count + 1,
528
+ last_used_at: now,
529
+ };
530
+ }
531
+ const item = {
532
+ id: randomUUID(),
533
+ work_dir: workDir,
534
+ text: normalizedText.text,
535
+ normalized_text: normalizedText.key,
536
+ session_id: input.sessionId || null,
537
+ message_id: input.messageId || null,
538
+ use_count: 1,
539
+ last_used_at: now,
540
+ created_at: now,
541
+ };
542
+ db.prepare(`INSERT INTO prompt_history (id, work_dir, text, normalized_text, session_id, message_id, use_count, last_used_at, created_at)
543
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(item.id, item.work_dir || null, item.text, item.normalized_text, item.session_id || null, item.message_id || null, item.use_count, item.last_used_at, item.created_at);
544
+ return item;
545
+ }
546
+ export function getPromptHistory(options = {}) {
547
+ const limit = Math.max(1, Math.min(100, Math.floor(options.limit || 50)));
548
+ const workDir = normalizeWorkDir(options.workDir);
549
+ const query = normalizePromptHistoryText(options.query || '')?.key || '';
550
+ const like = `%${query}%`;
551
+ if (workDir && query) {
552
+ return db.prepare(`SELECT * FROM prompt_history
553
+ WHERE work_dir = ? AND normalized_text LIKE ?
554
+ ORDER BY last_used_at DESC
555
+ LIMIT ?`).all(workDir, like, limit);
556
+ }
557
+ if (workDir) {
558
+ return db.prepare(`SELECT * FROM prompt_history
559
+ WHERE work_dir = ?
560
+ ORDER BY last_used_at DESC
561
+ LIMIT ?`).all(workDir, limit);
562
+ }
563
+ if (query) {
564
+ return db.prepare(`SELECT * FROM prompt_history
565
+ WHERE normalized_text LIKE ?
566
+ ORDER BY last_used_at DESC
567
+ LIMIT ?`).all(like, limit);
568
+ }
569
+ return db.prepare(`SELECT * FROM prompt_history
570
+ ORDER BY last_used_at DESC
571
+ LIMIT ?`).all(limit);
572
+ }
573
+ export function deletePromptHistory(id, workDir) {
574
+ const normalizedWorkDir = normalizeWorkDir(workDir);
575
+ const result = normalizedWorkDir
576
+ ? db.prepare(`DELETE FROM prompt_history WHERE id = ? AND work_dir = ?`).run(id, normalizedWorkDir)
577
+ : db.prepare(`DELETE FROM prompt_history WHERE id = ?`).run(id);
578
+ return result.changes > 0;
579
+ }
580
+ function backfillPromptHistoryFromMessages() {
581
+ const existing = db.prepare(`SELECT COUNT(*) AS count FROM prompt_history`).get();
582
+ if (existing.count > 0)
583
+ return;
584
+ const rows = db.prepare(`SELECT m.id AS message_id, m.session_id, m.content, s.cli_config
585
+ FROM messages m
586
+ LEFT JOIN sessions s ON s.id = m.session_id
587
+ WHERE m.type IN ('user_message', 'user') AND TRIM(m.content) != ''
588
+ ORDER BY m.seq ASC
589
+ LIMIT 10000`).all();
590
+ if (rows.length === 0)
591
+ return;
592
+ const backfill = db.transaction((items) => {
593
+ for (const item of items) {
594
+ recordPromptHistory({
595
+ text: item.content,
596
+ workDir: getWorkDirFromSessionConfig(item.cli_config),
597
+ sessionId: item.session_id,
598
+ messageId: item.message_id,
599
+ });
600
+ }
601
+ });
602
+ backfill(rows);
603
+ console.log(`[Store] Backfilled ${rows.length} user messages into prompt history`);
604
+ }
478
605
  // --- Migration (from client localStorage) ---
479
606
  export function importLocalData(messages, historyTasks, workDir) {
480
607
  const current = getCurrentSession();
@@ -496,7 +623,18 @@ export function importLocalData(messages, historyTasks, workDir) {
496
623
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
497
624
  const insertMany = db.transaction((msgs) => {
498
625
  for (const msg of msgs) {
499
- insertStmt.run(String(msg.id || randomUUID()), String(msg.type || 'system'), String(msg.content || ''), String(msg.time || new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })), msg.status || null, msg.toolName || null, msg.toolDetails || null, msg.permission ? JSON.stringify(msg.permission) : null, sessionId);
626
+ const id = String(msg.id || randomUUID());
627
+ const type = String(msg.type || 'system');
628
+ const content = String(msg.content || '');
629
+ insertStmt.run(id, type, content, String(msg.time || new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hourCycle: 'h23' })), msg.status || null, msg.toolName || null, msg.toolDetails || null, msg.permission ? JSON.stringify(msg.permission) : null, sessionId);
630
+ if (type === 'user_message' || type === 'user') {
631
+ recordPromptHistory({
632
+ text: content,
633
+ workDir: importWorkDir,
634
+ sessionId,
635
+ messageId: id,
636
+ });
637
+ }
500
638
  }
501
639
  });
502
640
  insertMany(messages);
@@ -517,10 +655,76 @@ export function importLocalData(messages, historyTasks, workDir) {
517
655
  }
518
656
  console.log(`[Store] Imported ${messages.length} messages and ${historyTasks.length} history tasks`);
519
657
  }
658
+ function jsonOrNull(value) {
659
+ if (value == null)
660
+ return null;
661
+ try {
662
+ return JSON.stringify(value);
663
+ }
664
+ catch {
665
+ return String(value);
666
+ }
667
+ }
668
+ function textOrNull(value) {
669
+ if (value == null)
670
+ return null;
671
+ const text = String(value);
672
+ return text.length > 0 ? text : null;
673
+ }
674
+ function getImportedSessionTiming(session) {
675
+ const startedAt = Number.isFinite(session.startedAt) && session.startedAt > 0 ? session.startedAt : Date.now();
676
+ const endedAt = Number.isFinite(session.endedAt) && session.endedAt > 0 ? session.endedAt : startedAt;
677
+ const createdAt = Number.isFinite(session.createdAt) && session.createdAt > 0 ? session.createdAt : endedAt;
678
+ const diffSec = Math.max(0, Math.round((endedAt - startedAt) / 1000));
679
+ const duration = diffSec > 0
680
+ ? diffSec >= 60
681
+ ? `${Math.floor(diffSec / 60)}m ${String(diffSec % 60).padStart(2, '0')}s`
682
+ : `${diffSec}s`
683
+ : null;
684
+ return { startedAt, endedAt, createdAt, duration };
685
+ }
686
+ function importedMessageSignature(message) {
687
+ return [
688
+ message.type || '',
689
+ message.time || '',
690
+ message.toolUseId || '',
691
+ message.toolName || '',
692
+ message.content || '',
693
+ ].join('\u001f');
694
+ }
695
+ function storedMessageSignature(message) {
696
+ return importedMessageSignature({
697
+ type: message.type,
698
+ content: message.content,
699
+ time: message.time,
700
+ toolName: message.toolName || undefined,
701
+ toolUseId: message.toolUseId || undefined,
702
+ });
703
+ }
704
+ function codexSpecialKeyFromDetails(details, type) {
705
+ if (type !== 'system')
706
+ return null;
707
+ const parsed = typeof details === 'string' ? parseConfig(details) : details;
708
+ if (!parsed || typeof parsed !== 'object')
709
+ return null;
710
+ const record = parsed;
711
+ const subtype = record.subtype;
712
+ const sessionId = record.session_id;
713
+ if ((subtype === 'init' || subtype === 'result') && typeof sessionId === 'string' && sessionId) {
714
+ return `${subtype}:${sessionId}`;
715
+ }
716
+ return null;
717
+ }
718
+ function putFirst(map, key, value) {
719
+ if (key == null || map.has(key))
720
+ return;
721
+ map.set(key, value);
722
+ }
520
723
  export function importCodexSessions(sessions) {
521
724
  const result = {
522
725
  scanned: sessions.length,
523
726
  imported: 0,
727
+ updated: 0,
524
728
  skipped: 0,
525
729
  failed: 0,
526
730
  messagesImported: 0,
@@ -529,60 +733,198 @@ export function importCodexSessions(sessions) {
529
733
  if (sessions.length === 0) {
530
734
  return result;
531
735
  }
532
- const sessionExists = db.prepare(`SELECT 1 FROM sessions WHERE id = ? LIMIT 1`);
533
- const taskExists = db.prepare(`SELECT 1 FROM history_tasks WHERE id = ? LIMIT 1`);
736
+ const getSession = db.prepare(`SELECT * FROM sessions WHERE id = ?`);
737
+ const getTaskById = db.prepare(`SELECT * FROM history_tasks WHERE id = ?`);
738
+ const getTaskBySession = db.prepare(`SELECT * FROM history_tasks WHERE session_id = ? ORDER BY created_at DESC LIMIT 1`);
739
+ const getMessagesBySession = db.prepare(`SELECT * FROM messages WHERE session_id = ? ORDER BY seq ASC`);
534
740
  const insertSession = db.prepare(`INSERT INTO sessions (id, status, cli_config, started_at, last_seq) VALUES (?, ?, ?, ?, 0)`);
741
+ const updateSession = db.prepare(`UPDATE sessions SET cli_config = ?, started_at = ? WHERE id = ?`);
535
742
  const insertMessage = db.prepare(`INSERT INTO messages (id, type, content, time, status, toolName, toolDetails, toolUseId, toolResult, permission, details, session_id)
536
743
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
744
+ const updateMessage = db.prepare(`UPDATE messages
745
+ SET id = ?, type = ?, content = ?, time = ?, status = ?, toolName = ?, toolDetails = ?, toolUseId = ?, toolResult = ?, permission = ?, details = ?
746
+ WHERE seq = ?`);
747
+ const getMaxSeq = db.prepare(`SELECT MAX(seq) AS seq FROM messages WHERE session_id = ?`);
537
748
  const updateLastSeq = db.prepare(`UPDATE sessions SET last_seq = ? WHERE id = ?`);
538
749
  const insertTask = db.prepare(`INSERT INTO history_tasks (id, session_id, work_dir, status, title, confirm_count, tool_count, duration, start_time, end_time, created_at)
539
750
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
751
+ const updateTask = db.prepare(`UPDATE history_tasks
752
+ SET session_id = ?, work_dir = ?, status = ?, title = ?, confirm_count = ?, tool_count = ?, duration = ?, start_time = ?, end_time = ?, created_at = ?
753
+ WHERE id = ?`);
540
754
  const importOne = db.transaction((session) => {
541
755
  const workDir = normalizeWorkDir(session.workDir);
542
756
  const messages = session.messages || [];
543
757
  const confirmCount = messages.filter(m => m.type === 'confirm_request' || m.type === 'confirm').length;
544
758
  const toolCount = messages.filter(m => m.type === 'tool_call' || m.type === 'tool').length;
545
- const startedAt = Number.isFinite(session.startedAt) && session.startedAt > 0 ? session.startedAt : Date.now();
546
- const endedAt = Number.isFinite(session.endedAt) && session.endedAt > 0 ? session.endedAt : startedAt;
547
- const createdAt = Number.isFinite(session.createdAt) && session.createdAt > 0 ? session.createdAt : endedAt;
548
- const diffSec = Math.max(0, Math.round((endedAt - startedAt) / 1000));
549
- const duration = diffSec > 0
550
- ? diffSec >= 60
551
- ? `${Math.floor(diffSec / 60)}m ${String(diffSec % 60).padStart(2, '0')}s`
552
- : `${diffSec}s`
553
- : null;
554
- insertSession.run(session.sessionId, 'archived', JSON.stringify(session.cliConfig || {}), startedAt);
555
- let lastSeq = 0;
759
+ const { startedAt, endedAt, createdAt, duration } = getImportedSessionTiming(session);
760
+ const existingSession = getSession.get(session.sessionId);
761
+ const createdSession = !existingSession;
762
+ let changed = false;
763
+ if (existingSession) {
764
+ const mergedConfig = { ...parseConfig(existingSession.cli_config), ...(session.cliConfig || {}) };
765
+ const nextConfig = JSON.stringify(mergedConfig);
766
+ const nextStartedAt = existingSession.started_at > 0 ? Math.min(existingSession.started_at, startedAt) : startedAt;
767
+ if ((existingSession.cli_config || null) !== nextConfig || existingSession.started_at !== nextStartedAt) {
768
+ updateSession.run(nextConfig, nextStartedAt, session.sessionId);
769
+ changed = true;
770
+ }
771
+ }
772
+ else {
773
+ insertSession.run(session.sessionId, 'archived', JSON.stringify(session.cliConfig || {}), startedAt);
774
+ changed = true;
775
+ }
776
+ const existingMessages = getMessagesBySession.all(session.sessionId);
777
+ const byId = new Map();
778
+ const bySpecial = new Map();
779
+ const byToolUseId = new Map();
780
+ const bySignature = new Map();
781
+ const rememberMessage = (message) => {
782
+ putFirst(byId, message.id, message);
783
+ putFirst(bySpecial, codexSpecialKeyFromDetails(message.details, message.type), message);
784
+ if (message.type === 'tool_call' || message.type === 'tool') {
785
+ putFirst(byToolUseId, message.toolUseId, message);
786
+ }
787
+ putFirst(bySignature, storedMessageSignature(message), message);
788
+ };
789
+ for (const message of existingMessages)
790
+ rememberMessage(message);
791
+ let insertedMessages = 0;
792
+ let updatedMessages = 0;
556
793
  for (const message of messages) {
557
- const inserted = insertMessage.run(message.id, message.type, message.content || '', message.time || '', message.status || null, message.toolName || null, message.toolDetails || null, message.toolUseId || null, message.toolResult || null, message.permission ? JSON.stringify(message.permission) : null, message.details ? JSON.stringify(message.details) : null, session.sessionId);
558
- lastSeq = Number(inserted.lastInsertRowid);
794
+ if (!message.id || !message.type)
795
+ continue;
796
+ const next = {
797
+ id: message.id,
798
+ type: message.type,
799
+ content: message.content || '',
800
+ time: message.time || '',
801
+ status: textOrNull(message.status),
802
+ toolName: textOrNull(message.toolName),
803
+ toolDetails: textOrNull(message.toolDetails),
804
+ toolUseId: textOrNull(message.toolUseId),
805
+ toolResult: textOrNull(message.toolResult),
806
+ permission: jsonOrNull(message.permission),
807
+ details: jsonOrNull(message.details),
808
+ };
809
+ const specialKey = codexSpecialKeyFromDetails(message.details, message.type);
810
+ const signature = importedMessageSignature(message);
811
+ const existing = byId.get(next.id)
812
+ || (next.toolUseId && (next.type === 'tool_call' || next.type === 'tool') ? byToolUseId.get(next.toolUseId) : undefined)
813
+ || (specialKey ? bySpecial.get(specialKey) : undefined)
814
+ || bySignature.get(signature);
815
+ if (existing) {
816
+ const differs = existing.id !== next.id
817
+ || existing.type !== next.type
818
+ || existing.content !== next.content
819
+ || existing.time !== next.time
820
+ || (existing.status || null) !== next.status
821
+ || (existing.toolName || null) !== next.toolName
822
+ || (existing.toolDetails || null) !== next.toolDetails
823
+ || (existing.toolUseId || null) !== next.toolUseId
824
+ || (existing.toolResult || null) !== next.toolResult
825
+ || (existing.permission || null) !== next.permission
826
+ || (existing.details || null) !== next.details;
827
+ if (differs) {
828
+ updateMessage.run(next.id, next.type, next.content, next.time, next.status, next.toolName, next.toolDetails, next.toolUseId, next.toolResult, next.permission, next.details, existing.seq);
829
+ updatedMessages += 1;
830
+ changed = true;
831
+ rememberMessage({
832
+ ...existing,
833
+ ...next,
834
+ status: next.status || undefined,
835
+ toolName: next.toolName || undefined,
836
+ toolDetails: next.toolDetails || undefined,
837
+ toolUseId: next.toolUseId || undefined,
838
+ toolResult: next.toolResult || undefined,
839
+ permission: next.permission || undefined,
840
+ details: next.details || undefined,
841
+ });
842
+ }
843
+ continue;
844
+ }
845
+ const inserted = insertMessage.run(next.id, next.type, next.content, next.time, next.status, next.toolName, next.toolDetails, next.toolUseId, next.toolResult, next.permission, next.details, session.sessionId);
846
+ insertedMessages += 1;
847
+ changed = true;
848
+ if (next.type === 'user_message' || next.type === 'user') {
849
+ recordPromptHistory({
850
+ text: next.content,
851
+ workDir,
852
+ sessionId: session.sessionId,
853
+ messageId: next.id,
854
+ });
855
+ }
856
+ rememberMessage({
857
+ seq: Number(inserted.lastInsertRowid),
858
+ id: next.id,
859
+ type: next.type,
860
+ content: next.content,
861
+ time: next.time,
862
+ status: next.status || undefined,
863
+ toolName: next.toolName || undefined,
864
+ toolDetails: next.toolDetails || undefined,
865
+ toolUseId: next.toolUseId || undefined,
866
+ toolResult: next.toolResult || undefined,
867
+ permission: next.permission || undefined,
868
+ details: next.details || undefined,
869
+ session_id: session.sessionId,
870
+ });
871
+ }
872
+ if (insertedMessages > 0 || updatedMessages > 0) {
873
+ const row = getMaxSeq.get(session.sessionId);
874
+ if (row?.seq)
875
+ updateLastSeq.run(row.seq, session.sessionId);
876
+ }
877
+ const refreshedSession = (existingSession ? getSession.get(session.sessionId) : null);
878
+ const isActiveSession = (refreshedSession || existingSession)?.status !== 'archived' && !!(refreshedSession || existingSession);
879
+ const existingTask = (getTaskById.get(session.taskId) || getTaskBySession.get(session.sessionId));
880
+ const taskStatus = session.status || 'completed';
881
+ const taskTitle = session.title || 'Codex 会话记录';
882
+ if (existingTask) {
883
+ const taskDiffers = existingTask.session_id !== session.sessionId
884
+ || (existingTask.work_dir || null) !== (workDir || null)
885
+ || existingTask.status !== taskStatus
886
+ || existingTask.title !== taskTitle
887
+ || existingTask.confirm_count !== confirmCount
888
+ || existingTask.tool_count !== toolCount
889
+ || (existingTask.duration || null) !== duration
890
+ || existingTask.start_time !== startedAt
891
+ || existingTask.end_time !== endedAt
892
+ || existingTask.created_at !== createdAt;
893
+ if (taskDiffers) {
894
+ updateTask.run(session.sessionId, workDir, taskStatus, taskTitle, confirmCount, toolCount, duration, startedAt, endedAt, createdAt, existingTask.id);
895
+ changed = true;
896
+ }
559
897
  }
560
- if (lastSeq > 0) {
561
- updateLastSeq.run(lastSeq, session.sessionId);
898
+ else if (!isActiveSession) {
899
+ insertTask.run(session.taskId, session.sessionId, workDir, taskStatus, taskTitle, confirmCount, toolCount, duration, startedAt, endedAt, createdAt);
900
+ changed = true;
562
901
  }
563
- insertTask.run(session.taskId, session.sessionId, workDir, session.status || 'completed', session.title || 'Codex 会话记录', confirmCount, toolCount, duration, startedAt, endedAt, createdAt);
564
- return messages.length;
902
+ return { createdSession, changed, insertedMessages };
565
903
  });
566
904
  for (const session of sessions) {
567
905
  try {
568
- if (!session.sessionId || !session.taskId || session.messages.length === 0) {
906
+ if (!session.sessionId || !session.taskId || !session.messages || session.messages.length === 0) {
569
907
  result.skipped += 1;
570
908
  continue;
571
909
  }
572
- if (sessionExists.get(session.sessionId) || taskExists.get(session.taskId)) {
910
+ const imported = importOne(session);
911
+ if (imported.createdSession) {
912
+ result.imported += 1;
913
+ }
914
+ else if (imported.changed) {
915
+ result.updated += 1;
916
+ }
917
+ else {
573
918
  result.skipped += 1;
574
- continue;
575
919
  }
576
- const importedMessages = importOne(session);
577
- result.imported += 1;
578
- result.messagesImported += importedMessages;
920
+ result.messagesImported += imported.insertedMessages;
579
921
  }
580
922
  catch (err) {
581
923
  result.failed += 1;
582
924
  result.errors.push(`${session.sessionId || 'unknown'}: ${err?.message || 'import failed'}`);
583
925
  }
584
926
  }
585
- console.log(`[Store] Synced Codex history: imported=${result.imported}, skipped=${result.skipped}, failed=${result.failed}`);
927
+ console.log(`[Store] Synced Codex history: imported=${result.imported}, updated=${result.updated}, skipped=${result.skipped}, failed=${result.failed}`);
586
928
  return result;
587
929
  }
588
930
  // --- Cleanup ---