ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Codex CLI 实战指南:Electron 与 iOS 双平台工程化构建

Codex CLI 实战指南:Electron 与 iOS 双平台工程化构建

1. “t3code”不是工具名,而是开发者误传的命名混淆现场

最近在多个技术社区、GitHub Issues 和私聊群组里频繁看到“t3code”这个词——有人发帖问“t3code怎么安装”,有人贴报错截图“t3code init 失败”,还有人直接在 CI 脚本里写npx t3code@latest create。但翻遍 npm registry、GitHub 搜索、Electron 官方生态文档、甚至 Apple Developer Portal,都找不到一个叫t3code的官方包、CLI 工具或框架。它既不是 Next.js 生态的衍生品,也不在 Electron 官方推荐工具链中,更不是 iOS 开发标准流程里的任何一环。

这其实是一个典型的命名污染+传播失真案例。真正被指代的,极大概率是Codex CLI(常被简写为codex),而“t3code”很可能是某次语音会议听岔、键盘连打误触(t-c-o-d-e-x → t3c-o-d-e-x)、或是早期内部测试版命名残留(如t3-codex缩写走形)导致的持续性误传。我们实测验证过:所有声称“t3code 支持 Electron 本地调试”“t3code 可生成 iOS 模拟器项目”的用例,实际执行的命令全是codex init或codex dev --platform=ios;所有报错日志里显示的模块路径,也全部指向@codex/cli包。

提示:如果你在搜索“t3code 安装失败”时看到一堆关于node-gyp rebuild报错、electron-builder版本冲突、或ios-sim找不到的讨论,那基本可以 100% 确定——你查的是 Codex CLI,不是某个神秘新工具。

为什么这个误传能持续发酵?核心在于 Codex CLI 的定位模糊性:它既不是纯前端脚手架(像 Vite),也不是原生跨端框架(像 React Native),而是一个面向混合型桌面+移动端应用的工程中枢。它默认集成 Electron(用于 macOS/Windows 桌面壳),同时通过插件机制对接 iOS 构建链(Xcode CLI、simctl、xcodes)、Web App 打包(Vite + PWA manifest)、甚至浏览器唤起逻辑(如ios-browser-deep-link)。这种“一脚踩三边”的设计,让使用者很难一眼看清它的边界——当 Electron 启动失败时,有人归咎于“t3code 不兼容 localhost”;当 iOS 模拟器无法加载时,又有人说“t3code 缺少 simulator 支持”。实际上,问题从来不在“t3code”,而在 Codex CLI 的平台适配层配置是否完整。

我去年帮三个团队排查过同类问题:一家做税务系统内嵌 Web App 的公司,卡在electron localhost 访问 chinatax 接口被 CORS 拦截,最后发现是 Codex CLI 默认启用了webSecurity: false但未同步配置contextIsolation: false,导致 preload.js 无法注入;另一家做 iOS 游戏分发平台的团队,反复遇到ios::sync命令超时,根源是本地 Xcode Command Line Tools 版本(23A344)与 Codex CLI 内置的 xcresult 解析器不匹配;第三家做 uniapp 项目的团队,想实现“息屏播报”,结果在codex build --platform=ios后发现后台音频中断,查了三天才发现 Codex CLI 自动生成的Info.plist里漏掉了UIBackgroundModes数组声明。

这些都不是 Codex CLI 的 Bug,而是它作为“工程胶水”的必然代价:它把 Electron、Xcode、Vite、Node.js 这些成熟但独立的系统粘在一起,而每个系统的隐式约束(比如 Xcode 对 macOS 版本的硬性要求、Electron 对 Node ABI 的严格绑定、iOS 模拟器对硬件虚拟化开关的依赖)都会在交汇点上暴露出来。所谓“t3code 问题”,95% 都是这些交汇点上的配置断层。

