首页
/ oh-my-codex 0.21.2 发布说明解析:macOS 原生运行时水合、GitGuardex HUD 进度与印尼语本地化

oh-my-codex 0.21.2 发布说明解析:macOS 原生运行时水合、GitGuardex HUD 进度与印尼语本地化

2026-09-09 21:47:01作者:尤峻淳Whitney

本篇文章以 oh-my-codex(OmX)0.21.2 补丁版本为主线,围绕三大核心变更展开:macOS arm64 上 omx-runtime 原生二进制的“水合(hydration)”机制、可选的 GitGuardex 分支收尾进度在 HUD 中的实时展示,以及覆盖全部本地化 README 的语言导航同步。读者读完本文后将掌握该版本的安装/更新行为变化、HUD 配置方法、事件流数据格式,以及对应的源码实现位置与测试验证路径。

版本概览与定位

oh-my-codex 是一个为 Codex CLI 提供 hooks、Agent 团队、HUD 等扩展能力的开源项目。0.21.2 是一个补丁(patch)版本,发布于 2026-09-01,覆盖 v0.21.1..04533ebfc887643586e37180ec3270473948115a 这一提交区间:共 11 个提交、29 个变更文件(净增 +1,245 行、净删 −10 行),并合并了三个 PR(#3599、#3601、#3602)。

该版本的可复现范围记录在 artifacts/release-0.21.2/inventory.md,官方发布说明见 docs/release-notes-0.21.2.md,同一内容的发布正文模板位于 RELEASE_BODY.md

三大 Highlights

  1. macOS arm64 原生运行时水合(#3602):全局安装与同版本重装时,会把 omx-runtime 水合进经过校验的原生缓存;即时更新与延迟更新路径也会在脚本抑制安装后执行水合,且网络行为有界、非致命。
  2. GitGuardex 收尾进度进入 HUD(#3601):可选的、项目级 HUD 集成,实时展示 review/autofix 收尾进度;支持从嵌套 worktree 路径解析配置,在 stat 调用前限制元数据读取量,并使用适配一秒 HUD 观察间隔的自旋动画节奏。
  3. 印尼语 README(#3599):新增 Bahasa Indonesia 翻译,并同步了所有本地化 README 的语言导航。

兼容性说明

  • 补丁版本,无破坏性 API 变更;
  • GitGuardex 集成默认关闭,需要显式进行项目级 HUD 配置才会启用;
  • 运行时水合保持“fail-closed”的校验和/缓存验证语义——即校验失败时宁可关闭对应功能,也不会放任未经验证的二进制进入缓存;
  • 当资产或网络不可用时,包安装不会因此失败(水合失败非致命)。

macOS arm64 原生运行时水合机制

为什么需要“水合”

oh-my-codex 的某些运行时能力由 Rust crate 提供(见 Cargo.toml 中的 omx-runtimeomx-sparkshellomx-muxomx-apiomx-exploreomx-runtime-core 等)。这些原生二进制需要针对具体平台架构分发。在 0.21.2 之前,macOS arm64 环境在“全局安装”和“同版本重装”场景下可能存在运行时未就绪的问题;0.21.2 补上了这两条路径的“水合”动作。

水合的入口与调用链

核心函数是 hydrateOmxRuntimeNonFatal,定义于 src/scripts/postinstall.ts

export async function hydrateOmxRuntimeNonFatal(options: {
  packageRoot?: string;
  env?: NodeJS.ProcessEnv;
  log?: (message: string) => void;
  timeoutMs?: number;
} = {}): Promise<string | undefined> {
  const log = options.log ?? ((message: string) => console.log(message));
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? RUNTIME_HYDRATION_TIMEOUT_MS);
  timer.unref?.();
  try {
    const runtimePath = await hydrateNativeBinary("omx-runtime", {
      packageRoot: options.packageRoot,
      env: options.env,
      signal: controller.signal,
    });
    if (runtimePath) log(`[omx] Hydrated omx-runtime for this platform: ${runtimePath}`);
    else log("[omx] omx-runtime was not available for this platform; native runtime features remain disabled.");
    return runtimePath;
  } catch (error) {
    log(`[omx] omx-runtime hydration failed non-fatally: ${error instanceof Error ? error.message : String(error)}`);
    return undefined;
  } finally {
    clearTimeout(timer);
  }
}

关键点:

  • 非致命语义:整个水合过程被 try/catch 包裹,失败时仅打印 omx-runtime hydration failed non-fatally: ... 并返回 undefined,不会让安装/更新流程崩溃;
  • 超时保护:默认超时 RUNTIME_HYDRATION_TIMEOUT_MS = 15_000(15 秒),通过 AbortController 中止底层下载,timer.unref() 保证不会阻塞进程退出;
  • 平台不可用兜底:若该平台没有对应的 omx-runtime 资产,会输出“native runtime features remain disabled”的提示并继续;
  • 底层实际下载/校验逻辑由 src/cli/native-assets.tshydrateNativeBinary 完成,配合 src/native-assets/policy.ts 的策略(校验和验证、缓存位置管理)。

更新路径如何接入水合

更新流程在 src/cli/update.ts 中把水合作为依赖注入:

interface UpdateDependencies {
  ...
  hydrateRuntime: (ownership?: PackageManagerOwnership) => Promise<string | undefined>;
  ...
}

其默认实现(src/cli/update.ts)为:

hydrateRuntime: (ownership) => hydrateOmxRuntimeNonFatal({
  packageRoot: ownership?.packageRoot,
  env: ownership?.environment,
}),

并且更新后的安装时间戳写入逻辑(writeSuccessfulInstallStampsrc/cli/update.ts)会记录 installed_versioninstall_channelinstall_sourceinstall_revisiondev_base_versionpackage_manager 等元数据。这意味着“即时更新”和“延迟更新”在脚本(postinstall)被抑制的安装场景下,仍会由更新流程主动执行一次水合。

水合的行为边界小结

场景 行为
全局安装 安装后水合 omx-runtime 到经校验的原生缓存
同版本重装 同样执行水合(noop-same-version 之外仍补水合动作)
即时更新(immediate update) 更新脚本安装后调用 hydrateRuntime 水合
延迟更新(deferred update) 后台任务中同样水合
校验和失败 / 网络不可用 非致命,功能禁用,安装不失败

相关的回归测试位于 src/scripts/tests/postinstall.test.tssrc/cli/tests/native-assets.test.ts,可据此验证“fail-closed 校验 + 非致命失败”的组合行为。

GitGuardex 收尾进度集成到 HUD

背景:HUD 是什么

omx hud --watch 是 OmX 的监控/状态界面(README 明确说明它是“monitoring/status surface, not the primary user workflow”,见 README.md)。它在一个可定制窗口中聚合展示版本、Git 分支、ralph、ultragoal、team、autopilot 等状态。0.21.2 给这个面板增加了一个可选的 GitGuardex(gx)收尾进度行。

配置方式(关键实操)

GitGuardex 集成默认关闭,且只读项目级配置。需要在项目的 .omx/hud-config.json 中显式开启:

{
  "preset": "focused",
  "git": { "display": "repo-branch" },
  "statusLine": { "preset": "focused" },
  "guardex": { "enabled": true }
}
  • "guardex": { "enabled": true } 存在时,HUD 会显示形如 gx:<step>/<total> <phase> 的进度;review/autofix 阶段运行时会有自旋动画;
  • 当该键缺失或为 false 时,OmX 完全不读取 Guardex 状态(见 README.md);
  • 即使没有配置 Guardex,HUD 其余部分照常渲染——该集成对未启用者零影响。

配置结构定义在 src/hud/types.ts

export interface HudGuardexConfig {
  /** Read repository-local `gx branch finish` progress into the HUD. */
  enabled?: boolean;
}

export interface HudConfig {
  preset?: HudPreset;
  git?: HudGitConfig;
  statusLine?: HudStatusLineConfig;
  guardex?: HudGuardexConfig;
}

export const DEFAULT_HUD_CONFIG: ResolvedHudConfig = {
  preset: 'focused',
  git: { display: 'repo-branch' },
  statusLine: { preset: 'focused' },
  guardex: { enabled: false },   // ← 默认关闭
};

相关测试 src/hud/tests/types.test.ts 中有两条关键断言:“keeps GitGuardex integration disabled by default”与“enables GitGuardex progress only when explicitly configured”,正好对应上述默认关闭、显式开启的行为。

事件流数据格式与解析规则

Guardex 把收尾进度写入仓库本地状态目录 .omx/state/finish-runs/(独立于 OMX 会话/团队状态根目录,见 src/hud/state.ts)。每个事件文件是 JSONL(每行一个 JSON 事件),文件名形如 finish-<runIdBase36>-<...>.jsonl

RawGuardexFinishEvent 的字段(src/hud/state.ts):

interface RawGuardexFinishEvent {
  schemaVersion?: unknown;
  runId?: unknown;
  timestamp?: unknown;
  stage?: unknown;      // 如 review / autofix / finish
  state?: unknown;      // 如 running / pending / failed / finished
  index?: unknown;      // 当前步骤序号(从 1 开始)
  total?: unknown;      // 总步骤数
  label?: unknown;
}

normalizeGuardexFinishEventsrc/hud/state.ts)的过滤规则非常严格,任何一条不满足即返回 null(不展示):

  • schemaVersion 必须等于 1
  • runIdtimestampstagestatelabel 必须非空;
  • stage 不能是 finishstate 不能是 pending
  • indextotal 必须是安全整数且 0 < index <= total
  • runId 需匹配 ^finish-(\d+)-,从中解析出 PID,且该进程必须存活(process.kill(pid, 0) 探活);
  • timestamp 必须是合法时间,且事件年龄不超过 GUARDEX_FINISH_MAX_AGE_MS = 24h
  • state 处于终态集合 {failed, finished} 时直接判为无效(收尾已结束,不再展示);
  • 输出字段会被截断:stage/state 最多 40 字符、label 最多 80 字符。

解析后对外暴露的 HUD 结构 GuardexFinishStateForHudsrc/hud/types.ts):

export interface GuardexFinishStateForHud {
  active: true;
  stage: string;
  state: string;
  index: number;
  total: number;
  label: string;
  updatedAt: string;
}

读取策略:先限定再 stat,保证每 tick O(1)

readGuardexFinishStatesrc/hud/state.ts)采用了刻意设计的读取策略,避免拖慢 HUD 的一秒观察周期:

  1. findGitLayout(cwd)?.worktreeRoot 解析嵌套 worktree 场景下的仓库根(这正是 PR 说明中“resolves configuration from nested worktree paths”的落地);
  2. 列出 .omx/state/finish-runs/ 下的文件,用正则 ^finish-([0-9a-z]+)-\d+-[^/]+\.jsonl$ 过滤,并把文件名中的 base36 编码的运行起始时间解析出来(Number.parseInt(match[1], 36));
  3. 按起始时间降序只保留最新的 GUARDEX_FINISH_MAX_CANDIDATES 个候选——在 stat 调用之前就完成数量裁剪,保证一次 HUD tick 的元数据读取有界;
  4. 对候选做 statmtimeMs,按修改时间降序,逐个读文件尾部readFileTail 只读最后 GUARDEX_FINISH_TAIL_BYTES 字节)取最后一条合法事件;
  5. 读到第一条有效状态即返回;文件不可读/不存在/解析失败全部 fail-open,仓库没有 Guardex 状态目录时 HUD 正常渲染。

