cchost 0.4.4 → 0.5.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 CHANGED
@@ -5,6 +5,70 @@ All notable changes to `cchost` are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [0.5.0] - 2026-11-07
9
+
10
+ ### Added
11
+
12
+ - **`cchost` command.** The CLI now installs as both `cchost` (primary) and
13
+ `cc` (legacy alias). `cc` is the system C compiler on macOS and Linux, so
14
+ docs, help, and new scripts use `cchost`; the alias stays for backwards
15
+ compatibility.
16
+ - **Inherited-auth warning.** `cchost use` now warns when `ANTHROPIC_API_KEY`,
17
+ `ANTHROPIC_AUTH_TOKEN`, or `CLAUDE_CODE_OAUTH_TOKEN` is set in the shell for a
18
+ subscription profile, because those variables can silently override the
19
+ profile's login. It does not strip them.
20
+ - **Endpoint environment isolation.** Endpoint profiles now drop inherited
21
+ `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, and
22
+ `ANTHROPIC_BASE_URL` values the profile does not set itself, with a short
23
+ notice listing what was removed.
24
+ - **macOS login visibility.** `cchost list` reports an inferred Keychain login
25
+ as `true` instead of `false`, exposes `credentialSource` in `--json`, and
26
+ `cchost doctor` explains that Keychain entries are namespaced by the profile's
27
+ exact `CLAUDE_CONFIG_DIR` path.
28
+ - **See what a profile shares.** `cchost list <profile>` prints each of the 7
29
+ resources as `shared`, `private`, `empty`, `missing`, or `foreign-link`;
30
+ `--json` adds a `resources` object.
31
+ - **`--dry-run`** on `cchost link`, `unlink`, and `copy` prints what would
32
+ change without touching the filesystem.
33
+ - **`cchost help sharing`**: a task-oriented guide to what is shared and how to
34
+ make a resource private, share it again, or copy into a profile.
35
+
36
+ ### Changed
37
+
38
+ - All README examples, help text, and error hints now use `cchost`.
39
+ - `cchost doctor` no longer reports `/usr/bin/cc` or a real C compiler
40
+ (clang, gcc, ccache) as cchost; a `cc` entry counts only when it resolves to
41
+ cchost, and the two shims from one install still count as one entry.
42
+ - README now separates **credential** isolation from the default shared pool.
43
+ `projects/` (full session transcripts) and `settings.json` / `mcp.json`
44
+ (code-executing config) are shared across profiles by default, and are
45
+ documented as a privacy/security trade-off rather than as isolated state.
46
+
47
+ ### Fixed
48
+
49
+ - `cchost link` reports a symlink that points outside `~/.claude/` as
50
+ `foreign-link` instead of claiming it is already linked.
51
+ - `cchost doctor --fix` prints and uses the post-fix results; `--json --fix`
52
+ is supported again and emits one post-fix JSON report (fix logs go to stderr).
53
+ - `cchost doctor` no longer warns that macOS subscription profiles may share
54
+ one Keychain login; it explains that entries are namespaced by the exact
55
+ profile path (renaming or moving a profile requires a new login), and stays
56
+ silent when no profile uses a Keychain login.
57
+ - `cchost help migrate` prints command-specific help.
58
+ - `.cc-env.json` is created user-only; optional non-string fields are ignored
59
+ instead of making the profile unusable.
60
+ - Help banner shows the version again, and `NO_COLOR` follows no-color.org
61
+ (a non-empty value disables color; `NO_COLOR=""` leaves it enabled).
62
+
63
+ ### Known limitations
64
+
65
+ - **macOS Keychain entries are keyed by the exact profile path.** Claude Code
66
+ (verified with 2.1.319) stores each profile's OAuth token under a service
67
+ name derived from the `CLAUDE_CONFIG_DIR` string, so renaming, moving, or copying a profile — or
68
+ accessing it through a different path spelling or symlinked home — requires a
69
+ fresh login. `cchost list` infers the login from `.claude.json`; it does not
70
+ read the Keychain secret or verify that the token is still live.
71
+
8
72
  ## [0.4.4] - 2026-06-19
9
73
 
10
74
  ### Changed
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # cchost
2
2
 
3
- > **nvm, but for your Claude Code accounts.** Works on Windows, Linux, and macOS.
3
+ > **nvm, but for your Claude Code accounts.** Works on Windows, Linux, and macOS — see [Platform notes](#platform-notes) for macOS credential-storage caveats.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/cchost.svg)](https://www.npmjs.com/package/cchost)
6
6
  [![CI](https://github.com/allenhack638/cchost/actions/workflows/ci.yml/badge.svg)](https://github.com/allenhack638/cchost/actions/workflows/ci.yml)
@@ -9,13 +9,13 @@
9
9
  [![platforms](https://img.shields.io/badge/platforms-windows%20%7C%20linux%20%7C%20macos-blue)](#install)
10
10
 
11
11
  <p align="center">
12
- <img src="https://raw.githubusercontent.com/allenhack638/cchost/master/docs/demo.png" alt="cchost CLI — cc list showing masked emails and shared resource counts" width="100%">
12
+ <img src="https://raw.githubusercontent.com/allenhack638/cchost/master/docs/demo.png" alt="cchost CLI — cchost list showing masked emails and shared resource counts" width="100%">
13
13
  </p>
14
14
 
15
15
  `cchost` is a multi-account profile manager for [Claude Code](https://code.claude.com).
16
16
 
17
- - **Isolated profiles** — each profile is a complete `CLAUDE_CONFIG_DIR` with its own credentials, session history, and settings. Switch with `cc use <name>`.
18
- - **Shared pool** — `skills/`, `agents/`, `commands/`, `projects/`, and shared config files (`mcp.json`, `settings.json`, `CLAUDE.md`) link across all profiles by default. Credentials never do.
17
+ - **Isolated credentials** — each profile is a complete `CLAUDE_CONFIG_DIR` with its own login, endpoint, and credential files. Credentials are never copied between profiles (on macOS, see [Platform notes](#platform-notes) for Keychain caveats). Switch with `cchost use <name>`.
18
+ - **Shared tooling and history by default** — `skills/`, `agents/`, `commands/`, `projects/`, and the config files (`mcp.json`, `settings.json`, `CLAUDE.md`) link across all profiles. That includes session transcripts and code-executing config; see [Sharing model](#sharing-model) before using profiles for separated trust boundaries.
19
19
  - **Custom endpoints** — route a profile through any Anthropic-compatible API (Moonshot, OpenRouter, self-hosted vLLM, corporate proxies) instead of Anthropic OAuth.
20
20
  - **Zero runtime dependencies** — single Node CLI on your `PATH`. Identical behavior across PowerShell, cmd, bash, zsh, and fish.
21
21
 
@@ -27,32 +27,36 @@ npm install -g cchost
27
27
 
28
28
  Requires Node.js 18+ and Claude Code 2.1.140+ (`npm install -g @anthropic-ai/claude-code`).
29
29
 
30
+ `cchost` is the primary command. The same binary is also installed as `cc` for
31
+ backwards compatibility, but `cc` is the standard C compiler on macOS and
32
+ Linux — prefer `cchost` in scripts, build files, and daily use.
33
+
30
34
  ## Quickstart
31
35
 
32
36
  ```bash
