@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.
Files changed (3) hide show
  1. package/install.js +44 -18
  2. package/install.test.js +125 -0
  3. 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.default = env.OPENAI_MODEL;
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
- const ccCodeSettings = { config: { providers } };
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.provider_type).join(", ")}`);
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(' "provider_type": "openai",');
263
- console.log(' "api_key": "sk-xxx",');
264
- console.log(' "base_url": "https://api.deepseek.com/v1",');
265
- console.log(' "models": { "default": "deepseek-chat" }');
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
- main().catch((err) => {
280
- console.error("Failed to install cc-code:", err.message);
281
- process.exit(1);
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 };
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cc-claw/code",
3
- "version": "0.6.45",
3
+ "version": "0.6.47",
4
4
  "description": "cc-code — Terminal coding agent powered by open-source models — Rust-built, Claude Code compatible",
5
5
  "keywords": [
6
6
  "agent",