codex-plusplus-tweak-system
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCodex++ Tweak System
Codex++ Tweak 系统
Skill by ara.so — Codex Skills collection.
Codex++ is a tweak system for the Codex desktop app that lets you inject custom features, fix UI bugs, and manage extensions without rebuilding the app. It patches the local Codex installation to load a runtime that discovers and executes small ESM modules (tweaks) with full access to the Electron renderer process.
由 ara.so 开发的Skill — Codex Skills 合集。
Codex++ 是 Codex 桌面应用的tweak系统,无需重新构建应用,即可注入自定义功能、修复UI漏洞并管理扩展。它会修补本地Codex安装包,加载一个运行时环境,用于发现并执行小型ESM模块(tweaks),这些模块拥有Electron渲染进程的完整访问权限。
Installation
安装
macOS/Linux (Homebrew)
macOS/Linux(Homebrew)
bash
brew install b-nnett/codex-plusplus/codexplusplus
codexplusplus installbash
brew install b-nnett/codex-plusplus/codexplusplus
codexplusplus installmacOS/Linux (Source Bootstrap)
macOS/Linux(源码引导)
bash
curl -fsSL https://raw.githubusercontent.com/b-nnett/codex-plusplus/main/install.sh | bashbash
curl -fsSL https://raw.githubusercontent.com/b-nnett/codex-plusplus/main/install.sh | bashWindows (PowerShell)
Windows(PowerShell)
powershell
irm https://raw.githubusercontent.com/b-nnett/codex-plusplus/main/install.ps1 | iexpowershell
irm https://raw.githubusercontent.com/b-nnett/codex-plusplus/main/install.ps1 | iexUsing Bun
使用 Bun
bash
bun install -g github:b-nnett/codex-plusplus
codexplusplus installWhat the installer does:
- Backs up Codex.app to
~/.codex-plusplus/backup/ - Patches to load the Codex++ runtime
app.asar - Re-signs the app with a local signing identity (macOS)
- Installs a watcher that auto-repairs on Codex updates
- Installs default tweaks from GitHub releases
Installation flags:
- — Skip installing default tweaks
--no-default-tweaks - — Use stable local signing identity (macOS)
--local
bash
bun install -g github:b-nnett/codex-plusplus
codexplusplus install安装器执行的操作:
- 将 Codex.app 备份至
~/.codex-plusplus/backup/ - 修补 以加载 Codex++ 运行时
app.asar - 使用本地签名身份重新签名应用(macOS)
- 安装监控程序,在Codex更新时自动修复
- 从GitHub版本库安装默认tweaks
安装参数:
- — 跳过安装默认tweaks
--no-default-tweaks - — 使用稳定的本地签名身份(macOS)
--local
Key Commands
核心命令
bash
undefinedbash
undefinedCheck installation status
检查安装状态
codexplusplus status
codexplusplus status
Validate and repair installation
验证并修复安装
codexplusplus doctor
codexplusplus repair
codexplusplus doctor
codexplusplus repair
Update Codex++ to latest release
将Codex++更新至最新版本
codexplusplus update
codexplusplus update
Update Codex++ from development branch (advanced)
从开发分支更新Codex++(高级功能)
codexplusplus update --ref main
codexplusplus update --ref main
Update Codex app itself (macOS - required for patched apps)
更新Codex应用本身(macOS - 修补后的应用需要执行此操作)
codexplusplus update-codex
codexplusplus update-codex
Create a new tweak from template
从模板创建新tweak
codexplusplus create-tweak my-tweak
codexplusplus create-tweak my-tweak
Validate tweak manifest and structure
验证tweak的清单文件与结构
codexplusplus validate-tweak ~/path/to/tweak
codexplusplus validate-tweak ~/path/to/tweak
Enter safe mode (disable all tweaks)
进入安全模式(禁用所有tweaks)
codexplusplus safe-mode
codexplusplus safe-mode --off
codexplusplus safe-mode
codexplusplus safe-mode --off
Development mode (watch and reload)
开发模式(监控文件变化并自动重载)
codexplusplus dev
codexplusplus dev
Uninstall Codex++ completely
完全卸载Codex++
codexplusplus uninstall
undefinedcodexplusplus uninstall
undefinedUser Data Directories
用户数据目录
| OS | Location |
|---|---|
| macOS | |
| Linux | |
| Windows | |
Directory structure:
codex-plusplus/
├── runtime/ # Codex++ runtime code
├── tweaks/ # Installed tweaks
│ ├── my-tweak/
│ │ ├── manifest.json
│ │ └── index.js
├── config.json # Runtime configuration
└── backup/ # Codex.app backup| 操作系统 | 路径 |
|---|---|
| macOS | |
| Linux | |
| Windows | |
目录结构:
codex-plusplus/
├── runtime/ # Codex++ 运行时代码
├── tweaks/ # 已安装的tweaks
│ ├── my-tweak/
│ │ ├── manifest.json
│ │ └── index.js
├── config.json # 运行时配置
└── backup/ # Codex.app 备份Creating a Tweak
创建Tweak
Minimal Tweak Structure
最简Tweak结构
my-tweak/
├── manifest.json
└── index.jsmy-tweak/
├── manifest.json
└── index.jsmanifest.json
manifest.json
json
{
"id": "com.example.my-tweak",
"name": "My Tweak",
"version": "1.0.0",
"githubRepo": "username/my-tweak",
"author": "Your Name",
"description": "Adds custom functionality to Codex",
"minRuntime": "0.1.0"
}Required fields:
- — Reverse-DNS unique identifier
id - — Display name
name - — Semver version
version - —
githubRepofor update checksowner/repo - — Minimum Codex++ runtime version
minRuntime
json
{
"id": "com.example.my-tweak",
"name": "My Tweak",
"version": "1.0.0",
"githubRepo": "username/my-tweak",
"author": "Your Name",
"description": "Adds custom functionality to Codex",
"minRuntime": "0.1.0"
}必填字段:
- — 反向DNS格式的唯一标识符
id - — 显示名称
name - — Semver版本号
version - — 用于更新检查的
githubRepo格式owner/repo - — 所需的最低Codex++运行时版本
minRuntime
Basic Tweak (JavaScript)
基础Tweak(JavaScript)
javascript
// index.js
export default {
start(api) {
api.log.info('Tweak started');
// Add settings panel
api.settings.register({
id: 'my-tweak',
title: 'My Tweak Settings',
render: (root) => {
root.innerHTML = `
<div>
<h3>Custom Settings</h3>
<button id="test-btn">Click me</button>
</div>
`;
root.querySelector('#test-btn').addEventListener('click', () => {
api.log.info('Button clicked!');
});
}
});
},
stop() {
// Cleanup resources
}
};javascript
// index.js
export default {
start(api) {
api.log.info('Tweak started');
// 添加设置面板
api.settings.register({
id: 'my-tweak',
title: 'My Tweak Settings',
render: (root) => {
root.innerHTML = `
<div>
<h3>Custom Settings</h3>
<button id="test-btn">Click me</button>
</div>
`;
root.querySelector('#test-btn').addEventListener('click', () => {
api.log.info('Button clicked!');
});
}
});
},
stop() {
// 清理资源
}
};TypeScript Tweak with Full API
完整API的TypeScript Tweak
typescript
// index.ts
import type { Tweak, CodexPlusPlusAPI } from "@codex-plusplus/sdk";
interface MyTweakConfig {
enabled: boolean;
customColor: string;
}
export default {
start(api: CodexPlusPlusAPI) {
// Access configuration
const config = api.config.get<MyTweakConfig>('my-tweak', {
enabled: true,
customColor: '#ff0000'
});
// Logging
api.log.info('Starting tweak with config:', config);
api.log.warn('This is a warning');
api.log.error('This is an error');
// DOM manipulation
const observer = new MutationObserver(() => {
const chatInput = document.querySelector('[contenteditable="true"]');
if (chatInput && config.enabled) {
chatInput.style.borderColor = config.customColor;
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Settings panel with persistence
api.settings.register({
id: 'my-tweak',
title: 'My Tweak',
render: (root) => {
root.innerHTML = `
<div class="tweak-settings">
<label>
<input type="checkbox" id="enabled" ${config.enabled ? 'checked' : ''}>
Enable custom styling
</label>
<label>
Custom color:
<input type="color" id="color" value="${config.customColor}">
</label>
<button id="save">Save</button>
</div>
`;
root.querySelector('#save')?.addEventListener('click', () => {
const enabled = (root.querySelector('#enabled') as HTMLInputElement).checked;
const customColor = (root.querySelector('#color') as HTMLInputElement).value;
api.config.set('my-tweak', { enabled, customColor });
api.log.info('Settings saved');
// Reload to apply changes
location.reload();
});
}
});
// Store observer for cleanup
(api as any)._observer = observer;
},
stop() {
const observer = (this as any)._observer;
if (observer) {
observer.disconnect();
}
}
} satisfies Tweak;typescript
// index.ts
import type { Tweak, CodexPlusPlusAPI } from "@codex-plusplus/sdk";
interface MyTweakConfig {
enabled: boolean;
customColor: string;
}
export default {
start(api: CodexPlusPlusAPI) {
// 获取配置
const config = api.config.get<MyTweakConfig>('my-tweak', {
enabled: true,
customColor: '#ff0000'
});
// 日志功能
api.log.info('Starting tweak with config:', config);
api.log.warn('This is a warning');
api.log.error('This is an error');
// DOM操作
const observer = new MutationObserver(() => {
const chatInput = document.querySelector('[contenteditable="true"]');
if (chatInput && config.enabled) {
chatInput.style.borderColor = config.customColor;
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// 带持久化的设置面板
api.settings.register({
id: 'my-tweak',
title: 'My Tweak',
render: (root) => {
root.innerHTML = `
<div class="tweak-settings">
<label>
<input type="checkbox" id="enabled" ${config.enabled ? 'checked' : ''}>
Enable custom styling
</label>
<label>
Custom color:
<input type="color" id="color" value="${config.customColor}">
</label>
<button id="save">Save</button>
</div>
`;
root.querySelector('#save')?.addEventListener('click', () => {
const enabled = (root.querySelector('#enabled') as HTMLInputElement).checked;
const customColor = (root.querySelector('#color') as HTMLInputElement).value;
api.config.set('my-tweak', { enabled, customColor });
api.log.info('Settings saved');
// 重载以应用更改
location.reload();
});
}
});
// 存储观察者用于清理
(api as any)._observer = observer;
},
stop() {
const observer = (this as any)._observer;
if (observer) {
observer.disconnect();
}
}
} satisfies Tweak;Advanced: Injecting Custom UI
进阶:注入自定义UI
typescript
export default {
start(api) {
// Wait for Codex UI to be ready
const injectCustomButton = () => {
const toolbar = document.querySelector('.chat-toolbar');
if (!toolbar) return;
const btn = document.createElement('button');
btn.textContent = '✨ Custom Action';
btn.className = 'toolbar-button';
btn.onclick = () => {
api.log.info('Custom action triggered');
// Your custom logic
};
toolbar.appendChild(btn);
};
// Observe for toolbar appearance
const observer = new MutationObserver(() => {
injectCustomButton();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
injectCustomButton(); // Try immediately
},
stop() {}
};typescript
export default {
start(api) {
// 等待Codex UI加载完成
const injectCustomButton = () => {
const toolbar = document.querySelector('.chat-toolbar');
if (!toolbar) return;
const btn = document.createElement('button');
btn.textContent = '✨ Custom Action';
btn.className = 'toolbar-button';
btn.onclick = () => {
api.log.info('Custom action triggered');
// 自定义逻辑
};
toolbar.appendChild(btn);
};
// 监控工具栏出现
const observer = new MutationObserver(() => {
injectCustomButton();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
injectCustomButton(); // 立即尝试注入
},
stop() {}
};Keyboard Shortcuts Tweak
键盘快捷键Tweak
typescript
export default {
start(api) {
const handleKeydown = (e: KeyboardEvent) => {
// Cmd/Ctrl + Shift + K
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'k') {
e.preventDefault();
api.log.info('Custom shortcut triggered');
// Your action here
}
};
document.addEventListener('keydown', handleKeydown);
// Store for cleanup
(api as any)._keyHandler = handleKeydown;
},
stop() {
const handler = (this as any)._keyHandler;
if (handler) {
document.removeEventListener('keydown', handler);
}
}
};typescript
export default {
start(api) {
const handleKeydown = (e: KeyboardEvent) => {
// Cmd/Ctrl + Shift + K
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'k') {
e.preventDefault();
api.log.info('Custom shortcut triggered');
// 执行操作
}
};
document.addEventListener('keydown', handleKeydown);
// 存储处理器用于清理
(api as any)._keyHandler = handleKeydown;
},
stop() {
const handler = (this as any)._keyHandler;
if (handler) {
document.removeEventListener('keydown', handler);
}
}
};Configuration
配置
Runtime Config
运行时配置
Located at :
<user-data-dir>/config.jsonjson
{
"enabledTweaks": [
"com.example.my-tweak",
"co.bennett.custom-keyboard-shortcuts"
],
"autoUpdate": true,
"safeMode": false,
"logLevel": "info"
}位于 :
<user-data-dir>/config.jsonjson
{
"enabledTweaks": [
"com.example.my-tweak",
"co.bennett.custom-keyboard-shortcuts"
],
"autoUpdate": true,
"safeMode": false,
"logLevel": "info"
}Per-Tweak Config
单个Tweak配置
Tweaks can store configuration using the API:
typescript
// Get config with defaults
const config = api.config.get('my-tweak', { theme: 'dark' });
// Update config
api.config.set('my-tweak', { theme: 'light' });
// Config is persisted automaticallyTweaks可通过API存储配置:
typescript
// 获取配置并设置默认值
const config = api.config.get('my-tweak', { theme: 'dark' });
// 更新配置
api.config.set('my-tweak', { theme: 'light' });
// 配置会自动持久化Tweak Distribution & Updates
Tweak分发与更新
Publishing a Tweak
发布Tweak
- Create a GitHub repository for your tweak
- Add tweak files (manifest.json, index.js/ts)
- Create a GitHub Release with a semver tag (e.g., )
v1.0.0 - Attach a of the tweak folder to the release
.zip
Release structure:
my-tweak-v1.0.0.zip
└── my-tweak/
├── manifest.json
└── index.js- 为你的tweak创建GitHub仓库
- 添加tweak文件(manifest.json、index.js/ts)
- 创建带有Semver标签的GitHub Release(例如)
v1.0.0 - 将tweak文件夹的包附加到Release中
.zip
Release结构:
my-tweak-v1.0.0.zip
└── my-tweak/
├── manifest.json
└── index.jsInstalling Tweaks
安装Tweaks
Users copy tweak folders to and enable them in Settings → Tweaks.
<user-data-dir>/tweaks/用户将tweak文件夹复制到 ,然后在设置 → Tweaks中启用。
<user-data-dir>/tweaks/Update Checking
更新检查
Codex++ checks in manifest.json for newer releases once per day. Users review release notes and manually update.
githubRepoCodex++ 每天检查manifest.json中的是否有新版本。用户查看发布说明后手动更新。
githubRepoCommon Patterns
常见模式
Persistent State
持久化状态
typescript
let myState = api.config.get('my-tweak', { count: 0 });
function incrementCounter() {
myState.count++;
api.config.set('my-tweak', myState);
}typescript
let myState = api.config.get('my-tweak', { count: 0 });
function incrementCounter() {
myState.count++;
api.config.set('my-tweak', myState);
}React to Codex Events
响应Codex事件
typescript
// Watch for new messages
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node instanceof HTMLElement && node.matches('.message')) {
api.log.info('New message detected');
}
});
});
});
observer.observe(document.querySelector('#chat-container'), {
childList: true,
subtree: true
});typescript
// 监控新消息
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node instanceof HTMLElement && node.matches('.message')) {
api.log.info('New message detected');
}
});
});
});
observer.observe(document.querySelector('#chat-container'), {
childList: true,
subtree: true
});CSS Injection
CSS注入
typescript
const style = document.createElement('style');
style.textContent = `
.custom-theme {
--primary-color: #00ff00;
}
.chat-message {
border-left: 3px solid var(--primary-color);
}
`;
document.head.appendChild(style);
// Store for cleanup
(api as any)._styleElement = style;typescript
const style = document.createElement('style');
style.textContent = `
.custom-theme {
--primary-color: #00ff00;
}
.chat-message {
border-left: 3px solid var(--primary-color);
}
`;
document.head.appendChild(style);
// 存储用于清理
(api as any)._styleElement = style;Troubleshooting
故障排除
Codex won't launch after install
安装后Codex无法启动
bash
undefinedbash
undefinedCheck status
检查状态
codexplusplus status
codexplusplus status
Repair installation
修复安装
codexplusplus repair
codexplusplus repair
If repair fails, uninstall and reinstall
如果修复失败,卸载后重新安装
codexplusplus uninstall
codexplusplus install
undefinedcodexplusplus uninstall
codexplusplus install
undefinedTweak not loading
Tweak未加载
bash
undefinedbash
undefinedValidate tweak structure
验证tweak结构
codexplusplus validate-tweak ~/Library/Application\ Support/codex-plusplus/tweaks/my-tweak
codexplusplus validate-tweak ~/Library/Application\ Support/codex-plusplus/tweaks/my-tweak
Check runtime logs (look for errors)
查看运行时日志(查找错误)
tail -f ~/Library/Logs/codex-plusplus/runtime.log
undefinedtail -f ~/Library/Logs/codex-plusplus/runtime.log
undefinedSafe mode (disable all tweaks)
安全模式(禁用所有tweaks)
bash
codexplusplus safe-modebash
codexplusplus safe-modeLaunch Codex, fix issues, then:
启动Codex,修复问题后执行:
codexplusplus safe-mode --off
undefinedcodexplusplus safe-mode --off
undefinedmacOS Gatekeeper issues
macOS Gatekeeper问题
After first launch of re-signed Codex:
- System Preferences → Privacy & Security
- Click "Open Anyway" for Codex.app
- Or:
xattr -cr /Applications/Codex.app
首次启动重新签名的Codex后:
- 系统偏好设置 → 隐私与安全性
- 点击Codex.app旁的“仍要打开”
- 或者执行:
xattr -cr /Applications/Codex.app
Codex update breaks Codex++
Codex更新导致Codex++失效
bash
undefinedbash
undefinedmacOS: Use official updater
macOS:使用官方更新器
codexplusplus update-codex
codexplusplus update-codex
After Codex updates, watcher auto-repairs
Codex更新后,监控程序会自动修复
Or manually:
或者手动执行:
codexplusplus repair
undefinedcodexplusplus repair
undefinedWindows: Wrong app launches
Windows:启动错误的应用
Launch Codex++ from Start Menu/Desktop, not the Microsoft Store Codex shortcut. The Store version is unpatched.
从开始菜单/桌面启动Codex++,而非Microsoft Store的Codex快捷方式。商店版本未被修补。
Development workflow
开发工作流
bash
undefinedbash
undefinedWatch for changes and auto-reload
监控文件变化并自动重载
codexplusplus dev
codexplusplus dev
Edit tweak files in:
在以下路径编辑tweak文件:
macOS: ~/Library/Application Support/codex-plusplus/tweaks/my-tweak/
macOS: ~/Library/Application Support/codex-plusplus/tweaks/my-tweak/
Reload Codex (Cmd+R / Ctrl+R) to see changes
重载Codex(Cmd+R / Ctrl+R)查看更改
undefinedundefinedClear all config
清除所有配置
bash
undefinedbash
undefinedRemove config but keep tweaks
删除配置但保留tweaks
rm ~/Library/Application\ Support/codex-plusplus/config.json
rm ~/Library/Application\ Support/codex-plusplus/config.json
Full reset
完全重置
rm -rf ~/Library/Application\ Support/codex-plusplus/
codexplusplus install
undefinedrm -rf ~/Library/Application\ Support/codex-plusplus/
codexplusplus install
undefinedSecurity Notes
安全说明
- Tweaks run with full Electron renderer privileges
- Only install tweaks from trusted sources
- Review tweak code before installing
- in manifest enables update notifications but doesn't auto-update
githubRepo - Codex++ never executes code without user consent
- Tweaks拥有Electron渲染进程的完整权限
- 仅从可信来源安装tweaks
- 安装前查看tweak代码
- manifest中的仅启用更新通知,不会自动更新
githubRepo - Codex++绝不会在未获得用户许可的情况下执行代码
Example: Complete UI Tweak
示例:完整UI Tweak
typescript
// Dark mode toggle tweak
import type { Tweak } from "@codex-plusplus/sdk";
export default {
start(api) {
const config = api.config.get('dark-mode', { enabled: false });
const applyTheme = (dark: boolean) => {
document.body.classList.toggle('dark-theme', dark);
};
applyTheme(config.enabled);
api.settings.register({
id: 'dark-mode',
title: 'Dark Mode',
render: (root) => {
root.innerHTML = `
<label>
<input type="checkbox" id="toggle" ${config.enabled ? 'checked' : ''}>
Enable dark mode
</label>
`;
root.querySelector('#toggle')?.addEventListener('change', (e) => {
const enabled = (e.target as HTMLInputElement).checked;
api.config.set('dark-mode', { enabled });
applyTheme(enabled);
});
}
});
// Inject CSS
const style = document.createElement('style');
style.textContent = `
.dark-theme {
background: #1a1a1a;
color: #ffffff;
}
`;
document.head.appendChild(style);
(api as any)._style = style;
},
stop() {
document.body.classList.remove('dark-theme');
const style = (this as any)._style;
if (style) style.remove();
}
} satisfies Tweak;typescript
// 深色模式切换tweak
import type { Tweak } from "@codex-plusplus/sdk";
export default {
start(api) {
const config = api.config.get('dark-mode', { enabled: false });
const applyTheme = (dark: boolean) => {
document.body.classList.toggle('dark-theme', dark);
};
applyTheme(config.enabled);
api.settings.register({
id: 'dark-mode',
title: 'Dark Mode',
render: (root) => {
root.innerHTML = `
<label>
<input type="checkbox" id="toggle" ${config.enabled ? 'checked' : ''}>
Enable dark mode
</label>
`;
root.querySelector('#toggle')?.addEventListener('change', (e) => {
const enabled = (e.target as HTMLInputElement).checked;
api.config.set('dark-mode', { enabled });
applyTheme(enabled);
});
}
});
// 注入CSS
const style = document.createElement('style');
style.textContent = `
.dark-theme {
background: #1a1a1a;
color: #ffffff;
}
`;
document.head.appendChild(style);
(api as any)._style = style;
},
stop() {
document.body.classList.remove('dark-theme');
const style = (this as any)._style;
if (style) style.remove();
}
} satisfies Tweak;