mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
Merge remote-tracking branch 'origin/main' into codex/viyatb/guardian-reviewer-retries
This commit is contained in:
@@ -105,9 +105,9 @@ common:ci --disk_cache=
|
||||
# Shared config for the main Bazel CI workflow.
|
||||
common:ci-bazel --config=ci
|
||||
common:ci-bazel --build_metadata=TAG_workflow=bazel
|
||||
# Bazel CI cross-compiles in several legs, and the V8-backed code-mode tests
|
||||
# are not stable in that setup yet. Keep running the rest of the Rust
|
||||
# integration suites through the workspace-root launcher.
|
||||
# Keep code-mode integration cases out of ordinary Bazel legs. The
|
||||
# Windows-cross config below re-enables them after generating its Windows V8
|
||||
# snapshot on the Windows runner.
|
||||
common:ci-bazel --test_env=CODEX_BAZEL_TEST_SKIP_FILTERS=suite::code_mode::
|
||||
|
||||
# Shared config for Bazel-backed Rust linting.
|
||||
@@ -186,12 +186,16 @@ common:ci-windows-cross --config=ci-windows
|
||||
common:ci-windows-cross --build_metadata=TAG_windows_cross_compile=true
|
||||
common:ci-windows-cross --host_platform=//:rbe
|
||||
common:ci-windows-cross --strategy=TestRunner=local
|
||||
# V8 embeds IsolateData offsets in snapshot builtins; Windows snapshots must be
|
||||
# generated by a Windows mksnapshot binary rather than the Linux RBE host tool.
|
||||
common:ci-windows-cross --strategy=V8Mksnapshot=local
|
||||
common:ci-windows-cross --local_test_jobs=4
|
||||
common:ci-windows-cross --test_env=RUST_TEST_THREADS=1
|
||||
# Native Windows CI still covers the PowerShell tests. The cross-built gnullvm
|
||||
# binaries currently hang in PowerShell AST parser tests when those binaries are
|
||||
# run on the Windows runner.
|
||||
common:ci-windows-cross --test_env=CODEX_BAZEL_TEST_SKIP_FILTERS=suite::code_mode::,powershell
|
||||
# run on the Windows runner. Keep V8-backed code-mode tests enabled except for
|
||||
# the hidden dynamic-tool callback test, which currently times out on Windows.
|
||||
common:ci-windows-cross --test_env=CODEX_BAZEL_TEST_SKIP_FILTERS=powershell,suite::code_mode::code_mode_can_call_hidden_dynamic_tools
|
||||
common:ci-windows-cross --platforms=//:windows_x86_64_gnullvm
|
||||
common:ci-windows-cross --extra_execution_platforms=//:rbe,//:windows_x86_64_msvc
|
||||
common:ci-windows-cross --extra_toolchains=//:windows_gnullvm_tests_on_msvc_host_toolchain
|
||||
@@ -210,6 +214,8 @@ common --@v8//:v8_enable_sandbox=True
|
||||
# artifact migration ships matching Rust feature selection for Cargo consumers.
|
||||
common:v8-release-compat --@v8//:v8_enable_pointer_compression=False
|
||||
common:v8-release-compat --@v8//:v8_enable_sandbox=False
|
||||
common:v8-target-x64 --@v8//bazel/config:v8_target_cpu=x64
|
||||
common:v8-target-arm64 --@v8//bazel/config:v8_target_cpu=arm64
|
||||
|
||||
# Match rusty_v8's upstream GN release contract for published artifacts: every
|
||||
# target object uses Chromium's custom libc++ headers and the archive folds in
|
||||
|
||||
@@ -3,7 +3,7 @@ version = 1
|
||||
name = "codex"
|
||||
|
||||
[setup]
|
||||
script = ""
|
||||
script = "python ./.codex/environments/setup.py"
|
||||
|
||||
[[actions]]
|
||||
name = "Run"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Set up ignored files that should be shared with Codex worktrees."""
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from functools import cache
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@cache
|
||||
def worktree_paths() -> tuple[Path, Path]:
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
worktree_root = git_path(script_dir / "../..", "--show-toplevel")
|
||||
common_git_dir = git_path(worktree_root, "--git-common-dir")
|
||||
return worktree_root, common_git_dir.parent
|
||||
|
||||
|
||||
def git_path(working_directory: Path, argument: str) -> Path:
|
||||
output = subprocess.check_output(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
str(working_directory),
|
||||
"rev-parse",
|
||||
"--path-format=absolute",
|
||||
argument,
|
||||
],
|
||||
text=True,
|
||||
)
|
||||
return Path(output.strip())
|
||||
|
||||
|
||||
def copy_from_main_worktree_to_worktree(repo_relative_path: str) -> None:
|
||||
relative_path = Path(repo_relative_path)
|
||||
if relative_path.is_absolute() or ".." in relative_path.parts:
|
||||
raise ValueError(f"path must be repository-relative: {repo_relative_path}")
|
||||
|
||||
worktree_root, main_worktree = worktree_paths()
|
||||
source_path = main_worktree / relative_path
|
||||
destination_path = worktree_root / relative_path
|
||||
|
||||
print(f" source: {source_path}")
|
||||
print(f" destination: {destination_path}")
|
||||
|
||||
if source_path == destination_path:
|
||||
print(" result: running in the main worktree; nothing to copy")
|
||||
elif destination_path.exists():
|
||||
print(" result: destination already exists; nothing to copy")
|
||||
elif not source_path.is_file():
|
||||
print(" result: source does not exist; nothing to copy")
|
||||
else:
|
||||
destination_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(source_path, destination_path)
|
||||
print(f" result: copied {repo_relative_path}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print("Codex environment setup:")
|
||||
# See codex-rs/docs/bazel.md for the repository's Bazel workflow.
|
||||
copy_from_main_worktree_to_worktree("user.bazelrc")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -29,7 +29,7 @@ Accept any of the following:
|
||||
4. If `diagnose_ci_failure` is present, inspect failed run logs and classify the failure.
|
||||
5. If the failure is likely caused by the current branch, patch code locally, commit, and push. Do not patch random flaky tests, CI infrastructure, dependency outages, runner issues, or other failures that are unrelated to the branch.
|
||||
6. If `process_review_comment` is present, inspect surfaced review items and decide whether to address them.
|
||||
7. If a review item is actionable and correct, patch code locally, commit, push, and then mark the associated review thread/comment as resolved once the fix is on GitHub.
|
||||
7. If a review item is actionable and correct, patch code locally, commit, push, and then resolve the associated review thread only when allowed by the GitHub state mutation policy below.
|
||||
8. Do not post replies to human-authored review comments/threads unless the user explicitly confirms the exact response. If a human review item is non-actionable, already addressed, or not valid, surface the item and recommended response to the user instead of replying on GitHub.
|
||||
9. If the failure is likely flaky/unrelated and `retry_failed_checks` is present, rerun failed jobs with `--retry-failed-now`.
|
||||
10. If both actionable review feedback and `retry_failed_checks` are present, prioritize review feedback first; a new commit will retrigger CI, so avoid rerunning flaky checks on the old SHA unless you intentionally defer the review change.
|
||||
@@ -101,7 +101,7 @@ When you agree with a comment and it is actionable:
|
||||
1. Patch code locally.
|
||||
2. Commit with `codex: address PR review feedback (#<n>)`.
|
||||
3. Push to the PR head branch.
|
||||
4. After the push succeeds, mark the associated GitHub review thread/comment as resolved.
|
||||
4. After the push succeeds, resolve the associated GitHub review thread only when allowed by the GitHub state mutation policy below.
|
||||
5. Resume watching on the new SHA immediately (do not stop after reporting the push).
|
||||
6. If monitoring was running in `--watch` mode, restart `--watch` immediately after the push in the same turn; do not wait for the user to ask again.
|
||||
|
||||
@@ -109,6 +109,31 @@ Do not post replies to human-authored GitHub review comments/threads automatical
|
||||
If the watcher later surfaces your own approved reply because the authenticated operator is treated as a trusted review author, treat that self-authored item as already handled and do not reply again.
|
||||
If a code review comment/thread is already marked as resolved in GitHub, treat it as non-actionable and safely ignore it unless new unresolved follow-up feedback appears.
|
||||
|
||||
## GitHub State Mutation Policy
|
||||
|
||||
You can read any PR state you need for monitoring. Writes must comply with this policy.
|
||||
|
||||
You can push PRs to update the code under review or to force CI re-runs as described above.
|
||||
|
||||
You can resolve review comment threads from the human who requested babysitting or from the Codex
|
||||
review bot. When resolving, leave a comment prefixed with `[from Codex]: ` and explain what changes
|
||||
you made and which commit includes them. Don't touch review threads if other humans other than the
|
||||
user who requested babysitting have participated.
|
||||
|
||||
Before making any changes, fetch the PR state yourself instead of relying on the PR watcher script's
|
||||
output.
|
||||
|
||||
Unless explicitly asked, do not:
|
||||
|
||||
* comment on other humans' review threads, communicate with the user in chat instead
|
||||
* resolve review threads from humans other than the user
|
||||
* interact with humans other than the user
|
||||
* mark PRs as drafts or ready for review
|
||||
* close or reopen PRs
|
||||
|
||||
In general, never act on GitHub in ways that would make it hard to tell whether you or the user did
|
||||
something visible to other humans. When in doubt, ask the user for clarification in chat.
|
||||
|
||||
## Git Safety Rules
|
||||
|
||||
- Work only on the PR head branch.
|
||||
@@ -133,10 +158,10 @@ Use this loop in a live Codex session:
|
||||
3. First check whether the PR is now merged or otherwise closed; if so, report that terminal state and stop polling immediately.
|
||||
4. Check CI summary, new review items, and mergeability/conflict status.
|
||||
5. Diagnose CI failures and classify branch-related vs flaky/unrelated. If the overall run is still pending but `failed_jobs` already includes a failed job, fetch that job's logs and diagnose immediately instead of waiting for the whole workflow run to finish. Patch only when the failure is branch-related.
|
||||
6. For each surfaced review item from another author, patch/commit/push and then resolve it if it is actionable. If it is non-actionable, already addressed, or requires a written answer, surface it to the user with a suggested response instead of posting automatically. If a later snapshot surfaces your own approved reply, treat it as informational and continue without responding again.
|
||||
6. For each surfaced review item from another author, patch/commit/push if it is actionable, then resolve it only when allowed by the GitHub state mutation policy above. If it is non-actionable, already addressed, or requires a written answer, surface it to the user with a suggested response instead of posting automatically. If a later snapshot surfaces your own approved reply, treat it as informational and continue without responding again.
|
||||
7. Process actionable review comments before flaky reruns when both are present; if a review fix requires a commit, push it and skip rerunning failed checks on the old SHA.
|
||||
8. Retry failed checks only when `retry_failed_checks` is present and you are not about to replace the current SHA with a review/CI fix commit. Do not make code changes for unrelated flakes or infrastructure failures just to get CI green.
|
||||
9. If you pushed a commit, resolved a review thread, or triggered a rerun, report the action briefly and continue polling (do not stop). If a human review comment needs a written GitHub response, stop and ask for confirmation before posting.
|
||||
9. If you pushed a commit, resolved an eligible review thread, or triggered a rerun, report the action briefly and continue polling (do not stop). If a human review comment needs a written GitHub response, stop and ask for confirmation before posting.
|
||||
10. After a review-fix push, proactively restart continuous monitoring (`--watch`) in the same turn unless a strict stop condition has already been reached.
|
||||
11. If everything is passing, mergeable, not blocked on required review approval, and there are no unaddressed review items, report that the PR is currently ready to merge but keep the watcher running so new review comments are surfaced quickly while the PR remains open.
|
||||
12. If blocked on a user-help-required issue (infra outage, exhausted flaky retries, unclear reviewer request, permissions), report the blocker and stop.
|
||||
|
||||
@@ -19,6 +19,8 @@ Limit discussion to the _net change_ of the commit. It is generally frowned upon
|
||||
|
||||
Avoid references to absolute paths on my local disk. When talking about a path that is within the repository, simply use the repo-relative path.
|
||||
|
||||
Avoid references to confidential information including but not limited to codenames or OpenAI-internal URLs.
|
||||
|
||||
It is generally helpful to discuss how the change was verified. That said, it is unnecessary to mention things that CI checks automatically, e.g., do not include "ran `just fmt`" as part of the test plan. Though identifying the new tests that were purposely introduced to verify the new behavior introduced by the pull request is often appropriate.
|
||||
|
||||
Make use of Markdown to format the pull request professionally. Ensure "code things" appear in single backticks when referenced inline. Fenced code blocks are useful when referencing code or showing a shell transcript. Also, make use of GitHub permalinks when citing existing pieces of code that are relevant to the change.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: pushing-ci-changes
|
||||
description: Pushing GitHub Actions changes, resolving push rejection, requesting upload exceptions.
|
||||
---
|
||||
|
||||
The Codex repository prevents anyone from uploading changes to its CI configuration unless they've
|
||||
been granted a temporary role.
|
||||
|
||||
In order to push changes to `.github/**/*.yml` and related files you'll need the user to read
|
||||
go/workflow-approvals and request an approval through that flow. It is not possible for you to
|
||||
make progress by requesting an exemption yourself.
|
||||
|
||||
If you know you're about to push changes that would fail due to these restrictions you should still
|
||||
try to push anyways to confirm the user's account doesn't already have the necessary approval.
|
||||
|
||||
If you encounter a failed push due to these restrictions, share the go/workflow-approvals link with
|
||||
the user and ask them to return control to you once the approval has propagated to GitHub.
|
||||
@@ -2,6 +2,12 @@
|
||||
/codex-rs/core/ @openai/codex-core-agent-team
|
||||
/codex-rs/ext/extension-api/ @openai/codex-core-agent-team
|
||||
/codex-rs/prompts/ @openai/codex-core-agent-team
|
||||
/codex-rs/utils/path-uri/ @openai/codex-core-agent-team
|
||||
|
||||
# Keep macOS AKV signing changes reviewed by Codex maintainers.
|
||||
/.github/actions/setup-akv-pkcs11-codesigning/ @openai/codex-core-agent-team
|
||||
/.github/scripts/macos-signing/ @openai/codex-core-agent-team
|
||||
/.github/workflows/rust-release.yml @openai/codex-core-agent-team
|
||||
|
||||
# Keep ownership changes reviewed by the same team.
|
||||
/.github/CODEOWNERS @openai/codex-core-agent-team
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
name: macos-code-sign
|
||||
description: Configure, sign, notarize, and clean up macOS code signing artifacts.
|
||||
inputs:
|
||||
target:
|
||||
description: Rust compilation target triple (e.g. aarch64-apple-darwin).
|
||||
required: true
|
||||
binaries:
|
||||
description: Space-delimited binary basenames to sign and notarize.
|
||||
default: "codex codex-responses-api-proxy"
|
||||
sign-binaries:
|
||||
description: Whether to sign and notarize the macOS binaries.
|
||||
required: false
|
||||
default: "true"
|
||||
sign-dmg:
|
||||
description: Whether to sign and notarize the macOS dmg.
|
||||
required: false
|
||||
default: "true"
|
||||
apple-certificate:
|
||||
description: Base64-encoded Apple signing certificate (P12).
|
||||
required: true
|
||||
apple-certificate-password:
|
||||
description: Password for the signing certificate.
|
||||
required: true
|
||||
apple-notarization-key-p8:
|
||||
description: Base64-encoded Apple notarization key (P8).
|
||||
required: true
|
||||
apple-notarization-key-id:
|
||||
description: Apple notarization key ID.
|
||||
required: true
|
||||
apple-notarization-issuer-id:
|
||||
description: Apple notarization issuer ID.
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Configure Apple code signing
|
||||
shell: bash
|
||||
env:
|
||||
KEYCHAIN_PASSWORD: actions
|
||||
APPLE_CERTIFICATE: ${{ inputs.apple-certificate }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ inputs.apple-certificate-password }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then
|
||||
echo "APPLE_CERTIFICATE is required for macOS signing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${APPLE_CERTIFICATE_PASSWORD:-}" ]]; then
|
||||
echo "APPLE_CERTIFICATE_PASSWORD is required for macOS signing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cert_path="${RUNNER_TEMP}/apple_signing_certificate.p12"
|
||||
echo "$APPLE_CERTIFICATE" | base64 -d > "$cert_path"
|
||||
|
||||
keychain_path="${RUNNER_TEMP}/codex-signing.keychain-db"
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
|
||||
security set-keychain-settings -lut 21600 "$keychain_path"
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
|
||||
|
||||
keychain_args=()
|
||||
cleanup_keychain() {
|
||||
if ((${#keychain_args[@]} > 0)); then
|
||||
security list-keychains -s "${keychain_args[@]}" || true
|
||||
security default-keychain -s "${keychain_args[0]}" || true
|
||||
else
|
||||
security list-keychains -s || true
|
||||
fi
|
||||
if [[ -f "$keychain_path" ]]; then
|
||||
security delete-keychain "$keychain_path" || true
|
||||
fi
|
||||
}
|
||||
|
||||
while IFS= read -r keychain; do
|
||||
[[ -n "$keychain" ]] && keychain_args+=("$keychain")
|
||||
done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g')
|
||||
|
||||
if ((${#keychain_args[@]} > 0)); then
|
||||
security list-keychains -s "$keychain_path" "${keychain_args[@]}"
|
||||
else
|
||||
security list-keychains -s "$keychain_path"
|
||||
fi
|
||||
|
||||
security default-keychain -s "$keychain_path"
|
||||
security import "$cert_path" -k "$keychain_path" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
|
||||
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$keychain_path" > /dev/null
|
||||
|
||||
codesign_hashes=()
|
||||
while IFS= read -r hash; do
|
||||
[[ -n "$hash" ]] && codesign_hashes+=("$hash")
|
||||
done < <(security find-identity -v -p codesigning "$keychain_path" \
|
||||
| sed -n 's/.*\([0-9A-F]\{40\}\).*/\1/p' \
|
||||
| sort -u)
|
||||
|
||||
if ((${#codesign_hashes[@]} == 0)); then
|
||||
echo "No signing identities found in $keychain_path"
|
||||
cleanup_keychain
|
||||
rm -f "$cert_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ((${#codesign_hashes[@]} > 1)); then
|
||||
echo "Multiple signing identities found in $keychain_path:"
|
||||
printf ' %s\n' "${codesign_hashes[@]}"
|
||||
cleanup_keychain
|
||||
rm -f "$cert_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APPLE_CODESIGN_IDENTITY="${codesign_hashes[0]}"
|
||||
|
||||
rm -f "$cert_path"
|
||||
|
||||
echo "APPLE_CODESIGN_IDENTITY=$APPLE_CODESIGN_IDENTITY" >> "$GITHUB_ENV"
|
||||
echo "APPLE_CODESIGN_KEYCHAIN=$keychain_path" >> "$GITHUB_ENV"
|
||||
echo "::add-mask::$APPLE_CODESIGN_IDENTITY"
|
||||
|
||||
- name: Sign macOS binaries
|
||||
if: ${{ inputs.sign-binaries == 'true' }}
|
||||
shell: bash
|
||||
env:
|
||||
TARGET: ${{ inputs.target }}
|
||||
BINARIES: ${{ inputs.binaries }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "${APPLE_CODESIGN_IDENTITY:-}" ]]; then
|
||||
echo "APPLE_CODESIGN_IDENTITY is required for macOS signing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
keychain_args=()
|
||||
if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then
|
||||
keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}")
|
||||
fi
|
||||
|
||||
entitlements_path="$GITHUB_ACTION_PATH/codex.entitlements.plist"
|
||||
|
||||
for binary in ${BINARIES}; do
|
||||
path="codex-rs/target/${TARGET}/release/${binary}"
|
||||
codesign --force --options runtime --timestamp --entitlements "$entitlements_path" --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$path"
|
||||
done
|
||||
|
||||
- name: Notarize macOS binaries
|
||||
if: ${{ inputs.sign-binaries == 'true' }}
|
||||
shell: bash
|
||||
env:
|
||||
TARGET: ${{ inputs.target }}
|
||||
BINARIES: ${{ inputs.binaries }}
|
||||
APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }}
|
||||
APPLE_NOTARIZATION_KEY_ID: ${{ inputs.apple-notarization-key-id }}
|
||||
APPLE_NOTARIZATION_ISSUER_ID: ${{ inputs.apple-notarization-issuer-id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
for var in APPLE_NOTARIZATION_KEY_P8 APPLE_NOTARIZATION_KEY_ID APPLE_NOTARIZATION_ISSUER_ID; do
|
||||
if [[ -z "${!var:-}" ]]; then
|
||||
echo "$var is required for notarization"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
notary_key_path="${RUNNER_TEMP}/notarytool.key.p8"
|
||||
echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$notary_key_path"
|
||||
cleanup_notary() {
|
||||
rm -f "$notary_key_path"
|
||||
}
|
||||
trap cleanup_notary EXIT
|
||||
|
||||
source "$GITHUB_ACTION_PATH/notary_helpers.sh"
|
||||
|
||||
notarize_binary() {
|
||||
local binary="$1"
|
||||
local source_path="codex-rs/target/${TARGET}/release/${binary}"
|
||||
local archive_path="${RUNNER_TEMP}/${binary}.zip"
|
||||
|
||||
if [[ ! -f "$source_path" ]]; then
|
||||
echo "Binary $source_path not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$archive_path"
|
||||
ditto -c -k --keepParent "$source_path" "$archive_path"
|
||||
|
||||
notarize_submission "$binary" "$archive_path" "$notary_key_path"
|
||||
}
|
||||
|
||||
for binary in ${BINARIES}; do
|
||||
notarize_binary "${binary}"
|
||||
done
|
||||
|
||||
- name: Sign and notarize macOS dmg
|
||||
if: ${{ inputs.sign-dmg == 'true' }}
|
||||
shell: bash
|
||||
env:
|
||||
TARGET: ${{ inputs.target }}
|
||||
APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }}
|
||||
APPLE_NOTARIZATION_KEY_ID: ${{ inputs.apple-notarization-key-id }}
|
||||
APPLE_NOTARIZATION_ISSUER_ID: ${{ inputs.apple-notarization-issuer-id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
for var in APPLE_CODESIGN_IDENTITY APPLE_NOTARIZATION_KEY_P8 APPLE_NOTARIZATION_KEY_ID APPLE_NOTARIZATION_ISSUER_ID; do
|
||||
if [[ -z "${!var:-}" ]]; then
|
||||
echo "$var is required"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
notary_key_path="${RUNNER_TEMP}/notarytool.key.p8"
|
||||
echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$notary_key_path"
|
||||
cleanup_notary() {
|
||||
rm -f "$notary_key_path"
|
||||
}
|
||||
trap cleanup_notary EXIT
|
||||
|
||||
source "$GITHUB_ACTION_PATH/notary_helpers.sh"
|
||||
|
||||
dmg_name="codex-${TARGET}.dmg"
|
||||
dmg_path="codex-rs/target/${TARGET}/release/${dmg_name}"
|
||||
|
||||
if [[ ! -f "$dmg_path" ]]; then
|
||||
echo "dmg $dmg_path not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
keychain_args=()
|
||||
if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then
|
||||
keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}")
|
||||
fi
|
||||
|
||||
codesign --force --timestamp --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$dmg_path"
|
||||
notarize_submission "$dmg_name" "$dmg_path" "$notary_key_path"
|
||||
xcrun stapler staple "$dmg_path"
|
||||
|
||||
- name: Remove signing keychain
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
env:
|
||||
APPLE_CODESIGN_KEYCHAIN: ${{ env.APPLE_CODESIGN_KEYCHAIN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" ]]; then
|
||||
keychain_args=()
|
||||
while IFS= read -r keychain; do
|
||||
[[ "$keychain" == "$APPLE_CODESIGN_KEYCHAIN" ]] && continue
|
||||
[[ -n "$keychain" ]] && keychain_args+=("$keychain")
|
||||
done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g')
|
||||
if ((${#keychain_args[@]} > 0)); then
|
||||
security list-keychains -s "${keychain_args[@]}"
|
||||
security default-keychain -s "${keychain_args[0]}"
|
||||
fi
|
||||
|
||||
if [[ -f "$APPLE_CODESIGN_KEYCHAIN" ]]; then
|
||||
security delete-keychain "$APPLE_CODESIGN_KEYCHAIN"
|
||||
fi
|
||||
fi
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
notarize_submission() {
|
||||
local label="$1"
|
||||
local path="$2"
|
||||
local notary_key_path="$3"
|
||||
|
||||
if [[ -z "${APPLE_NOTARIZATION_KEY_ID:-}" || -z "${APPLE_NOTARIZATION_ISSUER_ID:-}" ]]; then
|
||||
echo "APPLE_NOTARIZATION_KEY_ID and APPLE_NOTARIZATION_ISSUER_ID are required for notarization"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$notary_key_path" || ! -f "$notary_key_path" ]]; then
|
||||
echo "Notary key file $notary_key_path not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$path" ]]; then
|
||||
echo "Notarization payload $path not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local submission_json
|
||||
submission_json=$(xcrun notarytool submit "$path" \
|
||||
--key "$notary_key_path" \
|
||||
--key-id "$APPLE_NOTARIZATION_KEY_ID" \
|
||||
--issuer "$APPLE_NOTARIZATION_ISSUER_ID" \
|
||||
--output-format json \
|
||||
--wait)
|
||||
|
||||
local status submission_id
|
||||
status=$(printf '%s\n' "$submission_json" | jq -r '.status // "Unknown"')
|
||||
submission_id=$(printf '%s\n' "$submission_json" | jq -r '.id // ""')
|
||||
|
||||
if [[ -z "$submission_id" ]]; then
|
||||
echo "Failed to retrieve submission ID for $label"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "::notice title=Notarization::$label submission ${submission_id} completed with status ${status}"
|
||||
|
||||
if [[ "$status" != "Accepted" ]]; then
|
||||
echo "Notarization failed for ${label} (submission ${submission_id}, status ${status})"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
name: Set up AKV PKCS11 code signing
|
||||
description: Download prebuilt rcodesign and Azure Key Vault PKCS11 provider artifacts, then export macOS signing environment.
|
||||
|
||||
inputs:
|
||||
setup-mode:
|
||||
description: signing configures Azure and exports signing env vars; tools-only only downloads signing tools.
|
||||
required: false
|
||||
default: signing
|
||||
rcodesign-blob-uri:
|
||||
description: Azure Blob URI for the prebuilt Linux/amd64 rcodesign binary.
|
||||
required: true
|
||||
rcodesign-sha256:
|
||||
description: Expected SHA-256 digest for the prebuilt rcodesign binary.
|
||||
required: true
|
||||
akv-pkcs11-library-blob-uri:
|
||||
description: Azure Blob URI for the prebuilt Linux/amd64 AKV PKCS11 provider library.
|
||||
required: true
|
||||
akv-pkcs11-library-sha256:
|
||||
description: Expected SHA-256 digest for the prebuilt AKV PKCS11 provider library.
|
||||
required: true
|
||||
azure-client-id:
|
||||
description: GitHub OIDC client ID for the Azure signer application.
|
||||
required: true
|
||||
azure-tenant-id:
|
||||
description: Azure tenant ID for the signer application.
|
||||
required: true
|
||||
azure-subscription-id:
|
||||
description: Azure subscription ID that owns the signing vault.
|
||||
required: true
|
||||
key-vault-name:
|
||||
description: Azure Key Vault name containing the certificate-backed signing key.
|
||||
required: true
|
||||
key-name:
|
||||
description: Key Vault certificate/key name used as the PKCS11 key label.
|
||||
required: true
|
||||
key-version:
|
||||
description: Optional Key Vault key version to pin while signing.
|
||||
required: false
|
||||
default: ""
|
||||
certificate-sha256:
|
||||
description: Optional expected SHA-256 fingerprint for the downloaded public certificate.
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
outputs:
|
||||
pkcs11-library:
|
||||
description: Path to the downloaded AKV PKCS11 provider library.
|
||||
value: ${{ steps.paths.outputs.pkcs11_library }}
|
||||
signing-certificate-pem:
|
||||
description: Path to the downloaded public signing certificate.
|
||||
value: ${{ steps.paths.outputs.signing_certificate_pem }}
|
||||
rcodesign:
|
||||
description: Path to the downloaded rcodesign binary.
|
||||
value: ${{ steps.paths.outputs.rcodesign }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Validate pinned signing artifacts
|
||||
shell: bash
|
||||
env:
|
||||
SETUP_MODE: ${{ inputs.setup-mode }}
|
||||
RCODESIGN_BLOB_URI: ${{ inputs.rcodesign-blob-uri }}
|
||||
RCODESIGN_SHA256: ${{ inputs.rcodesign-sha256 }}
|
||||
AKV_PKCS11_LIBRARY_BLOB_URI: ${{ inputs.akv-pkcs11-library-blob-uri }}
|
||||
AKV_PKCS11_LIBRARY_SHA256: ${{ inputs.akv-pkcs11-library-sha256 }}
|
||||
KEY_VAULT_NAME: ${{ inputs.key-vault-name }}
|
||||
KEY_NAME: ${{ inputs.key-name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
case "$SETUP_MODE" in
|
||||
signing|tools-only)
|
||||
;;
|
||||
*)
|
||||
echo "setup-mode must be 'signing' or 'tools-only', got '$SETUP_MODE'." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
for variable_name in RCODESIGN_SHA256 AKV_PKCS11_LIBRARY_SHA256; do
|
||||
value="${!variable_name}"
|
||||
if [[ ! "$value" =~ ^[0-9a-f]{64}$ ]]; then
|
||||
echo "$variable_name must be a lowercase SHA-256 digest." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
for variable_name in RCODESIGN_BLOB_URI AKV_PKCS11_LIBRARY_BLOB_URI; do
|
||||
value="${!variable_name}"
|
||||
if [[ ! "$value" =~ ^az://[^/]+/[^/]+/.+ ]]; then
|
||||
echo "$variable_name must use az://<account>/<container>/<blob>." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$SETUP_MODE" == "signing" ]]; then
|
||||
for variable_name in \
|
||||
KEY_VAULT_NAME \
|
||||
KEY_NAME; do
|
||||
if [[ -z "${!variable_name}" ]]; then
|
||||
echo "$variable_name is required for AKV PKCS11 signing." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
- name: Resolve signing tool paths
|
||||
id: paths
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "${RUNNER_OS}" != "Linux" ]]; then
|
||||
echo "Prebuilt AKV PKCS11 signing tools are only vendored for Linux runners, got ${RUNNER_OS}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${RUNNER_ARCH}" != "X64" && "${RUNNER_ARCH}" != "AMD64" ]]; then
|
||||
echo "Prebuilt AKV PKCS11 signing tools are only vendored for amd64 runners, got ${RUNNER_ARCH}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
provider_root="${RUNNER_TEMP}/akv-pkcs11-provider"
|
||||
rcodesign_root="${RUNNER_TEMP}/rcodesign-root"
|
||||
signing_certificate_pem="${RUNNER_TEMP}/akv-signing-cert.pem"
|
||||
library_name="libakv_pkcs_11.so"
|
||||
|
||||
mkdir -p "$provider_root" "$rcodesign_root/bin"
|
||||
|
||||
{
|
||||
echo "pkcs11_library=$provider_root/$library_name"
|
||||
echo "pkcs11_manifest=$provider_root/akv-pkcs11-provider.manifest"
|
||||
echo "rcodesign_root=$rcodesign_root"
|
||||
echo "rcodesign=$rcodesign_root/bin/rcodesign"
|
||||
echo "signing_certificate_pem=$signing_certificate_pem"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Validate Azure credentials for private signing artifacts
|
||||
shell: bash
|
||||
env:
|
||||
AZURE_CLIENT_ID: ${{ inputs.azure-client-id }}
|
||||
AZURE_TENANT_ID: ${{ inputs.azure-tenant-id }}
|
||||
AZURE_SUBSCRIPTION_ID: ${{ inputs.azure-subscription-id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
for variable_name in AZURE_CLIENT_ID AZURE_TENANT_ID AZURE_SUBSCRIPTION_ID; do
|
||||
if [[ -z "${!variable_name}" ]]; then
|
||||
echo "$variable_name is required for private AKV PKCS11 signing artifacts." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Log in to Azure with GitHub OIDC
|
||||
uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0
|
||||
with:
|
||||
client-id: ${{ inputs.azure-client-id }}
|
||||
tenant-id: ${{ inputs.azure-tenant-id }}
|
||||
subscription-id: ${{ inputs.azure-subscription-id }}
|
||||
|
||||
- name: Install prebuilt signing tools
|
||||
shell: bash
|
||||
env:
|
||||
RCODESIGN_BLOB_URI: ${{ inputs.rcodesign-blob-uri }}
|
||||
RCODESIGN_SHA256: ${{ inputs.rcodesign-sha256 }}
|
||||
RCODESIGN: ${{ steps.paths.outputs.rcodesign }}
|
||||
AKV_PKCS11_LIBRARY_BLOB_URI: ${{ inputs.akv-pkcs11-library-blob-uri }}
|
||||
AKV_PKCS11_LIBRARY_SHA256: ${{ inputs.akv-pkcs11-library-sha256 }}
|
||||
PKCS11_LIBRARY: ${{ steps.paths.outputs.pkcs11_library }}
|
||||
PKCS11_MANIFEST: ${{ steps.paths.outputs.pkcs11_manifest }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
download_az_blob_uri() {
|
||||
local uri="$1"
|
||||
local destination="$2"
|
||||
local rest account container blob
|
||||
|
||||
rest="${uri#az://}"
|
||||
account="${rest%%/*}"
|
||||
rest="${rest#*/}"
|
||||
container="${rest%%/*}"
|
||||
blob="${rest#*/}"
|
||||
|
||||
if [[ -z "$account" || -z "$container" || -z "$blob" || "$blob" == "$rest" ]]; then
|
||||
echo "Invalid Azure Blob URI. Expected az://<account>/<container>/<blob>." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$destination")"
|
||||
rm -f "$destination"
|
||||
if ! az storage blob download \
|
||||
--account-name "$account" \
|
||||
--container-name "$container" \
|
||||
--name "$blob" \
|
||||
--file "$destination" \
|
||||
--auth-mode login \
|
||||
--only-show-errors \
|
||||
>/dev/null 2>&1; then
|
||||
echo "Failed to download a private signing artifact from Azure Blob Storage." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
verify_sha256() {
|
||||
local path="$1"
|
||||
local expected="$2"
|
||||
local actual
|
||||
|
||||
actual="$(shasum -a 256 "$path" | awk '{ print $1 }')"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
echo "SHA-256 verification failed for '$path'." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Downloading prebuilt rcodesign."
|
||||
download_az_blob_uri "$RCODESIGN_BLOB_URI" "$RCODESIGN"
|
||||
verify_sha256 "$RCODESIGN" "$RCODESIGN_SHA256"
|
||||
chmod 0755 "$RCODESIGN"
|
||||
|
||||
echo "Downloading prebuilt AKV PKCS11 provider."
|
||||
download_az_blob_uri "$AKV_PKCS11_LIBRARY_BLOB_URI" "$PKCS11_LIBRARY"
|
||||
verify_sha256 "$PKCS11_LIBRARY" "$AKV_PKCS11_LIBRARY_SHA256"
|
||||
chmod 0644 "$PKCS11_LIBRARY"
|
||||
|
||||
{
|
||||
echo "runner_os=$RUNNER_OS"
|
||||
echo "runner_arch=$RUNNER_ARCH"
|
||||
echo "library_name=$(basename "$PKCS11_LIBRARY")"
|
||||
} > "$PKCS11_MANIFEST"
|
||||
|
||||
- name: Verify downloaded signing tools
|
||||
shell: bash
|
||||
env:
|
||||
RCODESIGN: ${{ steps.paths.outputs.rcodesign }}
|
||||
RCODESIGN_SHA256: ${{ inputs.rcodesign-sha256 }}
|
||||
PKCS11_LIBRARY: ${{ steps.paths.outputs.pkcs11_library }}
|
||||
AKV_PKCS11_LIBRARY_SHA256: ${{ inputs.akv-pkcs11-library-sha256 }}
|
||||
PKCS11_MANIFEST: ${{ steps.paths.outputs.pkcs11_manifest }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
verify_sha256() {
|
||||
local path="$1"
|
||||
local expected="$2"
|
||||
local actual
|
||||
|
||||
actual="$(shasum -a 256 "$path" | awk '{ print $1 }')"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
echo "SHA-256 verification failed for '$path'." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! -x "$RCODESIGN" ]]; then
|
||||
echo "rcodesign is missing or not executable at '$RCODESIGN'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$PKCS11_LIBRARY" ]]; then
|
||||
echo "AKV PKCS11 provider library is missing at '$PKCS11_LIBRARY'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
verify_sha256 "$RCODESIGN" "$RCODESIGN_SHA256"
|
||||
verify_sha256 "$PKCS11_LIBRARY" "$AKV_PKCS11_LIBRARY_SHA256"
|
||||
|
||||
"$RCODESIGN" --version
|
||||
"$RCODESIGN" notarize --help > /dev/null
|
||||
|
||||
if [[ -f "$PKCS11_MANIFEST" ]]; then
|
||||
echo "AKV PKCS11 provider artifact manifest is present."
|
||||
else
|
||||
echo "AKV PKCS11 provider artifact manifest is absent." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Download signing certificate from Key Vault
|
||||
if: ${{ inputs.setup-mode == 'signing' }}
|
||||
shell: bash
|
||||
env:
|
||||
KEY_VAULT_NAME: ${{ inputs.key-vault-name }}
|
||||
KEY_NAME: ${{ inputs.key-name }}
|
||||
KEY_VERSION: ${{ inputs.key-version }}
|
||||
CERTIFICATE_SHA256: ${{ inputs.certificate-sha256 }}
|
||||
SIGNING_CERTIFICATE_PEM: ${{ steps.paths.outputs.signing_certificate_pem }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
certificate_version_args=()
|
||||
if [[ -n "$KEY_VERSION" ]]; then
|
||||
certificate_version_args+=(--version "$KEY_VERSION")
|
||||
fi
|
||||
|
||||
if ! az keyvault certificate download \
|
||||
--vault-name "$KEY_VAULT_NAME" \
|
||||
--name "$KEY_NAME" \
|
||||
"${certificate_version_args[@]}" \
|
||||
--file "$SIGNING_CERTIFICATE_PEM" \
|
||||
--encoding PEM \
|
||||
--only-show-errors \
|
||||
>/dev/null 2>&1; then
|
||||
echo "Failed to download the public signing certificate from Azure Key Vault." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$CERTIFICATE_SHA256" ]]; then
|
||||
actual_sha256="$(
|
||||
openssl x509 -in "$SIGNING_CERTIFICATE_PEM" -noout -fingerprint -sha256 |
|
||||
awk -F= '{ print toupper($2) }' |
|
||||
tr -d ':\r\n'
|
||||
)"
|
||||
expected_sha256="$(printf '%s' "$CERTIFICATE_SHA256" | tr '[:lower:]' '[:upper:]' | tr -d ':\r\n ')"
|
||||
if [[ "$actual_sha256" != "$expected_sha256" ]]; then
|
||||
echo "Downloaded signing certificate SHA-256 did not match the expected fingerprint." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Export AKV PKCS11 signing environment
|
||||
if: ${{ inputs.setup-mode == 'signing' }}
|
||||
shell: bash
|
||||
env:
|
||||
RCODESIGN_ROOT: ${{ steps.paths.outputs.rcodesign_root }}
|
||||
PKCS11_LIBRARY: ${{ steps.paths.outputs.pkcs11_library }}
|
||||
SIGNING_CERTIFICATE_PEM: ${{ steps.paths.outputs.signing_certificate_pem }}
|
||||
KEY_VAULT_NAME: ${{ inputs.key-vault-name }}
|
||||
KEY_NAME: ${{ inputs.key-name }}
|
||||
KEY_VERSION: ${{ inputs.key-version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
{
|
||||
echo "$RCODESIGN_ROOT/bin"
|
||||
} >> "$GITHUB_PATH"
|
||||
|
||||
{
|
||||
echo "OAI_CODESIGN_BACKEND=akv-pkcs11"
|
||||
echo "OAI_AKV_PKCS11_LIBRARY=$PKCS11_LIBRARY"
|
||||
echo "OAI_AKV_SIGNING_CERTIFICATE_PEM=$SIGNING_CERTIFICATE_PEM"
|
||||
echo "OAI_AKV_KEY_LABEL=$KEY_NAME"
|
||||
echo "AZURE_CREDENTIAL_KIND=azurecli"
|
||||
echo "AZURE_KEYVAULT_NAME=$KEY_VAULT_NAME"
|
||||
if [[ -n "$KEY_VERSION" ]]; then
|
||||
echo "AZURE_KEYVAULT_KEY_VERSION=$KEY_VERSION"
|
||||
fi
|
||||
} >> "$GITHUB_ENV"
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: archive-release-symbols-and-strip-binaries.sh \
|
||||
--target <rust-target> \
|
||||
--artifact-name <artifact-name> \
|
||||
--release-dir <dir> \
|
||||
--archive-dir <dir> \
|
||||
--binaries "<space-delimited binary basenames>"
|
||||
EOF
|
||||
}
|
||||
|
||||
target=""
|
||||
artifact_name=""
|
||||
release_dir=""
|
||||
archive_dir=""
|
||||
binaries=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--target)
|
||||
target="${2:?--target requires a value}"
|
||||
shift 2
|
||||
;;
|
||||
--artifact-name)
|
||||
artifact_name="${2:?--artifact-name requires a value}"
|
||||
shift 2
|
||||
;;
|
||||
--release-dir)
|
||||
release_dir="${2:?--release-dir requires a value}"
|
||||
shift 2
|
||||
;;
|
||||
--archive-dir)
|
||||
archive_dir="${2:?--archive-dir requires a value}"
|
||||
shift 2
|
||||
;;
|
||||
--binaries)
|
||||
binaries="${2:?--binaries requires a value}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unexpected argument: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$target" || -z "$artifact_name" || -z "$release_dir" || -z "$archive_dir" || -z "$binaries" ]]; then
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
symbols_root="${RUNNER_TEMP:-/tmp}/codex-symbols-${artifact_name}"
|
||||
symbols_dir="${symbols_root}/codex-symbols-${artifact_name}"
|
||||
archive_path="${archive_dir%/}/codex-symbols-${artifact_name}.tar.gz"
|
||||
rm -rf "$symbols_root"
|
||||
mkdir -p "$symbols_dir" "$archive_dir"
|
||||
read -r -a binary_names <<< "$binaries"
|
||||
|
||||
case "$target" in
|
||||
*apple-darwin)
|
||||
for binary in "${binary_names[@]}"; do
|
||||
binary_path="${release_dir%/}/${binary}"
|
||||
dsym_path="${binary_path}.dSYM"
|
||||
if [[ ! -f "$binary_path" ]]; then
|
||||
echo "Binary $binary_path not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -d "$dsym_path" ]]; then
|
||||
echo "dSYM $dsym_path not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp -RL "$dsym_path" "${symbols_dir}/${binary}.dSYM"
|
||||
strip -S -x "$binary_path"
|
||||
done
|
||||
;;
|
||||
*linux*)
|
||||
objcopy_bin="${OBJCOPY:-objcopy}"
|
||||
strip_bin="${STRIP:-strip}"
|
||||
for binary in "${binary_names[@]}"; do
|
||||
binary_path="${release_dir%/}/${binary}"
|
||||
debug_path="${symbols_dir}/${binary}.debug"
|
||||
if [[ ! -f "$binary_path" ]]; then
|
||||
echo "Binary $binary_path not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"$objcopy_bin" --only-keep-debug "$binary_path" "$debug_path"
|
||||
"$strip_bin" --strip-debug --strip-unneeded "$binary_path"
|
||||
"$objcopy_bin" --add-gnu-debuglink="$debug_path" "$binary_path"
|
||||
done
|
||||
;;
|
||||
*windows*)
|
||||
for binary in "${binary_names[@]}"; do
|
||||
pdb_path="${release_dir%/}/${binary}.pdb"
|
||||
if [[ ! -f "$pdb_path" ]]; then
|
||||
echo "PDB $pdb_path not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$pdb_path" "${symbols_dir}/${binary}.pdb"
|
||||
done
|
||||
;;
|
||||
*)
|
||||
echo "No symbols packaging support for target: $target" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -f "$archive_path"
|
||||
tar -C "$symbols_root" -czf "$archive_path" "codex-symbols-${artifact_name}"
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Submits a signed standalone macOS binary to Apple notarization through
|
||||
# rcodesign. Standalone binaries cannot carry a stapled ticket, so the binary
|
||||
# is submitted in a ZIP and the successful notarization log is retained.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
Usage: notarize_macos_binary_with_rcodesign.sh --binary PATH [--report-dir PATH] [--max-wait-seconds SECONDS]
|
||||
|
||||
Options:
|
||||
--binary PATH Signed standalone macOS binary to notarize.
|
||||
--report-dir PATH Directory for notarization logs.
|
||||
--max-wait-seconds SECONDS Maximum rcodesign notarization wait time.
|
||||
EOF
|
||||
}
|
||||
|
||||
binary_path=""
|
||||
report_dir="${RUNNER_TEMP:-/tmp}/macos-binary-notarization-verification"
|
||||
max_wait_seconds="600"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--binary)
|
||||
binary_path="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--report-dir)
|
||||
report_dir="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--max-wait-seconds)
|
||||
max_wait_seconds="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown notarization argument: $1" >&2
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$binary_path" ]]; then
|
||||
echo "--binary is required." >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$binary_path" ]]; then
|
||||
echo "Binary does not exist: $binary_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$max_wait_seconds" =~ ^[0-9]+$ ]]; then
|
||||
echo "--max-wait-seconds must be a non-negative integer." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
for command_name in rcodesign zip; do
|
||||
if ! command -v "$command_name" >/dev/null 2>&1; then
|
||||
echo "$command_name was not found on PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
missing_environment=0
|
||||
for variable_name in \
|
||||
APPLE_NOTARIZATION_ISSUER_ID \
|
||||
APPLE_NOTARIZATION_KEY_ID \
|
||||
APPLE_NOTARIZATION_KEY_P8
|
||||
do
|
||||
if [[ -z "${!variable_name:-}" ]]; then
|
||||
echo "$variable_name must be set from CI secrets before notarizing a binary." >&2
|
||||
missing_environment=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$missing_environment" -ne 0 ]]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$report_dir"
|
||||
|
||||
notarization_temp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$notarization_temp_dir" >/dev/null' EXIT
|
||||
|
||||
private_key_path="$notarization_temp_dir/AuthKey_${APPLE_NOTARIZATION_KEY_ID}.p8"
|
||||
if ! printf '%s' "$APPLE_NOTARIZATION_KEY_P8" | base64 --decode >"$private_key_path" 2>/dev/null; then
|
||||
if ! printf '%s' "$APPLE_NOTARIZATION_KEY_P8" | base64 -D >"$private_key_path" 2>/dev/null; then
|
||||
echo "APPLE_NOTARIZATION_KEY_P8 must be a base64-encoded .p8 private key." >&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
chmod 600 "$private_key_path"
|
||||
|
||||
api_key_path="$notarization_temp_dir/app-store-connect-api-key.json"
|
||||
rcodesign encode-app-store-connect-api-key \
|
||||
--output-path "$api_key_path" \
|
||||
"$APPLE_NOTARIZATION_ISSUER_ID" \
|
||||
"$APPLE_NOTARIZATION_KEY_ID" \
|
||||
"$private_key_path" \
|
||||
>"$report_dir/encode-app-store-connect-api-key.log" 2>&1
|
||||
|
||||
binary_name="$(basename "$binary_path")"
|
||||
archive_path="$notarization_temp_dir/${binary_name}.zip"
|
||||
(
|
||||
cd "$(dirname "$binary_path")"
|
||||
zip -q "$archive_path" "$binary_name"
|
||||
)
|
||||
|
||||
notarization_log="$report_dir/${binary_name}-notarization.log"
|
||||
rcodesign notarize \
|
||||
--api-key-file "$api_key_path" \
|
||||
--max-wait-seconds "$max_wait_seconds" \
|
||||
--wait \
|
||||
"$archive_path" \
|
||||
2>&1 | tee "$notarization_log"
|
||||
|
||||
{
|
||||
echo "binary_name=$binary_name"
|
||||
echo "max_wait_seconds=$max_wait_seconds"
|
||||
echo "binary_sha256=$(shasum -a 256 "$binary_path" | awk '{ print $1 }')"
|
||||
echo "rcodesign_notarize=completed"
|
||||
} >"$report_dir/${binary_name}-notarization-summary.txt"
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Notarizes and staples a signed macOS DMG through rcodesign.
|
||||
#
|
||||
# This is the Linux-compatible notarization path for the AKV/PKCS#11 signing
|
||||
# flow. It records notarization inputs and logs so workflow artifacts can be
|
||||
# audited without exposing the App Store Connect private key.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
Usage: notarize_macos_dmg_with_rcodesign.sh --dmg PATH [--report-dir PATH] [--max-wait-seconds SECONDS]
|
||||
|
||||
Options:
|
||||
--dmg PATH Signed DMG to submit to Apple notarization.
|
||||
--report-dir PATH Directory for notarization logs.
|
||||
--max-wait-seconds SECONDS Maximum rcodesign notarization wait time.
|
||||
EOF
|
||||
}
|
||||
|
||||
dmg_path=""
|
||||
report_dir="${RUNNER_TEMP:-/tmp}/macos-notarization-verification"
|
||||
max_wait_seconds="600"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dmg)
|
||||
dmg_path="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--report-dir)
|
||||
report_dir="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--max-wait-seconds)
|
||||
max_wait_seconds="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown notarization argument: $1" >&2
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$dmg_path" ]]; then
|
||||
echo "--dmg is required." >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$dmg_path" ]]; then
|
||||
echo "DMG does not exist: $dmg_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$max_wait_seconds" =~ ^[0-9]+$ ]]; then
|
||||
echo "--max-wait-seconds must be a non-negative integer." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! command -v rcodesign > /dev/null 2>&1; then
|
||||
echo "rcodesign was not found on PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
missing_environment=0
|
||||
for variable_name in \
|
||||
APPLE_NOTARIZATION_ISSUER_ID \
|
||||
APPLE_NOTARIZATION_KEY_ID \
|
||||
APPLE_NOTARIZATION_KEY_P8
|
||||
do
|
||||
if [[ -z "${!variable_name:-}" ]]; then
|
||||
echo "$variable_name must be set from CI secrets before notarizing a DMG." >&2
|
||||
missing_environment=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$missing_environment" -ne 0 ]]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$report_dir"
|
||||
|
||||
notarization_temp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$notarization_temp_dir" > /dev/null' EXIT
|
||||
|
||||
private_key_path="$notarization_temp_dir/AuthKey_${APPLE_NOTARIZATION_KEY_ID}.p8"
|
||||
if ! printf '%s' "$APPLE_NOTARIZATION_KEY_P8" | base64 --decode > "$private_key_path" 2> /dev/null; then
|
||||
if ! printf '%s' "$APPLE_NOTARIZATION_KEY_P8" | base64 -D > "$private_key_path" 2> /dev/null; then
|
||||
echo "APPLE_NOTARIZATION_KEY_P8 must be a base64-encoded .p8 private key." >&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
chmod 600 "$private_key_path"
|
||||
|
||||
api_key_path="$notarization_temp_dir/app-store-connect-api-key.json"
|
||||
rcodesign encode-app-store-connect-api-key \
|
||||
--output-path "$api_key_path" \
|
||||
"$APPLE_NOTARIZATION_ISSUER_ID" \
|
||||
"$APPLE_NOTARIZATION_KEY_ID" \
|
||||
"$private_key_path" \
|
||||
> "$report_dir/encode-app-store-connect-api-key.log" 2>&1
|
||||
|
||||
notarization_log="$report_dir/dmg-notarization.log"
|
||||
rcodesign notarize \
|
||||
--api-key-file "$api_key_path" \
|
||||
--max-wait-seconds "$max_wait_seconds" \
|
||||
--staple \
|
||||
"$dmg_path" \
|
||||
2>&1 | tee "$notarization_log"
|
||||
|
||||
{
|
||||
echo "dmg_path=$dmg_path"
|
||||
echo "max_wait_seconds=$max_wait_seconds"
|
||||
echo "dmg_sha256=$(shasum -a 256 "$dmg_path" | awk '{ print $1 }')"
|
||||
echo "rcodesign_notarize_staple=completed"
|
||||
} > "$report_dir/dmg-notarization-summary.txt"
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Small compatibility wrapper around native codesign and rcodesign.
|
||||
#
|
||||
# Existing packaging scripts call this instead of choosing a signing backend
|
||||
# directly. OAI_CODESIGN_BACKEND=akv-pkcs11 routes signing through rcodesign
|
||||
# while preserving the option, entitlement, identifier, timestamp, and deep
|
||||
# signing surface used by the native codesign path.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
Usage: sign_macos_code.sh --target PATH --identity IDENTITY [options]
|
||||
|
||||
Options:
|
||||
--deep true|false
|
||||
--entitlements PATH
|
||||
--identifier IDENTIFIER
|
||||
--identity IDENTITY
|
||||
--options FLAGS
|
||||
--target PATH
|
||||
--timestamp true|false|none
|
||||
EOF
|
||||
}
|
||||
|
||||
target=""
|
||||
identity=""
|
||||
options=""
|
||||
entitlements_file=""
|
||||
identifier=""
|
||||
deep="false"
|
||||
timestamp="true"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--deep)
|
||||
deep="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--entitlements)
|
||||
entitlements_file="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--identifier)
|
||||
identifier="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--identity)
|
||||
identity="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--options)
|
||||
options="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--target)
|
||||
target="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--timestamp)
|
||||
timestamp="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown signing argument: $1" >&2
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$target" ]]; then
|
||||
echo "--target is required." >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -e "$target" ]]; then
|
||||
echo "Signing target does not exist: $target" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$deep" in
|
||||
true|false) ;;
|
||||
*)
|
||||
echo "--deep must be true or false, got '$deep'." >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$timestamp" in
|
||||
true|false|none) ;;
|
||||
*)
|
||||
echo "--timestamp must be true, false, or none, got '$timestamp'." >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
sign_with_codesign() {
|
||||
if [[ -z "$identity" ]]; then
|
||||
echo "Native codesign requires --identity." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
local -a args
|
||||
args=(--force)
|
||||
|
||||
if [[ "$deep" == "true" ]]; then
|
||||
args+=(--deep)
|
||||
fi
|
||||
|
||||
if [[ -n "$options" ]]; then
|
||||
args+=(--options "$options")
|
||||
fi
|
||||
|
||||
case "$timestamp" in
|
||||
true)
|
||||
args+=(--timestamp)
|
||||
;;
|
||||
false|none)
|
||||
args+=(--timestamp=none)
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n "$entitlements_file" ]]; then
|
||||
args+=(--entitlements "$entitlements_file")
|
||||
fi
|
||||
|
||||
if [[ -n "$identifier" ]]; then
|
||||
args+=(--identifier "$identifier")
|
||||
fi
|
||||
|
||||
args+=(--sign "$identity" "$target")
|
||||
codesign "${args[@]}"
|
||||
}
|
||||
|
||||
append_rcodesign_flags() {
|
||||
local raw_options="$1"
|
||||
local option=""
|
||||
|
||||
if [[ -z "$raw_options" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
IFS=',' read -ra split_options <<< "$raw_options"
|
||||
for option in "${split_options[@]}"; do
|
||||
option="${option//[[:space:]]/}"
|
||||
[[ -z "$option" ]] && continue
|
||||
|
||||
case "$option" in
|
||||
host|hard|kill|expires|restrict|library|runtime|linker-signed)
|
||||
rcodesign_args+=(--code-signature-flags "$option")
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported rcodesign code signature option: $option" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
rcodesign_options_require_notarization() {
|
||||
local raw_options="$1"
|
||||
local option=""
|
||||
|
||||
if [[ -z "$raw_options" || "$timestamp" != "true" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
IFS=',' read -ra split_options <<< "$raw_options"
|
||||
for option in "${split_options[@]}"; do
|
||||
option="${option//[[:space:]]/}"
|
||||
if [[ "$option" == "runtime" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
sign_with_rcodesign() {
|
||||
: "${OAI_AKV_PKCS11_LIBRARY:?OAI_AKV_PKCS11_LIBRARY is required for AKV PKCS11 signing.}"
|
||||
: "${OAI_AKV_SIGNING_CERTIFICATE_PEM:?OAI_AKV_SIGNING_CERTIFICATE_PEM is required for AKV PKCS11 signing.}"
|
||||
: "${OAI_AKV_KEY_LABEL:?OAI_AKV_KEY_LABEL is required for AKV PKCS11 signing.}"
|
||||
|
||||
if ! command -v rcodesign >/dev/null 2>&1; then
|
||||
echo "rcodesign was not found on PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local -a rcodesign_args
|
||||
rcodesign_args=(
|
||||
sign
|
||||
--config-file /dev/null
|
||||
--pkcs11-library "$OAI_AKV_PKCS11_LIBRARY"
|
||||
--pkcs11-certificate-file "$OAI_AKV_SIGNING_CERTIFICATE_PEM"
|
||||
--pkcs11-key-label "$OAI_AKV_KEY_LABEL"
|
||||
)
|
||||
|
||||
if [[ "$deep" == "false" ]]; then
|
||||
rcodesign_args+=(--shallow)
|
||||
fi
|
||||
|
||||
case "$timestamp" in
|
||||
true)
|
||||
;;
|
||||
false|none)
|
||||
rcodesign_args+=(--timestamp-url none)
|
||||
;;
|
||||
esac
|
||||
|
||||
append_rcodesign_flags "$options"
|
||||
if rcodesign_options_require_notarization "$options"; then
|
||||
rcodesign_args+=(--for-notarization)
|
||||
fi
|
||||
|
||||
if [[ -n "$entitlements_file" ]]; then
|
||||
rcodesign_args+=(--entitlements-xml-file "$entitlements_file")
|
||||
fi
|
||||
|
||||
if [[ -n "$identifier" ]]; then
|
||||
rcodesign_args+=(--binary-identifier "$identifier")
|
||||
fi
|
||||
|
||||
rcodesign_args+=("$target")
|
||||
rcodesign "${rcodesign_args[@]}"
|
||||
}
|
||||
|
||||
case "${OAI_CODESIGN_BACKEND:-codesign}" in
|
||||
codesign|"")
|
||||
sign_with_codesign
|
||||
;;
|
||||
akv-pkcs11)
|
||||
sign_with_rcodesign
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported OAI_CODESIGN_BACKEND: ${OAI_CODESIGN_BACKEND}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -4,7 +4,8 @@ set -euo pipefail
|
||||
|
||||
# Run target-discovery queries with the same startup settings as the main
|
||||
# build/test invocation so they can reuse the same Bazel server. Queries only
|
||||
# enumerate labels, so they intentionally do not select CI or remote configs.
|
||||
# enumerate labels, so they intentionally do not select a CI build/test config
|
||||
# or remote execution.
|
||||
|
||||
if [[ $# -lt 2 || "${@: -2:1}" != "--" ]]; then
|
||||
echo "Usage: $0 [<bazel query args>...] -- <query expression>" >&2
|
||||
@@ -14,21 +15,16 @@ fi
|
||||
query_args=("${@:1:$#-2}")
|
||||
query_expression="${@: -1}"
|
||||
|
||||
bazel_startup_args=()
|
||||
if [[ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]]; then
|
||||
bazel_startup_args+=("--output_user_root=${BAZEL_OUTPUT_USER_ROOT}")
|
||||
fi
|
||||
|
||||
run_bazel() {
|
||||
if [[ "${RUNNER_OS:-}" == "Windows" ]]; then
|
||||
MSYS2_ARG_CONV_EXCL='*' bazel "$@"
|
||||
MSYS2_ARG_CONV_EXCL='*' "$(dirname "${BASH_SOURCE[0]}")/run_bazel_with_buildbuddy.py" "$@"
|
||||
return
|
||||
fi
|
||||
|
||||
bazel "$@"
|
||||
"$(dirname "${BASH_SOURCE[0]}")/run_bazel_with_buildbuddy.py" "$@"
|
||||
}
|
||||
|
||||
bazel_query_args=(--noexperimental_remote_repo_contents_cache query)
|
||||
bazel_query_args=(query)
|
||||
|
||||
if [[ -n "${BAZEL_REPO_CONTENTS_CACHE:-}" ]]; then
|
||||
bazel_query_args+=("--repo_contents_cache=${BAZEL_REPO_CONTENTS_CACHE}")
|
||||
@@ -43,8 +39,4 @@ if (( ${#query_args[@]} > 0 )); then
|
||||
fi
|
||||
bazel_query_args+=("$query_expression")
|
||||
|
||||
if (( ${#bazel_startup_args[@]} > 0 )); then
|
||||
run_bazel "${bazel_startup_args[@]}" "${bazel_query_args[@]}"
|
||||
else
|
||||
run_bazel "${bazel_query_args[@]}"
|
||||
fi
|
||||
run_bazel "${bazel_query_args[@]}"
|
||||
|
||||
@@ -22,6 +22,47 @@ REMOTE_EXECUTION_CONFIGS = {
|
||||
"--config=ci-v8",
|
||||
"--config=ci-windows-cross",
|
||||
}
|
||||
# Honor either explicit setting so the wrapper never overrides the caller's
|
||||
# choice when it supplies the CI default below.
|
||||
REMOTE_REPO_CONTENTS_CACHE_STARTUP_OPTIONS = {
|
||||
"--experimental_remote_repo_contents_cache",
|
||||
"--noexperimental_remote_repo_contents_cache",
|
||||
}
|
||||
|
||||
|
||||
def startup_args(args: Sequence[str], env: Mapping[str, str]) -> list[str]:
|
||||
"""Return shared startup options that are missing from a Bazel invocation.
|
||||
|
||||
Bazel startup options must precede the command, and changing them restarts
|
||||
the server and discards its analysis cache. GitHub Actions invokes Bazel
|
||||
through several helpers, so normalize their startup options here while
|
||||
preserving any explicit choice made by the caller.
|
||||
"""
|
||||
command_idx = next(
|
||||
(idx for idx, arg in enumerate(args) if not arg.startswith("-")),
|
||||
len(args),
|
||||
)
|
||||
configured_startup_args = args[:command_idx]
|
||||
injected_args = []
|
||||
|
||||
output_user_root = env.get("BAZEL_OUTPUT_USER_ROOT")
|
||||
if output_user_root and not any(
|
||||
arg.startswith("--output_user_root=") for arg in configured_startup_args
|
||||
):
|
||||
injected_args.append(f"--output_user_root={output_user_root}")
|
||||
|
||||
if env.get("GITHUB_ACTIONS") == "true" and not any(
|
||||
arg in REMOTE_REPO_CONTENTS_CACHE_STARTUP_OPTIONS
|
||||
for arg in configured_startup_args
|
||||
):
|
||||
# Work around Bazel 9 overlay materialization failures seen in CI. This
|
||||
# disables only the startup-level repo contents cache; keyed runs still
|
||||
# use BuildBuddy.
|
||||
injected_args.append("--noexperimental_remote_repo_contents_cache")
|
||||
|
||||
return injected_args
|
||||
|
||||
|
||||
# Only authenticated workflow runs executing trusted upstream code may use the
|
||||
# OpenAI BuildBuddy host. A pull request event without proof that its head is
|
||||
# in the upstream repository fails closed to the generic host.
|
||||
@@ -114,7 +155,7 @@ def bazel_args_with_remote_config(
|
||||
def bazel_command(*args: str, env: Mapping[str, str] | None = None) -> list[str]:
|
||||
env = os.environ if env is None else env
|
||||
bazel = env.get("CODEX_BAZEL_BIN", "bazel")
|
||||
return [bazel, *bazel_args_with_remote_config(args, env)]
|
||||
return [bazel, *startup_args(args, env), *bazel_args_with_remote_config(args, env)]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
@@ -182,6 +182,38 @@ class RunBazelWithBuildBuddyTest(unittest.TestCase):
|
||||
["fake-bazel", "info", "execution_root"],
|
||||
)
|
||||
|
||||
def test_bazel_command_normalizes_github_actions_startup_options(self) -> None:
|
||||
env = {
|
||||
"BAZEL_OUTPUT_USER_ROOT": "/tmp/bazel-output",
|
||||
"GITHUB_ACTIONS": "true",
|
||||
}
|
||||
|
||||
self.assertEqual(
|
||||
run_bazel_with_buildbuddy.bazel_command("build", "//codex-rs/...", env=env),
|
||||
[
|
||||
"bazel",
|
||||
"--output_user_root=/tmp/bazel-output",
|
||||
"--noexperimental_remote_repo_contents_cache",
|
||||
"build",
|
||||
"//codex-rs/...",
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
run_bazel_with_buildbuddy.bazel_command(
|
||||
"--experimental_remote_repo_contents_cache",
|
||||
"build",
|
||||
"//codex-rs/...",
|
||||
env=env,
|
||||
),
|
||||
[
|
||||
"bazel",
|
||||
"--output_user_root=/tmp/bazel-output",
|
||||
"--experimental_remote_repo_contents_cache",
|
||||
"build",
|
||||
"//codex-rs/...",
|
||||
],
|
||||
)
|
||||
|
||||
def test_main_preserves_spaced_argument_and_child_exit_status(self) -> None:
|
||||
spaced_arg = (
|
||||
r"--test_env=PATH=C:\Program Files\PowerShell\7;C:\Program Files\Git\bin"
|
||||
@@ -191,7 +223,9 @@ class RunBazelWithBuildBuddyTest(unittest.TestCase):
|
||||
)
|
||||
env = os.environ.copy()
|
||||
env["CODEX_BAZEL_BIN"] = sys.executable
|
||||
env.pop("BAZEL_OUTPUT_USER_ROOT", None)
|
||||
env.pop("BUILDBUDDY_API_KEY", None)
|
||||
env.pop("GITHUB_ACTIONS", None)
|
||||
|
||||
result = subprocess.run(
|
||||
[
|
||||
|
||||
+36
-21
@@ -20,8 +20,9 @@ jobs:
|
||||
test:
|
||||
# PRs use the sharded Windows cross-compiled test jobs below. Post-merge
|
||||
# pushes to main also run the native Windows test job for broader Windows
|
||||
# signal without putting PR latency back on the critical path. Cargo CI
|
||||
# owns V8/code-mode test coverage for now.
|
||||
# signal without putting PR latency back on the critical path. When
|
||||
# authenticated RBE is available, the Windows-cross shards exercise the
|
||||
# source-built V8/code-mode targets.
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -49,6 +50,9 @@ jobs:
|
||||
|
||||
# Configure a human readable name for each job
|
||||
name: Bazel test on ${{ matrix.os }} for ${{ matrix.target }}
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -91,9 +95,9 @@ jobs:
|
||||
# path. V8 consumers under `//codex-rs/...` still participate
|
||||
# transitively through `//...`.
|
||||
-//third_party/v8:all
|
||||
# V8-backed code-mode tests are covered by Cargo CI. Bazel CI
|
||||
# cross-compiles in several legs, and those tests are not stable in
|
||||
# that setup yet.
|
||||
# Keep V8-backed code-mode tests out of the ordinary macOS/Linux
|
||||
# legs; authenticated Windows-cross shards below exercise the
|
||||
# source-built gnullvm V8 path.
|
||||
-//codex-rs/code-mode:code-mode-unit-tests
|
||||
-//codex-rs/v8-poc:v8-poc-unit-tests
|
||||
)
|
||||
@@ -135,9 +139,9 @@ jobs:
|
||||
key: ${{ steps.prepare_bazel.outputs.repository-cache-key }}
|
||||
|
||||
test-windows-shard:
|
||||
# Split the Windows Bazel test leg across separate Windows
|
||||
# hosts. Each shard still uses Linux RBE for build actions, but the test
|
||||
# execution itself happens on its own Windows runner.
|
||||
# Split the Windows Bazel test leg across separate Windows hosts. Jobs with
|
||||
# BuildBuddy credentials use Linux RBE for build actions; test execution
|
||||
# remains on a Windows runner.
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -148,9 +152,12 @@ jobs:
|
||||
- 3
|
||||
- 4
|
||||
runs-on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
name: Bazel test on windows-latest for x86_64-pc-windows-gnullvm shard ${{ matrix.shard }}/4
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -183,9 +190,9 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
bazel_test_query='tests(//...) except tests(//third_party/v8:all) except //codex-rs/code-mode:code-mode-unit-tests except //codex-rs/v8-poc:v8-poc-unit-tests except attr(tags, "manual", tests(//...))'
|
||||
bazel_test_query='tests(//...) except tests(//third_party/v8:all) except attr(tags, "manual", tests(//...))'
|
||||
mapfile -t bazel_targets < <(
|
||||
MSYS2_ARG_CONV_EXCL='*' bazel query --output=label "${bazel_test_query}" \
|
||||
./.github/scripts/run-bazel-query-ci.sh --output=label -- "${bazel_test_query}" \
|
||||
| LC_ALL=C sort
|
||||
)
|
||||
|
||||
@@ -260,9 +267,12 @@ jobs:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
timeout-minutes: 40
|
||||
runs-on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
name: Bazel test on windows-latest for x86_64-pc-windows-gnullvm (native main)
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -289,9 +299,8 @@ jobs:
|
||||
# path. V8 consumers under `//codex-rs/...` still participate
|
||||
# transitively through `//...`.
|
||||
-//third_party/v8:all
|
||||
# Keep this aligned with the main Bazel job. The native Windows
|
||||
# job preserves broad post-merge coverage, but code-mode/V8 tests
|
||||
# are covered by Cargo CI rather than Bazel for now.
|
||||
# Keep this job broad and cheap; authenticated Windows-cross jobs
|
||||
# add source-built V8-backed code-mode coverage.
|
||||
-//codex-rs/code-mode:code-mode-unit-tests
|
||||
-//codex-rs/v8-poc:v8-poc-unit-tests
|
||||
)
|
||||
@@ -348,10 +357,13 @@ jobs:
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-gnullvm
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
runs-on: ${{ matrix.runs_on || matrix.os }}
|
||||
name: Bazel clippy on ${{ matrix.os }} for ${{ matrix.target }}
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -441,10 +453,13 @@ jobs:
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-gnullvm
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
runs-on: ${{ matrix.runs_on || matrix.os }}
|
||||
name: Verify release build on ${{ matrix.os }} for ${{ matrix.target }}
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
name: python-runtime-build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
runtime_version:
|
||||
description: "Runtime version to build, for example 0.136.0 or 0.136.0a2."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build-python-runtime:
|
||||
if: github.repository == 'openai/codex'
|
||||
name: build-python-runtime
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Validate and resolve Python runtime release
|
||||
id: python_runtime
|
||||
shell: bash
|
||||
env:
|
||||
REQUESTED_RUNTIME_VERSION: ${{ inputs.runtime_version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
python_version = os.environ["REQUESTED_RUNTIME_VERSION"]
|
||||
if match := re.fullmatch(r"([0-9]+\.[0-9]+\.[0-9]+)a([0-9]+)", python_version):
|
||||
release_version = f"{match.group(1)}-alpha.{match.group(2)}"
|
||||
elif re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", python_version):
|
||||
release_version = python_version
|
||||
else:
|
||||
raise SystemExit(
|
||||
"Python runtime version must be stable or a numbered alpha, "
|
||||
f"for example 0.136.0 or 0.136.0a2; found {python_version}"
|
||||
)
|
||||
|
||||
with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
|
||||
print(f"python_version={python_version}", file=output)
|
||||
print(f"release_tag=rust-v{release_version}", file=output)
|
||||
PY
|
||||
|
||||
- name: Download Python runtime release artifacts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PYTHON_RUNTIME_VERSION: ${{ steps.python_runtime.outputs.python_version }}
|
||||
RELEASE_TAG: ${{ steps.python_runtime.outputs.release_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p dist/python-runtime dist/python-runtime-packages
|
||||
gh release download "$RELEASE_TAG" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--pattern "openai_codex_cli_bin-${PYTHON_RUNTIME_VERSION}-*.whl" \
|
||||
--dir dist/python-runtime
|
||||
gh release download "$RELEASE_TAG" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--pattern "codex-package-*-unknown-linux-musl.tar.gz" \
|
||||
--dir dist/python-runtime-packages
|
||||
|
||||
shopt -s nullglob
|
||||
wheels=(dist/python-runtime/*.whl)
|
||||
if [[ "${#wheels[@]}" -ne 6 ]]; then
|
||||
echo "Expected 6 Python runtime wheels for ${PYTHON_RUNTIME_VERSION}, found ${#wheels[@]}."
|
||||
exit 1
|
||||
fi
|
||||
packages=(dist/python-runtime-packages/*.tar.gz)
|
||||
if [[ "${#packages[@]}" -ne 2 ]]; then
|
||||
echo "Expected 2 Linux package archives for ${PYTHON_RUNTIME_VERSION}, found ${#packages[@]}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build musllinux Python runtime wheels
|
||||
env:
|
||||
RELEASE_TAG: ${{ steps.python_runtime.outputs.release_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
python3 -m venv "${RUNNER_TEMP}/python-runtime-build-venv"
|
||||
"${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m pip install build
|
||||
|
||||
while read -r target platform_tag; do
|
||||
stage_dir="${RUNNER_TEMP}/openai-codex-cli-bin-${target}-${platform_tag}"
|
||||
python3 sdk/python/scripts/update_sdk_artifacts.py \
|
||||
stage-runtime \
|
||||
"$stage_dir" \
|
||||
"dist/python-runtime-packages/codex-package-${target}.tar.gz" \
|
||||
--codex-version "$RELEASE_TAG" \
|
||||
--platform-tag "$platform_tag"
|
||||
"${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m build \
|
||||
--wheel \
|
||||
--outdir dist/python-runtime \
|
||||
"$stage_dir"
|
||||
done <<'EOF'
|
||||
aarch64-unknown-linux-musl musllinux_1_1_aarch64
|
||||
x86_64-unknown-linux-musl musllinux_1_1_x86_64
|
||||
EOF
|
||||
|
||||
shopt -s nullglob
|
||||
wheels=(dist/python-runtime/*.whl)
|
||||
if [[ "${#wheels[@]}" -ne 8 ]]; then
|
||||
echo "Expected 8 Python runtime wheels, found ${#wheels[@]}."
|
||||
exit 1
|
||||
fi
|
||||
ls -lh dist/python-runtime
|
||||
|
||||
- name: Upload Python runtime wheels
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: python-runtime-wheels
|
||||
path: dist/python-runtime/*
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,100 @@
|
||||
name: python-runtime-release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runtime_version:
|
||||
description: "Runtime version to publish before updating the SDK pin, for example 0.136.0 or 0.136.0a2."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: python-runtime-release-${{ inputs.runtime_version }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
prepare-python-runtime:
|
||||
name: prepare-python-runtime
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/python-runtime-build.yml
|
||||
with:
|
||||
runtime_version: ${{ inputs.runtime_version }}
|
||||
|
||||
# PyPI must trust this top-level workflow for manual runtime publication.
|
||||
publish-python-runtime:
|
||||
if: github.repository == 'openai/codex'
|
||||
name: publish-python-runtime
|
||||
needs: prepare-python-runtime
|
||||
runs-on: ubuntu-latest
|
||||
environment: pypi
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # Required for PyPI trusted publishing.
|
||||
|
||||
steps:
|
||||
- name: Download Python runtime wheels
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: python-runtime-wheels
|
||||
path: dist/python-runtime
|
||||
|
||||
- name: Publish Python runtime wheels to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||
with:
|
||||
packages-dir: dist/python-runtime
|
||||
skip-existing: true
|
||||
|
||||
- name: Verify Python runtime wheels are available on PyPI
|
||||
env:
|
||||
PYTHON_RUNTIME_VERSION: ${{ inputs.runtime_version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for attempt in {1..30}; do
|
||||
if python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
version = os.environ["PYTHON_RUNTIME_VERSION"]
|
||||
tags = {
|
||||
"macosx_10_9_x86_64",
|
||||
"macosx_11_0_arm64",
|
||||
"manylinux_2_17_aarch64",
|
||||
"manylinux_2_17_x86_64",
|
||||
"musllinux_1_1_aarch64",
|
||||
"musllinux_1_1_x86_64",
|
||||
"win_amd64",
|
||||
"win_arm64",
|
||||
}
|
||||
expected = {
|
||||
f"openai_codex_cli_bin-{version}-py3-none-{tag}.whl"
|
||||
for tag in tags
|
||||
}
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(
|
||||
f"https://pypi.org/pypi/openai-codex-cli-bin/{version}/json",
|
||||
timeout=30,
|
||||
) as response:
|
||||
payload = json.load(response)
|
||||
except urllib.error.URLError as error:
|
||||
print(f"Could not read runtime {version} from PyPI: {error}.")
|
||||
raise SystemExit(1) from error
|
||||
|
||||
actual = {file["filename"] for file in payload["urls"]}
|
||||
if actual != expected:
|
||||
print(f"Missing runtime wheels: {sorted(expected - actual)}")
|
||||
print(f"Unexpected runtime files: {sorted(actual - expected)}")
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
echo "Runtime wheels are not available on PyPI yet; retrying (${attempt}/30)."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo "Runtime wheels did not become available on PyPI."
|
||||
exit 1
|
||||
@@ -4,29 +4,21 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- "python-v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runtime_version:
|
||||
description: "Runtime version to publish before updating the SDK pin, for example 0.136.0 or 0.136.0a2."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Publish the platform-specific Python runtime wheels before building the SDK
|
||||
# package that pins them, or explicitly before updating the SDK runtime pin.
|
||||
# PyPI project configuration must trust this workflow and job for publishing.
|
||||
publish-python-runtime:
|
||||
resolve-python-release:
|
||||
if: github.repository == 'openai/codex'
|
||||
name: publish-python-runtime
|
||||
name: resolve-python-release
|
||||
runs-on: ubuntu-latest
|
||||
environment: pypi
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # Required for PyPI trusted publishing.
|
||||
outputs:
|
||||
runtime_version: ${{ steps.python_release.outputs.runtime_version }}
|
||||
sdk_version: ${{ steps.python_release.outputs.sdk_version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -34,11 +26,9 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Validate SDK tag and resolve Python runtime release
|
||||
id: python_runtime
|
||||
- name: Validate SDK tag and resolve pinned runtime
|
||||
id: python_release
|
||||
shell: bash
|
||||
env:
|
||||
REQUESTED_RUNTIME_VERSION: ${{ inputs.runtime_version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
@@ -47,107 +37,60 @@ jobs:
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
event_name = os.environ["GITHUB_EVENT_NAME"]
|
||||
if event_name == "workflow_dispatch":
|
||||
python_version = os.environ["REQUESTED_RUNTIME_VERSION"]
|
||||
elif event_name == "push":
|
||||
sdk_version = os.environ["GITHUB_REF_NAME"].removeprefix("python-v")
|
||||
if not re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+b[0-9]+", sdk_version):
|
||||
raise SystemExit(
|
||||
"Python SDK release tags must identify a beta release, "
|
||||
"for example python-v0.1.0b1."
|
||||
)
|
||||
|
||||
pyproject = tomllib.loads(Path("sdk/python/pyproject.toml").read_text())
|
||||
prefix = "openai-codex-cli-bin=="
|
||||
versions = [
|
||||
dependency.removeprefix(prefix)
|
||||
for dependency in pyproject["project"]["dependencies"]
|
||||
if dependency.startswith(prefix)
|
||||
]
|
||||
if len(versions) != 1:
|
||||
raise SystemExit(f"Expected exactly one pinned {prefix} dependency, found {versions}")
|
||||
python_version = versions[0]
|
||||
else:
|
||||
raise SystemExit(f"Unsupported workflow event: {event_name}")
|
||||
|
||||
if match := re.fullmatch(r"([0-9]+\.[0-9]+\.[0-9]+)a([0-9]+)", python_version):
|
||||
release_version = f"{match.group(1)}-alpha.{match.group(2)}"
|
||||
elif re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", python_version):
|
||||
release_version = python_version
|
||||
else:
|
||||
sdk_version = os.environ["GITHUB_REF_NAME"].removeprefix("python-v")
|
||||
if not re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+b[0-9]+", sdk_version):
|
||||
raise SystemExit(
|
||||
"Python runtime version must be stable or a numbered alpha, "
|
||||
f"for example 0.136.0 or 0.136.0a2; found {python_version}"
|
||||
"Python SDK release tags must identify a beta release, "
|
||||
"for example python-v0.1.0b1."
|
||||
)
|
||||
|
||||
pyproject = tomllib.loads(Path("sdk/python/pyproject.toml").read_text())
|
||||
prefix = "openai-codex-cli-bin=="
|
||||
runtime_versions = [
|
||||
dependency.removeprefix(prefix)
|
||||
for dependency in pyproject["project"]["dependencies"]
|
||||
if dependency.startswith(prefix)
|
||||
]
|
||||
if len(runtime_versions) != 1:
|
||||
raise SystemExit(
|
||||
f"Expected exactly one pinned {prefix} dependency, found {runtime_versions}"
|
||||
)
|
||||
|
||||
with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
|
||||
print(f"python_version={python_version}", file=output)
|
||||
print(f"release_tag=rust-v{release_version}", file=output)
|
||||
print(f"runtime_version={runtime_versions[0]}", file=output)
|
||||
print(f"sdk_version={sdk_version}", file=output)
|
||||
PY
|
||||
|
||||
- name: Download Python runtime release artifacts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PYTHON_RUNTIME_VERSION: ${{ steps.python_runtime.outputs.python_version }}
|
||||
RELEASE_TAG: ${{ steps.python_runtime.outputs.release_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p dist/python-runtime dist/python-runtime-packages
|
||||
gh release download "$RELEASE_TAG" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--pattern "openai_codex_cli_bin-${PYTHON_RUNTIME_VERSION}-*.whl" \
|
||||
--dir dist/python-runtime
|
||||
gh release download "$RELEASE_TAG" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--pattern "codex-package-*-unknown-linux-musl.tar.gz" \
|
||||
--dir dist/python-runtime-packages
|
||||
prepare-python-runtime:
|
||||
name: prepare-python-runtime
|
||||
needs: resolve-python-release
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/python-runtime-build.yml
|
||||
with:
|
||||
runtime_version: ${{ needs.resolve-python-release.outputs.runtime_version }}
|
||||
|
||||
shopt -s nullglob
|
||||
wheels=(dist/python-runtime/*.whl)
|
||||
if [[ "${#wheels[@]}" -ne 6 ]]; then
|
||||
echo "Expected 6 Python runtime wheels for ${PYTHON_RUNTIME_VERSION}, found ${#wheels[@]}."
|
||||
exit 1
|
||||
fi
|
||||
packages=(dist/python-runtime-packages/*.tar.gz)
|
||||
if [[ "${#packages[@]}" -ne 2 ]]; then
|
||||
echo "Expected 2 Linux package archives for ${PYTHON_RUNTIME_VERSION}, found ${#packages[@]}."
|
||||
exit 1
|
||||
fi
|
||||
# Always publish the exact pinned runtime from this top-level workflow before
|
||||
# building the SDK package. PyPI does not support reusable workflows as
|
||||
# Trusted Publishers.
|
||||
publish-python-runtime:
|
||||
if: github.repository == 'openai/codex'
|
||||
name: publish-python-runtime
|
||||
needs:
|
||||
- prepare-python-runtime
|
||||
- resolve-python-release
|
||||
runs-on: ubuntu-latest
|
||||
environment: pypi
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # Required for PyPI trusted publishing.
|
||||
|
||||
- name: Build musllinux Python runtime wheels
|
||||
env:
|
||||
RELEASE_TAG: ${{ steps.python_runtime.outputs.release_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
python3 -m venv "${RUNNER_TEMP}/python-runtime-build-venv"
|
||||
"${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m pip install build
|
||||
|
||||
while read -r target platform_tag; do
|
||||
stage_dir="${RUNNER_TEMP}/openai-codex-cli-bin-${target}-${platform_tag}"
|
||||
python3 sdk/python/scripts/update_sdk_artifacts.py \
|
||||
stage-runtime \
|
||||
"$stage_dir" \
|
||||
"dist/python-runtime-packages/codex-package-${target}.tar.gz" \
|
||||
--codex-version "$RELEASE_TAG" \
|
||||
--platform-tag "$platform_tag"
|
||||
"${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m build \
|
||||
--wheel \
|
||||
--outdir dist/python-runtime \
|
||||
"$stage_dir"
|
||||
done <<'EOF'
|
||||
aarch64-unknown-linux-musl musllinux_1_1_aarch64
|
||||
x86_64-unknown-linux-musl musllinux_1_1_x86_64
|
||||
EOF
|
||||
|
||||
shopt -s nullglob
|
||||
wheels=(dist/python-runtime/*.whl)
|
||||
if [[ "${#wheels[@]}" -ne 8 ]]; then
|
||||
echo "Expected 8 Python runtime wheels, found ${#wheels[@]}."
|
||||
exit 1
|
||||
fi
|
||||
ls -lh dist/python-runtime
|
||||
steps:
|
||||
- name: Download Python runtime wheels
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: python-runtime-wheels
|
||||
path: dist/python-runtime
|
||||
|
||||
- name: Publish Python runtime wheels to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||
@@ -155,10 +98,66 @@ jobs:
|
||||
packages-dir: dist/python-runtime
|
||||
skip-existing: true
|
||||
|
||||
- name: Verify Python runtime wheels are available on PyPI
|
||||
env:
|
||||
PYTHON_RUNTIME_VERSION: ${{ needs.resolve-python-release.outputs.runtime_version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for attempt in {1..30}; do
|
||||
if python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
version = os.environ["PYTHON_RUNTIME_VERSION"]
|
||||
tags = {
|
||||
"macosx_10_9_x86_64",
|
||||
"macosx_11_0_arm64",
|
||||
"manylinux_2_17_aarch64",
|
||||
"manylinux_2_17_x86_64",
|
||||
"musllinux_1_1_aarch64",
|
||||
"musllinux_1_1_x86_64",
|
||||
"win_amd64",
|
||||
"win_arm64",
|
||||
}
|
||||
expected = {
|
||||
f"openai_codex_cli_bin-{version}-py3-none-{tag}.whl"
|
||||
for tag in tags
|
||||
}
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(
|
||||
f"https://pypi.org/pypi/openai-codex-cli-bin/{version}/json",
|
||||
timeout=30,
|
||||
) as response:
|
||||
payload = json.load(response)
|
||||
except urllib.error.URLError as error:
|
||||
print(f"Could not read runtime {version} from PyPI: {error}.")
|
||||
raise SystemExit(1) from error
|
||||
|
||||
actual = {file["filename"] for file in payload["urls"]}
|
||||
if actual != expected:
|
||||
print(f"Missing runtime wheels: {sorted(expected - actual)}")
|
||||
print(f"Unexpected runtime files: {sorted(actual - expected)}")
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
echo "Runtime wheels are not available on PyPI yet; retrying (${attempt}/30)."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo "Runtime wheels did not become available on PyPI."
|
||||
exit 1
|
||||
|
||||
build-python-sdk:
|
||||
if: github.event_name == 'push' && github.repository == 'openai/codex'
|
||||
if: github.repository == 'openai/codex'
|
||||
name: build-python-sdk
|
||||
needs: publish-python-runtime
|
||||
needs:
|
||||
- publish-python-runtime
|
||||
- resolve-python-release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -169,19 +168,20 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Validate tag and build Python SDK package
|
||||
- name: Build Python SDK package
|
||||
shell: bash
|
||||
env:
|
||||
SDK_VERSION: ${{ needs.resolve-python-release.outputs.sdk_version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
sdk_version="${GITHUB_REF_NAME#python-v}"
|
||||
# Build in a glibc Linux image so release type generation installs
|
||||
# the pinned manylinux runtime wheel.
|
||||
docker run --rm \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
-e HOME=/tmp/codex-python-sdk-home \
|
||||
-e UV_LINK_MODE=copy \
|
||||
-e SDK_VERSION="${sdk_version}" \
|
||||
-e SDK_VERSION \
|
||||
-e SDK_STAGE_DIR="${RUNNER_TEMP}/openai-codex" \
|
||||
-e SDK_DIST_DIR="${GITHUB_WORKSPACE}/dist/python-sdk" \
|
||||
-v "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \
|
||||
|
||||
@@ -98,6 +98,9 @@ jobs:
|
||||
name: Argument comment lint - ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.runs_on || matrix.runner }}
|
||||
timeout-minutes: 30
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -109,8 +112,8 @@ jobs:
|
||||
- name: Windows
|
||||
runner: windows-x64
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
@@ -167,8 +170,6 @@ jobs:
|
||||
USE_SCCACHE: ${{ (startsWith(matrix.runner, 'windows') || (matrix.runner == 'macos-15-xlarge' && matrix.target == 'x86_64-apple-darwin')) && 'false' || 'true' }}
|
||||
CARGO_INCREMENTAL: "0"
|
||||
SCCACHE_CACHE_SIZE: 10G
|
||||
# In rust-ci, representative release-profile checks use thin LTO for faster feedback.
|
||||
CARGO_PROFILE_RELEASE_LTO: ${{ matrix.profile == 'release' && 'thin' || 'fat' }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -184,38 +185,38 @@ jobs:
|
||||
target: x86_64-unknown-linux-musl
|
||||
profile: dev
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-linux-x64
|
||||
- runner: ubuntu-24.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: dev
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-linux-x64
|
||||
- runner: ubuntu-24.04-arm
|
||||
target: aarch64-unknown-linux-musl
|
||||
profile: dev
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-linux-arm64
|
||||
- runner: ubuntu-24.04-arm
|
||||
target: aarch64-unknown-linux-gnu
|
||||
profile: dev
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-linux-arm64
|
||||
- runner: windows-x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
profile: dev
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
- runner: windows-arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
profile: dev
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-arm64
|
||||
|
||||
# Also run representative release builds on Mac and Linux because
|
||||
# there could be release-only build errors we want to catch.
|
||||
@@ -228,26 +229,26 @@ jobs:
|
||||
target: x86_64-unknown-linux-musl
|
||||
profile: release
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-linux-x64
|
||||
- runner: ubuntu-24.04-arm
|
||||
target: aarch64-unknown-linux-musl
|
||||
profile: release
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-linux-arm64
|
||||
- runner: windows-x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
profile: release
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
- runner: windows-arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
profile: release
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -478,8 +479,8 @@ jobs:
|
||||
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
|
||||
with:
|
||||
runner: ubuntu-24.04
|
||||
runner_group: codex-runners
|
||||
runner_labels: codex-linux-x64
|
||||
runner_group: ${{ github.event.repository.name }}-runners
|
||||
runner_labels: ${{ github.event.repository.name }}-linux-x64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: ci-test
|
||||
artifact_id: linux-x64-remote
|
||||
@@ -492,8 +493,8 @@ jobs:
|
||||
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
|
||||
with:
|
||||
runner: ubuntu-24.04-arm
|
||||
runner_group: codex-runners
|
||||
runner_labels: codex-linux-arm64
|
||||
runner_group: ${{ github.event.repository.name }}-runners
|
||||
runner_labels: ${{ github.event.repository.name }}-linux-arm64
|
||||
target: aarch64-unknown-linux-gnu
|
||||
profile: ci-test
|
||||
artifact_id: linux-arm64
|
||||
@@ -505,8 +506,8 @@ jobs:
|
||||
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
|
||||
with:
|
||||
runner: windows-x64
|
||||
runner_group: codex-runners
|
||||
runner_labels: codex-windows-x64
|
||||
runner_group: ${{ github.event.repository.name }}-runners
|
||||
runner_labels: ${{ github.event.repository.name }}-windows-x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
profile: ci-test
|
||||
artifact_id: windows-x64
|
||||
@@ -518,11 +519,11 @@ jobs:
|
||||
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
|
||||
with:
|
||||
runner: windows-arm64
|
||||
runner_group: codex-runners
|
||||
runner_labels: codex-windows-arm64
|
||||
runner_group: ${{ github.event.repository.name }}-runners
|
||||
runner_labels: ${{ github.event.repository.name }}-windows-arm64
|
||||
archive_runner: windows-x64
|
||||
archive_runner_group: codex-runners
|
||||
archive_runner_labels: codex-windows-x64
|
||||
archive_runner_group: ${{ github.event.repository.name }}-runners
|
||||
archive_runner_labels: ${{ github.event.repository.name }}-windows-x64
|
||||
target: aarch64-pc-windows-msvc
|
||||
profile: ci-test
|
||||
artifact_id: windows-arm64
|
||||
|
||||
@@ -159,6 +159,9 @@ jobs:
|
||||
runs-on: ${{ matrix.runs_on || matrix.runner }}
|
||||
timeout-minutes: ${{ matrix.timeout_minutes }}
|
||||
needs: changed
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -173,8 +176,8 @@ jobs:
|
||||
runner: windows-x64
|
||||
timeout_minutes: 30
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
steps:
|
||||
- name: Check whether argument comment lint should run
|
||||
id: argument_comment_lint_gate
|
||||
|
||||
@@ -57,8 +57,8 @@ jobs:
|
||||
runner_binary: argument-comment-lint.exe
|
||||
cargo_dylint_binary: cargo-dylint.exe
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
@@ -2,23 +2,6 @@ name: rust-release-windows
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
release-lto:
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
AZURE_TRUSTED_SIGNING_CLIENT_ID:
|
||||
required: true
|
||||
AZURE_TRUSTED_SIGNING_TENANT_ID:
|
||||
required: true
|
||||
AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID:
|
||||
required: true
|
||||
AZURE_TRUSTED_SIGNING_ENDPOINT:
|
||||
required: true
|
||||
AZURE_TRUSTED_SIGNING_ACCOUNT_NAME:
|
||||
required: true
|
||||
AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME:
|
||||
required: true
|
||||
|
||||
# Cargo's libgit2 transport has been flaky when fetching git dependencies with
|
||||
# nested submodules. Prefer the system git CLI across every Cargo invocation.
|
||||
@@ -29,17 +12,14 @@ jobs:
|
||||
build-windows-binaries:
|
||||
name: Build Windows binaries - ${{ matrix.runner }} - ${{ matrix.target }} - ${{ matrix.bundle }}
|
||||
runs-on: ${{ matrix.runs_on }}
|
||||
# Windows release builds can exceed an hour on fat-LTO mainline releases,
|
||||
# so keep the timeout aligned with the top-level release build headroom.
|
||||
# Windows release builds can exceed an hour, so keep the timeout aligned
|
||||
# with the top-level release build headroom.
|
||||
timeout-minutes: 90
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: codex-rs
|
||||
env:
|
||||
CARGO_PROFILE_RELEASE_LTO: ${{ inputs.release-lto }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -49,43 +29,43 @@ jobs:
|
||||
bundle: primary
|
||||
binaries: "codex codex-responses-api-proxy"
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
- runner: windows-arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
bundle: primary
|
||||
binaries: "codex codex-responses-api-proxy"
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-arm64
|
||||
- runner: windows-x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
bundle: helpers
|
||||
binaries: "codex-windows-sandbox-setup codex-command-runner"
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
- runner: windows-arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
bundle: helpers
|
||||
binaries: "codex-windows-sandbox-setup codex-command-runner"
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-arm64
|
||||
- runner: windows-x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
bundle: app-server
|
||||
binaries: "codex-app-server"
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
- runner: windows-arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
bundle: app-server
|
||||
binaries: "codex-app-server"
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -132,10 +112,22 @@ jobs:
|
||||
- name: Stage Windows binaries
|
||||
shell: bash
|
||||
run: |
|
||||
output_dir="target/${{ matrix.target }}/release/staged-${{ matrix.bundle }}"
|
||||
release_dir="target/${{ matrix.target }}/release"
|
||||
output_dir="$release_dir/staged-${{ matrix.bundle }}"
|
||||
mkdir -p "$output_dir"
|
||||
for binary in ${{ matrix.binaries }}; do
|
||||
cp "target/${{ matrix.target }}/release/${binary}.exe" "$output_dir/${binary}.exe"
|
||||
pdb_name="${binary//-/_}"
|
||||
pdb_path="$release_dir/${pdb_name}.pdb"
|
||||
if [[ ! -f "$pdb_path" ]]; then
|
||||
pdb_path="$release_dir/${binary}.pdb"
|
||||
fi
|
||||
if [[ ! -f "$pdb_path" ]]; then
|
||||
echo "PDB for $binary not found at $release_dir/${pdb_name}.pdb or $release_dir/${binary}.pdb" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$release_dir/${binary}.exe" "$output_dir/${binary}.exe"
|
||||
cp "$pdb_path" "$output_dir/${binary}.pdb"
|
||||
done
|
||||
|
||||
- name: Upload Windows binaries
|
||||
@@ -150,6 +142,9 @@ jobs:
|
||||
- build-windows-binaries
|
||||
name: Build - ${{ matrix.runner }} - ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.runs_on }}
|
||||
environment:
|
||||
name: azure-artifact-signing
|
||||
deployment: false
|
||||
timeout-minutes: 90
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -167,13 +162,13 @@ jobs:
|
||||
- runner: windows-x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-x64
|
||||
- runner: windows-arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-arm64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-windows-arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -211,12 +206,29 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
binaries: ${{ env.WINDOWS_BINARIES }}
|
||||
client-id: ${{ secrets.AZURE_TRUSTED_SIGNING_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TRUSTED_SIGNING_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID }}
|
||||
endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
|
||||
account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
|
||||
certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }}
|
||||
client-id: ${{ secrets.AZURE_ARTIFACT_SIGNING_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_ARTIFACT_SIGNING_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_ARTIFACT_SIGNING_SUBSCRIPTION_ID }}
|
||||
endpoint: ${{ secrets.AZURE_ARTIFACT_SIGNING_ENDPOINT }}
|
||||
account-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_ACCOUNT_NAME }}
|
||||
certificate-profile-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME }}
|
||||
|
||||
- name: Build symbols archive
|
||||
shell: bash
|
||||
run: |
|
||||
bash "${GITHUB_WORKSPACE}/.github/scripts/archive-release-symbols-and-strip-binaries.sh" \
|
||||
--target "${{ matrix.target }}" \
|
||||
--artifact-name "${{ matrix.target }}" \
|
||||
--release-dir "target/${{ matrix.target }}/release" \
|
||||
--archive-dir "symbols-dist/${{ matrix.target }}" \
|
||||
--binaries "${WINDOWS_BINARIES}"
|
||||
|
||||
- name: Upload symbols archive
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: ${{ matrix.target }}-symbols
|
||||
path: codex-rs/symbols-dist/${{ matrix.target }}/*
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Stage artifacts
|
||||
shell: bash
|
||||
|
||||
+522
-491
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,9 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -73,72 +76,84 @@ jobs:
|
||||
platform: linux_amd64
|
||||
sandbox: false
|
||||
target: x86_64-unknown-linux-gnu
|
||||
v8_cpu: x64
|
||||
variant: release
|
||||
- runner: ubuntu-24.04
|
||||
bazel_config: ci-v8
|
||||
platform: linux_amd64
|
||||
sandbox: true
|
||||
target: x86_64-unknown-linux-gnu
|
||||
v8_cpu: x64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: ubuntu-24.04-arm
|
||||
bazel_config: ci-v8
|
||||
platform: linux_arm64
|
||||
sandbox: false
|
||||
target: aarch64-unknown-linux-gnu
|
||||
v8_cpu: arm64
|
||||
variant: release
|
||||
- runner: ubuntu-24.04-arm
|
||||
bazel_config: ci-v8
|
||||
platform: linux_arm64
|
||||
sandbox: true
|
||||
target: aarch64-unknown-linux-gnu
|
||||
v8_cpu: arm64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: macos-15-xlarge
|
||||
bazel_config: ci-macos
|
||||
platform: macos_amd64
|
||||
sandbox: false
|
||||
target: x86_64-apple-darwin
|
||||
v8_cpu: x64
|
||||
variant: release
|
||||
- runner: macos-15-xlarge
|
||||
bazel_config: ci-macos
|
||||
platform: macos_amd64
|
||||
sandbox: true
|
||||
target: x86_64-apple-darwin
|
||||
v8_cpu: x64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: macos-15-xlarge
|
||||
bazel_config: ci-macos
|
||||
platform: macos_arm64
|
||||
sandbox: false
|
||||
target: aarch64-apple-darwin
|
||||
v8_cpu: arm64
|
||||
variant: release
|
||||
- runner: macos-15-xlarge
|
||||
bazel_config: ci-macos
|
||||
platform: macos_arm64
|
||||
sandbox: true
|
||||
target: aarch64-apple-darwin
|
||||
v8_cpu: arm64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: ubuntu-24.04
|
||||
bazel_config: ci-v8
|
||||
platform: linux_amd64_musl
|
||||
sandbox: false
|
||||
target: x86_64-unknown-linux-musl
|
||||
v8_cpu: x64
|
||||
variant: release
|
||||
- runner: ubuntu-24.04-arm
|
||||
bazel_config: ci-v8
|
||||
platform: linux_arm64_musl
|
||||
sandbox: false
|
||||
target: aarch64-unknown-linux-musl
|
||||
v8_cpu: arm64
|
||||
variant: release
|
||||
- runner: ubuntu-24.04
|
||||
bazel_config: ci-v8
|
||||
platform: linux_amd64_musl
|
||||
sandbox: true
|
||||
target: x86_64-unknown-linux-musl
|
||||
v8_cpu: x64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: ubuntu-24.04-arm
|
||||
bazel_config: ci-v8
|
||||
platform: linux_arm64_musl
|
||||
sandbox: true
|
||||
target: aarch64-unknown-linux-musl
|
||||
v8_cpu: arm64
|
||||
variant: ptrcomp-sandbox
|
||||
|
||||
steps:
|
||||
@@ -167,6 +182,7 @@ jobs:
|
||||
PLATFORM: ${{ matrix.platform }}
|
||||
SANDBOX: ${{ matrix.sandbox }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
V8_CPU: ${{ matrix.v8_cpu }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -184,6 +200,7 @@ jobs:
|
||||
opt
|
||||
"--platforms=@llvm//platforms:${PLATFORM}"
|
||||
--config=rusty-v8-upstream-libcxx
|
||||
"--config=v8-target-${V8_CPU}"
|
||||
"${pair_target}"
|
||||
--build_metadata=COMMIT_SHA=$(git rev-parse HEAD)
|
||||
)
|
||||
@@ -203,6 +220,7 @@ jobs:
|
||||
PLATFORM: ${{ matrix.platform }}
|
||||
SANDBOX: ${{ matrix.sandbox }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
V8_CPU: ${{ matrix.v8_cpu }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -213,6 +231,7 @@ jobs:
|
||||
--compilation-mode opt
|
||||
--output-dir "dist/${TARGET}"
|
||||
--bazel-config "${BAZEL_CONFIG}"
|
||||
--bazel-config "v8-target-${V8_CPU}"
|
||||
)
|
||||
if [[ "${SANDBOX}" == "true" ]]; then
|
||||
stage_args+=(--sandbox)
|
||||
@@ -266,10 +285,150 @@ jobs:
|
||||
name: rusty-v8-${{ needs.metadata.outputs.v8_version }}-${{ matrix.variant }}-${{ matrix.target }}
|
||||
path: dist/${{ matrix.target }}/*
|
||||
|
||||
build-windows-source:
|
||||
name: Build ptrcomp-sandbox ${{ matrix.target }} from source
|
||||
needs: metadata
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: windows-2022
|
||||
target: x86_64-pc-windows-msvc
|
||||
- runner: windows-2022
|
||||
target: aarch64-pc-windows-msvc
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Configure git for upstream checkout
|
||||
shell: bash
|
||||
run: git config --global core.symlinks true
|
||||
|
||||
- name: Check out upstream rusty_v8
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: denoland/rusty_v8
|
||||
ref: v${{ needs.metadata.outputs.v8_version }}
|
||||
path: upstream-rusty-v8
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
architecture: x64
|
||||
|
||||
- name: Set up Codex Rust toolchain for Cargo smoke
|
||||
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
|
||||
with:
|
||||
toolchain: "1.95.0"
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Install rusty_v8 Rust toolchain
|
||||
env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rustup toolchain install 1.91.0 --profile minimal --no-self-update
|
||||
rustup target add --toolchain 1.91.0 "${TARGET}"
|
||||
|
||||
- name: Write upstream submodule status
|
||||
shell: bash
|
||||
working-directory: upstream-rusty-v8
|
||||
run: git submodule status --recursive > git_submodule_status.txt
|
||||
|
||||
- name: Restore upstream source-build cache
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: |
|
||||
upstream-rusty-v8/target/sccache
|
||||
upstream-rusty-v8/target/${{ matrix.target }}/release/gn_out
|
||||
key: rusty-v8-source-${{ matrix.target }}-sandbox-${{ hashFiles('upstream-rusty-v8/Cargo.lock', 'upstream-rusty-v8/build.rs', 'upstream-rusty-v8/git_submodule_status.txt') }}
|
||||
restore-keys: |
|
||||
rusty-v8-source-${{ matrix.target }}-sandbox-
|
||||
|
||||
- name: Install and start sccache
|
||||
shell: pwsh
|
||||
env:
|
||||
SCCACHE_CACHE_SIZE: 256M
|
||||
SCCACHE_DIR: ${{ github.workspace }}/upstream-rusty-v8/target/sccache
|
||||
SCCACHE_IDLE_TIMEOUT: 0
|
||||
run: |
|
||||
$version = "v0.8.2"
|
||||
$platform = "x86_64-pc-windows-msvc"
|
||||
$basename = "sccache-$version-$platform"
|
||||
$url = "https://github.com/mozilla/sccache/releases/download/$version/$basename.tar.gz"
|
||||
cd ~
|
||||
curl -LO $url
|
||||
tar -xzvf "$basename.tar.gz"
|
||||
. $basename/sccache --start-server
|
||||
echo "$(pwd)/$basename" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
|
||||
- name: Install Chromium clang for ARM64 MSVC cross build
|
||||
if: matrix.target == 'aarch64-pc-windows-msvc'
|
||||
shell: bash
|
||||
working-directory: upstream-rusty-v8
|
||||
run: python3 tools/clang/scripts/update.py
|
||||
|
||||
- name: Build upstream rusty_v8 sandbox release pair
|
||||
env:
|
||||
SCCACHE_IDLE_TIMEOUT: 0
|
||||
TARGET: ${{ matrix.target }}
|
||||
V8_FROM_SOURCE: "1"
|
||||
shell: bash
|
||||
working-directory: upstream-rusty-v8
|
||||
run: cargo +1.91.0 build --locked --release --target "${TARGET}" --features v8_enable_sandbox
|
||||
|
||||
- name: Stage upstream sandbox release pair
|
||||
env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 .github/scripts/rusty_v8_bazel.py stage-upstream-release-pair \
|
||||
--source-root upstream-rusty-v8 \
|
||||
--target "${TARGET}" \
|
||||
--output-dir "dist/${TARGET}" \
|
||||
--sandbox
|
||||
|
||||
- name: Smoke link staged artifact with Cargo
|
||||
env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
archive="$(find "dist/${TARGET}" -maxdepth 1 -type f -name 'rusty_v8_*.lib.gz' -print -quit)"
|
||||
binding="$(find "dist/${TARGET}" -maxdepth 1 -type f -name 'src_binding_*.rs' -print -quit)"
|
||||
if [[ -z "${archive}" || -z "${binding}" ]]; then
|
||||
echo "Missing staged archive or binding for ${TARGET}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(
|
||||
cd codex-rs
|
||||
RUSTY_V8_ARCHIVE="${GITHUB_WORKSPACE}/${archive}" \
|
||||
RUSTY_V8_SRC_BINDING_PATH="${GITHUB_WORKSPACE}/${binding}" \
|
||||
cargo +1.95.0 test -p codex-v8-poc --target "${TARGET}" --features sandbox --no-run
|
||||
)
|
||||
|
||||
- name: Upload staged artifacts
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: rusty-v8-${{ needs.metadata.outputs.v8_version }}-ptrcomp-sandbox-${{ matrix.target }}
|
||||
path: dist/${{ matrix.target }}/*
|
||||
|
||||
publish-release:
|
||||
needs:
|
||||
- metadata
|
||||
- build
|
||||
- build-windows-source
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -8,8 +8,8 @@ on:
|
||||
jobs:
|
||||
python-sdk:
|
||||
runs-on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-linux-x64
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -43,9 +43,12 @@ jobs:
|
||||
|
||||
sdks:
|
||||
runs-on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-x64
|
||||
group: ${{ github.event.repository.name }}-runners
|
||||
labels: ${{ github.event.repository.name }}-linux-x64
|
||||
timeout-minutes: 10
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
@@ -80,6 +80,9 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
environment:
|
||||
name: bazel
|
||||
deployment: false
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -89,72 +92,84 @@ jobs:
|
||||
platform: linux_amd64
|
||||
sandbox: false
|
||||
target: x86_64-unknown-linux-gnu
|
||||
v8_cpu: x64
|
||||
variant: release
|
||||
- runner: ubuntu-24.04
|
||||
bazel_config: ci-v8
|
||||
platform: linux_amd64
|
||||
sandbox: true
|
||||
target: x86_64-unknown-linux-gnu
|
||||
v8_cpu: x64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: ubuntu-24.04-arm
|
||||
bazel_config: ci-v8
|
||||
platform: linux_arm64
|
||||
sandbox: false
|
||||
target: aarch64-unknown-linux-gnu
|
||||
v8_cpu: arm64
|
||||
variant: release
|
||||
- runner: ubuntu-24.04-arm
|
||||
bazel_config: ci-v8
|
||||
platform: linux_arm64
|
||||
sandbox: true
|
||||
target: aarch64-unknown-linux-gnu
|
||||
v8_cpu: arm64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: macos-15-xlarge
|
||||
bazel_config: ci-macos
|
||||
platform: macos_amd64
|
||||
sandbox: false
|
||||
target: x86_64-apple-darwin
|
||||
v8_cpu: x64
|
||||
variant: release
|
||||
- runner: macos-15-xlarge
|
||||
bazel_config: ci-macos
|
||||
platform: macos_amd64
|
||||
sandbox: true
|
||||
target: x86_64-apple-darwin
|
||||
v8_cpu: x64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: macos-15-xlarge
|
||||
bazel_config: ci-macos
|
||||
platform: macos_arm64
|
||||
sandbox: false
|
||||
target: aarch64-apple-darwin
|
||||
v8_cpu: arm64
|
||||
variant: release
|
||||
- runner: macos-15-xlarge
|
||||
bazel_config: ci-macos
|
||||
platform: macos_arm64
|
||||
sandbox: true
|
||||
target: aarch64-apple-darwin
|
||||
v8_cpu: arm64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: ubuntu-24.04
|
||||
bazel_config: ci-v8
|
||||
platform: linux_amd64_musl
|
||||
sandbox: false
|
||||
target: x86_64-unknown-linux-musl
|
||||
v8_cpu: x64
|
||||
variant: release
|
||||
- runner: ubuntu-24.04
|
||||
bazel_config: ci-v8
|
||||
platform: linux_amd64_musl
|
||||
sandbox: true
|
||||
target: x86_64-unknown-linux-musl
|
||||
v8_cpu: x64
|
||||
variant: ptrcomp-sandbox
|
||||
- runner: ubuntu-24.04-arm
|
||||
bazel_config: ci-v8
|
||||
platform: linux_arm64_musl
|
||||
sandbox: false
|
||||
target: aarch64-unknown-linux-musl
|
||||
v8_cpu: arm64
|
||||
variant: release
|
||||
- runner: ubuntu-24.04-arm
|
||||
bazel_config: ci-v8
|
||||
platform: linux_arm64_musl
|
||||
sandbox: true
|
||||
target: aarch64-unknown-linux-musl
|
||||
v8_cpu: arm64
|
||||
variant: ptrcomp-sandbox
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -183,6 +198,7 @@ jobs:
|
||||
PLATFORM: ${{ matrix.platform }}
|
||||
SANDBOX: ${{ matrix.sandbox }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
V8_CPU: ${{ matrix.v8_cpu }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -198,6 +214,7 @@ jobs:
|
||||
build
|
||||
"--platforms=@llvm//platforms:${PLATFORM}"
|
||||
--config=rusty-v8-upstream-libcxx
|
||||
"--config=v8-target-${V8_CPU}"
|
||||
"${pair_target}"
|
||||
--build_metadata=COMMIT_SHA=$(git rev-parse HEAD)
|
||||
)
|
||||
@@ -217,6 +234,7 @@ jobs:
|
||||
PLATFORM: ${{ matrix.platform }}
|
||||
SANDBOX: ${{ matrix.sandbox }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
V8_CPU: ${{ matrix.v8_cpu }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -226,6 +244,7 @@ jobs:
|
||||
--target "${TARGET}"
|
||||
--output-dir "dist/${TARGET}"
|
||||
--bazel-config "${BAZEL_CONFIG}"
|
||||
--bazel-config "v8-target-${V8_CPU}"
|
||||
)
|
||||
if [[ "${SANDBOX}" == "true" ]]; then
|
||||
stage_args+=(--sandbox)
|
||||
|
||||
@@ -26,6 +26,8 @@ In the codex-rs folder where the rust code lives:
|
||||
- Implementations may still use `async fn foo(&self, ...) -> T` when they satisfy that contract.
|
||||
- Do not use `#[allow(async_fn_in_trait)]` as a shortcut around spelling the future contract explicitly.
|
||||
- When writing tests, prefer comparing the equality of entire objects over fields one by one.
|
||||
- Do not add tests for values that are statically defined.
|
||||
- Do not add negative tests for logic that was removed.
|
||||
- Do not add general product or user-facing documentation to the `docs/` folder. The official Codex documentation lives elsewhere. The exception is app-server API documentation, which is covered by the app-server guidance below.
|
||||
- Prefer private modules and explicitly exported public crate API.
|
||||
- If you change `ConfigToml` or nested config types, run `just write-config-schema` to update `codex-rs/core/config.schema.json`.
|
||||
@@ -190,6 +192,12 @@ If you don’t have the tool:
|
||||
|
||||
- `cargo install --locked cargo-insta`
|
||||
|
||||
### Benchmarks
|
||||
|
||||
cargo benchmarks can be run with `just bench`, use the divan crate to write new ones.
|
||||
|
||||
Use `just bench-smoke` to dry-run the benchmark for a single iteration to ensure it works.
|
||||
|
||||
### Test assertions
|
||||
|
||||
- Tests should use pretty_assertions::assert_eq for clearer diffs. Import this at the top of the test module if it isn't already.
|
||||
|
||||
+16
-16
@@ -415,18 +415,18 @@ crate.annotation(
|
||||
|
||||
inject_repo(crate, "alsa_lib")
|
||||
|
||||
bazel_dep(name = "v8", version = "14.7.173.20")
|
||||
bazel_dep(name = "v8", version = "14.9.207.2")
|
||||
archive_override(
|
||||
module_name = "v8",
|
||||
integrity = "sha256-v/x6I4X38a2wckzUIft3Dh0SUdkuOTokwxyF7lzW8Lc=",
|
||||
integrity = "sha256-tflbZE5srqal6leMxJjK/ZQtwpF96OMGJ6avd5lice4=",
|
||||
patch_strip = 3,
|
||||
patches = [
|
||||
"//patches:v8_module_deps.patch",
|
||||
"//patches:v8_bazel_rules.patch",
|
||||
"//patches:v8_source_portability.patch",
|
||||
],
|
||||
strip_prefix = "v8-14.7.173.20",
|
||||
urls = ["https://github.com/v8/v8/archive/refs/tags/14.7.173.20.tar.gz"],
|
||||
strip_prefix = "v8-14.9.207.2",
|
||||
urls = ["https://github.com/v8/v8/archive/refs/tags/14.9.207.2.tar.gz"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -439,12 +439,12 @@ http_archive(
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "v8_crate_147_4_0",
|
||||
name = "v8_crate_149_2_0",
|
||||
build_file = "//third_party/v8:v8_crate.BUILD.bazel",
|
||||
sha256 = "2df8fffd507fb18ed000673a83d937f58e60fb07f3306b2274284125b15137cd",
|
||||
strip_prefix = "v8-147.4.0",
|
||||
sha256 = "46dccf61a364b61bbaac70a8ba64a1a1006e87123b7d62eaeec999a3ba31ecdb",
|
||||
strip_prefix = "v8-149.2.0",
|
||||
type = "tar.gz",
|
||||
urls = ["https://static.crates.io/crates/v8/v8-147.4.0.crate"],
|
||||
urls = ["https://static.crates.io/crates/v8/v8-149.2.0.crate"],
|
||||
)
|
||||
|
||||
git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
@@ -452,7 +452,7 @@ git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "g
|
||||
git_repository(
|
||||
name = "rusty_v8_libcxx",
|
||||
build_file = "//third_party/v8:libcxx.BUILD.bazel",
|
||||
commit = "7ab65651aed6802d2599dcb7a73b1f82d5179d05",
|
||||
commit = "99457fa555797f8c5ac3c076ca288d8481d3b23a",
|
||||
remote = "https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git",
|
||||
)
|
||||
|
||||
@@ -466,25 +466,25 @@ git_repository(
|
||||
git_repository(
|
||||
name = "rusty_v8_llvm_libc",
|
||||
build_file = "//third_party/v8:llvm_libc.BUILD.bazel",
|
||||
commit = "b3aa5bb702ff9e890179fd1e7d3ba346e17ecf8e",
|
||||
commit = "cb952785ccee13811f293f3c419958d1e3ddafbf",
|
||||
remote = "https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libc.git",
|
||||
)
|
||||
|
||||
http_file(
|
||||
name = "rusty_v8_147_4_0_aarch64_pc_windows_msvc_archive",
|
||||
name = "rusty_v8_149_2_0_aarch64_pc_windows_msvc_archive",
|
||||
downloaded_file_path = "rusty_v8_release_aarch64-pc-windows-msvc.lib.gz",
|
||||
sha256 = "1fa3f94d9e09cff1f6bcce94c478e5cb072c0755f6a0357abadb9dd3b48d8127",
|
||||
sha256 = "923f2b6ccdc14526b814e171e34c9aafd7969f12304948857c6696d022f0fb3c",
|
||||
urls = [
|
||||
"https://github.com/denoland/rusty_v8/releases/download/v147.4.0/rusty_v8_release_aarch64-pc-windows-msvc.lib.gz",
|
||||
"https://github.com/denoland/rusty_v8/releases/download/v149.2.0/rusty_v8_release_aarch64-pc-windows-msvc.lib.gz",
|
||||
],
|
||||
)
|
||||
|
||||
http_file(
|
||||
name = "rusty_v8_147_4_0_x86_64_pc_windows_msvc_archive",
|
||||
name = "rusty_v8_149_2_0_x86_64_pc_windows_msvc_archive",
|
||||
downloaded_file_path = "rusty_v8_release_x86_64-pc-windows-msvc.lib.gz",
|
||||
sha256 = "e2827ff98b1a9d4c0343000fc5124ac30dfab3007bc0129c168c9355fc2fcd7c",
|
||||
sha256 = "12b5a791b54e92f748738ad8d0d12dad8d281a2d836638ad1aa6678e3b855d9a",
|
||||
urls = [
|
||||
"https://github.com/denoland/rusty_v8/releases/download/v147.4.0/rusty_v8_release_x86_64-pc-windows-msvc.lib.gz",
|
||||
"https://github.com/denoland/rusty_v8/releases/download/v149.2.0/rusty_v8_release_x86_64-pc-windows-msvc.lib.gz",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
Generated
+51
-37
File diff suppressed because one or more lines are too long
+21
-55
@@ -75,45 +75,25 @@ if (!platformPackage) {
|
||||
throw new Error(`Unsupported target triple: ${targetTriple}`);
|
||||
}
|
||||
|
||||
const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
|
||||
const localVendorRoot = path.join(__dirname, "..", "vendor");
|
||||
const packageBinaryPath = (vendorRoot) =>
|
||||
path.join(vendorRoot, targetTriple, "bin", codexBinaryName);
|
||||
const legacyBinaryPath = (vendorRoot) =>
|
||||
path.join(vendorRoot, targetTriple, "codex", codexBinaryName);
|
||||
|
||||
function resolveNativePackage(vendorRoot) {
|
||||
const packageRoot = path.join(vendorRoot, targetTriple);
|
||||
const binaryPath = packageBinaryPath(vendorRoot);
|
||||
if (existsSync(binaryPath)) {
|
||||
return {
|
||||
binaryPath,
|
||||
pathDir: path.join(packageRoot, "codex-path"),
|
||||
};
|
||||
function findCodexExecutable() {
|
||||
let vendorRoot;
|
||||
try {
|
||||
const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
|
||||
vendorRoot = path.join(path.dirname(packageJsonPath), "vendor");
|
||||
} catch {
|
||||
vendorRoot = path.join(__dirname, "..", "vendor");
|
||||
}
|
||||
|
||||
const legacyPath = legacyBinaryPath(vendorRoot);
|
||||
if (existsSync(legacyPath)) {
|
||||
return {
|
||||
binaryPath: legacyPath,
|
||||
pathDir: path.join(packageRoot, "path"),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
let nativePackage;
|
||||
try {
|
||||
const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
|
||||
nativePackage = resolveNativePackage(
|
||||
path.join(path.dirname(packageJsonPath), "vendor"),
|
||||
const codexExecutable = path.join(
|
||||
vendorRoot,
|
||||
targetTriple,
|
||||
"bin",
|
||||
process.platform === "win32" ? "codex.exe" : "codex",
|
||||
);
|
||||
} catch {
|
||||
nativePackage = resolveNativePackage(localVendorRoot);
|
||||
}
|
||||
if (existsSync(codexExecutable)) {
|
||||
return codexExecutable;
|
||||
}
|
||||
|
||||
if (!nativePackage) {
|
||||
const packageManager = detectPackageManager();
|
||||
const updateCommand =
|
||||
packageManager === "bun"
|
||||
@@ -124,7 +104,7 @@ if (!nativePackage) {
|
||||
);
|
||||
}
|
||||
|
||||
const { binaryPath, pathDir } = nativePackage;
|
||||
const binaryPath = findCodexExecutable();
|
||||
|
||||
// Use an asynchronous spawn instead of spawnSync so that Node is able to
|
||||
// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is
|
||||
@@ -132,16 +112,6 @@ const { binaryPath, pathDir } = nativePackage;
|
||||
// and guarantees that when either the child terminates or the parent
|
||||
// receives a fatal signal, both processes exit in a predictable manner.
|
||||
|
||||
function getUpdatedPath(newDirs) {
|
||||
const pathSep = process.platform === "win32" ? ";" : ":";
|
||||
const existingPath = process.env.PATH || "";
|
||||
const updatedPath = [
|
||||
...newDirs,
|
||||
...existingPath.split(pathSep).filter(Boolean),
|
||||
].join(pathSep);
|
||||
return updatedPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use heuristics to detect the package manager that was used to install Codex
|
||||
* in order to give the user a hint about how to update it.
|
||||
@@ -167,19 +137,15 @@ function detectPackageManager() {
|
||||
return userAgent ? "npm" : null;
|
||||
}
|
||||
|
||||
const additionalDirs = [];
|
||||
if (existsSync(pathDir)) {
|
||||
additionalDirs.push(pathDir);
|
||||
}
|
||||
const updatedPath = getUpdatedPath(additionalDirs);
|
||||
|
||||
const env = { ...process.env, PATH: updatedPath };
|
||||
const packageManagerEnvVar =
|
||||
detectPackageManager() === "bun"
|
||||
? "CODEX_MANAGED_BY_BUN"
|
||||
: "CODEX_MANAGED_BY_NPM";
|
||||
env[packageManagerEnvVar] = "1";
|
||||
env.CODEX_MANAGED_PACKAGE_ROOT = realpathSync(path.join(__dirname, ".."));
|
||||
const env = {
|
||||
...process.env,
|
||||
[packageManagerEnvVar]: "1",
|
||||
CODEX_MANAGED_PACKAGE_ROOT: realpathSync(path.join(__dirname, "..")),
|
||||
};
|
||||
|
||||
const child = spawn(binaryPath, process.argv.slice(2), {
|
||||
stdio: "inherit",
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
[advisories]
|
||||
# Reviewed 2026-04-15. Keep this list in sync with ../deny.toml.
|
||||
# Reviewed 2026-06-07. Keep this list in sync with ../deny.toml.
|
||||
ignore = [
|
||||
"RUSTSEC-2024-0388", # derivative 2.2.0 via starlark; upstream crate is unmaintained
|
||||
"RUSTSEC-2025-0057", # fxhash 0.2.1 via starlark_map; upstream crate is unmaintained
|
||||
"RUSTSEC-2024-0388", # derivative 2.2.0 via starlark/starlark_syntax; upstream crate is unmaintained
|
||||
"RUSTSEC-2025-0057", # fxhash 0.2.1 via starlark_map/bm25; upstream crate is unmaintained
|
||||
"RUSTSEC-2024-0436", # paste 1.0.15 via starlark/ratatui; upstream crate is unmaintained
|
||||
"RUSTSEC-2023-0089", # atomic-polyfill via postcard/heapless/pagable; upstream crate is unmaintained
|
||||
"RUSTSEC-2024-0320", # yaml-rust via syntect; remove when syntect drops or updates it
|
||||
"RUSTSEC-2025-0141", # bincode via syntect; remove when syntect drops or updates it
|
||||
"RUSTSEC-2026-0118", # hickory-proto via rama-dns/rama-tcp; remove when rama updates to hickory 0.26.1 or hickory-net
|
||||
"RUSTSEC-2026-0119", # hickory-proto via rama-dns/rama-tcp; remove when rama updates to hickory 0.26.1 or hickory-net
|
||||
"RUSTSEC-2026-0173", # proc-macro-error2 via i18n-embed-fl/age/codex-secrets; remove when local secrets storage migrates off age or age drops i18n-embed-fl
|
||||
]
|
||||
|
||||
Generated
+557
-356
File diff suppressed because it is too large
Load Diff
+9
-7
@@ -83,6 +83,7 @@ members = [
|
||||
"tools",
|
||||
"v8-poc",
|
||||
"utils/absolute-path",
|
||||
"utils/path-uri",
|
||||
"utils/cargo-bin",
|
||||
"git-utils",
|
||||
"utils/cache",
|
||||
@@ -318,6 +319,7 @@ libc = "0.2.182"
|
||||
log = "0.4"
|
||||
lru = "0.16.3"
|
||||
maplit = "1.0.2"
|
||||
memchr = "2.7.6"
|
||||
mime_guess = "2.0.5"
|
||||
multimap = "0.10.0"
|
||||
notify = "8.2.0"
|
||||
@@ -383,7 +385,7 @@ sqlx = { version = "0.9.0", default-features = false, features = [
|
||||
"time",
|
||||
"uuid",
|
||||
] }
|
||||
starlark = "0.13.0"
|
||||
starlark = { version = "0.14.2", default-features = false }
|
||||
strum = "0.27.2"
|
||||
strum_macros = "0.28.0"
|
||||
supports-color = "3.0.2"
|
||||
@@ -423,7 +425,7 @@ unicode-width = "0.2"
|
||||
url = "2"
|
||||
urlencoding = "2.1"
|
||||
uuid = "1"
|
||||
v8 = "=147.4.0"
|
||||
v8 = "=149.2.0"
|
||||
vt100 = "0.16.2"
|
||||
walkdir = "2.5.0"
|
||||
webbrowser = "1.0"
|
||||
@@ -482,7 +484,6 @@ unwrap_used = "deny"
|
||||
[workspace.metadata.cargo-shear]
|
||||
ignored = [
|
||||
"codex-agent-graph-store",
|
||||
"codex-goal-extension",
|
||||
"icu_provider",
|
||||
"openssl-sys",
|
||||
"codex-v8-poc",
|
||||
@@ -500,11 +501,12 @@ debug = "none"
|
||||
strip = "symbols"
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
lto = "thin"
|
||||
debug = "line-tables-only"
|
||||
split-debuginfo = "off"
|
||||
# Because we bundle some of these executables with the TypeScript CLI, we
|
||||
# remove everything to make the binary as small as possible.
|
||||
strip = "symbols"
|
||||
# Keep release binaries symbolicateable until packaging has archived the
|
||||
# sidecar symbols and stripped the binaries.
|
||||
strip = false
|
||||
|
||||
# See https://github.com/openai/codex/issues/1411 for details.
|
||||
codegen-units = 1
|
||||
|
||||
+2
-98
@@ -1,99 +1,3 @@
|
||||
# Codex CLI (Rust Implementation)
|
||||
# Codex CLI
|
||||
|
||||
We provide Codex CLI as a standalone executable to ensure a zero-dependency install.
|
||||
|
||||
## Installing Codex
|
||||
|
||||
Today, the easiest way to install Codex is via `npm`:
|
||||
|
||||
```shell
|
||||
npm i -g @openai/codex
|
||||
codex
|
||||
```
|
||||
|
||||
You can also install via Homebrew (`brew install --cask codex`) or download a platform-specific release directly from our [GitHub Releases](https://github.com/openai/codex/releases).
|
||||
|
||||
## Documentation quickstart
|
||||
|
||||
- First run with Codex? Start with [`docs/getting-started.md`](../docs/getting-started.md) (links to the walkthrough for prompts, keyboard shortcuts, and session management).
|
||||
- Want deeper control? See [`docs/config.md`](../docs/config.md) and [`docs/install.md`](../docs/install.md).
|
||||
|
||||
## What's new in the Rust CLI
|
||||
|
||||
The Rust implementation is now the maintained Codex CLI and serves as the default experience. It includes a number of features that the legacy TypeScript CLI never supported.
|
||||
|
||||
### Config
|
||||
|
||||
Codex supports a rich set of configuration options. Note that the Rust CLI uses `config.toml` instead of `config.json`. See [`docs/config.md`](../docs/config.md) for details.
|
||||
|
||||
### Model Context Protocol Support
|
||||
|
||||
#### MCP client
|
||||
|
||||
Codex CLI functions as an MCP client that allows the Codex CLI and IDE extension to connect to MCP servers on startup. See the [`configuration documentation`](../docs/config.md#connecting-to-mcp-servers) for details.
|
||||
|
||||
#### MCP server (experimental)
|
||||
|
||||
Codex can be launched as an MCP _server_ by running `codex mcp-server`. This allows _other_ MCP clients to use Codex as a tool for another agent.
|
||||
|
||||
Use the [`@modelcontextprotocol/inspector`](https://github.com/modelcontextprotocol/inspector) to try it out:
|
||||
|
||||
```shell
|
||||
npx @modelcontextprotocol/inspector codex mcp-server
|
||||
```
|
||||
|
||||
Use `codex mcp` to add/list/get/remove MCP server launchers defined in `config.toml`, and `codex mcp-server` to run the MCP server directly.
|
||||
|
||||
### Notifications
|
||||
|
||||
You can enable notifications by configuring a script that is run whenever the agent finishes a turn. The [notify documentation](../docs/config.md#notify) includes a detailed example that explains how to get desktop notifications via [terminal-notifier](https://github.com/julienXX/terminal-notifier) on macOS. When Codex detects that it is running under WSL 2 inside Windows Terminal (`WT_SESSION` is set), the TUI automatically falls back to native Windows toast notifications so approval prompts and completed turns surface even though Windows Terminal does not implement OSC 9.
|
||||
|
||||
### `codex exec` to run Codex programmatically/non-interactively
|
||||
|
||||
To run Codex non-interactively, run `codex exec PROMPT` (you can also pass the prompt via `stdin`) and Codex will work on your task until it decides that it is done and exits. If you provide both a prompt argument and piped stdin, Codex appends stdin as a `<stdin>` block after the prompt so patterns like `echo "my output" | codex exec "Summarize this concisely"` work naturally. Output is printed to the terminal directly. You can set the `RUST_LOG` environment variable to see more about what's going on.
|
||||
Use `codex exec --ephemeral ...` to run without persisting session rollout files to disk.
|
||||
|
||||
### Experimenting with the Codex Sandbox
|
||||
|
||||
To test to see what happens when a command is run under the sandbox provided by Codex, use the `sandbox` subcommand in Codex CLI:
|
||||
|
||||
```
|
||||
# Uses the sandbox implementation for the current host OS:
|
||||
# Seatbelt on macOS, the Linux sandbox on Linux, and Windows restricted token on Windows.
|
||||
codex sandbox [COMMAND]...
|
||||
|
||||
# macOS-only diagnostic option
|
||||
codex sandbox --log-denials [COMMAND]...
|
||||
```
|
||||
|
||||
`codex sandbox` also accepts `--profile NAME` (`-p NAME`) to layer
|
||||
`$CODEX_HOME/NAME.config.toml` onto the base user config for the sandboxed
|
||||
command.
|
||||
|
||||
### Selecting a sandbox policy via `--sandbox`
|
||||
|
||||
The Rust CLI exposes a dedicated `--sandbox` (`-s`) flag that lets you pick the sandbox policy **without** having to reach for the generic `-c/--config` option:
|
||||
|
||||
```shell
|
||||
# Run Codex with the default, read-only sandbox
|
||||
codex --sandbox read-only
|
||||
|
||||
# Allow the agent to write within the current workspace while still blocking network access
|
||||
codex --sandbox workspace-write
|
||||
|
||||
# Danger! Disable sandboxing entirely (only do this if you are already running in a container or other isolated env)
|
||||
codex --sandbox danger-full-access
|
||||
```
|
||||
|
||||
In `workspace-write`, Codex also includes `~/.codex/memories` in its writable roots so memory maintenance does not require an extra approval.
|
||||
|
||||
## Code Organization
|
||||
|
||||
This folder is the root of a Cargo workspace. It contains quite a bit of experimental code, but here are the key crates:
|
||||
|
||||
- [`core/`](./core) contains the business logic for Codex. Ultimately, we hope this becomes a library crate that is generally useful for building other Rust/native applications that use Codex.
|
||||
- [`exec/`](./exec) "headless" CLI for use in automation.
|
||||
- [`tui/`](./tui) CLI that launches a fullscreen TUI built with [Ratatui](https://ratatui.rs/).
|
||||
- [`cli/`](./cli) CLI multitool that provides the aforementioned CLIs via subcommands.
|
||||
|
||||
If you want to contribute or inspect behavior in detail, start by reading the module-level `README.md` files under each crate and run the project workspace from the top-level `codex-rs` directory so shared config, features, and build scripts stay aligned.
|
||||
[**Codex CLI Documentation**](https://developers.openai.com/codex/cli)
|
||||
|
||||
@@ -63,6 +63,8 @@ use crate::facts::SubAgentThreadStartedInput;
|
||||
use crate::facts::ThreadInitializationMode;
|
||||
use crate::facts::TrackEventsContext;
|
||||
use crate::facts::TurnCodexErrorFact;
|
||||
use crate::facts::TurnProfile;
|
||||
use crate::facts::TurnProfileFact;
|
||||
use crate::facts::TurnResolvedConfigFact;
|
||||
use crate::facts::TurnStatus;
|
||||
use crate::facts::TurnSteerRequestError;
|
||||
@@ -396,6 +398,18 @@ fn sample_turn_resolved_config(thread_id: &str, turn_id: &str) -> TurnResolvedCo
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_turn_profile() -> TurnProfile {
|
||||
TurnProfile {
|
||||
before_first_sampling_ms: 100,
|
||||
sampling_ms: 700,
|
||||
between_sampling_overhead_ms: 50,
|
||||
tool_blocking_ms: 250,
|
||||
after_last_sampling_ms: 134,
|
||||
sampling_request_count: 2,
|
||||
sampling_retry_count: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_turn_steer_request(
|
||||
thread_id: &str,
|
||||
expected_turn_id: &str,
|
||||
@@ -649,6 +663,18 @@ async fn ingest_turn_prerequisites(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
reducer
|
||||
.ingest(
|
||||
AnalyticsFact::Custom(CustomAnalyticsFact::TurnProfile(Box::new(
|
||||
TurnProfileFact {
|
||||
turn_id: "turn-2".to_string(),
|
||||
profile: sample_turn_profile(),
|
||||
},
|
||||
))),
|
||||
out,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn ingest_review_prerequisites(
|
||||
@@ -1227,6 +1253,8 @@ fn compaction_event_serializes_expected_shape() {
|
||||
error: None,
|
||||
active_context_tokens_before: 120_000,
|
||||
active_context_tokens_after: 18_000,
|
||||
retained_image_count: None,
|
||||
compaction_summary_tokens: None,
|
||||
started_at: 100,
|
||||
completed_at: 106,
|
||||
duration_ms: Some(6543),
|
||||
@@ -1275,6 +1303,8 @@ fn compaction_event_serializes_expected_shape() {
|
||||
"error": null,
|
||||
"active_context_tokens_before": 120000,
|
||||
"active_context_tokens_after": 18000,
|
||||
"retained_image_count": null,
|
||||
"compaction_summary_tokens": null,
|
||||
"started_at": 100,
|
||||
"completed_at": 106,
|
||||
"duration_ms": 6543
|
||||
@@ -1347,6 +1377,7 @@ fn thread_initialized_event_serializes_expected_shape() {
|
||||
initialization_mode: ThreadInitializationMode::New,
|
||||
subagent_source: None,
|
||||
parent_thread_id: None,
|
||||
forked_from_thread_id: None,
|
||||
created_at: 1,
|
||||
},
|
||||
});
|
||||
@@ -1379,6 +1410,7 @@ fn thread_initialized_event_serializes_expected_shape() {
|
||||
"initialization_mode": "new",
|
||||
"subagent_source": null,
|
||||
"parent_thread_id": null,
|
||||
"forked_from_thread_id": null,
|
||||
"created_at": 1
|
||||
}
|
||||
})
|
||||
@@ -1793,6 +1825,8 @@ async fn compaction_event_ingests_custom_fact() {
|
||||
error: Some("context limit exceeded".to_string()),
|
||||
active_context_tokens_before: 131_000,
|
||||
active_context_tokens_after: 131_000,
|
||||
retained_image_count: None,
|
||||
compaction_summary_tokens: None,
|
||||
started_at: 100,
|
||||
completed_at: 101,
|
||||
duration_ms: Some(1200),
|
||||
@@ -2429,6 +2463,7 @@ fn subagent_thread_started_review_serializes_expected_shape() {
|
||||
session_id: "session-root".to_string(),
|
||||
thread_id: "thread-review".to_string(),
|
||||
parent_thread_id: None,
|
||||
forked_from_thread_id: None,
|
||||
product_client_id: "codex-tui".to_string(),
|
||||
client_name: "codex-tui".to_string(),
|
||||
client_version: "1.0.0".to_string(),
|
||||
@@ -2461,18 +2496,26 @@ fn subagent_thread_started_review_serializes_expected_shape() {
|
||||
assert_eq!(payload["event_params"]["initialization_mode"], "new");
|
||||
assert_eq!(payload["event_params"]["subagent_source"], "review");
|
||||
assert_eq!(payload["event_params"]["parent_thread_id"], json!(null));
|
||||
assert_eq!(
|
||||
payload["event_params"]["forked_from_thread_id"],
|
||||
json!(null)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subagent_thread_started_thread_spawn_serializes_parent_thread_id() {
|
||||
fn subagent_thread_started_thread_spawn_serializes_thread_lineage() {
|
||||
let parent_thread_id =
|
||||
codex_protocol::ThreadId::from_string("11111111-1111-1111-1111-111111111111")
|
||||
.expect("valid thread id");
|
||||
let forked_from_thread_id =
|
||||
codex_protocol::ThreadId::from_string("22222222-2222-4222-8222-222222222222")
|
||||
.expect("valid thread id");
|
||||
let event = TrackEventRequest::ThreadInitialized(subagent_thread_started_event_request(
|
||||
SubAgentThreadStartedInput {
|
||||
session_id: "session-root".to_string(),
|
||||
thread_id: "thread-spawn".to_string(),
|
||||
parent_thread_id: Some(parent_thread_id.to_string()),
|
||||
forked_from_thread_id: Some(forked_from_thread_id.to_string()),
|
||||
product_client_id: "codex-tui".to_string(),
|
||||
client_name: "codex-tui".to_string(),
|
||||
client_version: "1.0.0".to_string(),
|
||||
@@ -2497,6 +2540,10 @@ fn subagent_thread_started_thread_spawn_serializes_parent_thread_id() {
|
||||
payload["event_params"]["parent_thread_id"],
|
||||
"11111111-1111-1111-1111-111111111111"
|
||||
);
|
||||
assert_eq!(
|
||||
payload["event_params"]["forked_from_thread_id"],
|
||||
"22222222-2222-4222-8222-222222222222"
|
||||
);
|
||||
assert_eq!(payload["event_params"]["session_id"], "session-root");
|
||||
}
|
||||
|
||||
@@ -2507,6 +2554,7 @@ fn subagent_thread_started_memory_consolidation_serializes_expected_shape() {
|
||||
session_id: "session-root".to_string(),
|
||||
thread_id: "thread-memory".to_string(),
|
||||
parent_thread_id: None,
|
||||
forked_from_thread_id: None,
|
||||
product_client_id: "codex-tui".to_string(),
|
||||
client_name: "codex-tui".to_string(),
|
||||
client_version: "1.0.0".to_string(),
|
||||
@@ -2533,6 +2581,7 @@ fn subagent_thread_started_other_serializes_expected_shape() {
|
||||
session_id: "session-root".to_string(),
|
||||
thread_id: "thread-guardian".to_string(),
|
||||
parent_thread_id: None,
|
||||
forked_from_thread_id: None,
|
||||
product_client_id: "codex-tui".to_string(),
|
||||
client_name: "codex-tui".to_string(),
|
||||
client_version: "1.0.0".to_string(),
|
||||
@@ -2558,6 +2607,7 @@ fn subagent_thread_started_other_serializes_explicit_parent_thread_id() {
|
||||
session_id: "session-root".to_string(),
|
||||
thread_id: "thread-guardian".to_string(),
|
||||
parent_thread_id: Some(parent_thread_id.to_string()),
|
||||
forked_from_thread_id: None,
|
||||
product_client_id: "codex-tui".to_string(),
|
||||
client_name: "codex-tui".to_string(),
|
||||
client_version: "1.0.0".to_string(),
|
||||
@@ -2588,6 +2638,7 @@ async fn subagent_thread_started_publishes_without_initialize() {
|
||||
session_id: "session-root".to_string(),
|
||||
thread_id: "thread-review".to_string(),
|
||||
parent_thread_id: None,
|
||||
forked_from_thread_id: None,
|
||||
product_client_id: "codex-tui".to_string(),
|
||||
client_name: "codex-tui".to_string(),
|
||||
client_version: "1.0.0".to_string(),
|
||||
@@ -2662,6 +2713,7 @@ async fn subagent_thread_started_inherits_parent_connection_for_new_thread() {
|
||||
session_id: "session-root".to_string(),
|
||||
thread_id: "thread-review".to_string(),
|
||||
parent_thread_id: Some(parent_thread_id.to_string()),
|
||||
forked_from_thread_id: None,
|
||||
product_client_id: "parent-client".to_string(),
|
||||
client_name: "parent-client".to_string(),
|
||||
client_version: "1.0.0".to_string(),
|
||||
@@ -2697,6 +2749,8 @@ async fn subagent_thread_started_inherits_parent_connection_for_new_thread() {
|
||||
error: None,
|
||||
active_context_tokens_before: 131_000,
|
||||
active_context_tokens_after: 64_000,
|
||||
retained_image_count: None,
|
||||
compaction_summary_tokens: None,
|
||||
started_at: 100,
|
||||
completed_at: 101,
|
||||
duration_ms: Some(1200),
|
||||
@@ -2732,6 +2786,7 @@ async fn subagent_tool_items_inherit_parent_connection_metadata() {
|
||||
session_id: "session-root".to_string(),
|
||||
thread_id: "thread-subagent".to_string(),
|
||||
parent_thread_id: Some("thread-1".to_string()),
|
||||
forked_from_thread_id: None,
|
||||
product_client_id: "codex-tui".to_string(),
|
||||
client_name: "codex-tui".to_string(),
|
||||
client_version: "1.0.0".to_string(),
|
||||
@@ -2828,6 +2883,7 @@ fn plugin_used_event_serializes_expected_shape() {
|
||||
"mcp_server_count": 2,
|
||||
"connector_ids": ["calendar", "drive"],
|
||||
"product_client_id": originator().value,
|
||||
"mcp_server_names": ["mcp-1", "mcp-2"],
|
||||
"thread_id": "thread-3",
|
||||
"turn_id": "turn-3",
|
||||
"model_slug": "gpt-5"
|
||||
@@ -3262,7 +3318,6 @@ fn turn_event_serializes_expected_shape() {
|
||||
status: Some(TurnStatus::Completed),
|
||||
turn_error: None,
|
||||
codex_error_kind: None,
|
||||
codex_error_subreason: None,
|
||||
codex_error_http_status_code: None,
|
||||
steer_count: Some(0),
|
||||
total_tool_call_count: None,
|
||||
@@ -3278,6 +3333,13 @@ fn turn_event_serializes_expected_shape() {
|
||||
output_tokens: None,
|
||||
reasoning_output_tokens: None,
|
||||
total_tokens: None,
|
||||
before_first_sampling_ms: 100,
|
||||
sampling_ms: 700,
|
||||
between_sampling_overhead_ms: 50,
|
||||
tool_blocking_ms: 250,
|
||||
after_last_sampling_ms: 134,
|
||||
sampling_request_count: 2,
|
||||
sampling_retry_count: 1,
|
||||
duration_ms: Some(1234),
|
||||
started_at: Some(455),
|
||||
completed_at: Some(456),
|
||||
@@ -3328,7 +3390,6 @@ fn turn_event_serializes_expected_shape() {
|
||||
"status": "completed",
|
||||
"turn_error": null,
|
||||
"codex_error_kind": null,
|
||||
"codex_error_subreason": null,
|
||||
"codex_error_http_status_code": null,
|
||||
"steer_count": 0,
|
||||
"total_tool_call_count": null,
|
||||
@@ -3344,6 +3405,13 @@ fn turn_event_serializes_expected_shape() {
|
||||
"output_tokens": null,
|
||||
"reasoning_output_tokens": null,
|
||||
"total_tokens": null,
|
||||
"before_first_sampling_ms": 100,
|
||||
"sampling_ms": 700,
|
||||
"between_sampling_overhead_ms": 50,
|
||||
"tool_blocking_ms": 250,
|
||||
"after_last_sampling_ms": 134,
|
||||
"sampling_request_count": 2,
|
||||
"sampling_retry_count": 1,
|
||||
"duration_ms": 1234,
|
||||
"started_at": 455,
|
||||
"completed_at": 456
|
||||
@@ -4028,10 +4096,6 @@ async fn turn_lifecycle_emits_failed_turn_event() {
|
||||
payload["event_params"]["codex_error_kind"],
|
||||
json!("invalid_request")
|
||||
);
|
||||
assert_eq!(
|
||||
payload["event_params"]["codex_error_subreason"],
|
||||
json!("unknown turn environment id `env-2`")
|
||||
);
|
||||
assert_eq!(
|
||||
payload["event_params"]["codex_error_http_status_code"],
|
||||
json!(null)
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::facts::SkillInvokedInput;
|
||||
use crate::facts::SubAgentThreadStartedInput;
|
||||
use crate::facts::TrackEventsContext;
|
||||
use crate::facts::TurnCodexErrorFact;
|
||||
use crate::facts::TurnProfileFact;
|
||||
use crate::facts::TurnResolvedConfigFact;
|
||||
use crate::facts::TurnTokenUsageFact;
|
||||
use crate::reducer::AnalyticsReducer;
|
||||
@@ -257,6 +258,12 @@ impl AnalyticsEventsClient {
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn track_turn_profile(&self, fact: TurnProfileFact) {
|
||||
self.record_fact(AnalyticsFact::Custom(CustomAnalyticsFact::TurnProfile(
|
||||
Box::new(fact),
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn track_turn_codex_error(&self, fact: TurnCodexErrorFact) {
|
||||
self.record_fact(AnalyticsFact::Custom(CustomAnalyticsFact::TurnCodexError(
|
||||
Box::new(fact),
|
||||
|
||||
@@ -157,6 +157,7 @@ pub(crate) struct ThreadInitializedEventParams {
|
||||
pub(crate) initialization_mode: ThreadInitializationMode,
|
||||
pub(crate) subagent_source: Option<String>,
|
||||
pub(crate) parent_thread_id: Option<String>,
|
||||
pub(crate) forked_from_thread_id: Option<String>,
|
||||
pub(crate) created_at: u64,
|
||||
}
|
||||
|
||||
@@ -757,6 +758,8 @@ pub(crate) struct CodexCompactionEventParams {
|
||||
pub(crate) error: Option<String>,
|
||||
pub(crate) active_context_tokens_before: i64,
|
||||
pub(crate) active_context_tokens_after: i64,
|
||||
pub(crate) retained_image_count: Option<usize>,
|
||||
pub(crate) compaction_summary_tokens: Option<i64>,
|
||||
pub(crate) started_at: u64,
|
||||
pub(crate) completed_at: u64,
|
||||
pub(crate) duration_ms: Option<u64>,
|
||||
@@ -800,7 +803,6 @@ pub(crate) struct CodexTurnEventParams {
|
||||
pub(crate) status: Option<TurnStatus>,
|
||||
pub(crate) turn_error: Option<CodexErrorInfo>,
|
||||
pub(crate) codex_error_kind: Option<CodexErrKind>,
|
||||
pub(crate) codex_error_subreason: Option<String>,
|
||||
pub(crate) codex_error_http_status_code: Option<u16>,
|
||||
pub(crate) steer_count: Option<usize>,
|
||||
pub(crate) total_tool_call_count: Option<usize>,
|
||||
@@ -816,6 +818,13 @@ pub(crate) struct CodexTurnEventParams {
|
||||
pub(crate) output_tokens: Option<i64>,
|
||||
pub(crate) reasoning_output_tokens: Option<i64>,
|
||||
pub(crate) total_tokens: Option<i64>,
|
||||
pub(crate) before_first_sampling_ms: u64,
|
||||
pub(crate) sampling_ms: u64,
|
||||
pub(crate) between_sampling_overhead_ms: u64,
|
||||
pub(crate) tool_blocking_ms: u64,
|
||||
pub(crate) after_last_sampling_ms: u64,
|
||||
pub(crate) sampling_request_count: u32,
|
||||
pub(crate) sampling_retry_count: u32,
|
||||
pub(crate) duration_ms: Option<u64>,
|
||||
pub(crate) started_at: Option<u64>,
|
||||
pub(crate) completed_at: Option<u64>,
|
||||
@@ -865,6 +874,7 @@ pub(crate) struct CodexPluginMetadata {
|
||||
pub(crate) struct CodexPluginUsedMetadata {
|
||||
#[serde(flatten)]
|
||||
pub(crate) plugin: CodexPluginMetadata,
|
||||
pub(crate) mcp_server_names: Option<Vec<String>>,
|
||||
pub(crate) thread_id: Option<String>,
|
||||
pub(crate) turn_id: Option<String>,
|
||||
pub(crate) model_slug: Option<String>,
|
||||
@@ -961,6 +971,8 @@ pub(crate) fn codex_compaction_event_params(
|
||||
error: input.error,
|
||||
active_context_tokens_before: input.active_context_tokens_before,
|
||||
active_context_tokens_after: input.active_context_tokens_after,
|
||||
retained_image_count: input.retained_image_count,
|
||||
compaction_summary_tokens: input.compaction_summary_tokens,
|
||||
started_at: input.started_at,
|
||||
completed_at: input.completed_at,
|
||||
duration_ms: input.duration_ms,
|
||||
@@ -971,8 +983,13 @@ pub(crate) fn codex_plugin_used_metadata(
|
||||
tracking: &TrackEventsContext,
|
||||
plugin: PluginTelemetryMetadata,
|
||||
) -> CodexPluginUsedMetadata {
|
||||
let mcp_server_names = plugin
|
||||
.capability_summary
|
||||
.as_ref()
|
||||
.map(|summary| summary.mcp_server_names.clone());
|
||||
CodexPluginUsedMetadata {
|
||||
plugin: codex_plugin_metadata(plugin),
|
||||
mcp_server_names,
|
||||
thread_id: Some(tracking.thread_id.clone()),
|
||||
turn_id: Some(tracking.turn_id.clone()),
|
||||
model_slug: Some(tracking.model_slug.clone()),
|
||||
@@ -1054,6 +1071,7 @@ pub(crate) fn subagent_thread_started_event_request(
|
||||
initialization_mode: ThreadInitializationMode::New,
|
||||
subagent_source: Some(subagent_source_name(&input.subagent_source)),
|
||||
parent_thread_id: input.parent_thread_id,
|
||||
forked_from_thread_id: input.forked_from_thread_id,
|
||||
created_at: input.created_at,
|
||||
};
|
||||
ThreadInitializedEvent {
|
||||
|
||||
@@ -30,9 +30,6 @@ use codex_protocol::request_permissions::RequestPermissionsResponse;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const INVALID_REQUEST_SUBREASON_MAX_BYTES: usize = 512;
|
||||
const INVALID_REQUEST_SUBREASON_TRUNCATION_SUFFIX: &str = "...";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
pub struct AcceptedLineFingerprint {
|
||||
pub path_hash: String,
|
||||
@@ -104,6 +101,23 @@ pub struct TurnTokenUsageFact {
|
||||
pub token_usage: TokenUsage,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
pub struct TurnProfile {
|
||||
pub before_first_sampling_ms: u64,
|
||||
pub sampling_ms: u64,
|
||||
pub between_sampling_overhead_ms: u64,
|
||||
pub tool_blocking_ms: u64,
|
||||
pub after_last_sampling_ms: u64,
|
||||
pub sampling_request_count: u32,
|
||||
pub sampling_retry_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TurnProfileFact {
|
||||
pub turn_id: String,
|
||||
pub profile: TurnProfile,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TurnCodexErrorFact {
|
||||
pub(crate) turn_id: String,
|
||||
@@ -165,7 +179,6 @@ pub(crate) enum CodexErrKind {
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct TurnCodexError {
|
||||
pub(crate) kind: CodexErrKind,
|
||||
pub(crate) subreason: Option<String>,
|
||||
pub(crate) http_status_code: Option<u16>,
|
||||
}
|
||||
|
||||
@@ -173,26 +186,6 @@ impl TurnCodexError {
|
||||
fn from_codex_err(error: &CodexErr) -> Self {
|
||||
Self {
|
||||
kind: error.into(),
|
||||
subreason: match error {
|
||||
CodexErr::InvalidRequest(message) => {
|
||||
// InvalidRequest can contain raw provider response bodies, so bound the
|
||||
// analytics copy without changing the source CodexErr.
|
||||
let subreason = if message.len() <= INVALID_REQUEST_SUBREASON_MAX_BYTES {
|
||||
message.clone()
|
||||
} else {
|
||||
let truncated_len = message.floor_char_boundary(
|
||||
INVALID_REQUEST_SUBREASON_MAX_BYTES
|
||||
.saturating_sub(INVALID_REQUEST_SUBREASON_TRUNCATION_SUFFIX.len()),
|
||||
);
|
||||
format!(
|
||||
"{}{INVALID_REQUEST_SUBREASON_TRUNCATION_SUFFIX}",
|
||||
&message[..truncated_len]
|
||||
)
|
||||
};
|
||||
Some(subreason)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
http_status_code: error.http_status_code_value(),
|
||||
}
|
||||
}
|
||||
@@ -348,6 +341,7 @@ pub struct SubAgentThreadStartedInput {
|
||||
pub session_id: String,
|
||||
pub thread_id: String,
|
||||
pub parent_thread_id: Option<String>,
|
||||
pub forked_from_thread_id: Option<String>,
|
||||
pub product_client_id: String,
|
||||
pub client_name: String,
|
||||
pub client_version: String,
|
||||
@@ -416,6 +410,8 @@ pub struct CodexCompactionEvent {
|
||||
pub error: Option<String>,
|
||||
pub active_context_tokens_before: i64,
|
||||
pub active_context_tokens_after: i64,
|
||||
pub retained_image_count: Option<usize>,
|
||||
pub compaction_summary_tokens: Option<i64>,
|
||||
pub started_at: u64,
|
||||
pub completed_at: u64,
|
||||
pub duration_ms: Option<u64>,
|
||||
@@ -475,6 +471,7 @@ pub(crate) enum CustomAnalyticsFact {
|
||||
GuardianReview(Box<GuardianReviewEventParams>),
|
||||
TurnResolvedConfig(Box<TurnResolvedConfigFact>),
|
||||
TurnTokenUsage(Box<TurnTokenUsageFact>),
|
||||
TurnProfile(Box<TurnProfileFact>),
|
||||
TurnCodexError(Box<TurnCodexErrorFact>),
|
||||
SkillInvoked(SkillInvokedInput),
|
||||
AppMentioned(AppMentionedInput),
|
||||
|
||||
@@ -39,6 +39,8 @@ pub use facts::SubAgentThreadStartedInput;
|
||||
pub use facts::ThreadInitializationMode;
|
||||
pub use facts::TrackEventsContext;
|
||||
pub use facts::TurnCodexErrorFact;
|
||||
pub use facts::TurnProfile;
|
||||
pub use facts::TurnProfileFact;
|
||||
pub use facts::TurnResolvedConfigFact;
|
||||
pub use facts::TurnStatus;
|
||||
pub use facts::TurnSteerRejectionReason;
|
||||
|
||||
@@ -72,6 +72,8 @@ use crate::facts::SubAgentThreadStartedInput;
|
||||
use crate::facts::ThreadInitializationMode;
|
||||
use crate::facts::TurnCodexError;
|
||||
use crate::facts::TurnCodexErrorFact;
|
||||
use crate::facts::TurnProfile;
|
||||
use crate::facts::TurnProfileFact;
|
||||
use crate::facts::TurnResolvedConfigFact;
|
||||
use crate::facts::TurnStatus;
|
||||
use crate::facts::TurnSteerRejectionReason;
|
||||
@@ -316,6 +318,7 @@ struct CompletedTurnState {
|
||||
duration_ms: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TurnState {
|
||||
connection_id: Option<u64>,
|
||||
thread_id: Option<String>,
|
||||
@@ -323,6 +326,7 @@ struct TurnState {
|
||||
resolved_config: Option<TurnResolvedConfigFact>,
|
||||
started_at: Option<u64>,
|
||||
token_usage: Option<TokenUsage>,
|
||||
profile: Option<TurnProfile>,
|
||||
completed: Option<CompletedTurnState>,
|
||||
codex_error: Option<TurnCodexError>,
|
||||
latest_diff: Option<String>,
|
||||
@@ -464,6 +468,9 @@ impl AnalyticsReducer {
|
||||
CustomAnalyticsFact::TurnTokenUsage(input) => {
|
||||
self.ingest_turn_token_usage(*input, out).await;
|
||||
}
|
||||
CustomAnalyticsFact::TurnProfile(input) => {
|
||||
self.ingest_turn_profile(*input, out).await;
|
||||
}
|
||||
CustomAnalyticsFact::TurnCodexError(input) => {
|
||||
self.ingest_turn_codex_error(*input);
|
||||
}
|
||||
@@ -604,19 +611,7 @@ impl AnalyticsReducer {
|
||||
let turn_id = input.turn_id.clone();
|
||||
let thread_id = input.thread_id.clone();
|
||||
let num_input_images = input.num_input_images;
|
||||
let turn_state = self.turns.entry(turn_id.clone()).or_insert(TurnState {
|
||||
connection_id: None,
|
||||
thread_id: None,
|
||||
num_input_images: None,
|
||||
resolved_config: None,
|
||||
started_at: None,
|
||||
token_usage: None,
|
||||
completed: None,
|
||||
codex_error: None,
|
||||
latest_diff: None,
|
||||
steer_count: 0,
|
||||
tool_counts: TurnToolCounts::default(),
|
||||
});
|
||||
let turn_state = self.turns.entry(turn_id.clone()).or_default();
|
||||
turn_state.thread_id = Some(thread_id);
|
||||
turn_state.num_input_images = Some(num_input_images);
|
||||
turn_state.resolved_config = Some(input);
|
||||
@@ -629,43 +624,30 @@ impl AnalyticsReducer {
|
||||
out: &mut Vec<TrackEventRequest>,
|
||||
) {
|
||||
let turn_id = input.turn_id.clone();
|
||||
let turn_state = self.turns.entry(turn_id.clone()).or_insert(TurnState {
|
||||
connection_id: None,
|
||||
thread_id: None,
|
||||
num_input_images: None,
|
||||
resolved_config: None,
|
||||
started_at: None,
|
||||
token_usage: None,
|
||||
completed: None,
|
||||
codex_error: None,
|
||||
latest_diff: None,
|
||||
steer_count: 0,
|
||||
tool_counts: TurnToolCounts::default(),
|
||||
});
|
||||
let turn_state = self.turns.entry(turn_id.clone()).or_default();
|
||||
turn_state.thread_id = Some(input.thread_id);
|
||||
turn_state.token_usage = Some(input.token_usage);
|
||||
self.maybe_emit_turn_event(&turn_id, out).await;
|
||||
}
|
||||
|
||||
async fn ingest_turn_profile(
|
||||
&mut self,
|
||||
input: TurnProfileFact,
|
||||
out: &mut Vec<TrackEventRequest>,
|
||||
) {
|
||||
let TurnProfileFact { turn_id, profile } = input;
|
||||
let turn_state = self.turns.entry(turn_id.clone()).or_default();
|
||||
turn_state.profile = Some(profile);
|
||||
self.maybe_emit_turn_event(&turn_id, out).await;
|
||||
}
|
||||
|
||||
fn ingest_turn_codex_error(&mut self, input: TurnCodexErrorFact) {
|
||||
let TurnCodexErrorFact {
|
||||
turn_id,
|
||||
thread_id,
|
||||
error,
|
||||
} = input;
|
||||
let turn_state = self.turns.entry(turn_id).or_insert(TurnState {
|
||||
connection_id: None,
|
||||
thread_id: None,
|
||||
num_input_images: None,
|
||||
resolved_config: None,
|
||||
started_at: None,
|
||||
token_usage: None,
|
||||
completed: None,
|
||||
codex_error: None,
|
||||
latest_diff: None,
|
||||
steer_count: 0,
|
||||
tool_counts: TurnToolCounts::default(),
|
||||
});
|
||||
let turn_state = self.turns.entry(turn_id).or_default();
|
||||
turn_state.thread_id.get_or_insert(thread_id);
|
||||
turn_state.codex_error = Some(error);
|
||||
}
|
||||
@@ -818,19 +800,7 @@ impl AnalyticsReducer {
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let turn_state = self.turns.entry(turn_id.clone()).or_insert(TurnState {
|
||||
connection_id: None,
|
||||
thread_id: None,
|
||||
num_input_images: None,
|
||||
resolved_config: None,
|
||||
started_at: None,
|
||||
token_usage: None,
|
||||
completed: None,
|
||||
codex_error: None,
|
||||
latest_diff: None,
|
||||
steer_count: 0,
|
||||
tool_counts: TurnToolCounts::default(),
|
||||
});
|
||||
let turn_state = self.turns.entry(turn_id.clone()).or_default();
|
||||
turn_state.connection_id = Some(connection_id);
|
||||
turn_state.thread_id = Some(pending_request.thread_id);
|
||||
turn_state.num_input_images = Some(pending_request.num_input_images);
|
||||
@@ -1178,61 +1148,19 @@ impl AnalyticsReducer {
|
||||
self.ingest_guardian_review_completed(notification, out);
|
||||
}
|
||||
ServerNotification::TurnStarted(notification) => {
|
||||
let turn_state = self.turns.entry(notification.turn.id).or_insert(TurnState {
|
||||
connection_id: None,
|
||||
thread_id: None,
|
||||
num_input_images: None,
|
||||
resolved_config: None,
|
||||
started_at: None,
|
||||
token_usage: None,
|
||||
completed: None,
|
||||
codex_error: None,
|
||||
latest_diff: None,
|
||||
steer_count: 0,
|
||||
tool_counts: TurnToolCounts::default(),
|
||||
});
|
||||
let turn_state = self.turns.entry(notification.turn.id).or_default();
|
||||
turn_state.started_at = notification
|
||||
.turn
|
||||
.started_at
|
||||
.and_then(|started_at| u64::try_from(started_at).ok());
|
||||
}
|
||||
ServerNotification::TurnDiffUpdated(notification) => {
|
||||
let turn_state =
|
||||
self.turns
|
||||
.entry(notification.turn_id.clone())
|
||||
.or_insert(TurnState {
|
||||
connection_id: None,
|
||||
thread_id: None,
|
||||
num_input_images: None,
|
||||
resolved_config: None,
|
||||
started_at: None,
|
||||
token_usage: None,
|
||||
completed: None,
|
||||
codex_error: None,
|
||||
latest_diff: None,
|
||||
steer_count: 0,
|
||||
tool_counts: TurnToolCounts::default(),
|
||||
});
|
||||
let turn_state = self.turns.entry(notification.turn_id.clone()).or_default();
|
||||
turn_state.thread_id = Some(notification.thread_id);
|
||||
turn_state.latest_diff = Some(notification.diff);
|
||||
}
|
||||
ServerNotification::TurnCompleted(notification) => {
|
||||
let turn_state =
|
||||
self.turns
|
||||
.entry(notification.turn.id.clone())
|
||||
.or_insert(TurnState {
|
||||
connection_id: None,
|
||||
thread_id: None,
|
||||
num_input_images: None,
|
||||
resolved_config: None,
|
||||
started_at: None,
|
||||
token_usage: None,
|
||||
completed: None,
|
||||
codex_error: None,
|
||||
latest_diff: None,
|
||||
steer_count: 0,
|
||||
tool_counts: TurnToolCounts::default(),
|
||||
});
|
||||
let turn_state = self.turns.entry(notification.turn.id.clone()).or_default();
|
||||
turn_state.completed = Some(CompletedTurnState {
|
||||
status: analytics_turn_status(notification.turn.status),
|
||||
turn_error: notification
|
||||
@@ -1268,6 +1196,7 @@ impl AnalyticsReducer {
|
||||
let session_id = thread.session_id;
|
||||
let thread_id = thread.id;
|
||||
let parent_thread_id = thread.parent_thread_id;
|
||||
let forked_from_thread_id = thread.forked_from_id;
|
||||
let Some(connection_state) = self.connections.get(&connection_id) else {
|
||||
return;
|
||||
};
|
||||
@@ -1299,6 +1228,7 @@ impl AnalyticsReducer {
|
||||
initialization_mode,
|
||||
subagent_source: thread_metadata.subagent_source.clone(),
|
||||
parent_thread_id: thread_metadata.parent_thread_id,
|
||||
forked_from_thread_id,
|
||||
created_at: u64::try_from(thread.created_at).unwrap_or_default(),
|
||||
},
|
||||
},
|
||||
@@ -1509,6 +1439,7 @@ impl AnalyticsReducer {
|
||||
if turn_state.thread_id.is_none()
|
||||
|| turn_state.num_input_images.is_none()
|
||||
|| turn_state.resolved_config.is_none()
|
||||
|| turn_state.profile.is_none()
|
||||
|| turn_state.completed.is_none()
|
||||
{
|
||||
return;
|
||||
@@ -2455,12 +2386,20 @@ fn codex_turn_event_params(
|
||||
turn_state: &TurnState,
|
||||
thread_metadata: &ThreadMetadataState,
|
||||
) -> CodexTurnEventParams {
|
||||
let (Some(thread_id), Some(num_input_images), Some(resolved_config), Some(completed)) = (
|
||||
let (
|
||||
Some(thread_id),
|
||||
Some(num_input_images),
|
||||
Some(resolved_config),
|
||||
Some(profile),
|
||||
Some(completed),
|
||||
) = (
|
||||
turn_state.thread_id.clone(),
|
||||
turn_state.num_input_images,
|
||||
turn_state.resolved_config.clone(),
|
||||
turn_state.profile.clone(),
|
||||
turn_state.completed.clone(),
|
||||
) else {
|
||||
)
|
||||
else {
|
||||
unreachable!("turn event params require a fully populated turn state");
|
||||
};
|
||||
let started_at = turn_state.started_at;
|
||||
@@ -2486,6 +2425,15 @@ fn codex_turn_event_params(
|
||||
workspace_kind,
|
||||
is_first_turn,
|
||||
} = resolved_config;
|
||||
let TurnProfile {
|
||||
before_first_sampling_ms,
|
||||
sampling_ms,
|
||||
between_sampling_overhead_ms,
|
||||
tool_blocking_ms,
|
||||
after_last_sampling_ms,
|
||||
sampling_request_count,
|
||||
sampling_retry_count,
|
||||
} = profile;
|
||||
let token_usage = turn_state.token_usage.clone();
|
||||
let codex_error = turn_state.codex_error.as_ref();
|
||||
CodexTurnEventParams {
|
||||
@@ -2522,7 +2470,6 @@ fn codex_turn_event_params(
|
||||
status: completed.status,
|
||||
turn_error: completed.turn_error,
|
||||
codex_error_kind: codex_error.map(|error| error.kind),
|
||||
codex_error_subreason: codex_error.and_then(|error| error.subreason.clone()),
|
||||
codex_error_http_status_code: codex_error.and_then(|error| error.http_status_code),
|
||||
steer_count: Some(turn_state.steer_count),
|
||||
total_tool_call_count: Some(turn_state.tool_counts.total),
|
||||
@@ -2548,6 +2495,13 @@ fn codex_turn_event_params(
|
||||
total_tokens: token_usage
|
||||
.as_ref()
|
||||
.map(|token_usage| token_usage.total_tokens),
|
||||
before_first_sampling_ms,
|
||||
sampling_ms,
|
||||
between_sampling_overhead_ms,
|
||||
tool_blocking_ms,
|
||||
after_last_sampling_ms,
|
||||
sampling_request_count,
|
||||
sampling_retry_count,
|
||||
duration_ms: completed.duration_ms,
|
||||
started_at,
|
||||
completed_at: Some(completed.completed_at),
|
||||
|
||||
+80
-9
@@ -34,6 +34,30 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"encrypted_content": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"encrypted_content"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContentType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"encrypted_content",
|
||||
"type"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContent",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApprovalsReviewer": {
|
||||
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
|
||||
"enum": [
|
||||
@@ -1971,15 +1995,8 @@
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningItemContent": {
|
||||
@@ -2127,6 +2144,37 @@
|
||||
"title": "MessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AgentMessageInputContent"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"agent_message"
|
||||
],
|
||||
"title": "AgentMessageResponseItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"author",
|
||||
"content",
|
||||
"recipient",
|
||||
"type"
|
||||
],
|
||||
"title": "AgentMessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"content": {
|
||||
@@ -5921,6 +5969,29 @@
|
||||
"title": "Account/rateLimits/readRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/RequestId"
|
||||
},
|
||||
"method": {
|
||||
"enum": [
|
||||
"account/usage/read"
|
||||
],
|
||||
"title": "Account/usage/readRequestMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"type": "null"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"method"
|
||||
],
|
||||
"title": "Account/usage/readRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+3
-3
@@ -288,8 +288,8 @@
|
||||
"environmentId": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemId": {
|
||||
@@ -326,4 +326,4 @@
|
||||
],
|
||||
"title": "PermissionsRequestApprovalParams",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@@ -525,6 +525,13 @@
|
||||
"agentIdentity"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Programmatic Codex auth backed by a personal access token.",
|
||||
"enum": [
|
||||
"personalAccessToken"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2227,6 +2234,12 @@
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/McpServerStartupState"
|
||||
},
|
||||
"threadId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -2777,15 +2790,8 @@
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningSummary": {
|
||||
@@ -4916,6 +4922,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"TurnModerationMetadataNotification": {
|
||||
"properties": {
|
||||
"metadata": true,
|
||||
"threadId": {
|
||||
"type": "string"
|
||||
},
|
||||
"turnId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata",
|
||||
"threadId",
|
||||
"turnId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TurnPlanStep": {
|
||||
"properties": {
|
||||
"status": {
|
||||
@@ -6255,6 +6278,26 @@
|
||||
"title": "Model/verificationNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
"enum": [
|
||||
"turn/moderationMetadata"
|
||||
],
|
||||
"title": "Turn/moderationMetadataNotificationMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/TurnModerationMetadataNotification"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Turn/moderationMetadataNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
|
||||
+3
-3
@@ -1593,8 +1593,8 @@
|
||||
"environmentId": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemId": {
|
||||
@@ -2005,4 +2005,4 @@
|
||||
}
|
||||
],
|
||||
"title": "ServerRequest"
|
||||
}
|
||||
}
|
||||
+293
-16
@@ -1833,6 +1833,29 @@
|
||||
"title": "Account/rateLimits/readRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/v2/RequestId"
|
||||
},
|
||||
"method": {
|
||||
"enum": [
|
||||
"account/usage/read"
|
||||
],
|
||||
"title": "Account/usage/readRequestMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"type": "null"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"method"
|
||||
],
|
||||
"title": "Account/usage/readRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -3786,8 +3809,8 @@
|
||||
"environmentId": {
|
||||
"default": null,
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemId": {
|
||||
@@ -4904,6 +4927,26 @@
|
||||
"title": "Model/verificationNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
"enum": [
|
||||
"turn/moderationMetadata"
|
||||
],
|
||||
"title": "Turn/moderationMetadataNotificationMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/v2/TurnModerationMetadataNotification"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Turn/moderationMetadataNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
@@ -5739,6 +5782,62 @@
|
||||
"title": "AccountRateLimitsUpdatedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"AccountTokenUsageDailyBucket": {
|
||||
"properties": {
|
||||
"startDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokens": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"startDate",
|
||||
"tokens"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AccountTokenUsageSummary": {
|
||||
"properties": {
|
||||
"currentStreakDays": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"lifetimeTokens": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"longestRunningTurnSec": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"longestStreakDays": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"peakDailyTokens": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"AccountUpdatedNotification": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
@@ -5900,6 +5999,30 @@
|
||||
"title": "AgentMessageDeltaNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"encrypted_content": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"encrypted_content"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContentType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"encrypted_content",
|
||||
"type"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContent",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"AgentPath": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -6278,6 +6401,69 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AppTemplateSummary": {
|
||||
"properties": {
|
||||
"canonicalConnectorId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"logoUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"logoUrlDark": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"materializedAppIds": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AppTemplateUnavailableReason"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"materializedAppIds",
|
||||
"name",
|
||||
"templateId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AppTemplateUnavailableReason": {
|
||||
"enum": [
|
||||
"NOT_CONFIGURED_FOR_WORKSPACE",
|
||||
"NO_ACTIVE_WORKSPACE"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"AppToolApproval": {
|
||||
"enum": [
|
||||
"auto",
|
||||
@@ -6491,6 +6677,13 @@
|
||||
"agentIdentity"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Programmatic Codex auth backed by a personal access token.",
|
||||
"enum": [
|
||||
"personalAccessToken"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -7827,12 +8020,12 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"allowedPermissions": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
"allowedPermissionProfiles": {
|
||||
"additionalProperties": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"object",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
@@ -7873,6 +8066,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"defaultPermissions": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"enforceResidency": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -9519,6 +9718,28 @@
|
||||
"title": "GetAccountResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"GetAccountTokenUsageResponse": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"dailyUsageBuckets": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/v2/AccountTokenUsageDailyBucket"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/definitions/v2/AccountTokenUsageSummary"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"summary"
|
||||
],
|
||||
"title": "GetAccountTokenUsageResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"GitInfo": {
|
||||
"properties": {
|
||||
"branch": {
|
||||
@@ -11241,6 +11462,12 @@
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/v2/McpServerStartupState"
|
||||
},
|
||||
"threadId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -12168,6 +12395,12 @@
|
||||
},
|
||||
"PluginDetail": {
|
||||
"properties": {
|
||||
"appTemplates": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/v2/AppTemplateSummary"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"apps": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/v2/AppSummary"
|
||||
@@ -12216,6 +12449,7 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"appTemplates",
|
||||
"apps",
|
||||
"hooks",
|
||||
"marketplaceName",
|
||||
@@ -13525,15 +13759,8 @@
|
||||
"type": "object"
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningEffortOption": {
|
||||
@@ -13987,6 +14214,37 @@
|
||||
"title": "MessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/v2/AgentMessageInputContent"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"agent_message"
|
||||
],
|
||||
"title": "AgentMessageResponseItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"author",
|
||||
"content",
|
||||
"recipient",
|
||||
"type"
|
||||
],
|
||||
"title": "AgentMessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"content": {
|
||||
@@ -18483,6 +18741,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"TurnModerationMetadataNotification": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"metadata": true,
|
||||
"threadId": {
|
||||
"type": "string"
|
||||
},
|
||||
"turnId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata",
|
||||
"threadId",
|
||||
"turnId"
|
||||
],
|
||||
"title": "TurnModerationMetadataNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"TurnPlanStep": {
|
||||
"properties": {
|
||||
"status": {
|
||||
@@ -19239,4 +19516,4 @@
|
||||
},
|
||||
"title": "CodexAppServerProtocol",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
+290
-13
@@ -104,6 +104,62 @@
|
||||
"title": "AccountRateLimitsUpdatedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"AccountTokenUsageDailyBucket": {
|
||||
"properties": {
|
||||
"startDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokens": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"startDate",
|
||||
"tokens"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AccountTokenUsageSummary": {
|
||||
"properties": {
|
||||
"currentStreakDays": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"lifetimeTokens": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"longestRunningTurnSec": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"longestStreakDays": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"peakDailyTokens": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"AccountUpdatedNotification": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
@@ -265,6 +321,30 @@
|
||||
"title": "AgentMessageDeltaNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"encrypted_content": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"encrypted_content"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContentType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"encrypted_content",
|
||||
"type"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContent",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"AgentPath": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -643,6 +723,69 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AppTemplateSummary": {
|
||||
"properties": {
|
||||
"canonicalConnectorId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"logoUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"logoUrlDark": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"materializedAppIds": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AppTemplateUnavailableReason"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"materializedAppIds",
|
||||
"name",
|
||||
"templateId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AppTemplateUnavailableReason": {
|
||||
"enum": [
|
||||
"NOT_CONFIGURED_FOR_WORKSPACE",
|
||||
"NO_ACTIVE_WORKSPACE"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"AppToolApproval": {
|
||||
"enum": [
|
||||
"auto",
|
||||
@@ -856,6 +999,13 @@
|
||||
"agentIdentity"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Programmatic Codex auth backed by a personal access token.",
|
||||
"enum": [
|
||||
"personalAccessToken"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2592,6 +2742,29 @@
|
||||
"title": "Account/rateLimits/readRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/RequestId"
|
||||
},
|
||||
"method": {
|
||||
"enum": [
|
||||
"account/usage/read"
|
||||
],
|
||||
"title": "Account/usage/readRequestMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"type": "null"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"method"
|
||||
],
|
||||
"title": "Account/usage/readRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -4189,12 +4362,12 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"allowedPermissions": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
"allowedPermissionProfiles": {
|
||||
"additionalProperties": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"object",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
@@ -4235,6 +4408,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"defaultPermissions": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"enforceResidency": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -5992,6 +6171,28 @@
|
||||
"title": "GetAccountResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"GetAccountTokenUsageResponse": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"dailyUsageBuckets": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AccountTokenUsageDailyBucket"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/definitions/AccountTokenUsageSummary"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"summary"
|
||||
],
|
||||
"title": "GetAccountTokenUsageResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"GitInfo": {
|
||||
"properties": {
|
||||
"branch": {
|
||||
@@ -7763,6 +7964,12 @@
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/McpServerStartupState"
|
||||
},
|
||||
"threadId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -8690,6 +8897,12 @@
|
||||
},
|
||||
"PluginDetail": {
|
||||
"properties": {
|
||||
"appTemplates": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AppTemplateSummary"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"apps": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AppSummary"
|
||||
@@ -8738,6 +8951,7 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"appTemplates",
|
||||
"apps",
|
||||
"hooks",
|
||||
"marketplaceName",
|
||||
@@ -10047,15 +10261,8 @@
|
||||
"type": "object"
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningEffortOption": {
|
||||
@@ -10509,6 +10716,37 @@
|
||||
"title": "MessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AgentMessageInputContent"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"agent_message"
|
||||
],
|
||||
"title": "AgentMessageResponseItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"author",
|
||||
"content",
|
||||
"recipient",
|
||||
"type"
|
||||
],
|
||||
"title": "AgentMessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"content": {
|
||||
@@ -12302,6 +12540,26 @@
|
||||
"title": "Model/verificationNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
"enum": [
|
||||
"turn/moderationMetadata"
|
||||
],
|
||||
"title": "Turn/moderationMetadataNotificationMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/TurnModerationMetadataNotification"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Turn/moderationMetadataNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
@@ -16300,6 +16558,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"TurnModerationMetadataNotification": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"metadata": true,
|
||||
"threadId": {
|
||||
"type": "string"
|
||||
},
|
||||
"turnId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata",
|
||||
"threadId",
|
||||
"turnId"
|
||||
],
|
||||
"title": "TurnModerationMetadataNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"TurnPlanStep": {
|
||||
"properties": {
|
||||
"status": {
|
||||
|
||||
@@ -31,6 +31,13 @@
|
||||
"agentIdentity"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Programmatic Codex auth backed by a personal access token.",
|
||||
"enum": [
|
||||
"personalAccessToken"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -667,15 +667,8 @@
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningSummary": {
|
||||
|
||||
+10
-4
@@ -94,12 +94,12 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"allowedPermissions": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
"allowedPermissionProfiles": {
|
||||
"additionalProperties": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"object",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
@@ -140,6 +140,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"defaultPermissions": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"enforceResidency": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AccountTokenUsageDailyBucket": {
|
||||
"properties": {
|
||||
"startDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokens": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"startDate",
|
||||
"tokens"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AccountTokenUsageSummary": {
|
||||
"properties": {
|
||||
"currentStreakDays": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"lifetimeTokens": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"longestRunningTurnSec": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"longestStreakDays": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"peakDailyTokens": {
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"dailyUsageBuckets": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AccountTokenUsageDailyBucket"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/definitions/AccountTokenUsageSummary"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"summary"
|
||||
],
|
||||
"title": "GetAccountTokenUsageResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+2
-9
@@ -462,15 +462,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"TextElement": {
|
||||
|
||||
@@ -462,15 +462,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"TextElement": {
|
||||
|
||||
+6
@@ -23,6 +23,12 @@
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/McpServerStartupState"
|
||||
},
|
||||
"threadId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -185,15 +185,8 @@
|
||||
"type": "object"
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningEffortOption": {
|
||||
|
||||
@@ -37,6 +37,69 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AppTemplateSummary": {
|
||||
"properties": {
|
||||
"canonicalConnectorId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"logoUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"logoUrlDark": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"materializedAppIds": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AppTemplateUnavailableReason"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"templateId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"materializedAppIds",
|
||||
"name",
|
||||
"templateId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AppTemplateUnavailableReason": {
|
||||
"enum": [
|
||||
"NOT_CONFIGURED_FOR_WORKSPACE",
|
||||
"NO_ACTIVE_WORKSPACE"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"HookEventName": {
|
||||
"enum": [
|
||||
"preToolUse",
|
||||
@@ -78,6 +141,12 @@
|
||||
},
|
||||
"PluginDetail": {
|
||||
"properties": {
|
||||
"appTemplates": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AppTemplateSummary"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"apps": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AppSummary"
|
||||
@@ -126,6 +195,7 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"appTemplates",
|
||||
"apps",
|
||||
"hooks",
|
||||
"marketplaceName",
|
||||
|
||||
+55
@@ -1,6 +1,30 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"encrypted_content": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"encrypted_content"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContentType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"encrypted_content",
|
||||
"type"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContent",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ContentItem": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -369,6 +393,37 @@
|
||||
"title": "MessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AgentMessageInputContent"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"agent_message"
|
||||
],
|
||||
"title": "AgentMessageResponseItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"author",
|
||||
"content",
|
||||
"recipient",
|
||||
"type"
|
||||
],
|
||||
"title": "AgentMessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"content": {
|
||||
|
||||
@@ -606,15 +606,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"TextElement": {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AbsolutePathBuf": {
|
||||
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
|
||||
"type": "string"
|
||||
},
|
||||
"ApprovalsReviewer": {
|
||||
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
|
||||
"enum": [
|
||||
|
||||
@@ -718,15 +718,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxPolicy": {
|
||||
|
||||
@@ -632,15 +632,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SessionSource": {
|
||||
|
||||
+2
-9
@@ -632,15 +632,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SessionSource": {
|
||||
|
||||
@@ -632,15 +632,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SessionSource": {
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AbsolutePathBuf": {
|
||||
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
|
||||
"type": "string"
|
||||
},
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"encrypted_content": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"encrypted_content"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContentType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"encrypted_content",
|
||||
"type"
|
||||
],
|
||||
"title": "EncryptedContentAgentMessageInputContent",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApprovalsReviewer": {
|
||||
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
|
||||
"enum": [
|
||||
@@ -436,6 +464,37 @@
|
||||
"title": "MessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AgentMessageInputContent"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"agent_message"
|
||||
],
|
||||
"title": "AgentMessageResponseItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"author",
|
||||
"content",
|
||||
"recipient",
|
||||
"type"
|
||||
],
|
||||
"title": "AgentMessageResponseItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"content": {
|
||||
|
||||
@@ -718,15 +718,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxPolicy": {
|
||||
|
||||
@@ -632,15 +632,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SessionSource": {
|
||||
|
||||
+2
-9
@@ -124,15 +124,8 @@
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningSummary": {
|
||||
|
||||
@@ -718,15 +718,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxPolicy": {
|
||||
|
||||
+2
-9
@@ -632,15 +632,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SessionSource": {
|
||||
|
||||
@@ -632,15 +632,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"SessionSource": {
|
||||
|
||||
+2
-9
@@ -606,15 +606,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"TextElement": {
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"metadata": true,
|
||||
"threadId": {
|
||||
"type": "string"
|
||||
},
|
||||
"turnId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata",
|
||||
"threadId",
|
||||
"turnId"
|
||||
],
|
||||
"title": "TurnModerationMetadataNotification",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -154,15 +154,8 @@
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningSummary": {
|
||||
|
||||
@@ -606,15 +606,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"TextElement": {
|
||||
|
||||
@@ -606,15 +606,8 @@
|
||||
]
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"enum": [
|
||||
"none",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
],
|
||||
"description": "A non-empty reasoning effort value advertised by the model.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"TextElement": {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type AgentMessageInputContent = { "type": "encrypted_content", encrypted_content: string, };
|
||||
+1
-1
@@ -5,4 +5,4 @@
|
||||
/**
|
||||
* Authentication mode for OpenAI-backed providers.
|
||||
*/
|
||||
export type AuthMode = "apikey" | "chatgpt" | "chatgptAuthTokens" | "agentIdentity";
|
||||
export type AuthMode = "apikey" | "chatgpt" | "chatgptAuthTokens" | "agentIdentity" | "personalAccessToken";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,4 +5,4 @@
|
||||
/**
|
||||
* See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning
|
||||
*/
|
||||
export type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
export type ReasoningEffort = string;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AgentMessageInputContent } from "./AgentMessageInputContent";
|
||||
import type { ContentItem } from "./ContentItem";
|
||||
import type { FunctionCallOutputBody } from "./FunctionCallOutputBody";
|
||||
import type { LocalShellAction } from "./LocalShellAction";
|
||||
@@ -10,7 +11,7 @@ import type { ReasoningItemContent } from "./ReasoningItemContent";
|
||||
import type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSummary";
|
||||
import type { WebSearchAction } from "./WebSearchAction";
|
||||
|
||||
export type ResponseItem = { "type": "message", role: string, content: Array<ContentItem>, phase?: MessagePhase, } | { "type": "reasoning", summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, } | { "type": "local_shell_call",
|
||||
export type ResponseItem = { "type": "message", role: string, content: Array<ContentItem>, phase?: MessagePhase, } | { "type": "agent_message", author: string, recipient: string, content: Array<AgentMessageInputContent>, } | { "type": "reasoning", summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, } | { "type": "local_shell_call",
|
||||
/**
|
||||
* Set when using the Responses API.
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
export type { AbsolutePathBuf } from "./AbsolutePathBuf";
|
||||
export type { AgentMessageInputContent } from "./AgentMessageInputContent";
|
||||
export type { AgentPath } from "./AgentPath";
|
||||
export type { ApplyPatchApprovalParams } from "./ApplyPatchApprovalParams";
|
||||
export type { ApplyPatchApprovalResponse } from "./ApplyPatchApprovalResponse";
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type AccountTokenUsageDailyBucket = { startDate: string, tokens: bigint, };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type AccountTokenUsageSummary = { lifetimeTokens: bigint | null, peakDailyTokens: bigint | null, longestRunningTurnSec: bigint | null, currentStreakDays: bigint | null, longestStreakDays: bigint | null, };
|
||||
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AppTemplateUnavailableReason } from "./AppTemplateUnavailableReason";
|
||||
|
||||
export type AppTemplateSummary = { templateId: string, name: string, description: string | null, canonicalConnectorId: string | null, logoUrl: string | null, logoUrlDark: string | null, materializedAppIds: Array<string>, reason: AppTemplateUnavailableReason | null, };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type AppTemplateUnavailableReason = "NOT_CONFIGURED_FOR_WORKSPACE" | "NO_ACTIVE_WORKSPACE";
|
||||
@@ -8,4 +8,4 @@ import type { ResidencyRequirement } from "./ResidencyRequirement";
|
||||
import type { SandboxMode } from "./SandboxMode";
|
||||
import type { WindowsSandboxSetupMode } from "./WindowsSandboxSetupMode";
|
||||
|
||||
export type ConfigRequirements = {allowedApprovalPolicies: Array<AskForApproval> | null, allowedSandboxModes: Array<SandboxMode> | null, allowedWindowsSandboxImplementations: Array<WindowsSandboxSetupMode> | null, allowedPermissions: Array<string> | null, allowedWebSearchModes: Array<WebSearchMode> | null, allowManagedHooksOnly: boolean | null, allowAppshots: boolean | null, computerUse: ComputerUseRequirements | null, featureRequirements: { [key in string]?: boolean } | null, enforceResidency: ResidencyRequirement | null};
|
||||
export type ConfigRequirements = {allowedApprovalPolicies: Array<AskForApproval> | null, allowedSandboxModes: Array<SandboxMode> | null, allowedWindowsSandboxImplementations: Array<WindowsSandboxSetupMode> | null, allowedPermissionProfiles: { [key in string]?: boolean } | null, defaultPermissions: string | null, allowedWebSearchModes: Array<WebSearchMode> | null, allowManagedHooksOnly: boolean | null, allowAppshots: boolean | null, computerUse: ComputerUseRequirements | null, featureRequirements: { [key in string]?: boolean } | null, enforceResidency: ResidencyRequirement | null};
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AccountTokenUsageDailyBucket } from "./AccountTokenUsageDailyBucket";
|
||||
import type { AccountTokenUsageSummary } from "./AccountTokenUsageSummary";
|
||||
|
||||
export type GetAccountTokenUsageResponse = { summary: AccountTokenUsageSummary, dailyUsageBuckets: Array<AccountTokenUsageDailyBucket> | null, };
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { McpServerStartupState } from "./McpServerStartupState";
|
||||
|
||||
export type McpServerStatusUpdatedNotification = { name: string, status: McpServerStartupState, error: string | null, };
|
||||
export type McpServerStatusUpdatedNotification = { threadId: string | null, name: string, status: McpServerStartupState, error: string | null, };
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { AppSummary } from "./AppSummary";
|
||||
import type { AppTemplateSummary } from "./AppTemplateSummary";
|
||||
import type { PluginHookSummary } from "./PluginHookSummary";
|
||||
import type { PluginSummary } from "./PluginSummary";
|
||||
import type { SkillSummary } from "./SkillSummary";
|
||||
|
||||
export type PluginDetail = { marketplaceName: string, marketplacePath: AbsolutePathBuf | null, summary: PluginSummary, description: string | null, skills: Array<SkillSummary>, hooks: Array<PluginHookSummary>, apps: Array<AppSummary>, mcpServers: Array<string>, };
|
||||
export type PluginDetail = { marketplaceName: string, marketplacePath: AbsolutePathBuf | null, summary: PluginSummary, description: string | null, skills: Array<SkillSummary>, hooks: Array<PluginHookSummary>, apps: Array<AppSummary>, appTemplates: Array<AppTemplateSummary>, mcpServers: Array<string>, };
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
|
||||
export type TurnModerationMetadataNotification = { threadId: string, turnId: string, metadata: JsonValue, };
|
||||
@@ -3,6 +3,8 @@
|
||||
export type { Account } from "./Account";
|
||||
export type { AccountLoginCompletedNotification } from "./AccountLoginCompletedNotification";
|
||||
export type { AccountRateLimitsUpdatedNotification } from "./AccountRateLimitsUpdatedNotification";
|
||||
export type { AccountTokenUsageDailyBucket } from "./AccountTokenUsageDailyBucket";
|
||||
export type { AccountTokenUsageSummary } from "./AccountTokenUsageSummary";
|
||||
export type { AccountUpdatedNotification } from "./AccountUpdatedNotification";
|
||||
export type { ActivePermissionProfile } from "./ActivePermissionProfile";
|
||||
export type { AddCreditsNudgeCreditType } from "./AddCreditsNudgeCreditType";
|
||||
@@ -21,6 +23,8 @@ export type { AppMetadata } from "./AppMetadata";
|
||||
export type { AppReview } from "./AppReview";
|
||||
export type { AppScreenshot } from "./AppScreenshot";
|
||||
export type { AppSummary } from "./AppSummary";
|
||||
export type { AppTemplateSummary } from "./AppTemplateSummary";
|
||||
export type { AppTemplateUnavailableReason } from "./AppTemplateUnavailableReason";
|
||||
export type { AppToolApproval } from "./AppToolApproval";
|
||||
export type { AppToolsConfig } from "./AppToolsConfig";
|
||||
export type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
@@ -139,6 +143,7 @@ export type { FsWriteFileResponse } from "./FsWriteFileResponse";
|
||||
export type { GetAccountParams } from "./GetAccountParams";
|
||||
export type { GetAccountRateLimitsResponse } from "./GetAccountRateLimitsResponse";
|
||||
export type { GetAccountResponse } from "./GetAccountResponse";
|
||||
export type { GetAccountTokenUsageResponse } from "./GetAccountTokenUsageResponse";
|
||||
export type { GitInfo } from "./GitInfo";
|
||||
export type { GrantedPermissionProfile } from "./GrantedPermissionProfile";
|
||||
export type { GuardianApprovalReview } from "./GuardianApprovalReview";
|
||||
@@ -445,6 +450,7 @@ export type { TurnError } from "./TurnError";
|
||||
export type { TurnInterruptParams } from "./TurnInterruptParams";
|
||||
export type { TurnInterruptResponse } from "./TurnInterruptResponse";
|
||||
export type { TurnItemsView } from "./TurnItemsView";
|
||||
export type { TurnModerationMetadataNotification } from "./TurnModerationMetadataNotification";
|
||||
export type { TurnPlanStep } from "./TurnPlanStep";
|
||||
export type { TurnPlanStepStatus } from "./TurnPlanStepStatus";
|
||||
export type { TurnPlanUpdatedNotification } from "./TurnPlanUpdatedNotification";
|
||||
|
||||
@@ -2963,11 +2963,14 @@ permissionProfile?: string | null};
|
||||
|
||||
let client_request_json = fs::read_to_string(output_dir.join("ClientRequest.json"))?;
|
||||
assert!(client_request_json.contains("remoteControl/pairing/start"));
|
||||
assert!(client_request_json.contains("remoteControl/pairing/status"));
|
||||
assert!(client_request_json.contains("remoteControl/client/list"));
|
||||
assert!(client_request_json.contains("remoteControl/client/revoke"));
|
||||
for schema in [
|
||||
"RemoteControlPairingStartParams.json",
|
||||
"RemoteControlPairingStartResponse.json",
|
||||
"RemoteControlPairingStatusParams.json",
|
||||
"RemoteControlPairingStatusResponse.json",
|
||||
"RemoteControlClientsListParams.json",
|
||||
"RemoteControlClientsListResponse.json",
|
||||
"RemoteControlClientsRevokeParams.json",
|
||||
|
||||
@@ -36,6 +36,21 @@ pub enum AuthMode {
|
||||
#[ts(rename = "agentIdentity")]
|
||||
#[strum(serialize = "agentIdentity")]
|
||||
AgentIdentity,
|
||||
/// Programmatic Codex auth backed by a personal access token.
|
||||
#[serde(rename = "personalAccessToken")]
|
||||
#[ts(rename = "personalAccessToken")]
|
||||
#[strum(serialize = "personalAccessToken")]
|
||||
PersonalAccessToken,
|
||||
}
|
||||
|
||||
impl AuthMode {
|
||||
/// Returns whether this mode represents an authenticated human ChatGPT account.
|
||||
pub fn has_chatgpt_account(self) -> bool {
|
||||
match self {
|
||||
Self::Chatgpt | Self::ChatgptAuthTokens | Self::PersonalAccessToken => true,
|
||||
Self::ApiKey | Self::AgentIdentity => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! experimental_reason_expr {
|
||||
@@ -849,6 +864,12 @@ client_request_definitions! {
|
||||
serialization: global("remote-control-pairing"),
|
||||
response: v2::RemoteControlPairingStartResponse,
|
||||
},
|
||||
#[experimental("remoteControl/pairing/status")]
|
||||
RemoteControlPairingStatus => "remoteControl/pairing/status" {
|
||||
params: v2::RemoteControlPairingStatusParams,
|
||||
serialization: global_shared_read("remote-control-pairing"),
|
||||
response: v2::RemoteControlPairingStatusResponse,
|
||||
},
|
||||
#[experimental("remoteControl/client/list")]
|
||||
RemoteControlClientsList => "remoteControl/client/list" {
|
||||
params: v2::RemoteControlClientsListParams,
|
||||
@@ -949,6 +970,12 @@ client_request_definitions! {
|
||||
response: v2::GetAccountRateLimitsResponse,
|
||||
},
|
||||
|
||||
GetAccountTokenUsage => "account/usage/read" {
|
||||
params: #[ts(type = "undefined")] #[serde(skip_serializing_if = "Option::is_none")] Option<()>,
|
||||
serialization: None,
|
||||
response: v2::GetAccountTokenUsageResponse,
|
||||
},
|
||||
|
||||
SendAddCreditsNudgeEmail => "account/sendAddCreditsNudgeEmail" {
|
||||
params: v2::SendAddCreditsNudgeEmailParams,
|
||||
serialization: global("account-auth"),
|
||||
@@ -1549,6 +1576,8 @@ server_notification_definitions! {
|
||||
ContextCompacted => "thread/compacted" (v2::ContextCompactedNotification),
|
||||
ModelRerouted => "model/rerouted" (v2::ModelReroutedNotification),
|
||||
ModelVerification => "model/verification" (v2::ModelVerificationNotification),
|
||||
#[experimental("turn/moderationMetadata")]
|
||||
TurnModerationMetadata => "turn/moderationMetadata" (v2::TurnModerationMetadataNotification),
|
||||
Warning => "warning" (v2::WarningNotification),
|
||||
GuardianWarning => "guardianWarning" (v2::GuardianWarningNotification),
|
||||
DeprecationNotice => "deprecationNotice" (v2::DeprecationNoticeNotification),
|
||||
@@ -2006,6 +2035,19 @@ mod tests {
|
||||
"remote-control-pairing"
|
||||
))
|
||||
);
|
||||
let remote_control_pairing_status = ClientRequest::RemoteControlPairingStatus {
|
||||
request_id: request_id(),
|
||||
params: v2::RemoteControlPairingStatusParams {
|
||||
pairing_code: Some("pairing-code".to_string()),
|
||||
manual_pairing_code: None,
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
remote_control_pairing_status.serialization_scope(),
|
||||
Some(ClientRequestSerializationScope::GlobalSharedRead(
|
||||
"remote-control-pairing"
|
||||
))
|
||||
);
|
||||
let remote_control_clients_list = ClientRequest::RemoteControlClientsList {
|
||||
request_id: request_id(),
|
||||
params: v2::RemoteControlClientsListParams::default(),
|
||||
@@ -2370,6 +2412,24 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_get_account_token_usage() -> Result<()> {
|
||||
let request = ClientRequest::GetAccountTokenUsage {
|
||||
request_id: RequestId::Integer(1),
|
||||
params: None,
|
||||
};
|
||||
assert_eq!(request.id(), &RequestId::Integer(1));
|
||||
assert_eq!(request.method(), "account/usage/read");
|
||||
assert_eq!(
|
||||
json!({
|
||||
"method": "account/usage/read",
|
||||
"id": 1,
|
||||
}),
|
||||
serde_json::to_value(&request)?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_client_response() -> Result<()> {
|
||||
let cwd = absolute_path("/tmp");
|
||||
@@ -3222,6 +3282,21 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_moderation_metadata_notification_is_marked_experimental() {
|
||||
let notification =
|
||||
ServerNotification::TurnModerationMetadata(v2::TurnModerationMetadataNotification {
|
||||
thread_id: "thr_123".to_string(),
|
||||
turn_id: "turn_123".to_string(),
|
||||
metadata: json!({"presentation": "inline"}),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
crate::experimental_api::ExperimentalApi::experimental_reason(¬ification),
|
||||
Some("turn/moderationMetadata")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_realtime_started_notification_is_marked_experimental() {
|
||||
let notification =
|
||||
|
||||
@@ -621,7 +621,7 @@ impl ThreadHistoryBuilder {
|
||||
receiver_thread_ids: Vec::new(),
|
||||
prompt: Some(payload.prompt.clone()),
|
||||
model: Some(payload.model.clone()),
|
||||
reasoning_effort: Some(payload.reasoning_effort),
|
||||
reasoning_effort: Some(payload.reasoning_effort.clone()),
|
||||
agents_states: HashMap::new(),
|
||||
};
|
||||
self.upsert_item_in_current_turn(item);
|
||||
@@ -656,7 +656,7 @@ impl ThreadHistoryBuilder {
|
||||
receiver_thread_ids,
|
||||
prompt: Some(payload.prompt.clone()),
|
||||
model: Some(payload.model.clone()),
|
||||
reasoning_effort: Some(payload.reasoning_effort),
|
||||
reasoning_effort: Some(payload.reasoning_effort.clone()),
|
||||
agents_states,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -138,6 +138,78 @@ pub struct CancelLoginAccountResponse {
|
||||
pub status: CancelLoginAccountStatus,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountSessionsAddParams {
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub switch_to_added_account: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountSessionsListParams {
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub refresh_workspace_metadata: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountSessionsLogoutParams {
|
||||
pub session_id: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountSessionsSwitchParams {
|
||||
pub session_id: String,
|
||||
pub account_id: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountSessionsResponse {
|
||||
pub active_session_id: Option<String>,
|
||||
pub sessions: Vec<AccountSession>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountSession {
|
||||
pub session_id: String,
|
||||
pub email: Option<String>,
|
||||
pub user_id: Option<String>,
|
||||
pub display_name: Option<String>,
|
||||
pub image_url: Option<String>,
|
||||
pub last_used_at: i64,
|
||||
pub is_active: bool,
|
||||
pub selected_workspace_account_id: Option<String>,
|
||||
pub workspaces: Vec<AccountSessionWorkspace>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountSessionWorkspace {
|
||||
pub account_id: String,
|
||||
pub name: Option<String>,
|
||||
pub image_url: Option<String>,
|
||||
pub kind: Option<AccountSessionWorkspaceKind>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub enum AccountSessionWorkspaceKind {
|
||||
Personal,
|
||||
Workspace,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
@@ -186,6 +258,33 @@ pub struct GetAccountRateLimitsResponse {
|
||||
pub rate_limits_by_limit_id: Option<HashMap<String, RateLimitSnapshot>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct GetAccountTokenUsageResponse {
|
||||
pub summary: AccountTokenUsageSummary,
|
||||
pub daily_usage_buckets: Option<Vec<AccountTokenUsageDailyBucket>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountTokenUsageSummary {
|
||||
pub lifetime_tokens: Option<i64>,
|
||||
pub peak_daily_tokens: Option<i64>,
|
||||
pub longest_running_turn_sec: Option<i64>,
|
||||
pub current_streak_days: Option<i64>,
|
||||
pub longest_streak_days: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountTokenUsageDailyBucket {
|
||||
pub start_date: String,
|
||||
pub tokens: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
|
||||
@@ -375,7 +375,8 @@ pub struct ConfigRequirements {
|
||||
pub allowed_approvals_reviewers: Option<Vec<ApprovalsReviewer>>,
|
||||
pub allowed_sandbox_modes: Option<Vec<SandboxMode>>,
|
||||
pub allowed_windows_sandbox_implementations: Option<Vec<WindowsSandboxSetupMode>>,
|
||||
pub allowed_permissions: Option<Vec<String>>,
|
||||
pub allowed_permission_profiles: Option<BTreeMap<String, bool>>,
|
||||
pub default_permissions: Option<String>,
|
||||
pub allowed_web_search_modes: Option<Vec<WebSearchMode>>,
|
||||
pub allow_managed_hooks_only: Option<bool>,
|
||||
pub allow_appshots: Option<bool>,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user