@cc-claw/code 0.6.45 → 0.6.47
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/install.js +44 -18
- package/install.test.js +125 -0
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -120,8 +120,7 @@ function extractZip(buffer, dest) {
|
|
|
120
120
|
zip.extractAllTo(dest, true);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
function migrateFromClaudeCode() {
|
|
124
|
-
const home = homedir();
|
|
123
|
+
function migrateFromClaudeCode(home = homedir()) {
|
|
125
124
|
const claudeSettingsPath = join(home, ".claude", "settings.json");
|
|
126
125
|
const ccCodeDir = join(home, ".cc-code");
|
|
127
126
|
const ccCodeSettingsPath = join(ccCodeDir, "settings.json");
|
|
@@ -150,12 +149,16 @@ function migrateFromClaudeCode() {
|
|
|
150
149
|
const anthropicKey = env.ANTHROPIC_API_KEY || env.ANTHROPIC_AUTH_TOKEN || "";
|
|
151
150
|
const anthropicBaseUrl = env.ANTHROPIC_BASE_URL || "";
|
|
152
151
|
if (anthropicKey || anthropicBaseUrl) {
|
|
153
|
-
const p = { provider_type: "anthropic", api_key: anthropicKey };
|
|
154
|
-
if (anthropicBaseUrl) p.base_url = anthropicBaseUrl;
|
|
155
152
|
const models = {};
|
|
156
|
-
if (env.ANTHROPIC_DEFAULT_SONNET_MODEL) models.default = env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
157
153
|
if (env.ANTHROPIC_DEFAULT_OPUS_MODEL) models.opus = env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
154
|
+
if (env.ANTHROPIC_DEFAULT_SONNET_MODEL) models.sonnet = env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
158
155
|
if (env.ANTHROPIC_DEFAULT_HAIKU_MODEL) models.haiku = env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
156
|
+
const p = {
|
|
157
|
+
id: "anthropic",
|
|
158
|
+
type: "anthropic",
|
|
159
|
+
apiKey: anthropicKey,
|
|
160
|
+
};
|
|
161
|
+
if (anthropicBaseUrl) p.baseUrl = anthropicBaseUrl;
|
|
159
162
|
if (Object.keys(models).length > 0) p.models = models;
|
|
160
163
|
providers.push(p);
|
|
161
164
|
}
|
|
@@ -164,10 +167,14 @@ function migrateFromClaudeCode() {
|
|
|
164
167
|
const openaiKey = env.OPENAI_API_KEY || env.CODEX_API_KEY || "";
|
|
165
168
|
const openaiBaseUrl = env.OPENAI_BASE_URL || env.OPENAI_API_BASE || "";
|
|
166
169
|
if (openaiKey || openaiBaseUrl) {
|
|
167
|
-
const p = { provider_type: "openai", api_key: openaiKey };
|
|
168
|
-
if (openaiBaseUrl) p.base_url = openaiBaseUrl;
|
|
169
170
|
const models = {};
|
|
170
|
-
if (env.OPENAI_MODEL) models.
|
|
171
|
+
if (env.OPENAI_MODEL) models.sonnet = env.OPENAI_MODEL;
|
|
172
|
+
const p = {
|
|
173
|
+
id: "openai",
|
|
174
|
+
type: "openai",
|
|
175
|
+
apiKey: openaiKey,
|
|
176
|
+
};
|
|
177
|
+
if (openaiBaseUrl) p.baseUrl = openaiBaseUrl;
|
|
171
178
|
if (Object.keys(models).length > 0) p.models = models;
|
|
172
179
|
providers.push(p);
|
|
173
180
|
}
|
|
@@ -180,11 +187,26 @@ function migrateFromClaudeCode() {
|
|
|
180
187
|
mkdirSync(ccCodeDir, { recursive: true });
|
|
181
188
|
}
|
|
182
189
|
|
|
183
|
-
|
|
190
|
+
// 根据第一个 provider 的可用模型决定默认激活别名
|
|
191
|
+
const firstProvider = providers[0];
|
|
192
|
+
let activeAlias = "opus";
|
|
193
|
+
if (firstProvider.models) {
|
|
194
|
+
if (firstProvider.models.opus) activeAlias = "opus";
|
|
195
|
+
else if (firstProvider.models.sonnet) activeAlias = "sonnet";
|
|
196
|
+
else if (firstProvider.models.haiku) activeAlias = "haiku";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const ccCodeSettings = {
|
|
200
|
+
config: {
|
|
201
|
+
active_alias: activeAlias,
|
|
202
|
+
active_provider_id: firstProvider.id,
|
|
203
|
+
providers,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
184
206
|
writeFileSync(ccCodeSettingsPath, JSON.stringify(ccCodeSettings, null, 2) + "\n");
|
|
185
207
|
console.log("");
|
|
186
208
|
console.log(" Migrated ~/.claude/settings.json -> ~/.cc-code/settings.json");
|
|
187
|
-
console.log(` Found ${providers.length} provider(s): ${providers.map(p => p.
|
|
209
|
+
console.log(` Found ${providers.length} provider(s): ${providers.map(p => p.type).join(", ")}`);
|
|
188
210
|
return true;
|
|
189
211
|
}
|
|
190
212
|
|
|
@@ -259,10 +281,10 @@ async function main() {
|
|
|
259
281
|
console.log(' "config": {');
|
|
260
282
|
console.log(' "providers": [');
|
|
261
283
|
console.log(" {");
|
|
262
|
-
console.log(' "
|
|
263
|
-
console.log(' "
|
|
264
|
-
console.log(' "
|
|
265
|
-
console.log(' "models": { "
|
|
284
|
+
console.log(' "type": "openai",');
|
|
285
|
+
console.log(' "apiKey": "sk-xxx",');
|
|
286
|
+
console.log(' "baseUrl": "https://api.deepseek.com/v1",');
|
|
287
|
+
console.log(' "models": { "sonnet": "deepseek-chat" }');
|
|
266
288
|
console.log(" }");
|
|
267
289
|
console.log(" ]");
|
|
268
290
|
console.log(" }");
|
|
@@ -276,7 +298,11 @@ async function main() {
|
|
|
276
298
|
console.log("");
|
|
277
299
|
}
|
|
278
300
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
301
|
+
if (require.main === module) {
|
|
302
|
+
main().catch((err) => {
|
|
303
|
+
console.error("Failed to install cc-code:", err.message);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
module.exports = { migrateFromClaudeCode };
|
package/install.test.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const { migrateFromClaudeCode } = require("./install");
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const { join } = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
function makeTempDir() {
|
|
8
|
+
return fs.mkdtempSync(join(os.tmpdir(), "cc-code-install-test-"));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function cleanup(dir) {
|
|
12
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function writeClaudeSettings(home, content) {
|
|
16
|
+
const claudeDir = join(home, ".claude");
|
|
17
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
18
|
+
fs.writeFileSync(join(claudeDir, "settings.json"), JSON.stringify(content, null, 2));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function readCcCodeSettings(home) {
|
|
22
|
+
return JSON.parse(fs.readFileSync(join(home, ".cc-code", "settings.json"), "utf-8"));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function test(name, fn) {
|
|
26
|
+
const home = makeTempDir();
|
|
27
|
+
try {
|
|
28
|
+
fn(home);
|
|
29
|
+
console.log(` ✓ ${name}`);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.error(` ✗ ${name}`);
|
|
32
|
+
console.error(e.message);
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
} finally {
|
|
35
|
+
cleanup(home);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log("npm/install.js tests");
|
|
40
|
+
|
|
41
|
+
test("migrateFromClaudeCode returns false when no claude settings exist", (home) => {
|
|
42
|
+
const result = migrateFromClaudeCode(home);
|
|
43
|
+
if (result !== false) throw new Error("expected false");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("migrateFromClaudeCode skips when cc-code settings already exist", (home) => {
|
|
47
|
+
const ccCodeDir = join(home, ".cc-code");
|
|
48
|
+
fs.mkdirSync(ccCodeDir, { recursive: true });
|
|
49
|
+
fs.writeFileSync(join(ccCodeDir, "settings.json"), JSON.stringify({ config: {} }));
|
|
50
|
+
writeClaudeSettings(home, { env: { ANTHROPIC_API_KEY: "sk-ant" } });
|
|
51
|
+
const result = migrateFromClaudeCode(home);
|
|
52
|
+
if (result !== true) throw new Error("expected true");
|
|
53
|
+
const content = fs.readFileSync(join(ccCodeDir, "settings.json"), "utf-8");
|
|
54
|
+
if (content !== JSON.stringify({ config: {} })) {
|
|
55
|
+
throw new Error("should not overwrite existing settings");
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("migrateFromClaudeCode produces camelCase provider config for Anthropic", (home) => {
|
|
60
|
+
writeClaudeSettings(home, {
|
|
61
|
+
env: {
|
|
62
|
+
ANTHROPIC_API_KEY: "sk-ant-xxx",
|
|
63
|
+
ANTHROPIC_BASE_URL: "https://api.anthropic.com",
|
|
64
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "claude-opus-4-7",
|
|
65
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "claude-sonnet-4-6",
|
|
66
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "claude-haiku-4-5",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
const result = migrateFromClaudeCode(home);
|
|
70
|
+
if (result !== true) throw new Error("expected true");
|
|
71
|
+
const cfg = readCcCodeSettings(home);
|
|
72
|
+
if (cfg.config.active_alias !== "opus") throw new Error(`expected active_alias opus, got ${cfg.config.active_alias}`);
|
|
73
|
+
if (cfg.config.active_provider_id !== "anthropic") throw new Error("expected active_provider_id anthropic");
|
|
74
|
+
const p = cfg.config.providers[0];
|
|
75
|
+
if (p.id !== "anthropic") throw new Error("expected provider id anthropic");
|
|
76
|
+
if (p.type !== "anthropic") throw new Error("expected provider type anthropic");
|
|
77
|
+
if (p.apiKey !== "sk-ant-xxx") throw new Error("expected apiKey");
|
|
78
|
+
if (p.baseUrl !== "https://api.anthropic.com") throw new Error("expected baseUrl");
|
|
79
|
+
if (p.provider_type !== undefined) throw new Error("provider_type (snake_case) should not exist");
|
|
80
|
+
if (p.api_key !== undefined) throw new Error("api_key (snake_case) should not exist");
|
|
81
|
+
if (p.base_url !== undefined) throw new Error("base_url (snake_case) should not exist");
|
|
82
|
+
if (p.models.opus !== "claude-opus-4-7") throw new Error("expected models.opus");
|
|
83
|
+
if (p.models.sonnet !== "claude-sonnet-4-6") throw new Error("expected models.sonnet");
|
|
84
|
+
if (p.models.haiku !== "claude-haiku-4-5") throw new Error("expected models.haiku");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("migrateFromClaudeCode produces camelCase provider config for OpenAI", (home) => {
|
|
88
|
+
writeClaudeSettings(home, {
|
|
89
|
+
env: {
|
|
90
|
+
OPENAI_API_KEY: "sk-openai-xxx",
|
|
91
|
+
OPENAI_BASE_URL: "https://api.deepseek.com/v1",
|
|
92
|
+
OPENAI_MODEL: "deepseek-chat",
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
const result = migrateFromClaudeCode(home);
|
|
96
|
+
if (result !== true) throw new Error("expected true");
|
|
97
|
+
const cfg = readCcCodeSettings(home);
|
|
98
|
+
if (cfg.config.active_alias !== "sonnet") throw new Error(`expected active_alias sonnet, got ${cfg.config.active_alias}`);
|
|
99
|
+
if (cfg.config.active_provider_id !== "openai") throw new Error("expected active_provider_id openai");
|
|
100
|
+
const p = cfg.config.providers[0];
|
|
101
|
+
if (p.id !== "openai") throw new Error("expected provider id openai");
|
|
102
|
+
if (p.type !== "openai") throw new Error("expected provider type openai");
|
|
103
|
+
if (p.apiKey !== "sk-openai-xxx") throw new Error("expected apiKey");
|
|
104
|
+
if (p.baseUrl !== "https://api.deepseek.com/v1") throw new Error("expected baseUrl");
|
|
105
|
+
if (p.models.sonnet !== "deepseek-chat") throw new Error("expected models.sonnet");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("migrateFromClaudeCode supports CODEX_API_KEY fallback", (home) => {
|
|
109
|
+
writeClaudeSettings(home, {
|
|
110
|
+
env: {
|
|
111
|
+
CODEX_API_KEY: "sk-codex-xxx",
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
const result = migrateFromClaudeCode(home);
|
|
115
|
+
if (result !== true) throw new Error("expected true");
|
|
116
|
+
const cfg = readCcCodeSettings(home);
|
|
117
|
+
if (cfg.config.providers[0].apiKey !== "sk-codex-xxx") throw new Error("expected CODEX_API_KEY to be used");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
console.log("");
|
|
121
|
+
if (process.exitCode) {
|
|
122
|
+
console.log("Some tests failed.");
|
|
123
|
+
} else {
|
|
124
|
+
console.log("All tests passed.");
|
|
125
|
+
}
|