其中 readFileTailsrc/hud/state.ts)先 stat 得到文件大小,再取 min(size, TAIL_BYTES) 从文件末尾偏移处读取,避免把大文件整体读入内存。解析时对“写入方正在追加最后一行 JSON”的竞态也做了容错(catch {} 忽略半行)。

渲染:进度文本与自旋动画

渲染逻辑在 src/hud/render.ts

const GUARDEX_SPINNER_FRAMES = ['◐', '◓', '◑', '◒'] as const;
const GUARDEX_SPINNER_FRAME_MS = 400;

function renderGuardexFinish(ctx: HudRenderContext): string | null {
  if (!ctx.guardexFinish?.active) return null;
  const { index, total, state } = ctx.guardexFinish;
  if (!Number.isSafeInteger(index) || !Number.isSafeInteger(total) || index <= 0 || total <= 0 || index > total) return null;
  const stage = sanitizeDynamicText(ctx.guardexFinish.stage).slice(0, 40);
  if (!stage) return null;
  const animates = state === 'running' && (stage === 'review' || stage === 'autofix');
  const frame = animates
    ? ` ${GUARDEX_SPINNER_FRAMES[Math.floor(Date.now() / GUARDEX_SPINNER_FRAME_MS) % GUARDEX_SPINNER_FRAMES.length]}`
    : '';
  return yellow(`gx:${index}/${total} ${stage}${frame}`);
}

