@wangxt0223/codex-switcher 0.6.2 → 0.6.4
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 +15 -0
- package/README.en.md +44 -1
- package/README.md +44 -1
- package/package.json +1 -4
- package/plugins/codex-switcher/README.en.md +6 -2
- package/plugins/codex-switcher/README.md +6 -2
- package/plugins/codex-switcher/scripts/codex-switcher +30 -9
- package/plugins/codex-switcher/scripts/profile-metrics.py +13 -17
- package/plugins/codex-switcher/scripts/test-switcher.sh +13 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.4 - 2026-04-12
|
|
4
|
+
|
|
5
|
+
- Updated `list` output columns to include `HOME` and a dedicated `SOURCE` column (`api`/`local`).
|
|
6
|
+
- Changed `LAST ACTIVITY` to absolute `MM-DD HH:MM` format and aligned weekly reset display to the same date+time style.
|
|
7
|
+
- Improved last-activity fallback behavior: when API omits activity timestamp, fallback to local session-derived time.
|
|
8
|
+
- Updated Chinese/English README and plugin docs for the latest `list` output schema.
|
|
9
|
+
- Updated smoke tests for `HOME`/`SOURCE` columns and absolute-time assertions.
|
|
10
|
+
- Removed accidental package self-dependency from `package.json`.
|
|
11
|
+
|
|
12
|
+
## 0.6.3 - 2026-04-12
|
|
13
|
+
|
|
14
|
+
- Changed `list` email rendering to show plain email only (removed `(account)` prefix).
|
|
15
|
+
- Reworked root Chinese/English README command reference into full command tables based on current capabilities.
|
|
16
|
+
- Updated smoke-test assertions for the new email column format.
|
|
17
|
+
|
|
3
18
|
## 0.6.2 - 2026-04-12
|
|
4
19
|
|
|
5
20
|
- Added usage-API proxy auto-detection (manual proxy > env proxy > macOS system proxy).
|
package/README.en.md
CHANGED
|
@@ -63,7 +63,50 @@ codex-switcher account use corp --env project-a
|
|
|
63
63
|
|
|
64
64
|
## Command reference
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
Commands below use `codex-sw` (all are equivalent under `codex-switcher`):
|
|
67
|
+
|
|
68
|
+
| Category | Command | Description |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| Env | `codex-sw env list` | List all envs with CLI/App current markers |
|
|
71
|
+
| Env | `codex-sw env create <env> [--empty\|--from-default\|--from-env <src>]` | Create env from empty/default/another env |
|
|
72
|
+
| Env | `codex-sw env use <env> [--target cli\|app\|both]` | Switch env pointer for CLI/App |
|
|
73
|
+
| Env | `codex-sw env remove <env> [--force]` | Remove env |
|
|
74
|
+
| Env | `codex-sw env current [cli\|app]` | Show current env pointer |
|
|
75
|
+
| Env | `codex-sw env path [env]` | Print exportable `CODEX_HOME` path |
|
|
76
|
+
| Account | `codex-sw account list [--env <env>]` | List accounts in env with current markers |
|
|
77
|
+
| Account | `codex-sw account add <account> [--env <env>]` | Add account slot |
|
|
78
|
+
| Account | `codex-sw account remove <account> [--env <env>] [--force]` | Remove account slot |
|
|
79
|
+
| Account | `codex-sw account login <account> [--env <env>] [--target cli\|app\|both] [--sync\|--no-sync]` | Login account and persist auth |
|
|
80
|
+
| Account | `codex-sw account use <account> [--env <env>] [--target cli\|app\|both] [--sync\|--no-sync]` | Switch to account |
|
|
81
|
+
| Account | `codex-sw account logout [account] [--env <env>] [--target cli\|app\|both]` | Logout account |
|
|
82
|
+
| Account | `codex-sw account current [cli\|app]` | Show current env/account pointer |
|
|
83
|
+
| Usage proxy | `codex-sw proxy [<host:port>\|off\|test]` | Configure/test usage API proxy (only affects `list`) |
|
|
84
|
+
| Query/Run | `codex-sw list` | Show `ENV/HOME/ACCOUNT/EMAIL/PLAN/5H/WEEKLY/LAST ACTIVITY/SOURCE` (time format: `MM-DD HH:MM`) |
|
|
85
|
+
| Query/Run | `codex-sw status` | Show login status for current CLI/App pointers |
|
|
86
|
+
| Query/Run | `codex-sw current [cli\|app]` | Show current env/account |
|
|
87
|
+
| Query/Run | `codex-sw exec -- <codex args...>` | Run `codex` under current CLI env/account |
|
|
88
|
+
| Compat | `codex-sw login [account] [--sync\|--no-sync]` | Compatibility alias for CLI login |
|
|
89
|
+
| Compat | `codex-sw logout [account]` | Compatibility alias for CLI logout |
|
|
90
|
+
| Compat | `codex-sw use <account> [--sync\|--no-sync] [--launch\|--no-launch] [-- <codex args...>]` | Compatibility alias for CLI switch |
|
|
91
|
+
| Compat | `codex-sw switch <account> [--sync\|--no-sync] [--launch\|--no-launch] [-- <codex args...>]` | Same as `use` |
|
|
92
|
+
| Compat | `codex-sw add <account>` | Compatibility alias for `account add` |
|
|
93
|
+
| Compat | `codex-sw remove <account> [--force]` | Compatibility alias for `account remove` |
|
|
94
|
+
| Compat | `codex-sw import-default <env> [--with-auth] [--force]` | Import default env data into target env |
|
|
95
|
+
| App | `codex-sw app open [account] [-- <app args...>]` | Open Codex App under account |
|
|
96
|
+
| App | `codex-sw app use <account> [-- <app args...>]` | Switch App account (alias of open) |
|
|
97
|
+
| App | `codex-sw app logout [account]` | Logout App account |
|
|
98
|
+
| App | `codex-sw app status` | Show managed App process status |
|
|
99
|
+
| App | `codex-sw app stop` | Stop managed App process |
|
|
100
|
+
| App | `codex-sw app current` | Show current App env/account |
|
|
101
|
+
| Maintenance | `codex-sw init [--shell zsh\|bash] [--dry-run]` | Initialize PATH bootstrap |
|
|
102
|
+
| Maintenance | `codex-sw upgrade [--dry-run]` | Upgrade from npm |
|
|
103
|
+
| Maintenance | `codex-sw recover [--dry-run]` | Recover corrupted pointers |
|
|
104
|
+
| Maintenance | `codex-sw version` | Print current tool version |
|
|
105
|
+
| Maintenance | `codex-sw check` | Basic health checks |
|
|
106
|
+
| Maintenance | `codex-sw doctor [--fix]` | Deep diagnostics and optional auto-fix |
|
|
107
|
+
| Maintenance | `codex-sw --help` | Show full help |
|
|
108
|
+
|
|
109
|
+
Plugin-level docs:
|
|
67
110
|
- Chinese: `plugins/codex-switcher/README.md`
|
|
68
111
|
- English: `plugins/codex-switcher/README.en.md`
|
|
69
112
|
|
package/README.md
CHANGED
|
@@ -60,7 +60,50 @@ codex-switcher account use corp --env project-a
|
|
|
60
60
|
|
|
61
61
|
## 命令参考
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
以下以 `codex-sw` 为例(`codex-switcher` 为等价兼容命令):
|
|
64
|
+
|
|
65
|
+
| 分类 | 命令 | 说明 |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| Env 管理 | `codex-sw env list` | 列出所有 env,并标记 CLI/App 当前 env |
|
|
68
|
+
| Env 管理 | `codex-sw env create <env> [--empty\|--from-default\|--from-env <src>]` | 创建 env(空目录或从已有 env 同步数据) |
|
|
69
|
+
| Env 管理 | `codex-sw env use <env> [--target cli\|app\|both]` | 切换 CLI/App 使用的 env |
|
|
70
|
+
| Env 管理 | `codex-sw env remove <env> [--force]` | 删除 env(必要时强制) |
|
|
71
|
+
| Env 管理 | `codex-sw env current [cli\|app]` | 查看当前 env 指针 |
|
|
72
|
+
| Env 管理 | `codex-sw env path [env]` | 输出 env 对应 `CODEX_HOME` 导出语句 |
|
|
73
|
+
| 账号管理 | `codex-sw account list [--env <env>]` | 列出 env 下账号并标记当前账号 |
|
|
74
|
+
| 账号管理 | `codex-sw account add <account> [--env <env>]` | 创建账号槽位(不登录) |
|
|
75
|
+
| 账号管理 | `codex-sw account remove <account> [--env <env>] [--force]` | 删除账号槽位 |
|
|
76
|
+
| 账号管理 | `codex-sw account login <account> [--env <env>] [--target cli\|app\|both] [--sync\|--no-sync]` | 在目标 env 登录并保存账号 `auth.json` |
|
|
77
|
+
| 账号管理 | `codex-sw account use <account> [--env <env>] [--target cli\|app\|both] [--sync\|--no-sync]` | 切换到目标账号 |
|
|
78
|
+
| 账号管理 | `codex-sw account logout [account] [--env <env>] [--target cli\|app\|both]` | 注销账号(删除对应 auth) |
|
|
79
|
+
| 账号管理 | `codex-sw account current [cli\|app]` | 查看当前 env/account 指针 |
|
|
80
|
+
| 用量代理 | `codex-sw proxy [<host:port>\|off\|test]` | 设置/关闭/测试“用量 API”代理(仅影响 `list`) |
|
|
81
|
+
| 查询执行 | `codex-sw list` | 展示 `ENV/HOME/ACCOUNT/EMAIL/PLAN/5H/WEEKLY/LAST ACTIVITY/SOURCE`(时间为 `MM-DD HH:MM`) |
|
|
82
|
+
| 查询执行 | `codex-sw status` | 检查 CLI/App 当前登录状态 |
|
|
83
|
+
| 查询执行 | `codex-sw current [cli\|app]` | 查看当前 env/account |
|
|
84
|
+
| 查询执行 | `codex-sw exec -- <codex args...>` | 在当前 CLI env/account 下执行 `codex` |
|
|
85
|
+
| 兼容命令 | `codex-sw login [account] [--sync\|--no-sync]` | 登录当前 CLI 账号(兼容入口) |
|
|
86
|
+
| 兼容命令 | `codex-sw logout [account]` | 注销当前 CLI 账号(兼容入口) |
|
|
87
|
+
| 兼容命令 | `codex-sw use <account> [--sync\|--no-sync] [--launch\|--no-launch] [-- <codex args...>]` | 切换 CLI 账号(兼容入口) |
|
|
88
|
+
| 兼容命令 | `codex-sw switch <account> [--sync\|--no-sync] [--launch\|--no-launch] [-- <codex args...>]` | `use` 的等价命令 |
|
|
89
|
+
| 兼容命令 | `codex-sw add <account>` | `account add` 的兼容入口 |
|
|
90
|
+
| 兼容命令 | `codex-sw remove <account> [--force]` | `account remove` 的兼容入口 |
|
|
91
|
+
| 兼容命令 | `codex-sw import-default <env> [--with-auth] [--force]` | 从默认 env 导入数据到指定 env |
|
|
92
|
+
| App 管理 | `codex-sw app open [account] [-- <app args...>]` | 以指定账号打开 Codex App |
|
|
93
|
+
| App 管理 | `codex-sw app use <account> [-- <app args...>]` | 切换 App 到指定账号(内部等价于 open) |
|
|
94
|
+
| App 管理 | `codex-sw app logout [account]` | 注销 App 当前账号 |
|
|
95
|
+
| App 管理 | `codex-sw app status` | 查看 App 管理进程状态 |
|
|
96
|
+
| App 管理 | `codex-sw app stop` | 停止由工具托管启动的 App 进程 |
|
|
97
|
+
| App 管理 | `codex-sw app current` | 查看 App 当前 env/account |
|
|
98
|
+
| 维护命令 | `codex-sw init [--shell zsh\|bash] [--dry-run]` | 初始化 PATH 快捷命令 |
|
|
99
|
+
| 维护命令 | `codex-sw upgrade [--dry-run]` | 升级到最新 npm 版本 |
|
|
100
|
+
| 维护命令 | `codex-sw recover [--dry-run]` | 自动恢复损坏指针 |
|
|
101
|
+
| 维护命令 | `codex-sw version` | 输出当前工具版本号 |
|
|
102
|
+
| 维护命令 | `codex-sw check` | 基础健康检查 |
|
|
103
|
+
| 维护命令 | `codex-sw doctor [--fix]` | 深度检查并可选自动修复 |
|
|
104
|
+
| 维护命令 | `codex-sw --help` | 查看完整帮助 |
|
|
105
|
+
|
|
106
|
+
插件侧详细说明:
|
|
64
107
|
|
|
65
108
|
- 中文:`plugins/codex-switcher/README.md`
|
|
66
109
|
- English:`plugins/codex-switcher/README.en.md`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wangxt0223/codex-switcher",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Env + account switcher for Codex CLI and Codex App with shared env data and per-account auth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "wangxt",
|
|
@@ -43,8 +43,5 @@
|
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public",
|
|
45
45
|
"registry": "https://registry.npmjs.org/"
|
|
46
|
-
},
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"@wangxt0223/codex-switcher": "^0.6.0"
|
|
49
46
|
}
|
|
50
47
|
}
|
|
@@ -49,6 +49,8 @@ codex-switcher app logout [account]
|
|
|
49
49
|
codex-switcher app status
|
|
50
50
|
codex-switcher app stop
|
|
51
51
|
codex-switcher app current
|
|
52
|
+
|
|
53
|
+
codex-switcher version
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
## Typical Flow
|
|
@@ -72,13 +74,15 @@ codex-switcher account use corp --env project-a
|
|
|
72
74
|
|
|
73
75
|
`codex-switcher list` prints:
|
|
74
76
|
|
|
75
|
-
`ENV / ACCOUNT / EMAIL / PLAN / 5H USAGE / WEEKLY USAGE / LAST ACTIVITY`
|
|
77
|
+
`ENV / HOME / ACCOUNT / EMAIL / PLAN / 5H USAGE / WEEKLY USAGE / LAST ACTIVITY / SOURCE`
|
|
76
78
|
|
|
77
79
|
Usage data strategy:
|
|
78
80
|
|
|
79
81
|
- API first (`chatgpt.com/backend-api/wham/usage`)
|
|
80
82
|
- Auto fallback to local `sessions/*.jsonl` on API failure
|
|
81
|
-
- `
|
|
83
|
+
- `5H USAGE` / `WEEKLY USAGE` reset time is unified to `MM-DD HH:MM` (example: `89% (04-19 11:45)`)
|
|
84
|
+
- `LAST ACTIVITY` uses absolute `MM-DD HH:MM` format
|
|
85
|
+
- `SOURCE` is shown as a dedicated column (`api` or `local`)
|
|
82
86
|
- You can configure a dedicated proxy for this usage API via `codex-switcher proxy 127.0.0.1:7899` (only affects `list`)
|
|
83
87
|
- If no manual proxy is configured, env/system proxy settings are auto-detected for usage API requests
|
|
84
88
|
|
|
@@ -49,6 +49,8 @@ codex-switcher app logout [account]
|
|
|
49
49
|
codex-switcher app status
|
|
50
50
|
codex-switcher app stop
|
|
51
51
|
codex-switcher app current
|
|
52
|
+
|
|
53
|
+
codex-switcher version
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
## 典型流程
|
|
@@ -72,13 +74,15 @@ codex-switcher account use corp --env project-a
|
|
|
72
74
|
|
|
73
75
|
`codex-switcher list` 默认输出:
|
|
74
76
|
|
|
75
|
-
`ENV / ACCOUNT / EMAIL / PLAN / 5H USAGE / WEEKLY USAGE / LAST ACTIVITY`
|
|
77
|
+
`ENV / HOME / ACCOUNT / EMAIL / PLAN / 5H USAGE / WEEKLY USAGE / LAST ACTIVITY / SOURCE`
|
|
76
78
|
|
|
77
79
|
其中用量数据策略为:
|
|
78
80
|
|
|
79
81
|
- 默认优先 API(`chatgpt.com/backend-api/wham/usage`)
|
|
80
82
|
- API 失败自动回退本地 `sessions/*.jsonl`
|
|
81
|
-
- `
|
|
83
|
+
- `5H USAGE` / `WEEKLY USAGE` 的重置时间统一为 `MM-DD HH:MM`(例如 `89% (04-19 11:45)`)
|
|
84
|
+
- `LAST ACTIVITY` 使用绝对时间 `MM-DD HH:MM`
|
|
85
|
+
- `SOURCE` 独立显示 `api` 或 `local`
|
|
82
86
|
- 可通过 `codex-switcher proxy 127.0.0.1:7899` 为该用量 API 单独配置代理(仅影响 `list`)
|
|
83
87
|
- 未手动设置时会自动检测环境变量/系统代理并用于用量 API 请求
|
|
84
88
|
|
|
@@ -75,6 +75,7 @@ Usage:
|
|
|
75
75
|
codex-sw init [--shell zsh|bash] [--dry-run]
|
|
76
76
|
codex-sw upgrade [--dry-run]
|
|
77
77
|
codex-sw recover [--dry-run]
|
|
78
|
+
codex-sw version
|
|
78
79
|
codex-sw check
|
|
79
80
|
codex-sw doctor [--fix]
|
|
80
81
|
|
|
@@ -86,7 +87,7 @@ Notes:
|
|
|
86
87
|
- Same-env account switch only swaps auth.json and does not sync shared data.
|
|
87
88
|
- `proxy` only affects usage API calls used by `list`.
|
|
88
89
|
- If manual proxy is not set, usage API proxy is auto-detected from env/system proxy settings.
|
|
89
|
-
- `list` prints: EMAIL / PLAN / 5H USAGE / WEEKLY USAGE / LAST ACTIVITY
|
|
90
|
+
- `list` prints: ENV / HOME / ACCOUNT / EMAIL / PLAN / 5H USAGE / WEEKLY USAGE / LAST ACTIVITY / SOURCE.
|
|
90
91
|
- Usage metrics are API-first and automatically fallback to local sessions; source is shown as (api|local).
|
|
91
92
|
USAGE
|
|
92
93
|
}
|
|
@@ -314,6 +315,19 @@ clear_usage_proxy() {
|
|
|
314
315
|
rm -f -- "$USAGE_PROXY_FILE"
|
|
315
316
|
}
|
|
316
317
|
|
|
318
|
+
display_path_for_output() {
|
|
319
|
+
local path="$1"
|
|
320
|
+
if [[ "$path" == "$HOME" ]]; then
|
|
321
|
+
echo "~"
|
|
322
|
+
return
|
|
323
|
+
fi
|
|
324
|
+
if [[ "$path" == "$HOME/"* ]]; then
|
|
325
|
+
echo "~/${path#$HOME/}"
|
|
326
|
+
return
|
|
327
|
+
fi
|
|
328
|
+
echo "$path"
|
|
329
|
+
}
|
|
330
|
+
|
|
317
331
|
set_current_env() {
|
|
318
332
|
local target="$1"
|
|
319
333
|
local env="$2"
|
|
@@ -1132,9 +1146,9 @@ PY
|
|
|
1132
1146
|
|
|
1133
1147
|
cmd_list() {
|
|
1134
1148
|
local cli_env app_env cli_account app_account
|
|
1135
|
-
local env account marks auth_file home metrics
|
|
1149
|
+
local env account marks auth_file home home_display metrics
|
|
1136
1150
|
local email plan usage_5h usage_weekly last_activity source
|
|
1137
|
-
local usage_proxy="" usage_info=""
|
|
1151
|
+
local usage_proxy="" usage_info=""
|
|
1138
1152
|
|
|
1139
1153
|
cli_env="$(read_current_env cli || true)"
|
|
1140
1154
|
app_env="$(read_current_env app || true)"
|
|
@@ -1142,12 +1156,13 @@ cmd_list() {
|
|
|
1142
1156
|
app_account="$(read_current_account app || true)"
|
|
1143
1157
|
usage_info="$(resolve_usage_proxy_with_source || true)"
|
|
1144
1158
|
if [[ -n "$usage_info" ]]; then
|
|
1145
|
-
IFS=$'\t' read -r
|
|
1159
|
+
IFS=$'\t' read -r _ usage_proxy <<< "$usage_info"
|
|
1146
1160
|
fi
|
|
1147
1161
|
|
|
1148
|
-
printf '%-12s %-30s %-
|
|
1162
|
+
printf '%-12s %-28s %-30s %-38s %-10s %-20s %-20s %-16s %s\n' "ENV" "HOME" "ACCOUNT" "EMAIL" "PLAN" "5H USAGE" "WEEKLY USAGE" "LAST ACTIVITY" "SOURCE"
|
|
1149
1163
|
while IFS= read -r env; do
|
|
1150
1164
|
home="$(env_home_path "$env")"
|
|
1165
|
+
home_display="$(display_path_for_output "$home")"
|
|
1151
1166
|
ensure_env_home "$env"
|
|
1152
1167
|
local found=0
|
|
1153
1168
|
while IFS= read -r account; do
|
|
@@ -1175,9 +1190,8 @@ cmd_list() {
|
|
|
1175
1190
|
last_activity="${last_activity:--}"
|
|
1176
1191
|
source="${source:-local}"
|
|
1177
1192
|
[[ "$source" == "api" || "$source" == "local" ]] || source="local"
|
|
1178
|
-
last_activity="${last_activity} (${source})"
|
|
1179
1193
|
|
|
1180
|
-
printf '%-12s %-30s %-
|
|
1194
|
+
printf '%-12s %-28s %-30s %-38s %-10s %-20s %-20s %-16s %s\n' "$env" "$home_display" "$account$marks" "$email" "$plan" "$usage_5h" "$usage_weekly" "$last_activity" "$source"
|
|
1181
1195
|
done < <(list_accounts_for_env "$env")
|
|
1182
1196
|
|
|
1183
1197
|
if [[ "$found" -eq 0 ]]; then
|
|
@@ -1198,8 +1212,7 @@ cmd_list() {
|
|
|
1198
1212
|
last_activity="${last_activity:--}"
|
|
1199
1213
|
source="${source:-local}"
|
|
1200
1214
|
[[ "$source" == "api" || "$source" == "local" ]] || source="local"
|
|
1201
|
-
|
|
1202
|
-
printf '%-12s %-30s %-44s %-10s %-16s %-18s %s\n' "$env" "-" "$email" "$plan" "$usage_5h" "$usage_weekly" "$last_activity"
|
|
1215
|
+
printf '%-12s %-28s %-30s %-38s %-10s %-20s %-20s %-16s %s\n' "$env" "$home_display" "-" "$email" "$plan" "$usage_5h" "$usage_weekly" "$last_activity" "$source"
|
|
1203
1216
|
fi
|
|
1204
1217
|
done < <(list_env_names | sort -u)
|
|
1205
1218
|
}
|
|
@@ -1605,6 +1618,10 @@ cmd_check() {
|
|
|
1605
1618
|
echo "check: ok"
|
|
1606
1619
|
}
|
|
1607
1620
|
|
|
1621
|
+
cmd_version() {
|
|
1622
|
+
echo "$(switcher_version)"
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1608
1625
|
cmd_doctor() {
|
|
1609
1626
|
local fix="${1:-false}"
|
|
1610
1627
|
local issues=0
|
|
@@ -2178,6 +2195,10 @@ main() {
|
|
|
2178
2195
|
fi
|
|
2179
2196
|
with_lock cmd_recover "$dry"
|
|
2180
2197
|
;;
|
|
2198
|
+
version)
|
|
2199
|
+
[[ "$#" -eq 0 ]] || err "usage: $SCRIPT_NAME version"
|
|
2200
|
+
cmd_version
|
|
2201
|
+
;;
|
|
2181
2202
|
check)
|
|
2182
2203
|
[[ "$#" -eq 0 ]] || err "usage: $SCRIPT_NAME check"
|
|
2183
2204
|
cmd_check
|
|
@@ -6,7 +6,6 @@ import json
|
|
|
6
6
|
import os
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
|
-
import time
|
|
10
9
|
from typing import Any, Dict, Optional
|
|
11
10
|
|
|
12
11
|
|
|
@@ -223,22 +222,15 @@ def format_usage(window: Optional[Dict[str, Any]]) -> str:
|
|
|
223
222
|
percent = clamp_percent(float(window["remaining_percent"]))
|
|
224
223
|
reset_epoch = window.get("reset_epoch")
|
|
225
224
|
if isinstance(reset_epoch, int):
|
|
226
|
-
reset_text = dt.datetime.fromtimestamp(reset_epoch).strftime("%H:%M")
|
|
225
|
+
reset_text = dt.datetime.fromtimestamp(reset_epoch).strftime("%m-%d %H:%M")
|
|
227
226
|
return f"{percent}% ({reset_text})"
|
|
228
227
|
return f"{percent}%"
|
|
229
228
|
|
|
230
229
|
|
|
231
|
-
def
|
|
230
|
+
def format_last_activity(last_epoch: Optional[int]) -> str:
|
|
232
231
|
if not isinstance(last_epoch, int):
|
|
233
232
|
return DASH
|
|
234
|
-
|
|
235
|
-
if delta < 60:
|
|
236
|
-
return "just now"
|
|
237
|
-
if delta < 3600:
|
|
238
|
-
return f"{delta // 60}m ago"
|
|
239
|
-
if delta < 86400:
|
|
240
|
-
return f"{delta // 3600}h ago"
|
|
241
|
-
return f"{delta // 86400}d ago"
|
|
233
|
+
return dt.datetime.fromtimestamp(last_epoch).strftime("%m-%d %H:%M")
|
|
242
234
|
|
|
243
235
|
|
|
244
236
|
def collect_local_metrics(data_path: str) -> Dict[str, Any]:
|
|
@@ -326,7 +318,6 @@ def collect_api_metrics(access_token: str, account_id: str, usage_proxy: str, ti
|
|
|
326
318
|
|
|
327
319
|
def main() -> int:
|
|
328
320
|
args = parse_args()
|
|
329
|
-
now_epoch = int(time.time())
|
|
330
321
|
auth_data = load_json(args.auth_file)
|
|
331
322
|
tokens = auth_data.get("tokens")
|
|
332
323
|
if not isinstance(tokens, dict):
|
|
@@ -352,7 +343,8 @@ def main() -> int:
|
|
|
352
343
|
plan_from_claims = normalize_plan(auth_data.get("chatgpt_plan_type") or auth_data.get("plan_type"))
|
|
353
344
|
|
|
354
345
|
api_metrics = collect_api_metrics(access_token, account_id, args.usage_proxy, args.timeout_seconds)
|
|
355
|
-
|
|
346
|
+
local_metrics = collect_local_metrics(args.data_path)
|
|
347
|
+
metrics = api_metrics if api_metrics is not None else local_metrics
|
|
356
348
|
|
|
357
349
|
windows = metrics.get("windows")
|
|
358
350
|
if not isinstance(windows, dict):
|
|
@@ -374,13 +366,17 @@ def main() -> int:
|
|
|
374
366
|
|
|
375
367
|
last_activity_epoch = metrics.get("last_activity_epoch")
|
|
376
368
|
if not isinstance(last_activity_epoch, int):
|
|
377
|
-
|
|
378
|
-
|
|
369
|
+
fallback_last_activity = local_metrics.get("last_activity_epoch")
|
|
370
|
+
if isinstance(fallback_last_activity, int):
|
|
371
|
+
last_activity_epoch = fallback_last_activity
|
|
372
|
+
else:
|
|
373
|
+
last_activity_epoch = None
|
|
374
|
+
last_activity = format_last_activity(last_activity_epoch)
|
|
379
375
|
|
|
380
376
|
if email:
|
|
381
|
-
display_email =
|
|
377
|
+
display_email = email
|
|
382
378
|
else:
|
|
383
|
-
display_email =
|
|
379
|
+
display_email = "-"
|
|
384
380
|
|
|
385
381
|
out_fields = [
|
|
386
382
|
display_email,
|
|
@@ -99,9 +99,11 @@ echo '{"memo":"persist"}' > "$DEFAULT_HOME/shared.json"
|
|
|
99
99
|
check_out="$("$SW" check)"
|
|
100
100
|
echo "$check_out" | grep -Eq '^version: [0-9]+\.[0-9]+\.[0-9]+$'
|
|
101
101
|
echo "$check_out" | grep -q "check: ok"
|
|
102
|
+
echo "$("$SW" version)" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'
|
|
102
103
|
link_check_out="$("$SW_LINK" check)"
|
|
103
104
|
echo "$link_check_out" | grep -Eq '^version: [0-9]+\.[0-9]+\.[0-9]+$'
|
|
104
105
|
echo "$link_check_out" | grep -q "check: ok"
|
|
106
|
+
echo "$("$SW_LINK" version)" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'
|
|
105
107
|
[[ "$("$SW" proxy)" == "usage_api_proxy: off" ]]
|
|
106
108
|
"$SW" proxy 127.0.0.1:7899
|
|
107
109
|
[[ "$("$SW" proxy)" == "usage_api_proxy: http://127.0.0.1:7899 (manual)" ]]
|
|
@@ -174,21 +176,25 @@ grep -q '{"shared":"project"}' "$ENVS/project/home/shared.json"
|
|
|
174
176
|
|
|
175
177
|
"$SW" list >/tmp/codex_sw_list_api
|
|
176
178
|
grep -q "ENV" /tmp/codex_sw_list_api
|
|
179
|
+
grep -q "HOME" /tmp/codex_sw_list_api
|
|
177
180
|
grep -q "ACCOUNT" /tmp/codex_sw_list_api
|
|
178
181
|
grep -q "EMAIL" /tmp/codex_sw_list_api
|
|
179
182
|
grep -q "PLAN" /tmp/codex_sw_list_api
|
|
180
183
|
grep -q "5H USAGE" /tmp/codex_sw_list_api
|
|
181
184
|
grep -q "WEEKLY USAGE" /tmp/codex_sw_list_api
|
|
182
185
|
grep -q "LAST ACTIVITY" /tmp/codex_sw_list_api
|
|
183
|
-
grep -q "
|
|
184
|
-
grep -q "
|
|
186
|
+
grep -q "SOURCE" /tmp/codex_sw_list_api
|
|
187
|
+
grep -q "personal@example.com" /tmp/codex_sw_list_api
|
|
188
|
+
grep -Eq "[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}[[:space:]]+api$" /tmp/codex_sw_list_api
|
|
185
189
|
grep -q "60% (" /tmp/codex_sw_list_api
|
|
186
190
|
grep -q "80% (" /tmp/codex_sw_list_api
|
|
191
|
+
grep -Eq "60% \\([0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}\\)" /tmp/codex_sw_list_api
|
|
192
|
+
grep -Eq "80% \\([0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}\\)" /tmp/codex_sw_list_api
|
|
187
193
|
grep -q "proxy=http://127.0.0.1:7899" "$CODEX_SWITCHER_TEST_CURL_LOG"
|
|
188
194
|
|
|
189
195
|
"$SW_LINK" list >/tmp/codex_sw_list_symlink
|
|
190
|
-
grep -q "
|
|
191
|
-
grep -
|
|
196
|
+
grep -q "personal@example.com" /tmp/codex_sw_list_symlink
|
|
197
|
+
grep -Eq "[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}[[:space:]]+api$" /tmp/codex_sw_list_symlink
|
|
192
198
|
|
|
193
199
|
mkdir -p "$ENVS/project/home/sessions/2026/04/12"
|
|
194
200
|
cat > "$ENVS/project/home/sessions/2026/04/12/rollout-test.jsonl" <<'JSONL'
|
|
@@ -199,9 +205,11 @@ export CODEX_SWITCHER_TEST_CURL_MODE="fail"
|
|
|
199
205
|
[[ "$("$SW" proxy)" == "usage_api_proxy: off" ]]
|
|
200
206
|
: > "$CODEX_SWITCHER_TEST_CURL_LOG"
|
|
201
207
|
"$SW" list >/tmp/codex_sw_list_local
|
|
202
|
-
grep -
|
|
208
|
+
grep -Eq "[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}[[:space:]]+local$" /tmp/codex_sw_list_local
|
|
203
209
|
grep -q "75% (" /tmp/codex_sw_list_local
|
|
204
210
|
grep -q "30% (" /tmp/codex_sw_list_local
|
|
211
|
+
grep -Eq "75% \\([0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}\\)" /tmp/codex_sw_list_local
|
|
212
|
+
grep -Eq "30% \\([0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}\\)" /tmp/codex_sw_list_local
|
|
205
213
|
grep -q "^proxy=$" "$CODEX_SWITCHER_TEST_CURL_LOG"
|
|
206
214
|
|
|
207
215
|
export CODEX_SWITCHER_TEST_CURL_MODE="success"
|