From 00f4330dd7cc4bd1ab172c719b79aae3ea20eafd Mon Sep 17 00:00:00 2001 From: luvs01 <27862058+luvs01@users.noreply.github.com> Date: Mon, 14 Sep 2026 18:32:32 +0900 Subject: [PATCH 1/2] fix(cli): describe the full Codex desktop restart scope --- skills/ocx/references/01_management_surface.md | 4 ++-- src/cli/capabilities.ts | 4 ++-- src/cli/system-command.ts | 8 ++++---- structure/clients/claude-desktop.md | 2 ++ structure/config.md | 2 ++ structure/ops/docs-and-release.md | 2 ++ structure/runtime.md | 4 ++++ 7 files changed, 18 insertions(+), 8 deletions(-) diff --git a/skills/ocx/references/01_management_surface.md b/skills/ocx/references/01_management_surface.md index da019297d1..4cb391504a 100644 --- a/skills/ocx/references/01_management_surface.md +++ b/skills/ocx/references/01_management_surface.md @@ -750,7 +750,7 @@ JSON mode: `payload`. ### `ocx system codex-restart` -Restart the Codex app-server. +Restart the Codex desktop app and app-servers. | Method | Route | |---|---| @@ -758,7 +758,7 @@ Restart the Codex app-server. | Flag | Value | Meaning | |---|---|---| -| `--yes` | boolean | Required: restarts the operator's running Codex app-server. | +| `--yes` | boolean | Required: fully quits and relaunches the operator's Codex desktop app and restarts its app-servers. | | `--json` | boolean | Emit the restart result as JSON. | JSON mode: `payload`. diff --git a/src/cli/capabilities.ts b/src/cli/capabilities.ts index 36aa8124cc..309badbfd2 100644 --- a/src/cli/capabilities.ts +++ b/src/cli/capabilities.ts @@ -710,10 +710,10 @@ export const CAPABILITIES: readonly Capability[] = [ }, { command: ["system", "codex-restart"], - summary: "Restart the Codex app-server.", + summary: "Restart the Codex desktop app and app-servers.", routes: [{ method: "POST", path: "/api/system/codex-restart" }], flags: [ - { name: "--yes", value: "boolean", summary: "Required: restarts the operator's running Codex app-server." }, + { name: "--yes", value: "boolean", summary: "Required: fully quits and relaunches the operator's Codex desktop app and restarts its app-servers." }, { name: "--json", value: "boolean", summary: "Emit the restart result as JSON." }, ], mutates: true, diff --git a/src/cli/system-command.ts b/src/cli/system-command.ts index 03eb900887..a3b46b49d6 100644 --- a/src/cli/system-command.ts +++ b/src/cli/system-command.ts @@ -130,14 +130,14 @@ export async function handleSystemCommand(argv: string[], deps: RuntimeApiDeps = const args = [...rest]; const wantsJson = takeFlag(args, "--json"); rejectArgs(args, USAGE); printData(await runtimeRequest("/api/system/codex-app-server", {}, deps), wantsJson); } else if (sub === "codex-restart") { - // --yes required: this restarts the user's running Codex app-server, so it is exactly the - // class of action that must not happen because an agent guessed a subcommand. + // --yes required: this fully quits and relaunches the user's Codex desktop app as well as + // restarting app-servers; an agent guessing a subcommand must not interrupt that session. const args = [...rest]; const wantsJson = takeFlag(args, "--json"); const yes = takeFlag(args, "--yes"); - if (!yes) throw new CliUsageError("system codex-restart requires --yes", USAGE); + if (!yes) throw new CliUsageError("system codex-restart requires --yes: this fully quits and relaunches the Codex desktop app and restarts its app-servers", USAGE); rejectArgs(args, USAGE); - printData(await runtimeRequest("/api/system/codex-restart", { method: "POST" }, deps), wantsJson, ["Codex app-server restart requested."]); + printData(await runtimeRequest("/api/system/codex-restart", { method: "POST" }, deps), wantsJson, ["Codex desktop app and app-server restart requested."]); } else if (sub === "update") await update(rest, deps); else throw new CliUsageError(`unknown system command ${sub}`, USAGE); }); diff --git a/structure/clients/claude-desktop.md b/structure/clients/claude-desktop.md index f64a278757..987591f435 100644 --- a/structure/clients/claude-desktop.md +++ b/structure/clients/claude-desktop.md @@ -12,6 +12,8 @@ Claude-only connections keep their existing non-failing readiness policy; displa The hub-side CLI dashboard uses the [management ingress address](../runtime.md#hub-management-dashboard-address); this does not change connected Desktop profile endpoints. +The Codex restart command follows the [CLI restart scope contract](../runtime.md#cli-codex-restart-scope). + ## Connected Claude Desktop profiles The connection's local Codex readiness check follows the [selected-runtime probe contract](../runtime.md#remote-hub-hardening-ownership); general status hands its resolved command to this check instead of probing the version twice. diff --git a/structure/config.md b/structure/config.md index a4c0b97ead..12bc841b50 100644 --- a/structure/config.md +++ b/structure/config.md @@ -7,6 +7,8 @@ Connected-client catalog diagnostics use the [terminal rendering contract](runti Hub management ingress also selects the [local dashboard address](runtime.md#hub-management-dashboard-address) using its configured port. +The Codex restart command follows the [CLI restart scope contract](runtime.md#cli-codex-restart-scope). + ## Config surface ### OpenCodex home and live process state diff --git a/structure/ops/docs-and-release.md b/structure/ops/docs-and-release.md index f4474c6419..2924261a4b 100644 --- a/structure/ops/docs-and-release.md +++ b/structure/ops/docs-and-release.md @@ -9,6 +9,8 @@ Human-readable connect and sync-refresh diagnostics follow the [terminal renderi The CLI default dashboard address follows the [management ingress bind](../runtime.md#hub-management-dashboard-address), covered by `tests/cli/cli-dispatch.test.ts`. +The Codex restart command follows the [CLI restart scope contract](../runtime.md#cli-codex-restart-scope). + ## Public docs The public documentation site lives in `docs-site/` and is built with Astro + Starlight. English is diff --git a/structure/runtime.md b/structure/runtime.md index e4a50ba3f8..ae8c4529b6 100644 --- a/structure/runtime.md +++ b/structure/runtime.md @@ -13,6 +13,10 @@ Shared parsing and streaming follow the [request-copy](transports/byte-accountin Catalog-derived reasoning-level diagnostics are escaped only at the human-output boundary, which `src/cli/runtime-api.ts` owns alongside the human/JSON print split. Every CLI path that prints a hub-supplied catalog value renders it there: the first-time refusal in `src/cli/connect.ts` and the connected `ocx sync` refusal in `src/cli/dispatch.ts`. C0/C1 controls, DEL, and Unicode line/paragraph separators print as visible hexadecimal escapes; structured status retains the exact reason, and a rendered failure keeps the domain error as its `cause`. The ready/unverified/incompatible classification and exit policy are unchanged. +## CLI Codex restart scope + +`ocx system codex-restart` requests a full Codex desktop-app restart and app-server restarts through the management endpoint. `src/cli/capabilities.ts` names that scope in its summary and `--yes` description; `src/cli/system-command.ts` explains the desktop interruption when confirmation is missing and sends no restart request. Human output says the restart was requested, while `--json` preserves the complete server result, including skipped or refused desktop outcomes. + ## Hub management dashboard address When hub management ingress is enabled, `src/cli/dispatch.ts` opens the dashboard on the literal IPv4 loopback address and configured ingress port, matching the listener in `src/server/index.ts`. Other dashboard address selection is unchanged. From 6f4e222e5c0c15f0962e321b7853790c407eff6a Mon Sep 17 00:00:00 2001 From: luvs01 <27862058+luvs01@users.noreply.github.com> Date: Mon, 14 Sep 2026 18:40:24 +0900 Subject: [PATCH 2/2] test(cli): verify restart confirmation and result boundaries --- tests/cli/cli-headless-parity.test.ts | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/cli/cli-headless-parity.test.ts b/tests/cli/cli-headless-parity.test.ts index 9a11c696ac..a3d861d14a 100644 --- a/tests/cli/cli-headless-parity.test.ts +++ b/tests/cli/cli-headless-parity.test.ts @@ -20,6 +20,40 @@ import { repoPath } from "../helpers/repo-root"; type Recorded = { path: string; method: string; body: unknown }; const servers: Array> = []; +describe("ocx system codex-restart confirmation", () => { + test("names the desktop interruption before any unconfirmed request", async () => { + const { requests, deps } = fakeRuntime(); + const errors = spyOn(console, "error").mockImplementation(() => {}); + try { + expect(await handleSystemCommand(["codex-restart"], deps)).toBe(2); + expect(requests).toHaveLength(0); + const warning = errors.mock.calls.flat().join(" "); + expect(warning).toContain("requires --yes"); + expect(warning).toContain("fully quits and relaunches the Codex desktop app"); + } finally { errors.mockRestore(); } + }); + + test.each([false, true])("preserves requested versus completed outcomes (json=%s)", async wantsJson => { + // A skipped Desktop outcome must survive JSON output; this fixture cannot restart processes. + const result = { success: true, code: "nothing_running", requested: [], stopped: [], + desktopApp: { attempted: false, relaunch: "skipped", reason: "self_ancestry" } }; + const { requests, deps } = fakeRuntime(() => result); + const output = spyOn(console, "log").mockImplementation(() => {}); + try { + const argv = ["codex-restart", "--yes", ...(wantsJson ? ["--json"] : [])]; + expect(await handleSystemCommand(argv, deps)).toBe(0); + expect(requests).toEqual([{ path: "/api/system/codex-restart", method: "POST", body: null }]); + const text = output.mock.calls.flat().join("\n"); + if (wantsJson) expect(JSON.parse(text)).toEqual(result); + else { + expect(text).toContain("Codex desktop app"); + expect(text).toContain("restart requested."); + expect(text).not.toContain("restarted"); + } + } finally { output.mockRestore(); } + }); +}); + describe("ocx system settings client compaction", () => { test("persists the explicit boolean through the shared settings endpoint", async () => { const { requests, deps } = fakeRuntime((_req, body) => ({ ok: true, ...body }));