33
- cc add work # create a profile
34
- cc use work # launch Claude under it (prompts for login on first run)
35
- cc use work --resume # resume the last conversation
36
- cc list # see all profiles
37
- cc remove old-profile # delete a profile
37
+ cchost add work # create a profile
38
+ cchost use work # launch Claude under it (prompts for login on first run)
39
+ cchost use work --resume # resume the last conversation
40
+ cchost list # see all profiles
41
+ cchost remove old-profile # delete a profile
38
42
  ```
39
43
 
40
44
  ## Commands
41
45
 
42
46
  | Command | What it does |
43
47
  | --- | --- |
44
- | `cc add <profile>` | Create a subscription profile. Logs in via Anthropic OAuth on first `cc use`. Auto-shares all 7 resources with `~/.claude/`. |
45
- | `cc add <profile> --custom [...]` | Create or edit a custom-endpoint profile. Interactive wizard, or `--base-url=URL --token=TOKEN [--model=…]`. See [Custom endpoints](#custom-endpoints). |
46
- | `cc use <profile> [...args]` | Launch Claude under that profile. Extra args are forwarded to `claude`. |
47
- | `cc list [<profile>] [--json] [--reveal]` | List all profiles or show one in detail. Emails and tokens are masked by default; `--reveal` prints them in full. |
48
- | `cc copy default <profile> [--force]` | Seed an unlinked profile resource from `~/.claude/`. No-op for fully-shared profiles. |
49
- | `cc link <profile> [--projects\|--tools\|--config]` | Link a profile's resources to `~/.claude/`. Defaults to all 7 when no flag is passed. |
50
- | `cc unlink <profile> [--projects\|--tools\|--config]` | Break the link and make those resources private to the profile. Non-destructive. |
51
- | `cc remove <profile> [--force]` | Delete a profile. Requires typing the profile name. `--force` required when private resources exist. |
52
- | `cc doctor [--fix [--force]] [--json]` | Diagnose environment, storage, and platform issues. `--fix` applies safe auto-fixes; `--force` is required for destructive ones. |
53
- | `cc help [command]` | Print usage. |
54
-
55
- Sample `cc list` output:
48
+ | `cchost add <profile>` | Create a subscription profile. Logs in via Anthropic OAuth on first `cchost use`. Auto-shares all 7 resources with `~/.claude/`. |
49
+ | `cchost add <profile> --custom [...]` | Create or edit a custom-endpoint profile. Interactive wizard, or `--base-url=URL --token=TOKEN [--model=…]`. See [Custom endpoints](#custom-endpoints). |
50
+ | `cchost use <profile> [...args]` | Launch Claude under that profile. Extra args are forwarded to `claude`. |
51
+ | `cchost list [<profile>] [--json] [--reveal]` | List all profiles, or show one in detail including the state of each of its 7 resources. Emails and tokens are masked by default; `--reveal` prints them in full. |
52
+ | `cchost copy default <profile> [--force] [--dry-run]` | Seed an unlinked profile resource from `~/.claude/`. No-op for fully-shared profiles. |
53
+ | `cchost link <profile> [--projects\|--tools\|--config] [--dry-run]` | Link a profile's resources to `~/.claude/`. Defaults to all 7 when no flag is passed. |
54
+ | `cchost unlink <profile> [--projects\|--tools\|--config] [--dry-run]` | Break the link and make those resources private to the profile. Non-destructive. |
55
+ | `cchost remove <profile> [--force]` | Delete a profile. Requires typing the profile name. `--force` required when private resources exist. |
56
+ | `cchost doctor [--fix [--force]] [--json]` | Diagnose environment, storage, and platform issues. `--fix` applies safe auto-fixes; `--force` is required for destructive ones. |
57
+ | `cchost help [command]` | Print usage. `cchost help sharing` is a task guide to linking, unlinking, and copying. |
58
+
59
+ Sample `cchost list` output:
56
60
 
57
61
  ```