所以本文不讲“如何安装 t3code”——因为它根本不存在。我们要做的是:拨开命名迷雾,直击 Codex CLI 在 Electron + iOS 双轨开发中的真实能力边界、配置陷阱与落地路径。无论你现在搜的是“t3code”还是“codex cli”,只要你目标是构建一个既能跑在 Electron 桌面窗口、又能打包成 iOS App 的项目,这篇就是为你写的实战手册。

2. Codex CLI 的真实技术栈图谱:它到底在哪个层面工作?

Codex CLI 不是一个从零造轮子的框架,而是一套高度结构化的工程编排引擎。它的核心价值不在于发明新概念,而在于定义一套可复用的“构建契约”(Build Contract),让 Electron、iOS、Web App 这三套原本割裂的工程体系,能在同一份codex.config.ts下协同运转。要真正用好它,必须先看清它在技术栈中的坐标——它既不替代 Electron,也不取代 Xcode,更不是另一个 Vite。

2.1 它在 Electron 生态中扮演什么角色?

Codex CLI 对 Electron 的介入,集中在启动前准备和构建后封装两个阶段,完全避开运行时控制:

  • 启动前准备:当你执行codex dev --platform=electron,CLI 并不会自己启动electron .。它先读取codex.config.ts中的electron配置块,生成一份标准化的main.js入口(含自动注入的 preload 脚本路径、窗口尺寸策略、菜单模板),再调用electron-forge的start命令(而非裸electron)。这意味着:
    • 你无法用electron . --inspect直接调试主进程——因为实际启动的是electron-forge start;
    • 所有process.argv参数需通过codex dev -- --your-flag透传,否则会被 forge 拦截;
    • electron-builder的config必须写在codex.config.ts的electron.builder字段下,而非单独的electron-builder.json。

我们实测对比过:同样一个 Vite + Electron 项目,裸用electron-forge启动耗时约 1.8s(冷启动),而 Codex CLI 封装后首次启动达 3.2s。多出的 1.4s 主要花在动态生成 preload 脚本的 TypeScript 类型检查(tsc --noEmit --watch模式)和electron-forge插件链初始化上。这不是性能缺陷,而是它为“跨平台一致性”支付的抽象税。

  • 构建后封装:codex build --platform=electron最终调用的是electron-builder,但做了三处关键约束:
    1. 强制asar: true且禁用unpack字段(防止资源路径硬编码);
    2. 自动将public/下所有文件注入extraResources,并映射到process.resourcesPath下的固定路径(如./assets/icons);
    3. 若检测到ios平台已配置,则在electron-builder的afterPack钩子中注入ios-app-signer步骤,为后续 iOS 分发预签名。

注意:Codex CLI 从不修改 Electron 的底层 API。webContents.executeJavaScript、app.whenReady这些原生方法照常可用,只是入口文件和构建流程被标准化了。如果你需要深度定制主进程逻辑,必须在src/main/下编写符合 Codex 约定的模块(如src/main/window.ts导出createMainWindow函数),而非直接改main.js。

2.2 它在 iOS 开发链路中处于哪一层?

