Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/zh-cn/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ opencodex 的 CLI 是 `ocx`。它会根据第一个命令名进行分发;文

管理命令会通过实时代理的管理 API 往返调用,使用记录下来的运行时端口和身份检查,而不是维护第二条配置路径。已停止或不可达的代理会被表示为 HTTP 503,并导致 CLI 以非零状态退出。明确标注为离线配置操作的命令,则可以在没有实时代理的情况下验证并编辑配置文件。

`check` 不查询注册表,也不运行 Codex。在 POSIX 上,当已发布启动器捕获的环境候选项与运行时解析器选择的规范路径一致时,`selectionAttested` 可以为 `true`;如果关联的 npm-global 软件包清单证明了所有权,`managed` 可以为 `true`。持久化候选项仍不获认证。Windows x64 还支持经证明绑定的绝对路径标准 npm `codex.cmd`,以及由持有句柄绑定的 Node/npm 工具链(存在时为前缀本地,确认不存在时为锚定到启动器架构的已捕获 PATH 对)和已验证的前缀内平台支持组件,并使用拒绝重解析的持有句柄。其他 Windows 布局仍然推迟或不受管理。Windows 的 `managed` 结果仅对观察到的安装布局进行分类:绑定的 Node/npm 是更新器的只读元数据工具链,并不保证后续 `codex.cmd` 调用将解析到哪个解释器。直接通过 Bun 或源码运行时没有启动器证明。`plan` 使用隔离的临时 npm 配置、缓存和日志查询官方注册表,并拒绝活动会话或不完整的进程观测。它不安装软件,也不向进程发送信号。Windows x64 的 apply 要求具有经过验证的私有写入权限的受限 npm 前缀,并保留无关的软件包和 shim;不受支持的已观测布局返回 `applicable: false` 且 `refusal: windows_apply_disabled`;仅 PATH 选择、OpenCodex 自启动包装器和 global/vendor 回退仍然推迟。请参阅[完整命令和拒绝原因(英文)](/reference/cli/agents/#read-only-codex-cli-update-plan)。
`check` 不查询注册表,也不运行 Codex。在 POSIX 上,当已发布启动器捕获的环境候选项与运行时解析器选择的规范路径一致时,`selectionAttested` 可以为 `true`;如果关联的 npm-global 软件包清单证明了所有权,并且在报告了候选版本时该版本与清单版本一致,`managed` 可以为 `true`。持久化候选项仍不获认证。Windows x64 还支持经证明绑定的绝对路径标准 npm `codex.cmd`,以及由持有句柄绑定的 Node/npm 工具链(存在时为前缀本地,确认不存在时为锚定到启动器架构的已捕获 PATH 对)和已验证的前缀内平台支持组件,并使用拒绝重解析的持有句柄。其他 Windows 布局仍然推迟或不受管理。Windows 的 `managed` 结果仅对观察到的安装布局进行分类:绑定的 Node/npm 是更新器的只读元数据工具链,并不保证后续 `codex.cmd` 调用将解析到哪个解释器。直接通过 Bun 或源码运行时没有启动器证明。`plan` 使用隔离的临时 npm 配置、缓存和日志查询官方注册表,并拒绝活动会话或不完整的进程观测。它不安装软件,也不向进程发送信号。Windows x64 的 apply 要求具有经过验证的私有写入权限的受限 npm 前缀,并保留无关的软件包和 shim;不受支持的已观测布局返回 `applicable: false` 且 `refusal: windows_apply_disabled`;仅 PATH 选择、OpenCodex 自启动包装器和 global/vendor 回退仍然推迟。请参阅[完整命令和拒绝原因(英文)](/reference/cli/agents/#read-only-codex-cli-update-plan)。

有关 Windows x64 安装观察,请参阅 [`attest` 命令](/zh-cn/reference/cli/agents/)。未提供显式路径时,该命令观察由受证明约束的启动器快照识别出的已选候选项;它不授予更新权限,也不证明运行时选择。

Expand Down
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/zh-cn/reference/cli/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ ocx system settings --stream-mode eager-relay
ocx system codex-cli-update check --json
```

`check` 不查询注册表,也不运行 Codex。在 POSIX 上,当已发布启动器捕获的环境候选项与运行时解析器选择的规范路径一致时,`selectionAttested` 可以为 `true`;如果关联的 npm-global 软件包清单证明了所有权,`managed` 可以为 `true`。持久化候选项仍不获认证。Windows x64 还支持经证明绑定的绝对路径标准 npm `codex.cmd`,以及由持有句柄绑定的 Node/npm 工具链(存在时为前缀本地,确认不存在时为锚定到启动器架构的已捕获 PATH 对)和已验证的前缀内平台支持组件,并使用拒绝重解析的持有句柄。其他 Windows 布局仍然推迟或不受管理。Windows 的 `managed` 结果仅对观察到的安装布局进行分类:绑定的 Node/npm 是更新器的只读元数据工具链,并不保证后续 `codex.cmd` 调用将解析到哪个解释器。直接通过 Bun 或源码运行时没有启动器证明。`plan` 使用隔离的临时 npm 配置、缓存和日志查询官方注册表,并拒绝活动会话或不完整的进程观测。它不安装软件,也不向进程发送信号。Windows x64 的 apply 要求具有经过验证的私有写入权限的受限 npm 前缀,并保留无关的软件包和 shim;不受支持的已观测布局返回 `applicable: false` 且 `refusal: windows_apply_disabled`;仅 PATH 选择、OpenCodex 自启动包装器和 global/vendor 回退仍然推迟。请参阅[完整命令和拒绝原因(英文)](/reference/cli/agents/#read-only-codex-cli-update-plan)。
`check` 不查询注册表,也不运行 Codex。在 POSIX 上,当已发布启动器捕获的环境候选项与运行时解析器选择的规范路径一致时,`selectionAttested` 可以为 `true`;如果关联的 npm-global 软件包清单证明了所有权,并且在报告了候选版本时该版本与清单版本一致,`managed` 可以为 `true`。持久化候选项仍不获认证。Windows x64 还支持经证明绑定的绝对路径标准 npm `codex.cmd`,以及由持有句柄绑定的 Node/npm 工具链(存在时为前缀本地,确认不存在时为锚定到启动器架构的已捕获 PATH 对)和已验证的前缀内平台支持组件,并使用拒绝重解析的持有句柄。其他 Windows 布局仍然推迟或不受管理。Windows 的 `managed` 结果仅对观察到的安装布局进行分类:绑定的 Node/npm 是更新器的只读元数据工具链,并不保证后续 `codex.cmd` 调用将解析到哪个解释器。直接通过 Bun 或源码运行时没有启动器证明。`plan` 使用隔离的临时 npm 配置、缓存和日志查询官方注册表,并拒绝活动会话或不完整的进程观测。它不安装软件,也不向进程发送信号。Windows x64 的 apply 要求具有经过验证的私有写入权限的受限 npm 前缀,并保留无关的软件包和 shim;不受支持的已观测布局返回 `applicable: false` 且 `refusal: windows_apply_disabled`;仅 PATH 选择、OpenCodex 自启动包装器和 global/vendor 回退仍然推迟。请参阅[完整命令和拒绝原因(英文)](/reference/cli/agents/#read-only-codex-cli-update-plan)。

在 Windows 上,如果捕获到 `CODEX_CLI_PATH=codex` 这样的裸命令、远程路径或设备路径,则报告 `candidate_path_unavailable`。这些情况下候选项已被捕获,但其路径不适用于此检查。

Expand Down
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/zh-tw/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ opencodex 的命令列工具是 `ocx`。它依第一個命令名稱分派,有
設定路徑。停止或無法連線的代理以 HTTP 503 呈現,並產生非零的 CLI 離開碼。明確記載為
離線設定操作的命令,可以在沒有執行中代理的情況下驗證與編輯設定檔。

`check` 不查詢註冊表,也不執行 Codex。在 POSIX 上,當已發布啟動器擷取的環境候選項與執行階段解析器選擇的正規路徑相同時,`selectionAttested` 可以為 `true`;若關聯的 npm-global 套件資訊清單證明了所有權,`managed` 可以為 `true`。持久化候選項仍不獲認證。Windows x64 也支援經證明繫結的絕對路徑標準 npm `codex.cmd`,以及由持有控制代碼繫結的 Node/npm 工具鏈(存在時為前綴本機,確認不存在時為錨定到啟動器架構的已擷取 PATH 配對)和已驗證的前綴內平台支援元件,並使用拒絕重新剖析的持有控制代碼。其他 Windows 配置仍然延後或不受管理。Windows 的 `managed` 結果僅對觀測到的安裝配置進行分類:繫結的 Node/npm 是更新器的唯讀中繼資料工具鏈,並不保證後續 `codex.cmd` 呼叫將解析到哪個直譯器。直接透過 Bun 或原始碼執行時沒有啟動器證明。`plan` 使用隔離的暫存 npm 設定、快取和日誌查詢官方註冊表,並拒絕活動工作階段或不完整的程序觀測。它不安裝軟體,也不向程序傳送訊號。Windows x64 的 apply 需要具備經過驗證的私人寫入權限的受限 npm 前綴,並保留無關的套件和 shim;不受支援的已觀測配置回傳 `applicable: false` 且 `refusal: windows_apply_disabled`;僅 PATH 選取、OpenCodex 自動啟動包裝器和 global/vendor 後援仍然延後。請參閱[完整命令與拒絕原因(英文)](/reference/cli/agents/#read-only-codex-cli-update-plan)。
`check` 不查詢註冊表,也不執行 Codex。在 POSIX 上,當已發布啟動器擷取的環境候選項與執行階段解析器選擇的正規路徑相同時,`selectionAttested` 可以為 `true`;若關聯的 npm-global 套件資訊清單證明了所有權,且在回報候選版本時該版本與資訊清單版本一致,`managed` 可以為 `true`。持久化候選項仍不獲認證。Windows x64 也支援經證明繫結的絕對路徑標準 npm `codex.cmd`,以及由持有控制代碼繫結的 Node/npm 工具鏈(存在時為前綴本機,確認不存在時為錨定到啟動器架構的已擷取 PATH 配對)和已驗證的前綴內平台支援元件,並使用拒絕重新剖析的持有控制代碼。其他 Windows 配置仍然延後或不受管理。Windows 的 `managed` 結果僅對觀測到的安裝配置進行分類:繫結的 Node/npm 是更新器的唯讀中繼資料工具鏈,並不保證後續 `codex.cmd` 呼叫將解析到哪個直譯器。直接透過 Bun 或原始碼執行時沒有啟動器證明。`plan` 使用隔離的暫存 npm 設定、快取和日誌查詢官方註冊表,並拒絕活動工作階段或不完整的程序觀測。它不安裝軟體,也不向程序傳送訊號。Windows x64 的 apply 需要具備經過驗證的私人寫入權限的受限 npm 前綴,並保留無關的套件和 shim;不受支援的已觀測配置回傳 `applicable: false` 且 `refusal: windows_apply_disabled`;僅 PATH 選取、OpenCodex 自動啟動包裝器和 global/vendor 後援仍然延後。請參閱[完整命令與拒絕原因(英文)](/reference/cli/agents/#read-only-codex-cli-update-plan)。

觀測 Windows x64 安裝的方式請參閱 [`attest` 命令](/zh-tw/reference/cli/agents/)。未提供明確路徑時,該命令會觀測由受證明約束的啟動器快照識別出的已選候選項;它不授予更新權限,也不證明執行階段選擇。

Expand Down
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/zh-tw/reference/cli/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ ocx system settings --stream-mode eager-relay
ocx system codex-cli-update check --json
```

`check` 不查詢註冊表,也不執行 Codex。在 POSIX 上,當已發布啟動器擷取的環境候選項與執行階段解析器選擇的正規路徑相同時,`selectionAttested` 可以為 `true`;若關聯的 npm-global 套件資訊清單證明了所有權,`managed` 可以為 `true`。持久化候選項仍不獲認證。Windows x64 也支援經證明繫結的絕對路徑標準 npm `codex.cmd`,以及由持有控制代碼繫結的 Node/npm 工具鏈(存在時為前綴本機,確認不存在時為錨定到啟動器架構的已擷取 PATH 配對)和已驗證的前綴內平台支援元件,並使用拒絕重新剖析的持有控制代碼。其他 Windows 配置仍然延後或不受管理。Windows 的 `managed` 結果僅對觀測到的安裝配置進行分類:繫結的 Node/npm 是更新器的唯讀中繼資料工具鏈,並不保證後續 `codex.cmd` 呼叫將解析到哪個直譯器。直接透過 Bun 或原始碼執行時沒有啟動器證明。`plan` 使用隔離的暫存 npm 設定、快取和日誌查詢官方註冊表,並拒絕活動工作階段或不完整的程序觀測。它不安裝軟體,也不向程序傳送訊號。Windows x64 的 apply 需要具備經過驗證的私人寫入權限的受限 npm 前綴,並保留無關的套件和 shim;不受支援的已觀測配置回傳 `applicable: false` 且 `refusal: windows_apply_disabled`;僅 PATH 選取、OpenCodex 自動啟動包裝器和 global/vendor 後援仍然延後。請參閱[完整命令與拒絕原因(英文)](/reference/cli/agents/#read-only-codex-cli-update-plan)。
`check` 不查詢註冊表,也不執行 Codex。在 POSIX 上,當已發布啟動器擷取的環境候選項與執行階段解析器選擇的正規路徑相同時,`selectionAttested` 可以為 `true`;若關聯的 npm-global 套件資訊清單證明了所有權,且在回報候選版本時該版本與資訊清單版本一致,`managed` 可以為 `true`。持久化候選項仍不獲認證。Windows x64 也支援經證明繫結的絕對路徑標準 npm `codex.cmd`,以及由持有控制代碼繫結的 Node/npm 工具鏈(存在時為前綴本機,確認不存在時為錨定到啟動器架構的已擷取 PATH 配對)和已驗證的前綴內平台支援元件,並使用拒絕重新剖析的持有控制代碼。其他 Windows 配置仍然延後或不受管理。Windows 的 `managed` 結果僅對觀測到的安裝配置進行分類:繫結的 Node/npm 是更新器的唯讀中繼資料工具鏈,並不保證後續 `codex.cmd` 呼叫將解析到哪個直譯器。直接透過 Bun 或原始碼執行時沒有啟動器證明。`plan` 使用隔離的暫存 npm 設定、快取和日誌查詢官方註冊表,並拒絕活動工作階段或不完整的程序觀測。它不安裝軟體,也不向程序傳送訊號。Windows x64 的 apply 需要具備經過驗證的私人寫入權限的受限 npm 前綴,並保留無關的套件和 shim;不受支援的已觀測配置回傳 `applicable: false` 且 `refusal: windows_apply_disabled`;僅 PATH 選取、OpenCodex 自動啟動包裝器和 global/vendor 後援仍然延後。請參閱[完整命令與拒絕原因(英文)](/reference/cli/agents/#read-only-codex-cli-update-plan)。

在 Windows 上,如果擷取到 `CODEX_CLI_PATH=codex` 這類單純命令名稱、遠端路徑或裝置路徑,則回報 `candidate_path_unavailable`。這些情況已有擷取的候選項,但其路徑不適用於此檢查。

Expand Down
2 changes: 1 addition & 1 deletion scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@
"cli-api-protocols.test.ts": "cli",
"cli-catalog-prewarm.test.ts": "cli",
"cli-ensure-readiness.test.ts": "cli",
"cli-codex-cli-update-boundary.test.ts": "cli", "windows-codex-cli-job-store.test.ts": "windows", "codex-cli-update-windows-apply.test.ts": "codex-integration", "codex-cli-update-windows-recovery.test.ts": "codex-integration",
"cli-codex-cli-update-boundary.test.ts": "cli", "windows-codex-cli-job-store.test.ts": "windows", "codex-cli-update-windows-apply.test.ts": "codex-integration", "codex-cli-update-windows-recovery.test.ts": "codex-integration", "codex-cli-update-windows-review.test.ts": "codex-integration",
"cli-codex-cli-update.test.ts": "cli",
"cli-codex-log-guard-compact.test.ts": "cli",
"cli-codex-log-guard-protection.test.ts": "cli",
Expand Down
30 changes: 25 additions & 5 deletions src/codex/cli-update-job-store-windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
* empirical cross-user proof.
*/
import { createHash, randomUUID } from "node:crypto";
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
import { closeSync, fsyncSync, mkdirSync, openSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
import { basename, join, resolve, toNamespacedPath } from "node:path";
import type { Library, Symbols } from "bun:ffi";
import * as recoveryFfi from "bun:ffi";
Expand Down Expand Up @@ -656,21 +656,27 @@ async function acquireLock(ctx: Ctx, prepared: Prepared): Promise<HeldLock> {
* while its pin shows this call's identity. A replaced owner or lock survives,
* which keeps admission fail-closed on the leftover.
*/
function releaseLock(ctx: Ctx, held: HeldLock): void {
function releaseLock(ctx: Ctx, held: HeldLock): boolean {
let ownerDeleted = false, dirDeleted = false;
try {
const now = inspect(ctx, held.owner, false);
if (sameIdentity(now, held.ownerIdentity) && now.links === 1
&& now.size === Buffer.byteLength(held.record)
&& now.size <= 1024
&& readAll(ctx, held.owner, now.size).equals(Buffer.from(held.record))) {
markDelete(ctx, held.owner);
ownerDeleted = true;
}
} catch { /* fail closed; a leftover owner keeps the lock alive */ }
ctx.pins.release(held.owner, ctx.kernel);
try {
if (sameIdentity(inspect(ctx, held.dir, true), held.dirIdentity)) markDelete(ctx, held.dir);
if (sameIdentity(inspect(ctx, held.dir, true), held.dirIdentity)) {
markDelete(ctx, held.dir);
dirDeleted = true;
}
} catch { /* fail closed; a leftover lock blocks later admission */ }
ctx.pins.release(held.dir, ctx.kernel);
return ownerDeleted && dirDeleted;
}

export interface WindowsCliRecoveryIo {
Expand Down Expand Up @@ -779,6 +785,7 @@ export async function openWindowsCliApplyAuthority(prefix: string, acquire: bool
let held: HeldLock | undefined;
let transferred = false;
let closed = false;
let resourcesReleased = false;
try {
// Publish the context before the first pin/open or async verification can fail.
// The caller already holds its Worker token; failed cleanup remains rooted there.
Expand Down Expand Up @@ -820,6 +827,8 @@ export async function openWindowsCliApplyAuthority(prefix: string, acquire: bool
return {
lock,
verify,
/** Disposal proof is independent of the ACL diagnostic returned by release(). */
get resourcesReleased() { return resourcesReleased; },
verifyIdentitySync() { if (closed) throw unsafe(); verifyIdentitySync(); },
async createStaging(): Promise<string> {
if (closed || !held || stage) throw unsafe();
Expand Down Expand Up @@ -847,9 +856,20 @@ export async function openWindowsCliApplyAuthority(prefix: string, acquire: bool
async release(): Promise<boolean> {
if (closed) return false;
closed = true;
try { await verifyHeld(); if (held) releaseLock(ctx, held); }
let aclVerified = true;
let lockDeleted = !acquire;
try {
verifyIdentitySync();
// An ACL lookup failure must not strand a lock whose original pins
// still prove ownership. It remains a failed release verdict, even
// when the independently token/identity-fenced deletion succeeds.
try { await verifyAcl(entries); } catch { aclVerified = false; }
verifyIdentitySync();
if (held) lockDeleted = releaseLock(ctx, held);
}
finally { ctx.pins.closeAll(kernel); }
return pins.remaining === 0 && (!acquire || !existsSync(lock));
resourcesReleased = pins.remaining === 0 && !stage && lockDeleted;
return aclVerified && resourcesReleased;
},
};
} finally { if (!transferred) ctx.pins.closeAll(kernel); }
Expand Down
Loading
Loading