@daylenjeez/ccm-switch 1.2.0

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.
@@ -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 type { DataStore, Profile } from "./interface.js";
6
+ import { t } from "../i18n/index.js";
7
+
8
+ const DB_PATH = join(homedir(), ".cc-switch", "cc-switch.db");
9
+ const SETTINGS_PATH = join(homedir(), ".cc-switch", "settings.json");
10
+
11
+ export function ccSwitchExists(): boolean {
12
+ return existsSync(DB_PATH);
13
+ }
14
+
15
+ export class CcSwitchStore implements DataStore {
16
+ private db: Database.Database;
17
+
18
+ constructor() {
19
+ if (!existsSync(DB_PATH)) {
20
+ throw new Error(t("store.db_not_found", { path: DB_PATH }));
21
+ }
22
+ this.db = new Database(DB_PATH);
23
+ }
24
+
25
+ list(): Profile[] {
26
+ const rows = this.db
27
+ .prepare(
28
+ `SELECT id, name, settings_config FROM providers WHERE app_type = 'claude' ORDER BY sort_index`
29
+ )
30
+ .all() as Array<{ id: string; name: string; settings_config: string }>;
31
+
32
+ return rows.map((row) => ({
33
+ id: row.id,
34
+ name: row.name,
35
+ settingsConfig: JSON.parse(row.settings_config),
36
+ }));
37
+ }
38
+
39
+ get(name: string): Profile | undefined {
40
+ const row = this.db
41
+ .prepare(
42
+ `SELECT id, name, settings_config FROM providers WHERE app_type = 'claude' AND name = ?`
43
+ )
44
+ .get(name) as
45
+ | { id: string; name: string; settings_config: string }
46
+ | undefined;
47
+
48
+ if (!row) return undefined;
49
+ return {
50
+ id: row.id,
51
+ name: row.name,
52
+ settingsConfig: JSON.parse(row.settings_config),
53
+ };
54
+ }
55
+
56
+ save(name: string, settingsConfig: Record<string, unknown>): void {
57
+ const existing = this.get(name);
58
+ if (existing) {
59
+ this.db
60
+ .prepare(
61
+ `UPDATE providers SET settings_config = ? WHERE app_type = 'claude' AND name = ?`
62
+ )
63
+ .run(JSON.stringify(settingsConfig), name);
64
+ } else {
65
+ const id = crypto.randomUUID();
66
+ this.db
67
+ .prepare(
68
+ `INSERT INTO providers (id, app_type, name, settings_config, meta, created_at) VALUES (?, 'claude', ?, ?, '{}', ?)`
69
+ )
70
+ .run(id, name, JSON.stringify(settingsConfig), Date.now());
71
+ }
72
+ }
73
+
74
+ remove(name: string): boolean {
75
+ const result = this.db
76
+ .prepare(
77
+ `DELETE FROM providers WHERE app_type = 'claude' AND name = ?`
78
+ )
79
+ .run(name);
80
+ return result.changes > 0;
81
+ }
82
+
83
+ getCurrent(): string | undefined {
84
+ if (!existsSync(SETTINGS_PATH)) return undefined;
85
+ try {
86
+ const settings = JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
87
+ const currentId = settings.currentProviderClaude;
88
+ if (!currentId) return undefined;
89
+ const row = this.db
90
+ .prepare(
91
+ `SELECT name FROM providers WHERE app_type = 'claude' AND id = ?`
92
+ )
93
+ .get(currentId) as { name: string } | undefined;
94
+ return row?.name;
95
+ } catch {
96
+ return undefined;
97
+ }
98
+ }
99
+
100
+ setCurrent(name: string): void {
101
+ const profile = this.get(name);
102
+ if (!profile) throw new Error(t("error.not_found", { name }));
103
+
104
+ if (existsSync(SETTINGS_PATH)) {
105
+ const settings = JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
106
+ settings.currentProviderClaude = profile.id;
107
+ writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
108
+ }
109
+ }
110
+
111
+ close(): void {
112
+ this.db.close();
113
+ }
114
+ }
@@ -0,0 +1 @@
1
+ export type { DataStore, Profile } from "../types.js";
@@ -0,0 +1,79 @@
1
+ import { homedir } from "os";
2
+ import { join } from "path";
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
+ import type { DataStore, Profile } from "./interface.js";
5
+ import { t } from "../i18n/index.js";
6
+
7
+ const CCM_DIR = join(homedir(), ".ccm");
8
+ const CONFIG_PATH = join(CCM_DIR, "config.json");
9
+
10
+ interface StandaloneConfig {
11
+ current?: string;
12
+ profiles: Record<string, Record<string, unknown>>;
13
+ }
14
+
15
+ function ensureDir(): void {
16
+ if (!existsSync(CCM_DIR)) {
17
+ mkdirSync(CCM_DIR, { recursive: true });
18
+ }
19
+ }
20
+
21
+ function readConfig(): StandaloneConfig {
22
+ if (!existsSync(CONFIG_PATH)) {
23
+ return { profiles: {} };
24
+ }
25
+ return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
26
+ }
27
+
28
+ function writeConfig(config: StandaloneConfig): void {
29
+ ensureDir();
30
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
31
+ }
32
+
33
+ export class StandaloneStore implements DataStore {
34
+ list(): Profile[] {
35
+ const config = readConfig();
36
+ return Object.entries(config.profiles).map(([name, settingsConfig]) => ({
37
+ id: name,
38
+ name,
39
+ settingsConfig,
40
+ }));
41
+ }
42
+
43
+ get(name: string): Profile | undefined {
44
+ const config = readConfig();
45
+ const settingsConfig = config.profiles[name];
46
+ if (!settingsConfig) return undefined;
47
+ return { id: name, name, settingsConfig };
48
+ }
49
+
50
+ save(name: string, settingsConfig: Record<string, unknown>): void {
51
+ const config = readConfig();
52
+ config.profiles[name] = settingsConfig;
53
+ writeConfig(config);
54
+ }
55
+
56
+ remove(name: string): boolean {
57
+ const config = readConfig();
58
+ if (!(name in config.profiles)) return false;
59
+ delete config.profiles[name];
60
+ if (config.current === name) {
61
+ config.current = undefined;
62
+ }
63
+ writeConfig(config);
64
+ return true;
65
+ }
66
+
67
+ getCurrent(): string | undefined {
68
+ return readConfig().current;
69
+ }
70
+
71
+ setCurrent(name: string): void {
72
+ const config = readConfig();
73
+ if (!(name in config.profiles)) {
74
+ throw new Error(t("error.not_found", { name }));
75
+ }
76
+ config.current = name;
77
+ writeConfig(config);
78
+ }
79
+ }
package/src/types.ts ADDED
@@ -0,0 +1,20 @@
1
+ export interface Profile {
2
+ id: string;
3
+ name: string;
4
+ settingsConfig: Record<string, unknown>;
5
+ }
6
+
7
+ export interface DataStore {
8
+ list(): Profile[];
9
+ get(name: string): Profile | undefined;
10
+ save(name: string, settingsConfig: Record<string, unknown>): void;
11
+ remove(name: string): boolean;
12
+ getCurrent(): string | undefined;
13
+ setCurrent(name: string): void;
14
+ }
15
+
16
+ export interface RcConfig {
17
+ mode: "cc-switch" | "standalone";
18
+ aliases?: Record<string, string>;
19
+ locale?: "zh" | "en";
20
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { homedir } from "os";
2
+ import { join } from "path";
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
+ import type { RcConfig } from "./types.js";
5
+ import type { DataStore } from "./store/interface.js";
6
+ import { CcSwitchStore } from "./store/cc-switch.js";
7
+ import { StandaloneStore } from "./store/standalone.js";
8
+
9
+ const CCM_DIR = join(homedir(), ".ccm");
10
+ const RC_PATH = join(CCM_DIR, "rc.json");
11
+
12
+ export function readRc(): RcConfig | undefined {
13
+ if (!existsSync(RC_PATH)) return undefined;
14
+ try {
15
+ return JSON.parse(readFileSync(RC_PATH, "utf-8"));
16
+ } catch {
17
+ return undefined;
18
+ }
19
+ }
20
+
21
+ export function writeRc(rc: RcConfig): void {
22
+ if (!existsSync(CCM_DIR)) {
23
+ mkdirSync(CCM_DIR, { recursive: true });
24
+ }
25
+ writeFileSync(RC_PATH, JSON.stringify(rc, null, 2));
26
+ }
27
+
28
+ export function getStore(): DataStore | null {
29
+ const rc = readRc();
30
+ if (!rc) return null;
31
+
32
+ if (rc.mode === "cc-switch") {
33
+ return new CcSwitchStore();
34
+ } else {
35
+ return new StandaloneStore();
36
+ }
37
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }