@tkpdx01/ccc 1.3.3 → 1.3.5
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 +1 -0
- package/package.json +1 -1
- package/src/commands/edit.js +8 -9
- package/src/commands/new.js +43 -3
- package/src/profiles.js +115 -18
package/README.md
CHANGED
|
@@ -60,6 +60,7 @@ ccc webdav status # View sync status / 查看同步状态
|
|
|
60
60
|
- **Template Support / 模板**: Based on `~/.claude/settings.json`
|
|
61
61
|
- **Smart Import / 智能导入**: Auto-detect API URL and token
|
|
62
62
|
- **Sync Settings / 同步**: Update from template, preserve credentials
|
|
63
|
+
- **Claude Env Defaults / Claude 环境变量默认值**: Auto-ensure these values in the `env` section of both `~/.claude/settings.json` and each profile: `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`, `CLAUDE_CODE_ATTRIBUTION_HEADER=0`, `DISABLE_INSTALLATION_CHECKS=1`
|
|
63
64
|
- **WebDAV Cloud Sync / 云同步**: Encrypted sync across devices
|
|
64
65
|
|
|
65
66
|
## Sync Command / 同步命令
|
package/package.json
CHANGED
package/src/commands/edit.js
CHANGED
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
resolveProfile,
|
|
14
14
|
getProfileCredentials,
|
|
15
15
|
getClaudeSettingsTemplate,
|
|
16
|
-
|
|
16
|
+
ensureRequiredClaudeEnvSettings,
|
|
17
|
+
ensureClaudeSettingsExtras
|
|
17
18
|
} from '../profiles.js';
|
|
18
19
|
|
|
19
20
|
export function editCommand(program) {
|
|
@@ -96,13 +97,12 @@ export function editCommand(program) {
|
|
|
96
97
|
currentProfile.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
97
98
|
currentProfile.env.ANTHROPIC_BASE_URL = apiUrl;
|
|
98
99
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
100
|
+
// 确保主配置(~/.claude/settings.json)与 profile 都包含必要 env 设置
|
|
101
|
+
ensureRequiredClaudeEnvSettings();
|
|
102
|
+
ensureClaudeSettingsExtras();
|
|
103
|
+
currentProfile.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
|
|
104
|
+
currentProfile.env.CLAUDE_CODE_ATTRIBUTION_HEADER = '0';
|
|
105
|
+
currentProfile.env.DISABLE_INSTALLATION_CHECKS = '1';
|
|
106
106
|
|
|
107
107
|
// 如果重命名
|
|
108
108
|
if (newName && newName !== profile) {
|
|
@@ -126,4 +126,3 @@ export function editCommand(program) {
|
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
|
-
|
package/src/commands/new.js
CHANGED
|
@@ -5,10 +5,42 @@ import {
|
|
|
5
5
|
getProfiles,
|
|
6
6
|
profileExists,
|
|
7
7
|
createProfileFromTemplate,
|
|
8
|
-
setDefaultProfile
|
|
8
|
+
setDefaultProfile,
|
|
9
|
+
ensureClaudeSettingsExtras
|
|
9
10
|
} from '../profiles.js';
|
|
10
11
|
import { launchClaude } from '../launch.js';
|
|
11
12
|
|
|
13
|
+
const RESERVED_PROFILE_NAMES = [
|
|
14
|
+
'list',
|
|
15
|
+
'ls',
|
|
16
|
+
'use',
|
|
17
|
+
'show',
|
|
18
|
+
'import',
|
|
19
|
+
'if',
|
|
20
|
+
'new',
|
|
21
|
+
'edit',
|
|
22
|
+
'delete',
|
|
23
|
+
'rm',
|
|
24
|
+
'sync',
|
|
25
|
+
'webdav',
|
|
26
|
+
'help'
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
function isReservedProfileName(name) {
|
|
30
|
+
return RESERVED_PROFILE_NAMES.includes(name);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function validateProfileName(input) {
|
|
34
|
+
const trimmed = input.trim();
|
|
35
|
+
if (!trimmed) {
|
|
36
|
+
return '请输入配置名称';
|
|
37
|
+
}
|
|
38
|
+
if (isReservedProfileName(trimmed)) {
|
|
39
|
+
return '配置名称不能使用命令关键词';
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
12
44
|
export function newCommand(program) {
|
|
13
45
|
program
|
|
14
46
|
.command('new [name]')
|
|
@@ -21,10 +53,16 @@ export function newCommand(program) {
|
|
|
21
53
|
type: 'input',
|
|
22
54
|
name: 'profileName',
|
|
23
55
|
message: '配置名称:',
|
|
24
|
-
validate:
|
|
56
|
+
validate: validateProfileName
|
|
25
57
|
}
|
|
26
58
|
]);
|
|
27
59
|
name = profileName;
|
|
60
|
+
} else {
|
|
61
|
+
const validationResult = validateProfileName(name);
|
|
62
|
+
if (validationResult !== true) {
|
|
63
|
+
console.log(chalk.red(validationResult));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
28
66
|
}
|
|
29
67
|
|
|
30
68
|
// 检查是否已存在
|
|
@@ -60,7 +98,8 @@ export function newCommand(program) {
|
|
|
60
98
|
type: 'input',
|
|
61
99
|
name: 'finalName',
|
|
62
100
|
message: 'Profile 名称:',
|
|
63
|
-
default: name
|
|
101
|
+
default: name,
|
|
102
|
+
validate: validateProfileName
|
|
64
103
|
}
|
|
65
104
|
]);
|
|
66
105
|
|
|
@@ -81,6 +120,7 @@ export function newCommand(program) {
|
|
|
81
120
|
}
|
|
82
121
|
|
|
83
122
|
ensureDirs();
|
|
123
|
+
ensureClaudeSettingsExtras();
|
|
84
124
|
createProfileFromTemplate(finalName, apiUrl, apiKey);
|
|
85
125
|
console.log(chalk.green(`\n✓ 配置 "${finalName}" 已创建(基于 ~/.claude/settings.json)`));
|
|
86
126
|
|
package/src/profiles.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
3
4
|
import { CONFIG_DIR, PROFILES_DIR, DEFAULT_FILE, CLAUDE_SETTINGS_PATH } from './config.js';
|
|
4
5
|
|
|
6
|
+
function stringifyClaudeSettings(settings) {
|
|
7
|
+
// Claude Code 默认 settings.json 使用 2 空格缩进,并以换行结尾(便于 diff/兼容各平台编辑器)
|
|
8
|
+
return `${JSON.stringify(settings, null, 2)}\n`;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
// 确保目录存在
|
|
6
12
|
export function ensureDirs() {
|
|
7
13
|
if (!fs.existsSync(CONFIG_DIR)) {
|
|
@@ -86,24 +92,109 @@ export function getClaudeSettingsTemplate() {
|
|
|
86
92
|
return null;
|
|
87
93
|
}
|
|
88
94
|
|
|
89
|
-
|
|
90
|
-
// 如果没有则添加,并返回更新后的模板
|
|
91
|
-
export function ensureDisableNonessentialTraffic() {
|
|
95
|
+
function ensureClaudeEnvSettings(envUpdates) {
|
|
92
96
|
const template = getClaudeSettingsTemplate();
|
|
93
97
|
if (!template) {
|
|
94
98
|
return null;
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
// 确保 env 对象存在
|
|
98
|
-
if (!template.env) {
|
|
102
|
+
if (!template.env || typeof template.env !== 'object' || Array.isArray(template.env)) {
|
|
99
103
|
template.env = {};
|
|
100
104
|
}
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
template.env
|
|
106
|
+
let changed = false;
|
|
107
|
+
for (const [key, value] of Object.entries(envUpdates)) {
|
|
108
|
+
if (template.env[key] !== value) {
|
|
109
|
+
template.env[key] = value;
|
|
110
|
+
changed = true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (changed) {
|
|
105
115
|
// 保存回主配置
|
|
106
|
-
fs.writeFileSync(CLAUDE_SETTINGS_PATH,
|
|
116
|
+
fs.writeFileSync(CLAUDE_SETTINGS_PATH, stringifyClaudeSettings(template));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return template;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 确保主配置中有 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 设置
|
|
123
|
+
// 如果没有则添加,并返回更新后的模板
|
|
124
|
+
export function ensureDisableNonessentialTraffic() {
|
|
125
|
+
return ensureClaudeEnvSettings({ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1' });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 确保主配置中禁用 Attribution Header(Claude Code env 变量)
|
|
129
|
+
export function ensureDisableAttributionHeader() {
|
|
130
|
+
return ensureClaudeEnvSettings({ CLAUDE_CODE_ATTRIBUTION_HEADER: '0' });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 一次性确保主配置包含本工具需要的 env 设置
|
|
134
|
+
export function ensureRequiredClaudeEnvSettings() {
|
|
135
|
+
return ensureClaudeEnvSettings({
|
|
136
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
|
|
137
|
+
CLAUDE_CODE_ATTRIBUTION_HEADER: '0',
|
|
138
|
+
DISABLE_INSTALLATION_CHECKS: '1'
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 获取 statusLine 的 ccline 路径(适配不同操作系统)
|
|
143
|
+
function getCclineCommand() {
|
|
144
|
+
const platform = os.platform();
|
|
145
|
+
if (platform === 'win32') {
|
|
146
|
+
return '%USERPROFILE%\\.claude\\ccline\\ccline.exe';
|
|
147
|
+
} else {
|
|
148
|
+
// Linux 和 macOS
|
|
149
|
+
return '~/.claude/ccline/ccline';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 确保主配置包含 attribution/includeCoAuthoredBy 和 statusLine 设置
|
|
154
|
+
export function ensureClaudeSettingsExtras() {
|
|
155
|
+
const template = getClaudeSettingsTemplate();
|
|
156
|
+
if (!template) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let changed = false;
|
|
161
|
+
|
|
162
|
+
// 确保 attribution 禁用(commit/pr 为空字符串)
|
|
163
|
+
if (!template.attribution || typeof template.attribution !== 'object' || Array.isArray(template.attribution)) {
|
|
164
|
+
template.attribution = { commit: '', pr: '' };
|
|
165
|
+
changed = true;
|
|
166
|
+
} else {
|
|
167
|
+
if (template.attribution.commit !== '' || template.attribution.pr !== '') {
|
|
168
|
+
template.attribution.commit = '';
|
|
169
|
+
template.attribution.pr = '';
|
|
170
|
+
changed = true;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 兼容旧版本:确保 includeCoAuthoredBy: false
|
|
175
|
+
if (template.includeCoAuthoredBy !== false) {
|
|
176
|
+
template.includeCoAuthoredBy = false;
|
|
177
|
+
changed = true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 确保 statusLine 配置
|
|
181
|
+
const expectedCommand = getCclineCommand();
|
|
182
|
+
const expectedStatusLine = {
|
|
183
|
+
type: 'command',
|
|
184
|
+
command: expectedCommand,
|
|
185
|
+
padding: 0
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
if (!template.statusLine ||
|
|
189
|
+
template.statusLine.type !== 'command' ||
|
|
190
|
+
template.statusLine.command !== expectedCommand ||
|
|
191
|
+
template.statusLine.padding !== 0) {
|
|
192
|
+
template.statusLine = expectedStatusLine;
|
|
193
|
+
changed = true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (changed) {
|
|
197
|
+
fs.writeFileSync(CLAUDE_SETTINGS_PATH, stringifyClaudeSettings(template));
|
|
107
198
|
}
|
|
108
199
|
|
|
109
200
|
return template;
|
|
@@ -126,21 +217,25 @@ export function readProfile(name) {
|
|
|
126
217
|
export function saveProfile(name, settings) {
|
|
127
218
|
ensureDirs();
|
|
128
219
|
const profilePath = getProfilePath(name);
|
|
129
|
-
fs.writeFileSync(profilePath,
|
|
220
|
+
fs.writeFileSync(profilePath, stringifyClaudeSettings(settings));
|
|
130
221
|
}
|
|
131
222
|
|
|
132
223
|
// 创建基于主配置的 profile(复制 ~/.claude/settings.json 并设置 env)
|
|
133
224
|
export function createProfileFromTemplate(name, apiUrl, apiKey) {
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const template = getClaudeSettingsTemplate() || {};
|
|
225
|
+
// 先确保主配置包含必要 env 设置(也会写回 ~/.claude/settings.json)
|
|
226
|
+
const ensuredTemplate = ensureRequiredClaudeEnvSettings();
|
|
227
|
+
const template = ensuredTemplate || getClaudeSettingsTemplate() || {};
|
|
138
228
|
|
|
139
229
|
// 确保 env 对象存在
|
|
140
230
|
if (!template.env) {
|
|
141
231
|
template.env = {};
|
|
142
232
|
}
|
|
143
233
|
|
|
234
|
+
// 确保 profile 也包含相同设置
|
|
235
|
+
template.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
|
|
236
|
+
template.env.CLAUDE_CODE_ATTRIBUTION_HEADER = '0';
|
|
237
|
+
template.env.DISABLE_INSTALLATION_CHECKS = '1';
|
|
238
|
+
|
|
144
239
|
// 只设置 API 凭证到 env
|
|
145
240
|
template.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
146
241
|
template.env.ANTHROPIC_BASE_URL = apiUrl;
|
|
@@ -151,10 +246,8 @@ export function createProfileFromTemplate(name, apiUrl, apiKey) {
|
|
|
151
246
|
|
|
152
247
|
// 同步主配置到 profile(保留 profile 的 API 凭证)
|
|
153
248
|
export function syncProfileWithTemplate(name) {
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const template = getClaudeSettingsTemplate();
|
|
249
|
+
// 先确保主配置包含必要 env 设置(也会写回 ~/.claude/settings.json)
|
|
250
|
+
const template = ensureRequiredClaudeEnvSettings() || getClaudeSettingsTemplate();
|
|
158
251
|
if (!template) {
|
|
159
252
|
return null;
|
|
160
253
|
}
|
|
@@ -175,6 +268,11 @@ export function syncProfileWithTemplate(name) {
|
|
|
175
268
|
// 确保 env 对象存在并保留 API 凭证
|
|
176
269
|
newProfile.env = { ...(template.env || {}), ANTHROPIC_AUTH_TOKEN: apiKey, ANTHROPIC_BASE_URL: apiUrl };
|
|
177
270
|
|
|
271
|
+
// 确保 profile 也包含相同设置
|
|
272
|
+
newProfile.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
|
|
273
|
+
newProfile.env.CLAUDE_CODE_ATTRIBUTION_HEADER = '0';
|
|
274
|
+
newProfile.env.DISABLE_INSTALLATION_CHECKS = '1';
|
|
275
|
+
|
|
178
276
|
saveProfile(name, newProfile);
|
|
179
277
|
return newProfile;
|
|
180
278
|
}
|
|
@@ -208,4 +306,3 @@ export function clearDefaultProfile() {
|
|
|
208
306
|
fs.unlinkSync(DEFAULT_FILE);
|
|
209
307
|
}
|
|
210
308
|
}
|
|
211
|
-
|