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
4 changes: 2 additions & 2 deletions skills/ocx/references/01_management_surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -750,15 +750,15 @@ JSON mode: `payload`.

### `ocx system codex-restart`

Restart the Codex app-server.
Restart the Codex desktop app and app-servers.

| Method | Route |
|---|---|
| POST | `/api/system/codex-restart` |

| 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`.
Expand Down
4 changes: 2 additions & 2 deletions src/cli/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions src/cli/system-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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."]);
Comment thread
luvs01 marked this conversation as resolved.
} else if (sub === "update") await update(rest, deps);
else throw new CliUsageError(`unknown system command ${sub}`, USAGE);
});
Expand Down
2 changes: 2 additions & 0 deletions structure/clients/claude-desktop.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ The hub-side CLI dashboard uses the [management ingress address](../runtime.md#h

Native main reauthentication follows the [CLI JSON output contract](../runtime.md#native-main-reauth-json-output).

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.
Expand Down
2 changes: 2 additions & 0 deletions structure/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Hub management ingress also selects the [local dashboard address](runtime.md#hub

Native main reauthentication follows the [CLI JSON output contract](runtime.md#native-main-reauth-json-output).

The Codex restart command follows the [CLI restart scope contract](runtime.md#cli-codex-restart-scope).

## Config surface

### OpenCodex home and live process state
Expand Down
2 changes: 2 additions & 0 deletions structure/ops/docs-and-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ The CLI default dashboard address follows the [management ingress bind](../runti

Native main reauthentication follows the [CLI JSON output contract](../runtime.md#native-main-reauth-json-output).

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
Expand Down
4 changes: 4 additions & 0 deletions structure/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Catalog-derived reasoning-level diagnostics are escaped only at the human-output

`src/cli/account-main.ts` emits one JSON object to stdout when `ocx account main reauth --device --no-wait --json` succeeds. The human-readable `follow up:` line is emitted only without `--json`; `flowId` remains available for status polling. `tests/cli/cli-native-profile.test.ts` parses the complete captured stdout and preserves coverage of the human follow-up.

## 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.
Expand Down
34 changes: 34 additions & 0 deletions tests/cli/cli-headless-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,40 @@ import { repoPath } from "../helpers/repo-root";
type Recorded = { path: string; method: string; body: unknown };
const servers: Array<ReturnType<typeof Bun.serve>> = [];

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 }));
Expand Down
Loading