可以试一下下面的脚本
#!/bin/bash
set -euo pipefail
APP_PATH="${CODEX_APP_PATH:-/Applications/Codex.app}"
RESOURCES_DIR="$APP_PATH/Contents/Resources"
APP_ASAR="$RESOURCES_DIR/app.asar"
APP_ASAR_BACKUP="$RESOURCES_DIR/app.asar.fastmode.bak"
UNPACKED_DIR="$RESOURCES_DIR/app"
red() { printf '\033[31m%s\033[0m\n' "$*"; }
green() { printf '\033[32m%s\033[0m\n' "$*"; }
yellow() { printf '\033[33m%s\033[0m\n' "$*"; }
blue() { printf '\033[36m%s\033[0m\n' "$*"; }
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
red "缺少依赖命令: $1"
exit 1
fi
}
confirm() {
local prompt="${1:-确认继续?}"
local answer
read -r -p "$prompt [y/N]: " answer
[[ "$answer" =~ ^([yY][eE][sS]|[yY])$ ]]
}
is_codex_running() {
pgrep -x "Codex" >/dev/null 2>&1
}
restart_app() {
require_cmd open
require_cmd pgrep
require_cmd sleep
if [[ ! -d "$APP_PATH" ]]; then
red "未找到 Codex.app,请通过环境变量 CODEX_APP_PATH 指定路径"
exit 1
fi
if is_codex_running; then
blue "正在关闭 Codex.app"
osascript -e 'tell application "Codex" to quit' >/dev/null 2>&1 || true
local waited=0
while is_codex_running && [[ "$waited" -lt 15 ]]; do
sleep 1
waited=$((waited + 1))
done
if is_codex_running; then
yellow "Codex.app 未在预期时间内退出,将继续尝试重新打开"
fi
fi
blue "正在启动 Codex.app"
open "$APP_PATH"
green "已触发 Codex.app 重启"
}
find_target_js() {
find "$UNPACKED_DIR/webview/assets" -type f -name 'general-settings*.js' 2>/dev/null | head -n 1
}
is_patched_file() {
local target_file="${1:-}"
[[ -n "$target_file" ]] || return 1
grep -Eq ',true\}|if\(!1\)return null;|if\(false\)return null;' "$target_file" 2>/dev/null
}
print_paths() {
blue "应用路径: $APP_PATH"
blue "资源目录: $RESOURCES_DIR"
blue "备份路径: $APP_ASAR_BACKUP"
}
status() {
print_paths
if [[ ! -d "$APP_PATH" ]]; then
red "未找到 Codex.app"
return 1
fi
if [[ -f "$APP_ASAR_BACKUP" ]]; then
green "已检测到原始 app.asar 备份"
else
yellow "未检测到原始 app.asar 备份"
fi
if [[ -d "$UNPACKED_DIR" ]]; then
green "已检测到解包目录: $UNPACKED_DIR"
else
yellow "未检测到解包目录"
fi
local target_js
target_js="$(find_target_js || true)"
if [[ -n "$target_js" ]]; then
blue "配置文件: $target_js"
if is_patched_file "$target_js"; then
green "fast 模式补丁: 已应用"
else
yellow "fast 模式补丁: 未应用"
fi
else
yellow "未找到 general-settings*.js,可能尚未解包或版本结构已变化"
fi
}
patch_js_file() {
local target_file="$1"
if is_patched_file "$target_file"; then
yellow "目标文件已处于补丁状态,跳过 JS 修改"
return 0
fi
# 兼容旧版帖子中的布尔判断写法。
perl -0pi -e 's/:r=e\[2\],r\}/:r=e[2],true\}/g' "$target_file"
if is_patched_file "$target_file"; then
green "已命中旧版 fast 模式补丁规则"
return 0
fi
# 兼容当前版本的功能门控写法:直接去掉 Speed 设置项的提前返回。
perl -0pi -e 's/if\(!n\)return null;/if(!1)return null;/g' "$target_file"
if is_patched_file "$target_file"; then
green "已命中当前版本的 fast 模式补丁规则"
return 0
fi
red "JS 补丁未生效,当前版本的代码结构可能已变化"
exit 1
}
write_fuses() {
require_cmd npx
local fuse
for fuse in \
GrantFileProtocolExtraPrivileges=off \
EnableCookieEncryption=off \
OnlyLoadAppFromAsar=off \
EnableEmbeddedAsarIntegrityValidation=off
do
blue "写入 fuse: $fuse"
npx @electron/fuses write --app "$APP_PATH" "$fuse"
done
}
resign_app() {
require_cmd codesign
blue "重新签名 Codex.app"
codesign --force --deep --sign - "$APP_PATH"
}
apply_patch_flow() {
print_paths
require_cmd find
require_cmd perl
require_cmd mv
require_cmd rm
if [[ ! -d "$APP_PATH" ]]; then
red "未找到 Codex.app,请通过环境变量 CODEX_APP_PATH 指定路径"
exit 1
fi
if [[ ! -d "$RESOURCES_DIR" ]]; then
red "未找到资源目录: $RESOURCES_DIR"
exit 1
fi
if [[ ! -d "$UNPACKED_DIR" ]]; then
if [[ ! -f "$APP_ASAR" ]]; then
red "未找到 app.asar,无法自动解包"
exit 1
fi
require_cmd npx
blue "开始解包 app.asar"
npx @electron/asar extract "$APP_ASAR" "$UNPACKED_DIR"
fi
if [[ -f "$APP_ASAR" && ! -f "$APP_ASAR_BACKUP" ]]; then
blue "创建原始包备份"
mv "$APP_ASAR" "$APP_ASAR_BACKUP"
fi
local target_js
target_js="$(find_target_js || true)"
if [[ -z "$target_js" ]]; then
red "未找到 general-settings*.js,当前版本可能不兼容"
exit 1
fi
blue "应用 JS 补丁: $target_js"
patch_js_file "$target_js"
write_fuses
resign_app
green "补丁已完成。重新启动 Codex.app 后,可在设置页检查是否出现 fast 选项。"
}
restore() {
print_paths
require_cmd rm
require_cmd mv
if [[ ! -f "$APP_ASAR_BACKUP" ]]; then
red "未找到备份文件,无法恢复"
exit 1
fi
if [[ -d "$UNPACKED_DIR" ]]; then
blue "删除解包目录"
rm -rf "$UNPACKED_DIR"
fi
if [[ -f "$APP_ASAR" ]]; then
blue "删除当前 app.asar"
rm -f "$APP_ASAR"
fi
blue "恢复原始 app.asar"
mv "$APP_ASAR_BACKUP" "$APP_ASAR"
resign_app
green "已恢复原始包。"
}
interactive_menu() {
print_paths
printf '\n'
echo "请选择操作:"
echo " 1) 查看状态"
echo " 2) 应用 fast 模式补丁"
echo " 3) 恢复原始文件"
echo " 4) 立即重启 Codex.app"
echo " 5) 退出"
printf '\n'
local choice
read -r -p "输入序号: " choice
case "$choice" in
1) status ;;
2)
confirm "即将修改 Codex.app,并重签名,是否继续?" || exit 0
apply_patch_flow
confirm "补丁完成,是否立即自动重启 Codex.app?" && restart_app
;;
3)
confirm "即将恢复原始 app.asar,是否继续?" || exit 0
restore
confirm "恢复完成,是否立即自动重启 Codex.app?" && restart_app
;;
4) restart_app ;;
5) exit 0 ;;
*) red "无效选项"; exit 1 ;;
esac
}
main() {
case "${1:-interactive}" in
status) status ;;
apply) apply_patch_flow ;;
restore) restore ;;
restart) restart_app ;;
interactive) interactive_menu ;;
*)
echo "用法:"
echo " $0 # 交互模式"
echo " $0 status # 查看状态"
echo " $0 apply # 应用补丁"
echo " $0 restore # 恢复原始包"
echo " $0 restart # 重启 Codex.app"
echo
echo "可选环境变量:"
echo " CODEX_APP_PATH=/Applications/Codex.app"
exit 1
;;
esac
}
main "$@"