58
62
  Profile LoggedIn Email Shared Endpoint
@@ -61,11 +65,11 @@ personal true p************.com 7/7 subscription
61
65
  work true w************.com 7/7 subscription
62
66
  ```
63
67
 
64
- The `Shared` column counts how many of the 7 shareable resources are linked into `~/.claude/`. `7/7` is the default; lower numbers mean one or more resources have been `cc unlink`ed and now hold private content.
68
+ The `Shared` column counts how many of the 7 shareable resources are linked into `~/.claude/`. `7/7` is the default; lower numbers mean one or more resources have been `cchost unlink`ed and now hold private content.
65
69
 
66
70
  ## Sharing model
67
71
 
68
- Anything that authenticates a profile stays per-profile; everything else is shared.
72
+ On Windows and Linux, anything that authenticates a profile stays per-profile and everything else is shared. On macOS, OAuth credentials live in the login Keychain rather than the profile directory — see [Platform notes](#platform-notes).
69
73
 
70
74
  | Resource | Shared by default | Flag group |
71
75
  | --- | :---: | --- |
@@ -75,21 +79,28 @@ Anything that authenticates a profile stays per-profile; everything else is shar
75
79
  | `.credentials.json`, `.claude.json`, `.cc-env.json` | never | — |
76
80
  | `sessions/`, `plugins/` | never | — |
77
81
 
78
- Sharing uses POSIX symlinks on Linux/macOS and directory junctions on Windows (no admin or Developer Mode required). File resources fall back to a hard link on Windows when symlink creation would require elevation. Note that `mcp.json` and `settings.json` can execute arbitrary code through MCP servers and hooks — if a profile needs an isolated copy of either, run `cc unlink <profile> --config`.
82
+ Sharing uses POSIX symlinks on Linux/macOS and directory junctions on Windows (no admin or Developer Mode required). File resources fall back to a hard link on Windows when symlink creation would require elevation. Note that `mcp.json` and `settings.json` can execute arbitrary code through MCP servers and hooks — if a profile needs an isolated copy of either, run `cchost unlink <profile> --config`.
83
+
84
+ > **Privacy note:** the defaults above are convenient, not isolated. `projects/`
85
+ > contains full conversation transcripts, and `settings.json` / `mcp.json` can
86
+ > execute hooks and MCP servers. For a work/personal split, unlink the resources
87
+ > that must not cross over before first use, or use a separate OS account.
79
88
 
80
- To break sharing for a single profile:
89
+ To see exactly what a profile shares, run `cchost list <profile>` — it prints each resource as `shared`, `private`, `empty`, `missing`, or `foreign-link`. `cchost help sharing` walks through the common tasks.
90
+
91
+ To break sharing for a single profile (add `--dry-run` to any of these to preview first):
81
92
 
82
93
  ```bash
