Ts a Mistake
This commit is contained in:
11 files changed
+22292
No files matched your search
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
/codexport/target
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"rust-analyzer.linkedProjects": [
|
||||
// giving up is real
|
||||
// "./codexport/Cargo.toml"
|
||||
"./Cargo.toml"
|
||||
]
|
||||
}
|
||||
Generated
+12096
File diff suppressed because it is too large.
Load diff
+47
@@ -0,0 +1,47 @@
|
||||
[package]
|
||||
name = "lda-codex-account"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = ["gitea"]
|
||||
|
||||
[dependencies]
|
||||
clap = "4.6.1"
|
||||
reqwest = { version = "0.13.3", features = ["blocking", "json"] }
|
||||
|
||||
anyhow.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
codexport.workspace = true
|
||||
|
||||
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
anyhow = "1.0.102"
|
||||
tokio = { version = "1.52.3", features = ["full"] }
|
||||
rand = "0.10.1"
|
||||
|
||||
[workspace.dependencies."codexport"]
|
||||
path = "./codexport"
|
||||
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"codexport",
|
||||
]
|
||||
|
||||
# codexport shit
|
||||
# why do you do me like this cargo i hate you cargo
|
||||
# why do i have to care about an external dependency's patches. i do not care about what patches codex-rs has. i just want to use codex-login and codex-config. but no, if i want to use codex-login and codex-config, i have to also take all the relevant patches that codex-rs has.
|
||||
# And the next problem, THEY APPLY TO EVERY OTHER PACKAGES that want to use tokio-tungstenite and tungstenite. So if i want to use codex-login, then all my dependencies that uses tokio-tungstenite and tungstenite will also be affected by these patches.
|
||||
# Now i'm at the mercy of openai's forks. Unless openai perfectly maintains these forks and keeps them up to date with the original repos, my project will be using outdated versions of tokio-tungstenite and tungstenite, and i have no choice in the matter. I can't even choose to only apply these patches to codex-login and codex-config, because cargo doesn't allow that. I just want to use codex-login and codex-config, but now i have to also worry about the state of openai's forks of tokio-tungstenite and tungstenite. This is a nightmare.
|
||||
# If openai's forks of tokio-tungstenite and tungstenite are not well maintained, then my project will be using outdated versions of these libraries, which could have security vulnerabilities or bugs that have been fixed in the original repos. And since i have no choice in the matter, i am forced to trust that openai will maintain these forks properly. This is a huge risk for my project, and it's all because of cargo's patch system and how it applies to the entire dependency graph. I just want to use codex-login and codex-config, but now i have to also worry about the state of openai's forks of tokio-tungstenite and tungstenite. This is a nightmare.
|
||||
# I really wish cargo had a way to apply patches only to specific dependencies, instead of the entire dependency graph. That way, i could just apply these patches to codex-login and codex-config, and not affect any other dependencies that use tokio-tungstenite and tungstenite. But no, cargo doesn't allow that, so now i have to deal with this mess. I just want to use codex-login and codex-config, but now i have to also worry about the state of openai's forks of tokio-tungstenite and tungstenite. This is a nightmare.
|
||||
# at least if openai maintains these forks well, then it's not a huge problem. but if they don't, then my project is in trouble. i just want to use codex-login and codex-config, but now i have to also worry about the state of openai's forks of tokio-tungstenite and tungstenite. This is a nightmare.
|
||||
# from codex-rs/Cargo.toml
|
||||
[patch.crates-io]
|
||||
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" }
|
||||
Generated
+9847
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,43 @@
|
||||
[package]
|
||||
name = "codexport"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
anyhow.workspace = true
|
||||
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
|
||||
|
||||
[dependencies.codex-backend-client]
|
||||
git = "https://github.com/openai/codex.git"
|
||||
rev = "54ec99cb545176d23790a757a98f18925dcc3191" # today is a good day to update the rev
|
||||
|
||||
[dependencies.codex-login]
|
||||
git = "https://github.com/openai/codex.git"
|
||||
rev = "54ec99cb545176d23790a757a98f18925dcc3191"
|
||||
|
||||
[dependencies.codex-config]
|
||||
git = "https://github.com/openai/codex.git"
|
||||
rev = "54ec99cb545176d23790a757a98f18925dcc3191"
|
||||
|
||||
[dependencies.codex-core]
|
||||
git = "https://github.com/openai/codex.git"
|
||||
rev = "54ec99cb545176d23790a757a98f18925dcc3191"
|
||||
|
||||
[dependencies.codex-protocol]
|
||||
git = "https://github.com/openai/codex.git"
|
||||
rev = "54ec99cb545176d23790a757a98f18925dcc3191"
|
||||
|
||||
# codexport shit
|
||||
# from codex-rs/Cargo.toml
|
||||
[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" }
|
||||
@@ -0,0 +1,13 @@
|
||||
yo wsp
|
||||
|
||||
findings
|
||||
|
||||
# authenticate
|
||||
|
||||
you need a Config, then build a AuthManager from it.
|
||||
config will solve: Codex home, chatgpt base url
|
||||
Authdotjson is just File save type.
|
||||
|
||||
# status
|
||||
|
||||
you need to convert Authmanager to Codexauth, then run the backend_client::Client method get_rate_limits with it
|
||||
@@ -0,0 +1,138 @@
|
||||
//! Exports and convenience from [codex-cli][ccrs]
|
||||
//!
|
||||
//! [ccrs]: https://github.com/openai/codex/tree/main/codex-rs
|
||||
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use codex_backend_client::Client as BackendClient;
|
||||
use codex_core::config::Config;
|
||||
pub use codex_login::AuthDotJson;
|
||||
pub use codex_login::AuthManager as CodexAuthManager;
|
||||
use codex_login::AuthManager;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Huge {
|
||||
pub config: Config,
|
||||
pub auth_manager: Arc<AuthManager>,
|
||||
pub client: BackendClient,
|
||||
}
|
||||
|
||||
async fn stock_backend_client() -> anyhow::Result<Huge> {
|
||||
let config = Config::load_with_cli_overrides(Vec::new())
|
||||
.await
|
||||
.context("Failed to load config")?;
|
||||
let auth_manager =
|
||||
AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await;
|
||||
let auth = auth_manager
|
||||
.auth()
|
||||
.await
|
||||
.ok_or_else(|| anyhow::anyhow!("Not logged in; run `codex login`"))?;
|
||||
|
||||
// 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 client = BackendClient::from_auth(config.chatgpt_base_url.clone(), &auth)?;
|
||||
Ok(Huge {
|
||||
config,
|
||||
auth_manager,
|
||||
client,
|
||||
})
|
||||
}
|
||||
|
||||
pub use codex_protocol::protocol::RateLimitSnapshot as RateLimit; // wym snapshot
|
||||
|
||||
async fn quota(client: &BackendClient) -> anyhow::Result<RateLimit> {
|
||||
let quota = client.get_rate_limits().await?;
|
||||
Ok(quota)
|
||||
}
|
||||
|
||||
pub async fn renew(auth_mgr: &AuthManager) -> anyhow::Result<()> {
|
||||
// how is the codex-cli codebase even alive
|
||||
auth_mgr.refresh_token().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use anyhow::Result;
|
||||
use codex_login::{CLIENT_ID, ServerOptions, run_login_server};
|
||||
|
||||
/// side effects include:
|
||||
/// - running a temporary server for the device code flow
|
||||
/// - logged in for the rest of the process
|
||||
/// - that thing goes as soon as the process ends because ephemeral store is a lazy mutex
|
||||
/// - does not log out. that will expire after a while.
|
||||
/// - Do not write to disk. If you want to use the same login again, construct an AuthManager with Ephemeral store, and codex_home.
|
||||
/// you'll get the same auth.json in memory
|
||||
/// - this way you CAN logout using the auth manager.
|
||||
pub async fn chatgpt_login_to_auth_json(
|
||||
codex_home: std::path::PathBuf,
|
||||
) -> Result<Option<AuthDotJson>> {
|
||||
let store = codex_login::AuthCredentialsStoreMode::Ephemeral;
|
||||
let opts = ServerOptions::new(
|
||||
codex_home.clone(),
|
||||
CLIENT_ID.to_string(), // oh hell nah son
|
||||
None,
|
||||
store,
|
||||
);
|
||||
|
||||
let server = run_login_server(opts)?;
|
||||
eprintln!("Open this URL to login:\n\n{}\n", server.auth_url);
|
||||
|
||||
server.block_until_done().await?;
|
||||
|
||||
codex_login::load_auth_dot_json(&codex_home, store).context("can not load saved auth.json")
|
||||
}
|
||||
|
||||
pub async fn get_rate_limit() -> anyhow::Result<RateLimit> {
|
||||
let huge = stock_backend_client().await?;
|
||||
quota(&huge.client).await
|
||||
}
|
||||
|
||||
pub async fn use_login_server(
|
||||
codex_home: Option<std::path::PathBuf>,
|
||||
) -> anyhow::Result<Arc<AuthManager>> {
|
||||
// manual map because the ? operator doesn't work in closures and I don't wanna deal with it
|
||||
let codex_home = match codex_home {
|
||||
Some(path) => path,
|
||||
None => codex_core::config::find_codex_home()
|
||||
.context("Can not find codex home")?
|
||||
.into_path_buf(),
|
||||
};
|
||||
let cih = || codex_home.clone();
|
||||
chatgpt_login_to_auth_json(cih()).await?;
|
||||
let auth_mgr = auth_manager(cih(), codex_login::AuthCredentialsStoreMode::Ephemeral).await;
|
||||
Ok(auth_mgr)
|
||||
}
|
||||
|
||||
use codex_config::types::AuthCredentialsStoreMode;
|
||||
use std::path::PathBuf;
|
||||
|
||||
async fn auth_manager(
|
||||
codex_home: PathBuf,
|
||||
store_mode: AuthCredentialsStoreMode,
|
||||
) -> Arc<AuthManager> {
|
||||
AuthManager::shared(
|
||||
codex_home, /*enable_codex_api_key_env*/ false, store_mode,
|
||||
None, // uses default https://chatgpt.com/backend-api
|
||||
)
|
||||
.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)
|
||||
.chain(name),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn auth_mgr_from_auth_json(auth_json: AuthDotJson) -> Result<Arc<AuthManager>> {
|
||||
let store = codex_login::AuthCredentialsStoreMode::Ephemeral;
|
||||
let auth_manager = AuthManager::shared(random_path_generator(None), false, store, None).await; // C ahh arguments
|
||||
Ok(auth_manager)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
# yo idk how to use this
|
||||
|
||||
build *args:
|
||||
cargo build {{ args }}
|
||||
build-codexport *args:
|
||||
cargo build -p codexport {{ args }}
|
||||
|
||||
bc *args: (build-codexport args)
|
||||
b *args: (build-codexport args) (build args)
|
||||
br: (b "--release")
|
||||
|
||||
test *args:
|
||||
cargo test {{ args }}
|
||||
test-codexport *args:
|
||||
cargo test -p codexport {{ args }}
|
||||
|
||||
tc *args: (test-codexport args)
|
||||
t *args: (test-codexport args) (test args)
|
||||
tr: (t "--release")
|
||||
|
||||
fmt all="--all":
|
||||
cargo fmt {{ all }}
|
||||
fmt-codexport all="--all":
|
||||
cargo fmt -p codexport {{ all }}
|
||||
clippy all="--workspace --all-targets --all-features" *args:
|
||||
cargo clippy {{ all }} {{ args }}
|
||||
clippy-codexport all="--workspace --all-targets --all-features" *args:
|
||||
cargo clippy -p codexport {{ all }} {{ args }}
|
||||
lint *args: (clippy-codexport args) (clippy args)
|
||||
format: fmt-codexport fmt
|
||||
|
||||
f release="": (lint release) format
|
||||
fr: (f "--release")
|
||||
|
||||
r *args:
|
||||
cargo run {{ args }}
|
||||
rr: (r "--release")
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
use anyhow::Context;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let codex_auth_file = std::env::home_dir()
|
||||
.context("failed to get home directory")?
|
||||
.join(".codex")
|
||||
.join("auth.json");
|
||||
let (access_token, account_id) =
|
||||
read_tokens(&codex_auth_file).context("failed to read tokens")?;
|
||||
fetch_account_info(&access_token, &account_id).context("failed to fetch account info")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_tokens(p: impl AsRef<Path>) -> anyhow::Result<(String, String)> {
|
||||
let auth = serde_json::from_str::<codexport::AuthDotJson>(
|
||||
&std::fs::read_to_string(p).context("failed to read auth file")?,
|
||||
)
|
||||
.context("failed to parse auth file as JSON")?;
|
||||
let access_token = auth
|
||||
.tokens
|
||||
.as_ref()
|
||||
.context("missing tokens field in auth.json")?
|
||||
.access_token
|
||||
.clone();
|
||||
let account_id = auth
|
||||
.tokens
|
||||
.as_ref()
|
||||
.context("missing tokens field in auth.json")?
|
||||
.account_id
|
||||
.clone()
|
||||
.context("missing account_id field in auth.json")?;
|
||||
Ok((access_token, account_id))
|
||||
}
|
||||
|
||||
fn fetch_account_info(access_token: &str, account_id: &str) -> anyhow::Result<()> {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let response = client
|
||||
.get("https://chatgpt.com/backend-api/wham/usage") // only for chatgpt accounts which is MY account. may tap into the real codex-cli for endpoint stability.
|
||||
.header("Authorization", format!("Bearer {}", access_token))
|
||||
// .header("ChatGPT-Account-Id", account_id) // so auth is enough to get the info, no need for account id
|
||||
.send()?;
|
||||
let json = response
|
||||
.json::<serde_json::Value>()
|
||||
.context("doesnt look like a json response")?;
|
||||
println!("info: {:#?}", json); // holy shit this works
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user