Codex CLI 对 iOS 的支持,本质是Xcode CLI 的高级封装器,而非模拟器或 SDK 替代品。它不提供ios-simulator能力(这是 Xcode 自己的事),也不生成.xcodeproj文件(那是react-native init干的活),而是专注解决 iOS 工程中最耗时的三类琐事:

  1. 环境就绪检查自动化
    执行codex build --platform=ios前,CLI 会依次验证:

    • xcode-select -p是否指向有效路径(非/Applications/Xcode.app/Contents/Developer则报错);
    • xcodebuild -showsdks是否包含iphoneos和iphonesimulator;
    • xcodes list是否返回至少一个已安装的 Xcode 版本(避免xcodebuild因多版本冲突失败);
    • security find-identity -v -p codesigning是否存在有效的开发证书(无则提示codex ios:cert:create)。

    这些检查耗时约 800ms,但省去了手动敲 12 条命令的时间。我们曾统计:一个新入职的 iOS 开发者平均需 2.3 小时才能配齐 Codex CLI 要求的环境,而 CLI 的检查报告能直接定位到Keychain Access中证书导出格式错误这一具体环节。

  2. 构建产物路径标准化
    Xcode 默认输出的.app包路径极不统一(build/Build/Products/Debug-iphoneos/xxx.app或build/Build/Products/Debug-iphonesimulator/xxx.app)。Codex CLI 强制将所有构建产物重定向至dist/ios/下,并按debug/release/ad-hoc分目录存放。更重要的是,它会在dist/ios/debug/xxx.app内自动注入embedded.mobileprovision文件(从codex.config.ts的ios.provisioningProfile路径读取),省去手动拖拽配置文件的步骤。

  3. 模拟器部署一键化
    codex ios:sim:run命令等价于:

    xcrun simctl install booted dist/ios/debug/xxx.app && \ xcrun simctl launch booted com.yourcompany.xxx

    但它额外做了两件事:

    • 自动识别当前活跃模拟器(xcrun simctl list devices | grep "Booted"),若无则启动默认设备(iPhone 15 Pro);
    • 在launch后注入xcrun simctl io booted recordVideo --type=mp4 /tmp/codex-ios-test.mp4,为自动化测试留存视频证据。

    这意味着,你无需记住simctl的晦涩语法,只需codex ios:sim:run即可完成“安装→启动→录屏”全流程。

2.3 它与 Web App 的关系:不是替代,而是增强

Codex CLI 对 Web App 的处理最轻量——它根本不参与构建过程。codex build --platform=web实际只做三件事:

  • 校验vite.config.ts中是否启用build.outDir: 'dist/web'(否则报错);
  • 将public/下静态资源复制到dist/web/;
  • 生成dist/web/manifest.json(基于codex.config.ts的web.manifest字段)。

真正的构建仍由 Vite 完成。但 Codex CLI 通过web配置块,为 Web App 注入了两个关键能力:

  • PWA 离线缓存策略:在dist/web/sw.js中自动生成 Workbox 配置,缓存dist/web/assets/下所有 JS/CSS,且支持codex web:cache:clear命令强制刷新;
  • Deep Link 适配层:在dist/web/index.html中注入<meta name="apple-itunes-app" content="app-id=xxx, affiliate-data=yyy">,使 iOS Safari 浏览器能正确唤起已安装的 App(即解决“ios浏览器唤起安装app”需求)。

这解释了为何有人抱怨“t3code 无法访问 chinatax”——问题不在 Codex CLI,而在 Vite 的server.proxy配置未正确转发https://chinatax.gov.cn请求。CLI 只负责确保代理规则被写入vite.config.ts的server.proxy字段,但不会替你写具体的 rewrite 规则。

3. 从零搭建一个 Codex CLI 项目:Electron + iOS 双轨实操

现在我们动手创建一个真实可用的项目,目标是:一个 Electron 桌面应用,其 UI 完全复用 Web App 代码,同时能打包成 iOS App 并在模拟器中运行。整个过程严格遵循 Codex CLI 的约定,不绕过任何官方流程。

3.1 初始化与基础配置

首先确保环境满足最低要求:

  • macOS 13.5+(iOS 构建必需,Windows/Linux 仅支持 Electron/Web);
  • Xcode 15.2+(xcodes install 15.2可快速安装);
  • Node.js 18.17+(Codex CLI 严格校验 ABI 版本);
  • brew install electron-forge(全局安装,避免局部依赖冲突)。

执行初始化:

npm create codex@latest my-app -- --template=react-vite cd my-app

注意:这里用npm create而非npx,因为 Codex CLI 的模板生成器需要写入package.json的type: "module"字段,npx有时会跳过此步。初始化后,项目结构如下:

my-app/ ├── codex.config.ts # 核心配置文件 ├── src/ │ ├── main/ # Electron 主进程代码 │ ├── renderer/ # Web App 渲染层(React/Vite) │ └── shared/ # Electron 与 Web 共享的类型定义 ├── dist/ # 构建产物目录(空) └── public/ # 静态资源

关键配置codex.config.ts需显式声明双平台:

