mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
dev
60 Commits
-
[plugins] Enforce marketplace source policy at runtime (#29691)
## Summary - project effective marketplace/plugin config through the enterprise source policy so blocked installed plugins become inactive - filter plugin list/read/discovery and CLI marketplace source/snapshot reporting using the same policy - enforce source admission for background marketplace cache refreshes - continue refreshing/upgrading independent marketplaces and plugins when one entry fails, returning per-entry errors - include policy-projected plugin state in cache and refresh keys so requirement changes invalidate stale results ## Stack This is PR 2 of 2 and is based on #29690. Review the admission model and source matcher in #29690 first; this PR contains only runtime enforcement. ## Test plan - `just test -p codex-core-plugins` (287 tests) - `just test -p codex-cli plugin_list_ignores_implicit_system_marketplace_roots_without_manifests` - `cargo check -p codex-cli -p codex-app-server --tests`
xl-openai ·
2026-06-27 15:22:05 -07:00 -
app-server: structure and test JSON shutdown logs (#30314)
## Why `LOG_FORMAT=json` and `RUST_LOG` are supported by app-server, but the behavior was only covered indirectly. We should verify the actual JSONL written by both user-facing entry points: `codex app-server` and the standalone `codex-app-server` binary. The existing processor shutdown message also always said the channel closed, even though the processor can exit for several different reasons. Structured fields make that event more accurate and useful to log consumers. ## What changed - Record the processor `exit_reason`, remaining connection count, and forced-shutdown state as structured tracing fields. - Add a shared process-test helper that enables JSON logging, validates every stderr line as JSON, and verifies the top-level timestamp is RFC 3339. - Cover both `codex app-server` and `codex-app-server`, asserting the stable `level`, `fields`, and `target` payload. ## Test plan - `just test -p codex-app-server standalone_app_server_emits_json_info_events` - `just test -p codex-cli app_server_emits_json_info_events`
Michael Bolin ·
2026-06-26 18:19:56 -07:00 -
[codex] Observe remote exec-server lifecycle (#27470)
## Summary - Record bounded duration and outcome metrics for remote environment registration and Noise rendezvous connection attempts. - Count reconnects by bounded reason: disconnect, connection failure, or rejected registration. - Trace registration at the owning client boundary without exporting raw environment or registration identifiers. - Replace the stale pre-Noise WebSocket observability design with the current remote transport model. ## Stack Review and land this stack in order: 1. #27466 — trace exec-server JSON-RPC requests 2. #27467 — record bounded connection, request, and process lifecycle metrics 3. #27470 — observe remote registration and Noise rendezvous lifecycle **(this PR)** ## Validation - `just test -p codex-exec-server --lib` (149 passed) - `just test -p codex-cli --test exec_server` (4 passed) - `just argument-comment-lint` - `just bazel-lock-check` - `just fix -p codex-exec-server -p codex-cli` - `just fmt`
richardopenai ·
2026-06-25 13:42:40 -07:00 -
cli: rename sandbox permission profile flag (#30095)
## Why `codex sandbox` accepts a single named permissions profile, so the existing plural `--permissions-profile` spelling is misleading. The canonical flag and its help text should use the singular form without breaking scripts that already use the old spelling. ## What changed - Make `--permission-profile` the canonical flag for all sandbox backends. - Keep `--permissions-profile` as a hidden backwards-compatible alias. - Cover the canonical spelling, legacy alias, and help visibility with regression tests. ## Testing Ran `just c sandbox --help` and verified I saw: ```shell -P, --permission-profile <NAME> Named permissions profile to apply from the active configuration stack ```Michael Bolin ·
2026-06-25 11:25:19 -07:00 -
[codex] Record exec-server lifecycle metrics (#27467)
## Summary - Record bounded connection, request, and process lifecycle metrics. - Report active gauges from callbacks on every collection, including delta exports. - Serialize active-count updates so concurrent starts and finishes cannot publish stale values. - Serialize process exit, explicit termination, and shutdown through the process registry so exactly one completion result wins. - Keep the implementation small with single-owner RAII guards and one real OTLP/HTTP integration test using the existing `wiremock` dependency. ## Root cause Process exit and session shutdown previously used cloned completion state. That avoided duplicate emission, but it duplicated lifecycle ownership and made the ordering harder to reason about. The process registry mutex already defines the lifecycle ordering, so the final implementation stores the metric guard and termination flag directly on the process entry. Whichever path claims the entry first owns the completion result. Production metric export uses delta temporality. Event-only synchronous gauge recordings disappear after the next collection when no count changes, so active counts now use observable callbacks that report current state on every collection. The cleanup also removes the constant `result="accepted"` connection tag, redundant route and response assertions, a custom HTTP collector, and fallback initialization machinery that did not add behavior. ## Stack Review and land this stack in order: 1. #27466 — trace exec-server JSON-RPC requests 2. #27467 — record bounded connection, request, and process lifecycle metrics **(this PR)** 3. #27470 — observe remote registration and Noise rendezvous lifecycle ## Validation - `just test -p codex-exec-server --lib` (158 passed) - `just test -p codex-cli --test exec_server` (3 passed) - `just test -p codex-otel observable_gauge_is_collected_on_every_delta_snapshot` (1 passed) - `CARGO_BUILD_JOBS=1 just fix -p codex-otel -p codex-exec-server` - `just fmt` - `git diff --check`
richardopenai ·
2026-06-25 11:02:11 -07:00 -
[codex] Initialize exec-server OpenTelemetry at startup (#25019)
## Summary - Initialize stderr tracing and the configured OpenTelemetry provider for local and remote `codex exec-server` startup. - Instrument the local and remote server entrypoints with a root runtime span. - Keep raw Noise environment, registration, and stream identifiers out of exported spans while preserving them in local debug events. - Keep telemetry setup in a focused CLI module instead of growing the top-level command entrypoint. ## Stack - Previous: none (`#27058` has merged) - Next: #27466 ## Validation - `just test -p codex-exec-server --lib` (139 passed) - `just test -p codex-cli --test exec_server` (3 passed) - `just bazel-lock-check` - `just fix -p codex-exec-server -p codex-cli` - `just fmt` --------- Co-authored-by: Richard Lee <richardlee@openai.com>
starr-openai ·
2026-06-18 11:03:42 -07:00 -
[login] revoke existing auth before starting login (#27674)
## Why `codex login` previously persisted newly issued OAuth credentials and only then attempted to revoke the superseded refresh token. The old credential must be revoked before a replacement browser or device-code flow starts, and successful login must not perform any post-login revocation attempt. ## What changed - Revoke and clear existing stored auth before browser or device-code CLI login begins. - Remove superseded-token detection and revocation from the shared token persistence path; successful login now only saves the new credentials. - Read the raw configured auth store during CLI cleanup so environment-provided auth cannot mask the stored refresh token. - Preserve `auto` storage fallback semantics when keyring deletion fails by clearing the fallback auth file. - Add a process-level CLI regression test that requires the revoke request to precede every device-login request and occur exactly once. If replacement login is canceled or fails, the previous local credentials have already been cleared. Remote revocation remains best effort, matching explicit logout behavior. ## Validation ### Process-level before/after reproduction I compiled the real `codex` CLI from the pre-fix parent (`14df0e8833`) and from the PR implementation (`25c002f23b`; the login behavior is unchanged at the current head), then ran the same device-code flow against a local HTTP mock OAuth authority. Each run: 1. Used a fresh temporary `CODEX_HOME` configured with `cli_auth_credentials_store = "file"`. 2. Seeded that temporary home with managed ChatGPT auth containing `old-access` and `old-refresh` tokens. 3. Pointed `CODEX_REVOKE_TOKEN_URL_OVERRIDE` at the mock `/oauth/revoke` endpoint. 4. Ran the compiled CLI as: ```shell CODEX_HOME=<temporary-home> \ CODEX_REVOKE_TOKEN_URL_OVERRIDE=<mock-issuer>/oauth/revoke \ <compiled-codex> login --device-auth --experimental_issuer <mock-issuer> ``` 5. Recorded every request received by the mock authority. The mock marked `new-access` valid when `/oauth/token` issued it and invalidated it if `/oauth/revoke` arrived afterward, reproducing the observed session-invalidating failure mode. After login exited, the harness also verified the persisted refresh token and probed a protected endpoint with `new-access`. | Build | Observed request order | CLI/persistence result | `new-access` probe | | --- | --- | --- | --- | | Pre-fix | `usercode → device token → OAuth token → revoke(old-refresh)` | Exit `0`; `new-refresh` persisted | `401` | | PR | `revoke(old-refresh) → usercode → device token → OAuth token` | Exit `0`; `new-refresh` persisted | `200` | The PR run therefore issued exactly one revocation request, before any request that initiated the replacement login, and issued no revocation after token exchange. ### Regression coverage `codex-rs/cli/tests/login.rs::device_login_revokes_existing_auth_before_requesting_new_tokens` runs the real first-party `codex` binary against a `wiremock` OAuth server with an isolated temporary `CODEX_HOME`. It asserts: - the exact request sequence is `/oauth/revoke`, `/api/accounts/deviceauth/usercode`, `/api/accounts/deviceauth/token`, then `/oauth/token`; - there is exactly one revoke request and its body contains `old-refresh` with the `refresh_token` hint; - the completed login persists `new-refresh`. Local validation: - `just test -p codex-login` — 130 passed - `just test -p codex-cli` — 280 passed, including the new process-level regression test - `just bazel-lock-check`cooper-oai ·
2026-06-12 12:38:30 -07:00 -
Add session delete commands in CLI and TUI (#27476)
## Summary The app server exposes `thread/delete`, but users cannot invoke it from the CLI or TUI. Because deletion is irreversible, the user-facing commands need deliberate confirmation and safer handling of name-based targets. - Add `codex delete <SESSION>` with interactive confirmation, restricting `--force` to UUID targets. - Resolve exact names across active and archived sessions, including renamed sessions, and validate prompted UUID targets before confirmation. - Add a `/delete` command with a confirmation popup that warns the current session and its subagent threads will be permanently deleted. ## Manual testing - Deleted by UUID with `--force` and verified the rollout, session-index entry, and database row were removed. - Exercised name-based confirmation for both cancellation and affirmative deletion; cancellation preserved the session and confirmation removed it. - Verified deletion refuses to proceed without `--force`, while `--force` rejects names, including duplicate names. - Verified duplicate-name confirmation displays the concrete UUID selected. - Deleted an archived session by name. - Verified an already-missing UUID fails before displaying a confirmation prompt. - Exercised `/delete` in the TUI: the popup defaults to No, cancellation preserves the session, and confirmation deletes the session and exits. - Verified that `codex delete` works for both archived and non-archived sessions.
Eric Traut ·
2026-06-10 18:04:02 -07:00 -
Enforce configured network proxy in codex sandbox (#27035)
## Why `codex sandbox` can start a network proxy from a configured permission profile. Previously, sandbox-level containment was tied to managed network requirements rather than whether a proxy was actually active. This meant config-driven proxy policies were not consistently enforced as the sandbox's only network path. ## What changed - Enable proxy-only network containment whenever `codex sandbox` starts a network proxy. - Apply the same active-proxy check to the macOS and Linux sandbox paths. - Add a Linux regression test that verifies a sandboxed command cannot establish a direct connection while the configured proxy is active. ## Test plan - `just test -p codex-cli debug_sandbox::tests` - `sandbox_with_network_proxy_blocks_direct_loopback_access` runs on Linux to cover the config-driven proxy path end to end.
viyatb-oai ·
2026-06-08 14:03:37 -07:00 -
[plugins] Expose marketplace source in marketplace list JSON (#27009)
## Summary - Follow-up to #26417 and #26631 - Add `marketplaceSource` to `codex plugin marketplace list --json` entries for configured marketplaces - Reuse the existing `marketplaceSource` shape from `codex plugin list --json` - Keep human-readable marketplace list output unchanged - Add CLI coverage for configured local and git marketplace sources Example: ```json { "marketplaces": [ { "name": "debug", "root": "/path/to/.codex/.tmp/marketplaces/debug", "marketplaceSource": { "sourceType": "git", "source": "https://example.com/acme/agent-skills.git" } } ] } ``` ## Validation - `just fmt` - `just fix -p codex-cli` - `just test -p codex-cli marketplace_list` - `just test -p codex-cli`
mpc-oai ·
2026-06-08 13:37:55 -05:00 -
Add JSON output for plugin subcommands (#26631)
## Summary - Follow-up to #25330 and #26417 - Add `--json` output for `codex plugin add` and `codex plugin remove` - Add `--json` output for `codex plugin marketplace add/list/upgrade/remove` - Keep existing human-readable output unchanged - Keep existing error handling/stderr behavior unchanged; `--json` changes successful stdout output only - Align marketplace add/remove JSON field names with the existing app-server protocol shape - Add CLI coverage for plugin and marketplace JSON outputs ## Validation - `just fmt` - `just fix -p codex-cli` - `just test -p codex-cli`
mpc-oai ·
2026-06-05 14:40:31 -05:00 -
Expose configured marketplace source in plugin list JSON (#26417)
## Summary - Follow-up to #25330 - Add `marketplaceSource` to `codex plugin list --json` entries for configured marketplaces - Keep the existing per-plugin `source` field unchanged; this still reports the local plugin source path - Include only the configured marketplace `sourceType` and `source` from `config.toml` - Keep human-readable output unchanged - Add CLI coverage for configured local and git marketplace sources Example: ```json { "source": { "source": "local", "path": "/path/to/.codex/.tmp/marketplaces/debug/plugins/sample" }, "marketplaceSource": { "sourceType": "git", "source": "https://example.com/acme/agent-skills.git" } } ``` ## Validation - `just fmt` - `just fix -p codex-cli` - `just test -p codex-cli plugin_list`
mpc-oai ·
2026-06-04 12:20:32 -05:00 -
[codex] Add plugin list JSON output (#25330)
## Summary - add `--json` output to `codex plugin list` with `installed` and `available` arrays - add `--available` for JSON output only; using it without `--json` is rejected - keep the existing non-JSON table output unchanged - add CLI coverage for JSON installed/available output and the `--available`/`--json` requirement ## Validation - `just test -p codex-cli plugin_list` - `just fix -p codex-cli` - `git diff --check` Note: `just fmt` ran Rust formatting first, then failed in the Python ruff step because `openai-codex-cli-bin==0.132.0` has no wheel for this Linux platform.
xl-openai ·
2026-06-01 21:27:06 -07:00 -
Move memory state to a dedicated SQLite DB (#24591)
## Summary Generated memory rows and their stage-one/stage-two job state currently live in `state_5.sqlite` alongside thread metadata. That makes memory cleanup and regeneration share the main state schema even though those rows are memory-pipeline data and can be rebuilt independently from the durable thread records. This PR moves the memory-owned tables into a dedicated `memories_1.sqlite` runtime database while keeping thread metadata in `state_5.sqlite`. ## Changes - Adds a separate memories DB runtime, migrator, path helpers, telemetry kind, and Bazel compile data for `state/memory_migrations`. - Introduces `MemoryStore` behind `StateRuntime::memories()` and moves memory table/job operations onto that store. - Drops the old memory tables from the state DB and recreates their schema in `state/memory_migrations/0001_memories.sql`. - Updates memory startup, citation usage tracking, rollout pollution handling, `debug clear-memories`, and app-server `memory/reset` to operate through the memories DB. - Preserves cross-DB behavior by hydrating thread metadata from the state DB when selecting visible memory outputs and checking stage-one staleness. ## Verification - Added/updated `codex-state` tests for deleted-thread memory visibility and already-polluted phase-two enqueue behavior. - Updated `debug clear-memories`, app-server `memory/reset`, and memories startup tests to seed and assert memory rows through `memories_1.sqlite`.
jif-oai ·
2026-05-26 20:07:25 +02:00 -
Support OAuth options in codex mcp add (#24120)
## Summary - add `--oauth-client-id` and `--oauth-resource` options for streamable HTTP `codex mcp add` registrations - persist those options in MCP server config and use them during the immediate OAuth login flow - cover add-time serialization of both OAuth options in the CLI integration tests ## Testing - `just fmt` - `cargo test -p codex-cli` - `just fix -p codex-cli`
Matthew Zeng ·
2026-05-22 13:21:01 -07:00 -
mcp: surface profile migration guidance under --profile (#23890)
## Why `codex --profile <name> mcp ...` should reach the same profile-v2 migration guard as runtime commands. Otherwise legacy `[profiles.<name>]` users see the generic command-scope rejection instead of the existing guidance to move settings into `$CODEX_HOME/<name>.config.toml`. ## What - Allow `codex mcp` through the `--profile` subcommand gate. - Pass profile loader overrides into the MCP entry point only to validate profile-v2 migration when a profile is present. - Keep MCP add/remove/list/get/login/logout behavior otherwise unchanged; this does not add profile-scoped MCP server management. - Cover the legacy profile migration error for `codex --profile work mcp list`. ## Testing - `cargo test -p codex-cli`
jif-oai ·
2026-05-22 10:40:33 +02:00 -
[codex] List marketplaces considered by plugin discovery
Co-authored-by: Codex <noreply@openai.com>
Casey Chow ·
2026-05-20 19:17:46 -04:00 -
feat(plugins): tabulate plugin list output (#23727)
## Summary - render `codex plugin list` as one table per marketplace with the marketplace manifest path shown above each table - surface the installed plugin version in the CLI output by threading `installed_version` through marketplace listing state - narrow the system-root exemption so only known bundled/runtime marketplaces skip missing-manifest failures, and keep `VERSION` empty for cached-but-unconfigured plugins ## Rationale The plugin list UX was hard to scan as a flat list and did not show which installed version was active. This change makes the CLI output easier to read in the real multi-marketplace case, keeps the plugin path visible, fixes the Sapphire regression where bundled/runtime marketplace roots were blocking `plugin list`, and addresses the two review findings that came out of the follow-up deep review. ## Key Decisions - kept the CLI output grouped per marketplace instead of one global table so the marketplace path can live with the rows it owns - kept `VERSION` as the installed version, which means it is empty until a plugin is actually installed - handled the bundled/runtime regression in the CLI snapshot validation path rather than widening app-server protocol or changing marketplace loading behavior - narrowed the exemption to known system marketplace names plus expected system paths, so user-configured marketplaces under those directories still fail loudly - gated `installed_version` on actual installed state so `VERSION` cannot show stale cache state for `not installed` rows ## Validation - `just fmt` - Sapphire: `cargo test -p codex-cli --test plugin_cli` (`14 passed; 0 failed`) - Sapphire smoke test: bundled/runtime roots still work - `cargo run -q -p codex-cli -- plugin add sample@debug` - `cargo run -q -p codex-cli -- plugin list` - verified the bundled/runtime-root scenario no longer errors and shows the expected marketplace table output - Sapphire smoke test: custom marketplace under bundled path still errors - verified `failed to load configured marketplace snapshot(s)` for `custom-marketplace` - Sapphire smoke test: cached-but-unconfigured plugin hides version - verified `sample@debug not installed` renders with an empty `VERSION` column ## Sample Output ```text /tmp/custom-marketplace/plugin.json NAME VERSION STATUS DESCRIPTION sample@debug 1.0.0 enabled Debug sample plugin other@local not installed Local development plugin ```
Casey Chow ·
2026-05-20 18:04:49 -04:00 -
cli: add strict config to exec-server (#23719)
## Why PR #20559 added opt-in strict config parsing to the config-loading command surfaces, but `codex exec-server` was left out. That meant `codex exec-server --strict-config` was rejected even though the command can load config for remote registration, and local server startup had no way to fail fast on misspelled config keys. ## What Changed - Added `--strict-config` to `codex exec-server`. - Allowed root-level inheritance from `codex --strict-config exec-server`. - Validated config before local exec-server startup when strict mode is requested. - Reused the loaded strict-config-aware config for remote exec-server registration auth. - Added CLI coverage showing `codex exec-server --strict-config` rejects unknown config fields. ## Verification - `cargo test -p codex-cli` - New integration test: `strict_config_rejects_unknown_config_fields_for_exec_server` ## Documentation Any strict-config command list on developers.openai.com/codex should include `codex exec-server` with the other supported config-loading entry points.
Michael Bolin ·
2026-05-20 13:12:31 -07:00 -
[codex] add plugin marketplace CLI commands (#21396)
## Why Plugin CLI installs should behave more like `apt-get install`: configured marketplaces are the only install sources, the local marketplace snapshot is the package index used at install time, and `plugins/cache` is only a cache of already-downloaded plugin bytes. That distinction matters once marketplaces and plugins have auth or availability state. A repo-local marketplace manifest or leftover cached plugin artifact should not silently become an install source unless the marketplace was explicitly configured and its readable snapshot still authorizes the plugin. ## What Changed - add CLI commands to list configured marketplaces and add, list, or remove marketplace plugins - accept stable `plugin@marketplace` ids for add/remove while preserving the explicit `--marketplace` form - restrict `codex plugin add` and `codex plugin list` to configured marketplaces instead of also discovering current-working-directory marketplace roots - fail `codex plugin add` and `codex plugin list` when a configured marketplace snapshot is missing or malformed instead of treating it as an empty source or a generic plugin miss - preserve marketplace snapshot semantics: a configured local/Git marketplace snapshot can authorize installs without consulting the original upstream source - allow `plugins/cache` reuse only after configured marketplace resolution succeeds - keep removal resilient after marketplace deletion or drift and ignore malformed marketplace config entries in listing ## Commands Added - `codex plugin add <plugin>@<marketplace>` - `codex plugin add <plugin> --marketplace <marketplace>` - `codex plugin list` - `codex plugin list --marketplace <marketplace>` - `codex plugin remove <plugin>@<marketplace>` - `codex plugin remove <plugin> --marketplace <marketplace>` - `codex plugin marketplace add <source>` - `codex plugin marketplace add <source> --ref <ref>` - `codex plugin marketplace add <source> --sparse <path>` - `codex plugin marketplace list` - `codex plugin marketplace upgrade` - `codex plugin marketplace upgrade <marketplace>` - `codex plugin marketplace remove <marketplace>` ## CLI Help Output <details> <summary><code>codex plugin --help</code></summary> ```text Manage Codex plugins Usage: codex plugin [OPTIONS] <COMMAND> Commands: add Install a plugin from a configured marketplace snapshot list List plugins available from configured marketplace snapshots marketplace Add, list, upgrade, or remove configured plugin marketplaces remove Remove an installed plugin from local config and cache help Print this message or the help of the given subcommand(s) ``` </details> <details> <summary><code>codex plugin add --help</code></summary> ```text Install a plugin from a configured marketplace snapshot. Pass either `PLUGIN@MARKETPLACE` or pass `PLUGIN` with `--marketplace MARKETPLACE`. Usage: codex plugin add [OPTIONS] <PLUGIN[@MARKETPLACE]> Arguments: <PLUGIN[@MARKETPLACE]> Plugin selector to install: either PLUGIN@MARKETPLACE or PLUGIN with --marketplace Options: -m, --marketplace <MARKETPLACE> Configured marketplace name to use when PLUGIN does not include @MARKETPLACE Examples: codex plugin add sample@debug codex plugin add sample --marketplace debug ``` </details> <details> <summary><code>codex plugin list --help</code></summary> ```text List plugins available from configured marketplace snapshots Usage: codex plugin list [OPTIONS] Options: -m, --marketplace <MARKETPLACE> Only list plugins from this configured marketplace name Examples: codex plugin list codex plugin list --marketplace debug ``` </details> <details> <summary><code>codex plugin remove --help</code></summary> ```text Remove an installed plugin from local config and cache. Pass either `PLUGIN@MARKETPLACE` or pass `PLUGIN` with `--marketplace MARKETPLACE`. Usage: codex plugin remove [OPTIONS] <PLUGIN[@MARKETPLACE]> Arguments: <PLUGIN[@MARKETPLACE]> Plugin selector to remove: either PLUGIN@MARKETPLACE or PLUGIN with --marketplace Options: -m, --marketplace <MARKETPLACE> Marketplace name to use when PLUGIN does not include @MARKETPLACE Examples: codex plugin remove sample@debug codex plugin remove sample --marketplace debug ``` </details> <details> <summary><code>codex plugin marketplace --help</code></summary> ```text Add, list, upgrade, or remove configured plugin marketplaces Usage: codex plugin marketplace [OPTIONS] <COMMAND> Commands: add Add a local or Git marketplace to the configured marketplace sources list List configured marketplace names and their local snapshot roots upgrade Refresh configured Git marketplace snapshots remove Remove a configured marketplace source by name ``` </details> <details> <summary><code>codex plugin marketplace add --help</code></summary> ```text Add a local or Git marketplace to the configured marketplace sources Usage: codex plugin marketplace add [OPTIONS] <SOURCE> Arguments: <SOURCE> Marketplace source: a local path, owner/repo[@ref], HTTPS Git URL, or SSH Git URL Options: --ref <REF> Git ref to fetch for Git marketplace sources --sparse <PATH> Sparse checkout path for Git marketplace sources. Can be repeated Examples: codex plugin marketplace add ./path/to/marketplace codex plugin marketplace add owner/repo --ref main codex plugin marketplace add https://github.com/owner/repo --sparse plugins/foo ``` </details> <details> <summary><code>codex plugin marketplace list --help</code></summary> ```text List configured marketplace names and their local snapshot roots Usage: codex plugin marketplace list [OPTIONS] ``` </details> <details> <summary><code>codex plugin marketplace upgrade --help</code></summary> ```text Refresh configured Git marketplace snapshots. Omit MARKETPLACE_NAME to upgrade all configured Git marketplaces. Usage: codex plugin marketplace upgrade [OPTIONS] [MARKETPLACE_NAME] Arguments: [MARKETPLACE_NAME] Optional configured marketplace name to upgrade. Omit to upgrade all Git marketplaces Examples: codex plugin marketplace upgrade codex plugin marketplace upgrade debug ``` </details> <details> <summary><code>codex plugin marketplace remove --help</code></summary> ```text Remove a configured marketplace source by name Usage: codex plugin marketplace remove [OPTIONS] <MARKETPLACE_NAME> Arguments: <MARKETPLACE_NAME> Configured marketplace name to remove Example: codex plugin marketplace remove debug ``` </details> ## Public Semantics - `codex plugin add <plugin>@<marketplace>` succeeds only when `<marketplace>` is configured and its local marketplace snapshot contains `<plugin>` - repo-local marketplaces are not install sources until the user runs `codex plugin marketplace add ...` - configured marketplace snapshots must be readable; missing or malformed snapshots fail the CLI operation rather than silently falling through to cache or empty results - cached plugin artifacts can satisfy reinstall only when the configured marketplace snapshot still authorizes that plugin - cached plugin artifacts alone never make a plugin installable ## Tests - `cargo test -p codex-cli --test plugin_cli` - `cargo clippy -p codex-cli --tests -- -D warnings` - `cargo test -p codex-cli` - `git diff --check` - `just bazel-lock-update` - `just bazel-lock-check`Casey Chow ·
2026-05-14 09:33:38 -07:00 -
config: add strict config parsing (#20559)
## Why Codex intentionally ignores unknown `config.toml` fields by default so older and newer config files keep working across versions. That leniency also makes typo detection hard because misspelled or misplaced keys disappear silently. This change adds an opt-in strict config mode so users and tooling can fail fast on unrecognized config fields without changing the default permissive behavior. This feature is possible because `serde_ignored` exposes the exact signal Codex needs: it lets Codex run ordinary Serde deserialization while recording fields Serde would otherwise ignore. That avoids requiring `#[serde(deny_unknown_fields)]` across every config type and keeps strict validation opt-in around the existing config model. ## What Changed ### Added strict config validation - Added `serde_ignored`-based validation for `ConfigToml` in `codex-rs/config/src/strict_config.rs`. - Combined `serde_ignored` with `serde_path_to_error` so strict mode preserves typed config error paths while also collecting fields Serde would otherwise ignore. - Added strict-mode validation for unknown `[features]` keys, including keys that would otherwise be accepted by `FeaturesToml`'s flattened boolean map. - Kept typed config errors ahead of ignored-field reporting, so malformed known fields are reported before unknown-field diagnostics. - Added source-range diagnostics for top-level and nested unknown config fields, including non-file managed preference source names. ### Kept parsing single-pass per source - Reworked file and managed-config loading so strict validation reuses the already parsed `TomlValue` for that source. - For actual config files and managed config strings, the loader now reads once, parses once, and validates that same parsed value instead of deserializing multiple times. - Validated `-c` / `--config` override layers with the same base-directory context used for normal relative-path resolution, so unknown override keys are still reported when another override contains a relative path. ### Scoped `--strict-config` to config-heavy entry points - Added support for `--strict-config` on the main config-loading entry points where it is most useful: - `codex` - `codex resume` - `codex fork` - `codex exec` - `codex review` - `codex mcp-server` - `codex app-server` when running the server itself - the standalone `codex-app-server` binary - the standalone `codex-exec` binary - Commands outside that set now reject `--strict-config` early with targeted errors instead of accepting it everywhere through shared CLI plumbing. - `codex app-server` subcommands such as `proxy`, `daemon`, and `generate-*` are intentionally excluded from the first rollout. - When app-server strict mode sees invalid config, app-server exits with the config error instead of logging a warning and continuing with defaults. - Introduced a dedicated `ReviewCommand` wrapper in `codex-rs/cli` instead of extending shared `ReviewArgs`, so `--strict-config` stays on the outer config-loading command surface and does not become part of the reusable review payload used by `codex exec review`. ### Coverage - Added tests for top-level and nested unknown config fields, unknown `[features]` keys, typed-error precedence, source-location reporting, and non-file managed preference source names. - Added CLI coverage showing invalid `--enable`, invalid `--disable`, and unknown `-c` overrides still error when `--strict-config` is present, including compound-looking feature names such as `multi_agent_v2.subagent_usage_hint_text`. - Added integration coverage showing both `codex app-server --strict-config` and standalone `codex-app-server --strict-config` exit with an error for unknown config fields instead of starting with fallback defaults. - Added coverage showing unsupported command surfaces reject `--strict-config` with explicit errors. ## Example Usage Run Codex with strict config validation enabled: ```shell codex --strict-config ``` Strict config mode is also available on the supported config-heavy subcommands: ```shell codex --strict-config exec "explain this repository" codex review --strict-config --uncommitted codex mcp-server --strict-config codex app-server --strict-config --listen off codex-app-server --strict-config --listen off ``` For example, if `~/.codex/config.toml` contains a typo in a key name: ```toml model = "gpt-5" approval_polic = "on-request" ``` then `codex --strict-config` reports the misspelled key instead of silently ignoring it. The path is shortened to `~` here for readability: ```text $ codex --strict-config Error loading config.toml: ~/.codex/config.toml:2:1: unknown configuration field `approval_polic` | 2 | approval_polic = "on-request" | ^^^^^^^^^^^^^^ ``` Without `--strict-config`, Codex keeps the existing permissive behavior and ignores the unknown key. Strict config mode also validates ad-hoc `-c` / `--config` overrides: ```text $ codex --strict-config -c foo=bar Error: unknown configuration field `foo` in -c/--config override $ codex --strict-config -c features.foo=true Error: unknown configuration field `features.foo` in -c/--config override ``` Invalid feature toggles are rejected too, including values that look like nested config paths: ```text $ codex --strict-config --enable does_not_exist Error: Unknown feature flag: does_not_exist $ codex --strict-config --disable does_not_exist Error: Unknown feature flag: does_not_exist $ codex --strict-config --enable multi_agent_v2.subagent_usage_hint_text Error: Unknown feature flag: multi_agent_v2.subagent_usage_hint_text ``` Unsupported commands reject the flag explicitly: ```text $ codex --strict-config cloud list Error: `--strict-config` is not supported for `codex cloud` ``` ## Verification The `codex-cli` `strict_config` tests cover invalid `--enable`, invalid `--disable`, the compound `multi_agent_v2.subagent_usage_hint_text` case, unknown `-c` overrides, app-server strict startup failure through `codex app-server`, and rejection for unsupported commands such as `codex cloud`, `codex mcp`, `codex remote-control`, and `codex app-server proxy`. The config and config-loader tests cover unknown top-level fields, unknown nested fields, unknown `[features]` keys, source-location reporting, non-file managed config sources, and `-c` validation for keys such as `features.foo`. The app-server test suite covers standalone `codex-app-server --strict-config` startup failure for an unknown config field. ## Documentation The Codex CLI docs on developers.openai.com/codex should mention `--strict-config` as an opt-in validation mode for supported config-heavy entry points once this ships.
Michael Bolin ·
2026-05-13 16:08:05 +00:00 -
Rename agent identity login surface to access token (#21059)
## Why The external startup/login surface for this auth path should talk about an access token instead of exposing the internal Agent Identity terminology. Users should pass `CODEX_ACCESS_TOKEN` or pipe a token into `codex login --with-access-token`; the old external env/flag spellings are removed so there is only one supported user-facing path. ## What Changed - Added `CODEX_ACCESS_TOKEN` as the supported environment variable for this auth path. - Added `codex login --with-access-token` as the supported stdin-based login command. - Removed the legacy `CODEX_AGENT_IDENTITY` env-var fallback and hidden `--with-agent-identity` CLI alias. - Updated CLI error, status, and stdin prompts to use access-token language. - Added coverage for access-token env loading, CLI login failure behavior, and renamed login status text. ## Validation - `cargo test -p codex-login` - `cargo test -p codex-cli` - `just fix -p codex-login` - `just fix -p codex-cli`
Shijie Rao ·
2026-05-04 19:43:48 -07:00 -
efrazer-oai ·
2026-04-28 09:56:20 -07:00 -
Add
codex updatecommand (#19933)## Why Addresses #9274 Running `codex update` currently starts an interactive Codex session with `update` as the prompt. That is a rough edge for users who expect a direct self-update command after seeing the existing update notice, and it forces them to copy the suggested package-manager command manually. ## What changed - Added a top-level `codex update` subcommand. - Reused the existing install-channel detection and update command runner that the TUI already uses for update prompts. - Exposed the update-action lookup from `codex-tui` so the CLI can invoke the same behavior. - Added CLI coverage to ensure `codex update` is parsed as a subcommand instead of becoming an interactive prompt. ## Verification - `cargo test -p codex-cli` - `cargo test -p codex-tui update_action::tests`
Eric Traut ·
2026-04-27 23:33:59 -07:00 -
feat: load AgentIdentity from JWT login/env (#18904)
## Summary This PR lets programmatic AgentIdentity users provide one token through either stdin login or environment auth. `codex login --with-agent-identity` reads an Agent Identity JWT from stdin, validates that it has the required claims, and stores that token as the `agent_identity` value in `auth.json`. The file format is token-only; the decoded account and key fields are runtime state, not hand-authored auth.json fields. The Agent Identity JWT claim shape and decoder live in `codex-agent-identity`; `codex-login` only owns env/storage precedence and conversion into `CodexAuth::AgentIdentity`. When env auth is enabled, `CODEX_AGENT_IDENTITY` can provide the same JWT without writing auth state to disk. `CODEX_API_KEY` still wins if both env vars are set. Reference old stack: https://github.com/openai/codex/pull/17387/changes Reference JWT/env stack: https://github.com/openai/codex/pull/18176 ## Stack 1. https://github.com/openai/codex/pull/18757: full revert 2. https://github.com/openai/codex/pull/18871: isolated Agent Identity crate 3. https://github.com/openai/codex/pull/18785: explicit AgentIdentity auth mode and startup task allocation 4. https://github.com/openai/codex/pull/18811: migrate Codex backend auth callsites through AuthProvider 5. This PR: accept AgentIdentity JWTs through login/env ## Testing Tests: targeted login and Agent Identity crate tests, CLI checks, scoped formatter/linter cleanup, and CI. --------- Co-authored-by: Shijie Rao <shijie.rao@openai.com>
efrazer-oai ·
2026-04-26 19:49:54 +00:00 -
Move marketplace add/remove and startup sync out of core. (#19099)
Move more things to core-plugins. --------- Co-authored-by: Codex <noreply@openai.com>
xl-openai ·
2026-04-23 11:27:17 -07:00 -
Stabilize debug clear memories integration test (#18858)
## Why `debug_clear_memories_resets_state_and_removes_memory_dir` can be flaky because the test drops its `sqlx::SqlitePool` immediately before invoking `codex debug clear-memories`. Dropping the pool does not wait for all SQLite connections to close, so the CLI can race with still-open test connections. ## What changed - Await `pool.close()` before spawning `codex debug clear-memories`. - Close the reopened verification pool before the temp `CODEX_HOME` is torn down. ## Verification - `cargo test -p codex-cli --test debug_clear_memories debug_clear_memories_resets_state_and_removes_memory_dir`
jif-oai ·
2026-04-21 18:15:37 +01:00 -
Andrey Mishchenko ·
2026-04-20 05:42:22 +00:00 -
[5/6] Wire executor-backed MCP stdio (#18212)
## Summary - Add the executor-backed RMCP stdio transport. - Wire MCP stdio placement through the executor environment config. - Cover local and executor-backed stdio paths with the existing MCP test helpers. ## Stack ```text o #18027 [6/6] Fail exec client operations after disconnect │ @ #18212 [5/6] Wire executor-backed MCP stdio │ o #18087 [4/6] Abstract MCP stdio server launching │ o #18020 [3/6] Add pushed exec process events │ o #18086 [2/6] Support piped stdin in exec process API │ o #18085 [1/6] Add MCP server environment config │ o main ``` --------- Co-authored-by: Codex <noreply@openai.com>
Ahmed Ibrahim ·
2026-04-18 21:47:43 -07:00 -
[codex] Add marketplace remove command and shared logic (#17752)
## Summary Move the marketplace remove implementation into shared core logic so both the CLI command and follow-up app-server RPC can reuse the same behavior. This change: - adds a shared `codex_core::plugins::remove_marketplace(...)` flow - moves validation, config removal, and installed-root deletion out of the CLI - keeps the CLI as a thin wrapper over the shared implementation - adds focused core coverage for the shared remove path ## Validation - `just fmt` - focused local coverage for the shared remove path - heavier follow-up validation deferred to stacked PR CI
xli-oai ·
2026-04-17 21:44:47 -07:00 -
Move marketplace add under plugin command (#18116)
## Summary - move the marketplace add CLI from `codex marketplace add` to `codex plugin marketplace add` - keep marketplace config overrides working through the nested plugin command - reject `--sparse` for local marketplace directory sources before the local-source install path bypasses git-source validation ## Validation - `just fmt` - `git diff --check` - `cargo test -p codex-cli` - `cargo test -p codex-core marketplace_add -- --nocapture` - `cargo test -p codex-core install_plugin_updates_config_with_relative_path_and_plugin_key -- --nocapture` - `xli-test-marketplace-cli` local isolated matrix: `T1`, `L1`-`L10`
xli-oai ·
2026-04-16 17:06:34 -07:00 -
chore: unify memory drop endpoints (#18134)
Unify all the memories drop behind a single implementation that drops both the main memories and the extensions
jif-oai ·
2026-04-16 15:44:23 +01:00 -
jif-oai ·
2026-04-15 12:05:39 +01:00 -
[codex] Support local marketplace sources (#17756)
## Summary - Port marketplace source support into the shared core marketplace-add flow - Support local marketplace directory sources - Support direct `marketplace.json` URL sources - Persist the new source types in config/schema and cover them in CLI and app-server tests ## Validation - `cargo test -p codex-core marketplace_add` - `cargo test -p codex-cli marketplace_add` - `cargo test -p codex-app-server marketplace_add` - `just write-config-schema` - `just fmt` - `just fix -p codex-core` - `just fix -p codex-cli` ## Context Current `main` moved marketplace-add behavior into shared core code and still assumed only git-backed sources. This change keeps that structure but restores support for local directories and direct manifest URLs in the shared path.
xli-oai ·
2026-04-14 15:58:14 -07:00 -
Stabilize marketplace add local source test (#17424)
## Summary - Update the marketplace add local-source integration test to pass an explicit relative local path. - Keep the change test-only; no CLI source parsing behavior changes. ## Tests - cargo fmt -p codex-cli - cargo test -p codex-cli --test marketplace_add ## Impact - Production behavior is unchanged. - No impact to feedback upload logic, DAGs, exports, or downstream pipelines. Co-authored-by: Codex <noreply@openai.com>
ningyi-oai ·
2026-04-11 05:06:59 +00:00 -
Add marketplace command (#17087)
Added a new top-level `codex marketplace add` command for installing plugin marketplaces into Codex’s local marketplace cache. This change adds source parsing for local directories, GitHub shorthand, and git URLs, supports optional `--ref` and git-only `--sparse` checkout paths, stages the source in a temp directory, validates the marketplace manifest, and installs it under `$CODEX_HOME/marketplaces/<marketplace-name>` Included tests cover local install behavior in the CLI and marketplace discovery from installed roots in core. Scoped formatting and fix passes were run, and targeted CLI/core tests passed.
xli-oai ·
2026-04-10 19:18:37 -07:00 -
[codex] Remove codex-core config type shim (#16529)
## Why This finishes the config-type move out of `codex-core` by removing the temporary compatibility shim in `codex_core::config::types`. Callers now depend on `codex-config` directly, which keeps these config model types owned by the config crate instead of re-expanding `codex-core` as a transitive API surface. ## What Changed - Removed the `codex-rs/core/src/config/types.rs` re-export shim and the `core::config::ApprovalsReviewer` re-export. - Updated `codex-core`, `codex-cli`, `codex-tui`, `codex-app-server`, `codex-mcp-server`, and `codex-linux-sandbox` call sites to import `codex_config::types` directly. - Added explicit `codex-config` dependencies to downstream crates that previously relied on the `codex-core` re-export. - Regenerated `codex-rs/core/config.schema.json` after updating the config docs path reference.
Michael Bolin ·
2026-04-02 01:19:44 -07:00 -
jif-oai ·
2026-03-07 03:48:36 -08:00 -
feat: add debug clear-memories command to hard-wipe memories state (#13085)
#### what adds a `codex debug clear-memories` command to help with clearing all memories state from disk, sqlite db, and marking threads as `memory_mode=disabled` so they don't get resummarized when the `memories` feature is re-enabled. #### tests add tests
sayan-oai ·
2026-02-27 17:45:55 -08:00 -
fix: sort codex features list alphabetically (#12944)
## Why `codex features list` currently prints features in declaration order from `codex_core::features::FEATURES`. That makes the output harder to scan when looking for a specific flag, and the order can change for reasons unrelated to the CLI. ## What changed - Sort the `codex features list` rows by feature key before printing them in `codex-rs/cli/src/main.rs`. - Add an integration test in `codex-rs/cli/tests/features.rs` that runs `codex features list` and asserts the feature-name column is alphabetized. ## Verification - Added `features_list_is_sorted_alphabetically_by_feature_name`. - Ran `cargo test -p codex-cli`.
Michael Bolin ·
2026-02-26 14:44:39 -08:00 -
jif-oai ·
2026-02-23 16:12:23 +00:00 -
Add features enable/disable subcommands (#10180)
## Summary - add `codex features enable <feature>` and `codex features disable <feature>` - persist feature flag changes to `config.toml` (respecting profile) - print the under-development feature warning when enabling prerelease features - keep `features list` behavior unchanged and add unit/integration tests ## Testing - cargo test -p codex-cli
Josh McKinney ·
2026-01-29 20:35:03 +00:00 -
Propagate MCP disabled reason (#9207)
Indicate why MCP servers are disabled when they are disabled by requirements: ``` ➜ codex git:(main) ✗ just codex mcp list cargo run --bin codex -- "$@" Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s Running `target/debug/codex mcp list` Name Command Args Env Cwd Status Auth docs docs-mcp - - - disabled: requirements (MDM com.openai.codex:requirements_toml_base64) Unsupported hello_world hello-world-mcp - - - disabled: requirements (MDM com.openai.codex:requirements_toml_base64) Unsupported ➜ codex git:(main) ✗ just c cargo run --bin codex -- "$@" Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.90s Running `target/debug/codex` ╭─────────────────────────────────────────────╮ │ >_ OpenAI Codex (v0.0.0) │ │ │ │ model: gpt-5.2 xhigh /model to change │ │ directory: ~/code/codex/codex-rs │ ╰─────────────────────────────────────────────╯ /mcp 🔌 MCP Tools • No MCP tools available. • docs (disabled) • Reason: requirements (MDM com.openai.codex:requirements_toml_base64) • hello_world (disabled) • Reason: requirements (MDM com.openai.codex:requirements_toml_base64) ```gt-oai ·
2026-01-15 17:24:00 +00:00 -
feat: add justification arg to prefix_rule() in *.rules (#8751)
Adds an optional `justification` parameter to the `prefix_rule()` execpolicy DSL so policy authors can attach human-readable rationale to a rule. That justification is propagated through parsing/matching and can be surfaced to the model (or approval UI) when a command is blocked or requires approval. When a command is rejected (or gated behind approval) due to policy, a generic message makes it hard for the model/user to understand what went wrong and what to do instead. Allowing policy authors to supply a short justification improves debuggability and helps guide the model toward compliant alternatives. Example: ```python prefix_rule( pattern = ["git", "push"], decision = "forbidden", justification = "pushing is blocked in this repo", ) ``` If Codex tried to run `git push origin main`, now the failure would include: ``` `git push origin main` rejected: pushing is blocked in this repo ``` whereas previously, all it was told was: ``` execpolicy forbids this command ```Michael Bolin ·
2026-01-05 21:24:48 +00:00 -
feat: introduce codex-utils-cargo-bin as an alternative to assert_cmd::Command (#8496)
This PR introduces a `codex-utils-cargo-bin` utility crate that wraps/replaces our use of `assert_cmd::Command` and `escargot::CargoBuild`. As you can infer from the introduction of `buck_project_root()` in this PR, I am attempting to make it possible to build Codex under [Buck2](https://buck2.build) as well as `cargo`. With Buck2, I hope to achieve faster incremental local builds (largely due to Buck2's [dice](https://buck2.build/docs/insights_and_knowledge/modern_dice/) build strategy, as well as benefits from its local build daemon) as well as faster CI builds if we invest in remote execution and caching. See https://buck2.build/docs/getting_started/what_is_buck2/#why-use-buck2-key-advantages for more details about the performance advantages of Buck2. Buck2 enforces stronger requirements in terms of build and test isolation. It discourages assumptions about absolute paths (which is key to enabling remote execution). Because the `CARGO_BIN_EXE_*` environment variables that Cargo provides are absolute paths (which `assert_cmd::Command` reads), this is a problem for Buck2, which is why we need this `codex-utils-cargo-bin` utility. My WIP-Buck2 setup sets the `CARGO_BIN_EXE_*` environment variables passed to a `rust_test()` build rule as relative paths. `codex-utils-cargo-bin` will resolve these values to absolute paths, when necessary. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8496). * #8498 * __->__ #8496
Michael Bolin ·
2025-12-23 19:29:32 -08:00 -
fix: policy/*.codexpolicy -> rules/*.rules (#7888)
We decided that `*.rules` is a more fitting (and concise) file extension than `*.codexpolicy`, so we are changing the file extension for the "execpolicy" effort. We are also changing the subfolder of `$CODEX_HOME` from `policy` to `rules` to match. This PR updates the in-repo docs and we will update the public docs once the next CLI release goes out. Locally, I created `~/.codex/rules/default.rules` with the following contents: ``` prefix_rule(pattern=["gh", "pr", "view"]) ``` And then I asked Codex to run: ``` gh pr view 7888 --json title,body,comments ``` and it was able to!
Michael Bolin ·
2025-12-11 14:46:00 -08:00 -
Refactor execpolicy fallback evaluation (#7544)
## Refactor of the `execpolicy` crate To illustrate why we need this refactor, consider an agent attempting to run `apple | rm -rf ./`. Suppose `apple` is allowed by `execpolicy`. Before this PR, `execpolicy` would consider `apple` and `pear` and only render one rule match: `Allow`. We would skip any heuristics checks on `rm -rf ./` and immediately approve `apple | rm -rf ./` to run. To fix this, we now thread a `fallback` evaluation function into `execpolicy` that runs when no `execpolicy` rules match a given command. In our example, we would run `fallback` on `rm -rf ./` and prevent `apple | rm -rf ./` from being run without approval.
zhao-oai ·
2025-12-03 23:39:48 -08:00 -
execpolicycheck command in codex cli (#7012)
adding execpolicycheck tool onto codex cli this is useful for validating policies (can be multiple) against commands. it will also surface errors in policy syntax: <img width="1150" height="281" alt="Screenshot 2025-11-19 at 12 46 21 PM" src="https://github.com/user-attachments/assets/8f99b403-564c-4172-acc9-6574a8d13dc3" /> this PR also changes output format when there's no match in the CLI. instead of returning the raw string `noMatch`, we return `{"noMatch":{}}` this PR is a rewrite of: https://github.com/openai/codex/pull/6932 (due to the numerous merge conflicts present in the original PR) --------- Co-authored-by: Michael Bolin <mbolin@openai.com>
zhao-oai ·
2025-11-20 16:44:31 -05:00 -
jif-oai ·
2025-10-30 10:28:32 +00:00 -
chore: config editor (#5878)
The goal is to have a single place where we actually write files In a follow-up PR, will move everything config related in a dedicated module and move the helpers in a dedicated file
jif-oai ·
2025-10-29 20:52:46 +00:00