-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathnode-path-provider.ts
More file actions
153 lines (127 loc) · 4.13 KB
/
Copy pathnode-path-provider.ts
File metadata and controls
153 lines (127 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/**
* PathProvider 的 Node.js 独立实现
*
* 为 CLI / npm 服务版提供路径管理,不依赖 Electron。
*
* 目录分为两类:
* - 系统数据:固定在 ~/.chatlab/,存放配置、日志、缓存、AI 数据等
* - 用户数据:可配置位置,存放聊天记录数据库等核心资产
*
* 用户数据目录(userDataDir)解析优先级:
* 1. 构造函数传入的 userDataDir 参数
* 2. CHATLAB_DATA_DIR 环境变量
* 3. ~/.chatlab/config.toml → [data] user_data_dir
* 4. 平台默认路径
*/
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
import type { PathProvider } from '@openchatlab/core'
import { writeConfigField, loadConfig } from '@openchatlab/config'
import { migrateFromElectronIfNeeded } from './migrations/electron-data-migration'
import { applyPendingNodeDataDirMigration } from './data-dir-switch'
const SYSTEM_DIR = path.join(os.homedir(), '.chatlab')
/**
* Set after resolveUserDataDir() detects Electron installation but cannot find databases.
* CLI should check this and block execution with instructions.
*/
let _pendingElectronDataWarning = false
export function hasPendingElectronDataWarning(): boolean {
return _pendingElectronDataWarning
}
export function applyPendingNodeDataDirMigrationIfNeeded(): { success: boolean; skipped?: boolean; error?: string } {
if (process.env.CHATLAB_DATA_DIR) return { success: true, skipped: true }
return applyPendingNodeDataDirMigration(SYSTEM_DIR)
}
export class NodePathProvider implements PathProvider {
private systemDir: string
private userDataDir: string
constructor(userDataDir?: string) {
this.systemDir = SYSTEM_DIR
this.userDataDir = userDataDir || resolveUserDataDir()
}
getSystemDir(): string {
return this.systemDir
}
getUserDataDir(): string {
return this.userDataDir
}
getDatabaseDir(): string {
return path.join(this.userDataDir, 'databases')
}
getAiDataDir(): string {
return path.join(this.systemDir, 'ai')
}
getSettingsDir(): string {
return path.join(this.systemDir, 'settings')
}
getCacheDir(): string {
return path.join(this.systemDir, 'cache')
}
getTempDir(): string {
return path.join(this.systemDir, 'temp')
}
getLogsDir(): string {
return path.join(this.systemDir, 'logs')
}
getDownloadsDir(): string {
return path.join(os.homedir(), 'Downloads')
}
/**
* 确保系统目录和用户数据目录都存在
*/
ensureAllDirs(): void {
const dirs = [
this.systemDir,
this.userDataDir,
this.getDatabaseDir(),
this.getAiDataDir(),
this.getSettingsDir(),
this.getCacheDir(),
this.getTempDir(),
this.getLogsDir(),
]
for (const dir of dirs) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
}
const markerPath = path.join(this.userDataDir, '.chatlab')
if (!fs.existsSync(markerPath)) {
fs.writeFileSync(markerPath, 'ChatLab Data Directory', 'utf-8')
}
}
}
function resolveUserDataDir(): string {
const envDir = process.env.CHATLAB_DATA_DIR
if (envDir) {
return expandHome(envDir)
}
const config = loadConfig()
if (config.data.user_data_dir) {
return expandHome(config.data.user_data_dir)
}
// First run: check for existing Electron desktop data before using default
const result = migrateFromElectronIfNeeded(SYSTEM_DIR)
if (result.migrated && result.userDataDir) {
return result.userDataDir
}
const defaultDir = getDefaultNodeUserDataDir()
if (result.electronDetected) {
// Electron was used but we couldn't find databases — likely a custom data directory.
// Don't persist default to config.toml so migration retries on next startup.
_pendingElectronDataWarning = true
return defaultDir
}
writeConfigField('data', 'user_data_dir', defaultDir)
return defaultDir
}
export function getDefaultNodeUserDataDir(): string {
return path.join(os.homedir(), '.chatlab', 'data')
}
function expandHome(filePath: string): string {
if (filePath.startsWith('~/') || filePath === '~') {
return path.join(os.homedir(), filePath.slice(1))
}
return filePath
}