Normalize formatting in docs, web assets, and sample scripts (#52256)

## What changed

- Reformat Markdown tables and lists, login pages, inline visualization HTML, and sample JavaScript scripts.
- Escape union-type pipes in Python SDK API tables so they render within their cells.
- Keep the formatting-recipe test focused on the working directory and formatter commands by removing assertions on comment text.

GitOrigin-RevId: ae46d5b40fd013553de8b5ccf2c41d76be65aba8
This commit is contained in:
Adam Perry @ OpenAI
2026-10-08 20:11:43 +00:00
committed by copyberry
parent 75e0b4088e
commit 3b6ab3471a
23 changed files with 481 additions and 384 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ runs:
release_tag="rusty-v8-v${version}"
base_url="https://github.com/openai/codex/releases/download/${release_tag}"
binding_dir="${RUNNER_TEMP}/rusty_v8"
profile="ptrcomp_sandbox_release"
if [[ "$TARGET" == *-pc-windows-msvc ]]; then
+10 -8
View File
@@ -49,9 +49,11 @@ Eligible managed daemons check for updates after five minutes, then hourly by
default. Edit `CODEX_HOME/app-server-daemon/settings.json` to change this:
```json
{"remoteControlEnabled": false,
"shutdownGraceSeconds": 60,
"updater": {"autoUpdateEnabled": false, "updateIntervalMinutes": 120}}
{
"remoteControlEnabled": false,
"shutdownGraceSeconds": 60,
"updater": { "autoUpdateEnabled": false, "updateIntervalMinutes": 120 }
}
```
Positive minute intervals have no configured cap. `daemon restart` applies the
@@ -114,11 +116,11 @@ compatible dedicated package before stopping the legacy updater and daemon,
selecting the new package, and restarting only a previously running daemon.
The old CLI package files and selection remain unchanged.
| Situation | What starts | Does this daemon fetch new binaries? | Does a running app-server eventually move to a newer binary on its own? |
| --- | --- | --- | --- |
| Latest-channel installer has run; `start` or `bootstrap` is used with automatic updates enabled | Managed binary and detached updater when supported | When supported, the platform's installer runs on the configured cadence. | When supported, the running server restarts with the new binary before the updater replaces itself. |
| Installer selected an explicit release; `bootstrap` is used | Managed binary only | No; the selected release stays pinned. | No; an explicit restart uses the selected binary. |
| Another tool updates the managed binary | A fresh start or explicit restart uses it; a running server is reused. | Yes, when a latest-channel updater is running, on the configured cadence. | An updater that was running through the change compares binary contents on its next successful installer pass and refreshes the server first. |
| Situation | What starts | Does this daemon fetch new binaries? | Does a running app-server eventually move to a newer binary on its own? |
| ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Latest-channel installer has run; `start` or `bootstrap` is used with automatic updates enabled | Managed binary and detached updater when supported | When supported, the platform's installer runs on the configured cadence. | When supported, the running server restarts with the new binary before the updater replaces itself. |
| Installer selected an explicit release; `bootstrap` is used | Managed binary only | No; the selected release stays pinned. | No; an explicit restart uses the selected binary. |
| Another tool updates the managed binary | A fresh start or explicit restart uses it; a running server is reused. | Yes, when a latest-channel updater is running, on the configured cadence. | An updater that was running through the change compares binary contents on its next successful installer pass and refreshes the server first. |
### Managed packages
@@ -1,4 +1,5 @@
# App Server Test Client
Quickstart for running and hitting `codex app-server`.
## Quickstart
+8 -7
View File
@@ -165,13 +165,13 @@ Local UI clients use five methods. They require the existing
`unavailable/providerUnavailable` on unsupported platforms or without the required
ChatGPT account identity.
| Method | Params | Result |
| --- | --- | --- |
| `userVerification/status` | `{}` | `{credentialId, unavailableReason, unavailableMessage}` |
| `userVerification/enroll` | `{}` | `{credentialId, algorithm?, publicKey?}` |
| `userVerification/delete` | `{}` | `{}` |
| `userVerification/verify` | `{challenge, title, description}` | `{proof: {credentialId, signature}}` |
| `userVerification/cancel` | `{requestId}` | `{}` |
| Method | Params | Result |
| ------------------------- | --------------------------------- | ------------------------------------------------------- |
| `userVerification/status` | `{}` | `{credentialId, unavailableReason, unavailableMessage}` |
| `userVerification/enroll` | `{}` | `{credentialId, algorithm?, publicKey?}` |
| `userVerification/delete` | `{}` | `{}` |
| `userVerification/verify` | `{challenge, title, description}` | `{proof: {credentialId, signature}}` |
| `userVerification/cancel` | `{requestId}` | `{}` |
Status reads local readiness without prompting or contacting a backend. A null
`unavailableReason` means local checks passed, not that registration is valid.
@@ -277,6 +277,7 @@ the entire network configuration.
- `thread/attachmentOwner/list` — find stored threads with an exact attachment type and identity key, with cursor pagination and an optional archive filter.
- `thread/attachment/remove` — remove an attachment by its thread, attachment type, and identity key; returns `{}`.
- `thread/attachment/updated` — notification broadcast after an attachment is created or removed; contains the thread, attachment identity, attachment id, and operation.
### Example: Manage stored thread attachments
Attachments record the resources currently associated with a thread, independently of conversation history. Clients can add, remove, and list attachments for one stored thread at a time without resuming those threads. Adding or removing an attachment does not create or delete the underlying resource or rewrite history. An attachment is idempotently identified by its thread, `attachmentType`, and `identityKey`. For pull requests, clients should reuse the canonical application identity `JSON.stringify([canonicalHostname, lowercaseOwner, lowercaseRepository, pullRequestNumber])` so addition and removal agree across surfaces.
+1
View File
@@ -12,6 +12,7 @@ Typed clients for Codex/OpenAI APIs built on top of the generic transport in `co
The public interface of this crate is intentionally small and uniform:
- **Responses endpoint**
- Input:
- `ResponsesApiRequest` for the request body (`model`, `input`, `tools`, `parallel_tool_calls`, reasoning/text controls).
- `ResponsesOptions` for transport/header concerns (`conversation_id`, `session_source`, `extra_headers`, `compression`, `turn_state`).
+23 -23
View File
@@ -91,36 +91,36 @@ only enumerate labels and do not need remote caches or execution.
The `Cache/BES` host is also used for remote downloads.
| Invocation/config | Key Required | Cache/BES | Build exec | Test exec |
| --- | --- | --- | --- | --- |
| `bazel ...` | No | None | Local | Local |
| `bazel ... --config=buildbuddy-generic` | Yes | `remote.buildbuddy.io` | Local | Local |
| `bazel ... --config=buildbuddy-generic-rbe` | Yes | `remote.buildbuddy.io` | Remote | Remote |
| `bazel ... --config=buildbuddy-openai` | Yes | `openai.buildbuddy.io` | Local | Local |
| `bazel ... --config=buildbuddy-openai-rbe` | Yes | `openai.buildbuddy.io` | Remote | Remote |
| Invocation/config | Key Required | Cache/BES | Build exec | Test exec |
| ------------------------------------------- | ------------ | ---------------------- | ---------- | --------- |
| `bazel ...` | No | None | Local | Local |
| `bazel ... --config=buildbuddy-generic` | Yes | `remote.buildbuddy.io` | Local | Local |
| `bazel ... --config=buildbuddy-generic-rbe` | Yes | `remote.buildbuddy.io` | Remote | Remote |
| `bazel ... --config=buildbuddy-openai` | Yes | `openai.buildbuddy.io` | Local | Local |
| `bazel ... --config=buildbuddy-openai-rbe` | Yes | `openai.buildbuddy.io` | Remote | Remote |
Without an API key, the wrapper removes remote CI configurations and runs
locally. With a key, workflows choose the host as follows:
| Run | Key | Uses OpenAI BuildBuddy Host |
| --- | --- | --- |
| Push to `main` in `openai/codex` | Yes | Yes |
| `workflow_dispatch` in `openai/codex` | Yes | Yes |
| Same-repository pull request in `openai/codex` | Yes | Yes |
| Fork pull request into `openai/codex` | No | No; local |
| Push or `workflow_dispatch` in a fork with a key | Yes | No; generic host |
| Pull request run in a fork repository with a key | Yes | No; generic host |
| Run | Key | Uses OpenAI BuildBuddy Host |
| ------------------------------------------------ | --- | --------------------------- |
| Push to `main` in `openai/codex` | Yes | Yes |
| `workflow_dispatch` in `openai/codex` | Yes | Yes |
| Same-repository pull request in `openai/codex` | Yes | Yes |
| Fork pull request into `openai/codex` | No | No; local |
| Push or `workflow_dispatch` in a fork with a key | Yes | No; generic host |
| Pull request run in a fork repository with a key | Yes | No; generic host |
CI configurations determine whether builds and tests execute remotely:
| CI config | Remote config | Build exec | Test exec |
| --- | --- | --- | --- |
| `ci-linux` | `*-rbe` | Remote host | Remote host |
| `ci-v8` | `*-rbe` | Remote host | Remote host |
| `ci-macos` | `*-rbe` | Remote host | Local |
| `ci-windows-cross` | `*-rbe` | Remote host | Local |
| `ci-windows` | non-RBE | Local | Local |
| Keyless CI fallback | none | Local | Local |
| CI config | Remote config | Build exec | Test exec |
| ------------------- | ------------- | ----------- | ----------- |
| `ci-linux` | `*-rbe` | Remote host | Remote host |
| `ci-v8` | `*-rbe` | Remote host | Remote host |
| `ci-macos` | `*-rbe` | Remote host | Local |
| `ci-windows-cross` | `*-rbe` | Remote host | Local |
| `ci-windows` | non-RBE | Local | Local |
| Keyless CI fallback | none | Local | Local |
To exercise the generic remote configuration with your key:
+1
View File
@@ -66,6 +66,7 @@ Since only 1 `Task` can be run at a time, for parallel tasks it is recommended t
For complete documentation of the `Op` and `EventMsg` variants, refer to [protocol.rs](../protocol/src/protocol.rs). Some example payload types:
- `Op`
- `Op::UserTurn` – Any input from the user to kick off a `Turn`, including full per-turn context such as cwd, model, sandbox, approval policy, and optional `approvals_reviewer`
- `Op::Interrupt` – Interrupts a running turn
- `Op::ExecApproval` – Approve or deny code execution
+14 -12
View File
@@ -1,14 +1,16 @@
Everything becomes a good contributor design, which contributors do we need?
git attribution Context
memories Context + Tool + Output
guardian Context + Request
goal Tool + Runtime
image generation Tool + Output
skills Context + Turn
personality Context
plugins / apps / connectors Context + Turn
shell snapshot Runtime
web search Tool
AGENTS.md Context (Runtime too only if you want eager refresh/cache behavior)
future sandboxing probably Request + Runtime
| Area | Contributors |
| --------------------------- | ------------------------------------------------------------------- |
| git attribution | Context |
| memories | Context + Tool + Output |
| guardian | Context + Request |
| goal | Tool + Runtime |
| image generation | Tool + Output |
| skills | Context + Turn |
| personality | Context |
| plugins / apps / connectors | Context + Turn |
| shell snapshot | Runtime |
| web search | Tool |
| AGENTS.md | Context (Runtime too only if you want eager refresh/cache behavior) |
| future sandboxing | probably Request + Runtime |
+2
View File
@@ -22,6 +22,7 @@ it cannot create the required user namespaces, so Codex rejects sandboxed shell
commands that would enter the bubblewrap path.
**Current Behavior**
- Legacy `SandboxPolicy` / `sandbox_mode` configs remain supported.
- Bubblewrap is the default filesystem sandbox.
- If `bwrap` is present on `PATH` outside the current working directory, the
@@ -108,4 +109,5 @@ commands that would enter the bubblewrap path.
remain in force. The default `isolate` mode retains PID isolation.
**Notes**
- The CLI surface is `codex sandbox`; the host OS selects the sandbox backend.
+35 -7
View File
@@ -1,9 +1,13 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Codex Sign-in Error</title>
<link rel="icon" href='data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 32 32"%3E%3Cpath stroke="%23000" stroke-linecap="round" stroke-width="2.484" d="M22.356 19.797H17.17M9.662 12.29l1.979 3.576a.511.511 0 0 1-.005.504l-1.974 3.409M30.758 16c0 8.15-6.607 14.758-14.758 14.758-8.15 0-14.758-6.607-14.758-14.758C1.242 7.85 7.85 1.242 16 1.242c8.15 0 14.758 6.608 14.758 14.758Z"/%3E%3C/svg%3E' type="image/svg+xml">
<link
rel="icon"
href='data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 32 32"%3E%3Cpath stroke="%23000" stroke-linecap="round" stroke-width="2.484" d="M22.356 19.797H17.17M9.662 12.29l1.979 3.576a.511.511 0 0 1-.005.504l-1.974 3.409M30.758 16c0 8.15-6.607 14.758-14.758 14.758-8.15 0-14.758-6.607-14.758-14.758C1.242 7.85 7.85 1.242 16 1.242c8.15 0 14.758 6.608 14.758 14.758Z"/%3E%3C/svg%3E'
type="image/svg+xml"
/>
<style>
:root {
color-scheme: light;
@@ -11,8 +15,18 @@
body {
margin: 0;
min-height: 100vh;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Oxygen,
Ubuntu,
Cantarell,
"Open Sans",
"Helvetica Neue",
sans-serif;
background: radial-gradient(circle at top, #f7f8fb 0%, #ffffff 48%);
color: #0d0d0d;
}
@@ -80,8 +94,9 @@
color: #5d5d5d;
}
code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
word-break: break-all;
}
.help {
@@ -96,7 +111,20 @@
<div class="card">
<div class="brand">
<div class="logo" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 32 32"><path stroke="#000" stroke-linecap="round" stroke-width="2.484" d="M22.356 19.797H17.17M9.662 12.29l1.979 3.576a.511.511 0 0 1-.005.504l-1.974 3.409M30.758 16c0 8.15-6.607 14.758-14.758 14.758-8.15 0-14.758-6.607-14.758-14.758C1.242 7.85 7.85 1.242 16 1.242c8.15 0 14.758 6.608 14.758 14.758Z"></path></svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 32 32"
>
<path
stroke="#000"
stroke-linecap="round"
stroke-width="2.484"
d="M22.356 19.797H17.17M9.662 12.29l1.979 3.576a.511.511 0 0 1-.005.504l-1.974 3.409M30.758 16c0 8.15-6.607 14.758-14.758 14.758-8.15 0-14.758-6.607-14.758-14.758C1.242 7.85 7.85 1.242 16 1.242c8.15 0 14.758 6.608 14.758 14.758Z"
></path>
</svg>
</div>
<div class="brand-title">Codex login</div>
</div>
+22 -8
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
@@ -11,7 +11,9 @@
/>
<style>
:root {
--font-use: "SF Pro", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-use:
"SF Pro", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", sans-serif;
--gray-0: #ffffff;
--gray-50: #f9f9f9;
--gray-100: #ededed;
@@ -88,7 +90,9 @@
.message {
margin: 0;
color: var(--text-secondary, #5d5d5d);
font-feature-settings: "liga" off, "clig" off;
font-feature-settings:
"liga" off,
"clig" off;
font-size: 14px;
font-style: normal;
font-weight: 400;
@@ -108,7 +112,9 @@
color: var(--interactive-label-secondary-default, #0d0d0d);
cursor: pointer;
font-family: var(--font-use, "SF Pro");
font-feature-settings: "liga" off, "clig" off;
font-feature-settings:
"liga" off,
"clig" off;
font-size: 14px;
font-style: normal;
font-weight: 510;
@@ -161,7 +167,9 @@
<div class="wordmark" aria-label="ChatGPT">ChatGPT</div>
<main>
<div class="content">
<p class="message" id="status-message">You&rsquo;re signed in and may close this tab</p>
<p class="message" id="status-message">
You&rsquo;re signed in and may close this tab
</p>
<button class="button" type="button" id="open-codex">
<svg
class="codex-mark"
@@ -181,7 +189,10 @@
</button>
<div class="setup-box" id="setup-box">
<p class="setup-title">Finish setting up your API organization</p>
<p class="setup-description">Add a payment method to use your organization. Redirecting in <span id="countdown">3</span>s...</p>
<p class="setup-description">
Add a payment method to use your organization. Redirecting in
<span id="countdown">3</span>s...
</p>
</div>
</div>
</main>
@@ -191,7 +202,8 @@
const params = new URLSearchParams(window.location.search);
const preview = params.get("preview") === "true";
const needsSetup = params.get("needs_setup") === "true";
const platformUrl = params.get("platform_url") || "https://platform.openai.com";
const platformUrl =
params.get("platform_url") || "https://platform.openai.com";
const orgId = params.get("org_id");
const projectId = params.get("project_id");
const planType = params.get("plan_type");
@@ -203,7 +215,9 @@
window.location.href = CODEX_URL;
}
document.getElementById("open-codex").addEventListener("click", openCodex);
document
.getElementById("open-codex")
.addEventListener("click", openCodex);
if (!preview) {
setTimeout(openCodex, 250);
}
+77 -36
View File
@@ -1,9 +1,13 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Sign into Codex</title>
<link rel="icon" href='data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 32 32"%3E%3Cpath stroke="%23000" stroke-linecap="round" stroke-width="2.484" d="M22.356 19.797H17.17M9.662 12.29l1.979 3.576a.511.511 0 0 1-.005.504l-1.974 3.409M30.758 16c0 8.15-6.607 14.758-14.758 14.758-8.15 0-14.758-6.607-14.758-14.758C1.242 7.85 7.85 1.242 16 1.242c8.15 0 14.758 6.608 14.758 14.758Z"/%3E%3C/svg%3E' type="image/svg+xml">
<link
rel="icon"
href='data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 32 32"%3E%3Cpath stroke="%23000" stroke-linecap="round" stroke-width="2.484" d="M22.356 19.797H17.17M9.662 12.29l1.979 3.576a.511.511 0 0 1-.005.504l-1.974 3.409M30.758 16c0 8.15-6.607 14.758-14.758 14.758-8.15 0-14.758-6.607-14.758-14.758C1.242 7.85 7.85 1.242 16 1.242c8.15 0 14.758 6.608 14.758 14.758Z"/%3E%3C/svg%3E'
type="image/svg+xml"
/>
<style>
.container {
margin: auto;
@@ -14,7 +18,18 @@
position: relative;
background: white;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Oxygen,
Ubuntu,
Cantarell,
"Open Sans",
"Helvetica Neue",
sans-serif;
}
.inner-container {
width: 400px;
@@ -38,7 +53,7 @@
}
.title {
text-align: center;
color: var(--text-primary, #0D0D0D);
color: var(--text-primary, #0d0d0d);
font-size: 32px;
font-weight: 400;
line-height: 40px;
@@ -50,7 +65,7 @@
background: var(--bg-primary, white);
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.05);
border-radius: 16px;
outline: 1px var(--border-default, rgba(13, 13, 13, 0.10)) solid;
outline: 1px var(--border-default, rgba(13, 13, 13, 0.1)) solid;
outline-offset: -1px;
justify-content: flex-start;
align-items: center;
@@ -74,7 +89,7 @@
}
.setup-title {
align-self: stretch;
color: var(--text-primary, #0D0D0D);
color: var(--text-primary, #0d0d0d);
font-size: 14px;
font-weight: 510;
line-height: 20px;
@@ -82,7 +97,7 @@
}
.setup-description {
align-self: stretch;
color: var(--text-secondary, #5D5D5D);
color: var(--text-secondary, #5d5d5d);
font-size: 14px;
font-weight: 400;
line-height: 20px;
@@ -98,7 +113,7 @@
.redirect-button {
height: 28px;
padding: 8px 16px;
background: var(--interactive-bg-primary-default, #0D0D0D);
background: var(--interactive-bg-primary-default, #0d0d0d);
border-radius: 999px;
justify-content: center;
align-items: center;
@@ -121,7 +136,7 @@
width: 4rem;
height: 4rem;
border-radius: 16px;
border: .5px solid rgba(0, 0, 0, 0.1);
border: 0.5px solid rgba(0, 0, 0, 0.1);
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 16px 0px;
box-sizing: border-box;
background-color: rgb(255, 255, 255);
@@ -133,21 +148,47 @@
<div class="inner-container">
<div class="content">
<div class="logo">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 32 32"><path stroke="#000" stroke-linecap="round" stroke-width="2.484" d="M22.356 19.797H17.17M9.662 12.29l1.979 3.576a.511.511 0 0 1-.005.504l-1.974 3.409M30.758 16c0 8.15-6.607 14.758-14.758 14.758-8.15 0-14.758-6.607-14.758-14.758C1.242 7.85 7.85 1.242 16 1.242c8.15 0 14.758 6.608 14.758 14.758Z"></path></svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
fill="none"
viewBox="0 0 32 32"
>
<path
stroke="#000"
stroke-linecap="round"
stroke-width="2.484"
d="M22.356 19.797H17.17M9.662 12.29l1.979 3.576a.511.511 0 0 1-.005.504l-1.974 3.409M30.758 16c0 8.15-6.607 14.758-14.758 14.758-8.15 0-14.758-6.607-14.758-14.758C1.242 7.85 7.85 1.242 16 1.242c8.15 0 14.758 6.608 14.758 14.758Z"
></path>
</svg>
</div>
<div class="title">Signed in to Codex</div>
</div>
<div class="close-box" style="display: none;">
<div class="close-box" style="display: none">
<div class="setup-description">You may now close this page</div>
</div>
<div class="setup-box" style="display: none;">
<div class="setup-box" style="display: none">
<div class="setup-content">
<div class="setup-text">
<div class="setup-title">Finish setting up your API organization</div>
<div class="setup-description">Add a payment method to use your organization.</div>
<div class="setup-title">
Finish setting up your API organization
</div>
<div class="setup-description">
Add a payment method to use your organization.
</div>
</div>
<div class="redirect-box">
<div data-hasendicon="false" data-hasstarticon="false" data-ishovered="false" data-isinactive="false" data-ispressed="false" data-size="large" data-type="primary" class="redirect-button">
<div
data-hasendicon="false"
data-hasstarticon="false"
data-ishovered="false"
data-isinactive="false"
data-ispressed="false"
data-size="large"
data-type="primary"
class="redirect-button"
>
<div class="redirect-text">Redirecting in 3s...</div>
</div>
</div>
@@ -158,34 +199,34 @@
<script>
(function () {
const params = new URLSearchParams(window.location.search);
const needsSetup = params.get('needs_setup') === 'true';
const platformUrl = params.get('platform_url') || 'https://platform.openai.com';
const orgId = params.get('org_id');
const projectId = params.get('project_id');
const planType = params.get('plan_type');
const idToken = params.get('id_token');
const needsSetup = params.get("needs_setup") === "true";
const platformUrl =
params.get("platform_url") || "https://platform.openai.com";
const orgId = params.get("org_id");
const projectId = params.get("project_id");
const planType = params.get("plan_type");
const idToken = params.get("id_token");
// Show different message and optional redirect when setup is required
if (needsSetup) {
// The login server selects one of these platform origins from the OAuth issuer.
if (
platformUrl !== 'https://platform.openai.com' &&
platformUrl !== 'https://platform.api.openai.org'
platformUrl !== "https://platform.openai.com" &&
platformUrl !== "https://platform.api.openai.org"
) {
return;
}
const setupBox = document.querySelector('.setup-box');
setupBox.style.display = 'flex';
const redirectUrlObj = new URL('/org-setup', platformUrl);
redirectUrlObj.searchParams.set('p', planType);
redirectUrlObj.searchParams.set('t', idToken);
redirectUrlObj.searchParams.set('with_org', orgId);
redirectUrlObj.searchParams.set('project_id', projectId);
const setupBox = document.querySelector(".setup-box");
setupBox.style.display = "flex";
const redirectUrlObj = new URL("/org-setup", platformUrl);
redirectUrlObj.searchParams.set("p", planType);
redirectUrlObj.searchParams.set("t", idToken);
redirectUrlObj.searchParams.set("with_org", orgId);
redirectUrlObj.searchParams.set("project_id", projectId);
const redirectUrl = redirectUrlObj.toString();
const message = document.querySelector('.redirect-text');
const message = document.querySelector(".redirect-text");
let countdown = 3;
function tick() {
message.textContent =
'Redirecting in ' + countdown + 's…';
message.textContent = "Redirecting in " + countdown + "s…";
if (countdown === 0) {
window.location.replace(redirectUrl);
} else {
@@ -195,10 +236,10 @@
}
tick();
} else {
const closeBox = document.querySelector('.close-box');
closeBox.style.display = 'flex';
const closeBox = document.querySelector(".close-box");
closeBox.style.display = "flex";
}
})();
</script>
</body>
</html>
</html>
+7 -8
View File
@@ -6,13 +6,13 @@ production dependency is needed.
## Supported subsets
| Family | Supported syntax |
| --- | --- |
| `flowchart`, `graph` | TD/TB (default), BT, LR, RL; rectangle `[label]`, decision `{label}`, and stadium `([label])` nodes; solid `-->`/`---`/`<-->` and dashed `-.->`/`-.-`/`<-.->` edges; pipe labels and spaced directed infix labels (`-- text -->`, `-. text .->`); `&` fan-out/fan-in, chains, branches, merges, loops |
| `sequenceDiagram` | Implicit participants, `participant`/`actor`, aliases, `->`, `->>`, `-->`, `-->>`, `-x`, `--x`, self-messages, `Note over A[,B]`, nested `loop`/`alt`/`opt`/`critical`/`break`, one labeled `else` per `alt` |
| `stateDiagram-v2`, `stateDiagram` | Flat states, `state "label" as ID`, descriptions, directed transitions with optional labels, initial/final `[*]`, direction declarations |
| `classDiagram` | `class ID`, multiline member bodies, `ID : member`, solid/dashed links, association, inheritance, composition, aggregation, dependency, realization, quoted endpoint cardinalities, relationship labels, direction declarations |
| `erDiagram` | Entities, multiline attribute bodies, `type name [PK, FK, UK] ["comment"]`, all four endpoint cardinalities, identifying/non-identifying relationships, relationship labels, direction declarations |
| Family | Supported syntax |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `flowchart`, `graph` | TD/TB (default), BT, LR, RL; rectangle `[label]`, decision `{label}`, and stadium `([label])` nodes; solid `-->`/`---`/`<-->` and dashed `-.->`/`-.-`/`<-.->` edges; pipe labels and spaced directed infix labels (`-- text -->`, `-. text .->`); `&` fan-out/fan-in, chains, branches, merges, loops |
| `sequenceDiagram` | Implicit participants, `participant`/`actor`, aliases, `->`, `->>`, `-->`, `-->>`, `-x`, `--x`, self-messages, `Note over A[,B]`, nested `loop`/`alt`/`opt`/`critical`/`break`, one labeled `else` per `alt` |
| `stateDiagram-v2`, `stateDiagram` | Flat states, `state "label" as ID`, descriptions, directed transitions with optional labels, initial/final `[*]`, direction declarations |
| `classDiagram` | `class ID`, multiline member bodies, `ID : member`, solid/dashed links, association, inheritance, composition, aggregation, dependency, realization, quoted endpoint cardinalities, relationship labels, direction declarations |
| `erDiagram` | Entities, multiline attribute bodies, `type name [PK, FK, UK] ["comment"]`, all four endpoint cardinalities, identifying/non-identifying relationships, relationship labels, direction declarations |
Identifiers are ASCII letters followed by letters, digits, or underscores. Text
supports ordinary Unicode and CJK. Full-line `%%` comments and
@@ -76,6 +76,5 @@ family, relationship endpoints, all ER cardinalities, width/error bounds,
truncated input, and reconstruction of every edge in all 512 directed three-node
graphs in each of the four layout directions.
`render_spans` returns the same layout as lines of semantic `Node`, `Edge`, and
`Text` spans. Callers apply their own theme; the crate never emits ANSI escapes.
+2 -2
View File
@@ -257,11 +257,11 @@ what it can reasonably guarantee.
- the HTTP proxy listener clamps non-loopback binds unless explicitly enabled via
`dangerously_allow_non_loopback_proxy`
- when unix socket proxying is enabled, all proxy listeners are forced to loopback to avoid turning the
proxy into a remote bridge into local daemons.
proxy into a remote bridge into local daemons.
- `dangerously_allow_all_unix_sockets = true` bypasses the unix socket allowlist entirely (still
macOS-only and absolute-path-only). Use only in tightly controlled environments.
- `enabled` is enforced at runtime; when false the proxy no-ops and does not bind listeners.
Limitations:
Limitations:
- DNS rebinding is hard to fully prevent without pinning the resolved IP(s) all the way down to the
transport layer. If your threat model includes hostile DNS, enforce network egress at a lower
+7 -7
View File
@@ -233,13 +233,13 @@ metrics.shutdown()?; // flushes in-memory exporter
failed sends. It carries existing session tags plus `mode` (`incremental`/`full`),
`phase` (`warmup`/`generation`), and `reason`:
| Reason | Meaning |
| --- | --- |
| `incremental` | Send the previous response ID and new input. |
| `no_previous_request` | First request from a fresh client. |
| `restored_history` | First request after loading resumed or forked history. |
| `connection_closed` | Full input after observing the previous socket closed. |
| `other` | Full input for another reason, such as changed input/settings or unavailable response state. |
| Reason | Meaning |
| --------------------- | -------------------------------------------------------------------------------------------- |
| `incremental` | Send the previous response ID and new input. |
| `no_previous_request` | First request from a fresh client. |
| `restored_history` | First request after loading resumed or forked history. |
| `connection_closed` | Full input after observing the previous socket closed. |
| `other` | Full input for another reason, such as changed input/settings or unavailable response state. |
Per-socket backend metrics label a resend after reconnect as `initial`; this client
metric retains the first reset reason through reconnect failures and turn boundaries.
@@ -16,7 +16,8 @@ import process from "node:process";
import { pathToFileURL } from "node:url";
import { inspect, promisify } from "node:util";
const DEFAULT_MANUAL_URL = "https://developers.openai.com/codex/codex-manual.md";
const DEFAULT_MANUAL_URL =
"https://developers.openai.com/codex/codex-manual.md";
const DEFAULT_CACHE_DIR_NAME = "openai-docs-cache";
const CACHE_FILE_NAME = "codex-manual.md";
const OUTLINE_FILE_NAME = "codex-manual.outline.md";
@@ -79,7 +80,7 @@ const parseCurlHeaders = (rawHeaders) => {
const statusMatch = /^HTTP\/\S+\s+(\d{3})/.exec(statusLine);
if (!statusMatch) {
throw new ManualFetchError(
`Could not parse HTTP status from curl response: ${statusLine}`
`Could not parse HTTP status from curl response: ${statusLine}`,
);
}
@@ -103,7 +104,7 @@ const tempFilePath = (cacheDir, suffix) =>
cacheDir,
`.fetch-codex-manual-${process.pid}-${Date.now()}-${Math.random()
.toString(16)
.slice(2)}${suffix}`
.slice(2)}${suffix}`,
);
const requestManualWithCurl = async (url, { cacheDir, method, timeoutMs }) => {
@@ -166,7 +167,7 @@ const requestManualWithCurl = async (url, { cacheDir, method, timeoutMs }) => {
const requestManualWithFetch = async (url, { method, timeoutMs }) => {
if (typeof fetch !== "function") {
throw new ManualFetchError(
"Native fetch is unavailable in this Node runtime."
"Native fetch is unavailable in this Node runtime.",
);
}
@@ -177,7 +178,7 @@ const requestManualWithFetch = async (url, { method, timeoutMs }) => {
headers: { "User-Agent": USER_AGENT },
signal,
}),
timeoutMs
timeoutMs,
);
};
@@ -199,7 +200,7 @@ const requestManual = async (url, { cacheDir, method, timeoutMs }) => {
const response = await transport();
if (!response.ok) {
throw new ManualFetchError(
`${method} ${url} failed with HTTP ${response.status}.`
`${method} ${url} failed with HTTP ${response.status}.`,
);
}
return response;
@@ -307,7 +308,10 @@ const manualLines = (manual) => {
};
const sectionTitle = (rawTitle) =>
rawTitle.replace(/\s+#+\s*$/, "").replace(/\s+/g, " ").trim();
rawTitle
.replace(/\s+#+\s*$/, "")
.replace(/\s+/g, " ")
.trim();
const buildOutline = (manual) => {
const lines = manualLines(manual);
@@ -366,7 +370,8 @@ const buildOutline = (manual) => {
};
};
const outlineMarkdown = (outline) => `# Codex Manual Outline\n\n${outline.text}\n`;
const outlineMarkdown = (outline) =>
`# Codex Manual Outline\n\n${outline.text}\n`;
const manualStatusLine = (status) =>
status.cacheStatus === "hit"
@@ -413,7 +418,7 @@ const fetchCodexManual = async ({
const resolvedCacheDir = await resolveCacheDir(cacheDir);
if (!resolvedCacheDir) {
throw new ManualFetchError(
"Manual cache directory is unavailable; pass --cache-dir to override or use OpenAI Docs MCP fallback."
"Manual cache directory is unavailable; pass --cache-dir to override or use OpenAI Docs MCP fallback.",
);
}
await mkdir(resolvedCacheDir, { recursive: true });
@@ -460,7 +465,7 @@ const fetchCodexManual = async ({
const getHeaderSha256 = readHeaderSha(getResponse);
if (getHeaderSha256 !== expectedSha256) {
throw new ManualFetchError(
`${HASH_HEADER} changed between HEAD and GET for ${manualUrl}.`
`${HASH_HEADER} changed between HEAD and GET for ${manualUrl}.`,
);
}
@@ -469,7 +474,7 @@ const fetchCodexManual = async ({
const manualHashMatches = actualSha256 === expectedSha256;
if (!manualHashMatches) {
throw new ManualFetchError(
`${HASH_HEADER} did not match the fetched manual body for ${manualUrl}.`
`${HASH_HEADER} did not match the fetched manual body for ${manualUrl}.`,
);
}
@@ -49,7 +49,9 @@ async function readSource(source) {
return response.text();
}
lastError = new Error("failed to fetch " + source + ": " + response.status);
lastError = new Error(
"failed to fetch " + source + ": " + response.status,
);
if (response.status < 500 && response.status !== 429) {
break;
}
@@ -101,7 +103,7 @@ function parseFlatInfo(block) {
function extractLatestModelInfo(markdown) {
const lines = markdown.split(/\r?\n/);
const latestModelInfoIndex = lines.findIndex((line) =>
/^latestModelInfo:\s*$/.test(line)
/^latestModelInfo:\s*$/.test(line),
);
if (latestModelInfoIndex >= 0) {
@@ -109,7 +111,7 @@ function extractLatestModelInfo(markdown) {
}
const commentMatch = markdown.match(
/<!--\s*latestModelInfo\s*\n([\s\S]*?)\n\s*-->/m
/<!--\s*latestModelInfo\s*\n([\s\S]*?)\n\s*-->/m,
);
if (commentMatch) {
return parseFlatInfo(commentMatch[1]);
@@ -133,7 +135,7 @@ function normalizeInfo(info, baseUrl) {
if (!model || !migrationGuide || !promptingGuide) {
throw new Error(
"latestModelInfo must include model, migrationGuide, and promptingGuide"
"latestModelInfo must include model, migrationGuide, and promptingGuide",
);
}
@@ -155,7 +157,7 @@ async function main() {
}
process.stdout.write(
`${JSON.stringify(normalizeInfo(info, baseUrl), null, 2)}\n`
`${JSON.stringify(normalizeInfo(info, baseUrl), null, 2)}\n`,
);
}
@@ -2,238 +2,242 @@
<script src="https://unpkg.com/@floating-ui/core@1.7.3/dist/floating-ui.core.umd.min.js"></script>
<script src="https://unpkg.com/@floating-ui/dom@1.7.4/dist/floating-ui.dom.umd.min.js"></script>
<script>
(() => {
if (window.FloatingUIDOM == null) {
return;
}
const { autoUpdate, computePosition, flip, offset, shift, size } =
window.FloatingUIDOM;
const tooltipPlacements = new Set(["top", "right", "bottom", "left"]);
const tooltipId = `codex-visualization-tooltip-${
window.crypto?.randomUUID?.() ?? Date.now().toString(36)
}`;
const tooltipGap = 5;
const hoverDelay = 700;
const skipDelay = 300;
let activeTrigger = null;
let pendingTrigger = null;
let tooltip = null;
let openTimeout = null;
let autoUpdateCleanup = null;
let positionRequest = 0;
let skipDelayUntil = 0;
(() => {
if (window.FloatingUIDOM == null) {
return;
}
const { autoUpdate, computePosition, flip, offset, shift, size } =
window.FloatingUIDOM;
const tooltipPlacements = new Set(["top", "right", "bottom", "left"]);
const tooltipId = `codex-visualization-tooltip-${
window.crypto?.randomUUID?.() ?? Date.now().toString(36)
}`;
const tooltipGap = 5;
const hoverDelay = 700;
const skipDelay = 300;
let activeTrigger = null;
let pendingTrigger = null;
let tooltip = null;
let openTimeout = null;
let autoUpdateCleanup = null;
let positionRequest = 0;
let skipDelayUntil = 0;
const getTrigger = (target) =>
target?.nodeType === Node.ELEMENT_NODE
? target.closest("[data-tooltip]")
: null;
const containsTarget = (trigger, target) =>
target?.nodeType != null && trigger.contains(target);
const getContent = (trigger) =>
trigger.getAttribute("data-tooltip")?.trim() ?? "";
const getPlacement = (trigger) => {
const placement = trigger.getAttribute("data-tooltip-placement");
return tooltipPlacements.has(placement) ? placement : "top";
};
const getTooltip = () => {
if (tooltip == null) {
tooltip = document.createElement("div");
tooltip.id = tooltipId;
tooltip.className = "tooltip";
tooltip.setAttribute("role", "tooltip");
}
return tooltip;
};
const setDescribedBy = (trigger, described) => {
const ids = new Set(
(trigger.getAttribute("aria-describedby") ?? "")
.split(/\s+/)
.filter(Boolean),
);
if (described) {
ids.add(tooltipId);
} else {
ids.delete(tooltipId);
}
if (ids.size === 0) {
trigger.removeAttribute("aria-describedby");
} else {
trigger.setAttribute("aria-describedby", [...ids].join(" "));
}
};
const clearOpenTimeout = () => {
if (openTimeout != null) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
pendingTrigger = null;
};
const closeTooltip = (activateSkipDelay = true) => {
clearOpenTimeout();
positionRequest += 1;
autoUpdateCleanup?.();
autoUpdateCleanup = null;
if (activeTrigger == null) {
return;
}
setDescribedBy(activeTrigger, false);
activeTrigger = null;
tooltip?.remove();
if (activateSkipDelay) {
skipDelayUntil = Date.now() + skipDelay;
}
};
const updatePosition = (trigger, floating) => {
const request = ++positionRequest;
void computePosition(trigger, floating, {
middleware: [
offset(tooltipGap),
flip({ padding: tooltipGap }),
shift({ padding: tooltipGap }),
size({
padding: tooltipGap,
apply({ availableHeight, availableWidth, elements }) {
elements.floating.style.setProperty(
"--tooltip-available-width",
`${Math.max(0, availableWidth)}px`,
);
elements.floating.style.setProperty(
"--tooltip-available-height",
`${Math.max(0, availableHeight)}px`,
);
},
}),
],
placement: getPlacement(trigger),
strategy: "fixed",
})
.then(({ x, y }) => {
if (activeTrigger !== trigger || request !== positionRequest) {
return;
}
const scale = window.devicePixelRatio || 1;
floating.style.transform = `translate(${Math.round(x * scale) / scale}px, ${Math.round(y * scale) / scale}px)`;
floating.style.visibility = "visible";
})
.catch(() => {
if (activeTrigger === trigger && request === positionRequest) {
closeTooltip(false);
}
});
};
const openTooltip = (trigger) => {
const content = getContent(trigger);
if (!trigger.isConnected || content.length === 0) {
return;
}
clearOpenTimeout();
if (activeTrigger === trigger) {
return;
}
closeTooltip();
const floating = getTooltip();
floating.textContent = content;
floating.style.visibility = "hidden";
floating.style.transform = "translate(0, 0)";
document.body.appendChild(floating);
activeTrigger = trigger;
setDescribedBy(trigger, true);
autoUpdateCleanup = autoUpdate(trigger, floating, () => {
updatePosition(trigger, floating);
});
};
const requestOpen = (trigger, immediate = false) => {
if (
getContent(trigger).length === 0 ||
activeTrigger === trigger ||
pendingTrigger === trigger
) {
return;
}
clearOpenTimeout();
pendingTrigger = trigger;
const delay =
immediate || activeTrigger != null || Date.now() < skipDelayUntil
? 0
: hoverDelay;
if (delay === 0) {
openTooltip(trigger);
return;
}
openTimeout = window.setTimeout(() => {
openTimeout = null;
const getTrigger = (target) =>
target?.nodeType === Node.ELEMENT_NODE
? target.closest("[data-tooltip]")
: null;
const containsTarget = (trigger, target) =>
target?.nodeType != null && trigger.contains(target);
const getContent = (trigger) =>
trigger.getAttribute("data-tooltip")?.trim() ?? "";
const getPlacement = (trigger) => {
const placement = trigger.getAttribute("data-tooltip-placement");
return tooltipPlacements.has(placement) ? placement : "top";
};
const getTooltip = () => {
if (tooltip == null) {
tooltip = document.createElement("div");
tooltip.id = tooltipId;
tooltip.className = "tooltip";
tooltip.setAttribute("role", "tooltip");
}
return tooltip;
};
const setDescribedBy = (trigger, described) => {
const ids = new Set(
(trigger.getAttribute("aria-describedby") ?? "")
.split(/\s+/)
.filter(Boolean),
);
if (described) {
ids.add(tooltipId);
} else {
ids.delete(tooltipId);
}
if (ids.size === 0) {
trigger.removeAttribute("aria-describedby");
} else {
trigger.setAttribute("aria-describedby", [...ids].join(" "));
}
};
const clearOpenTimeout = () => {
if (openTimeout != null) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
pendingTrigger = null;
openTooltip(trigger);
}, delay);
};
const handlePointerOpen = (event) => {
if (event.pointerType === "touch") {
return;
}
const trigger = getTrigger(event.target);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
requestOpen(trigger);
};
const handleLeave = (event) => {
const trigger = getTrigger(event.target);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
if (activeTrigger === trigger || pendingTrigger === trigger) {
};
const closeTooltip = (activateSkipDelay = true) => {
clearOpenTimeout();
positionRequest += 1;
autoUpdateCleanup?.();
autoUpdateCleanup = null;
if (activeTrigger == null) {
return;
}
setDescribedBy(activeTrigger, false);
activeTrigger = null;
tooltip?.remove();
if (activateSkipDelay) {
skipDelayUntil = Date.now() + skipDelay;
}
};
const updatePosition = (trigger, floating) => {
const request = ++positionRequest;
void computePosition(trigger, floating, {
middleware: [
offset(tooltipGap),
flip({ padding: tooltipGap }),
shift({ padding: tooltipGap }),
size({
padding: tooltipGap,
apply({ availableHeight, availableWidth, elements }) {
elements.floating.style.setProperty(
"--tooltip-available-width",
`${Math.max(0, availableWidth)}px`,
);
elements.floating.style.setProperty(
"--tooltip-available-height",
`${Math.max(0, availableHeight)}px`,
);
},
}),
],
placement: getPlacement(trigger),
strategy: "fixed",
})
.then(({ x, y }) => {
if (activeTrigger !== trigger || request !== positionRequest) {
return;
}
const scale = window.devicePixelRatio || 1;
floating.style.transform = `translate(${Math.round(x * scale) / scale}px, ${Math.round(y * scale) / scale}px)`;
floating.style.visibility = "visible";
})
.catch(() => {
if (activeTrigger === trigger && request === positionRequest) {
closeTooltip(false);
}
});
};
const openTooltip = (trigger) => {
const content = getContent(trigger);
if (!trigger.isConnected || content.length === 0) {
return;
}
clearOpenTimeout();
if (activeTrigger === trigger) {
return;
}
closeTooltip();
}
};
const handleFocusIn = (event) => {
const trigger = getTrigger(event.target);
if (trigger?.matches(":focus-visible")) {
requestOpen(trigger, true);
}
};
const handleKeyDown = (event) => {
if (event.key === "Escape") {
closeTooltip();
}
};
const handleContextMenu = (event) => {
const trigger = getTrigger(event.target);
if (activeTrigger === trigger || pendingTrigger === trigger) {
closeTooltip();
}
};
const destroy = () => {
closeTooltip(false);
document.removeEventListener("pointerover", handlePointerOpen);
document.removeEventListener("pointerout", handleLeave);
document.removeEventListener("focusin", handleFocusIn);
document.removeEventListener("focusout", handleLeave);
document.removeEventListener("keydown", handleKeyDown);
document.removeEventListener("contextmenu", handleContextMenu);
tooltip = null;
};
const floating = getTooltip();
floating.textContent = content;
floating.style.visibility = "hidden";
floating.style.transform = "translate(0, 0)";
document.body.appendChild(floating);
activeTrigger = trigger;
setDescribedBy(trigger, true);
autoUpdateCleanup = autoUpdate(trigger, floating, () => {
updatePosition(trigger, floating);
});
};
const requestOpen = (trigger, immediate = false) => {
if (
getContent(trigger).length === 0 ||
activeTrigger === trigger ||
pendingTrigger === trigger
) {
return;
}
clearOpenTimeout();
pendingTrigger = trigger;
const delay =
immediate || activeTrigger != null || Date.now() < skipDelayUntil
? 0
: hoverDelay;
if (delay === 0) {
openTooltip(trigger);
return;
}
openTimeout = window.setTimeout(() => {
openTimeout = null;
pendingTrigger = null;
openTooltip(trigger);
}, delay);
};
const handlePointerOpen = (event) => {
if (event.pointerType === "touch") {
return;
}
const trigger = getTrigger(event.target);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
requestOpen(trigger);
};
const handleLeave = (event) => {
const trigger = getTrigger(event.target);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
if (activeTrigger === trigger || pendingTrigger === trigger) {
closeTooltip();
}
};
const handleFocusIn = (event) => {
const trigger = getTrigger(event.target);
if (trigger?.matches(":focus-visible")) {
requestOpen(trigger, true);
}
};
const handleKeyDown = (event) => {
if (event.key === "Escape") {
closeTooltip();
}
};
const handleContextMenu = (event) => {
const trigger = getTrigger(event.target);
if (activeTrigger === trigger || pendingTrigger === trigger) {
closeTooltip();
}
};
const destroy = () => {
closeTooltip(false);
document.removeEventListener("pointerover", handlePointerOpen);
document.removeEventListener("pointerout", handleLeave);
document.removeEventListener("focusin", handleFocusIn);
document.removeEventListener("focusout", handleLeave);
document.removeEventListener("keydown", handleKeyDown);
document.removeEventListener("contextmenu", handleContextMenu);
tooltip = null;
};
document.addEventListener("pointerover", handlePointerOpen);
document.addEventListener("pointerout", handleLeave);
document.addEventListener("focusin", handleFocusIn);
document.addEventListener("focusout", handleLeave);
document.addEventListener("keydown", handleKeyDown);
document.addEventListener("contextmenu", handleContextMenu);
window.addEventListener("pagehide", destroy, { once: true });
})();
document.addEventListener("pointerover", handlePointerOpen);
document.addEventListener("pointerout", handleLeave);
document.addEventListener("focusin", handleFocusIn);
document.addEventListener("focusout", handleLeave);
document.addEventListener("keydown", handleKeyDown);
document.addEventListener("contextmenu", handleContextMenu);
window.addEventListener("pagehide", destroy, { once: true });
})();
</script>
<script id="codex-visualization-lucide" async src="https://unpkg.com/lucide@1.17.0/dist/umd/lucide.js"></script>
<script
id="codex-visualization-lucide"
async
src="https://unpkg.com/lucide@1.17.0/dist/umd/lucide.js"
></script>
<script>
(() => {
const initialize = () => {
globalThis.lucide?.createIcons({ attrs: { width: 16, height: 16 } });
};
if (globalThis.lucide != null) {
initialize();
return;
}
document
.getElementById("codex-visualization-lucide")
?.addEventListener("load", initialize, { once: true });
})();
(() => {
const initialize = () => {
globalThis.lucide?.createIcons({ attrs: { width: 16, height: 16 } });
};
if (globalThis.lucide != null) {
initialize();
return;
}
document
.getElementById("codex-visualization-lucide")
?.addEventListener("load", initialize, { once: true });
})();
</script>
+1 -1
View File
@@ -9,7 +9,7 @@
- **Keyboard hints:** Use `key_hint` span helpers for compact, uniformly bold shortcut labels
and readable secondary descriptions. Include `+`, `/`, and chord separators in the label's
emphasis. The `?` shortcut reference uses readable accent-colored keys
without bold. Separate composer footer actions with ` · `.
without bold. Separate composer footer actions with `·`.
# Foreground colors
+2
View File
@@ -7,6 +7,7 @@ behavior consistent in local and remote builds on all platforms. Bazel sets
`runfiles` crate to resolve runfiles via that manifest.
Function behavior:
- `cargo_bin`: reads `CARGO_BIN_EXE_*` environment variables (set by Cargo or
Bazel) and resolves them via the runfiles manifest when `RUNFILES_MANIFEST_FILE`
is present. When not under runfiles, it only accepts absolute paths from
@@ -16,5 +17,6 @@ Function behavior:
falls back to a `CARGO_MANIFEST_DIR`-relative path for Cargo runs.
Background:
- https://bazel.build/docs/runfiles
- https://bazel.build/docs/runfiles#runfiles-manifest
+1 -1
View File
@@ -94,4 +94,4 @@ assert_eq!(out.extracted[0].content, "x");
- Tags are matched literally and case-sensitively
- No nested tag support
- A stream can return empty objects.
- A stream can return empty objects.
+11 -11
View File
@@ -221,12 +221,12 @@ Use `turn(...)` when you need low-level turn control (`stream()`, `steer()`,
These options have the same behavior on sync and async `run(...)` and `turn(...)`:
| Option | Behavior |
| --- | --- |
| `personality: Personality \| None = None` | `Personality.friendly` and `Personality.pragmatic` are deprecated and no longer select a style. See [deprecated personality selection](#deprecated-personality-selection). |
| `service_tier: str | None = None` | Sets the thread's service tier for this and subsequent turns. |
| `turn_service_tier: str | None = None` | Overrides the tier for a newly started turn only. `None` inherits the thread setting; `"default"` selects standard speed. Does not change the thread default and is ignored when input joins an active turn. |
| `source: str | None = None` | Labels the caller that initiated a new turn, such as `"review_ui"`. This is metadata; it does not schedule work or grant authority. Ignored when input joins an active turn. |
| Option | Behavior |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `personality: Personality \| None = None` | `Personality.friendly` and `Personality.pragmatic` are deprecated and no longer select a style. See [deprecated personality selection](#deprecated-personality-selection). |
| `service_tier: str \| None = None` | Sets the thread's service tier for this and subsequent turns. |
| `turn_service_tier: str \| None = None` | Overrides the tier for a newly started turn only. `None` inherits the thread setting; `"default"` selects standard speed. Does not change the thread default and is ignored when input joins an active turn. |
| `source: str \| None = None` | Labels the caller that initiated a new turn, such as `"review_ui"`. This is metadata; it does not schedule work or grant authority. Ignored when input joins an active turn. |
`ExternalMessage`, `turn_service_tier`, `source`, and explicit `include_turns`
on resume/fork require Codex CLI 0.151.0 or newer. The SDK raises `CodexError`
@@ -325,11 +325,11 @@ message = ExternalMessage(
result = thread.run(message)
```
| Field | Meaning |
| --- | --- |
| `tool_name: str` | Required, nonempty name of the tool or application delivering the message. |
| `content` | Required text, or a sequence of structured content dictionaries or generated `FunctionCallOutputContentItem` models. Structured image content requires inline data URLs. |
| `namespace: str | None = None` | Optional namespace for the tool name. |
| Field | Meaning |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `tool_name: str` | Required, nonempty name of the tool or application delivering the message. |
| `content` | Required text, or a sequence of structured content dictionaries or generated `FunctionCallOutputContentItem` models. Structured image content requires inline data URLs. |
| `namespace: str \| None = None` | Optional namespace for the tool name. |
Pass one `ExternalMessage` as the complete input to `run(...)` or `turn(...)`.
It starts a turn when the thread is idle or joins an active regular turn. It
@@ -115,26 +115,18 @@ def test_root_fmt_recipes_use_shared_formatter_driver() -> None:
)
actual = {
"working_directory": lines[0],
"fmt_comment": next(line for line in reversed(lines[:fmt_index]) if line.startswith("#")),
"fmt_commands": [
line.strip()
for line in lines[fmt_index + 1 : fmt_check_index]
if line.strip() and not line.startswith("#")
],
"fmt_check_comment": next(
line for line in reversed(lines[:fmt_check_index]) if line.startswith("#")
),
"fmt_check_commands": [
line.strip() for line in lines[fmt_check_index + 1 : next_recipe_index] if line.strip()
],
}
expected = {
"working_directory": 'set working-directory := "codex-rs"',
"fmt_comment": (
"# Format the justfile, Rust, Bazel/Starlark, Python SDK code, and Python scripts."
),
"fmt_commands": ["@{{ python }} ../scripts/format.py"],
"fmt_check_comment": "# Check formatting without modifying files.",
"fmt_check_commands": ["@{{ python }} ../scripts/format.py --check"],
}