83
- cc unlink work --tools # private skills/agents/commands
84
- cc unlink work --projects # private projects/
85
- cc unlink work --config # private mcp.json/settings.json/CLAUDE.md
86
- cc unlink work # all 7 private
87
- cc link work --tools # re-share
94
+ cchost unlink work --tools # private skills/agents/commands
95
+ cchost unlink work --projects # private projects/
96
+ cchost unlink work --config # private mcp.json/settings.json/CLAUDE.md
97
+ cchost unlink work # all 7 private
98
+ cchost link work --tools # re-share
88
99
  ```
89
100
 
90
- `cc unlink` is non-destructive — pool content is copied into the profile before the link is broken. `cc link` refuses to overwrite existing private content; remove or relocate the private resource manually, then re-run.
101
+ `cchost unlink` is non-destructive — pool content is copied into the profile before the link is broken. `cchost link` refuses to overwrite existing private content; remove or relocate the private resource manually, then re-run.
91
102
 
92
- Use `cc copy default <profile>` to seed an unlinked resource from the current `~/.claude/`. For fully-shared profiles this is a no-op.
103
+ Use `cchost copy default <profile>` to seed an unlinked resource from the current `~/.claude/`. For fully-shared profiles this is a no-op.
93
104
 
94
105
  ## Custom endpoints
95
106
 
@@ -97,43 +108,77 @@ A profile can route through any Anthropic-compatible API — Moonshot/Kimi, Open
97
108
 
98
109
  > **Billing:** custom-endpoint profiles bill through the **third-party provider**, not your Anthropic subscription.
99
110
 
111
+ Inherited Anthropic auth/endpoint variables (`ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_BASE_URL`) are removed for endpoint profiles when the profile doesn't set them, so a shell-level key cannot override the endpoint.
112
+
100
113
  ```bash
101
- cc add kimi --custom --base-url=https://api.moonshot.ai/anthropic \
114
+ cchost add kimi --custom --base-url=https://api.moonshot.ai/anthropic \
102
115
  --token=sk-your-key \
103
116
  --model=kimi-k2.5
104
- cc use kimi
117
+ cchost use kimi
105
118
  ```
106
119
 
107
- Run `cc add <profile> --custom` with no value flags for an interactive wizard — it prompts for every field and never echoes the token. Re-running on an existing custom-endpoint profile enters **edit mode**: the wizard prefills current values, and with flags only the flags you pass are changed.
120
+ Run `cchost add <profile> --custom` with no value flags for an interactive wizard — it prompts for every field and never echoes the token. Re-running on an existing custom-endpoint profile enters **edit mode**: the wizard prefills current values, and with flags only the flags you pass are changed.
108
121
 
109
122
  ```bash
110
- cc add kimi --custom --model=kimi-k3 # change just the model
111
- cc add kimi --custom --token=sk-rotated # rotate the API key
123
+ cchost add kimi --custom --model=kimi-k3 # change just the model
124
+ cchost add kimi --custom --token=sk-rotated # rotate the API key
112
125
  ```
113
126
 
114
- Subscription and custom-endpoint profiles are mutually exclusive: a profile is either one or the other for its entire lifetime. To switch, remove the profile with `cc remove` and recreate it.
127
+ Subscription and custom-endpoint profiles are mutually exclusive: a profile is either one or the other for its entire lifetime. To switch, remove the profile with `cchost remove` and recreate it.
115
128
 
116
- Inspect with `cc list <profile>` (token masked) or `cc list <profile> --reveal` (full). The endpoint URL should not include `/v1` — Claude Code appends `/v1/messages` itself. Most providers serve a single model; `--model` applies to every tier (`opus`/`sonnet`/`haiku`/`subagent`), or override per-tier with `--opus=`, `--sonnet=`, etc.
129
+ Inspect with `cchost list <profile>` (token masked) or `cchost list <profile> --reveal` (full). The endpoint URL should not include `/v1` — Claude Code appends `/v1/messages` itself. Most providers serve a single model; `--model` applies to every tier (`opus`/`sonnet`/`haiku`/`subagent`), or override per-tier with `--opus=`, `--sonnet=`, etc.
117
130
 
118
- On launch, `cc use` prints a one-line endpoint banner to stderr:
131
+ On launch, `cchost use` prints a one-line endpoint banner to stderr:
119
132
 
120
133
  ```
