multi-ccp 0.1.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.
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/program.d.ts +2 -0
- package/dist/cli/program.js +196 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/core/ccr.d.ts +5 -0
- package/dist/core/ccr.js +7 -0
- package/dist/core/ccr.js.map +1 -0
- package/dist/core/errors.d.ts +4 -0
- package/dist/core/errors.js +18 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/launcher.d.ts +19 -0
- package/dist/core/launcher.js +80 -0
- package/dist/core/launcher.js.map +1 -0
- package/dist/core/paths.d.ts +13 -0
- package/dist/core/paths.js +31 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/profiles.d.ts +17 -0
- package/dist/core/profiles.js +141 -0
- package/dist/core/profiles.js.map +1 -0
- package/dist/core/settings.d.ts +10 -0
- package/dist/core/settings.js +44 -0
- package/dist/core/settings.js.map +1 -0
- package/dist/core/types.d.ts +34 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/platform/editor.d.ts +2 -0
- package/dist/platform/editor.js +19 -0
- package/dist/platform/editor.js.map +1 -0
- package/package.json +45 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial TypeScript npm package scaffold.
|
|
6
|
+
- Added cross-platform profile core for API and Claude login profiles.
|
|
7
|
+
- Added CLI commands for `list`, `add`, `add-login`, `remove`, `status`, `path`, `edit`, and `start`.
|
|
8
|
+
- Added Vitest coverage for core profile behavior.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# multi-ccp
|
|
2
|
+
|
|
3
|
+
Cross-platform Claude Code profile manager for multiple API profiles, separate Claude login accounts, and future Claude Code Router workflows.
|
|
4
|
+
|
|
5
|
+
The command name is `ccp`.
|
|
6
|
+
|
|
7
|
+
## Goals
|
|
8
|
+
|
|
9
|
+
- Manage multiple Claude Code config directories through `CLAUDE_CONFIG_DIR`.
|
|
10
|
+
- Support API profiles with `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, and model env.
|
|
11
|
+
- Support login profiles that keep separate Claude Code account login state without storing account passwords.
|
|
12
|
+
- Run on Windows, macOS, and Linux from one npm package.
|
|
13
|
+
- Keep the core profile logic reusable for a future framework-free local Web UI.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g multi-ccp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For local development:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install
|
|
25
|
+
npm run build
|
|
26
|
+
npm run dev -- help
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ccp list
|
|
33
|
+
ccp add <profile>
|
|
34
|
+
ccp add-login <profile>
|
|
35
|
+
ccp remove <profile>
|
|
36
|
+
ccp status <profile|main>
|
|
37
|
+
ccp start <profile> [claude args...]
|
|
38
|
+
ccp path <profile|main>
|
|
39
|
+
ccp edit <profile>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Profile Types
|
|
43
|
+
|
|
44
|
+
### API profile
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ccp add kimi
|
|
48
|
+
ccp start kimi
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Creates `~/.claude-profiles/kimi/settings.json` with Anthropic-compatible API environment variables.
|
|
52
|
+
|
|
53
|
+
### Login profile
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
ccp add-login work
|
|
57
|
+
ccp start work
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Creates a profile without `ANTHROPIC_BASE_URL` or `ANTHROPIC_AUTH_TOKEN`. Claude Code stores that account's login state under the profile config directory when you complete the normal Claude Code login flow.
|
|
61
|
+
|
|
62
|
+
Create another login account with:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ccp add-login personal
|
|
66
|
+
ccp start personal
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Each profile gets its own `CLAUDE_CONFIG_DIR`.
|
|
70
|
+
|
|
71
|
+
## Current Scope
|
|
72
|
+
|
|
73
|
+
This TypeScript npm version currently implements the cross-platform profile manager MVP:
|
|
74
|
+
|
|
75
|
+
- `list`
|
|
76
|
+
- `add`
|
|
77
|
+
- `add-login`
|
|
78
|
+
- `remove`
|
|
79
|
+
- `status`
|
|
80
|
+
- `path`
|
|
81
|
+
- `edit`
|
|
82
|
+
- `start`
|
|
83
|
+
|
|
84
|
+
Planned migrations from the legacy PowerShell tool:
|
|
85
|
+
|
|
86
|
+
- `add-ccr`
|
|
87
|
+
- `ccp ccr status|install|start|stop|restart|ui|model`
|
|
88
|
+
- `sync-session`
|
|
89
|
+
- `ccp ui`
|
|
90
|
+
|
|
91
|
+
The future Web UI should use vanilla browser APIs or Web Components. Vue and React are intentionally out of scope for the runtime UI. Icon libraries such as Lucide are acceptable.
|
|
92
|
+
|
|
93
|
+
## Development
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npm install
|
|
97
|
+
npm run typecheck
|
|
98
|
+
npm test
|
|
99
|
+
npm run build
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createProgram } from "./program.js";
|
|
3
|
+
import { CcpError } from "../core/errors.js";
|
|
4
|
+
try {
|
|
5
|
+
await createProgram().parseAsync(process.argv);
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
if (error instanceof CcpError) {
|
|
9
|
+
console.error(`ccp: ${error.message}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,IAAI,CAAC;IACH,MAAM,aAAa,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { confirm, input, password } from "@inquirer/prompts";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { CcpError } from "../core/errors.js";
|
|
4
|
+
import { launchClaude } from "../core/launcher.js";
|
|
5
|
+
import { resolveConfigDir } from "../core/profiles.js";
|
|
6
|
+
import { createApiProfile, createLoginProfile, listProfiles, removeProfile, summarizeProfile } from "../core/profiles.js";
|
|
7
|
+
import { getSettingsPath } from "../core/settings.js";
|
|
8
|
+
import { getCcrStatusPlaceholder } from "../core/ccr.js";
|
|
9
|
+
import { openEditor } from "../platform/editor.js";
|
|
10
|
+
function printProfile(profile) {
|
|
11
|
+
if (profile.model) {
|
|
12
|
+
console.log(`${profile.name}\t${profile.model}\t${profile.baseUrl}`);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
console.log(`${profile.name}\t${profile.baseUrl}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function showStatus(name) {
|
|
19
|
+
const config = await resolveConfigDir(name, { allowMain: true });
|
|
20
|
+
const profile = await summarizeProfile(config.name, config.dir);
|
|
21
|
+
console.log(`Profile: ${profile.name}`);
|
|
22
|
+
console.log(`Path: ${profile.dir}`);
|
|
23
|
+
console.log(`Settings: ${profile.settingsPath}`);
|
|
24
|
+
if (profile.meta) {
|
|
25
|
+
console.log(`Type: ${profile.meta.type}`);
|
|
26
|
+
if (profile.meta.endpoint)
|
|
27
|
+
console.log(`Endpoint: ${profile.meta.endpoint}`);
|
|
28
|
+
if (profile.meta.autoStart !== undefined)
|
|
29
|
+
console.log(`AutoStart: ${profile.meta.autoStart}`);
|
|
30
|
+
if (profile.meta.ccrPreset)
|
|
31
|
+
console.log(`CCR Preset: ${profile.meta.ccrPreset}`);
|
|
32
|
+
if (profile.meta.ccrRoute)
|
|
33
|
+
console.log(`CCR Route: ${profile.meta.ccrRoute}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log(`Type: ${profile.type}`);
|
|
37
|
+
}
|
|
38
|
+
if (profile.type === "login") {
|
|
39
|
+
console.log("Auth: Claude Code login");
|
|
40
|
+
console.log("Env: missing");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!profile.baseUrl && !profile.model && profile.tokenStatus === "missing") {
|
|
44
|
+
console.log("Env: missing");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log(`Base: ${profile.baseUrl}`);
|
|
48
|
+
if (profile.type === "ccr" && !profile.model) {
|
|
49
|
+
console.log("Model: (routed by CCR preset)");
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log(`Model: ${profile.model}`);
|
|
53
|
+
}
|
|
54
|
+
console.log(`Token: ${profile.tokenStatus}`);
|
|
55
|
+
}
|
|
56
|
+
export function createProgram() {
|
|
57
|
+
const program = new Command();
|
|
58
|
+
program
|
|
59
|
+
.name("ccp")
|
|
60
|
+
.description("Claude Code profile manager")
|
|
61
|
+
.version("0.1.0");
|
|
62
|
+
program
|
|
63
|
+
.command("list")
|
|
64
|
+
.description("List Claude Code profiles")
|
|
65
|
+
.action(async () => {
|
|
66
|
+
const profiles = await listProfiles();
|
|
67
|
+
if (profiles.length === 0) {
|
|
68
|
+
console.log("No profiles found.");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
profiles.forEach(printProfile);
|
|
72
|
+
});
|
|
73
|
+
program
|
|
74
|
+
.command("add")
|
|
75
|
+
.argument("<profile>")
|
|
76
|
+
.description("Create an API profile with base URL, token, and model")
|
|
77
|
+
.action(async (profile) => {
|
|
78
|
+
console.log(`Create Claude API profile: ${profile}`);
|
|
79
|
+
const baseUrl = await input({ message: "ANTHROPIC_BASE_URL", required: true });
|
|
80
|
+
const token = await password({ message: "ANTHROPIC_AUTH_TOKEN (hidden, Enter to leave placeholder)", mask: "*" });
|
|
81
|
+
const model = await input({ message: "Model", required: true });
|
|
82
|
+
const ok = await confirm({ message: "Create this profile?", default: true });
|
|
83
|
+
if (!ok) {
|
|
84
|
+
console.log("Cancelled.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const created = await createApiProfile({ name: profile, baseUrl, token, model });
|
|
88
|
+
console.log(`Created profile '${created.name}'.`);
|
|
89
|
+
console.log(`Run: ccp start ${created.name}`);
|
|
90
|
+
});
|
|
91
|
+
program
|
|
92
|
+
.command("add-login")
|
|
93
|
+
.argument("<profile>")
|
|
94
|
+
.description("Create a login profile that stores a separate Claude Code account login state")
|
|
95
|
+
.action(async (profile) => {
|
|
96
|
+
console.log(`Create Claude login profile: ${profile}`);
|
|
97
|
+
console.log("This profile will not set ANTHROPIC_BASE_URL or ANTHROPIC_AUTH_TOKEN.");
|
|
98
|
+
const ok = await confirm({ message: "Create this login profile?", default: true });
|
|
99
|
+
if (!ok) {
|
|
100
|
+
console.log("Cancelled.");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const created = await createLoginProfile({ name: profile });
|
|
104
|
+
console.log(`Created Claude login profile '${created.name}'.`);
|
|
105
|
+
console.log(`Run: ccp start ${created.name}`);
|
|
106
|
+
console.log("Then complete the Claude Code login flow for this account.");
|
|
107
|
+
});
|
|
108
|
+
program
|
|
109
|
+
.command("remove")
|
|
110
|
+
.argument("<profile>")
|
|
111
|
+
.description("Delete a profile")
|
|
112
|
+
.action(async (profile) => {
|
|
113
|
+
const config = await resolveConfigDir(profile, { allowMain: false });
|
|
114
|
+
console.log(`This will permanently delete Claude profile '${profile}':`);
|
|
115
|
+
console.log(config.dir);
|
|
116
|
+
console.log("This cannot be undone. No backup will be created.");
|
|
117
|
+
const typed = await input({ message: `Type '${profile}' to delete` });
|
|
118
|
+
if (typed !== profile) {
|
|
119
|
+
console.log("Cancelled.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
await removeProfile(profile);
|
|
123
|
+
console.log(`Deleted profile '${profile}'.`);
|
|
124
|
+
});
|
|
125
|
+
program
|
|
126
|
+
.command("status")
|
|
127
|
+
.argument("<profile>")
|
|
128
|
+
.description("Show profile status")
|
|
129
|
+
.action(showStatus);
|
|
130
|
+
program
|
|
131
|
+
.command("path")
|
|
132
|
+
.argument("<profile>")
|
|
133
|
+
.description("Print profile config path")
|
|
134
|
+
.action(async (profile) => {
|
|
135
|
+
const config = await resolveConfigDir(profile, { allowMain: true });
|
|
136
|
+
console.log(config.dir);
|
|
137
|
+
});
|
|
138
|
+
program
|
|
139
|
+
.command("edit")
|
|
140
|
+
.argument("<profile>")
|
|
141
|
+
.description("Open a profile settings file in your editor")
|
|
142
|
+
.action(async (profile) => {
|
|
143
|
+
const config = await resolveConfigDir(profile, { allowMain: false });
|
|
144
|
+
const code = await openEditor(getSettingsPath(config.dir));
|
|
145
|
+
process.exitCode = code;
|
|
146
|
+
});
|
|
147
|
+
program
|
|
148
|
+
.command("start")
|
|
149
|
+
.argument("<profile>")
|
|
150
|
+
.allowUnknownOption(true)
|
|
151
|
+
.argument("[claudeArgs...]", "Arguments passed through to Claude Code")
|
|
152
|
+
.description("Start Claude Code with a profile")
|
|
153
|
+
.action(async (profile, claudeArgs) => {
|
|
154
|
+
const code = await launchClaude({
|
|
155
|
+
name: profile,
|
|
156
|
+
claudeArgs,
|
|
157
|
+
confirmMainConfigCwd: async ({ currentDir, fallbackDir, profileName }) => {
|
|
158
|
+
console.log("");
|
|
159
|
+
console.log("Current directory is the Claude main config location:");
|
|
160
|
+
console.log(` ${currentDir}`);
|
|
161
|
+
console.log("");
|
|
162
|
+
console.log(`Claude Code may read project settings that override profile '${profileName}'.`);
|
|
163
|
+
console.log("");
|
|
164
|
+
console.log("If you continue, ccp will switch to this isolated workdir:");
|
|
165
|
+
console.log(` ${fallbackDir}`);
|
|
166
|
+
console.log("");
|
|
167
|
+
return confirm({ message: "Continue and switch workdir?", default: false });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
process.exitCode = code;
|
|
171
|
+
});
|
|
172
|
+
program
|
|
173
|
+
.command("ccr")
|
|
174
|
+
.argument("[action]")
|
|
175
|
+
.description("Manage Claude Code Router integration")
|
|
176
|
+
.action(() => {
|
|
177
|
+
const status = getCcrStatusPlaceholder();
|
|
178
|
+
console.log(status.message);
|
|
179
|
+
process.exitCode = 1;
|
|
180
|
+
});
|
|
181
|
+
program
|
|
182
|
+
.command("sync-session")
|
|
183
|
+
.allowUnknownOption(true)
|
|
184
|
+
.description("Sync Claude Code sessions between profiles")
|
|
185
|
+
.action(() => {
|
|
186
|
+
throw new CcpError("sync-session is planned for a later TypeScript CLI release. Use the legacy PowerShell ccp for now.");
|
|
187
|
+
});
|
|
188
|
+
program
|
|
189
|
+
.command("ui")
|
|
190
|
+
.description("Start the local web UI")
|
|
191
|
+
.action(() => {
|
|
192
|
+
throw new CcpError("Web UI is planned. It will use vanilla web components and no Vue/React runtime.");
|
|
193
|
+
});
|
|
194
|
+
return program;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=program.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program.js","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"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"}
|
package/dist/core/ccr.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ccr.js","sourceRoot":"","sources":["../../src/core/ccr.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,uBAAuB;IACrC,OAAO;QACL,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,uHAAuH;KACjI,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class CcpError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "CcpError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export function assertProfileName(name) {
|
|
8
|
+
if (!name.trim()) {
|
|
9
|
+
throw new CcpError("Missing profile name.");
|
|
10
|
+
}
|
|
11
|
+
if (name.toLowerCase() === "main") {
|
|
12
|
+
throw new CcpError("'main' is reserved and cannot be used as a profile name.");
|
|
13
|
+
}
|
|
14
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(name)) {
|
|
15
|
+
throw new CcpError(`Invalid profile name '${name}'. Use letters, numbers, underscore, or hyphen, and start with a letter or number.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,QAAQ,CAAC,0DAA0D,CAAC,CAAC;IACjF,CAAC;IAED,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,QAAQ,CAChB,yBAAyB,IAAI,oFAAoF,CAClH,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type PathContext } from "./paths.js";
|
|
2
|
+
export interface LaunchOptions {
|
|
3
|
+
name: string;
|
|
4
|
+
claudeArgs?: string[];
|
|
5
|
+
context?: PathContext;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
confirmMainConfigCwd?: (details: {
|
|
8
|
+
currentDir: string;
|
|
9
|
+
fallbackDir: string;
|
|
10
|
+
profileName: string;
|
|
11
|
+
}) => Promise<boolean>;
|
|
12
|
+
}
|
|
13
|
+
export declare function prepareClaudeLaunch(options: LaunchOptions): Promise<{
|
|
14
|
+
command: string;
|
|
15
|
+
args: string[];
|
|
16
|
+
cwd: string;
|
|
17
|
+
env: NodeJS.ProcessEnv;
|
|
18
|
+
}>;
|
|
19
|
+
export declare function launchClaude(options: LaunchOptions): Promise<number>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { CcpError } from "./errors.js";
|
|
5
|
+
import { getHomeDir, getHomeWorkDir, getMainClaudeDir } from "./paths.js";
|
|
6
|
+
import { resolveConfigDir } from "./profiles.js";
|
|
7
|
+
import { readMeta } from "./settings.js";
|
|
8
|
+
const MODEL_ENV_NAMES = [
|
|
9
|
+
"ANTHROPIC_MODEL",
|
|
10
|
+
"ANTHROPIC_SMALL_FAST_MODEL",
|
|
11
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
12
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
13
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL_NAME",
|
|
14
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
15
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL_NAME"
|
|
16
|
+
];
|
|
17
|
+
function normalizePath(value) {
|
|
18
|
+
return path.resolve(value).replace(/[\\/]+$/, "").toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
function buildLaunchEnv(profileDir, profileName) {
|
|
21
|
+
const env = { ...process.env };
|
|
22
|
+
for (const name of MODEL_ENV_NAMES) {
|
|
23
|
+
delete env[name];
|
|
24
|
+
}
|
|
25
|
+
env.CLAUDE_CONFIG_DIR = profileDir;
|
|
26
|
+
env.CCP_PROFILE = profileName;
|
|
27
|
+
return env;
|
|
28
|
+
}
|
|
29
|
+
async function resolveLaunchCwd(options) {
|
|
30
|
+
const context = options.context ?? {};
|
|
31
|
+
const currentDir = options.cwd ?? context.cwd ?? process.cwd();
|
|
32
|
+
const homeDir = getHomeDir(context);
|
|
33
|
+
const mainDir = getMainClaudeDir(context);
|
|
34
|
+
if (normalizePath(currentDir) !== normalizePath(homeDir) && normalizePath(currentDir) !== normalizePath(mainDir)) {
|
|
35
|
+
return currentDir;
|
|
36
|
+
}
|
|
37
|
+
const fallbackDir = getHomeWorkDir(context);
|
|
38
|
+
const ok = await options.confirmMainConfigCwd?.({ currentDir, fallbackDir, profileName: options.name });
|
|
39
|
+
if (!ok) {
|
|
40
|
+
throw new CcpError("Cancelled.");
|
|
41
|
+
}
|
|
42
|
+
await mkdir(fallbackDir, { recursive: true });
|
|
43
|
+
return fallbackDir;
|
|
44
|
+
}
|
|
45
|
+
export async function prepareClaudeLaunch(options) {
|
|
46
|
+
const config = await resolveConfigDir(options.name, { allowMain: false, context: options.context });
|
|
47
|
+
const meta = await readMeta(config.dir);
|
|
48
|
+
if (meta?.type === "ccr") {
|
|
49
|
+
throw new CcpError("CCR profiles are not implemented in the TypeScript CLI yet. Use the legacy ccp.ps1 for this profile for now.");
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
command: "claude",
|
|
53
|
+
args: options.claudeArgs ?? [],
|
|
54
|
+
cwd: await resolveLaunchCwd(options),
|
|
55
|
+
env: buildLaunchEnv(config.dir, options.name)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export async function launchClaude(options) {
|
|
59
|
+
const launch = await prepareClaudeLaunch(options);
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const child = spawn(launch.command, launch.args, {
|
|
62
|
+
cwd: launch.cwd,
|
|
63
|
+
env: launch.env,
|
|
64
|
+
stdio: "inherit",
|
|
65
|
+
shell: process.platform === "win32"
|
|
66
|
+
});
|
|
67
|
+
child.on("error", (error) => {
|
|
68
|
+
reject(new CcpError(`Failed to start Claude Code: ${error.message}`));
|
|
69
|
+
});
|
|
70
|
+
child.on("exit", (code, signal) => {
|
|
71
|
+
if (signal) {
|
|
72
|
+
resolve(1);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
resolve(code ?? 0);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=launcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/core/launcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface PathContext {
|
|
2
|
+
homeDir?: string;
|
|
3
|
+
cwd?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function getHomeDir(context?: PathContext): string;
|
|
6
|
+
export declare function getProfilesRoot(context?: PathContext): string;
|
|
7
|
+
export declare function getMainClaudeDir(context?: PathContext): string;
|
|
8
|
+
export declare function getHomeWorkDir(context?: PathContext): string;
|
|
9
|
+
export declare function getClaudeCodeRouterDir(context?: PathContext): string;
|
|
10
|
+
export declare function getClaudeCodeRouterConfigPath(context?: PathContext): string;
|
|
11
|
+
export declare function getProjectKey(projectPath?: string): string;
|
|
12
|
+
export declare function getProjectDir(configDir: string, projectKey: string): string;
|
|
13
|
+
export declare function isWindows(): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { homedir, platform } from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function getHomeDir(context = {}) {
|
|
4
|
+
return context.homeDir ?? homedir();
|
|
5
|
+
}
|
|
6
|
+
export function getProfilesRoot(context = {}) {
|
|
7
|
+
return path.join(getHomeDir(context), ".claude-profiles");
|
|
8
|
+
}
|
|
9
|
+
export function getMainClaudeDir(context = {}) {
|
|
10
|
+
return path.join(getHomeDir(context), ".claude");
|
|
11
|
+
}
|
|
12
|
+
export function getHomeWorkDir(context = {}) {
|
|
13
|
+
return path.join(getProfilesRoot(context), ".workdirs", "home");
|
|
14
|
+
}
|
|
15
|
+
export function getClaudeCodeRouterDir(context = {}) {
|
|
16
|
+
return path.join(getHomeDir(context), ".claude-code-router");
|
|
17
|
+
}
|
|
18
|
+
export function getClaudeCodeRouterConfigPath(context = {}) {
|
|
19
|
+
return path.join(getClaudeCodeRouterDir(context), "config.json");
|
|
20
|
+
}
|
|
21
|
+
export function getProjectKey(projectPath = process.cwd()) {
|
|
22
|
+
const resolved = path.resolve(projectPath).replace(/[\\/]+$/, "");
|
|
23
|
+
return resolved.replace(/:/g, "-").replace(/[\\/]/g, "-");
|
|
24
|
+
}
|
|
25
|
+
export function getProjectDir(configDir, projectKey) {
|
|
26
|
+
return path.join(configDir, "projects", projectKey);
|
|
27
|
+
}
|
|
28
|
+
export function isWindows() {
|
|
29
|
+
return platform() === "win32";
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAO7B,MAAM,UAAU,UAAU,CAAC,UAAuB,EAAE;IAClD,OAAO,OAAO,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAuB,EAAE;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAuB,EAAE;IACxD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAuB,EAAE;IACtD,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAuB,EAAE;IAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,UAAuB,EAAE;IACrE,OAAO,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE;IACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,UAAkB;IACjE,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,QAAQ,EAAE,KAAK,OAAO,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type PathContext } from "./paths.js";
|
|
2
|
+
import type { ClaudeSettings, CreateApiProfileInput, CreateLoginProfileInput, ProfileMeta, ProfileSummary, ProfileType } from "./types.js";
|
|
3
|
+
export declare function getProfileDir(name: string, context?: PathContext): string;
|
|
4
|
+
export declare function resolveConfigDir(name: string, options?: {
|
|
5
|
+
allowMain?: boolean;
|
|
6
|
+
context?: PathContext;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
name: string;
|
|
9
|
+
dir: string;
|
|
10
|
+
isMain: boolean;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function inferProfileType(settings?: ClaudeSettings, meta?: ProfileMeta): ProfileType;
|
|
13
|
+
export declare function summarizeProfile(name: string, dir: string): Promise<ProfileSummary>;
|
|
14
|
+
export declare function listProfiles(context?: PathContext): Promise<ProfileSummary[]>;
|
|
15
|
+
export declare function createApiProfile(input: CreateApiProfileInput, context?: PathContext): Promise<ProfileSummary>;
|
|
16
|
+
export declare function createLoginProfile(input: CreateLoginProfileInput, context?: PathContext): Promise<ProfileSummary>;
|
|
17
|
+
export declare function removeProfile(name: string, context?: PathContext): Promise<string>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { access, mkdir, readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { assertProfileName, CcpError } from "./errors.js";
|
|
4
|
+
import { getMainClaudeDir, getProfilesRoot } from "./paths.js";
|
|
5
|
+
import { getSettingsPath, readMeta, readSettings, removeProfileDir, writeMeta, writeSettings } from "./settings.js";
|
|
6
|
+
async function exists(target) {
|
|
7
|
+
try {
|
|
8
|
+
await access(target);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function getProfileDir(name, context = {}) {
|
|
16
|
+
return path.join(getProfilesRoot(context), name);
|
|
17
|
+
}
|
|
18
|
+
export async function resolveConfigDir(name, options = {}) {
|
|
19
|
+
const allowMain = options.allowMain ?? true;
|
|
20
|
+
const context = options.context ?? {};
|
|
21
|
+
if (!name.trim()) {
|
|
22
|
+
throw new CcpError("Missing profile name. Run 'ccp list' to see available profiles.");
|
|
23
|
+
}
|
|
24
|
+
if (allowMain && name.toLowerCase() === "main") {
|
|
25
|
+
const dir = getMainClaudeDir(context);
|
|
26
|
+
if (!(await exists(dir))) {
|
|
27
|
+
throw new CcpError(`Main Claude config directory does not exist: ${dir}`);
|
|
28
|
+
}
|
|
29
|
+
return { name: "main", dir, isMain: true };
|
|
30
|
+
}
|
|
31
|
+
const dir = getProfileDir(name, context);
|
|
32
|
+
if (!(await exists(dir))) {
|
|
33
|
+
throw new CcpError(`Profile '${name}' does not exist: ${dir}`);
|
|
34
|
+
}
|
|
35
|
+
return { name, dir, isMain: false };
|
|
36
|
+
}
|
|
37
|
+
export function inferProfileType(settings, meta) {
|
|
38
|
+
if (meta?.type) {
|
|
39
|
+
return meta.type;
|
|
40
|
+
}
|
|
41
|
+
if (settings?.env?.ANTHROPIC_BASE_URL || settings?.env?.ANTHROPIC_AUTH_TOKEN) {
|
|
42
|
+
return "api";
|
|
43
|
+
}
|
|
44
|
+
return "unknown";
|
|
45
|
+
}
|
|
46
|
+
export async function summarizeProfile(name, dir) {
|
|
47
|
+
const settings = await readSettings(dir);
|
|
48
|
+
const meta = await readMeta(dir);
|
|
49
|
+
const type = inferProfileType(settings, meta);
|
|
50
|
+
const env = settings?.env ?? {};
|
|
51
|
+
const token = env.ANTHROPIC_AUTH_TOKEN ?? "";
|
|
52
|
+
let model = env.ANTHROPIC_MODEL ?? "";
|
|
53
|
+
const baseUrl = env.ANTHROPIC_BASE_URL ?? "";
|
|
54
|
+
if (type === "ccr" && meta?.ccrRoute) {
|
|
55
|
+
model = `ccr:${meta.ccrRoute}`;
|
|
56
|
+
}
|
|
57
|
+
else if (type === "ccr") {
|
|
58
|
+
model = "ccr";
|
|
59
|
+
}
|
|
60
|
+
else if (type === "login") {
|
|
61
|
+
model = "login";
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
name,
|
|
65
|
+
dir,
|
|
66
|
+
type,
|
|
67
|
+
baseUrl,
|
|
68
|
+
model,
|
|
69
|
+
tokenStatus: token && token !== "REPLACE_WITH_FULL_TOKEN" ? "set" : "missing",
|
|
70
|
+
settingsPath: getSettingsPath(dir),
|
|
71
|
+
meta
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export async function listProfiles(context = {}) {
|
|
75
|
+
const root = getProfilesRoot(context);
|
|
76
|
+
if (!(await exists(root))) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
80
|
+
const profiles = [];
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
if (!entry.isDirectory()) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const dir = path.join(root, entry.name);
|
|
86
|
+
if (await exists(getSettingsPath(dir))) {
|
|
87
|
+
profiles.push(await summarizeProfile(entry.name, dir));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return profiles.sort((a, b) => a.name.localeCompare(b.name));
|
|
91
|
+
}
|
|
92
|
+
async function assertNewProfile(name, context) {
|
|
93
|
+
assertProfileName(name);
|
|
94
|
+
const profileDir = getProfileDir(name, context);
|
|
95
|
+
if (await exists(profileDir)) {
|
|
96
|
+
throw new CcpError(`Profile '${name}' already exists: ${profileDir}`);
|
|
97
|
+
}
|
|
98
|
+
return profileDir;
|
|
99
|
+
}
|
|
100
|
+
export async function createApiProfile(input, context = {}) {
|
|
101
|
+
const profileDir = await assertNewProfile(input.name, context);
|
|
102
|
+
if (!input.baseUrl.trim()) {
|
|
103
|
+
throw new CcpError("ANTHROPIC_BASE_URL is required.");
|
|
104
|
+
}
|
|
105
|
+
if (!input.model.trim()) {
|
|
106
|
+
throw new CcpError("Model is required.");
|
|
107
|
+
}
|
|
108
|
+
await mkdir(profileDir, { recursive: true });
|
|
109
|
+
await writeSettings(profileDir, {
|
|
110
|
+
theme: "dark",
|
|
111
|
+
env: {
|
|
112
|
+
ANTHROPIC_AUTH_TOKEN: input.token.trim() || "REPLACE_WITH_FULL_TOKEN",
|
|
113
|
+
ANTHROPIC_BASE_URL: input.baseUrl.trim(),
|
|
114
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: input.model.trim(),
|
|
115
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: input.model.trim(),
|
|
116
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: input.model.trim(),
|
|
117
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: input.model.trim(),
|
|
118
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: input.model.trim(),
|
|
119
|
+
ANTHROPIC_MODEL: input.model.trim()
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
await writeMeta(profileDir, { version: 1, type: "api", createdAt: new Date().toISOString() });
|
|
123
|
+
return summarizeProfile(input.name, profileDir);
|
|
124
|
+
}
|
|
125
|
+
export async function createLoginProfile(input, context = {}) {
|
|
126
|
+
const profileDir = await assertNewProfile(input.name, context);
|
|
127
|
+
await mkdir(profileDir, { recursive: true });
|
|
128
|
+
await writeSettings(profileDir, { theme: "dark" });
|
|
129
|
+
await writeMeta(profileDir, { version: 1, type: "login", createdAt: new Date().toISOString() });
|
|
130
|
+
return summarizeProfile(input.name, profileDir);
|
|
131
|
+
}
|
|
132
|
+
export async function removeProfile(name, context = {}) {
|
|
133
|
+
assertProfileName(name);
|
|
134
|
+
const profileDir = getProfileDir(name, context);
|
|
135
|
+
if (!(await exists(profileDir))) {
|
|
136
|
+
throw new CcpError(`Profile '${name}' does not exist: ${profileDir}`);
|
|
137
|
+
}
|
|
138
|
+
await removeProfileDir(profileDir);
|
|
139
|
+
return profileDir;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/core/profiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ClaudeSettings, ProfileMeta } from "./types.js";
|
|
2
|
+
export declare const SETTINGS_FILE = "settings.json";
|
|
3
|
+
export declare const META_FILE = ".ccp.json";
|
|
4
|
+
export declare function getSettingsPath(profileDir: string): string;
|
|
5
|
+
export declare function getMetaPath(profileDir: string): string;
|
|
6
|
+
export declare function readSettings(profileDir: string): Promise<ClaudeSettings | undefined>;
|
|
7
|
+
export declare function writeSettings(profileDir: string, settings: ClaudeSettings): Promise<void>;
|
|
8
|
+
export declare function readMeta(profileDir: string): Promise<ProfileMeta | undefined>;
|
|
9
|
+
export declare function writeMeta(profileDir: string, meta: ProfileMeta): Promise<void>;
|
|
10
|
+
export declare function removeProfileDir(profileDir: string): Promise<void>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CcpError } from "./errors.js";
|
|
4
|
+
export const SETTINGS_FILE = "settings.json";
|
|
5
|
+
export const META_FILE = ".ccp.json";
|
|
6
|
+
async function readJsonFile(filePath) {
|
|
7
|
+
try {
|
|
8
|
+
const raw = await readFile(filePath, "utf8");
|
|
9
|
+
return JSON.parse(raw.replace(/^/, ""));
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error.code === "ENOENT") {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const reason = error instanceof Error ? ` ${error.message}` : "";
|
|
16
|
+
throw new CcpError(`Failed to read JSON file: ${filePath}.${reason}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function writeJsonFile(filePath, value) {
|
|
20
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
21
|
+
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
22
|
+
}
|
|
23
|
+
export function getSettingsPath(profileDir) {
|
|
24
|
+
return path.join(profileDir, SETTINGS_FILE);
|
|
25
|
+
}
|
|
26
|
+
export function getMetaPath(profileDir) {
|
|
27
|
+
return path.join(profileDir, META_FILE);
|
|
28
|
+
}
|
|
29
|
+
export async function readSettings(profileDir) {
|
|
30
|
+
return readJsonFile(getSettingsPath(profileDir));
|
|
31
|
+
}
|
|
32
|
+
export async function writeSettings(profileDir, settings) {
|
|
33
|
+
await writeJsonFile(getSettingsPath(profileDir), settings);
|
|
34
|
+
}
|
|
35
|
+
export async function readMeta(profileDir) {
|
|
36
|
+
return readJsonFile(getMetaPath(profileDir));
|
|
37
|
+
}
|
|
38
|
+
export async function writeMeta(profileDir, meta) {
|
|
39
|
+
await writeJsonFile(getMetaPath(profileDir), meta);
|
|
40
|
+
}
|
|
41
|
+
export async function removeProfileDir(profileDir) {
|
|
42
|
+
await rm(profileDir, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/core/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAC7C,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;AAErC,KAAK,UAAU,YAAY,CAAI,QAAgB;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAM,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,QAAQ,CAAC,6BAA6B,QAAQ,IAAI,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,KAAc;IAC3D,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB;IACnD,OAAO,YAAY,CAAiB,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB,EAAE,QAAwB;IAC9E,MAAM,aAAa,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,UAAkB;IAC/C,OAAO,YAAY,CAAc,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,UAAkB,EAAE,IAAiB;IACnE,MAAM,aAAa,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAkB;IACvD,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ProfileType = "api" | "login" | "ccr" | "unknown";
|
|
2
|
+
export interface ClaudeSettings {
|
|
3
|
+
theme?: string;
|
|
4
|
+
env?: Record<string, string>;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface ProfileMeta {
|
|
8
|
+
version: number;
|
|
9
|
+
type: Exclude<ProfileType, "unknown">;
|
|
10
|
+
createdAt?: string;
|
|
11
|
+
endpoint?: string;
|
|
12
|
+
autoStart?: boolean;
|
|
13
|
+
ccrPreset?: string;
|
|
14
|
+
ccrRoute?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ProfileSummary {
|
|
17
|
+
name: string;
|
|
18
|
+
dir: string;
|
|
19
|
+
type: ProfileType;
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
model: string;
|
|
22
|
+
tokenStatus: "set" | "missing";
|
|
23
|
+
settingsPath: string;
|
|
24
|
+
meta?: ProfileMeta;
|
|
25
|
+
}
|
|
26
|
+
export interface CreateApiProfileInput {
|
|
27
|
+
name: string;
|
|
28
|
+
baseUrl: string;
|
|
29
|
+
token: string;
|
|
30
|
+
model: string;
|
|
31
|
+
}
|
|
32
|
+
export interface CreateLoginProfileInput {
|
|
33
|
+
name: string;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { CcpError } from "../core/errors.js";
|
|
3
|
+
export function getDefaultEditor() {
|
|
4
|
+
if (process.env.EDITOR) {
|
|
5
|
+
return process.env.EDITOR;
|
|
6
|
+
}
|
|
7
|
+
if (process.platform === "win32") {
|
|
8
|
+
return "notepad";
|
|
9
|
+
}
|
|
10
|
+
return "vi";
|
|
11
|
+
}
|
|
12
|
+
export async function openEditor(filePath, editor = getDefaultEditor()) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const child = spawn(editor, [filePath], { stdio: "inherit", shell: process.platform === "win32" });
|
|
15
|
+
child.on("error", (error) => reject(new CcpError(`Failed to open editor '${editor}': ${error.message}`)));
|
|
16
|
+
child.on("exit", (code) => resolve(code ?? 0));
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/platform/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,UAAU,gBAAgB;IAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;IAC5B,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAC5E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;QACnG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,0BAA0B,MAAM,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1G,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "multi-ccp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cross-platform Claude Code profile manager for API, login, and router workflows.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ccp": "dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"CHANGELOG.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"dev": "tsx src/cli/index.ts",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
20
|
+
"prepublishOnly": "npm run build && npm test"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"claude-code",
|
|
24
|
+
"claude",
|
|
25
|
+
"profiles",
|
|
26
|
+
"cli",
|
|
27
|
+
"anthropic"
|
|
28
|
+
],
|
|
29
|
+
"author": "",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@inquirer/prompts": "^7.10.1",
|
|
36
|
+
"commander": "^14.0.2",
|
|
37
|
+
"zod": "^4.1.13"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^24.10.1",
|
|
41
|
+
"tsx": "^4.21.0",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"vitest": "^4.0.15"
|
|
44
|
+
}
|
|
45
|
+
}
|