@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 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
- See plugin guide:
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.2",
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
- - `LAST ACTIVITY` appends source marker `(api)` or `(local)`
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
- - `LAST ACTIVITY` 列会追加 `(api)` `(local)` 标记来源
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 (+ env/account context).
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="" usage_proxy_source=""
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 usage_proxy_source usage_proxy <<< "$usage_info"
1159
+ IFS=$'\t' read -r _ usage_proxy <<< "$usage_info"
1146
1160
  fi
1147
1161
 
1148
- printf '%-12s %-30s %-44s %-10s %-16s %-18s %s\n' "ENV" "ACCOUNT" "EMAIL" "PLAN" "5H USAGE" "WEEKLY USAGE" "LAST ACTIVITY"
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 %-44s %-10s %-16s %-18s %s\n' "$env" "$account$marks" "$email" "$plan" "$usage_5h" "$usage_weekly" "$last_activity"
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
- last_activity="${last_activity} (${source})"
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 format_relative(last_epoch: Optional[int], now_epoch: int) -> str:
230
+ def format_last_activity(last_epoch: Optional[int]) -> str:
232
231
  if not isinstance(last_epoch, int):
233
232
  return DASH
234
- delta = max(0, now_epoch - last_epoch)
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
- metrics = api_metrics if api_metrics is not None else collect_local_metrics(args.data_path)
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
- last_activity_epoch = None
378
- last_activity = format_relative(last_activity_epoch, now_epoch)
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 = f"({args.account_name}){email}"
377
+ display_email = email
382
378
  else:
383
- display_email = f"({args.account_name})-"
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 "(personal)personal@example.com" /tmp/codex_sw_list_api
184
- grep -q "(api)" /tmp/codex_sw_list_api
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 "(personal)personal@example.com" /tmp/codex_sw_list_symlink
191
- grep -q "(api)" /tmp/codex_sw_list_symlink
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 -q "(local)" /tmp/codex_sw_list_local
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"