121
134
  [cchost] Profile 'kimi' → api.moonshot.ai (custom endpoint billing applies)
122
135
  ```
123
136
 
137
+ ## Platform notes
138
+
139
+ ### macOS
140
+
141
+ Claude Code stores OAuth credentials in the login **Keychain**, not in
142
+ `<profile>/.credentials.json`. Claude Code namespaces the Keychain entry by
143
+ the exact `CLAUDE_CONFIG_DIR` path (verified with Claude Code 2.1.319):
144
+
145
+ ```
146
+ Claude Code-credentials-<first 8 hex of sha256(path)>
147
+ ```
148
+
149
+ So each profile gets its own Keychain entry. Two consequences:
150
+
151
+ - `cchost list` infers the login from `.claude.json`; it does not read the
152
+ Keychain secret. Treat `LoggedIn` as a login hint, not proof of a live token.
153
+ - The entry is keyed by the exact path string. Renaming, moving, or copying a
154
+ profile — or addressing it through a different path spelling or symlinked
155
+ home — changes the key and looks like a logout. Run `claude` under the new
156
+ path to log in again.
157
+
158
+ If you need guaranteed account separation on macOS, **custom-endpoint profiles**
159
+ (each has its own token in `.cc-env.json`) or separate macOS user accounts are
160
+ still the most explicit option.
161
+
162
+ ### Windows and Linux
163
+
164
+ Credentials are file-based (`.credentials.json` in the profile directory), so
165
+ the `LoggedIn` column reflects the profile's own credential file.
166
+
124
167
  ## Security
125
168
 
126
- `~/.claude-profiles/<profile>/` holds real OAuth credentials and API keys. Treat it like `~/.ssh`: don't commit it, don't share it, don't sync it to public cloud storage. The shipped `.gitignore` excludes profile directories. `cc copy` and `cc link` never touch credential files (`.credentials.json`, `.claude.json`, `.cc-env.json`); each profile keeps its own auth state.
169
+ `~/.claude-profiles/<profile>/` holds real OAuth credentials and API keys. Treat it like `~/.ssh`: don't commit it, don't share it, don't sync it to public cloud storage. The shipped `.gitignore` excludes profile directories. `cchost copy` and `cchost link` never touch credential files (`.credentials.json`, `.claude.json`, `.cc-env.json`); each profile keeps its own auth state. On macOS, OAuth tokens are stored in the login Keychain instead — see [Platform notes](#platform-notes).
127
170
 
128
171
  ## Troubleshooting
129
172
 
130
- Run `cc doctor` first — it checks Node, Claude Code version, profile integrity, symlinks, and the shared pool.
173
+ Run `cchost doctor` first — it checks Node, Claude Code version, profile integrity, symlinks, and the shared pool.
131
174
 
132
175
  **`Claude Code does not appear to be installed`** — install with `npm install -g @anthropic-ai/claude-code` and verify `claude --version` runs in a fresh terminal.
133
176
 
134
177
  **`warning: Claude Code <version> is older than 2.1.140`** — earlier versions on Windows read `.claude.json` from `%USERPROFILE%` regardless of `CLAUDE_CONFIG_DIR`, breaking isolation. Run `claude update`.
135
178
 
136
- **`Profile "<name>" does not exist`** — `cc use` does not auto-create profiles. Run `cc add <name>` first.
179
+ **`warning: ANTHROPIC_API_KEY ... may override the subscription login`** — a globally exported API key takes precedence over a profile's OAuth login. Unset it in that shell (`unset ANTHROPIC_API_KEY`, and the same for `ANTHROPIC_AUTH_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN`) or use a custom-endpoint profile.
180
+
181
+ **`Profile "<name>" does not exist`** — `cchost use` does not auto-create profiles. Run `cchost add <name>` first.
137
182
 
138
183
  **Profile name rejected** — names must not contain path separators, whitespace, `..`, leading dots, or DOS reserved names (`CON`, `PRN`, `COM1`…). Use letters, digits, dashes, and underscores.
139
184
 
package/lib/banner.js CHANGED
@@ -14,9 +14,11 @@ function renderBanner() {
14
14
  try {
15
15
  version = require('../package.json').version;
16
16
  } catch {
17
+ // Not fatal — help still works if package.json is unreadable.
17
18
  }
18
19
  const tagline = 'Multi-profile launcher for Claude Code';
19
- return BANNER + ' ' + '\n\n';
20
+ const label = version ? `cchost ${version} — ${tagline}` : `cchost — ${tagline}`;
21
+ return BANNER + ' ' + label + '\n\n';
20
22
  }
21
23
 
22
24
  module.exports = { renderBanner, BANNER };