Compare commits
2
Commits
19414dc9cd
...
ff4a93d967
No files matched your search
Generated
+131
-105
@@ -1536,7 +1536,7 @@ checksum = "e9b18233253483ce2f65329a24072ec414db782531bdbb7d0bbc4bd2ce6b7e21"
|
||||
[[package]]
|
||||
name = "codex-agent-identity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@@ -1555,7 +1555,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-analytics"
|
||||
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 = [
|
||||
"codex-app-server-protocol",
|
||||
"codex-git-utils",
|
||||
@@ -1574,7 +1574,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-api"
|
||||
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 = [
|
||||
"async-channel",
|
||||
"async-trait",
|
||||
@@ -1603,7 +1603,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-app-server-protocol"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1627,7 +1627,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-apply-patch"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"codex-exec-server",
|
||||
@@ -1642,7 +1642,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-async-utils"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"tokio",
|
||||
@@ -1652,7 +1652,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-aws-auth"
|
||||
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 = [
|
||||
"aws-config",
|
||||
"aws-credential-types",
|
||||
@@ -1666,7 +1666,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-backend-client"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"codex-api",
|
||||
@@ -1683,7 +1683,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-backend-openapi-models"
|
||||
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 = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -1693,7 +1693,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-client"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@@ -1719,7 +1719,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-code-mode"
|
||||
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 = [
|
||||
"async-channel",
|
||||
"async-trait",
|
||||
@@ -1736,12 +1736,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-collaboration-mode-templates"
|
||||
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]]
|
||||
name = "codex-config"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1785,7 +1785,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-connectors"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"codex-app-server-protocol",
|
||||
@@ -1799,7 +1799,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-core"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -1897,7 +1897,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-core-plugins"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -1918,6 +1918,7 @@ dependencies = [
|
||||
"dirs",
|
||||
"flate2",
|
||||
"reqwest 0.12.28",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tar",
|
||||
@@ -1933,7 +1934,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-core-skills"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"codex-analytics",
|
||||
@@ -1963,13 +1964,14 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-exec-server"
|
||||
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 = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"axum",
|
||||
"base64",
|
||||
"bytes",
|
||||
"codex-api",
|
||||
"codex-app-server-protocol",
|
||||
"codex-client",
|
||||
"codex-file-system",
|
||||
@@ -1995,7 +1997,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-execpolicy"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2011,7 +2013,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-experimental-api-macros"
|
||||
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 = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2021,8 +2023,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-extension-api"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"codex-protocol",
|
||||
"codex-tools",
|
||||
]
|
||||
@@ -2030,7 +2033,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-features"
|
||||
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 = [
|
||||
"codex-otel",
|
||||
"codex-protocol",
|
||||
@@ -2043,7 +2046,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-feedback"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"codex-login",
|
||||
@@ -2056,7 +2059,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-file-search"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2071,7 +2074,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-file-system"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"codex-protocol",
|
||||
@@ -2082,7 +2085,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-git-utils"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -2106,7 +2109,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-hooks"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -2128,7 +2131,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-keyring-store"
|
||||
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 = [
|
||||
"keyring",
|
||||
"tracing",
|
||||
@@ -2137,7 +2140,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-login"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
@@ -2171,7 +2174,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-mcp"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-channel",
|
||||
@@ -2202,7 +2205,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-memories-read"
|
||||
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 = [
|
||||
"codex-protocol",
|
||||
"codex-shell-command",
|
||||
@@ -2215,7 +2218,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-model-provider"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"codex-agent-identity",
|
||||
@@ -2237,7 +2240,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-model-provider-info"
|
||||
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 = [
|
||||
"codex-api",
|
||||
"codex-app-server-protocol",
|
||||
@@ -2250,7 +2253,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-models-manager"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
@@ -2270,7 +2273,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-network-proxy"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -2300,7 +2303,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-otel"
|
||||
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 = [
|
||||
"chrono",
|
||||
"codex-api",
|
||||
@@ -2332,7 +2335,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-plugin"
|
||||
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 = [
|
||||
"codex-config",
|
||||
"codex-utils-absolute-path",
|
||||
@@ -2343,7 +2346,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-protocol"
|
||||
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 = [
|
||||
"chardetng",
|
||||
"chrono",
|
||||
@@ -2380,7 +2383,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-response-debug-context"
|
||||
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 = [
|
||||
"base64",
|
||||
"codex-api",
|
||||
@@ -2391,7 +2394,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-rmcp-client"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -2426,7 +2429,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-rollout"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -2450,7 +2453,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-rollout-trace"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"codex-code-mode",
|
||||
@@ -2465,7 +2468,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-sandboxing"
|
||||
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 = [
|
||||
"codex-network-proxy",
|
||||
"codex-protocol",
|
||||
@@ -2482,7 +2485,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-shell-command"
|
||||
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 = [
|
||||
"base64",
|
||||
"codex-protocol",
|
||||
@@ -2501,7 +2504,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-shell-escalation"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -2521,7 +2524,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-skills"
|
||||
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 = [
|
||||
"codex-utils-absolute-path",
|
||||
"include_dir",
|
||||
@@ -2531,7 +2534,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-state"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -2553,7 +2556,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-terminal-detection"
|
||||
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 = [
|
||||
"tracing",
|
||||
]
|
||||
@@ -2561,7 +2564,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-thread-store"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
@@ -2580,7 +2583,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-tools"
|
||||
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 = [
|
||||
"async-trait",
|
||||
"codex-app-server-protocol",
|
||||
@@ -2588,6 +2591,7 @@ dependencies = [
|
||||
"codex-features",
|
||||
"codex-protocol",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-output-truncation",
|
||||
"codex-utils-pty",
|
||||
"codex-utils-string",
|
||||
"rmcp",
|
||||
@@ -2600,7 +2604,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-absolute-path"
|
||||
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 = [
|
||||
"dirs",
|
||||
"dunce",
|
||||
@@ -2612,7 +2616,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-cache"
|
||||
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 = [
|
||||
"lru",
|
||||
"sha1",
|
||||
@@ -2622,7 +2626,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-home-dir"
|
||||
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 = [
|
||||
"codex-utils-absolute-path",
|
||||
"dirs",
|
||||
@@ -2631,7 +2635,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-image"
|
||||
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 = [
|
||||
"base64",
|
||||
"codex-utils-cache",
|
||||
@@ -2644,7 +2648,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-output-truncation"
|
||||
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 = [
|
||||
"codex-protocol",
|
||||
"codex-utils-string",
|
||||
@@ -2653,7 +2657,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-path"
|
||||
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 = [
|
||||
"codex-utils-absolute-path",
|
||||
"dunce",
|
||||
@@ -2663,7 +2667,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-plugins"
|
||||
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 = [
|
||||
"codex-exec-server",
|
||||
"codex-login",
|
||||
@@ -2675,7 +2679,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-pty"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"filedescriptor",
|
||||
@@ -2691,7 +2695,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-rustls-provider"
|
||||
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 = [
|
||||
"rustls",
|
||||
]
|
||||
@@ -2699,12 +2703,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-stream-parser"
|
||||
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]]
|
||||
name = "codex-utils-string"
|
||||
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 = [
|
||||
"regex-lite",
|
||||
"serde",
|
||||
@@ -2714,12 +2718,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "codex-utils-template"
|
||||
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]]
|
||||
name = "codex-windows-sandbox"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@@ -2747,12 +2751,12 @@ name = "codexport"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"codex-app-server-protocol",
|
||||
"codex-backend-client",
|
||||
"codex-config",
|
||||
"codex-core",
|
||||
"codex-login",
|
||||
"codex-protocol",
|
||||
"icu_calendar",
|
||||
"rand 0.10.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -3451,9 +3455,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "diplomat"
|
||||
version = "0.14.0"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9adb46b05e2f53dcf6a7dfc242e4ce9eb60c369b6b6eb10826a01e93167f59c6"
|
||||
checksum = "7935649d00000f5c5d735448ad3dc07b9738160727017914cf42138b8e8e6611"
|
||||
dependencies = [
|
||||
"diplomat_core",
|
||||
"proc-macro2",
|
||||
@@ -3463,15 +3467,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "diplomat-runtime"
|
||||
version = "0.14.0"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0569bd3caaf13829da7ee4e83dbf9197a0e1ecd72772da6d08f0b4c9285c8d29"
|
||||
checksum = "970ac38ad677632efcee6d517e783958da9bc78ec206d8d5e35b459ffc5e4864"
|
||||
|
||||
[[package]]
|
||||
name = "diplomat_core"
|
||||
version = "0.14.0"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51731530ed7f2d4495019abc7df3744f53338e69e2863a6a64ae91821c763df1"
|
||||
checksum = "9cf41b94101a4bce993febaf0098092b0bb31deaf0ecaf6e0a2562465f61b383"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5567,9 +5571,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar"
|
||||
version = "2.1.1"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f0e52e009b6b16ba9c0693578796f2dd4aaa59a7f8f920423706714a89ac4e"
|
||||
checksum = "a2b2acc6263f494f1df50685b53ff8e57869e47d5c6fe39c23d518ae9a4f3e45"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"displaydoc",
|
||||
@@ -5577,25 +5581,25 @@ dependencies = [
|
||||
"icu_locale",
|
||||
"icu_locale_core",
|
||||
"icu_provider",
|
||||
"ixdtf",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar_data"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527f04223b17edfe0bd43baf14a0cb1b017830db65f3950dc00224860a9a446d"
|
||||
checksum = "118577bcf3a0fa7c6ac0a7d6e951814da84ee56b9b1f68fb4d8d10b08cefaf4d"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
|
||||
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"utf8_iter",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
@@ -5603,14 +5607,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a38c52231bc348f9b982c1868a2af3195199623007ba2c7650f432038f5b3e8e"
|
||||
checksum = "288247df2e32aa776ac54fdd64de552149ac43cb840f2761811f0e8d09719dd4"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_decimal_data",
|
||||
"icu_locale",
|
||||
"icu_locale_core",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
@@ -5618,15 +5624,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal_data"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2905b4044eab2dd848fe84199f9195567b63ab3a93094711501363f63546fef7"
|
||||
checksum = "6f14a5ca9e8af29eef62064f269078424283d90dbaffeac5225addf62aaabc22"
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "532b11722e350ab6bf916ba6eb0efe3ee54b932666afec989465f9243fe6dd60"
|
||||
checksum = "d5a396343c7208121dc86e35623d3dfe19814a7613cfd14964994cdc9c9a2e26"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
@@ -5653,15 +5659,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_data"
|
||||
version = "2.1.2"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c5f1d16b4c3a2642d3a719f18f6b06070ab0aef246a6418130c955ae08aa831"
|
||||
checksum = "d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
|
||||
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
@@ -5673,15 +5679,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
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]]
|
||||
name = "icu_properties"
|
||||
version = "2.1.2"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
|
||||
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
@@ -5693,9 +5718,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.1.2"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
|
||||
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
@@ -9961,14 +9986,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "temporal_capi"
|
||||
version = "0.1.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a151e402c2bdb6a3a2a2f3f225eddaead2e7ce7dd5d3fa2090deb11b17aa4ed8"
|
||||
checksum = "8a2a1f001e756a9f5f2d175a9965c4c0b3a054f09f30de3a75ab49765f2deb36"
|
||||
dependencies = [
|
||||
"diplomat",
|
||||
"diplomat-runtime",
|
||||
"icu_calendar",
|
||||
"icu_locale",
|
||||
"icu_locale_core",
|
||||
"num-traits",
|
||||
"temporal_rs",
|
||||
"timezone_provider",
|
||||
@@ -9978,13 +10003,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "temporal_rs"
|
||||
version = "0.1.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88afde3bd75d2fc68d77a914bece426aa08aa7649ffd0cdd4a11c3d4d33474d1"
|
||||
checksum = "9a902a45282e5175186b21d355efc92564601efe6e2d92818dc9e333d50bd4de"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"core_maths",
|
||||
"icu_calendar",
|
||||
"icu_locale",
|
||||
"icu_locale_core",
|
||||
"ixdtf",
|
||||
"num-traits",
|
||||
"timezone_provider",
|
||||
@@ -10105,9 +10131,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "timezone_provider"
|
||||
version = "0.1.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9ba0000e9e73862f3e7ca1ff159e2ddf915c9d8bb11e38a7874760f445d993"
|
||||
checksum = "c48f9b04628a2b813051e4dfe97c65281e49625eabd09ec343190e31e399a8c2"
|
||||
dependencies = [
|
||||
"tinystr",
|
||||
"zerotrie",
|
||||
@@ -10834,9 +10860,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "v8"
|
||||
version = "146.4.0"
|
||||
version = "147.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d97bcac5cdc5a195a4813f1855a6bc658f240452aac36caa12fd6c6f16026ab1"
|
||||
checksum = "2df8fffd507fb18ed000673a83d937f58e60fb07f3306b2274284125b15137cd"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bitflags 2.11.1",
|
||||
@@ -12014,9 +12040,9 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
[[package]]
|
||||
name = "zoneinfo64"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2e5597efbe7c421da8a7fd396b20b571704e787c21a272eecf35dfe9d386f0"
|
||||
checksum = "ed6eb2607e906160c457fd573e9297e65029669906b9ac8fb1b5cd5e055f0705"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"icu_locale_core",
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ codexport.workspace = true
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
anyhow = "1.0.102"
|
||||
anyhow = {version = "1.0.102", features = ["std"] }
|
||||
tokio = { version = "1.52.3", features = ["full"] }
|
||||
rand = "0.10.1"
|
||||
|
||||
|
||||
+21
-10
@@ -11,33 +11,44 @@ rand.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
# can this not show up
|
||||
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
|
||||
winapi = { version = "^0.3.9", features = ["std"] } # because CODEX toolchain is lowk different
|
||||
# 23898 on openai/codex i filed it hehehe
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "bin2"
|
||||
path = "src/bin2.rs"
|
||||
|
||||
|
||||
|
||||
[dependencies.codex-backend-client]
|
||||
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]
|
||||
git = "https://github.com/openai/codex.git"
|
||||
rev = "326e31ab65dcbdf70c4a034b7adc5c8bd335d996"
|
||||
rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1"
|
||||
|
||||
[dependencies.codex-config]
|
||||
git = "https://github.com/openai/codex.git"
|
||||
rev = "326e31ab65dcbdf70c4a034b7adc5c8bd335d996"
|
||||
rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1"
|
||||
|
||||
[dependencies.codex-core]
|
||||
git = "https://github.com/openai/codex.git"
|
||||
rev = "326e31ab65dcbdf70c4a034b7adc5c8bd335d996"
|
||||
rev = "0e9d2221789734dc08313243ecbdc7cc20ee48d1"
|
||||
|
||||
[dependencies.codex-protocol]
|
||||
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
|
||||
# from codex-rs/Cargo.toml
|
||||
[patch.crates-io]
|
||||
# [patch.crates-io]
|
||||
# crossterm = { git = "https://github.com/nornagon/crossterm", rev = "87db8bfa6dc99427fd3b071681b07fc31c6ce995" }
|
||||
# ratatui = { git = "https://github.com/nornagon/ratatui", rev = "9b2ad1298408c45918ee9f8241a6f95498cdbed2" }
|
||||
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" }
|
||||
# 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" }
|
||||
@@ -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
@@ -9,8 +9,11 @@
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
pub use codex_app_server_protocol::AuthMode;
|
||||
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::AuthManager as CodexAuthManager;
|
||||
use codex_login::AuthManager;
|
||||
@@ -24,22 +27,26 @@ pub struct Huge {
|
||||
|
||||
// this uses config
|
||||
async fn stock_backend_client() -> anyhow::Result<Huge> {
|
||||
let config = Config::load_with_cli_overrides(Vec::new())
|
||||
.await
|
||||
.context("Failed to load config")?;
|
||||
let config = default_config().await.context("Failed to load config")?;
|
||||
let auth_manager =
|
||||
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 {
|
||||
config,
|
||||
auth_manager,
|
||||
client,
|
||||
})
|
||||
}
|
||||
/// this uses config
|
||||
pub async fn backend_client_from_auth_mgr_and_default_config(
|
||||
|
||||
pub async fn backend_client(
|
||||
auth_mgr: &AuthManager,
|
||||
base_url: Option<String>,
|
||||
) -> 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
|
||||
.auth()
|
||||
.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”)
|
||||
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)?;
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
use codex_protocol::account::PlanType as AccountPlanType;
|
||||
pub use codex_protocol::protocol::RateLimitSnapshot as RateLimit; // wym snapshot
|
||||
async fn default_base_url() -> Result<String, anyhow::Error> {
|
||||
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> {
|
||||
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.
|
||||
///
|
||||
/// 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>> {
|
||||
let store = codex_login::AuthCredentialsStoreMode::Ephemeral;
|
||||
pub async fn chatgpt_login_to_auth_json(
|
||||
codex_home: &Path,
|
||||
port: Option<u16>,
|
||||
) -> Result<Option<AuthDotJson>> {
|
||||
let store = AuthCredentialsStoreMode::Ephemeral;
|
||||
let mut opts = ServerOptions::new(
|
||||
codex_home.to_path_buf(),
|
||||
CLIENT_ID.to_string(), // oh hell nah son
|
||||
None,
|
||||
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?
|
||||
// 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;
|
||||
let server = run_login_server(opts)?;
|
||||
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?;
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let huge = stock_backend_client().await?;
|
||||
quota(&huge.client).await
|
||||
@@ -156,21 +175,23 @@ pub async fn use_login_server(
|
||||
.into_path_buf(),
|
||||
};
|
||||
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?
|
||||
.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.
|
||||
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(|| {
|
||||
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.
|
||||
Ok((auth_mgr, auth_json))
|
||||
}
|
||||
|
||||
use codex_config::types::AuthCredentialsStoreMode;
|
||||
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(
|
||||
codex_home: &Path,
|
||||
store_mode: AuthCredentialsStoreMode,
|
||||
@@ -184,38 +205,58 @@ pub async fn auth_manager(
|
||||
.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.
|
||||
pub fn random_path_generator(name: Option<String>) -> PathBuf {
|
||||
PathBuf::from_iter(
|
||||
std::iter::repeat_with(|| rand::random::<u64>().to_string())
|
||||
.take(3)
|
||||
.take(5) // you would NEVER get the same path as me
|
||||
.chain(name),
|
||||
)
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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
|
||||
/// ```
|
||||
///
|
||||
/// 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(
|
||||
auth_json: AuthDotJson,
|
||||
) -> Result<(Arc<AuthManager>, PathBuf)> {
|
||||
let store_mode = codex_login::AuthCredentialsStoreMode::Ephemeral;
|
||||
let path = random_path_generator(None);
|
||||
let path = random_path_generator(id_from_auth_json(&auth_json).ok());
|
||||
|
||||
// load auth.json
|
||||
codex_login::save_auth(&path, &auth_json, store_mode)?;
|
||||
let store_mode = save_auth_json_ephemeral(&path, auth_json)?;
|
||||
// this must finish its work
|
||||
// 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
|
||||
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.
|
||||
pub async fn get_plan(auth_mgr: &AuthManager) -> anyhow::Result<Option<AccountPlanType>> {
|
||||
let plan = auth_mgr
|
||||
@@ -225,3 +266,64 @@ pub async fn get_plan(auth_mgr: &AuthManager) -> anyhow::Result<Option<AccountPl
|
||||
.account_plan_type();
|
||||
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"))
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
use codexport::chatgpt_login_to_auth_json;
|
||||
|
||||
mod bin2;
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
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(
|
||||
&random_home,
|
||||
codex_config::types::AuthCredentialsStoreMode::Ephemeral,
|
||||
|
||||
@@ -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 <acc> -- codex logout -> csa swap to that acount, then execvpe
|
||||
|
||||
optional features
|
||||
// important feature
|
||||
csa refresh (--all --expired OR <acc name>)
|
||||
|
||||
csa login
|
||||
csa logout
|
||||
csa refresh
|
||||
// account:
|
||||
csa account show (--all, --expired, <name>) default current
|
||||
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)
|
||||
|
||||
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.
|
||||
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
@@ -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
@@ -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
@@ -1,14 +1,6 @@
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
pub mod auth;
|
||||
pub mod cli;
|
||||
pub mod store;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
pub mod logic;
|
||||
+374
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//! whatever looks like an general utility goes here.
|
||||
Reference in new issue
Block a user