iivo-sub 0.1.6 → 0.1.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.
package/README.md CHANGED
@@ -35,7 +35,9 @@ Backup configuration lets you choose Codex, Claude Code, Hermes, OpenClaw, or al
35
35
 
36
36
  Chat record backup lets you back up common local conversation paths for Codex, Claude Code, Hermes, and OpenClaw. Restore creates a safety backup before replacing existing chat record paths.
37
37
 
38
- For Codex, chat backup includes sessions, the recent-task index, history, attachments, shell snapshots, and local state files. SQLite state files are backed up with a consistent SQLite snapshot when `sqlite3` is available. Restore rebuilds the recent-task index from restored session files and also rebuilds the Codex SQLite `threads` task list when possible, because recent Codex builds read the task list from `~/.codex/state_*.sqlite`.
38
+ For Codex, chat backup includes sessions, the recent-task index, history, attachments, shell snapshots, and local state files. SQLite state files are backed up with a consistent SQLite snapshot when `sqlite3` is available. Restore rebuilds the recent-task index from restored session files and also rebuilds the Codex SQLite `threads` task list plus `backfill_state` when possible, because recent Codex builds read the task list from `~/.codex/state_*.sqlite`.
39
+
40
+ After restoring Codex chats in VS Code, run `Developer: Reload Window` or fully restart the VS Code/Codex window. The Codex extension keeps an in-process task-list cache, so the sidebar may continue to show the old small list until the window reloads.
39
41
 
40
42
  After you enter an API key once, it is cached in `~/.iivo-sub/cache.json`. Next time the prompt shows a masked key such as `sk-x...abcd`; press Enter to reuse it or type a new key to replace it.
41
43
 
package/bin/iivo-sub.js CHANGED
@@ -6,7 +6,7 @@ import path from 'node:path'
6
6
  import readline from 'node:readline'
7
7
  import { spawnSync } from 'node:child_process'
8
8
 
9
- const VERSION = '0.1.6'
9
+ const VERSION = '0.1.7'
10
10
  const APP_DIR = path.join(os.homedir(), '.iivo-sub')
11
11
  const CACHE_FILE = path.join(APP_DIR, 'cache.json')
12
12
  const SCRIPTED_INPUT = !process.stdin.isTTY ? fs.readFileSync(0, 'utf8').split(/\r?\n/) : null
@@ -663,6 +663,10 @@ function listCodexSessionFiles(dir) {
663
663
  return out
664
664
  }
665
665
 
666
+ function codexRelativePath(filePath) {
667
+ return path.relative(path.join(os.homedir(), '.codex'), filePath).split(path.sep).join('/')
668
+ }
669
+
666
670
  function rebuildCodexSessionIndex() {
667
671
  const sessionsDir = path.join(os.homedir(), '.codex', 'sessions')
668
672
  const indexPath = path.join(os.homedir(), '.codex', 'session_index.jsonl')
@@ -695,6 +699,25 @@ function sqlString(value) {
695
699
  return `'${String(value ?? '').replaceAll("'", "''")}'`
696
700
  }
697
701
 
702
+ function updateCodexBackfillState(sqlite3, dbPath, watermark) {
703
+ if (!watermark) return false
704
+ const tableCheck = spawnSync(sqlite3, [dbPath, "select name from sqlite_master where type='table' and name='backfill_state';"], {
705
+ encoding: 'utf8',
706
+ timeout: 10000
707
+ })
708
+ if (tableCheck.status !== 0 || !tableCheck.stdout.includes('backfill_state')) return false
709
+
710
+ const completedAt = Math.trunc(Date.now() / 1000)
711
+ const script = [
712
+ 'BEGIN;',
713
+ 'DELETE FROM backfill_state WHERE id = 1;',
714
+ `INSERT INTO backfill_state (id, status, last_watermark, last_success_at, updated_at) VALUES (1, 'complete', ${sqlString(watermark)}, ${completedAt}, ${completedAt});`,
715
+ 'COMMIT;'
716
+ ].join('\n')
717
+ const result = runSqliteScript(sqlite3, dbPath, script)
718
+ return result.status === 0
719
+ }
720
+
698
721
  function rebuildCodexThreadsState() {
699
722
  const sqlite3 = findSqlite3()
700
723
  if (!sqlite3) return { rebuilt: 0, reason: 'sqlite3 not found' }
@@ -709,7 +732,14 @@ function rebuildCodexThreadsState() {
709
732
  const rows = files.map(codexSessionSummary).filter((row) => row.id)
710
733
  if (rows.length === 0) return { rebuilt: 0, reason: 'no session ids' }
711
734
 
735
+ const watermark = files
736
+ .map(codexRelativePath)
737
+ .filter((value) => value && !value.startsWith('..'))
738
+ .sort()
739
+ .at(-1) || ''
740
+
712
741
  let total = 0
742
+ let backfillUpdated = 0
713
743
  for (const dbPath of stateDbs) {
714
744
  const tableCheck = spawnSync(sqlite3, [dbPath, "select name from sqlite_master where type='table' and name='threads';"], {
715
745
  encoding: 'utf8',
@@ -744,9 +774,12 @@ ${createdMs}, ${updatedMs}, ${row.thread_source ? sqlString(row.thread_source) :
744
774
  statements.push('COMMIT;')
745
775
 
746
776
  const result = runSqliteScript(sqlite3, dbPath, statements.join('\n'))
747
- if (result.status === 0) total += rows.length
777
+ if (result.status === 0) {
778
+ total += rows.length
779
+ if (updateCodexBackfillState(sqlite3, dbPath, watermark)) backfillUpdated += 1
780
+ }
748
781
  }
749
- return { rebuilt: total, reason: total > 0 ? '' : 'threads table not rebuilt' }
782
+ return { rebuilt: total, backfillUpdated, reason: total > 0 ? '' : 'threads table not rebuilt' }
750
783
  }
751
784
 
752
785
  function loadBackupManifest(dir) {
@@ -1365,9 +1398,16 @@ async function restoreBackupByKind(kind, title, emptyMessage) {
1365
1398
  }
1366
1399
  if (rebuiltCodexThreads.rebuilt > 0) {
1367
1400
  console.log(c('cyan', `已重建 Codex sqlite 任务列表: ${rebuiltCodexThreads.rebuilt} 条`))
1401
+ if (rebuiltCodexThreads.backfillUpdated > 0) {
1402
+ console.log(c('cyan', `已同步 Codex sqlite 回填状态: ${rebuiltCodexThreads.backfillUpdated} 个数据库`))
1403
+ }
1368
1404
  } else if (rebuiltCodexThreads.reason) {
1369
1405
  console.log(c('yellow', `未重建 Codex sqlite 任务列表: ${rebuiltCodexThreads.reason}`))
1370
1406
  }
1407
+ if (kind === 'chat' && restoredCodexSessions) {
1408
+ console.log()
1409
+ console.log(c('yellow', '重要: Codex VS Code 插件会缓存任务列表。还原后请执行 "Developer: Reload Window",或完全退出并重开 VS Code/Codex 窗口,否则侧边栏可能仍显示还原前的少量记录。'))
1410
+ }
1371
1411
  console.log()
1372
1412
  console.log(c('yellow', `恢复前安全备份目录: ${safetyBackup}`))
1373
1413
  await pause()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iivo-sub",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "IIVO AI gateway quick configuration CLI",
5
5
  "type": "module",
6
6
  "bin": {