Compare commits

..
2 Commits
16 changed files with 1286 additions and 176 deletions

No files matched your search

Generated
+131 -105
View File
@@ -1536,7 +1536,7 @@ checksum = "e9b18233253483ce2f65329a24072ec414db782531bdbb7d0bbc4bd2ce6b7e21"
[[package]] [[package]]
name = "codex-agent-identity" name = "codex-agent-identity"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
@@ -1555,7 +1555,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-analytics" name = "codex-analytics"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-app-server-protocol", "codex-app-server-protocol",
"codex-git-utils", "codex-git-utils",
@@ -1574,7 +1574,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-api" name = "codex-api"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"async-trait", "async-trait",
@@ -1603,7 +1603,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-app-server-protocol" name = "codex-app-server-protocol"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -1627,7 +1627,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-apply-patch" name = "codex-apply-patch"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"codex-exec-server", "codex-exec-server",
@@ -1642,7 +1642,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-async-utils" name = "codex-async-utils"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"tokio", "tokio",
@@ -1652,7 +1652,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-aws-auth" name = "codex-aws-auth"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"aws-config", "aws-config",
"aws-credential-types", "aws-credential-types",
@@ -1666,7 +1666,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-backend-client" name = "codex-backend-client"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"codex-api", "codex-api",
@@ -1683,7 +1683,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-backend-openapi-models" name = "codex-backend-openapi-models"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
@@ -1693,7 +1693,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-client" name = "codex-client"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@@ -1719,7 +1719,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-code-mode" name = "codex-code-mode"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"async-trait", "async-trait",
@@ -1736,12 +1736,12 @@ dependencies = [
[[package]] [[package]]
name = "codex-collaboration-mode-templates" name = "codex-collaboration-mode-templates"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
[[package]] [[package]]
name = "codex-config" name = "codex-config"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -1785,7 +1785,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-connectors" name = "codex-connectors"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"codex-app-server-protocol", "codex-app-server-protocol",
@@ -1799,7 +1799,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-core" name = "codex-core"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@@ -1897,7 +1897,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-core-plugins" name = "codex-core-plugins"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -1918,6 +1918,7 @@ dependencies = [
"dirs", "dirs",
"flate2", "flate2",
"reqwest 0.12.28", "reqwest 0.12.28",
"semver",
"serde", "serde",
"serde_json", "serde_json",
"tar", "tar",
@@ -1933,7 +1934,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-core-skills" name = "codex-core-skills"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"codex-analytics", "codex-analytics",
@@ -1963,13 +1964,14 @@ dependencies = [
[[package]] [[package]]
name = "codex-exec-server" name = "codex-exec-server"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"async-trait", "async-trait",
"axum", "axum",
"base64", "base64",
"bytes", "bytes",
"codex-api",
"codex-app-server-protocol", "codex-app-server-protocol",
"codex-client", "codex-client",
"codex-file-system", "codex-file-system",
@@ -1995,7 +1997,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-execpolicy" name = "codex-execpolicy"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -2011,7 +2013,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-experimental-api-macros" name = "codex-experimental-api-macros"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2021,8 +2023,9 @@ dependencies = [
[[package]] [[package]]
name = "codex-extension-api" name = "codex-extension-api"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait",
"codex-protocol", "codex-protocol",
"codex-tools", "codex-tools",
] ]
@@ -2030,7 +2033,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-features" name = "codex-features"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-otel", "codex-otel",
"codex-protocol", "codex-protocol",
@@ -2043,7 +2046,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-feedback" name = "codex-feedback"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"codex-login", "codex-login",
@@ -2056,7 +2059,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-file-search" name = "codex-file-search"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -2071,7 +2074,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-file-system" name = "codex-file-system"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"codex-protocol", "codex-protocol",
@@ -2082,7 +2085,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-git-utils" name = "codex-git-utils"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -2106,7 +2109,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-hooks" name = "codex-hooks"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -2128,7 +2131,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-keyring-store" name = "codex-keyring-store"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"keyring", "keyring",
"tracing", "tracing",
@@ -2137,7 +2140,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-login" name = "codex-login"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64", "base64",
@@ -2171,7 +2174,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-mcp" name = "codex-mcp"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-channel", "async-channel",
@@ -2202,7 +2205,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-memories-read" name = "codex-memories-read"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-protocol", "codex-protocol",
"codex-shell-command", "codex-shell-command",
@@ -2215,7 +2218,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-model-provider" name = "codex-model-provider"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"codex-agent-identity", "codex-agent-identity",
@@ -2237,7 +2240,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-model-provider-info" name = "codex-model-provider-info"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-api", "codex-api",
"codex-app-server-protocol", "codex-app-server-protocol",
@@ -2250,7 +2253,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-models-manager" name = "codex-models-manager"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",
@@ -2270,7 +2273,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-network-proxy" name = "codex-network-proxy"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -2300,7 +2303,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-otel" name = "codex-otel"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"chrono", "chrono",
"codex-api", "codex-api",
@@ -2332,7 +2335,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-plugin" name = "codex-plugin"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-config", "codex-config",
"codex-utils-absolute-path", "codex-utils-absolute-path",
@@ -2343,7 +2346,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-protocol" name = "codex-protocol"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"chardetng", "chardetng",
"chrono", "chrono",
@@ -2380,7 +2383,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-response-debug-context" name = "codex-response-debug-context"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"base64", "base64",
"codex-api", "codex-api",
@@ -2391,7 +2394,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-rmcp-client" name = "codex-rmcp-client"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum", "axum",
@@ -2426,7 +2429,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-rollout" name = "codex-rollout"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -2450,7 +2453,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-rollout-trace" name = "codex-rollout-trace"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"codex-code-mode", "codex-code-mode",
@@ -2465,7 +2468,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-sandboxing" name = "codex-sandboxing"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-network-proxy", "codex-network-proxy",
"codex-protocol", "codex-protocol",
@@ -2482,7 +2485,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-shell-command" name = "codex-shell-command"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"base64", "base64",
"codex-protocol", "codex-protocol",
@@ -2501,7 +2504,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-shell-escalation" name = "codex-shell-escalation"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -2521,7 +2524,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-skills" name = "codex-skills"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-utils-absolute-path", "codex-utils-absolute-path",
"include_dir", "include_dir",
@@ -2531,7 +2534,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-state" name = "codex-state"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -2553,7 +2556,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-terminal-detection" name = "codex-terminal-detection"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"tracing", "tracing",
] ]
@@ -2561,7 +2564,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-thread-store" name = "codex-thread-store"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",
@@ -2580,7 +2583,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-tools" name = "codex-tools"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"codex-app-server-protocol", "codex-app-server-protocol",
@@ -2588,6 +2591,7 @@ dependencies = [
"codex-features", "codex-features",
"codex-protocol", "codex-protocol",
"codex-utils-absolute-path", "codex-utils-absolute-path",
"codex-utils-output-truncation",
"codex-utils-pty", "codex-utils-pty",
"codex-utils-string", "codex-utils-string",
"rmcp", "rmcp",
@@ -2600,7 +2604,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-absolute-path" name = "codex-utils-absolute-path"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"dirs", "dirs",
"dunce", "dunce",
@@ -2612,7 +2616,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-cache" name = "codex-utils-cache"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"lru", "lru",
"sha1", "sha1",
@@ -2622,7 +2626,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-home-dir" name = "codex-utils-home-dir"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-utils-absolute-path", "codex-utils-absolute-path",
"dirs", "dirs",
@@ -2631,7 +2635,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-image" name = "codex-utils-image"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"base64", "base64",
"codex-utils-cache", "codex-utils-cache",
@@ -2644,7 +2648,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-output-truncation" name = "codex-utils-output-truncation"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-protocol", "codex-protocol",
"codex-utils-string", "codex-utils-string",
@@ -2653,7 +2657,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-path" name = "codex-utils-path"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-utils-absolute-path", "codex-utils-absolute-path",
"dunce", "dunce",
@@ -2663,7 +2667,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-plugins" name = "codex-utils-plugins"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"codex-exec-server", "codex-exec-server",
"codex-login", "codex-login",
@@ -2675,7 +2679,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-pty" name = "codex-utils-pty"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"filedescriptor", "filedescriptor",
@@ -2691,7 +2695,7 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-rustls-provider" name = "codex-utils-rustls-provider"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"rustls", "rustls",
] ]
@@ -2699,12 +2703,12 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-stream-parser" name = "codex-utils-stream-parser"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
[[package]] [[package]]
name = "codex-utils-string" name = "codex-utils-string"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"regex-lite", "regex-lite",
"serde", "serde",
@@ -2714,12 +2718,12 @@ dependencies = [
[[package]] [[package]]
name = "codex-utils-template" name = "codex-utils-template"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
[[package]] [[package]]
name = "codex-windows-sandbox" name = "codex-windows-sandbox"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/openai/codex.git?rev=326e31ab65dcbdf70c4a034b7adc5c8bd335d996#326e31ab65dcbdf70c4a034b7adc5c8bd335d996" source = "git+https://github.com/openai/codex.git?rev=0e9d2221789734dc08313243ecbdc7cc20ee48d1#0e9d2221789734dc08313243ecbdc7cc20ee48d1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
@@ -2747,12 +2751,12 @@ name = "codexport"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"codex-app-server-protocol",
"codex-backend-client", "codex-backend-client",
"codex-config", "codex-config",
"codex-core", "codex-core",
"codex-login", "codex-login",
"codex-protocol", "codex-protocol",
"icu_calendar",
"rand 0.10.1", "rand 0.10.1",
"serde", "serde",
"serde_json", "serde_json",
@@ -3451,9 +3455,9 @@ dependencies = [
[[package]] [[package]]
name = "diplomat" name = "diplomat"
version = "0.14.0" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9adb46b05e2f53dcf6a7dfc242e4ce9eb60c369b6b6eb10826a01e93167f59c6" checksum = "7935649d00000f5c5d735448ad3dc07b9738160727017914cf42138b8e8e6611"
dependencies = [ dependencies = [
"diplomat_core", "diplomat_core",
"proc-macro2", "proc-macro2",
@@ -3463,15 +3467,15 @@ dependencies = [
[[package]] [[package]]
name = "diplomat-runtime" name = "diplomat-runtime"
version = "0.14.0" version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0569bd3caaf13829da7ee4e83dbf9197a0e1ecd72772da6d08f0b4c9285c8d29" checksum = "970ac38ad677632efcee6d517e783958da9bc78ec206d8d5e35b459ffc5e4864"
[[package]] [[package]]
name = "diplomat_core" name = "diplomat_core"
version = "0.14.0" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51731530ed7f2d4495019abc7df3744f53338e69e2863a6a64ae91821c763df1" checksum = "9cf41b94101a4bce993febaf0098092b0bb31deaf0ecaf6e0a2562465f61b383"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -5567,9 +5571,9 @@ dependencies = [
[[package]] [[package]]
name = "icu_calendar" name = "icu_calendar"
version = "2.1.1" version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f0e52e009b6b16ba9c0693578796f2dd4aaa59a7f8f920423706714a89ac4e" checksum = "a2b2acc6263f494f1df50685b53ff8e57869e47d5c6fe39c23d518ae9a4f3e45"
dependencies = [ dependencies = [
"calendrical_calculations", "calendrical_calculations",
"displaydoc", "displaydoc",
@@ -5577,25 +5581,25 @@ dependencies = [
"icu_locale", "icu_locale",
"icu_locale_core", "icu_locale_core",
"icu_provider", "icu_provider",
"ixdtf",
"tinystr", "tinystr",
"zerovec", "zerovec",
] ]
[[package]] [[package]]
name = "icu_calendar_data" name = "icu_calendar_data"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527f04223b17edfe0bd43baf14a0cb1b017830db65f3950dc00224860a9a446d" checksum = "118577bcf3a0fa7c6ac0a7d6e951814da84ee56b9b1f68fb4d8d10b08cefaf4d"
[[package]] [[package]]
name = "icu_collections" name = "icu_collections"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"potential_utf", "potential_utf",
"utf8_iter",
"yoke", "yoke",
"zerofrom", "zerofrom",
"zerovec", "zerovec",
@@ -5603,14 +5607,16 @@ dependencies = [
[[package]] [[package]]
name = "icu_decimal" name = "icu_decimal"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38c52231bc348f9b982c1868a2af3195199623007ba2c7650f432038f5b3e8e" checksum = "288247df2e32aa776ac54fdd64de552149ac43cb840f2761811f0e8d09719dd4"
dependencies = [ dependencies = [
"displaydoc",
"fixed_decimal", "fixed_decimal",
"icu_decimal_data", "icu_decimal_data",
"icu_locale", "icu_locale",
"icu_locale_core", "icu_locale_core",
"icu_plurals",
"icu_provider", "icu_provider",
"writeable", "writeable",
"zerovec", "zerovec",
@@ -5618,15 +5624,15 @@ dependencies = [
[[package]] [[package]]
name = "icu_decimal_data" name = "icu_decimal_data"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2905b4044eab2dd848fe84199f9195567b63ab3a93094711501363f63546fef7" checksum = "6f14a5ca9e8af29eef62064f269078424283d90dbaffeac5225addf62aaabc22"
[[package]] [[package]]
name = "icu_locale" name = "icu_locale"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532b11722e350ab6bf916ba6eb0efe3ee54b932666afec989465f9243fe6dd60" checksum = "d5a396343c7208121dc86e35623d3dfe19814a7613cfd14964994cdc9c9a2e26"
dependencies = [ dependencies = [
"icu_collections", "icu_collections",
"icu_locale_core", "icu_locale_core",
@@ -5653,15 +5659,15 @@ dependencies = [
[[package]] [[package]]
name = "icu_locale_data" name = "icu_locale_data"
version = "2.1.2" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c5f1d16b4c3a2642d3a719f18f6b06070ab0aef246a6418130c955ae08aa831" checksum = "d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993"
[[package]] [[package]]
name = "icu_normalizer" name = "icu_normalizer"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [ dependencies = [
"icu_collections", "icu_collections",
"icu_normalizer_data", "icu_normalizer_data",
@@ -5673,15 +5679,34 @@ dependencies = [
[[package]] [[package]]
name = "icu_normalizer_data" name = "icu_normalizer_data"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
[[package]]
name = "icu_plurals"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a50023f1d49ad5c4333380328a0d4a19e4b9d6d842ec06639affd5ba47c8103"
dependencies = [
"fixed_decimal",
"icu_locale",
"icu_plurals_data",
"icu_provider",
"zerovec",
]
[[package]]
name = "icu_plurals_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8485497155dc865f901decb93ecc20d3e467df67bfeceb91e3ba34e2b11e8e1d"
[[package]] [[package]]
name = "icu_properties" name = "icu_properties"
version = "2.1.2" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [ dependencies = [
"icu_collections", "icu_collections",
"icu_locale_core", "icu_locale_core",
@@ -5693,9 +5718,9 @@ dependencies = [
[[package]] [[package]]
name = "icu_properties_data" name = "icu_properties_data"
version = "2.1.2" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
[[package]] [[package]]
name = "icu_provider" name = "icu_provider"
@@ -9961,14 +9986,14 @@ dependencies = [
[[package]] [[package]]
name = "temporal_capi" name = "temporal_capi"
version = "0.1.2" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a151e402c2bdb6a3a2a2f3f225eddaead2e7ce7dd5d3fa2090deb11b17aa4ed8" checksum = "8a2a1f001e756a9f5f2d175a9965c4c0b3a054f09f30de3a75ab49765f2deb36"
dependencies = [ dependencies = [
"diplomat", "diplomat",
"diplomat-runtime", "diplomat-runtime",
"icu_calendar", "icu_calendar",
"icu_locale", "icu_locale_core",
"num-traits", "num-traits",
"temporal_rs", "temporal_rs",
"timezone_provider", "timezone_provider",
@@ -9978,13 +10003,14 @@ dependencies = [
[[package]] [[package]]
name = "temporal_rs" name = "temporal_rs"
version = "0.1.2" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88afde3bd75d2fc68d77a914bece426aa08aa7649ffd0cdd4a11c3d4d33474d1" checksum = "9a902a45282e5175186b21d355efc92564601efe6e2d92818dc9e333d50bd4de"
dependencies = [ dependencies = [
"calendrical_calculations",
"core_maths", "core_maths",
"icu_calendar", "icu_calendar",
"icu_locale", "icu_locale_core",
"ixdtf", "ixdtf",
"num-traits", "num-traits",
"timezone_provider", "timezone_provider",
@@ -10105,9 +10131,9 @@ dependencies = [
[[package]] [[package]]
name = "timezone_provider" name = "timezone_provider"
version = "0.1.2" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9ba0000e9e73862f3e7ca1ff159e2ddf915c9d8bb11e38a7874760f445d993" checksum = "c48f9b04628a2b813051e4dfe97c65281e49625eabd09ec343190e31e399a8c2"
dependencies = [ dependencies = [
"tinystr", "tinystr",
"zerotrie", "zerotrie",
@@ -10834,9 +10860,9 @@ dependencies = [
[[package]] [[package]]
name = "v8" name = "v8"
version = "146.4.0" version = "147.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d97bcac5cdc5a195a4813f1855a6bc658f240452aac36caa12fd6c6f16026ab1" checksum = "2df8fffd507fb18ed000673a83d937f58e60fb07f3306b2274284125b15137cd"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"bitflags 2.11.1", "bitflags 2.11.1",
@@ -12014,9 +12040,9 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]] [[package]]
name = "zoneinfo64" name = "zoneinfo64"
version = "0.2.1" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2e5597efbe7c421da8a7fd396b20b571704e787c21a272eecf35dfe9d386f0" checksum = "ed6eb2607e906160c457fd573e9297e65029669906b9ac8fb1b5cd5e055f0705"
dependencies = [ dependencies = [
"calendrical_calculations", "calendrical_calculations",
"icu_locale_core", "icu_locale_core",
+1 -1
View File
@@ -20,7 +20,7 @@ codexport.workspace = true
[workspace.dependencies] [workspace.dependencies]
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149" serde_json = "1.0.149"
anyhow = "1.0.102" anyhow = {version = "1.0.102", features = ["std"] }
tokio = { version = "1.52.3", features = ["full"] } tokio = { version = "1.52.3", features = ["full"] }
rand = "0.10.1" rand = "0.10.1"
+21 -10
View File
@@ -11,33 +11,44 @@ rand.workspace = true
tokio.workspace = true tokio.workspace = true
# can this not show up # can this not show up
winapi = { version = "=0.3.9", features = ["std"] } # because CODEX toolchain is lowk different winapi = { version = "^0.3.9", features = ["std"] } # because CODEX toolchain is lowk different
icu_calendar = "=2.1.1" # you have to understand i had to look at the codex-rs lockfile for this bs # 23898 on openai/codex i filed it hehehe
[[bin]]
name = "bin2"
path = "src/bin2.rs"
[dependencies.codex-backend-client] [dependencies.codex-backend-client]
git = "https://github.com/openai/codex.git" git = "https://github.com/openai/codex.git"
rev = "326e31ab65dcbdf70c4a034b7adc5c8bd335d996" # today is a good day to update the rev rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1" # today is a good day to update the rev
[dependencies.codex-login] [dependencies.codex-login]
git = "https://github.com/openai/codex.git" git = "https://github.com/openai/codex.git"
rev = "326e31ab65dcbdf70c4a034b7adc5c8bd335d996" rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1"
[dependencies.codex-config] [dependencies.codex-config]
git = "https://github.com/openai/codex.git" git = "https://github.com/openai/codex.git"
rev = "326e31ab65dcbdf70c4a034b7adc5c8bd335d996" rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1"
[dependencies.codex-core] [dependencies.codex-core]
git = "https://github.com/openai/codex.git" git = "https://github.com/openai/codex.git"
rev = "326e31ab65dcbdf70c4a034b7adc5c8bd335d996" rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1"
[dependencies.codex-protocol] [dependencies.codex-protocol]
git = "https://github.com/openai/codex.git" git = "https://github.com/openai/codex.git"
rev = "326e31ab65dcbdf70c4a034b7adc5c8bd335d996" rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1"
[dependencies.codex-app-server-protocol]
git = "https://github.com/openai/codex.git"
rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1"
# codexport shit # codexport shit
# from codex-rs/Cargo.toml # from codex-rs/Cargo.toml
[patch.crates-io] # [patch.crates-io]
# crossterm = { git = "https://github.com/nornagon/crossterm", rev = "87db8bfa6dc99427fd3b071681b07fc31c6ce995" } # crossterm = { git = "https://github.com/nornagon/crossterm", rev = "87db8bfa6dc99427fd3b071681b07fc31c6ce995" }
# ratatui = { git = "https://github.com/nornagon/ratatui", rev = "9b2ad1298408c45918ee9f8241a6f95498cdbed2" } # ratatui = { git = "https://github.com/nornagon/ratatui", rev = "9b2ad1298408c45918ee9f8241a6f95498cdbed2" }
tokio-tungstenite = { git = "https://github.com/openai-oss-forks/tokio-tungstenite", rev = "132f5b39c862e3a970f731d709608b3e6276d5f6" } # tokio-tungstenite = { git = "https://github.com/openai-oss-forks/tokio-tungstenite", rev = "132f5b39c862e3a970f731d709608b3e6276d5f6" }
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "9200079d3b54a1ff51072e24d81fd354f085156f" } # tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "9200079d3b54a1ff51072e24d81fd354f085156f" }
+25
View File
@@ -0,0 +1,25 @@
use anyhow::Context;
/// inspect a live auth.json file and print out the account info and quota info. this is to help with debugging and understanding the auth.json file and the auth manager.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let arg1 = std::env::args().nth(1).context("non supplied")?;
let file_content = std::fs::read_to_string(arg1).context("failed to read file")?;
let auth_json: codexport::AuthDotJson =
serde_json::from_str(&file_content).context("failed to parse auth json")?;
let (auth_mgr, _path) = codexport::auth_mgr_from_auth_json(auth_json).await.context("failed to create auth manager from auth json")?;
let backend = codexport::backend_client_from_auth_mgr(&auth_mgr).await.context("failed to create backend client from auth manager")?;
let codex_auth = auth_mgr.auth().await.context("failed to get auth info from auth manager")?;
let account_id = codex_auth.get_account_id().context("failed to get account id from auth info")?;
let cgpt_id = codex_auth.get_chatgpt_user_id().context("failed to get chatgpt user id from auth info")?;
let email = codex_auth.get_account_email().context("failed to get account email from auth info")?;
// let quota = codexport::quota(&backend).await.context("failed to get quota")?;
let quota = backend.get_rate_limits_many().await.context("failed to get rate limits")?;
println!("email: {email}");
println!("account_id: {account_id}");
println!("cgpt_id: {cgpt_id}");
println!("quota: {quota:#?}");
Ok(())
}
+142 -40
View File
@@ -9,8 +9,11 @@
use std::{path::Path, sync::Arc}; use std::{path::Path, sync::Arc};
use anyhow::Context; use anyhow::Context;
pub use codex_app_server_protocol::AuthMode;
use codex_backend_client::Client as BackendClient; use codex_backend_client::Client as BackendClient;
use codex_core::config::Config; // this package takes 14 thousand years to compile; cant do Anything about it use codex_core::config::Config;
// this package takes 14 thousand years to compile; cant do Anything about it
use codex_login::AuthCredentialsStoreMode;
pub use codex_login::AuthDotJson; pub use codex_login::AuthDotJson;
pub use codex_login::AuthManager as CodexAuthManager; pub use codex_login::AuthManager as CodexAuthManager;
use codex_login::AuthManager; use codex_login::AuthManager;
@@ -24,22 +27,26 @@ pub struct Huge {
// this uses config // this uses config
async fn stock_backend_client() -> anyhow::Result<Huge> { async fn stock_backend_client() -> anyhow::Result<Huge> {
let config = Config::load_with_cli_overrides(Vec::new()) let config = default_config().await.context("Failed to load config")?;
.await
.context("Failed to load config")?;
let auth_manager = let auth_manager =
AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await; AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await;
let client = backend_client_from_auth_mgr_and_default_config(&auth_manager).await?; let client = backend_client(&auth_manager, None).await?;
Ok(Huge { Ok(Huge {
config, config,
auth_manager, auth_manager,
client, client,
}) })
} }
/// this uses config
pub async fn backend_client_from_auth_mgr_and_default_config( pub async fn backend_client(
auth_mgr: &AuthManager, auth_mgr: &AuthManager,
base_url: Option<String>,
) -> anyhow::Result<BackendClient> { ) -> anyhow::Result<BackendClient> {
let base_url = if let Some(url) = base_url {
url
} else {
"https://chatgpt.com/backend-api".to_string()
};
let auth = auth_mgr let auth = auth_mgr
.auth() .auth()
.await .await
@@ -47,26 +54,29 @@ pub async fn backend_client_from_auth_mgr_and_default_config(
// Important: only ChatGPT-style auth modes can hit the Codex backend (“wham” or “api/codex”) // Important: only ChatGPT-style auth modes can hit the Codex backend (“wham” or “api/codex”)
anyhow::ensure!(auth.uses_codex_backend(), "Need ChatGPT/Codex-backend auth"); anyhow::ensure!(auth.uses_codex_backend(), "Need ChatGPT/Codex-backend auth");
let config = Config::load_with_cli_overrides(Vec::new())
.await
.context("Failed to load config")?;
let client = BackendClient::from_auth(config.chatgpt_base_url.clone(), &auth)?;
Ok(client)
}
pub async fn backend_client_from_auth_mgr(auth_mgr: &AuthManager) -> anyhow::Result<BackendClient> {
let base_url = "https://chatgpt.com/backend-api".to_string();
let auth = auth_mgr
.auth()
.await
.ok_or_else(|| anyhow::anyhow!("Not logged in; run `codex login`"))?;
anyhow::ensure!(auth.uses_codex_backend(), "Need ChatGPT/Codex-backend auth");
let client = BackendClient::from_auth(base_url, &auth)?; let client = BackendClient::from_auth(base_url, &auth)?;
Ok(client) Ok(client)
} }
use codex_protocol::account::PlanType as AccountPlanType; async fn default_base_url() -> Result<String, anyhow::Error> {
pub use codex_protocol::protocol::RateLimitSnapshot as RateLimit; // wym snapshot let config = default_config().await.context("Failed to load config")?;
let base_url = config.chatgpt_base_url;
Ok(base_url)
}
async fn default_config() -> anyhow::Result<Config> {
Ok(Config::load_with_cli_overrides(Vec::new()).await?)
}
pub async fn backend_client_from_auth_mgr(auth_mgr: &AuthManager) -> anyhow::Result<BackendClient> {
let base_url = "https://chatgpt.com/backend-api".to_string();
backend_client(auth_mgr, Some(base_url)).await
}
pub use codex_protocol::account::PlanType as AccountPlanType;
pub use codex_protocol::protocol::RateLimitReachedType;
pub use codex_protocol::protocol::RateLimitSnapshot as RateLimit;
pub use codex_protocol::protocol::RateLimitWindow; // wym snapshot
pub async fn quota(client: &BackendClient) -> anyhow::Result<RateLimit> { pub async fn quota(client: &BackendClient) -> anyhow::Result<RateLimit> {
let quota = client.get_rate_limits().await?; let quota = client.get_rate_limits().await?;
@@ -92,15 +102,20 @@ use codex_login::{CLIENT_ID, ServerOptions, run_login_server};
/// - this way you CAN logout using the auth manager. /// - this way you CAN logout using the auth manager.
/// ///
/// tips: use a random codex_home for the ephemeral store if you want to login multiple times in the same process... why would you do that? idk, but hey, you do you. use the [random_path_generator] function below for that. /// tips: use a random codex_home for the ephemeral store if you want to login multiple times in the same process... why would you do that? idk, but hey, you do you. use the [random_path_generator] function below for that.
pub async fn chatgpt_login_to_auth_json(codex_home: &Path) -> Result<Option<AuthDotJson>> { pub async fn chatgpt_login_to_auth_json(
let store = codex_login::AuthCredentialsStoreMode::Ephemeral; codex_home: &Path,
port: Option<u16>,
) -> Result<Option<AuthDotJson>> {
let store = AuthCredentialsStoreMode::Ephemeral;
let mut opts = ServerOptions::new( let mut opts = ServerOptions::new(
codex_home.to_path_buf(), codex_home.to_path_buf(),
CLIENT_ID.to_string(), // oh hell nah son CLIENT_ID.to_string(), // oh hell nah son
None, None,
store, store,
); );
// if let some(port) = port { opts.port = port; } // if we decide to put port in the args later, this is how we would do it. for now, it is hardcoded to 1455, which is fine. if let Some(port) = port {
opts.port = port;
}
// run login server, what does it do? // run login server, what does it do?
// first it does some bs // first it does some bs
@@ -133,6 +148,7 @@ pub async fn chatgpt_login_to_auth_json(codex_home: &Path) -> Result<Option<Auth
opts.open_browser = false; opts.open_browser = false;
let server = run_login_server(opts)?; let server = run_login_server(opts)?;
eprintln!("Open this URL to login:\n\n{}\n", server.auth_url); eprintln!("Open this URL to login:\n\n{}\n", server.auth_url);
// rewriting the topmost function with our logic could help if we want to do something custom with the server, but for now, this is good enough. we can just block until the server is done, which means the login flow is done, and the auth.json is saved in the ephemeral store.
server.block_until_done().await?; server.block_until_done().await?;
@@ -140,6 +156,9 @@ pub async fn chatgpt_login_to_auth_json(codex_home: &Path) -> Result<Option<Auth
codex_login::load_auth_dot_json(codex_home, store).context("can not load saved auth.json") codex_login::load_auth_dot_json(codex_home, store).context("can not load saved auth.json")
} }
/// get rate limit! the [RateLimit] struct has a lot of info, including the
/// plan type, and the actual limits and usage. this is a good way to check if
/// your login worked, and what plan you have.
pub async fn get_rate_limit() -> anyhow::Result<RateLimit> { pub async fn get_rate_limit() -> anyhow::Result<RateLimit> {
let huge = stock_backend_client().await?; let huge = stock_backend_client().await?;
quota(&huge.client).await quota(&huge.client).await
@@ -156,21 +175,23 @@ pub async fn use_login_server(
.into_path_buf(), .into_path_buf(),
}; };
let cih = || codex_home.clone(); let cih = || codex_home.clone();
let auth_json = chatgpt_login_to_auth_json(&cih()) let auth_json = chatgpt_login_to_auth_json(&cih(), None)
.await? .await?
.context("no auth json found")?; .context("no auth json found")?;
// explicitly check if there is an auth.json, because if there isn't, then the auth manager will just be empty and not logged in, which is not what we want. we want to know if the login failed. // explicitly check if there is an auth.json, because if there isn't, then the auth manager will just be empty and not logged in, which is not what we want. we want to know if the login failed.
let auth_mgr = auth_manager(&cih(), codex_login::AuthCredentialsStoreMode::Ephemeral).await; let auth_mgr = auth_manager(&cih(), AuthCredentialsStoreMode::Ephemeral).await;
auth_mgr.auth().await.ok_or_else(|| { auth_mgr.auth().await.ok_or_else(|| {
anyhow::anyhow!("Login failed; no auth found in auth manager after login") anyhow::anyhow!("Login failed; no auth found in auth manager after login")
})?; // this would never reach since we checked for auth.json. but just in case, we check again. })?; // this would never reach since we checked for auth.json. but just in case, we check again.
Ok((auth_mgr, auth_json)) Ok((auth_mgr, auth_json))
} }
use codex_config::types::AuthCredentialsStoreMode;
use std::path::PathBuf; use std::path::PathBuf;
/// lightweight helper to construct an auth manager from codex home and store mode. use this if you already have an auth.json in the ephemeral store, or if you want to use the same login across multiple auth managers in the same process (by using the same codex home). /// lightweight helper to construct an auth manager from codex home and store
/// mode. use this if you already have an auth.json in the ephemeral store, or
/// if you want to use the same login across multiple auth managers in the same
/// process (by using the same codex home).
pub async fn auth_manager( pub async fn auth_manager(
codex_home: &Path, codex_home: &Path,
store_mode: AuthCredentialsStoreMode, store_mode: AuthCredentialsStoreMode,
@@ -184,38 +205,58 @@ pub async fn auth_manager(
.await .await
} }
/// use this so ephemeral store key is different; idk why you need to login multiple times per session and not just dumping the auth.json to a folder somewhere but hey, you do you
pub fn random_ass_codex_home(real_codex_home: &Path, yo: &str) -> PathBuf {
real_codex_home.join(yo)
}
/// use to get a COMPLETELY random path. /// use to get a COMPLETELY random path.
pub fn random_path_generator(name: Option<String>) -> PathBuf { pub fn random_path_generator(name: Option<String>) -> PathBuf {
PathBuf::from_iter( PathBuf::from_iter(
std::iter::repeat_with(|| rand::random::<u64>().to_string()) std::iter::repeat_with(|| rand::random::<u64>().to_string())
.take(3) .take(5) // you would NEVER get the same path as me
.chain(name), .chain(name),
) )
} }
/// Creates an authentication manager from a pre-existing auth.json. /// Creates an authentication manager from a pre-existing auth.json.
///
/// THIS USES ephemeral store. It will not write to disk. To write to disk,
/// recommended action is to serde::to_string the auth json, then save it to
/// disk yourself.
///
/// ```ignore /// ```ignore
/// let mgr, _ = auth_mgr_from_auth_json(auth_json).await?; /// let mgr = auth_mgr_from_auth_json(auth_json).await?;
/// // build backend client from mgr /// // build backend client from mgr
/// ``` /// ```
///
/// this may:
/// - keep a random auth_json inside the ephemeral map for the rest of the process.
pub async fn auth_mgr_from_auth_json( pub async fn auth_mgr_from_auth_json(
auth_json: AuthDotJson, auth_json: AuthDotJson,
) -> Result<(Arc<AuthManager>, PathBuf)> { ) -> Result<(Arc<AuthManager>, PathBuf)> {
let store_mode = codex_login::AuthCredentialsStoreMode::Ephemeral; let path = random_path_generator(id_from_auth_json(&auth_json).ok());
let path = random_path_generator(None);
// load auth.json let store_mode = save_auth_json_ephemeral(&path, auth_json)?;
codex_login::save_auth(&path, &auth_json, store_mode)?;
// this must finish its work // this must finish its work
// volatile ahh - codex login use a static map to do ts; alright sure // volatile ahh - codex login use a static map to do ts; alright sure
let auth_manager = AuthManager::shared(path.clone(), false, store_mode, None).await; // C ahh arguments let auth_manager = AuthManager::shared(path.clone(), false, store_mode, None).await; // C ahh arguments
Ok((auth_manager, path)) Ok((auth_manager, path))
} }
pub fn save_auth_json_ephemeral(
home_path: &Path,
auth_json: AuthDotJson,
) -> Result<AuthCredentialsStoreMode, anyhow::Error> {
let store_mode = AuthCredentialsStoreMode::Ephemeral;
codex_login::save_auth(home_path, &auth_json, store_mode)?;
Ok(store_mode)
}
pub fn save_auth_json_file(
home_path: &Path,
auth_json: AuthDotJson,
) -> Result<AuthCredentialsStoreMode, anyhow::Error> {
let store_mode = AuthCredentialsStoreMode::File;
codex_login::save_auth(home_path, &auth_json, store_mode)?;
Ok(store_mode)
}
/// quota should bundle with plan type. but for now, we can get the plan type separately like this. /// quota should bundle with plan type. but for now, we can get the plan type separately like this.
pub async fn get_plan(auth_mgr: &AuthManager) -> anyhow::Result<Option<AccountPlanType>> { pub async fn get_plan(auth_mgr: &AuthManager) -> anyhow::Result<Option<AccountPlanType>> {
let plan = auth_mgr let plan = auth_mgr
@@ -225,3 +266,64 @@ pub async fn get_plan(auth_mgr: &AuthManager) -> anyhow::Result<Option<AccountPl
.account_plan_type(); .account_plan_type();
Ok(plan) Ok(plan)
} }
// this is basically a logout; the auth manager didnt know it yet.
// the instant authmanager reaches reload or something, it will not see the
// file, and thus be logged out.
pub async fn remove_auth_json_from_store(
store_mode: AuthCredentialsStoreMode,
codex_home: &Path,
) -> anyhow::Result<bool> {
codex_login::auth::logout(codex_home, store_mode)
.context("Failed to remove auth.json from store")
}
pub async fn delete_auth_json(path: &Path) -> anyhow::Result<()> {
remove_auth_json_from_store(AuthCredentialsStoreMode::Ephemeral, path).await?;
Ok(())
}
/// oohhh this doesnt support anything but chatgpt auth for now, rough! when i
/// see more auth types, ill add support for them. but for now, this is good
/// enough for my use case.
pub fn id_from_auth_json(auth_json: &AuthDotJson) -> anyhow::Result<String> {
// since i dont know a lot about the auth.json file, ill do this for now.
if Some(AuthMode::Chatgpt) == auth_json.auth_mode {
Ok((auth_json.tokens.as_ref())
.map(|t| t.account_id.as_ref())
.ok_or_else(|| anyhow::anyhow!("accessToken not found in auth_data"))?
.ok_or_else(|| anyhow::anyhow!("account_id not found in tokens"))?
.to_string())
} else {
Err(anyhow::anyhow!(
"unsupported auth mode: {:?}",
auth_json.auth_mode
))
}
}
pub async fn id_from_auth_mgr(auth_mgr: &AuthManager) -> anyhow::Result<String> {
let auth = auth_mgr
.auth()
.await
.context("Failed to get auth info")?
.get_account_id()
.ok_or_else(|| anyhow::anyhow!("account_id not found in auth info"))?;
Ok(auth.to_string())
}
/// helper if you use our other functions
///
/// this pulls from the ephemeral store.
pub fn ephemeral_auth_json(path: &Path) -> anyhow::Result<AuthDotJson> {
auth_json(path, AuthCredentialsStoreMode::Ephemeral)
}
pub fn file_auth_json(path: &Path) -> anyhow::Result<AuthDotJson> {
auth_json(path, AuthCredentialsStoreMode::File)
}
pub fn auth_json(path: &Path, store_mode: AuthCredentialsStoreMode) -> anyhow::Result<AuthDotJson> {
codex_login::load_auth_dot_json(path, store_mode)?
.ok_or_else(|| anyhow::anyhow!("Failed to load auth.json from path"))
}
+2 -2
View File
@@ -1,9 +1,9 @@
use codexport::chatgpt_login_to_auth_json; use codexport::chatgpt_login_to_auth_json;
mod bin2;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
let random_home = codexport::random_path_generator(Some("me".to_string())); let random_home = codexport::random_path_generator(Some("me".to_string()));
let auth_json = chatgpt_login_to_auth_json(&random_home).await?; let auth_json = chatgpt_login_to_auth_json(&random_home, None).await?;
let mgr = codexport::auth_manager( let mgr = codexport::auth_manager(
&random_home, &random_home,
codex_config::types::AuthCredentialsStoreMode::Ephemeral, codex_config::types::AuthCredentialsStoreMode::Ephemeral,
+15 -5
View File
@@ -6,14 +6,24 @@ csa show <acc> -> verbose account <acc>
csa swap -- codex resume -> csa swap auth to the best account, then execvpe csa swap -- codex resume -> csa swap auth to the best account, then execvpe
csa swap <acc> -- codex logout -> csa swap to that acount, then execvpe csa swap <acc> -- codex logout -> csa swap to that acount, then execvpe
optional features // important feature
csa refresh (--all --expired OR <acc name>)
csa login // account:
csa logout csa account show (--all, --expired, <name>) default current
csa refresh csa account rename
csa accound add
csa account refresh
csa login <account>
csa logout <account> --DO NOT Support --all, DO support --expired ONLY with confirmation
logout and refresh have official bindings in codex-login (logout with revoke, refresh_token) logout and refresh have official bindings in codex-login (logout with revoke, refresh_token)
add could either add could either
1. stash the auth.json (clear) and run codex login -> csa add -- --opt -> codex login --opt, then stash THIS auth.json then put prev back if not csa add --use. 1. stash the auth.json (clear) and run codex login -> csa add -- --opt -> codex login --opt, then stash THIS auth.json then put prev back if not csa add --use.
2. use the code that chatgpt_login_to_auth_json already does 2. use the code that chatgpt_login_to_auth_json already does
future direction
maybe you could save an entire CODEX_HOME, which has EVERYTHING symlinked over... i would advise against. just exit out, csa swap, then login again
+47
View File
@@ -0,0 +1,47 @@
//! this deals with authentication. validate, refresh, and quota check.
use crate::store::Account;
use crate::store::AccountStore;
pub async fn validate_account<S: AccountStore>(store: &S, id: &str) -> anyhow::Result<()> {
let account = store.load(id)?;
let (auth_manager, _) = codexport::auth_mgr_from_auth_json(account.auth_json).await?;
auth_manager
.auth()
.await
.ok_or_else(|| anyhow::anyhow!("not logged in!"))?;
Ok(())
}
// auth manager hides path
// path gets auth json
// auth json plus path gets auth manager
// so you lowk only need path and auth json
pub async fn refresh_account<S: AccountStore>(store: &mut S, id: &str) -> anyhow::Result<()> {
let account = store.load(id)?;
let (auth_manager, path) = codexport::auth_mgr_from_auth_json(account.auth_json).await?;
codexport::renew(&auth_manager).await?;
let new_auth_json = codexport::ephemeral_auth_json(&path)?;
let new_account = Account {
id: account.id,
nickname: account.nickname,
auth_json: new_auth_json,
};
store.upsert(new_account)?;
Ok(())
}
pub async fn delete_account<S: AccountStore>(store: &mut S, id: &str) -> anyhow::Result<()> {
let account = store.load(id)?;
let (auth_manager, _path) = codexport::auth_mgr_from_auth_json(account.auth_json).await?;
auth_manager.logout_with_revoke().await?;
store.delete(id)?;
Ok(())
}
pub async fn check_quota<S: AccountStore>(store: &S, id: &str) -> anyhow::Result<codexport::RateLimit> {
let account = store.load(id)?;
let (auth_manager, _path) = codexport::auth_mgr_from_auth_json(account.auth_json).await?;
let client = codexport::backend_client_from_auth_mgr(&auth_manager).await?;
let quota = codexport::quota(&client).await?;
Ok(quota)
}
+203
View File
@@ -0,0 +1,203 @@
#[derive(clap::Parser, Debug)]
#[clap(version, about = "A CLI tool to manage multiple Codex accounts")]
pub struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(clap::Subcommand, Debug)]
pub enum Commands {
Show(ShowCommand),
Add(AddCommand),
Swap(SwapCommand),
Refresh {
/// specify to refresh the account. Else refresh all accounts.
// this and AccountCommand.account at the same time is an error.
// chat how do i enforce above rule?
acc: Option<String>,
/// specify to only refresh the account if it's expired. Else refresh anyway.
#[clap(long)]
expired: bool,
},
// also logout
Remove {
/// specify to remove the account
// do NOT support remove all accounts.
acc: String,
},
Rename {
/// specify to rename the account
acc: String,
/// the new name for the account
new_name: String,
},
}
#[derive(Clone, Debug, clap::Parser)]
pub struct ShowCommand {
/// specify to show the account.
pub acc: Option<String>,
}
/// `csa add --use --name myacc` or `csa add myacc --use` would add an account with nickname "myacc" and switch to it right away.
#[derive(Clone, Debug, clap::Parser)]
pub struct AddCommand {
/// use the account right away after adding, instead of just adding to the store.
#[clap(long = "use", short = 'u')]
use_rn: bool,
/// use to specify a nickname for the account. If not specified, will be the email or something else identifiable.
/// highly recommend.
pub name: Option<String>,
}
/// `csa swap acc1` would swap to acc1 if it exists. Else error out. `csa swap` would swap to the most capable account, which is determined by the internal logic of csa, and is usually the best valid account with the best availability.
///
/// to use something right after add/swap, use your favourite shell feature, the semicolon `;`.
// that looks like an emoji yo (`;`) wth
#[derive(Clone, Debug, clap::Parser)]
pub struct SwapCommand {
/// specify to swap to the account, instead of best effort swap to the most capable account.
pub acc: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[test]
fn show() {
let cli = Cli::try_parse_from(["test", "show"]).expect("csa show");
println!("{cli:#?}");
}
#[test]
fn show_me() {
let cli = Cli::try_parse_from(["test", "show", "me"]).expect("csa show me");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Show(ShowCommand { acc: Some(acc) }) if acc == "me")
);
}
#[test]
fn add() {
let cli = Cli::try_parse_from(["test", "add"]).expect("csa add");
println!("{cli:#?}");
}
#[test]
fn add_use() {
let cli = Cli::try_parse_from(["test", "add", "--use"]).expect("csa add --use");
println!("{cli:#?}");
assert!(matches!(
cli.command,
Commands::Add(AddCommand {
use_rn: true,
name: None,
})
));
}
#[test]
fn add_use_name() {
let cli =
Cli::try_parse_from(["test", "add", "--use", "myacc"]).expect("csa add --use myacc");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Add(AddCommand { use_rn: true, name: Some(name) }) if name == "myacc")
);
}
#[test]
fn add_name() {
let cli = Cli::try_parse_from(["test", "add", "myacc"]).expect("csa add myacc");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Add(AddCommand { use_rn: false, name: Some(name) }) if name == "myacc")
);
}
#[test]
fn swap() {
let cli = Cli::try_parse_from(["test", "swap"]).expect("csa swap");
println!("{cli:#?}");
}
#[test]
fn swap_acc() {
let cli = Cli::try_parse_from(["test", "swap", "acc1"]).expect("csa swap acc1");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Swap(SwapCommand { acc: Some(acc) }) if acc == "acc1")
);
}
#[test]
fn refresh() {
let cli = Cli::try_parse_from(["test", "refresh"]).expect("csa refresh");
println!("{cli:#?}");
}
#[test]
fn refresh_acc() {
let cli = Cli::try_parse_from(["test", "refresh", "acc1"])
.expect("csa refresh acc1");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Refresh { acc: Some(acc), expired } if acc == "acc1" && !expired)
);
}
#[test]
fn refresh_acc_expired() {
let cli = Cli::try_parse_from(["test", "refresh", "acc1", "--expired"])
.expect("csa refresh acc1 --expired");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Refresh { acc: Some(acc), expired }if acc == "acc1" && expired)
);
}
#[test]
fn refresh_expired() {
let cli =
Cli::try_parse_from(["test", "refresh", "--expired"]).expect("csa refresh --expired");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Refresh { acc: None, expired } if expired)
);
}
#[test]
fn remove() {
let cli = Cli::try_parse_from(["test", "remove", "acc1"])
.expect("csa remove acc1");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Remove { acc } if acc == "acc1")
);
}
#[test]
#[should_panic]
fn remove_no_acc() {
let cli = Cli::try_parse_from(["test", "remove"]).expect("csa remove");
println!("{cli:#?}");
}
#[test]
fn rename() {
let cli = Cli::try_parse_from(["test", "rename", "acc1", "acc_new"])
.expect("csa rename acc1 acc_new");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Rename { acc, new_name } if acc == "acc1" && new_name == "acc_new")
);
}
#[test]
#[should_panic]
fn rename_no_args() {
let cli = Cli::try_parse_from(["test", "rename"]).expect("csa rename");
println!("{cli:#?}");
}
#[test]
#[should_panic]
fn rename_no_new_name() {
let cli = Cli::try_parse_from(["test", "rename", "acc1"]).expect("csa rename acc1");
println!("{cli:#?}");
}
}
+5 -13
View File
@@ -1,14 +1,6 @@
pub fn add(left: u64, right: u64) -> u64 { pub mod auth;
left + right pub mod cli;
} pub mod store;
pub mod util;
#[cfg(test)] pub mod logic;
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
+374
View File
@@ -0,0 +1,374 @@
use std::time::Duration;
use crate::store::Account;
use anyhow::Context;
use codexport::AccountPlanType;
use codexport::RateLimit;
use codexport::RateLimitWindow;
/// what is the best account?
pub fn best_account(accounts: &[Account]) -> anyhow::Result<&Account> {
// Placeholder implementation - replace with actual logic to determine the best account
accounts
.first()
.ok_or_else(|| anyhow::anyhow!("No accounts available"))
}
/// plan weight.
/// this is going to be the weight per week. i just made ts up.
/// To get weight per session, divide by 6 (or 100/16)
///
/// this may align with my idea of if there is an account with api key, DON'T switch to it.
// that might mean we need to have a priority system.
pub fn plan_weight(plan: &AccountPlanType) -> anyhow::Result<u32> {
// Placeholder implementation - replace with actual logic to determine the weight of a plan
match plan {
// weekly faster than pluses session for some reason, so this may get LESS weight, idk
AccountPlanType::Free | AccountPlanType::Go => Ok(1),
// i think these are the same
AccountPlanType::Plus | AccountPlanType::Team | AccountPlanType::Business => Ok(10),
// pro 5x
AccountPlanType::ProLite => Ok(50),
// OG pro, pro 20x
AccountPlanType::Pro => Ok(200),
// idk below
AccountPlanType::Enterprise => anyhow::bail!("Enterprise plan is not supported"),
AccountPlanType::SelfServeBusinessUsageBased => {
anyhow::bail!("Self-Serve Business Usage-Based plan is not supported")
}
AccountPlanType::EnterpriseCbpUsageBased => {
anyhow::bail!("Enterprise CBP Usage-Based plan is not supported")
}
AccountPlanType::Edu => anyhow::bail!("Edu plan is not supported"),
AccountPlanType::Unknown => anyhow::bail!("Unknown plan type"),
}
}
fn now() -> std::time::SystemTime {
std::time::SystemTime::now()
}
fn now_u64() -> u64 {
now()
.duration_since(std::time::UNIX_EPOCH)
.expect("failed to calculate duration since UNIX epoch")
.as_secs()
}
fn stale_window(window: &RateLimitWindow, now: u64) -> Option<bool> {
window.resets_at.map(|resets_at| (resets_at as u64) < now)
}
/// how to interpret the rate limit window:
///
/// ```rust
/// # fn main() -> anyhow::Result<()> {
/// # use lda_codex_account::logic::window_weight;
/// # use codexport::RateLimitWindow;
/// # fn now_u64() -> u64 {
/// # std::time::SystemTime::now()
/// # .duration_since(std::time::UNIX_EPOCH)
/// # .expect("failed to calculate duration since UNIX epoch")
/// # .as_secs()
/// # }
/// # let window = RateLimitWindow {
/// # window_minutes: Some(60),
/// # used_percent: 50.0,
/// # resets_at: Some(now_u64() as i64 + 30 * 60),
/// # };
/// let (window_duration_minutes, window_weight) = window_weight(window)?;
/// # Ok(())
/// # }
/// ```
///
/// the weight is determined by the percentage of the window used and the time left until it resets.
/// the less of the window that is used and the less time left until it resets, the higher the weight.
///
/// this only runs if all the Options in the struct are Some.
/// If not, i advise not ranking the window at all.
///
/// Match the window with certain constants (300, 10080) and use that in tandem with plan weight
/// to get the final weight for the window.
/// [try_match_window]
pub fn window_weight(window: &RateLimitWindow) -> anyhow::Result<(u64, f64)> {
let now = now();
let window_duration = window
.window_minutes
.context("missing field 'window_minutes'")? as u64;
let used = if (0.0..=100.0).contains(&window.used_percent) {
window.used_percent
} else {
anyhow::bail!("invalid 'used_percent' value: {}", window.used_percent);
};
let left = 100.0 - used;
let resets_at = window.resets_at.context("missing field 'resets_at'")?;
let resets_at = std::time::UNIX_EPOCH + Duration::from_secs(resets_at as u64);
let time_left = resets_at
.duration_since(now)
.context("stale window")?
.as_secs();
Ok((window_duration, (left / time_left as f64)))
}
/// small function to match the window with certain constants (300, 10080) and use that in tandem with plan weight
pub fn try_match_window(window: &RateLimitWindow) -> anyhow::Result<Option<f64>> {
let (window_duration, weight) = window_weight(window)?;
match window_duration {
300 => Ok(Some(weight / 6.25)), // 5 hours
10080 => Ok(Some(weight)), // 1 week
_ => Ok(None), // unknown window duration, can not determine weight
}
}
pub fn account_weight(quota: &RateLimit) -> anyhow::Result<f64> {
let plan = quota.plan_type.context("missing field 'plan_type'")?;
let plan_weight = plan_weight(&plan)?;
let mut total_weight: f64 = 1.0;
if let Some(ref primary_window) = quota.primary
&& let Some(window_weight) = try_match_window(primary_window)?
{
total_weight *= window_weight;
}
if let Some(ref secondary_window) = quota.secondary
&& let Some(window_weight) = try_match_window(secondary_window)?
{
total_weight *= window_weight;
}
total_weight *= plan_weight as f64;
Ok(total_weight)
}
// lets step back and define how the BEST account is determined. we want to consider the following factors:
// 1. plan type (pro > plus > free)
// 2. rate limit window (how much of the window is used and how much time is left until it resets)
// - the primary window vs the secondary window (if the primary window is close to being used up,
// we might want to switch to an account with a less used primary window, even if it has a worse plan type)
// - a window weight is based on window duration. If it is equal to 5 hours, the weight is 1/6 of the plan weight,
// and if its 1 week, the weight is 1 of the plan weight.
// - the window weight is determined by the percentage of the window used and the time left until it resets.
// the logic could complicate if this account is at the end of a window, which means it will reset soon,
// so we might want to switch to it even if it has a worse plan type, because it will reset soon and we can use it for a long time after that.
// but if its window is just reset, we might want to switch away from it, because it will be used up soon.
// so we need to consider the time left until the window resets, and the percentage of the window that is used.
// the logic could complicate further if the secondary window is also close to being used up, or just started.
// imagine big Lda using this thing in an hour. An hour often depletes the plus 5h window. Because openai is just so bad.
// an account that has 50% of its primary window used, but it resets in 30 minutes.
// its going to be better than a fresh one but resets in 5 hours.
// so we need to consider the time left until the window resets, and the percentage of the window that is used.
/// standard tests
#[cfg(test)]
mod tests {
use super::*;
/// i lowk need this function outside
fn w(window_minutes: u64, used_percent: f64, resets_at: i64) -> codexport::RateLimitWindow {
codexport::RateLimitWindow {
window_minutes: Some(window_minutes as i64),
used_percent,
resets_at: Some(resets_at),
}
}
fn rl(
plan_type: AccountPlanType,
primary: Option<RateLimitWindow>,
secondary: Option<RateLimitWindow>,
) -> codexport::RateLimit {
codexport::RateLimit {
plan_type: Some(plan_type),
primary,
secondary,
limit_id: None,
limit_name: None,
rate_limit_reached_type: None,
credits: None,
}
}
/// these are gonna test some of my theories
mod lda_tests {
use super::*;
#[test]
fn weekly_better_than_five_hours() {
let five_hours_window = w(300, 50.0, now_u64() as i64 + 30 * 60); // 5 hours window, 50% used, resets in 30 minutes
let weekly_window = w(10080, 50.0, now_u64() as i64 + 30 * 60); // 1 week window, 50% used, resets in 30 minutes
let five_hours_weight = crate::logic::try_match_window(&five_hours_window)
.unwrap()
.unwrap();
let weekly_weight = crate::logic::try_match_window(&weekly_window)
.unwrap()
.unwrap();
println!(
"Five hours weight: {}, Weekly weight: {}",
five_hours_weight, weekly_weight
);
assert!(weekly_weight > five_hours_weight);
}
#[test]
fn five_hours_worse_than_30_minutes() {
let window = 300; // 5 hours window
let five_hours_window = w(window, 0.0, now_u64() as i64 + 300 * 60); // 5 hours window, 0% used, resets in 5 hours
let thirty_minutes_window = w(window, 0.0, now_u64() as i64 + 30 * 60); // 5 hours window, 0% used, resets in 30 minutes
let five_hours_weight = crate::logic::try_match_window(&five_hours_window)
.unwrap()
.unwrap();
let thirty_minutes_weight = crate::logic::try_match_window(&thirty_minutes_window)
.unwrap()
.unwrap();
println!(
"Five hours weight: {}, Thirty minutes weight: {}",
five_hours_weight, thirty_minutes_weight
);
assert!(five_hours_weight < thirty_minutes_weight);
}
#[test]
fn pro_20x_better_than_plus() {
let pro_window = w(300, 0.0, now_u64() as i64 + 30 * 60); // 5 hours window, 0% used, resets in 30 minutes
let plus_window = w(300, 0.0, now_u64() as i64 + 30 * 60); // 5 hours window, 0% used, resets in 30 minutes
let pro_quota = rl(AccountPlanType::Pro, Some(pro_window), None);
let plus_quota = rl(AccountPlanType::Plus, Some(plus_window), None);
let pro_weight = crate::logic::account_weight(&pro_quota).unwrap();
let plus_weight = crate::logic::account_weight(&plus_quota).unwrap();
println!("Pro weight: {}, Plus weight: {}", pro_weight, plus_weight);
assert!(pro_weight > plus_weight);
assert!(pro_weight > 19.9 * plus_weight);
assert!(pro_weight < 20.1 * plus_weight); // lmao
}
#[test]
fn weekly_0_left_is_dead() {
let now = now_u64() as i64;
let five_hours_window = w(300, 30.0, now + 30 * 60); // 5 hours window, 30% used, resets in 30 minutes
let weekly_window = w(10080, 100.0, now + 2 * 24 * 60); // 1 week window, 100% used, resets in 2 days
let mut rate_limit = rl(
AccountPlanType::Pro,
Some(five_hours_window),
Some(weekly_window),
);
rate_limit.rate_limit_reached_type = // this simulates the case where the account has hit the weekly limit
Some(codexport::RateLimitReachedType::RateLimitReached);
let total_weight = crate::logic::account_weight(&rate_limit).unwrap();
println!("Total weight: {}", total_weight);
assert!(total_weight <= 0.0); // its dead
}
#[test]
fn weekly_low_doesnt_care_about_5h() {
let now = now_u64() as i64;
let fh_left_1 = 90.0; // 90% left in 5 hours window
let fh_left_2 = 50.0; // 50% left in 5 hours window
let w_left = 4.0; // 4% left in weekly window
assert!(w_left * 6.25 < fh_left_1);
assert!(w_left * 6.25 < fh_left_2);
// so the weekly window is going to go out before the 5 hours window
let five_hours_window = w(300, fh_left_1, now + 30 * 60); // 5 hours window, 90% used, resets in 30 minutes
let five_hours_window_2 = w(300, fh_left_2, now + 30 * 60); // 5 hours window, 50% used, resets in 30 minutes
let weekly_window = w(10080, w_left, now + 2 * 24 * 60); // 1 week window, 4% used, resets in 2 days
let rate_limit = rl(
AccountPlanType::Pro,
Some(five_hours_window),
Some(weekly_window.clone()),
);
let rate_limit_2 = rl(
AccountPlanType::Pro,
Some(five_hours_window_2),
Some(weekly_window),
);
let total_weight = crate::logic::account_weight(&rate_limit).unwrap();
let total_weight_2 = crate::logic::account_weight(&rate_limit_2).unwrap();
println!("Total weight: {}", total_weight);
println!("Total weight 2: {}", total_weight_2);
assert!(total_weight > 0.0); // its not dead
assert!(total_weight_2 > 0.0); // its not dead either
// assert!(total_weight == total_weight_2); // its just as dead tho
assert!(total_weight >= total_weight_2);
// should i go through with that comparison?
// the 5 hours window is not going to go out before the weekly
// window, so it doesn't matter how much is left in the 5 hours
// window, because the weekly window is going to go out first
// however, for comparisons like this, should one still be lower than the other?
// in this case, they are the same, because the weekly window is the bottleneck.
// The API generously lets us finish turns even if there is 0% left.
}
}
#[test]
fn test_window_weight() {
let now = now_u64() as i64;
let window = w(300, 50.0, now + 30 * 60); // 5 hours window, 50% used, resets in 30 minutes
let (duration, weight) = crate::logic::window_weight(&window).unwrap();
println!("Duration: {} minutes, Weight: {}", duration, weight);
assert_eq!(duration, 300);
assert!(weight > 0.0);
}
#[test]
fn test_try_match_window() {
let now = now_u64() as i64;
let window = w(300, 50.0, now + 30 * 60); // 5 hours window, 50% used, resets in 30 minutes
let weight = crate::logic::try_match_window(&window).unwrap();
println!("Weight: {:?}", weight);
assert!(weight.is_some());
}
#[test]
fn test_try_match_window_unknown() {
let now = now_u64() as i64;
let window = w(12345, 50.0, now + 30 * 60); // unknown window duration
let weight = crate::logic::try_match_window(&window).unwrap();
println!("Weight: {:?}", weight);
assert!(weight.is_none());
}
#[test]
fn test_plan_weight() {
use codexport::AccountPlanType::*;
let plans = [Free, Go, Plus, Team, Business, ProLite, Pro]; // supported plans
for plan in plans {
let weight = crate::logic::plan_weight(&plan).unwrap();
println!("Plan: {:?}, Weight: {}", plan, weight);
}
}
#[test]
fn test_window_weight_invalid_used_percent() {
let now = now_u64() as i64;
// SUPA EDGES
for i in [150.0, 0.0_f64.next_down(), 100.0_f64.next_up()] {
let window = w(300, i, now + 30 * 60);
let result = crate::logic::window_weight(&window);
println!("Result for used_percent {}: {:?}", i, result);
assert!(result.is_err());
}
}
#[test]
fn test_window_weight_valid_used_percent() {
let now = now_u64() as i64;
for i in [0.0, 50.0, 100.0] {
let window = w(300, i, now + 30 * 60);
let result = crate::logic::window_weight(&window);
println!("Result for used_percent {}: {:?}", i, result);
assert!(result.is_ok());
if i == 100.0 {
assert_eq!(result.unwrap().1, 0.0); // if 100% used, weight should be 0
} else if i < 100.0 {
assert!(result.unwrap().1 > 0.0); // if not fully used, weight should be greater than 0
}
}
}
#[test]
fn test_window_weight_stale() {
let now = now_u64() as i64;
let window = w(300, 50.0, now - 30 * 60); // stale window, reset time is in the past
let result = crate::logic::window_weight(&window);
println!("Result: {:?}", result);
assert!(result.is_err());
}
}
+71
View File
@@ -0,0 +1,71 @@
//! deals with storage of accounts.
//! updates, pull out, standard crud... all here.
//! 0 logic to do with the actual accounts, just storage and retrieval.
use codexport::AuthDotJson;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Account {
pub id: String,
pub nickname: String,
pub auth_json: AuthDotJson,
}
/// this is the interface for storing accounts. We can have different
/// implementations, such as in-memory, file-based, or even a database. The key
/// is that the rest of the code should not care about how the accounts are
/// stored, as long as it can save, load, delete, list, and rename accounts.
pub trait AccountStore {
fn upsert(&mut self, account: Account) -> anyhow::Result<()>;
fn load(&self, id: &str) -> anyhow::Result<Account>;
fn delete(&mut self, id: &str) -> anyhow::Result<()>;
fn list(&self) -> anyhow::Result<Vec<Account>>;
fn rename(&mut self, id: &str, new_nickname: &str) -> anyhow::Result<()>;
fn has(&self, id: &str) -> anyhow::Result<bool> {
Ok(self.load(id).is_ok())
}
fn get_all(&self) -> anyhow::Result<Vec<Account>> {
self.list()
}
fn clear(&mut self) -> anyhow::Result<()> {
for account in self.list()? {
self.delete(&account.id)?;
}
Ok(())
}
fn count(&self) -> anyhow::Result<usize> {
Ok(self.list()?.len())
}
fn is_empty(&self) -> anyhow::Result<bool> {
Ok(self.count()? == 0)
}
fn load_by_nickname(&self, nickname: &str) -> anyhow::Result<Account> {
let accounts = self.list()?;
for account in accounts {
if account.nickname == nickname {
return Ok(account);
}
}
Err(anyhow::anyhow!(
"Account with nickname '{}' not found",
nickname
))
}
fn delete_by_nickname(&mut self, nickname: &str) -> anyhow::Result<()> {
let account = self.load_by_nickname(nickname)?;
self.delete(&account.id)
}
fn rename_by_nickname(&mut self, old_nickname: &str, new_nickname: &str) -> anyhow::Result<()> {
let account = self.load_by_nickname(old_nickname)?;
self.rename(&account.id, new_nickname)
}
}
pub mod file;
pub mod in_memory;
pub mod file2;
// ooh! account strore has a lil PROBLEM. we should not let name dictate the key, of the inmem store. the key, is the account ID!
// the name is a nickname, which can be changed however. And it is not, the key!
// but nicknames are useful. we should support these.
+79
View File
@@ -0,0 +1,79 @@
//! file store!
use crate::store::Account;
use super::AccountStore;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Clone, Debug)]
pub struct FileStore {
dir: PathBuf,
}
impl FileStore {
pub fn new<P: AsRef<Path>>(dir: P) -> Self {
Self {
dir: dir.as_ref().to_path_buf(),
}
}
fn account_path(&self, id: &str) -> PathBuf {
self.dir.join(format!("{}.json", id))
}
pub fn ensure_dir(&self) -> anyhow::Result<()> {
if !self.dir.exists() {
fs::create_dir_all(&self.dir)?;
}
Ok(())
}
pub fn dir(&self) -> &Path {
&self.dir
}
}
impl AccountStore for FileStore {
fn upsert(&mut self, account: Account) -> anyhow::Result<()> {
let path = self.account_path(&account.id);
let data = serde_json::to_string_pretty(&account)?;
fs::write(path, data)?;
Ok(())
}
fn load(&self, id: &str) -> anyhow::Result<Account> {
let path = self.account_path(id);
let data = fs::read_to_string(path)?;
let account: Account = serde_json::from_str(&data)?;
Ok(account)
}
fn delete(&mut self, id: &str) -> anyhow::Result<()> {
let path = self.account_path(id);
fs::remove_file(path)?;
Ok(())
}
fn list(&self) -> anyhow::Result<Vec<Account>> {
let mut accounts = Vec::new();
for entry in fs::read_dir(&self.dir)? {
let entry = entry?;
if entry.path().is_file()
&& let Some(ext) = entry.path().extension()
&& ext == "json"
{
let data = fs::read_to_string(entry.path())?;
let account: Account = serde_json::from_str(&data)?;
accounts.push(account);
}
}
Ok(accounts)
}
fn rename(&mut self, id: &str, new_nickname: &str) -> anyhow::Result<()> {
let mut account = self.load(id)?;
account.nickname = new_nickname.to_string();
self.upsert(account)
}
}
+123
View File
@@ -0,0 +1,123 @@
//! this setup gives you a codex home if you want to also
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::store::{Account, AccountStore};
/// this is a file-based implementation of the AccountStore trait. it stores the accounts in a json file, and the auth.json files in the codex home directories.
pub struct FileAccountStore {
pub db: Db,
pub homes: Homes,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Db {
#[serde(skip)]
pub path: std::path::PathBuf,
/// this is id: nickname. search for nicknames is O(n) but whatever, we are not expecting a huge number of accounts. and this is just a demo.
#[serde(flatten)]
store: HashMap<String, String>,
}
impl Db {
fn new(path: std::path::PathBuf) -> Self {
Self {
path,
store: HashMap::new(),
}
}
pub fn load(path: std::path::PathBuf) -> anyhow::Result<Self> {
if !path.exists() {
return Ok(Self::new(path));
}
let data = std::fs::read_to_string(&path)?;
let store: HashMap<_, _> = serde_json::from_str(&data)?;
let db = Self { path, store };
Ok(db)
}
/// call this after you make changes to the store, to save it to disk. this is separate from the upsert/delete functions, so you can make multiple changes and then save once.
pub fn save(&self) -> anyhow::Result<()> {
let data = serde_json::to_string_pretty(&self)?;
std::fs::write(&self.path, data)?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Homes {
path: std::path::PathBuf,
}
impl Homes {
/// CODEX_HOME, not auth.json
pub fn get_home(&self, id: &str) -> std::path::PathBuf {
self.path.join(id)
}
/// the file that will deserialize into [`AuthDotJson`](codexport::AuthDotJson)
pub fn get_auth_json_path(&self, id: &str) -> std::path::PathBuf {
self.get_home(id).join("auth.json") // codex expects this file name
}
}
impl AccountStore for FileAccountStore {
fn upsert(&mut self, account: Account) -> anyhow::Result<()> {
{
let codex_home = self.homes.get_home(&account.id);
std::fs::create_dir_all(&codex_home)?;
codexport::save_auth_json_file(&codex_home, account.auth_json)?;
}
{
self.db.store.insert(account.id, account.nickname);
self.db.save()?;
}
Ok(())
}
fn load(&self, id: &str) -> anyhow::Result<Account> {
let nickname = self
.db
.store
.get(id)
.ok_or_else(|| anyhow::anyhow!("Account with id '{}' not found", id))?
.clone();
let auth_json = codexport::file_auth_json(&self.homes.get_auth_json_path(id))?;
Ok(Account {
id: id.to_string(),
nickname,
auth_json,
})
}
/// this will not delete the directory of the account, if in case you want to login again
fn delete(&mut self, id: &str) -> anyhow::Result<()> {
self.db.store.remove(id);
self.db.save()?;
Ok(())
}
fn list(&self) -> anyhow::Result<Vec<Account>> {
let mut accounts = Vec::new();
for (id, nickname) in &self.db.store {
let auth_json = codexport::file_auth_json(&self.homes.get_auth_json_path(id))?;
accounts.push(Account {
id: id.clone(),
nickname: nickname.clone(),
auth_json,
});
}
Ok(accounts)
}
fn rename(&mut self, id: &str, new_nickname: &str) -> anyhow::Result<()> {
if let Some(nickname) = self.db.store.get_mut(id) {
*nickname = new_nickname.to_string();
self.db.save()?;
Ok(())
} else {
Err(anyhow::anyhow!("Account with id '{}' not found", id))
}
}
}
+46
View File
@@ -0,0 +1,46 @@
//! in memory store!
use crate::store::Account;
use super::AccountStore;
#[derive(Clone, Debug, Default)]
pub struct InMemoryStore {
/// id to account map!
accounts: std::collections::HashMap<String, Account>,
}
impl AccountStore for InMemoryStore {
fn upsert(&mut self, account: Account) -> anyhow::Result<()> {
self.accounts.insert(account.id.clone(), account);
Ok(())
}
fn load(&self, id: &str) -> anyhow::Result<Account> {
self.accounts
.get(id)
.cloned()
.ok_or_else(|| anyhow::anyhow!("Account with id '{}' not found", id))
}
fn delete(&mut self, id: &str) -> anyhow::Result<()> {
self.accounts
.remove(id)
.ok_or_else(|| anyhow::anyhow!("Account with id '{}' not found", id))?;
Ok(())
}
fn list(&self) -> anyhow::Result<Vec<Account>> {
Ok(self.accounts.values().cloned().collect())
}
fn rename(&mut self, id: &str, new_nickname: &str) -> anyhow::Result<()> {
let account = self
.accounts
.get_mut(id)
.ok_or_else(|| anyhow::anyhow!("Account with id '{}' not found", id))?;
account.nickname = new_nickname.to_string();
Ok(())
}
}
+1
View File
@@ -0,0 +1 @@
//! whatever looks like an general utility goes here.