@praeviso/code-env-switch 0.1.0 → 0.1.2
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/.eslintrc.cjs +18 -0
- package/.github/workflows/npm-publish.yml +25 -0
- package/.vscode/settings.json +4 -0
- package/AGENTS.md +32 -0
- package/LICENSE +21 -0
- package/PLAN.md +33 -0
- package/README.md +208 -32
- package/README_zh.md +265 -0
- package/bin/cli/args.js +303 -0
- package/bin/cli/help.js +77 -0
- package/bin/cli/index.js +13 -0
- package/bin/commands/add.js +81 -0
- package/bin/commands/index.js +21 -0
- package/bin/commands/launch.js +330 -0
- package/bin/commands/list.js +57 -0
- package/bin/commands/show.js +10 -0
- package/bin/commands/statusline.js +12 -0
- package/bin/commands/unset.js +20 -0
- package/bin/commands/use.js +92 -0
- package/bin/config/defaults.js +85 -0
- package/bin/config/index.js +20 -0
- package/bin/config/io.js +72 -0
- package/bin/constants.js +27 -0
- package/bin/index.js +279 -0
- package/bin/profile/display.js +78 -0
- package/bin/profile/index.js +26 -0
- package/bin/profile/match.js +40 -0
- package/bin/profile/resolve.js +79 -0
- package/bin/profile/type.js +90 -0
- package/bin/shell/detect.js +40 -0
- package/bin/shell/index.js +18 -0
- package/bin/shell/snippet.js +92 -0
- package/bin/shell/utils.js +35 -0
- package/bin/statusline/claude.js +153 -0
- package/bin/statusline/codex.js +356 -0
- package/bin/statusline/index.js +469 -0
- package/bin/types.js +5 -0
- package/bin/ui/index.js +16 -0
- package/bin/ui/interactive.js +189 -0
- package/bin/ui/readline.js +76 -0
- package/bin/usage/index.js +709 -0
- package/code-env.example.json +36 -23
- package/package.json +14 -2
- package/src/cli/args.ts +318 -0
- package/src/cli/help.ts +75 -0
- package/src/cli/index.ts +5 -0
- package/src/commands/add.ts +91 -0
- package/src/commands/index.ts +10 -0
- package/src/commands/launch.ts +395 -0
- package/src/commands/list.ts +91 -0
- package/src/commands/show.ts +12 -0
- package/src/commands/statusline.ts +18 -0
- package/src/commands/unset.ts +19 -0
- package/src/commands/use.ts +121 -0
- package/src/config/defaults.ts +88 -0
- package/src/config/index.ts +19 -0
- package/src/config/io.ts +69 -0
- package/src/constants.ts +28 -0
- package/src/index.ts +359 -0
- package/src/profile/display.ts +77 -0
- package/src/profile/index.ts +12 -0
- package/src/profile/match.ts +41 -0
- package/src/profile/resolve.ts +84 -0
- package/src/profile/type.ts +83 -0
- package/src/shell/detect.ts +30 -0
- package/src/shell/index.ts +6 -0
- package/src/shell/snippet.ts +92 -0
- package/src/shell/utils.ts +30 -0
- package/src/statusline/claude.ts +172 -0
- package/src/statusline/codex.ts +393 -0
- package/src/statusline/index.ts +626 -0
- package/src/types.ts +95 -0
- package/src/ui/index.ts +5 -0
- package/src/ui/interactive.ts +220 -0
- package/src/ui/readline.ts +85 -0
- package/src/usage/index.ts +833 -0
- package/tsconfig.json +12 -0
- package/bin/codenv.js +0 -377
package/README_zh.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# code-env-switch
|
|
2
|
+
|
|
3
|
+
一个轻量的 CLI,用于在 Claude Code 与 Codex 的环境变量之间快速切换。
|
|
4
|
+
|
|
5
|
+
[English](README.md)
|
|
6
|
+
|
|
7
|
+
## 特性
|
|
8
|
+
|
|
9
|
+
- 多 profile 管理,按名称或类型切换
|
|
10
|
+
- `codenv use` 输出可执行的 shell 命令,方便在当前终端生效
|
|
11
|
+
- 支持交互式添加与选择 profile
|
|
12
|
+
- 支持 `removeFiles` 与 `commands` 做清理与自动化
|
|
13
|
+
- 配置文件自动发现与按类型自动补充 `unset` 键
|
|
14
|
+
|
|
15
|
+
## 快速开始
|
|
16
|
+
|
|
17
|
+
1) 安装:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g @praeviso/code-env-switch
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2) 交互式添加 profile(若不存在会创建 `~/.config/code-env/config.json`):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
codenv add
|
|
27
|
+
# 再执行一次,用来添加另一种 type
|
|
28
|
+
codenv add
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
交互示例:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
$ codenv add
|
|
35
|
+
Select type (1=codex, 2=claude): 1
|
|
36
|
+
Profile name (default: default): primary
|
|
37
|
+
Base URL (required): https://api.example.com/v1
|
|
38
|
+
API key (required): YOUR_API_KEY
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
3) 按 type 设置默认项:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
codenv default codex primary
|
|
45
|
+
codenv default claude default
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
4) 启用自动应用:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
codenv init
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
新开终端(或执行 `source ~/.bashrc` / `source ~/.zshrc`)即可自动应用默认配置。
|
|
55
|
+
|
|
56
|
+
本地开发可用:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm install -g .
|
|
60
|
+
# 或
|
|
61
|
+
npm link
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 使用方法
|
|
65
|
+
|
|
66
|
+
> 默认情况下,`codenv use` 仅输出 shell 命令;执行 `codenv init` 后,
|
|
67
|
+
> shell 包装函数会自动在当前终端生效。
|
|
68
|
+
> 该片段还会包装 `codex`/`claude` 以绑定会话到 profile;如需绕过,
|
|
69
|
+
> 可使用 `command codex` / `command claude`。
|
|
70
|
+
|
|
71
|
+
### 常用命令
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
codenv list
|
|
75
|
+
codenv show codex primary
|
|
76
|
+
codenv default codex primary
|
|
77
|
+
codenv remove codex primary
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`codenv list`(或 `codenv ls`)会输出 `PROFILE` / `TYPE` / `NOTE` 的表格。默认项会标注在 `NOTE` 列,当前激活的配置会用绿色显示。
|
|
81
|
+
如果设置了 `profile.name`,`PROFILE` 列会显示该名称;否则显示 profile 的 key(会尽量去掉旧的 `type-` 前缀)。
|
|
82
|
+
|
|
83
|
+
### 添加 / 更新 profile
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
codenv add primary OPENAI_BASE_URL=https://api.example.com/v1 OPENAI_API_KEY=YOUR_API_KEY --note "Primary endpoint"
|
|
87
|
+
# 指定 type(codex/claude,claude 也可写 cc)
|
|
88
|
+
codenv add --type codex primary OPENAI_BASE_URL=https://api.example.com/v1 OPENAI_API_KEY=YOUR_API_KEY
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
当设置 `--type` 时,名称保持不变,`type` 会单独存储。
|
|
92
|
+
profiles 使用内部 key,展示名称存放在 `profile.name`。
|
|
93
|
+
|
|
94
|
+
交互式添加(默认):
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
codenv add
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 删除 profile
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
codenv remove primary
|
|
104
|
+
# 或按类型 + 名称(名称重复时推荐)
|
|
105
|
+
codenv remove codex primary
|
|
106
|
+
# 一次删多个
|
|
107
|
+
codenv remove codex primary claude default
|
|
108
|
+
# (也兼容形如 codex-primary 的旧 key)
|
|
109
|
+
codenv remove codex-primary claude-default
|
|
110
|
+
# 全部删除
|
|
111
|
+
codenv remove --all
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 切换并生效(bash/zsh)
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
codenv use
|
|
118
|
+
# 上下选择,回车确认(q 退出)
|
|
119
|
+
codenv use primary
|
|
120
|
+
# 或按类型 + 名称匹配(也兼容形如 codex-primary 的旧 key)
|
|
121
|
+
codenv use codex primary
|
|
122
|
+
codenv use cc primary
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
先执行一次 `codenv init` 安装 shell 包装函数:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
codenv init
|
|
129
|
+
# 或指定 shell
|
|
130
|
+
codenv init --shell zsh
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
该包装函数会让 `codenv use` 和 `codenv unset` 在当前终端自动生效。
|
|
134
|
+
如果只想打印片段而不写入,可用 `codenv init --print`。
|
|
135
|
+
|
|
136
|
+
### 默认 profile 自动生效(按 type)
|
|
137
|
+
|
|
138
|
+
为不同 type 设置默认 profile,并重新执行一次 `codenv init`:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
codenv default codex primary
|
|
142
|
+
codenv default claude default
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"defaultProfiles": {
|
|
148
|
+
"codex": "primary",
|
|
149
|
+
"claude": "default"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
之后每次新开终端都会自动执行 `codenv auto` 应用所有默认配置。
|
|
155
|
+
如需清除全部默认设置,可执行 `codenv default --clear`(需确认)。
|
|
156
|
+
|
|
157
|
+
如果不想安装,可一次性执行:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
eval "$(codenv use codex primary)"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
注意:写入后对新终端生效;如需立刻生效,可执行:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
source ~/.bashrc
|
|
167
|
+
# 或 zsh
|
|
168
|
+
source ~/.zshrc
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 清理已知键
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
codenv unset
|
|
175
|
+
# 或一次性执行
|
|
176
|
+
eval "$(codenv unset)"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Fish shell
|
|
180
|
+
|
|
181
|
+
```fish
|
|
182
|
+
codenv use codex primary
|
|
183
|
+
# 或一次性执行
|
|
184
|
+
codenv use codex primary | source
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## 配置文件查找顺序
|
|
188
|
+
|
|
189
|
+
`codenv` 按以下顺序查找:
|
|
190
|
+
|
|
191
|
+
1) `--config <path>`
|
|
192
|
+
2) `CODE_ENV_CONFIG`
|
|
193
|
+
3) `~/.config/code-env/config.json`
|
|
194
|
+
|
|
195
|
+
可用 `codenv config` 输出当前目录会使用的配置路径。
|
|
196
|
+
|
|
197
|
+
`codenv add` 在找不到配置时,会默认写入 `~/.config/code-env/config.json`。
|
|
198
|
+
|
|
199
|
+
## 配置格式
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"unset": [],
|
|
204
|
+
"defaultProfiles": {
|
|
205
|
+
"codex": "primary",
|
|
206
|
+
"claude": "default"
|
|
207
|
+
},
|
|
208
|
+
"codexStatusline": {
|
|
209
|
+
"command": ["codenv", "statusline", "--type", "codex", "--sync-usage"],
|
|
210
|
+
"showHints": false,
|
|
211
|
+
"updateIntervalMs": 300,
|
|
212
|
+
"timeoutMs": 1000
|
|
213
|
+
},
|
|
214
|
+
"claudeStatusline": {
|
|
215
|
+
"command": "codenv statusline --type claude --sync-usage",
|
|
216
|
+
"type": "command",
|
|
217
|
+
"padding": 0
|
|
218
|
+
},
|
|
219
|
+
"profiles": {
|
|
220
|
+
"p_a1b2c3": {
|
|
221
|
+
"name": "primary",
|
|
222
|
+
"type": "codex",
|
|
223
|
+
"note": "Primary endpoint",
|
|
224
|
+
"env": {
|
|
225
|
+
"OPENAI_BASE_URL": "https://api.example.com/v1",
|
|
226
|
+
"OPENAI_API_KEY": "YOUR_API_KEY"
|
|
227
|
+
},
|
|
228
|
+
"removeFiles": ["$HOME/.config/example/auth.json"],
|
|
229
|
+
"commands": ["echo \"Switched to codex primary\""]
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
说明:
|
|
236
|
+
- `unset`:全局需要清理的环境变量。按 type 的默认清理键只会对当前 type 生效,不会影响其他 type。
|
|
237
|
+
- `defaultProfiles`:可选;`codex`/`claude` 对应的默认 profile 名称或 key,供 `codenv auto` 使用。
|
|
238
|
+
- `codexStatusline`:可选;在启动 `codex` 时写入 Codex TUI status line 配置。
|
|
239
|
+
- `command`:字符串或字符串数组;写入 Codex 的 `tui.status_line.command`。
|
|
240
|
+
- `showHints`:布尔值;状态栏激活时是否拼接 footer 提示。
|
|
241
|
+
- `updateIntervalMs`:数字;状态栏命令的刷新间隔(毫秒)。
|
|
242
|
+
- `timeoutMs`:数字;状态栏命令超时(毫秒)。
|
|
243
|
+
- `configPath`:可选;覆盖 `~/.codex/config.toml`(也可用 `CODE_ENV_CODEX_CONFIG_PATH`)。
|
|
244
|
+
- `claudeStatusline`:可选;在启动 `claude` 时写入 Claude Code statusLine 配置。
|
|
245
|
+
- `command`:字符串(或字符串数组;数组会被拼接成单个命令字符串)。
|
|
246
|
+
- `type`:字符串;statusLine 类型(默认 `command`)。
|
|
247
|
+
- `padding`:数字;statusLine padding(默认 0)。
|
|
248
|
+
- `settingsPath`:可选;覆盖 `~/.claude/settings.json`(也可用 `CODE_ENV_CLAUDE_SETTINGS_PATH`)。
|
|
249
|
+
- `name`:用于展示的 profile 名称,`codenv list` 与 `codenv use <name>` 会使用它。
|
|
250
|
+
- `type`:可选,`codex` 或 `claude`(别名 `cc`),便于用 `codenv use <type> <name>` 匹配。
|
|
251
|
+
- `note`:显示在 `codenv list` 输出中。
|
|
252
|
+
- `removeFiles`:可选;`codenv use` 会输出对应 `rm -f`。Codex profile 还会删除 `~/.codex/auth.json`。
|
|
253
|
+
- `ANTHROPIC_AUTH_TOKEN`:当设置了 `ANTHROPIC_API_KEY` 时,`codenv use` 会自动以同样的值导出 `ANTHROPIC_AUTH_TOKEN`。
|
|
254
|
+
- `commands`:可选;原样输出到切换脚本中。
|
|
255
|
+
|
|
256
|
+
## 安全提示
|
|
257
|
+
|
|
258
|
+
配置文件包含 API key,请妥善保存并避免提交到公共仓库。
|
|
259
|
+
|
|
260
|
+
## 开发
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npm install
|
|
264
|
+
npm run build
|
|
265
|
+
```
|
package/bin/cli/args.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseArgs = parseArgs;
|
|
4
|
+
exports.parseInitArgs = parseInitArgs;
|
|
5
|
+
exports.parseAddArgs = parseAddArgs;
|
|
6
|
+
exports.parseStatuslineArgs = parseStatuslineArgs;
|
|
7
|
+
const type_1 = require("../profile/type");
|
|
8
|
+
function parseArgs(argv) {
|
|
9
|
+
let configPath = null;
|
|
10
|
+
const args = [];
|
|
11
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12
|
+
const arg = argv[i];
|
|
13
|
+
if (arg === "-h" || arg === "--help") {
|
|
14
|
+
return { args: [], configPath: null, help: true };
|
|
15
|
+
}
|
|
16
|
+
if (arg === "-c" || arg === "--config") {
|
|
17
|
+
configPath = argv[i + 1];
|
|
18
|
+
i++;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (arg.startsWith("--config=")) {
|
|
22
|
+
configPath = arg.slice("--config=".length);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
args.push(arg);
|
|
26
|
+
}
|
|
27
|
+
return { args, configPath, help: false };
|
|
28
|
+
}
|
|
29
|
+
function parseInitArgs(args) {
|
|
30
|
+
const result = { apply: true, print: false, shell: null };
|
|
31
|
+
for (let i = 0; i < args.length; i++) {
|
|
32
|
+
const arg = args[i];
|
|
33
|
+
if (arg === "--apply") {
|
|
34
|
+
result.apply = true;
|
|
35
|
+
result.print = false;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (arg === "--print") {
|
|
39
|
+
result.print = true;
|
|
40
|
+
result.apply = false;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (arg === "--shell") {
|
|
44
|
+
const val = args[i + 1];
|
|
45
|
+
if (!val)
|
|
46
|
+
throw new Error("Missing value for --shell.");
|
|
47
|
+
result.shell = val;
|
|
48
|
+
i++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (arg.startsWith("--shell=")) {
|
|
52
|
+
result.shell = arg.slice("--shell=".length);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`Unknown init argument: ${arg}`);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
function parseAddArgs(args) {
|
|
60
|
+
const result = {
|
|
61
|
+
profile: null,
|
|
62
|
+
pairs: [],
|
|
63
|
+
note: null,
|
|
64
|
+
removeFiles: [],
|
|
65
|
+
commands: [],
|
|
66
|
+
unset: [],
|
|
67
|
+
type: null,
|
|
68
|
+
};
|
|
69
|
+
for (let i = 0; i < args.length; i++) {
|
|
70
|
+
const arg = args[i];
|
|
71
|
+
if (!result.profile && !arg.startsWith("-")) {
|
|
72
|
+
result.profile = arg;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (arg === "-n" || arg === "--note") {
|
|
76
|
+
const val = args[i + 1];
|
|
77
|
+
if (!val)
|
|
78
|
+
throw new Error("Missing value for --note.");
|
|
79
|
+
result.note = val;
|
|
80
|
+
i++;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (arg.startsWith("--note=")) {
|
|
84
|
+
result.note = arg.slice("--note=".length);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (arg === "-t" || arg === "--type") {
|
|
88
|
+
const val = args[i + 1];
|
|
89
|
+
if (!val)
|
|
90
|
+
throw new Error("Missing value for --type.");
|
|
91
|
+
result.type = val;
|
|
92
|
+
i++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (arg.startsWith("--type=")) {
|
|
96
|
+
result.type = arg.slice("--type=".length);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (arg === "-r" || arg === "--remove-file") {
|
|
100
|
+
const val = args[i + 1];
|
|
101
|
+
if (!val)
|
|
102
|
+
throw new Error("Missing value for --remove-file.");
|
|
103
|
+
result.removeFiles.push(val);
|
|
104
|
+
i++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (arg.startsWith("--remove-file=")) {
|
|
108
|
+
result.removeFiles.push(arg.slice("--remove-file=".length));
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (arg === "-x" || arg === "--command") {
|
|
112
|
+
const val = args[i + 1];
|
|
113
|
+
if (!val)
|
|
114
|
+
throw new Error("Missing value for --command.");
|
|
115
|
+
result.commands.push(val);
|
|
116
|
+
i++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (arg.startsWith("--command=")) {
|
|
120
|
+
result.commands.push(arg.slice("--command=".length));
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (arg === "-u" || arg === "--unset") {
|
|
124
|
+
const val = args[i + 1];
|
|
125
|
+
if (!val)
|
|
126
|
+
throw new Error("Missing value for --unset.");
|
|
127
|
+
result.unset.push(val);
|
|
128
|
+
i++;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (arg.startsWith("--unset=")) {
|
|
132
|
+
result.unset.push(arg.slice("--unset=".length));
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (arg.includes("=")) {
|
|
136
|
+
result.pairs.push(arg);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`Unknown add argument: ${arg}`);
|
|
140
|
+
}
|
|
141
|
+
if (!result.profile) {
|
|
142
|
+
throw new Error("Missing profile name.");
|
|
143
|
+
}
|
|
144
|
+
if (result.type) {
|
|
145
|
+
const normalized = (0, type_1.normalizeType)(result.type);
|
|
146
|
+
if (!normalized) {
|
|
147
|
+
throw new Error(`Unknown type: ${result.type}`);
|
|
148
|
+
}
|
|
149
|
+
result.type = normalized;
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
function parseNumberFlag(value, flag) {
|
|
154
|
+
if (value === null || value === undefined || value === "") {
|
|
155
|
+
throw new Error(`Missing value for ${flag}.`);
|
|
156
|
+
}
|
|
157
|
+
const num = Number(value);
|
|
158
|
+
if (!Number.isFinite(num)) {
|
|
159
|
+
throw new Error(`Invalid number for ${flag}: ${value}`);
|
|
160
|
+
}
|
|
161
|
+
return num;
|
|
162
|
+
}
|
|
163
|
+
function parseStatuslineArgs(args) {
|
|
164
|
+
const result = {
|
|
165
|
+
format: "text",
|
|
166
|
+
cwd: null,
|
|
167
|
+
type: null,
|
|
168
|
+
profileKey: null,
|
|
169
|
+
profileName: null,
|
|
170
|
+
model: null,
|
|
171
|
+
usageToday: null,
|
|
172
|
+
usageTotal: null,
|
|
173
|
+
usageInput: null,
|
|
174
|
+
usageOutput: null,
|
|
175
|
+
syncUsage: false,
|
|
176
|
+
};
|
|
177
|
+
for (let i = 0; i < args.length; i++) {
|
|
178
|
+
const arg = args[i];
|
|
179
|
+
if (arg === "--format") {
|
|
180
|
+
const val = args[i + 1];
|
|
181
|
+
if (!val)
|
|
182
|
+
throw new Error("Missing value for --format.");
|
|
183
|
+
const normalized = val.toLowerCase();
|
|
184
|
+
if (normalized !== "text" && normalized !== "json") {
|
|
185
|
+
throw new Error(`Unknown format: ${val}`);
|
|
186
|
+
}
|
|
187
|
+
result.format = normalized;
|
|
188
|
+
i++;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (arg.startsWith("--format=")) {
|
|
192
|
+
const val = arg.slice("--format=".length);
|
|
193
|
+
const normalized = val.toLowerCase();
|
|
194
|
+
if (normalized !== "text" && normalized !== "json") {
|
|
195
|
+
throw new Error(`Unknown format: ${val}`);
|
|
196
|
+
}
|
|
197
|
+
result.format = normalized;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (arg === "--cwd") {
|
|
201
|
+
const val = args[i + 1];
|
|
202
|
+
if (!val)
|
|
203
|
+
throw new Error("Missing value for --cwd.");
|
|
204
|
+
result.cwd = val;
|
|
205
|
+
i++;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (arg.startsWith("--cwd=")) {
|
|
209
|
+
result.cwd = arg.slice("--cwd=".length);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (arg === "--type") {
|
|
213
|
+
const val = args[i + 1];
|
|
214
|
+
if (!val)
|
|
215
|
+
throw new Error("Missing value for --type.");
|
|
216
|
+
result.type = val;
|
|
217
|
+
i++;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (arg.startsWith("--type=")) {
|
|
221
|
+
result.type = arg.slice("--type=".length);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (arg === "--profile-key") {
|
|
225
|
+
const val = args[i + 1];
|
|
226
|
+
if (!val)
|
|
227
|
+
throw new Error("Missing value for --profile-key.");
|
|
228
|
+
result.profileKey = val;
|
|
229
|
+
i++;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (arg.startsWith("--profile-key=")) {
|
|
233
|
+
result.profileKey = arg.slice("--profile-key=".length);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (arg === "--profile-name") {
|
|
237
|
+
const val = args[i + 1];
|
|
238
|
+
if (!val)
|
|
239
|
+
throw new Error("Missing value for --profile-name.");
|
|
240
|
+
result.profileName = val;
|
|
241
|
+
i++;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (arg.startsWith("--profile-name=")) {
|
|
245
|
+
result.profileName = arg.slice("--profile-name=".length);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (arg === "--model") {
|
|
249
|
+
const val = args[i + 1];
|
|
250
|
+
if (!val)
|
|
251
|
+
throw new Error("Missing value for --model.");
|
|
252
|
+
result.model = val;
|
|
253
|
+
i++;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (arg.startsWith("--model=")) {
|
|
257
|
+
result.model = arg.slice("--model=".length);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (arg === "--usage-today") {
|
|
261
|
+
result.usageToday = parseNumberFlag(args[i + 1], "--usage-today");
|
|
262
|
+
i++;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (arg.startsWith("--usage-today=")) {
|
|
266
|
+
result.usageToday = parseNumberFlag(arg.slice("--usage-today=".length), "--usage-today");
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (arg === "--usage-total") {
|
|
270
|
+
result.usageTotal = parseNumberFlag(args[i + 1], "--usage-total");
|
|
271
|
+
i++;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (arg.startsWith("--usage-total=")) {
|
|
275
|
+
result.usageTotal = parseNumberFlag(arg.slice("--usage-total=".length), "--usage-total");
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (arg === "--usage-input") {
|
|
279
|
+
result.usageInput = parseNumberFlag(args[i + 1], "--usage-input");
|
|
280
|
+
i++;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (arg.startsWith("--usage-input=")) {
|
|
284
|
+
result.usageInput = parseNumberFlag(arg.slice("--usage-input=".length), "--usage-input");
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (arg === "--usage-output") {
|
|
288
|
+
result.usageOutput = parseNumberFlag(args[i + 1], "--usage-output");
|
|
289
|
+
i++;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (arg.startsWith("--usage-output=")) {
|
|
293
|
+
result.usageOutput = parseNumberFlag(arg.slice("--usage-output=".length), "--usage-output");
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (arg === "--sync-usage") {
|
|
297
|
+
result.syncUsage = true;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
throw new Error(`Unknown statusline argument: ${arg}`);
|
|
301
|
+
}
|
|
302
|
+
return result;
|
|
303
|
+
}
|
package/bin/cli/help.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Help message for codenv CLI
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.printHelp = printHelp;
|
|
7
|
+
function printHelp() {
|
|
8
|
+
const msg = `codenv - switch Claude/Codex env vars
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
codenv list
|
|
12
|
+
codenv ls
|
|
13
|
+
codenv config
|
|
14
|
+
codenv auto
|
|
15
|
+
codenv use
|
|
16
|
+
codenv use <profile>
|
|
17
|
+
codenv use <type> <name>
|
|
18
|
+
codenv show <profile>
|
|
19
|
+
codenv show <type> <name>
|
|
20
|
+
codenv default <profile>
|
|
21
|
+
codenv default <type> <name>
|
|
22
|
+
codenv default --clear
|
|
23
|
+
codenv remove <profile> [<profile> ...]
|
|
24
|
+
codenv remove <type> <name> [<type> <name> ...]
|
|
25
|
+
codenv remove --all
|
|
26
|
+
codenv unset
|
|
27
|
+
codenv add <profile> KEY=VALUE [KEY=VALUE ...]
|
|
28
|
+
codenv add
|
|
29
|
+
codenv launch <codex|claude> [--] [args...]
|
|
30
|
+
codenv init
|
|
31
|
+
codenv statusline [options]
|
|
32
|
+
|
|
33
|
+
Options:
|
|
34
|
+
-c, --config <path> Path to config JSON
|
|
35
|
+
-h, --help Show help
|
|
36
|
+
|
|
37
|
+
Init options:
|
|
38
|
+
--apply Append shell helper to your shell rc (default)
|
|
39
|
+
--print Print helper snippet to stdout
|
|
40
|
+
--shell <bash|zsh|fish> Explicitly set the target shell
|
|
41
|
+
|
|
42
|
+
Add options:
|
|
43
|
+
-t, --type <codex|claude> Set profile type (alias: cc)
|
|
44
|
+
-n, --note <text> Set profile note
|
|
45
|
+
-r, --remove-file <path> Add a removeFiles entry (repeat)
|
|
46
|
+
-x, --command <cmd> Add a commands entry (repeat)
|
|
47
|
+
-u, --unset <KEY> Add a global unset key (repeat)
|
|
48
|
+
|
|
49
|
+
Statusline options:
|
|
50
|
+
--format <text|json> Output format (default: text)
|
|
51
|
+
--cwd <path> Override working directory
|
|
52
|
+
--type <type> Set profile type
|
|
53
|
+
--profile-key <key> Set profile key
|
|
54
|
+
--profile-name <name> Set profile name
|
|
55
|
+
--model <model> Set model label
|
|
56
|
+
--usage-today <n> Set today's token usage
|
|
57
|
+
--usage-total <n> Set total token usage
|
|
58
|
+
--usage-input <n> Set input token usage
|
|
59
|
+
--usage-output <n> Set output token usage
|
|
60
|
+
--sync-usage Sync usage from sessions before reading
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
codenv init
|
|
64
|
+
codenv use codex primary
|
|
65
|
+
codenv list
|
|
66
|
+
codenv default codex primary
|
|
67
|
+
codenv remove codex primary
|
|
68
|
+
codenv remove codex primary claude default
|
|
69
|
+
codenv remove --all
|
|
70
|
+
codenv launch codex -- --help
|
|
71
|
+
codenv statusline --format json
|
|
72
|
+
CODE_ENV_CONFIG=~/.config/code-env/config.json codenv use claude default
|
|
73
|
+
codenv add --type codex primary OPENAI_BASE_URL=https://api.example.com/v1 OPENAI_API_KEY=YOUR_API_KEY
|
|
74
|
+
codenv add
|
|
75
|
+
`;
|
|
76
|
+
console.log(msg);
|
|
77
|
+
}
|
package/bin/cli/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printHelp = exports.parseStatuslineArgs = exports.parseAddArgs = exports.parseInitArgs = exports.parseArgs = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* CLI module exports
|
|
6
|
+
*/
|
|
7
|
+
var args_1 = require("./args");
|
|
8
|
+
Object.defineProperty(exports, "parseArgs", { enumerable: true, get: function () { return args_1.parseArgs; } });
|
|
9
|
+
Object.defineProperty(exports, "parseInitArgs", { enumerable: true, get: function () { return args_1.parseInitArgs; } });
|
|
10
|
+
Object.defineProperty(exports, "parseAddArgs", { enumerable: true, get: function () { return args_1.parseAddArgs; } });
|
|
11
|
+
Object.defineProperty(exports, "parseStatuslineArgs", { enumerable: true, get: function () { return args_1.parseStatuslineArgs; } });
|
|
12
|
+
var help_1 = require("./help");
|
|
13
|
+
Object.defineProperty(exports, "printHelp", { enumerable: true, get: function () { return help_1.printHelp; } });
|