要点:

  • 输出格式为 gx:<index>/<total> <stage>,例如 gx:2/5 review
  • 只有 state === 'running'stagereviewautofix 时才附加自旋动画帧;
  • 自旋帧切换周期 GUARDEX_SPINNER_FRAME_MS = 400ms,四个帧 ◐ ◓ ◑ ◒ 循环。HUD 的观察间隔为一秒,400ms 帧周期保证动画推进节奏稳定、不会在一个 tick 内跳跃式快进——这正是 PR 说明中“spinner cadence advances under the one-second HUD watch interval”的含义;
  • sanitizeDynamicText 对动态内容做清洗后再截断,避免把 Guardex 文件中的原始文本直接写入终端。

数据流总结

GitGuardex (gx branch finish)
   ↓ 写入 JSONL 事件
.omx/state/finish-runs/finish-<base36>-<...>.jsonl
   ↓ omx hud 每 tick 读取(限候选数 → stat → 读尾部 → 校验/归一化)
GuardexFinishStateForHud
   ↓ renderGuardexFinish
HUD 窗口:gx:2/5 review ◑

相关测试见 src/hud/tests/state.test.ts(覆盖事件解析、终态忽略、年龄过滤、PID 存活判定、依赖注入的 statFile 模拟)与 src/hud/tests/render.test.ts(覆盖 gx: 渲染与动画)。