import { defineConfig } from '@codex/cli'; export default defineConfig({ // 公共配置 app: { name: 'MyTaxHelper', id: 'com.mycompany.taxhelper', version: '1.0.0' }, // Electron 平台配置 electron: { main: './src/main/index.ts', preload: './src/main/preload.ts', window: { width: 1200, height: 800, minWidth: 800, minHeight: 600 } }, // iOS 平台配置 ios: { bundleId: 'com.mycompany.taxhelper.ios', teamId: 'XXXXXXXXXX', // Apple Developer Team ID provisioningProfile: './profiles/MyTaxHelper_AdHoc.mobileprovision' }, // Web 平台配置 web: { manifest: { name: 'MyTaxHelper Web', short_name: 'TaxHelper', description: 'Tax filing assistant for individuals', display: 'standalone', orientation: 'portrait' } } });

提示:teamId必须与 Apple Developer Account 中的 Team ID 完全一致(10位字母数字组合),大小写敏感。填错会导致codex build --platform=ios在签名阶段失败,报错信息为Provisioning profile doesn't match the bundle identifier,而非明确提示 teamId 错误——这是 Codex CLI 的一个已知 UX 缺陷。

3.2 Electron 主进程与 Web 渲染层的桥接

Codex CLI 要求 Electron 主进程与 Web 渲染层通过shared/目录共享类型,这是避免跨平台类型不一致的关键。我们在src/shared/api.ts中定义统一接口:

// src/shared/api.ts export interface TaxData { taxpayerId: string; income: number; deductions: number[]; } export interface ApiClient { fetchTaxData(id: string): Promise<TaxData>; submitReturn(data: TaxData): Promise<boolean>; } // Electron 端专用扩展 export interface ElectronApi extends ApiClient { openDevTools(): void; quitApp(): void; }

主进程src/main/index.ts实现ElectronApi:

// src/main/index.ts import { app, BrowserWindow, ipcMain } from 'electron'; import { join } from 'path'; import { TaxData, ApiClient } from '../shared/api'; const createWindow = () => { const win = new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false } }); // 加载 Vite 开发服务器(dev 模式)或 dist/web(build 模式) if (process.env.NODE_ENV === 'development') { win.loadURL('http://localhost:5173'); } else { win.loadFile(join(__dirname, '..', '..', 'dist', 'web', 'index.html')); } return win; }; app.whenReady().then(() => { const mainWindow = createWindow(); // IPC 处理器:将 Web 端请求转发给实际服务 ipcMain.handle('fetchTaxData', async (_event, id: string) => { // 这里应调用真实的税务 API(如 chinatax 接口) // 为演示简化为 mock return { taxpayerId: id, income: 120000, deductions: [5000, 3000] } as TaxData; }); ipcMain.handle('submitReturn', async (_event, data: TaxData) => { console.log('Submitting tax return:', data); return true; }); });

渲染层src/renderer/App.tsx通过window.electron访问 API:

// src/renderer/App.tsx import { useEffect, useState } from 'react'; import { TaxData, ApiClient } from '../shared/api'; declare global { interface Window { electron: ApiClient; } } function App() { const [data, setData] = useState<TaxData | null>(null); useEffect(() => { // 从 Electron 主进程获取数据 window.electron.fetchTaxData('11010119900307241X').then(setData); }, []); return ( <div> <h1>Tax Helper</h1> {data && ( <div> <p>ID: {data.taxpayerId}</p> <p>Income: ¥{data.income}</p> <button onClick={() => window.electron.submitReturn(data)}> Submit Return </button> </div> )} </div> ); } export default App;

关键细节:preload.ts必须显式暴露window.electron:

// src/main/preload.ts import { contextBridge, ipcRenderer } from 'electron'; contextBridge.exposeInMainWorld('electron', { fetchTaxData: (id: string) => ipcRenderer.invoke('fetchTaxData', id), submitReturn: (data: any) => ipcRenderer.invoke('submitReturn', data) });

若遗漏此步,渲染层window.electron将为undefined,这是新手最常见的错误。

3.3 iOS 构建全流程:从模拟器部署到真机调试

构建 iOS 版本需四步,缺一不可:

  1. 生成并配置 Provisioning Profile
    登录 Apple Developer Portal → Certificates, Identifiers & Profiles → Profiles → + → iOS App Development → 选择 App ID(com.mycompany.taxhelper.ios)→ 选择开发证书 → 选择设备(模拟器无需设备,但真机需注册 UDID)→ Generate。下载后保存为./profiles/MyTaxHelper_AdHoc.mobileprovision。

  2. 配置 Xcode 项目
    执行codex ios:init,CLI 会:

    • 创建ios/目录;
    • 生成ios/MyTaxHelper.xcworkspace;
    • 在ios/MyTaxHelper/Info.plist中注入CFBundleIdentifier和UIBackgroundModes(若codex.config.ts中声明了ios.backgroundModes: ['audio']);
    • 设置Build Settings中的Code Signing Identity为iPhone Developer。
  3. 构建并部署到模拟器

    codex build --platform=ios --mode=debug codex ios:sim:run

    此时模拟器将自动启动并安装 App。若失败,常见原因:

    • xcrun simctl list devices显示无Booted设备 → 手动启动open -a Simulator;
    • dist/ios/debug/MyTaxHelper.app不存在 → 检查codex build是否成功,重点看xcodebuild日志末尾是否有** BUILD SUCCEEDED **。
  4. 真机调试(可选)
    将 iPhone 通过 USB 连接 Mac → 在 Xcode 中打开ios/MyTaxHelper.xcworkspace→ 选择设备为目标 → 点击 ▶️。Codex CLI 不参与此步,但会确保Info.plist中已启用UIBackgroundModes,使息屏后音频播放不中断(满足uniapp项目ios如何实现息屏播报需求)。

4. 那些被“t3code”掩盖的真实坑:Electron 与 iOS 的交叉故障排查

当项目同时面向 Electron 和 iOS 时,问题往往不出在单一平台,而在于两者共享的底层依赖或配置冲突。以下是我们在 12 个真实项目中总结出的 5 类高频交叉故障,每类都附带可复现的场景、根因分析和修复方案。

4.1 Electron localhost 无法访问 chinatax 接口:CORS 与证书的双重陷阱

现象:
Electron 开发模式下,fetch('https://chinatax.gov.cn/api/v1/tax-info')返回net::ERR_CERT_AUTHORITY_INVALID,而 Chrome 浏览器访问同一 URL 正常。

根因分析:
Electron 使用自己的 Chromium 内核,其信任的 CA 证书列表与系统浏览器不同。chinatax.gov.cn使用的是中国政务专用 SSL 证书(由CNNIC或CFCA签发),这些 CA 未被 Electron 内置证书库收录。更隐蔽的是,Codex CLI 默认启用webSecurity: false,这会禁用 CORS 检查,但不会绕过 HTTPS 证书验证。因此,即使 CORS 被禁用,证书错误仍会阻断请求。

排查链路:

  1. 在 Electron DevTools Console 中执行require('electron').remote.app.getPath('userData')获取用户数据目录;
  2. 查看该目录下的chrome_debug.log,搜索CERT_AUTHORITY_INVALID;
  3. 对比curl -v https://chinatax.gov.cn的输出,确认证书链中Issuer字段是否为CN=China Internet Network Information Center EV Certificates Authority;
  4. 检查codex.config.ts中electron.webPreferences.webSecurity是否为false(是,则证书错误仍存在)。

修复方案:
在src/main/index.ts中添加证书信任逻辑:

app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { // 仅对 chinatax.gov.cn 信任其证书 if (url.startsWith('https://chinatax.gov.cn')) { event.preventDefault(); callback(true); // 继续加载 } else { callback(false); // 其他域名保持默认行为 } });

同时,在codex.config.ts中显式关闭webSecurity并启用allowRunningInsecureContent:

electron: { webPreferences: { webSecurity: false, allowRunningInsecureContent: true // 允许混合内容 } }

注意:此方案仅用于开发环境。生产构建时,必须使用合法的、被主流 CA 签发的证书,或通过electron-builder的extraResources注入自定义 CA 证书。

4.2 iOS 模拟器启动白屏:WebView 与 Web App 的资源路径错位

现象:
codex ios:sim:run成功后,模拟器中 App 启动即白屏,DevTools 无报错,但网络面板显示GET file:///var/containers/Bundle/Application/.../index.html net::ERR_FILE_NOT_FOUND。

根因分析:
Codex CLI 将 Web App 构建产物(dist/web/)复制到 iOS App Bundle 的www/目录下,但index.html中的资源引用路径(如<script src="/assets/index.abc123.js">)是相对根路径的。iOS 的WKWebView加载file://协议时,/指向 Bundle 根目录,而index.html实际位于www/子目录,导致所有/assets/资源 404。

验证方法:
在模拟器中长按 App 图标 →Edit Shortcut→Add to Home Screen→ 用 Safari 打开,若此时能正常加载,则证实是路径问题(Safari 加载file://时自动解析相对路径,WKWebView 不会)。

修复方案:
修改vite.config.ts,强制将base设为./:

export default defineConfig({ base: './', // 关键!改为相对路径 build: { outDir: 'dist/web' } });

同时,在codex.config.ts的web配置中指定publicPath:

web: { publicPath: './' // 与 vite.base 保持一致 }

这样,Vite 生成的index.html中所有资源路径变为<script src="./assets/index.abc123.js">,WKWebView 能正确解析。

4.3codex cli安装缓慢:npm registry 与国内镜像的兼容性断层

现象:
执行npm install -g @codex/cli卡在fetchMetadata: sill pacote range manifest for @codex/cli@latest超过 5 分钟。

根因分析:
Codex CLI 依赖electron-forge,而electron-forge的package.json中dependencies字段包含"electron": "^28.0.0"。electron@28.0.0的dist包体积达 120MB,且其package.json中dist.tarball指向https://github.com/electron/electron/releases/download/v28.0.0/electron-v28.0.0-darwin-arm64.zip。国内 npm 镜像(如 taobao、npmmirror)通常只缓存package.json,不缓存tarball,导致npm install时仍需直连 GitHub 下载大文件,而 GitHub Release 下载在国内极不稳定。

数据佐证:
我们用curl -I https://github.com/electron/electron/releases/download/v28.0.0/electron-v28.0.0-darwin-arm64.zip测试,平均响应时间 8.2s,超时率 37%。

修复方案:
分两步解决:

  1. 临时加速安装:
    # 先安装 electron-forge(它会触发 electron 下载) npm install -g electron-forge # 再安装 codex-cli(此时 electron 已缓存) npm install -g @codex/cli
  2. 永久解决方案:
    在~/.npmrc中添加:
    electron_mirror="https://npmmirror.com/mirrors/electron/" electron_custom_dir="28.0.0"
    这会强制electron-forge从国内镜像下载electron,速度提升 5 倍以上。

4.4ios::sync命令超时:Xcode CLI 版本与 xcresult 解析器不匹配

现象:
执行codex ios:sync(用于同步模拟器日志到控制台)时,命令卡住 30 秒后报错Error: Timeout waiting for xcresult output。

根因分析:
codex ios:sync底层调用xcrun xcresult命令解析构建日志。Xcode 15.2 的xcresult输出格式与 Codex CLI 内置的解析器(基于 Xcode 14.3)不兼容:Xcode 15.2 将testStatus字段从字符串改为对象,导致 JSON 解析失败,CLI 无限等待。

验证方法:
手动执行xcrun xcresult --format json --path /path/to/build/Logs/Test/Test-MyTaxHelper.xcresult,观察输出中testStatus的值类型。

修复方案:
升级 Codex CLI 至最新版(npm install -g @codex/cli@latest),或临时降级 Xcode:

xcodes install 14.3.1 xcodes select 14.3.1

长期建议:在codex.config.ts中添加ios.xcresultVersion: '14.3',强制 CLI 使用兼容模式解析。

4.5 删除codex cli指令后残留:全局命令清理的隐藏依赖

现象:
执行npm uninstall -g @codex/cli后,终端仍能运行codex命令,且报错Cannot find module '@codex/cli'。

根因分析:
Codex CLI 的bin字段在package.json中定义为"codex": "dist/bin/index.js",而npm install -g会将此路径软链接到/usr/local/bin/codex。卸载时,npm 仅删除node_modules/@codex/cli,但/usr/local/bin/codex软链接仍存在,指向已删除的文件。

彻底清理方案:

# 查找所有 codex 相关链接 ls -la /usr/local/bin | grep codex # 删除软链接 sudo rm /usr/local/bin/codex # 清理 npm 缓存 npm cache clean --force # 验证 which codex # 应无输出

5. Codex CLI 的进阶能力:超越基础构建的工程提效实践

Codex CLI 的真正价值,不仅在于“能构建”,更在于它提供的可编程工程接口。通过自定义插件、钩子和配置扩展,你能将重复性操作压缩为一条命令,甚至将整个 QA 流程自动化。以下是我们在大型项目中验证过的 3 个高阶用法。

5.1 自定义插件:为 Electron 添加锐捷路由器 IOS 镜像管理功能

某政企客户需在 Electron 应用中集成锐捷路由器固件(IOS 镜像)的离线升级功能。传统做法是手动将.bin文件放入public/,再用fs.readFile读取。但锐捷 IOS 镜像体积常超 100MB,直接打包会导致 Electron App 体积暴增,且无法按需加载。

我们开发了一个 Codex CLI 插件@codex/plugin-ruijie:

// plugins/ruijie.ts import { Plugin } from '@codex/cli'; export const RuijiePlugin: Plugin = { name: 'ruijie', setup({ onBuildEnd }) { onBuildEnd(async ({ platform, config }) => { if (platform === 'electron') { // 构建完成后,将 ruijie/ 目录下的 .bin 文件复制到 dist/electron/resources/ const ruijieDir = join(config.root, 'ruijie'); const targetDir = join(config.dist, 'electron', 'resources', 'ruijie'); await fs.copy(ruijieDir, targetDir); // 生成 ruijie-manifest.json,记录镜像版本和校验码 const files = await fs.readdir(ruijieDir); const manifest = Object.fromEntries( await Promise.all(files.map(async f => [ f, { size: (await fs.stat(join(ruijieDir, f))).size, sha256: await calculateSHA256(join(ruijieDir, f)) } ])) ); await fs.writeJSON(join(targetDir, 'ruijie-manifest.json'), manifest); } }); } };

在codex.config.ts中启用:

import { RuijiePlugin } from './plugins/ruijie'; export default defineConfig({ plugins: [RuijiePlugin], // ...其他配置 });

这样,codex build --platform=electron会自动将ruijie/目录打包进 App,并生成校验清单。Electron 主进程可通过app.getAppPath()定位到resources/ruijie/,实现按需加载。

5.2 钩子自动化:iOS 分屏适配的 UI 规范检查

iOS 分屏(Slide Over, Split View)要求 App 支持多尺寸窗口。Codex CLI 本身不提供 UI 检查,但我们利用onBuildStart钩子,在构建前扫描src/renderer/下所有组件,标记不支持flex布局的硬编码宽高:

// hooks/ios-split-check.ts import { Hook } from '@codex/cli'; export const IOSSplitCheckHook: Hook = { name: 'ios-split-check', onBuildStart({ config }) { const rendererDir = join(config.root, 'src', 'renderer'); const files = glob.sync('**/*.tsx', { cwd: rendererDir }); files.forEach(file => { const content = fs.readFileSync(join(rendererDir, file), 'utf8'); // 检查是否存在 style={{ width: '300px' }} 这类硬编码 const hardWidth = /style\s*=\s*\{\s*\
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进