yingclaw 2.0.1 → 2.0.6
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 +3 -3
- package/lib/desktop.js +98 -85
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,10 +124,10 @@ npm uninstall -g yingclaw
|
|
|
124
124
|
|
|
125
125
|
`claw desktop` 会额外写入 Claude Desktop 第三方推理配置:
|
|
126
126
|
|
|
127
|
-
- macOS:`~/Library/Application Support/Claude-3p/
|
|
128
|
-
- Windows:`%
|
|
127
|
+
- macOS:`~/Library/Application Support/Claude-3p/claude_desktop_config.json`
|
|
128
|
+
- Windows:`%APPDATA%\Claude-3p\claude_desktop_config.json`
|
|
129
129
|
|
|
130
|
-
写入的 `enterpriseConfig` 使用 `inferenceProvider=gateway`、`inferenceGatewayAuthScheme=bearer
|
|
130
|
+
写入的 `enterpriseConfig` 使用 `inferenceProvider=gateway`、`inferenceGatewayAuthScheme=bearer`,并按旧版方式把当前厂商模型写入 `inferenceModels`。如果本机存在新版 `configLibrary` 本地配置,`claw desktop` 会同步移除,避免 Claude 优先读取旧的错误配置。DeepSeek 的 Claude Code 终端模型仍使用 `deepseek-v4-pro[1m]`;Claude 桌面应用会写入 `deepseek-v4-pro` 和 `deepseek-v4-flash`。
|
|
131
131
|
|
|
132
132
|
## License
|
|
133
133
|
|
package/lib/desktop.js
CHANGED
|
@@ -6,7 +6,15 @@ const { spawnSync } = require('child_process');
|
|
|
6
6
|
const { normalizeAnthropicBaseUrl } = require('./config');
|
|
7
7
|
|
|
8
8
|
const CLAUDE_DESKTOP_LABEL = 'Claude 桌面应用配置';
|
|
9
|
-
const
|
|
9
|
+
const DESKTOP_GATEWAY_KEYS = [
|
|
10
|
+
'inferenceProvider',
|
|
11
|
+
'inferenceGatewayBaseUrl',
|
|
12
|
+
'inferenceGatewayApiKey',
|
|
13
|
+
'inferenceGatewayAuthScheme',
|
|
14
|
+
'inferenceModels',
|
|
15
|
+
'disableDeploymentModeChooser',
|
|
16
|
+
'deploymentOrganizationUuid',
|
|
17
|
+
];
|
|
10
18
|
|
|
11
19
|
// Claude 3P 数据目录(与 1P 的 Claude/ 目录区分)
|
|
12
20
|
function getClaudeDesktopDataDir(options = {}) {
|
|
@@ -18,23 +26,21 @@ function getClaudeDesktopDataDir(options = {}) {
|
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
if (platform === 'win32') {
|
|
21
|
-
const
|
|
22
|
-
return path.join(
|
|
29
|
+
const appData = options.appData || process.env.APPDATA || options.localAppData || path.join(homeDir, 'AppData', 'Roaming');
|
|
30
|
+
return path.join(appData, 'Claude-3p');
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
return null;
|
|
26
34
|
}
|
|
27
35
|
|
|
28
|
-
function
|
|
36
|
+
function getClaudeDesktopConfigPath(options = {}) {
|
|
29
37
|
const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
|
|
30
|
-
|
|
31
|
-
return path.join(dataDir, 'configLibrary');
|
|
38
|
+
return dataDir ? path.join(dataDir, 'claude_desktop_config.json') : null;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return lib ? path.join(lib, '_meta.json') : null;
|
|
41
|
+
function getClaudeDesktopConfigLibraryDir(options = {}) {
|
|
42
|
+
const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
|
|
43
|
+
return dataDir ? path.join(dataDir, 'configLibrary') : null;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
function readJsonFile(file) {
|
|
@@ -45,24 +51,19 @@ function readJsonFile(file) {
|
|
|
45
51
|
}
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
return config.provider === 'deepseek' || baseUrl === 'https://api.deepseek.com/anthropic';
|
|
54
|
+
function normalizeLegacyDesktopModelId(model) {
|
|
55
|
+
return model.replace(/\[\w+\]$/, '');
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
58
|
+
function toDesktopModelId(model) {
|
|
59
|
+
return model.startsWith('claude-') ? model : `claude-${model}`;
|
|
60
|
+
}
|
|
57
61
|
|
|
62
|
+
function collectModels(config) {
|
|
58
63
|
const list = Array.isArray(config.availableModels) && config.availableModels.length > 0
|
|
59
64
|
? [config.model, config.fastModel, ...config.availableModels]
|
|
60
65
|
: [config.model, config.fastModel];
|
|
61
|
-
return list.filter(Boolean);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function collectModels(config) {
|
|
65
|
-
return [...new Set(getDesktopGatewayModels(config))];
|
|
66
|
+
return [...new Set(list.filter(Boolean).map(normalizeLegacyDesktopModelId).map(toDesktopModelId))];
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
// 按官方 schema:所有值必须是字符串(包括布尔、数组都序列化)
|
|
@@ -83,92 +84,104 @@ function buildClaudeDesktopEnterpriseConfig(config, options = {}) {
|
|
|
83
84
|
};
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
function clearClaudeDesktopConfigLibrary(options = {}) {
|
|
88
|
+
const dir = options.configLibraryDir || getClaudeDesktopConfigLibraryDir(options);
|
|
89
|
+
if (!dir || !fs.existsSync(dir)) {
|
|
90
|
+
return { result: 'missing', dir };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const metaFile = path.join(dir, '_meta.json');
|
|
94
|
+
const meta = readJsonFile(metaFile);
|
|
95
|
+
const ids = new Set();
|
|
96
|
+
if (typeof meta.appliedId === 'string' && meta.appliedId) {
|
|
97
|
+
ids.add(meta.appliedId);
|
|
98
|
+
}
|
|
99
|
+
if (Array.isArray(meta.entries)) {
|
|
100
|
+
for (const entry of meta.entries) {
|
|
101
|
+
if (entry && typeof entry.id === 'string' && entry.id) {
|
|
102
|
+
ids.add(entry.id);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
91
105
|
}
|
|
92
106
|
|
|
93
|
-
|
|
107
|
+
for (const id of ids) {
|
|
108
|
+
const file = path.join(dir, `${id}.json`);
|
|
109
|
+
if (fs.existsSync(file)) {
|
|
110
|
+
fs.unlinkSync(file);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (fs.existsSync(metaFile)) {
|
|
114
|
+
fs.unlinkSync(metaFile);
|
|
115
|
+
}
|
|
94
116
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const existingMeta = readJsonFile(metaFile);
|
|
98
|
-
const existingEntry = Array.isArray(existingMeta.entries)
|
|
99
|
-
? existingMeta.entries.find(e => e.name === CONFIG_NAME)
|
|
100
|
-
: null;
|
|
117
|
+
return { result: 'updated', dir };
|
|
118
|
+
}
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
// 旧版写法:写入 Claude-3p/claude_desktop_config.json,保留用户其他偏好。
|
|
121
|
+
function writeClaudeDesktopConfig(config, options = {}) {
|
|
122
|
+
const file = options.configFile || getClaudeDesktopConfigPath(options);
|
|
123
|
+
if (!file) {
|
|
124
|
+
return { result: 'unsupported', file: null };
|
|
125
|
+
}
|
|
103
126
|
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
127
|
+
const current = readJsonFile(file);
|
|
128
|
+
const existingEnterpriseConfig = current.enterpriseConfig && typeof current.enterpriseConfig === 'object'
|
|
129
|
+
? current.enterpriseConfig
|
|
130
|
+
: {};
|
|
131
|
+
const deploymentUuid = existingEnterpriseConfig.deploymentOrganizationUuid || options.uuid;
|
|
108
132
|
|
|
109
133
|
const enterpriseConfig = buildClaudeDesktopEnterpriseConfig(config, {
|
|
110
134
|
authScheme: options.authScheme,
|
|
111
135
|
uuid: deploymentUuid,
|
|
112
136
|
});
|
|
113
137
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const meta = {
|
|
122
|
-
appliedId: entryId,
|
|
123
|
-
entries: [
|
|
124
|
-
...otherEntries,
|
|
125
|
-
{
|
|
126
|
-
id: entryId,
|
|
127
|
-
name: CONFIG_NAME,
|
|
128
|
-
provider: 'gateway',
|
|
129
|
-
note: enterpriseConfig.inferenceGatewayBaseUrl,
|
|
130
|
-
},
|
|
131
|
-
],
|
|
138
|
+
const next = {
|
|
139
|
+
...current,
|
|
140
|
+
deploymentMode: '3p',
|
|
141
|
+
enterpriseConfig: {
|
|
142
|
+
...existingEnterpriseConfig,
|
|
143
|
+
...enterpriseConfig,
|
|
144
|
+
},
|
|
132
145
|
};
|
|
133
|
-
fs.writeFileSync(metaFile, JSON.stringify(meta, null, 2) + '\n');
|
|
134
146
|
|
|
135
|
-
|
|
147
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
148
|
+
const before = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
|
|
149
|
+
const body = JSON.stringify(next, null, 2) + '\n';
|
|
150
|
+
fs.writeFileSync(file, body);
|
|
151
|
+
clearClaudeDesktopConfigLibrary({ ...options, dataDir: path.dirname(file) });
|
|
152
|
+
return { result: before === body ? 'unchanged' : 'updated', file };
|
|
136
153
|
}
|
|
137
154
|
|
|
138
155
|
function clearClaudeDesktopConfig(options = {}) {
|
|
139
|
-
const
|
|
140
|
-
if (!
|
|
141
|
-
return { result: 'missing', file
|
|
156
|
+
const file = options.configFile || getClaudeDesktopConfigPath(options);
|
|
157
|
+
if (!file || !fs.existsSync(file)) {
|
|
158
|
+
return { result: 'missing', file };
|
|
142
159
|
}
|
|
143
160
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const yingclawEntry = entries.find(e => e.name === CONFIG_NAME);
|
|
148
|
-
|
|
149
|
-
if (!yingclawEntry) {
|
|
150
|
-
return { result: 'missing', file: lib };
|
|
161
|
+
const current = readJsonFile(file);
|
|
162
|
+
if (!current.enterpriseConfig || typeof current.enterpriseConfig !== 'object') {
|
|
163
|
+
return { result: 'missing', file };
|
|
151
164
|
}
|
|
152
165
|
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
fs.unlinkSync(configFile);
|
|
166
|
+
const enterpriseConfig = { ...current.enterpriseConfig };
|
|
167
|
+
for (const key of DESKTOP_GATEWAY_KEYS) {
|
|
168
|
+
delete enterpriseConfig[key];
|
|
157
169
|
}
|
|
158
170
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
fs.unlinkSync(metaFile);
|
|
171
|
+
const next = { ...current };
|
|
172
|
+
if (Object.keys(enterpriseConfig).length > 0) {
|
|
173
|
+
next.enterpriseConfig = enterpriseConfig;
|
|
163
174
|
} else {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
fs.writeFileSync(metaFile, JSON.stringify(newMeta, null, 2) + '\n');
|
|
175
|
+
delete next.enterpriseConfig;
|
|
176
|
+
}
|
|
177
|
+
if (Object.keys(next).length === 1 && next.deploymentMode === '3p') {
|
|
178
|
+
delete next.deploymentMode;
|
|
169
179
|
}
|
|
170
180
|
|
|
171
|
-
|
|
181
|
+
fs.writeFileSync(file, JSON.stringify(next, null, 2) + '\n');
|
|
182
|
+
clearClaudeDesktopConfigLibrary({ ...options, dataDir: path.dirname(file) });
|
|
183
|
+
|
|
184
|
+
return { result: 'updated', file };
|
|
172
185
|
}
|
|
173
186
|
|
|
174
187
|
function buildClaudeDesktopOpenCommands(platform = process.platform) {
|
|
@@ -194,10 +207,11 @@ async function openClaudeDesktop(options = {}) {
|
|
|
194
207
|
|
|
195
208
|
const runner = options.runner || spawnSync;
|
|
196
209
|
const isMocked = options.runner !== undefined;
|
|
210
|
+
const timeoutMs = options.timeoutMs || 5000;
|
|
197
211
|
|
|
198
212
|
const trace = [];
|
|
199
213
|
for (const { command, args, optional, waitAfter } of commands) {
|
|
200
|
-
const result = runner(command, args, { stdio: 'pipe', encoding: 'utf8', windowsHide: true });
|
|
214
|
+
const result = runner(command, args, { stdio: 'pipe', encoding: 'utf8', windowsHide: true, timeout: timeoutMs });
|
|
201
215
|
const stderr = (result.stderr || '').toString().trim();
|
|
202
216
|
trace.push({ command, args, status: result.status, stderr });
|
|
203
217
|
|
|
@@ -220,7 +234,6 @@ module.exports = {
|
|
|
220
234
|
clearClaudeDesktopConfig,
|
|
221
235
|
getClaudeDesktopConfigPath,
|
|
222
236
|
getClaudeDesktopDataDir,
|
|
223
|
-
getClaudeDesktopConfigLibrary,
|
|
224
237
|
openClaudeDesktop,
|
|
225
238
|
writeClaudeDesktopConfig,
|
|
226
239
|
CLAUDE_DESKTOP_LABEL,
|