mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Precompute app-server protocol exports (#36212)
## Why Normal app-server protocol builds do not need the `ts-rs` and `schemars` implementations used to generate schema exports. ## What changed - Embed compressed stable and experimental TypeScript and JSON schema exports, and serve the existing export APIs from those artifacts. - Use no-op schema derives in non-test builds while keeping the real generators available for fixture regeneration and tests. - Move schema fixture regeneration behind a Python helper and update `just write-app-server-schema --experimental` to refresh the experimental embedded exports. ## Testing - Verify embedded exports match freshly generated stable and experimental schemas. - Verify export options and on-disk output remain compatible. GitOrigin-RevId: e8536338b457e6eec34bdf29ec0684144bd13734
This commit is contained in:
Generated
+6
-1
@@ -2137,7 +2137,7 @@ name = "codex-app-server-protocol"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"codex-app-server-protocol-noop-macros",
|
||||
"codex-experimental-api-macros",
|
||||
"codex-extension-items",
|
||||
"codex-protocol",
|
||||
@@ -2159,8 +2159,13 @@ dependencies = [
|
||||
"tracing",
|
||||
"ts-rs",
|
||||
"uuid",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-app-server-protocol-noop-macros"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "codex-app-server-test-client"
|
||||
version = "0.0.0"
|
||||
|
||||
@@ -13,6 +13,7 @@ members = [
|
||||
"app-server-daemon",
|
||||
"app-server-client",
|
||||
"app-server-protocol",
|
||||
"app-server-protocol-noop-macros",
|
||||
"app-server-test-client",
|
||||
"apply-patch",
|
||||
"arg0",
|
||||
@@ -152,6 +153,7 @@ codex-app-server-transport = { path = "app-server-transport" }
|
||||
codex-app-server-daemon = { path = "app-server-daemon" }
|
||||
codex-app-server-client = { path = "app-server-client" }
|
||||
codex-app-server-protocol = { path = "app-server-protocol" }
|
||||
codex-app-server-protocol-noop-macros = { path = "app-server-protocol-noop-macros" }
|
||||
codex-app-server-test-client = { path = "app-server-test-client" }
|
||||
codex-apply-patch = { path = "apply-patch" }
|
||||
codex-arg0 = { path = "arg0" }
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
load("//:defs.bzl", "codex_rust_crate")
|
||||
|
||||
codex_rust_crate(
|
||||
name = "app-server-protocol-noop-macros",
|
||||
crate_name = "codex_app_server_protocol_noop_macros",
|
||||
proc_macro = True,
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "codex-app-server-protocol-noop-macros"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,20 @@
|
||||
//! No-op schema derives for production app-server protocol builds.
|
||||
//!
|
||||
//! The real `ts-rs` and `schemars` derives are only needed when regenerating
|
||||
//! the vendored protocol exports. Normal builds retain the annotations so the
|
||||
//! protocol definitions stay readable, but use these derives to avoid
|
||||
//! generating implementations that cannot be reached at runtime.
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
/// Accepts `#[schemars(...)]` helper attributes without generating an impl.
|
||||
#[proc_macro_derive(JsonSchema, attributes(schemars))]
|
||||
pub fn derive_json_schema(_input: TokenStream) -> TokenStream {
|
||||
TokenStream::new()
|
||||
}
|
||||
|
||||
/// Accepts `#[ts(...)]` helper attributes without generating an impl.
|
||||
#[proc_macro_derive(TS, attributes(ts))]
|
||||
pub fn derive_ts(_input: TokenStream) -> TokenStream {
|
||||
TokenStream::new()
|
||||
}
|
||||
@@ -2,6 +2,7 @@ load("//:defs.bzl", "codex_rust_crate")
|
||||
|
||||
codex_rust_crate(
|
||||
name = "app-server-protocol",
|
||||
compile_data = glob(["schema/precomputed/**"]),
|
||||
crate_name = "codex_app_server_protocol",
|
||||
test_data_extra = glob(
|
||||
["schema/**"],
|
||||
|
||||
@@ -14,14 +14,13 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
codex-experimental-api-macros = { workspace = true }
|
||||
codex-app-server-protocol-noop-macros = { workspace = true }
|
||||
codex-extension-items = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
codex-shell-command = { workspace = true }
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
codex-utils-path-uri = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_with = { workspace = true }
|
||||
@@ -30,17 +29,24 @@ thiserror = { workspace = true }
|
||||
rmcp = { workspace = true, default-features = false, features = [
|
||||
"base64",
|
||||
"macros",
|
||||
"schemars",
|
||||
"server",
|
||||
] }
|
||||
ts-rs = { workspace = true }
|
||||
inventory = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
uuid = { workspace = true, features = ["serde", "v7"] }
|
||||
zstd = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
rmcp = { workspace = true, default-features = false, features = [
|
||||
"base64",
|
||||
"macros",
|
||||
"schemars",
|
||||
"server",
|
||||
] }
|
||||
schemars = { workspace = true }
|
||||
similar = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
ts-rs = { workspace = true }
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Regenerate vendored app-server schema fixtures"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--schema-root",
|
||||
type=Path,
|
||||
help="root directory containing the schema fixtures",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--prettier",
|
||||
type=Path,
|
||||
help="optional Prettier executable used to format TypeScript files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--experimental",
|
||||
action="store_true",
|
||||
help="regenerate the precomputed experimental exports",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
workspace_root = Path(__file__).resolve().parents[2]
|
||||
schema_root = args.schema_root or workspace_root / "app-server-protocol" / "schema"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["CODEX_APP_SERVER_SCHEMA_ROOT"] = str(schema_root)
|
||||
env["CODEX_APP_SERVER_SCHEMA_EXPERIMENTAL"] = (
|
||||
"1" if args.experimental else "0"
|
||||
)
|
||||
if args.prettier:
|
||||
env["CODEX_APP_SERVER_SCHEMA_PRETTIER"] = str(args.prettier)
|
||||
|
||||
subprocess.run(
|
||||
[
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"codex-app-server-protocol",
|
||||
"--lib",
|
||||
"schema_fixtures_tests::write_schema_fixtures_from_env",
|
||||
"--",
|
||||
"--exact",
|
||||
"--ignored",
|
||||
],
|
||||
cwd=workspace_root,
|
||||
env=env,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,34 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
about = "Generate TypeScript bindings and JSON Schemas for the Codex app-server protocol"
|
||||
)]
|
||||
struct Args {
|
||||
/// Output directory where generated files will be written
|
||||
#[arg(short = 'o', long = "out", value_name = "DIR")]
|
||||
out_dir: PathBuf,
|
||||
|
||||
/// Optional Prettier executable path to format generated TypeScript files
|
||||
#[arg(short = 'p', long = "prettier", value_name = "PRETTIER_BIN")]
|
||||
prettier: Option<PathBuf>,
|
||||
|
||||
/// Include experimental API methods and fields in generated output.
|
||||
#[arg(long = "experimental")]
|
||||
experimental: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
codex_app_server_protocol::generate_ts_with_options(
|
||||
&args.out_dir,
|
||||
args.prettier.as_deref(),
|
||||
codex_app_server_protocol::GenerateTsOptions {
|
||||
experimental_api: args.experimental,
|
||||
..codex_app_server_protocol::GenerateTsOptions::default()
|
||||
},
|
||||
)?;
|
||||
codex_app_server_protocol::generate_json_with_experimental(&args.out_dir, args.experimental)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(about = "Regenerate vendored app-server schema fixtures")]
|
||||
struct Args {
|
||||
/// Root directory containing `typescript/` and `json/`.
|
||||
#[arg(long = "schema-root", value_name = "DIR")]
|
||||
schema_root: Option<PathBuf>,
|
||||
|
||||
/// Optional path to the Prettier executable to format generated TypeScript files.
|
||||
#[arg(short = 'p', long = "prettier", value_name = "PRETTIER_BIN")]
|
||||
prettier: Option<PathBuf>,
|
||||
|
||||
/// Include experimental API methods and fields in generated fixtures.
|
||||
#[arg(long = "experimental")]
|
||||
experimental: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
let schema_root = args
|
||||
.schema_root
|
||||
.unwrap_or_else(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("schema"));
|
||||
|
||||
codex_app_server_protocol::write_schema_fixtures_with_options(
|
||||
&schema_root,
|
||||
args.prettier.as_deref(),
|
||||
codex_app_server_protocol::SchemaFixtureOptions {
|
||||
experimental_api: args.experimental,
|
||||
},
|
||||
)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to regenerate schema fixtures under {}",
|
||||
schema_root.display()
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
use crate::ClientNotification;
|
||||
use crate::ClientRequest;
|
||||
use crate::JsonSchema;
|
||||
use crate::ServerNotification;
|
||||
use crate::ServerNotificationEnvelope;
|
||||
use crate::ServerRequest;
|
||||
use crate::TS;
|
||||
use crate::experimental_api::experimental_fields;
|
||||
use crate::export_client_notification_schemas;
|
||||
use crate::export_client_param_schemas;
|
||||
@@ -22,7 +24,6 @@ use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use codex_protocol::protocol::RolloutLine;
|
||||
use schemars::JsonSchema;
|
||||
use schemars::schema_for;
|
||||
use serde::Serialize;
|
||||
use serde_json::Map;
|
||||
@@ -38,7 +39,6 @@ use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
use ts_rs::TS;
|
||||
|
||||
pub(crate) const GENERATED_TS_HEADER: &str = "// GENERATED CODE! DO NOT MODIFY BY HAND!\n\n";
|
||||
const IGNORED_DEFINITIONS: &[&str] = &["Option<()>"];
|
||||
@@ -87,11 +87,6 @@ impl GeneratedSchema {
|
||||
}
|
||||
|
||||
type JsonSchemaEmitter = fn(&Path) -> Result<GeneratedSchema>;
|
||||
pub fn generate_types(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
|
||||
generate_ts(out_dir, prettier)?;
|
||||
generate_json(out_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct GenerateTsOptions {
|
||||
@@ -112,10 +107,6 @@ impl Default for GenerateTsOptions {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_ts(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
|
||||
generate_ts_with_options(out_dir, prettier, GenerateTsOptions::default())
|
||||
}
|
||||
|
||||
pub fn generate_ts_with_options(
|
||||
out_dir: &Path,
|
||||
prettier: Option<&Path>,
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
mod experimental_api;
|
||||
#[cfg(test)]
|
||||
mod export;
|
||||
mod precomputed_exports;
|
||||
#[cfg(test)]
|
||||
#[path = "precomputed_exports_tests.rs"]
|
||||
mod precomputed_exports_tests;
|
||||
mod protocol;
|
||||
pub mod rpc;
|
||||
#[cfg(test)]
|
||||
mod schema_fixtures;
|
||||
#[cfg(test)]
|
||||
#[path = "schema_fixtures_tests.rs"]
|
||||
mod schema_fixtures_tests;
|
||||
|
||||
pub use experimental_api::*;
|
||||
pub use export::GenerateTsOptions;
|
||||
pub use export::generate_internal_json_schema;
|
||||
pub use export::generate_json;
|
||||
pub use export::generate_json_with_experimental;
|
||||
pub use export::generate_ts;
|
||||
pub use export::generate_ts_with_options;
|
||||
pub use export::generate_types;
|
||||
pub use precomputed_exports::GenerateTsOptions;
|
||||
pub use precomputed_exports::generate_internal_json_schema;
|
||||
pub use precomputed_exports::generate_json;
|
||||
pub use precomputed_exports::generate_json_with_experimental;
|
||||
pub use precomputed_exports::generate_ts;
|
||||
pub use precomputed_exports::generate_ts_with_options;
|
||||
pub use precomputed_exports::generate_types;
|
||||
pub use protocol::common::*;
|
||||
pub use protocol::event_mapping::*;
|
||||
pub use protocol::item_builders::*;
|
||||
@@ -41,10 +50,22 @@ pub use protocol::v1::Tools;
|
||||
pub use protocol::v1::UserSavedConfig;
|
||||
pub use protocol::v2::*;
|
||||
pub use rpc::*;
|
||||
#[cfg(test)]
|
||||
pub use schema_fixtures::SchemaFixtureOptions;
|
||||
#[doc(hidden)]
|
||||
pub use schema_fixtures::generate_typescript_schema_fixture_subtree_for_tests;
|
||||
#[cfg(test)]
|
||||
pub use schema_fixtures::read_schema_fixture_subtree;
|
||||
#[cfg(test)]
|
||||
pub use schema_fixtures::read_schema_fixture_tree;
|
||||
#[cfg(test)]
|
||||
pub use schema_fixtures::write_schema_fixtures;
|
||||
#[cfg(test)]
|
||||
pub use schema_fixtures::write_schema_fixtures_with_options;
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub(crate) use codex_app_server_protocol_noop_macros::JsonSchema;
|
||||
#[cfg(not(test))]
|
||||
pub(crate) use codex_app_server_protocol_noop_macros::TS;
|
||||
#[cfg(test)]
|
||||
pub(crate) use schemars::JsonSchema;
|
||||
#[cfg(test)]
|
||||
pub(crate) use ts_rs::TS;
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::bail;
|
||||
use serde::Deserialize;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::io::Cursor;
|
||||
use std::path::Component;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
pub(crate) const GENERATED_TS_HEADER: &str = "// GENERATED CODE! DO NOT MODIFY BY HAND!\n\n";
|
||||
const STABLE_EXPORTS: &[u8] =
|
||||
include_bytes!("../schema/precomputed/app-server-exports-stable.json.zst");
|
||||
const EXPERIMENTAL_EXPORTS: &[u8] =
|
||||
include_bytes!("../schema/precomputed/app-server-exports-experimental.json.zst");
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct GenerateTsOptions {
|
||||
pub generate_indices: bool,
|
||||
pub ensure_headers: bool,
|
||||
pub run_prettier: bool,
|
||||
pub experimental_api: bool,
|
||||
}
|
||||
|
||||
impl Default for GenerateTsOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
generate_indices: true,
|
||||
ensure_headers: true,
|
||||
run_prettier: true,
|
||||
experimental_api: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq, serde::Serialize))]
|
||||
pub(crate) struct PrecomputedExports {
|
||||
pub(crate) typescript: BTreeMap<String, String>,
|
||||
pub(crate) json_schema: BTreeMap<String, String>,
|
||||
pub(crate) internal_json_schema: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum ExportSet {
|
||||
Stable,
|
||||
Experimental,
|
||||
}
|
||||
|
||||
pub fn generate_types(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
|
||||
generate_ts(out_dir, prettier)?;
|
||||
generate_json(out_dir)
|
||||
}
|
||||
|
||||
pub fn generate_ts(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
|
||||
generate_ts_with_options(out_dir, prettier, GenerateTsOptions::default())
|
||||
}
|
||||
|
||||
pub fn generate_ts_with_options(
|
||||
out_dir: &Path,
|
||||
prettier: Option<&Path>,
|
||||
options: GenerateTsOptions,
|
||||
) -> Result<()> {
|
||||
let export_set = if options.experimental_api {
|
||||
ExportSet::Experimental
|
||||
} else {
|
||||
ExportSet::Stable
|
||||
};
|
||||
let exports = load_exports(export_set)?;
|
||||
let ts_files = write_typescript_exports(out_dir, &exports.typescript, options)?;
|
||||
|
||||
if options.run_prettier
|
||||
&& let Some(prettier_bin) = prettier
|
||||
&& !ts_files.is_empty()
|
||||
{
|
||||
let status = Command::new(prettier_bin)
|
||||
.arg("--write")
|
||||
.arg("--log-level")
|
||||
.arg("warn")
|
||||
.args(ts_files.iter().map(PathBuf::as_path))
|
||||
.status()
|
||||
.with_context(|| format!("Failed to invoke Prettier at {}", prettier_bin.display()))?;
|
||||
if !status.success() {
|
||||
bail!("Prettier failed with status {status}");
|
||||
}
|
||||
}
|
||||
|
||||
trim_trailing_whitespace_in_ts_files(&ts_files)
|
||||
}
|
||||
|
||||
pub fn generate_json(out_dir: &Path) -> Result<()> {
|
||||
generate_json_with_experimental(out_dir, /*experimental_api*/ false)
|
||||
}
|
||||
|
||||
pub fn generate_json_with_experimental(out_dir: &Path, experimental_api: bool) -> Result<()> {
|
||||
let export_set = if experimental_api {
|
||||
ExportSet::Experimental
|
||||
} else {
|
||||
ExportSet::Stable
|
||||
};
|
||||
let exports = load_exports(export_set)?;
|
||||
write_exports(out_dir, &exports.json_schema)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_internal_json_schema(out_dir: &Path) -> Result<()> {
|
||||
let exports = load_exports(ExportSet::Stable)?;
|
||||
write_exports(out_dir, &exports.internal_json_schema)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_exports(export_set: ExportSet) -> Result<PrecomputedExports> {
|
||||
let compressed = match export_set {
|
||||
ExportSet::Stable => STABLE_EXPORTS,
|
||||
ExportSet::Experimental => EXPERIMENTAL_EXPORTS,
|
||||
};
|
||||
let bytes = zstd::stream::decode_all(Cursor::new(compressed))
|
||||
.context("decompress precomputed app-server protocol exports")?;
|
||||
serde_json::from_slice(&bytes).context("decode precomputed app-server protocol exports")
|
||||
}
|
||||
|
||||
fn write_typescript_exports(
|
||||
out_dir: &Path,
|
||||
exports: &BTreeMap<String, String>,
|
||||
options: GenerateTsOptions,
|
||||
) -> Result<Vec<PathBuf>> {
|
||||
let mut written = Vec::new();
|
||||
for (relative_path, archived_contents) in exports {
|
||||
let is_index = Path::new(relative_path).file_name() == Some(OsStr::new("index.ts"));
|
||||
if is_index && !options.generate_indices {
|
||||
continue;
|
||||
}
|
||||
|
||||
let contents = if options.ensure_headers || is_index {
|
||||
archived_contents.as_str()
|
||||
} else {
|
||||
archived_contents
|
||||
.strip_prefix(GENERATED_TS_HEADER)
|
||||
.unwrap_or(archived_contents)
|
||||
};
|
||||
written.push(write_export(out_dir, relative_path, contents)?);
|
||||
}
|
||||
Ok(written)
|
||||
}
|
||||
|
||||
fn write_exports(out_dir: &Path, exports: &BTreeMap<String, String>) -> Result<Vec<PathBuf>> {
|
||||
exports
|
||||
.iter()
|
||||
.map(|(relative_path, contents)| write_export(out_dir, relative_path, contents))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn write_export(out_dir: &Path, relative_path: &str, contents: &str) -> Result<PathBuf> {
|
||||
let relative_path = validated_relative_path(relative_path)?;
|
||||
let output_path = out_dir.join(relative_path);
|
||||
let parent = output_path
|
||||
.parent()
|
||||
.context("precomputed export path should have a parent")?;
|
||||
fs::create_dir_all(parent).with_context(|| format!("Failed to create {}", parent.display()))?;
|
||||
fs::write(&output_path, contents)
|
||||
.with_context(|| format!("Failed to write {}", output_path.display()))?;
|
||||
Ok(output_path)
|
||||
}
|
||||
|
||||
fn validated_relative_path(path: &str) -> Result<&Path> {
|
||||
let path = Path::new(path);
|
||||
if path.as_os_str().is_empty()
|
||||
|| path
|
||||
.components()
|
||||
.any(|component| !matches!(component, Component::Normal(_)))
|
||||
{
|
||||
bail!("invalid precomputed export path: {}", path.display());
|
||||
}
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
fn trim_trailing_whitespace_in_ts_files(paths: &[PathBuf]) -> Result<()> {
|
||||
for path in paths {
|
||||
let content = fs::read_to_string(path)
|
||||
.with_context(|| format!("Failed to read {}", path.display()))?;
|
||||
let trimmed = trim_trailing_line_whitespace(&content);
|
||||
if trimmed != content {
|
||||
fs::write(path, trimmed)
|
||||
.with_context(|| format!("Failed to write {}", path.display()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn trim_trailing_line_whitespace(content: &str) -> String {
|
||||
let mut trimmed = String::with_capacity(content.len());
|
||||
for line in content.split_inclusive('\n') {
|
||||
if let Some(line_without_newline) = line.strip_suffix('\n') {
|
||||
trimmed.push_str(line_without_newline.trim_end_matches([' ', '\t']));
|
||||
trimmed.push('\n');
|
||||
} else {
|
||||
trimmed.push_str(line.trim_end_matches([' ', '\t']));
|
||||
}
|
||||
}
|
||||
trimmed
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn decode_precomputed_exports(experimental_api: bool) -> Result<PrecomputedExports> {
|
||||
let export_set = if experimental_api {
|
||||
ExportSet::Experimental
|
||||
} else {
|
||||
ExportSet::Stable
|
||||
};
|
||||
load_exports(export_set)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
use crate::GenerateTsOptions;
|
||||
use crate::generate_internal_json_schema;
|
||||
use crate::generate_json_with_experimental;
|
||||
use crate::generate_ts_with_options;
|
||||
use crate::precomputed_exports::GENERATED_TS_HEADER;
|
||||
use crate::precomputed_exports::decode_precomputed_exports;
|
||||
use crate::schema_fixtures::collect_export_files_recursive;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn precomputed_exports_are_written_to_disk() -> Result<()> {
|
||||
let output_dir = tempfile::tempdir().context("create export temp dir")?;
|
||||
let typescript_dir = output_dir.path().join("typescript");
|
||||
let json_dir = output_dir.path().join("json");
|
||||
let internal_json_dir = output_dir.path().join("internal-json");
|
||||
|
||||
generate_ts_with_options(
|
||||
&typescript_dir,
|
||||
/*prettier*/ None,
|
||||
GenerateTsOptions::default(),
|
||||
)?;
|
||||
generate_json_with_experimental(&json_dir, /*experimental_api*/ false)?;
|
||||
generate_internal_json_schema(&internal_json_dir)?;
|
||||
|
||||
let exports = decode_precomputed_exports(/*experimental_api*/ false)?;
|
||||
assert_eq!(
|
||||
collect_export_files_recursive(&typescript_dir)?,
|
||||
exports.typescript
|
||||
);
|
||||
assert_eq!(
|
||||
collect_export_files_recursive(&json_dir)?,
|
||||
exports.json_schema
|
||||
);
|
||||
assert_eq!(
|
||||
collect_export_files_recursive(&internal_json_dir)?,
|
||||
exports.internal_json_schema
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typescript_export_options_preserve_generator_behavior() -> Result<()> {
|
||||
let output_dir = tempfile::tempdir().context("create TypeScript export temp dir")?;
|
||||
generate_ts_with_options(
|
||||
output_dir.path(),
|
||||
/*prettier*/ None,
|
||||
GenerateTsOptions {
|
||||
generate_indices: false,
|
||||
ensure_headers: false,
|
||||
..GenerateTsOptions::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let files = collect_export_files_recursive(output_dir.path())?;
|
||||
assert!(!files.keys().any(|path| {
|
||||
Path::new(path)
|
||||
.file_name()
|
||||
.is_some_and(|name| name == "index.ts")
|
||||
}));
|
||||
assert!(
|
||||
files
|
||||
.values()
|
||||
.all(|contents| !contents.starts_with(GENERATED_TS_HEADER))
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,19 +1,22 @@
|
||||
#[cfg(test)]
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::JSONRPCNotification;
|
||||
use crate::JSONRPCRequest;
|
||||
use crate::JsonSchema;
|
||||
use crate::RequestId;
|
||||
use crate::TS;
|
||||
#[cfg(test)]
|
||||
use crate::export::GeneratedSchema;
|
||||
#[cfg(test)]
|
||||
use crate::export::write_json_schema;
|
||||
use crate::protocol::v1;
|
||||
use crate::protocol::v2;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum_macros::Display;
|
||||
use ts_rs::TS;
|
||||
|
||||
/// Authentication mode for OpenAI-backed providers.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Display, JsonSchema, TS)]
|
||||
@@ -90,6 +93,7 @@ macro_rules! experimental_reason_expr {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! experimental_method_entry {
|
||||
(#[experimental($reason:expr)] => $wire:literal) => {
|
||||
$wire
|
||||
@@ -102,6 +106,7 @@ macro_rules! experimental_method_entry {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! experimental_type_entry {
|
||||
(#[experimental($reason:expr)] $ty:ty) => {
|
||||
stringify!($ty)
|
||||
@@ -393,22 +398,26 @@ macro_rules! client_request_definitions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) const EXPERIMENTAL_CLIENT_METHODS: &[&str] = &[
|
||||
$(
|
||||
experimental_method_entry!($(#[experimental($reason)])? => $wire),
|
||||
)*
|
||||
];
|
||||
#[cfg(test)]
|
||||
pub(crate) const EXPERIMENTAL_CLIENT_METHOD_PARAM_TYPES: &[&str] = &[
|
||||
$(
|
||||
experimental_type_entry!($(#[experimental($reason)])? $params),
|
||||
)*
|
||||
];
|
||||
#[cfg(test)]
|
||||
pub(crate) const EXPERIMENTAL_CLIENT_METHOD_RESPONSE_TYPES: &[&str] = &[
|
||||
$(
|
||||
experimental_type_entry!($(#[experimental($reason)])? $response),
|
||||
)*
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn export_client_responses(
|
||||
out_dir: &::std::path::Path,
|
||||
) -> ::std::result::Result<(), ::ts_rs::ExportError> {
|
||||
@@ -418,12 +427,14 @@ macro_rules! client_request_definitions {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn visit_client_response_types(v: &mut impl ::ts_rs::TypeVisitor) {
|
||||
$(
|
||||
v.visit::<$response>();
|
||||
)*
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
pub fn export_client_response_schemas(
|
||||
out_dir: &::std::path::Path,
|
||||
@@ -435,6 +446,7 @@ macro_rules! client_request_definitions {
|
||||
Ok(schemas)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
pub fn export_client_param_schemas(
|
||||
out_dir: &::std::path::Path,
|
||||
@@ -1330,22 +1342,26 @@ macro_rules! server_request_definitions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) const EXPERIMENTAL_SERVER_METHODS: &[&str] = &[
|
||||
$(
|
||||
experimental_method_entry!($(#[experimental($reason)])? $(=> $wire)?),
|
||||
)*
|
||||
];
|
||||
#[cfg(test)]
|
||||
pub(crate) const EXPERIMENTAL_SERVER_METHOD_PARAM_TYPES: &[&str] = &[
|
||||
$(
|
||||
experimental_type_entry!($(#[experimental($reason)])? $params),
|
||||
)*
|
||||
];
|
||||
#[cfg(test)]
|
||||
pub(crate) const EXPERIMENTAL_SERVER_METHOD_RESPONSE_TYPES: &[&str] = &[
|
||||
$(
|
||||
experimental_type_entry!($(#[experimental($reason)])? $response),
|
||||
)*
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn export_server_responses(
|
||||
out_dir: &::std::path::Path,
|
||||
) -> ::std::result::Result<(), ::ts_rs::ExportError> {
|
||||
@@ -1355,12 +1371,14 @@ macro_rules! server_request_definitions {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn visit_server_response_types(v: &mut impl ::ts_rs::TypeVisitor) {
|
||||
$(
|
||||
v.visit::<$response>();
|
||||
)*
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
pub fn export_server_response_schemas(
|
||||
out_dir: &Path,
|
||||
@@ -1375,6 +1393,7 @@ macro_rules! server_request_definitions {
|
||||
Ok(schemas)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
pub fn export_server_param_schemas(
|
||||
out_dir: &Path,
|
||||
@@ -1438,6 +1457,7 @@ macro_rules! server_notification_definitions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
pub fn export_server_notification_schemas(
|
||||
out_dir: &::std::path::Path,
|
||||
@@ -1466,6 +1486,7 @@ macro_rules! client_notification_definitions {
|
||||
)*
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn export_client_notification_schemas(
|
||||
_out_dir: &::std::path::Path,
|
||||
) -> ::anyhow::Result<Vec<GeneratedSchema>> {
|
||||
|
||||
@@ -5,6 +5,7 @@ use serde::Deserializer;
|
||||
use serde::Serialize;
|
||||
use serde::Serializer;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn nullable_string_schema(
|
||||
generator: &mut schemars::r#gen::SchemaGenerator,
|
||||
) -> schemars::schema::Schema {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -16,10 +18,8 @@ use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::TurnAbortReason;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::protocol::common::AuthMode;
|
||||
use crate::protocol::v2::ForcedChatgptWorkspaceIds;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use crate::protocol::common::AuthMode;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use codex_protocol::account::PlanType;
|
||||
@@ -7,11 +9,9 @@ use codex_protocol::protocol::RateLimitReachedType as CoreRateLimitReachedType;
|
||||
use codex_protocol::protocol::RateLimitSnapshot as CoreRateLimitSnapshot;
|
||||
use codex_protocol::protocol::RateLimitWindow as CoreRateLimitWindow;
|
||||
use codex_protocol::protocol::SpendControlLimitSnapshot as CoreSpendControlLimitSnapshot;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::shared::default_enabled;
|
||||
use schemars::JsonSchema;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use schemars::JsonSchema;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::config_types::CollaborationModeMask as CoreCollaborationModeMask;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
/// EXPERIMENTAL - list collaboration mode presets.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use super::SandboxPolicy;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use ts_rs::TS;
|
||||
|
||||
/// PTY size in character cells for `command/exec` PTY sessions.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
||||
|
||||
@@ -3,6 +3,8 @@ use super::AskForApproval;
|
||||
use super::SandboxMode;
|
||||
use super::WindowsSandboxSetupMode;
|
||||
use super::shared::default_enabled;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use codex_protocol::config_types::AutoCompactTokenLimitScope;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
@@ -13,14 +15,12 @@ use codex_protocol::config_types::WebSearchToolConfig;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use schemars::JsonSchema;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use schemars::JsonSchema;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeMap;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use schemars::JsonSchema;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
/// Read a file from the host filesystem.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use super::shared::v2_enum_from_core;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::protocol::HookEventName as CoreHookEventName;
|
||||
use codex_protocol::protocol::HookExecutionMode as CoreHookExecutionMode;
|
||||
use codex_protocol::protocol::HookHandlerType as CoreHookHandlerType;
|
||||
@@ -10,10 +12,8 @@ use codex_protocol::protocol::HookScope as CoreHookScope;
|
||||
use codex_protocol::protocol::HookSource as CoreHookSource;
|
||||
use codex_protocol::protocol::HookTrustStatus as CoreHookTrustStatus;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
v2_enum_from_core!(
|
||||
pub enum HookEventName from CoreHookEventName {
|
||||
|
||||
@@ -8,6 +8,8 @@ use super::NetworkPolicyAmendment;
|
||||
use super::RequestPermissionProfile;
|
||||
use super::UserInput;
|
||||
use super::shared::v2_enum_from_core;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use crate::protocol::item_builders::command_actions_for_path_uri;
|
||||
use crate::protocol::item_builders::convert_patch_changes;
|
||||
use crate::protocol::item_builders::review_output_text;
|
||||
@@ -44,7 +46,6 @@ use codex_protocol::protocol::SubAgentActivityKind as CoreSubAgentActivityKind;
|
||||
use codex_shell_command::parse_command::shlex_join;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::LegacyAppPathString;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
@@ -52,7 +53,6 @@ use serde_with::serde_as;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use super::shared::v2_enum_from_core;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::approvals::ElicitationRequest as CoreElicitationRequest;
|
||||
use codex_protocol::items::McpToolCallError as CoreMcpToolCallError;
|
||||
use codex_protocol::mcp::CallToolResult as CoreMcpCallToolResult;
|
||||
@@ -7,12 +9,10 @@ use codex_protocol::mcp::Resource as McpResource;
|
||||
pub use codex_protocol::mcp::ResourceContent as McpResourceContent;
|
||||
use codex_protocol::mcp::ResourceTemplate as McpResourceTemplate;
|
||||
use codex_protocol::mcp::Tool as McpTool;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::BTreeMap;
|
||||
use ts_rs::TS;
|
||||
|
||||
v2_enum_from_core!(
|
||||
pub enum McpAuthStatus from codex_protocol::protocol::McpAuthStatus {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use super::shared::v2_enum_from_core;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::openai_models::InputModality;
|
||||
use codex_protocol::openai_models::ModelAvailabilityNux as CoreModelAvailabilityNux;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::openai_models::default_input_modalities;
|
||||
use codex_protocol::protocol::ModelRerouteReason as CoreModelRerouteReason;
|
||||
use codex_protocol::protocol::ModelVerification as CoreModelVerification;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use ts_rs::TS;
|
||||
|
||||
v2_enum_from_core!(
|
||||
pub enum ModelRerouteReason from CoreModelRerouteReason {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::TurnError;
|
||||
use crate::JsonSchema;
|
||||
use crate::RequestId;
|
||||
use schemars::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use super::shared::v2_enum_from_core;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment as CoreExecPolicyAmendment;
|
||||
use codex_protocol::approvals::NetworkApprovalContext as CoreNetworkApprovalContext;
|
||||
use codex_protocol::approvals::NetworkApprovalProtocol as CoreNetworkApprovalProtocol;
|
||||
@@ -19,13 +21,11 @@ use codex_protocol::request_permissions::RequestPermissionProfile as CoreRequest
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::LegacyAppPathString;
|
||||
use codex_utils_path_uri::PathConvention;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::io;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::Path;
|
||||
use ts_rs::TS;
|
||||
|
||||
v2_enum_from_core! {
|
||||
pub enum NetworkApprovalProtocol from CoreNetworkApprovalProtocol {
|
||||
|
||||
@@ -3,17 +3,17 @@ use super::HookEventName;
|
||||
use super::HookHandlerType;
|
||||
use super::HookSource;
|
||||
use super::HookTrustStatus;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::protocol::SkillDependencies as CoreSkillDependencies;
|
||||
use codex_protocol::protocol::SkillInterface as CoreSkillInterface;
|
||||
use codex_protocol::protocol::SkillMetadata as CoreSkillMetadata;
|
||||
use codex_protocol::protocol::SkillScope as CoreSkillScope;
|
||||
use codex_protocol::protocol::SkillToolDependency as CoreSkillToolDependency;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use ts_rs::TS;
|
||||
|
||||
/// PTY size in character cells for `process/spawn` PTY sessions.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::protocol::CodexResponseHandoffMode;
|
||||
use codex_protocol::protocol::ConversationTextRole;
|
||||
use codex_protocol::protocol::RealtimeAudioFrame as CoreRealtimeAudioFrame;
|
||||
@@ -5,12 +7,10 @@ use codex_protocol::protocol::RealtimeConversationVersion;
|
||||
use codex_protocol::protocol::RealtimeOutputModality;
|
||||
use codex_protocol::protocol::RealtimeVoice;
|
||||
use codex_protocol::protocol::RealtimeVoicesList;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::BTreeMap;
|
||||
use ts_rs::TS;
|
||||
|
||||
/// EXPERIMENTAL - thread realtime audio chunk.
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use schemars::JsonSchema;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::Turn;
|
||||
use super::shared::v2_enum_from_core;
|
||||
use schemars::JsonSchema;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
v2_enum_from_core!(
|
||||
pub enum ReviewDelivery from codex_protocol::protocol::ReviewDelivery {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use codex_protocol::config_types::ApprovalsReviewer as CoreApprovalsReviewer;
|
||||
use codex_protocol::config_types::SandboxMode as CoreSandboxMode;
|
||||
@@ -5,16 +7,20 @@ use codex_protocol::protocol::AskForApproval as CoreAskForApproval;
|
||||
use codex_protocol::protocol::CodexErrorInfo as CoreCodexErrorInfo;
|
||||
use codex_protocol::protocol::GranularApprovalConfig as CoreGranularApprovalConfig;
|
||||
use codex_protocol::protocol::NonSteerableTurnKind as CoreNonSteerableTurnKind;
|
||||
use schemars::JsonSchema;
|
||||
#[cfg(test)]
|
||||
use schemars::r#gen::SchemaGenerator;
|
||||
#[cfg(test)]
|
||||
use schemars::schema::InstanceType;
|
||||
#[cfg(test)]
|
||||
use schemars::schema::Metadata;
|
||||
#[cfg(test)]
|
||||
use schemars::schema::Schema;
|
||||
#[cfg(test)]
|
||||
use schemars::schema::SchemaObject;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
#[cfg(test)]
|
||||
use serde_json::Value as JsonValue;
|
||||
use ts_rs::TS;
|
||||
|
||||
// Macro to declare a camelCased API v2 enum mirroring a core enum which
|
||||
// tends to use either snake_case or kebab-case.
|
||||
@@ -236,6 +242,7 @@ pub enum ApprovalsReviewer {
|
||||
AutoReview,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl JsonSchema for ApprovalsReviewer {
|
||||
fn schema_name() -> String {
|
||||
"ApprovalsReviewer".to_string()
|
||||
@@ -249,6 +256,7 @@ impl JsonSchema for ApprovalsReviewer {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn string_enum_schema_with_description(values: &[&str], description: &str) -> Schema {
|
||||
let mut schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
|
||||
@@ -12,6 +12,8 @@ use super::Turn;
|
||||
use super::TurnEnvironmentParams;
|
||||
use super::TurnItemsView;
|
||||
use super::shared::v2_enum_from_core;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
pub use codex_protocol::capabilities::CapabilityRootLocation;
|
||||
pub use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
@@ -31,13 +33,11 @@ use codex_protocol::protocol::TokenUsageInfo as CoreTokenUsageInfo;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::LegacyAppPathString;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -2,20 +2,22 @@ use super::CodexErrorInfo;
|
||||
use super::ThreadItem;
|
||||
use super::ThreadStatus;
|
||||
use super::TurnStatus;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use codex_protocol::protocol::SessionSource as CoreSessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource as CoreSubAgentSource;
|
||||
use codex_protocol::protocol::ThreadHistoryMode as CoreThreadHistoryMode;
|
||||
use codex_protocol::protocol::ThreadSource as CoreThreadSource;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use schemars::JsonSchema;
|
||||
#[cfg(test)]
|
||||
use schemars::r#gen::SchemaGenerator;
|
||||
#[cfg(test)]
|
||||
use schemars::schema::Schema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -103,6 +105,7 @@ pub enum ThreadSource {
|
||||
MemoryConsolidation,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl JsonSchema for ThreadSource {
|
||||
fn schema_name() -> String {
|
||||
"ThreadSource".to_string()
|
||||
|
||||
@@ -2,6 +2,8 @@ use super::ApprovalsReviewer;
|
||||
use super::AskForApproval;
|
||||
use super::SandboxPolicy;
|
||||
use super::Turn;
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::MultiAgentMode;
|
||||
@@ -16,13 +18,11 @@ use codex_protocol::user_input::TextElement as CoreTextElement;
|
||||
use codex_protocol::user_input::UserInput as CoreUserInput;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::LegacyAppPathString;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//! We do not do true JSON-RPC 2.0, as we neither send nor expect the
|
||||
//! "jsonrpc": "2.0" field.
|
||||
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use codex_protocol::protocol::W3cTraceContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
use ts_rs::TS;
|
||||
|
||||
pub const JSONRPC_VERSION: &str = "2.0";
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ use crate::ClientRequest;
|
||||
use crate::ServerNotification;
|
||||
use crate::ServerNotificationEnvelope;
|
||||
use crate::ServerRequest;
|
||||
use crate::TS;
|
||||
use crate::export::GENERATED_TS_HEADER;
|
||||
use crate::export::filter_experimental_ts_tree;
|
||||
use crate::export::generate_index_ts_tree;
|
||||
use crate::export::trim_trailing_line_whitespace;
|
||||
use crate::precomputed_exports::PrecomputedExports;
|
||||
use crate::protocol::common::visit_client_response_types;
|
||||
use crate::protocol::common::visit_server_response_types;
|
||||
use anyhow::Context;
|
||||
@@ -17,9 +19,9 @@ use std::any::TypeId;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use ts_rs::TS;
|
||||
use ts_rs::TypeVisitor;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
@@ -81,7 +83,7 @@ pub fn generate_typescript_schema_fixture_subtree_for_tests() -> Result<BTreeMap
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Regenerates `schema/typescript/` and `schema/json/`.
|
||||
/// Regenerates `schema/typescript/`, `schema/json/`, and the stable embedded exports.
|
||||
///
|
||||
/// This is intended to be used by tooling (e.g., `just write-app-server-schema`).
|
||||
/// It deletes any previously generated files so stale artifacts are removed.
|
||||
@@ -95,25 +97,77 @@ pub fn write_schema_fixtures_with_options(
|
||||
prettier: Option<&Path>,
|
||||
options: SchemaFixtureOptions,
|
||||
) -> Result<()> {
|
||||
if options.experimental_api {
|
||||
return write_experimental_precomputed_exports(schema_root, prettier);
|
||||
}
|
||||
|
||||
let typescript_out_dir = schema_root.join("typescript");
|
||||
let json_out_dir = schema_root.join("json");
|
||||
|
||||
ensure_empty_dir(&typescript_out_dir)?;
|
||||
ensure_empty_dir(&json_out_dir)?;
|
||||
|
||||
crate::generate_ts_with_options(
|
||||
crate::export::generate_ts_with_options(
|
||||
&typescript_out_dir,
|
||||
prettier,
|
||||
crate::GenerateTsOptions {
|
||||
experimental_api: options.experimental_api,
|
||||
..crate::GenerateTsOptions::default()
|
||||
},
|
||||
crate::export::GenerateTsOptions::default(),
|
||||
)?;
|
||||
crate::generate_json_with_experimental(&json_out_dir, options.experimental_api)?;
|
||||
crate::export::generate_json(&json_out_dir)?;
|
||||
|
||||
let internal_dir = tempfile::tempdir().context("create internal schema temp dir")?;
|
||||
crate::export::generate_internal_json_schema(internal_dir.path())?;
|
||||
let exports = PrecomputedExports {
|
||||
typescript: collect_export_files_recursive(&typescript_out_dir)?,
|
||||
json_schema: collect_export_files_recursive(&json_out_dir)?,
|
||||
internal_json_schema: collect_export_files_recursive(internal_dir.path())?,
|
||||
};
|
||||
write_precomputed_exports(schema_root, "stable", &exports)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_experimental_precomputed_exports(
|
||||
schema_root: &Path,
|
||||
prettier: Option<&Path>,
|
||||
) -> Result<()> {
|
||||
let temp_dir = tempfile::tempdir().context("create experimental schema temp dir")?;
|
||||
let typescript_out_dir = temp_dir.path().join("typescript");
|
||||
let json_out_dir = temp_dir.path().join("json");
|
||||
|
||||
crate::export::generate_ts_with_options(
|
||||
&typescript_out_dir,
|
||||
prettier,
|
||||
crate::export::GenerateTsOptions {
|
||||
experimental_api: true,
|
||||
..crate::export::GenerateTsOptions::default()
|
||||
},
|
||||
)?;
|
||||
crate::export::generate_json_with_experimental(&json_out_dir, /*experimental_api*/ true)?;
|
||||
|
||||
let exports = PrecomputedExports {
|
||||
typescript: collect_export_files_recursive(&typescript_out_dir)?,
|
||||
json_schema: collect_export_files_recursive(&json_out_dir)?,
|
||||
internal_json_schema: BTreeMap::new(),
|
||||
};
|
||||
write_precomputed_exports(schema_root, "experimental", &exports)
|
||||
}
|
||||
|
||||
fn write_precomputed_exports(
|
||||
schema_root: &Path,
|
||||
name: &str,
|
||||
exports: &PrecomputedExports,
|
||||
) -> Result<()> {
|
||||
let output_dir = schema_root.join("precomputed");
|
||||
std::fs::create_dir_all(&output_dir)
|
||||
.with_context(|| format!("create {}", output_dir.display()))?;
|
||||
let output_path = output_dir.join(format!("app-server-exports-{name}.json.zst"));
|
||||
let json = serde_json::to_vec(exports).context("serialize precomputed protocol exports")?;
|
||||
let compressed = zstd::stream::encode_all(Cursor::new(json), 19)
|
||||
.context("compress precomputed protocol exports")?;
|
||||
std::fs::write(&output_path, compressed)
|
||||
.with_context(|| format!("write {}", output_path.display()))
|
||||
}
|
||||
|
||||
fn ensure_empty_dir(dir: &Path) -> Result<()> {
|
||||
if dir.exists() {
|
||||
std::fs::remove_dir_all(dir)
|
||||
@@ -229,8 +283,31 @@ fn schema_array_item_sort_key(item: &Value) -> Option<String> {
|
||||
}
|
||||
|
||||
fn collect_files_recursive(root: &Path) -> Result<BTreeMap<PathBuf, Vec<u8>>> {
|
||||
let mut files = BTreeMap::new();
|
||||
files_recursive(root)?
|
||||
.into_iter()
|
||||
.map(|(relative_path, path)| Ok((relative_path, read_file_bytes(&path)?)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn collect_export_files_recursive(root: &Path) -> Result<BTreeMap<String, String>> {
|
||||
files_recursive(root)?
|
||||
.into_iter()
|
||||
.map(|(relative_path, path)| {
|
||||
let relative_path = relative_path
|
||||
.components()
|
||||
.map(|component| component.as_os_str().to_string_lossy())
|
||||
.collect::<Vec<_>>()
|
||||
.join("/");
|
||||
let contents = std::fs::read_to_string(&path)
|
||||
.with_context(|| format!("read UTF-8 export {}", path.display()))?
|
||||
.replace("\r\n", "\n");
|
||||
Ok((relative_path, contents))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn files_recursive(root: &Path) -> Result<Vec<(PathBuf, PathBuf)>> {
|
||||
let mut files = Vec::new();
|
||||
let mut stack = vec![root.to_path_buf()];
|
||||
while let Some(dir) = stack.pop() {
|
||||
for entry in std::fs::read_dir(&dir)
|
||||
@@ -262,10 +339,11 @@ fn collect_files_recursive(root: &Path) -> Result<BTreeMap<PathBuf, Vec<u8>>> {
|
||||
})?
|
||||
.to_path_buf();
|
||||
|
||||
files.insert(rel, read_file_bytes(&path)?);
|
||||
files.push((rel, path));
|
||||
}
|
||||
}
|
||||
|
||||
files.sort_by(|(left, _), (right, _)| left.cmp(right));
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
|
||||
+103
-9
@@ -1,8 +1,16 @@
|
||||
use crate::export::GenerateTsOptions;
|
||||
use crate::export::generate_internal_json_schema;
|
||||
use crate::export::generate_json_with_experimental;
|
||||
use crate::export::generate_ts_with_options;
|
||||
use crate::precomputed_exports::decode_precomputed_exports;
|
||||
use crate::schema_fixtures::SchemaFixtureOptions;
|
||||
use crate::schema_fixtures::collect_export_files_recursive;
|
||||
use crate::schema_fixtures::generate_typescript_schema_fixture_subtree_for_tests;
|
||||
use crate::schema_fixtures::read_schema_fixture_subtree;
|
||||
use crate::schema_fixtures::write_schema_fixtures_with_options;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use codex_app_server_protocol::generate_json_with_experimental;
|
||||
use codex_app_server_protocol::generate_typescript_schema_fixture_subtree_for_tests;
|
||||
use codex_app_server_protocol::read_schema_fixture_subtree;
|
||||
use pretty_assertions::assert_eq;
|
||||
use similar::TextDiff;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
@@ -35,6 +43,81 @@ fn json_schema_fixtures_match_generated() -> Result<()> {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stable_precomputed_exports_match_schema_fixtures() -> Result<()> {
|
||||
let schema_root = schema_root()?;
|
||||
let exports = decode_precomputed_exports(/*experimental_api*/ false)?;
|
||||
|
||||
assert_eq!(
|
||||
exports.typescript,
|
||||
collect_export_files_recursive(&schema_root.join("typescript"))?
|
||||
);
|
||||
assert_eq!(
|
||||
exports.json_schema,
|
||||
collect_export_files_recursive(&schema_root.join("json"))?
|
||||
);
|
||||
|
||||
let internal_dir = tempfile::tempdir().context("create internal schema temp dir")?;
|
||||
generate_internal_json_schema(internal_dir.path())?;
|
||||
assert_json_export_trees_match(
|
||||
&exports.internal_json_schema,
|
||||
&collect_export_files_recursive(internal_dir.path())?,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn experimental_precomputed_exports_match_generated() -> Result<()> {
|
||||
let output_dir = tempfile::tempdir().context("create experimental schema temp dir")?;
|
||||
let typescript_dir = output_dir.path().join("typescript");
|
||||
let json_dir = output_dir.path().join("json");
|
||||
generate_ts_with_options(
|
||||
&typescript_dir,
|
||||
/*prettier*/ None,
|
||||
GenerateTsOptions {
|
||||
experimental_api: true,
|
||||
..GenerateTsOptions::default()
|
||||
},
|
||||
)?;
|
||||
generate_json_with_experimental(&json_dir, /*experimental_api*/ true)?;
|
||||
|
||||
let exports = decode_precomputed_exports(/*experimental_api*/ true)?;
|
||||
assert_eq!(
|
||||
exports.typescript,
|
||||
collect_export_files_recursive(&typescript_dir)?
|
||||
);
|
||||
assert_json_export_trees_match(
|
||||
&exports.json_schema,
|
||||
&collect_export_files_recursive(&json_dir)?,
|
||||
)?;
|
||||
assert_eq!(exports.internal_json_schema, BTreeMap::new());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "invoked by `just write-app-server-schema`"]
|
||||
fn write_schema_fixtures_from_env() -> Result<()> {
|
||||
let schema_root = std::env::var_os("CODEX_APP_SERVER_SCHEMA_ROOT")
|
||||
.map(PathBuf::from)
|
||||
.context("CODEX_APP_SERVER_SCHEMA_ROOT must be set")?;
|
||||
let prettier = std::env::var_os("CODEX_APP_SERVER_SCHEMA_PRETTIER").map(PathBuf::from);
|
||||
let experimental = std::env::var("CODEX_APP_SERVER_SCHEMA_EXPERIMENTAL")
|
||||
.context("CODEX_APP_SERVER_SCHEMA_EXPERIMENTAL must be set")?;
|
||||
let experimental_api = match experimental.as_str() {
|
||||
"0" => false,
|
||||
"1" => true,
|
||||
value => {
|
||||
anyhow::bail!("CODEX_APP_SERVER_SCHEMA_EXPERIMENTAL must be 0 or 1, got {value:?}")
|
||||
}
|
||||
};
|
||||
|
||||
write_schema_fixtures_with_options(
|
||||
&schema_root,
|
||||
prettier.as_deref(),
|
||||
SchemaFixtureOptions { experimental_api },
|
||||
)
|
||||
}
|
||||
|
||||
fn assert_schema_fixtures_match_generated(
|
||||
label: &'static str,
|
||||
generate: impl FnOnce(&Path) -> Result<()>,
|
||||
@@ -52,9 +135,24 @@ fn assert_schema_fixtures_match_generated(
|
||||
})?;
|
||||
|
||||
let generated_tree = read_tree(temp_dir.path(), label)?;
|
||||
assert_schema_trees_match(label, &fixture_tree, &generated_tree)
|
||||
}
|
||||
|
||||
assert_schema_trees_match(label, &fixture_tree, &generated_tree)?;
|
||||
|
||||
fn assert_json_export_trees_match(
|
||||
expected: &BTreeMap<String, String>,
|
||||
actual: &BTreeMap<String, String>,
|
||||
) -> Result<()> {
|
||||
assert_eq!(
|
||||
expected.keys().collect::<Vec<_>>(),
|
||||
actual.keys().collect::<Vec<_>>()
|
||||
);
|
||||
for (path, expected) in expected {
|
||||
let expected: serde_json::Value = serde_json::from_str(expected)
|
||||
.with_context(|| format!("parse precomputed JSON export {path}"))?;
|
||||
let actual: serde_json::Value = serde_json::from_str(&actual[path])
|
||||
.with_context(|| format!("parse freshly generated JSON export {path}"))?;
|
||||
assert_eq!(expected, actual, "JSON export differs: {path}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -86,7 +184,6 @@ Run `just write-app-server-schema` to overwrite with your changes.\n\n{diff}"
|
||||
);
|
||||
}
|
||||
|
||||
// If the file sets match, diff contents for each file for a nicer error.
|
||||
for (path, expected) in fixture_tree {
|
||||
let actual = generated_tree
|
||||
.get(path)
|
||||
@@ -113,8 +210,6 @@ Run `just write-app-server-schema` to overwrite with your changes.\n\n{diff}",
|
||||
}
|
||||
|
||||
fn schema_root() -> Result<PathBuf> {
|
||||
// In Bazel runfiles (especially manifest-only mode), resolving directories is not
|
||||
// reliable. Resolve a known file, then walk up to the schema root.
|
||||
let typescript_index = codex_utils_cargo_bin::find_resource!("schema/typescript/index.ts")
|
||||
.context("resolve TypeScript schema index.ts")?;
|
||||
let schema_root = typescript_index
|
||||
@@ -123,7 +218,6 @@ fn schema_root() -> Result<PathBuf> {
|
||||
.context("derive schema root from schema/typescript/index.ts")?
|
||||
.to_path_buf();
|
||||
|
||||
// Sanity check that the JSON fixtures resolve to the same schema root.
|
||||
let json_bundle =
|
||||
codex_utils_cargo_bin::find_resource!("schema/json/codex_app_server_protocol.schemas.json")
|
||||
.context("resolve JSON schema bundle")?;
|
||||
@@ -2438,7 +2438,7 @@ For server-initiated request payloads, annotate the field the same way so schema
|
||||
|
||||
```bash
|
||||
just write-app-server-schema
|
||||
# Include experimental API fields/methods in fixtures.
|
||||
# Refresh the embedded exports that include experimental API fields/methods.
|
||||
just write-app-server-schema --experimental
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user