印尼语 README 与本地化导航同步

#3599 新增了 Bahasa Indonesia 翻译(docs/readme/README.id.md),并同步了所有本地化 README 的语言导航。目前仓库 docs/readme/ 下已存在 16 种语言版本(deelesfriditjakoplptrutrukvizh-TWzh 及英文主 README),所有语言的导航区块保持一致,便于读者在各语言版本之间切换。这属于纯文档变更,不涉及运行时行为。

常见问题与使用建议

Q:升级到 0.21.2 后什么都没配,为什么 HUD 没有 gx: 行? A:这是预期行为。GitGuardex 集成默认关闭(guardex.enabled === false),且只有项目级 .omx/hud-config.json 显式开启后 OmX 才会读取 Guardex 状态。

Q:水合失败会影响安装吗? A:不会。hydrateOmxRuntimeNonFatal 对任何失败都只打印非致命日志并返回 undefined;安装/更新照常完成,仅原生运行时功能保持禁用。

Q:Guardex 状态目录不存在时会怎样? A:HUD 正常渲染,readGuardexFinishState 捕获目录缺失异常并返回 null,不会影响其他状态行。

Q:如何验证我的环境是否成功水合? A:安装/更新日志中会出现 [omx] Hydrated omx-runtime for this platform: <path>;若平台无可用资产,则出现“native runtime features remain disabled”提示。

Q:这些变更与 0.21.1 相比有破坏性吗? A:没有。0.21.2 是补丁版本,两项功能变更均为新增能力且默认不改变既有行为(水合补全了安装/更新路径,Guardex 集成默认关闭)。

结语

0.21.2 是一个“小而稳”的补丁版本:它补全了 macOS arm64 上原生运行时在安装/更新路径中的水合闭环,为 HUD 增加了可选、有界、fail-open 的 GitGuardex 收尾进度展示,并完成了印尼语本地化。其实现处处体现工程上的克制——水合非致命、校验 fail-closed、Guardex 默认关闭、读取候选在 stat 前裁剪、动画帧周期适配观察间隔——这些细节都值得在 src/scripts/postinstall.tssrc/cli/update.tssrc/hud/state.tssrc/hud/render.ts 中逐一对照阅读。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
899
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
525