Skip to main content

ai_usagebar/modelstudio/
mod.rs

1//! Alibaba Cloud Model Studio — Token Plan usage over the console gateway the
2//! official `bl` CLI uses (`bl auth login --console`). A local-login vendor:
3//! the credential is the CLI's own `~/.bailian/config.json` console token
4//! (read-only, never written; AK/SK refresh is out of scope).
5//!
6//! Reports two ratio windows — a 5-hour and a weekly — with epoch-ms reset
7//! instants, dispatched per region×site through the gateway matrix in
8//! `types.rs`. Cache is scoped by a fingerprint of the access token, so a
9//! re-login never inherits the previous session's figures, and the token
10//! itself never persists anywhere but the CLI's file.
11
12pub mod creds;
13pub mod fetch;
14pub mod types;
15pub mod vendor;
16
17use std::path::{Path, PathBuf};
18
19use crate::config::ModelStudioConfig;
20use crate::error::Result;
21
22/// The `bl` CLI's own config directory, under `$HOME`.
23pub const APP_DIR_NAME: &str = ".bailian";
24/// The console-login file inside it.
25pub const CONFIG_FILE_NAME: &str = "config.json";
26/// Environment override for the whole directory (tilde-expanded).
27pub const CONFIG_DIR_ENV: &str = "BAILIAN_CONFIG_DIR";
28
29/// The config file path with every input injected — the test seam, so no test
30/// resolves a real `$HOME` or branches on the ambient environment. Precedence
31/// matches the CLI: an explicit `[modelstudio] config_dir`, then
32/// `BAILIAN_CONFIG_DIR`, then `~/.bailian`.
33pub fn config_path_in(cfg: &ModelStudioConfig, env_dir: Option<PathBuf>, home: &Path) -> PathBuf {
34    cfg.config_dir
35        .clone()
36        .or(env_dir)
37        .unwrap_or_else(|| home.join(APP_DIR_NAME))
38        .join(CONFIG_FILE_NAME)
39}
40
41/// The config file path against the real environment.
42pub fn config_path(cfg: &ModelStudioConfig) -> Result<PathBuf> {
43    let env_dir = std::env::var_os(CONFIG_DIR_ENV)
44        .filter(|value| !value.is_empty())
45        .map(PathBuf::from)
46        .map(|p| expand_tilde(&p));
47    Ok(config_path_in(cfg, env_dir, &crate::cache::home_dir()?))
48}
49
50/// Expand a leading `~` (or `~/`) against the user's home directory, the same
51/// rule `[grokbot] secrets_path` gets at config-load time. Anything else —
52/// including `~user` — is left untouched.
53fn expand_tilde(p: &Path) -> PathBuf {
54    let home = crate::cache::home_dir().unwrap_or_else(|_| p.to_path_buf());
55    expand_tilde_in(p, &home)
56}
57
58/// The pure half of [`expand_tilde`], with the home directory injected.
59fn expand_tilde_in(p: &Path, home: &Path) -> PathBuf {
60    let Some(s) = p.to_str() else {
61        return p.to_path_buf();
62    };
63    let rest = if s == "~" {
64        ""
65    } else if let Some(r) = s.strip_prefix("~/") {
66        r
67    } else {
68        return p.to_path_buf();
69    };
70    if rest.is_empty() {
71        home.to_path_buf()
72    } else {
73        home.join(rest)
74    }
75}
76
77/// Resolve the console session: the CLI's file, read-only.
78pub fn resolve_credentials(cfg: &ModelStudioConfig) -> Result<creds::Credentials> {
79    creds::read_from(&config_path(cfg)?)
80}
81
82pub use fetch::{FetchOutcome, fetch_snapshot_with};
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn the_default_path_lives_in_the_bl_cli_dir() {
90        let cfg = ModelStudioConfig::default();
91        let path = config_path_in(&cfg, None, Path::new("/home/u"));
92        assert_eq!(path, PathBuf::from("/home/u/.bailian/config.json"));
93    }
94
95    #[test]
96    fn a_configured_dir_wins_over_the_env_and_the_default() {
97        let cfg = ModelStudioConfig {
98            config_dir: Some(PathBuf::from("/cfg/bl")),
99            ..ModelStudioConfig::default()
100        };
101        let path = config_path_in(&cfg, Some(PathBuf::from("/env/bl")), Path::new("/home/u"));
102        assert_eq!(path, PathBuf::from("/cfg/bl/config.json"));
103    }
104
105    #[test]
106    fn the_env_dir_wins_over_the_default() {
107        let cfg = ModelStudioConfig::default();
108        let path = config_path_in(&cfg, Some(PathBuf::from("/env/bl")), Path::new("/home/u"));
109        assert_eq!(path, PathBuf::from("/env/bl/config.json"));
110    }
111
112    #[test]
113    fn a_tilde_in_the_env_dir_expands() {
114        assert_eq!(
115            expand_tilde_in(Path::new("~/bl"), Path::new("/home/u")),
116            PathBuf::from("/home/u/bl")
117        );
118        // Bare `~` is the home itself; `~user` stays literal.
119        assert_eq!(
120            expand_tilde_in(Path::new("~"), Path::new("/home/u")),
121            PathBuf::from("/home/u")
122        );
123        assert_eq!(
124            expand_tilde_in(Path::new("~other/config"), Path::new("/home/u")),
125            PathBuf::from("~other/config")
126        );
127    }
128}