yingclaw 1.9.1 → 2.0.0
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/lib/desktop.js +86 -50
- package/package.json +1 -1
package/lib/desktop.js
CHANGED
|
@@ -6,32 +6,37 @@ const { spawnSync } = require('child_process');
|
|
|
6
6
|
const { normalizeAnthropicBaseUrl } = require('./config');
|
|
7
7
|
|
|
8
8
|
const CLAUDE_DESKTOP_LABEL = 'Claude 桌面应用配置';
|
|
9
|
-
const
|
|
10
|
-
'inferenceProvider',
|
|
11
|
-
'inferenceGatewayBaseUrl',
|
|
12
|
-
'inferenceGatewayApiKey',
|
|
13
|
-
'inferenceGatewayAuthScheme',
|
|
14
|
-
'inferenceModels',
|
|
15
|
-
'disableDeploymentModeChooser',
|
|
16
|
-
'deploymentOrganizationUuid',
|
|
17
|
-
];
|
|
9
|
+
const CONFIG_NAME = 'yingclaw';
|
|
18
10
|
|
|
19
|
-
|
|
11
|
+
// Claude 3P 数据目录(与 1P 的 Claude/ 目录区分)
|
|
12
|
+
function getClaudeDesktopDataDir(options = {}) {
|
|
20
13
|
const platform = options.platform || process.platform;
|
|
21
14
|
const homeDir = options.homeDir || os.homedir();
|
|
22
15
|
|
|
23
16
|
if (platform === 'darwin') {
|
|
24
|
-
return path.join(homeDir, 'Library', 'Application Support', 'Claude'
|
|
17
|
+
return path.join(homeDir, 'Library', 'Application Support', 'Claude-3p');
|
|
25
18
|
}
|
|
26
19
|
|
|
27
20
|
if (platform === 'win32') {
|
|
28
|
-
const
|
|
29
|
-
return path.join(
|
|
21
|
+
const localAppData = options.localAppData || process.env.LOCALAPPDATA || path.join(homeDir, 'AppData', 'Local');
|
|
22
|
+
return path.join(localAppData, 'Claude-3p');
|
|
30
23
|
}
|
|
31
24
|
|
|
32
25
|
return null;
|
|
33
26
|
}
|
|
34
27
|
|
|
28
|
+
function getClaudeDesktopConfigLibrary(options = {}) {
|
|
29
|
+
const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
|
|
30
|
+
if (!dataDir) return null;
|
|
31
|
+
return path.join(dataDir, 'configLibrary');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 兼容旧测试名(指向 _meta.json 入口)
|
|
35
|
+
function getClaudeDesktopConfigPath(options = {}) {
|
|
36
|
+
const lib = getClaudeDesktopConfigLibrary(options);
|
|
37
|
+
return lib ? path.join(lib, '_meta.json') : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
function readJsonFile(file) {
|
|
36
41
|
try {
|
|
37
42
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
@@ -41,14 +46,13 @@ function readJsonFile(file) {
|
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
function collectModels(config) {
|
|
44
|
-
// 优先用 config 中保存的全量模型列表(setup/switch 时联网拉取的)
|
|
45
|
-
// 退化:主模型 + 快速模型
|
|
46
49
|
const list = Array.isArray(config.availableModels) && config.availableModels.length > 0
|
|
47
50
|
? [config.model, config.fastModel, ...config.availableModels]
|
|
48
51
|
: [config.model, config.fastModel];
|
|
49
52
|
return [...new Set(list.filter(Boolean))];
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
// 按官方 schema:所有值必须是字符串(包括布尔、数组都序列化)
|
|
52
56
|
function buildClaudeDesktopEnterpriseConfig(config, options = {}) {
|
|
53
57
|
const models = collectModels(config);
|
|
54
58
|
const baseUrl = normalizeAnthropicBaseUrl(config.baseUrl);
|
|
@@ -66,67 +70,97 @@ function buildClaudeDesktopEnterpriseConfig(config, options = {}) {
|
|
|
66
70
|
};
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
// 写入官方 3P 配置目录:configLibrary/_meta.json + configLibrary/<id>.json
|
|
69
74
|
function writeClaudeDesktopConfig(config, options = {}) {
|
|
70
|
-
const
|
|
71
|
-
if (!
|
|
75
|
+
const lib = options.configLibrary || getClaudeDesktopConfigLibrary(options);
|
|
76
|
+
if (!lib) {
|
|
72
77
|
return { result: 'unsupported', file: null };
|
|
73
78
|
}
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
+
fs.mkdirSync(lib, { recursive: true });
|
|
81
|
+
|
|
82
|
+
// 读已有 _meta.json,复用 entry id(保持稳定 UUID)
|
|
83
|
+
const metaFile = path.join(lib, '_meta.json');
|
|
84
|
+
const existingMeta = readJsonFile(metaFile);
|
|
85
|
+
const existingEntry = Array.isArray(existingMeta.entries)
|
|
86
|
+
? existingMeta.entries.find(e => e.name === CONFIG_NAME)
|
|
87
|
+
: null;
|
|
88
|
+
|
|
89
|
+
const entryId = existingEntry?.id || crypto.randomUUID();
|
|
90
|
+
|
|
91
|
+
// 读已有 <id>.json,复用 deploymentOrganizationUuid
|
|
92
|
+
const configFile = path.join(lib, `${entryId}.json`);
|
|
93
|
+
const existingConfig = readJsonFile(configFile);
|
|
94
|
+
const deploymentUuid = existingConfig.deploymentOrganizationUuid || options.uuid;
|
|
95
|
+
|
|
80
96
|
const enterpriseConfig = buildClaudeDesktopEnterpriseConfig(config, {
|
|
81
97
|
authScheme: options.authScheme,
|
|
82
|
-
uuid:
|
|
98
|
+
uuid: deploymentUuid,
|
|
83
99
|
});
|
|
84
100
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
// 写 <id>.json
|
|
102
|
+
fs.writeFileSync(configFile, JSON.stringify(enterpriseConfig, null, 2) + '\n');
|
|
103
|
+
|
|
104
|
+
// 更新 _meta.json:appliedId + entries(复用其他 entry,仅替换 yingclaw 这一条)
|
|
105
|
+
const otherEntries = Array.isArray(existingMeta.entries)
|
|
106
|
+
? existingMeta.entries.filter(e => e.name !== CONFIG_NAME)
|
|
107
|
+
: [];
|
|
108
|
+
const meta = {
|
|
109
|
+
appliedId: entryId,
|
|
110
|
+
entries: [
|
|
111
|
+
...otherEntries,
|
|
112
|
+
{
|
|
113
|
+
id: entryId,
|
|
114
|
+
name: CONFIG_NAME,
|
|
115
|
+
provider: 'gateway',
|
|
116
|
+
note: enterpriseConfig.inferenceGatewayBaseUrl,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
91
119
|
};
|
|
120
|
+
fs.writeFileSync(metaFile, JSON.stringify(meta, null, 2) + '\n');
|
|
92
121
|
|
|
93
|
-
|
|
94
|
-
const before = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
|
|
95
|
-
const body = JSON.stringify(next, null, 2) + '\n';
|
|
96
|
-
fs.writeFileSync(file, body);
|
|
97
|
-
return { result: before === body ? 'unchanged' : 'updated', file };
|
|
122
|
+
return { result: 'updated', file: lib, entryId };
|
|
98
123
|
}
|
|
99
124
|
|
|
100
125
|
function clearClaudeDesktopConfig(options = {}) {
|
|
101
|
-
const
|
|
102
|
-
if (!
|
|
103
|
-
return { result: 'missing', file };
|
|
126
|
+
const lib = options.configLibrary || getClaudeDesktopConfigLibrary(options);
|
|
127
|
+
if (!lib || !fs.existsSync(lib)) {
|
|
128
|
+
return { result: 'missing', file: lib };
|
|
104
129
|
}
|
|
105
130
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
131
|
+
const metaFile = path.join(lib, '_meta.json');
|
|
132
|
+
const meta = readJsonFile(metaFile);
|
|
133
|
+
const entries = Array.isArray(meta.entries) ? meta.entries : [];
|
|
134
|
+
const yingclawEntry = entries.find(e => e.name === CONFIG_NAME);
|
|
135
|
+
|
|
136
|
+
if (!yingclawEntry) {
|
|
137
|
+
return { result: 'missing', file: lib };
|
|
109
138
|
}
|
|
110
139
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
140
|
+
// 删除 <id>.json
|
|
141
|
+
const configFile = path.join(lib, `${yingclawEntry.id}.json`);
|
|
142
|
+
if (fs.existsSync(configFile)) {
|
|
143
|
+
fs.unlinkSync(configFile);
|
|
114
144
|
}
|
|
115
145
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
146
|
+
// 更新 _meta.json
|
|
147
|
+
const remaining = entries.filter(e => e.name !== CONFIG_NAME);
|
|
148
|
+
if (remaining.length === 0) {
|
|
149
|
+
fs.unlinkSync(metaFile);
|
|
119
150
|
} else {
|
|
120
|
-
|
|
151
|
+
const newMeta = {
|
|
152
|
+
appliedId: meta.appliedId === yingclawEntry.id ? (remaining[0]?.id || '') : meta.appliedId,
|
|
153
|
+
entries: remaining,
|
|
154
|
+
};
|
|
155
|
+
fs.writeFileSync(metaFile, JSON.stringify(newMeta, null, 2) + '\n');
|
|
121
156
|
}
|
|
122
157
|
|
|
123
|
-
|
|
124
|
-
return { result: 'updated', file };
|
|
158
|
+
return { result: 'updated', file: lib };
|
|
125
159
|
}
|
|
126
160
|
|
|
127
161
|
function buildClaudeDesktopOpenCommands(platform = process.platform) {
|
|
128
162
|
if (platform !== 'darwin') return null;
|
|
129
|
-
//
|
|
163
|
+
// 必须先完全退出 Claude,新配置只在启动时读取一次
|
|
130
164
|
return [
|
|
131
165
|
{ command: 'osascript', args: ['-e', 'tell application "Claude" to quit'], optional: true, waitAfter: 800 },
|
|
132
166
|
{ command: 'pkill', args: ['-TERM', '-x', 'Claude'], optional: true, waitAfter: 500 },
|
|
@@ -172,6 +206,8 @@ module.exports = {
|
|
|
172
206
|
buildClaudeDesktopOpenCommands,
|
|
173
207
|
clearClaudeDesktopConfig,
|
|
174
208
|
getClaudeDesktopConfigPath,
|
|
209
|
+
getClaudeDesktopDataDir,
|
|
210
|
+
getClaudeDesktopConfigLibrary,
|
|
175
211
|
openClaudeDesktop,
|
|
176
212
|
writeClaudeDesktopConfig,
|
|
177
213
|
CLAUDE_DESKTOP_LABEL,
|