cc-cast 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/LICENSE +21 -0
- package/README.md +202 -0
- package/README.zh-CN.md +202 -0
- package/dist/claude.d.ts +3 -0
- package/dist/claude.js +27 -0
- package/dist/i18n/en.d.ts +3 -0
- package/dist/i18n/en.js +139 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.js +36 -0
- package/dist/i18n/zh.d.ts +120 -0
- package/dist/i18n/zh.js +139 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1060 -0
- package/dist/store/cc-switch.d.ts +13 -0
- package/dist/store/cc-switch.js +114 -0
- package/dist/store/interface.d.ts +1 -0
- package/dist/store/interface.js +1 -0
- package/dist/store/standalone.d.ts +9 -0
- package/dist/store/standalone.js +65 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +42 -0
- package/package.json +37 -0
- package/src/claude.ts +32 -0
- package/src/i18n/en.ts +161 -0
- package/src/i18n/index.ts +44 -0
- package/src/i18n/zh.ts +160 -0
- package/src/index.ts +1149 -0
- package/src/store/cc-switch.ts +145 -0
- package/src/store/interface.ts +1 -0
- package/src/store/standalone.ts +80 -0
- package/src/types.ts +19 -0
- package/src/utils.ts +47 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DataStore, Profile } from "./interface.js";
|
|
2
|
+
export declare function ccSwitchExists(): boolean;
|
|
3
|
+
export declare class CcSwitchStore implements DataStore {
|
|
4
|
+
private db;
|
|
5
|
+
constructor();
|
|
6
|
+
list(): Profile[];
|
|
7
|
+
get(name: string): Profile | undefined;
|
|
8
|
+
save(name: string, settingsConfig: Record<string, unknown>): void;
|
|
9
|
+
remove(name: string): boolean;
|
|
10
|
+
getCurrent(): string | undefined;
|
|
11
|
+
setCurrent(name: string): void;
|
|
12
|
+
close(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { t } from "../i18n/index.js";
|
|
6
|
+
const DB_PATH = join(homedir(), ".cc-switch", "cc-switch.db");
|
|
7
|
+
const SETTINGS_PATH = join(homedir(), ".cc-switch", "settings.json");
|
|
8
|
+
export function ccSwitchExists() {
|
|
9
|
+
return existsSync(DB_PATH);
|
|
10
|
+
}
|
|
11
|
+
export class CcSwitchStore {
|
|
12
|
+
db;
|
|
13
|
+
constructor() {
|
|
14
|
+
if (!existsSync(DB_PATH)) {
|
|
15
|
+
throw new Error(t("store.db_not_found", { path: DB_PATH }));
|
|
16
|
+
}
|
|
17
|
+
this.db = new Database(DB_PATH);
|
|
18
|
+
}
|
|
19
|
+
list() {
|
|
20
|
+
const rows = this.db
|
|
21
|
+
.prepare(`SELECT id, name, settings_config FROM providers WHERE app_type = 'claude' ORDER BY sort_index`)
|
|
22
|
+
.all();
|
|
23
|
+
return rows.map((row) => ({
|
|
24
|
+
id: row.id,
|
|
25
|
+
name: row.name,
|
|
26
|
+
settingsConfig: JSON.parse(row.settings_config),
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
get(name) {
|
|
30
|
+
const row = this.db
|
|
31
|
+
.prepare(`SELECT id, name, settings_config FROM providers WHERE app_type = 'claude' AND name = ?`)
|
|
32
|
+
.get(name);
|
|
33
|
+
if (!row)
|
|
34
|
+
return undefined;
|
|
35
|
+
return {
|
|
36
|
+
id: row.id,
|
|
37
|
+
name: row.name,
|
|
38
|
+
settingsConfig: JSON.parse(row.settings_config),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
save(name, settingsConfig) {
|
|
42
|
+
const existing = this.get(name);
|
|
43
|
+
if (existing) {
|
|
44
|
+
this.db
|
|
45
|
+
.prepare(`UPDATE providers SET settings_config = ? WHERE app_type = 'claude' AND name = ?`)
|
|
46
|
+
.run(JSON.stringify(settingsConfig), name);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const maxSort = this.db
|
|
50
|
+
.prepare(`SELECT COALESCE(MAX(sort_index), -1) as max_sort FROM providers WHERE app_type = 'claude'`)
|
|
51
|
+
.get();
|
|
52
|
+
const sortIndex = (maxSort?.max_sort ?? -1) + 1;
|
|
53
|
+
const id = crypto.randomUUID();
|
|
54
|
+
this.db
|
|
55
|
+
.prepare(`INSERT INTO providers (
|
|
56
|
+
id, app_type, name, settings_config, website_url, category,
|
|
57
|
+
created_at, sort_index, notes, icon, icon_color, meta, is_current, in_failover_queue
|
|
58
|
+
) VALUES (?, 'claude', ?, ?, NULL, NULL, ?, ?, NULL, NULL, NULL, '{}', 0, 0)`)
|
|
59
|
+
.run(id, name, JSON.stringify(settingsConfig), Date.now(), sortIndex);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
remove(name) {
|
|
63
|
+
const result = this.db
|
|
64
|
+
.prepare(`DELETE FROM providers WHERE app_type = 'claude' AND name = ?`)
|
|
65
|
+
.run(name);
|
|
66
|
+
return result.changes > 0;
|
|
67
|
+
}
|
|
68
|
+
getCurrent() {
|
|
69
|
+
// Prefer DB is_current so we stay in sync with cc-switch UI
|
|
70
|
+
const dbRow = this.db
|
|
71
|
+
.prepare(`SELECT name FROM providers WHERE app_type = 'claude' AND is_current = 1 LIMIT 1`)
|
|
72
|
+
.get();
|
|
73
|
+
if (dbRow)
|
|
74
|
+
return dbRow.name;
|
|
75
|
+
// Fallback to settings.json
|
|
76
|
+
if (!existsSync(SETTINGS_PATH))
|
|
77
|
+
return undefined;
|
|
78
|
+
try {
|
|
79
|
+
const settings = JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
|
|
80
|
+
const currentId = settings.currentProviderClaude;
|
|
81
|
+
if (!currentId)
|
|
82
|
+
return undefined;
|
|
83
|
+
const row = this.db
|
|
84
|
+
.prepare(`SELECT name FROM providers WHERE app_type = 'claude' AND id = ?`)
|
|
85
|
+
.get(currentId);
|
|
86
|
+
return row?.name;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
setCurrent(name) {
|
|
93
|
+
const profile = this.get(name);
|
|
94
|
+
if (!profile)
|
|
95
|
+
throw new Error(t("error.not_found", { name }));
|
|
96
|
+
const tx = this.db.transaction(() => {
|
|
97
|
+
this.db
|
|
98
|
+
.prepare(`UPDATE providers SET is_current = 0 WHERE app_type = 'claude'`)
|
|
99
|
+
.run();
|
|
100
|
+
this.db
|
|
101
|
+
.prepare(`UPDATE providers SET is_current = 1 WHERE app_type = 'claude' AND id = ?`)
|
|
102
|
+
.run(profile.id);
|
|
103
|
+
});
|
|
104
|
+
tx();
|
|
105
|
+
const settings = existsSync(SETTINGS_PATH)
|
|
106
|
+
? JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"))
|
|
107
|
+
: {};
|
|
108
|
+
settings.currentProviderClaude = profile.id;
|
|
109
|
+
writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
110
|
+
}
|
|
111
|
+
close() {
|
|
112
|
+
this.db.close();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { DataStore, Profile } from "../types.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DataStore, Profile } from "./interface.js";
|
|
2
|
+
export declare class StandaloneStore implements DataStore {
|
|
3
|
+
list(): Profile[];
|
|
4
|
+
get(name: string): Profile | undefined;
|
|
5
|
+
save(name: string, settingsConfig: Record<string, unknown>): void;
|
|
6
|
+
remove(name: string): boolean;
|
|
7
|
+
getCurrent(): string | undefined;
|
|
8
|
+
setCurrent(name: string): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { homedir } from "os";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
4
|
+
import { t } from "../i18n/index.js";
|
|
5
|
+
const CC_CAST_DIR = join(homedir(), ".cc-cast");
|
|
6
|
+
const CONFIG_PATH = join(CC_CAST_DIR, "config.json");
|
|
7
|
+
function ensureDir() {
|
|
8
|
+
if (!existsSync(CC_CAST_DIR)) {
|
|
9
|
+
mkdirSync(CC_CAST_DIR, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function readConfig() {
|
|
13
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
14
|
+
return { profiles: {} };
|
|
15
|
+
}
|
|
16
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
17
|
+
}
|
|
18
|
+
function writeConfig(config) {
|
|
19
|
+
ensureDir();
|
|
20
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
21
|
+
}
|
|
22
|
+
export class StandaloneStore {
|
|
23
|
+
list() {
|
|
24
|
+
const config = readConfig();
|
|
25
|
+
return Object.entries(config.profiles).map(([name, settingsConfig]) => ({
|
|
26
|
+
id: name,
|
|
27
|
+
name,
|
|
28
|
+
settingsConfig,
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
get(name) {
|
|
32
|
+
const config = readConfig();
|
|
33
|
+
const settingsConfig = config.profiles[name];
|
|
34
|
+
if (!settingsConfig)
|
|
35
|
+
return undefined;
|
|
36
|
+
return { id: name, name, settingsConfig };
|
|
37
|
+
}
|
|
38
|
+
save(name, settingsConfig) {
|
|
39
|
+
const config = readConfig();
|
|
40
|
+
config.profiles[name] = settingsConfig;
|
|
41
|
+
writeConfig(config);
|
|
42
|
+
}
|
|
43
|
+
remove(name) {
|
|
44
|
+
const config = readConfig();
|
|
45
|
+
if (!(name in config.profiles))
|
|
46
|
+
return false;
|
|
47
|
+
delete config.profiles[name];
|
|
48
|
+
if (config.current === name) {
|
|
49
|
+
config.current = undefined;
|
|
50
|
+
}
|
|
51
|
+
writeConfig(config);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
getCurrent() {
|
|
55
|
+
return readConfig().current;
|
|
56
|
+
}
|
|
57
|
+
setCurrent(name) {
|
|
58
|
+
const config = readConfig();
|
|
59
|
+
if (!(name in config.profiles)) {
|
|
60
|
+
throw new Error(t("error.not_found", { name }));
|
|
61
|
+
}
|
|
62
|
+
config.current = name;
|
|
63
|
+
writeConfig(config);
|
|
64
|
+
}
|
|
65
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface Profile {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
settingsConfig: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
export interface DataStore {
|
|
7
|
+
list(): Profile[];
|
|
8
|
+
get(name: string): Profile | undefined;
|
|
9
|
+
save(name: string, settingsConfig: Record<string, unknown>): void;
|
|
10
|
+
remove(name: string): boolean;
|
|
11
|
+
getCurrent(): string | undefined;
|
|
12
|
+
setCurrent(name: string): void;
|
|
13
|
+
}
|
|
14
|
+
export interface RcConfig {
|
|
15
|
+
aliases?: Record<string, string>;
|
|
16
|
+
locale?: "zh" | "en";
|
|
17
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RcConfig } from "./types.js";
|
|
2
|
+
import type { DataStore } from "./store/interface.js";
|
|
3
|
+
export declare function readRc(): RcConfig;
|
|
4
|
+
export declare function writeRc(rc: RcConfig): void;
|
|
5
|
+
export declare function getStore(): DataStore;
|
|
6
|
+
export declare function isCcSwitchGuiRunning(): boolean;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { homedir } from "os";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
4
|
+
import { spawnSync } from "child_process";
|
|
5
|
+
import { StandaloneStore } from "./store/standalone.js";
|
|
6
|
+
import { CcSwitchStore, ccSwitchExists } from "./store/cc-switch.js";
|
|
7
|
+
const CC_CAST_DIR = join(homedir(), ".cc-cast");
|
|
8
|
+
const RC_PATH = join(CC_CAST_DIR, "rc.json");
|
|
9
|
+
export function readRc() {
|
|
10
|
+
if (!existsSync(RC_PATH)) {
|
|
11
|
+
writeRc({});
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(readFileSync(RC_PATH, "utf-8"));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
writeRc({});
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function writeRc(rc) {
|
|
23
|
+
if (!existsSync(CC_CAST_DIR)) {
|
|
24
|
+
mkdirSync(CC_CAST_DIR, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
writeFileSync(RC_PATH, JSON.stringify(rc, null, 2));
|
|
27
|
+
}
|
|
28
|
+
export function getStore() {
|
|
29
|
+
if (ccSwitchExists()) {
|
|
30
|
+
return new CcSwitchStore();
|
|
31
|
+
}
|
|
32
|
+
return new StandaloneStore();
|
|
33
|
+
}
|
|
34
|
+
export function isCcSwitchGuiRunning() {
|
|
35
|
+
try {
|
|
36
|
+
const result = spawnSync("pgrep", ["-f", "cc-switch"], { encoding: "utf-8" });
|
|
37
|
+
return result.status === 0 && (result.stdout?.trim().length ?? 0) > 0;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cc-cast",
|
|
3
|
+
"version": "1.3.5",
|
|
4
|
+
"description": "Claude Code Model Switcher - 快速切换 Claude Code 自定义模型配置",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cc-cast": "dist/index.js",
|
|
9
|
+
"ccm": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"claude",
|
|
17
|
+
"claude-code",
|
|
18
|
+
"model",
|
|
19
|
+
"switcher"
|
|
20
|
+
],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/daylenjeez/cc-cast.git"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"better-sqlite3": "^12.8.0",
|
|
28
|
+
"chalk": "^5.6.2",
|
|
29
|
+
"commander": "^14.0.3",
|
|
30
|
+
"enquirer": "^2.4.1"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
34
|
+
"@types/node": "^25.5.2",
|
|
35
|
+
"typescript": "^6.0.2"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/claude.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { homedir } from "os";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
+
|
|
5
|
+
const SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
|
|
6
|
+
|
|
7
|
+
export function readClaudeSettings(): Record<string, unknown> {
|
|
8
|
+
if (!existsSync(SETTINGS_PATH)) return {};
|
|
9
|
+
return JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function applyProfile(_name: string, settingsConfig: Record<string, unknown>): void {
|
|
13
|
+
const current = readClaudeSettings();
|
|
14
|
+
|
|
15
|
+
// 保留用户级字段,用 profile 的配置覆盖
|
|
16
|
+
const preserved: Record<string, unknown> = {};
|
|
17
|
+
const USER_FIELDS = ["language", "permissions"];
|
|
18
|
+
for (const key of USER_FIELDS) {
|
|
19
|
+
if (key in current) {
|
|
20
|
+
preserved[key] = current[key];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Merge instead of replace: keep any top-level keys from the existing file
|
|
25
|
+
// that the profile does not explicitly set (e.g. common snippets from cc-switch)
|
|
26
|
+
const merged = { ...current, ...preserved, ...settingsConfig };
|
|
27
|
+
writeFileSync(SETTINGS_PATH, JSON.stringify(merged, null, 2));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getSettingsPath(): string {
|
|
31
|
+
return SETTINGS_PATH;
|
|
32
|
+
}
|
package/src/i18n/en.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { TranslationKey } from "./zh.js";
|
|
2
|
+
|
|
3
|
+
const en: Record<TranslationKey, string> = {
|
|
4
|
+
// program
|
|
5
|
+
"program.description": "Claude Code Model Switcher - Quickly switch Claude Code custom model configurations",
|
|
6
|
+
|
|
7
|
+
// common
|
|
8
|
+
"common.not_init": "Not initialized yet. Run: cc-cast init",
|
|
9
|
+
"common.model": "Model",
|
|
10
|
+
"common.model_default": "default",
|
|
11
|
+
"common.source": "Source",
|
|
12
|
+
"common.cancelled": "cancelled",
|
|
13
|
+
|
|
14
|
+
// error
|
|
15
|
+
"error.not_found": 'Configuration "{name}" not found',
|
|
16
|
+
"error.alias_target_missing": 'Alias "{alias}" points to "{target}", but it does not exist',
|
|
17
|
+
"error.invalid_choice": "Invalid choice",
|
|
18
|
+
|
|
19
|
+
// suggest
|
|
20
|
+
"suggest.did_you_mean": "Did you mean: {name}?",
|
|
21
|
+
"suggest.did_you_mean_header": "Did you mean:",
|
|
22
|
+
"suggest.use_list": "Use cc-cast list to see all available configurations",
|
|
23
|
+
|
|
24
|
+
// init
|
|
25
|
+
"init.description": "Initialize cc-cast",
|
|
26
|
+
"init.cc_switch_found":"cc-switch detected. Import configurations from it? (Y/n) ",
|
|
27
|
+
"init.cc_switch_mode": "✓ cc-switch detected — cc-cast will use cc-switch's configuration store directly",
|
|
28
|
+
"init.cc_switch_migrate": "Standalone cc-cast configurations found. Migrate them to cc-switch? (Y/n) ",
|
|
29
|
+
"init.cc_switch_migrate_done": "✓ Migrated {count} configurations to cc-switch",
|
|
30
|
+
"init.done": "✓ Initialized",
|
|
31
|
+
|
|
32
|
+
// list
|
|
33
|
+
"list.description": "List and select configurations",
|
|
34
|
+
"list.empty": "No configurations yet. Use cc-cast save <name> to save current config",
|
|
35
|
+
"list.header": "Available configurations:",
|
|
36
|
+
"list.select": "Select configuration:",
|
|
37
|
+
"list.current_marker": "(current)",
|
|
38
|
+
"list.choose_number": "Enter number to switch (Enter to skip): ",
|
|
39
|
+
|
|
40
|
+
// current
|
|
41
|
+
"current.description": "Show the currently active configuration",
|
|
42
|
+
"current.none": "No active configuration",
|
|
43
|
+
"current.settings_header": "Current settings.json:",
|
|
44
|
+
"current.not_exist": 'Current configuration "{name}" no longer exists',
|
|
45
|
+
"current.header": "Current configuration: {name}",
|
|
46
|
+
|
|
47
|
+
// use
|
|
48
|
+
"use.description": "Switch to a specified configuration",
|
|
49
|
+
"use.done": "✓ Switched to {name}",
|
|
50
|
+
"use.restart": "Restart Claude Code to apply",
|
|
51
|
+
"use.cc_switch_running": "cc-switch GUI is running. To avoid config conflicts, only the active marker was updated. Please switch manually in cc-switch GUI, or quit GUI and try again.",
|
|
52
|
+
|
|
53
|
+
// save
|
|
54
|
+
"save.description": "Save current settings.json as a new configuration",
|
|
55
|
+
"save.overwrite": 'Configuration "{name}" already exists, will overwrite',
|
|
56
|
+
"save.done": '✓ Saved current configuration as "{name}"',
|
|
57
|
+
|
|
58
|
+
// show
|
|
59
|
+
"show.description": "View configuration details (defaults to current)",
|
|
60
|
+
"show.no_current": "No active configuration. Specify a name: cc-cast show <name>",
|
|
61
|
+
"show.all_header": "All configurations:",
|
|
62
|
+
|
|
63
|
+
// remove
|
|
64
|
+
"remove.description": "Delete a configuration",
|
|
65
|
+
"remove.select": "Select configuration to delete:",
|
|
66
|
+
"remove.confirm": 'Delete "{name}"? (y/N) ',
|
|
67
|
+
"remove.done": '✓ Deleted "{name}"',
|
|
68
|
+
|
|
69
|
+
// alias
|
|
70
|
+
"alias.description": "Manage aliases",
|
|
71
|
+
"alias.set_description": "Set alias, e.g.: cc-cast alias set or openrouter-opus4.6",
|
|
72
|
+
"alias.set_done": "✓ Alias set: {short} → {name}",
|
|
73
|
+
"alias.rm_description": "Remove an alias",
|
|
74
|
+
"alias.rm_not_found": 'Alias "{short}" not found',
|
|
75
|
+
"alias.rm_done": '✓ Removed alias "{short}"',
|
|
76
|
+
"alias.list_description": "List all aliases",
|
|
77
|
+
"alias.list_empty": "No aliases yet. Use cc-cast alias set <short> <name> to add one",
|
|
78
|
+
"alias.list_header": "Aliases:",
|
|
79
|
+
|
|
80
|
+
// locale
|
|
81
|
+
"locale.description": "Manage interface language",
|
|
82
|
+
"locale.current": "Current language: {locale}",
|
|
83
|
+
"locale.set_description": "Set language (zh/en)",
|
|
84
|
+
"locale.set_done": "✓ Language set to {locale}",
|
|
85
|
+
"locale.set_invalid": "Unsupported language: {locale}. Available: zh, en",
|
|
86
|
+
"locale.list_description": "List and select language",
|
|
87
|
+
"locale.list_header": "Supported languages:",
|
|
88
|
+
"locale.list_current_marker": "(current)",
|
|
89
|
+
"locale.select": "Select language:",
|
|
90
|
+
"locale.choose_number": "Enter number to switch (Enter to skip): ",
|
|
91
|
+
|
|
92
|
+
// add
|
|
93
|
+
"add.description": "Interactively add a new configuration",
|
|
94
|
+
"add.prompt_name": "Provider name (e.g. OpenRouter): ",
|
|
95
|
+
"add.prompt_base_url": "ANTHROPIC_BASE_URL: ",
|
|
96
|
+
"add.prompt_auth_token": "ANTHROPIC_AUTH_TOKEN: ",
|
|
97
|
+
"add.prompt_model": "ANTHROPIC_MODEL: ",
|
|
98
|
+
"add.prompt_default_opus": "ANTHROPIC_DEFAULT_OPUS_MODEL (press Enter to skip): ",
|
|
99
|
+
"add.prompt_default_sonnet": "ANTHROPIC_DEFAULT_SONNET_MODEL (press Enter to skip): ",
|
|
100
|
+
"add.prompt_default_haiku": "ANTHROPIC_DEFAULT_HAIKU_MODEL (press Enter to skip): ",
|
|
101
|
+
"add.mode_select": "Choose how to add:",
|
|
102
|
+
"add.mode_interactive": "Step by step",
|
|
103
|
+
"add.mode_json": "Write JSON directly",
|
|
104
|
+
"add.mode_choose": "Choose (1/2): ",
|
|
105
|
+
"add.json_template_hint": "Fill in the configuration in editor, save and exit",
|
|
106
|
+
"add.json_parse_error": "JSON parse error, please check format",
|
|
107
|
+
"add.back_hint": "Type < to go back",
|
|
108
|
+
"add.name_required": "Provider name cannot be empty",
|
|
109
|
+
"add.field_required": "{field} cannot be empty",
|
|
110
|
+
"add.already_exists": 'Configuration "{name}" already exists. Overwrite? (y/N) ',
|
|
111
|
+
"add.edit_confirm": "Edit configuration in editor? (y/N) ",
|
|
112
|
+
"add.preview_header": "Configuration preview:",
|
|
113
|
+
"add.done": '✓ Saved configuration "{name}"',
|
|
114
|
+
"add.switch_confirm": "Switch to this configuration now? (Y/n) ",
|
|
115
|
+
"add.cancelled": "Cancelled",
|
|
116
|
+
|
|
117
|
+
// modify
|
|
118
|
+
"modify.description": "Modify an existing configuration",
|
|
119
|
+
"modify.select": "Select configuration to modify:",
|
|
120
|
+
"modify.done": '✓ Updated configuration "{name}"',
|
|
121
|
+
"modify.no_change": "No changes made",
|
|
122
|
+
|
|
123
|
+
// alias conflict
|
|
124
|
+
"alias.is_alias": '"{name}" is an alias for "{target}"',
|
|
125
|
+
"alias.conflict": '"{name}" is both an alias (→ {target}) and a config name. Which one?',
|
|
126
|
+
"alias.conflict_alias": "Alias (→ {target})",
|
|
127
|
+
"alias.conflict_config": "Config {name}",
|
|
128
|
+
"alias.choose_conflict": "Choose (1/2): ",
|
|
129
|
+
"alias.rm_which": "Which one to delete?",
|
|
130
|
+
"alias.rm_alias": "Alias {name}",
|
|
131
|
+
"alias.rm_config": "Config {target}",
|
|
132
|
+
"alias.rm_choose": "Choose (1/2): ",
|
|
133
|
+
|
|
134
|
+
// sync
|
|
135
|
+
"sync.description": "Sync configurations from cc-switch",
|
|
136
|
+
"sync.no_cc_switch": "cc-switch database not detected",
|
|
137
|
+
"sync.empty": "No Claude configurations found in cc-switch",
|
|
138
|
+
"sync.done": "✓ Synced {count} configurations",
|
|
139
|
+
"sync.current": "Active: {name}",
|
|
140
|
+
"sync.no_current": "No active configuration",
|
|
141
|
+
|
|
142
|
+
// clear
|
|
143
|
+
"clear.description": "Clean up cc-cast data files",
|
|
144
|
+
"clear.confirm": "Delete all cc-cast data files? (y/N) ",
|
|
145
|
+
"clear.cancelled": "Cancelled",
|
|
146
|
+
"clear.removed": "✓ Deleted {path}",
|
|
147
|
+
"clear.done": "✓ Cleanup complete",
|
|
148
|
+
|
|
149
|
+
// import
|
|
150
|
+
"import.description": "Import configurations from JSON (file or stdin)",
|
|
151
|
+
"import.paste_hint": "Paste JSON and press Ctrl+D (or Ctrl+Z on Windows) to finish:",
|
|
152
|
+
"import.file_not_found": 'File not found: {file}',
|
|
153
|
+
"import.json_parse_error": "Invalid JSON format",
|
|
154
|
+
"import.invalid_format": "Invalid format: expected object with configuration names as keys",
|
|
155
|
+
"import.done": "✓ Imported {count} configurations",
|
|
156
|
+
|
|
157
|
+
// store errors
|
|
158
|
+
"store.db_not_found": "cc-switch database not found: {path}",
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default en;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import zh, { type TranslationKey } from "./zh.js";
|
|
2
|
+
import en from "./en.js";
|
|
3
|
+
import { readRc } from "../utils.js";
|
|
4
|
+
|
|
5
|
+
export type Locale = "zh" | "en";
|
|
6
|
+
|
|
7
|
+
const locales: Record<Locale, Record<string, string>> = { zh, en };
|
|
8
|
+
|
|
9
|
+
function detectLocale(): Locale {
|
|
10
|
+
// 1. rc.json locale setting takes priority
|
|
11
|
+
const rc = readRc();
|
|
12
|
+
if (rc?.locale && rc.locale in locales) return rc.locale;
|
|
13
|
+
|
|
14
|
+
// 2. Fallback to system LANG/LC_ALL
|
|
15
|
+
const lang = process.env.LC_ALL || process.env.LANG || "";
|
|
16
|
+
if (lang.startsWith("en")) return "en";
|
|
17
|
+
|
|
18
|
+
// 3. Default to English
|
|
19
|
+
return "en";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let currentLocale: Locale | undefined;
|
|
23
|
+
|
|
24
|
+
function getLocale(): Locale {
|
|
25
|
+
if (!currentLocale) currentLocale = detectLocale();
|
|
26
|
+
return currentLocale;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function setLocale(locale: Locale): void {
|
|
30
|
+
currentLocale = locale;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function t(key: TranslationKey, vars?: Record<string, string>): string {
|
|
34
|
+
const locale = getLocale();
|
|
35
|
+
let text = locales[locale][key] ?? locales.en[key] ?? key;
|
|
36
|
+
if (vars) {
|
|
37
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
38
|
+
text = text.replace(new RegExp(`\\{${k}\\}`, "g"), v);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { getLocale, type TranslationKey };
|