Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfa8264642 | ||
|
|
fcb567ec3c |
No files matched your search
@@ -13,9 +13,25 @@ cargo run -p support-tool -- kinovi_web generate_video --prompt "A corgi at the
|
||||
|
||||
Requires a `.env-support-tool-secrets` file (or env vars) with:
|
||||
|
||||
- `SEEDANCE2PRO_COOKIES` — session cookies for Kinovi/KinoviWeb API auth
|
||||
- `SEEDANCE2PRO_VOLCENGINE_COOKIES` — Volcengine cookies (legacy fallback: `SEEDANCE2PRO_COOKIES`)
|
||||
- `SEEDANCE2PRO_BYTEPLUS_COOKIES` — BytePlus cookies (legacy fallback: `SEEDANCE2PRO_ALT_COOKIES`)
|
||||
- `SEEDANCE2PRO_BYTEPLUS_ULTRA_COOKIES` — BytePlus Ultra cookies
|
||||
- `ARTCRAFT_COOKIES` — session cookies for ArtCraft API auth (format: `session=...; visitor=...`)
|
||||
|
||||
All `kinovi_web` commands (also available as `kinovi` / `seedance2pro`) accept
|
||||
`--kinovi-account volcengine|byteplus|byteplusultra`, defaulting to `volcengine`.
|
||||
Use `--cookies-env ENV_VAR_NAME` to explicitly override that account's cookie
|
||||
configuration. The override has no default; when omitted, only the selected
|
||||
account's variables and its legacy fallback are checked. Either flag can appear
|
||||
before or after the subcommand. CSV account labels default to the resolved cookie
|
||||
env var name; `--account` still overrides the output label.
|
||||
|
||||
```bash
|
||||
support-tool kinovi_web account_info --kinovi-account byteplus
|
||||
support-tool seedance2pro find_job --token <order_id> --kinovi-account byteplusultra
|
||||
support-tool kinovi_web account_info --kinovi-account byteplus --cookies-env CUSTOM_COOKIES
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
- Entry point: `src/main.rs` → normalizes args (underscores optional) → dispatches
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use clap::ValueEnum;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
|
||||
#[value(rename_all = "lowercase")]
|
||||
pub enum KinoviAccount {
|
||||
Volcengine,
|
||||
BytePlus,
|
||||
BytePlusUltra,
|
||||
}
|
||||
|
||||
impl KinoviAccount {
|
||||
/// Cookie configuration and legacy fallbacks used by seedance2-pro-job.
|
||||
pub fn cookie_env_vars(self) -> &'static [&'static str] {
|
||||
match self {
|
||||
Self::Volcengine => &["SEEDANCE2PRO_VOLCENGINE_COOKIES", "SEEDANCE2PRO_COOKIES"],
|
||||
Self::BytePlus => &["SEEDANCE2PRO_BYTEPLUS_COOKIES", "SEEDANCE2PRO_ALT_COOKIES"],
|
||||
Self::BytePlusUltra => &["SEEDANCE2PRO_BYTEPLUS_ULTRA_COOKIES"],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use anyhow::anyhow;
|
||||
use clap::Subcommand;
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
use super::account::KinoviAccount;
|
||||
use super::state::KinoviWebState;
|
||||
use super::subcommands;
|
||||
|
||||
@@ -16,6 +16,20 @@ pub const SUBCOMMAND_NAMES: &[&str] = &[
|
||||
"generate_video",
|
||||
];
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct KinoviWebArgs {
|
||||
/// Kinovi account whose cookie configuration to use.
|
||||
#[arg(long, global = true, value_enum, ignore_case = true, default_value = "volcengine")]
|
||||
pub kinovi_account: KinoviAccount,
|
||||
|
||||
/// Read cookies from this env var, overriding --kinovi-account only when supplied.
|
||||
#[arg(long = "cookies-env", global = true, value_name = "ENV_VAR_NAME")]
|
||||
pub maybe_cookies_env: Option<String>,
|
||||
|
||||
#[command(subcommand)]
|
||||
pub command: KinoviWebCommand,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
#[command(rename_all = "snake_case")]
|
||||
pub enum KinoviWebCommand {
|
||||
@@ -44,13 +58,10 @@ pub enum KinoviWebCommand {
|
||||
GenerateVideo(subcommands::generate_video::GenerateVideoArgs),
|
||||
}
|
||||
|
||||
pub async fn run(command: KinoviWebCommand) -> anyhow::Result<()> {
|
||||
let cookies = easyenv::get_env_string_required("SEEDANCE2PRO_COOKIES")
|
||||
.map_err(|err| anyhow!("Missing SEEDANCE2PRO_COOKIES env var: {:?}", err))?;
|
||||
pub async fn run(args: KinoviWebArgs) -> anyhow::Result<()> {
|
||||
let state = KinoviWebState::from_env(args.kinovi_account, args.maybe_cookies_env.as_deref())?;
|
||||
|
||||
let state = KinoviWebState { cookies };
|
||||
|
||||
match command {
|
||||
match args.command {
|
||||
KinoviWebCommand::AccountInfo(args) => subcommands::account_info::run(&state, args).await,
|
||||
KinoviWebCommand::AuditCredits(args) => subcommands::audit_credits::run(&state, args).await,
|
||||
KinoviWebCommand::AuditOrders(args) => subcommands::audit_orders::run(&state, args).await,
|
||||
@@ -60,3 +71,85 @@ pub async fn run(command: KinoviWebCommand) -> anyhow::Result<()> {
|
||||
KinoviWebCommand::GenerateVideo(args) => subcommands::generate_video::run(&state, args).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use clap::{error::ErrorKind, Parser};
|
||||
|
||||
use super::*;
|
||||
use crate::commands::run::{all_canonical_names, Cli, TopLevelCommand};
|
||||
use crate::utils::normalize_subcommands::normalize_subcommand_args;
|
||||
|
||||
const COMMANDS: &[&[&str]] = &[
|
||||
&["account_info"],
|
||||
&["audit_credits", "--out", "/tmp/credits.csv"],
|
||||
&["audit_orders", "--out", "/tmp/orders.csv"],
|
||||
&["audit_payments", "--out", "/tmp/payments.csv"],
|
||||
&["find_job", "--token", "test-order"],
|
||||
&["failed_job_histogram"],
|
||||
&["generate_video", "--prompt", "A corgi at the lake"],
|
||||
];
|
||||
|
||||
mod cli_parsing {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn every_command_defaults_to_volcengine_without_a_cookie_override() {
|
||||
for command in COMMANDS {
|
||||
let args = parse_kinovi(command);
|
||||
assert_eq!(args.kinovi_account, KinoviAccount::Volcengine);
|
||||
assert!(args.maybe_cookies_env.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_command_accepts_all_accounts_and_cookie_overrides_before_or_after_subcommand() {
|
||||
for (value, account) in [
|
||||
("volcengine", KinoviAccount::Volcengine),
|
||||
("byteplus", KinoviAccount::BytePlus),
|
||||
("byteplusultra", KinoviAccount::BytePlusUltra),
|
||||
] {
|
||||
for command in COMMANDS {
|
||||
let flags = ["--kinovi-account", value, "--cookies-env", "CUSTOM_COOKIES"];
|
||||
for args in [
|
||||
[flags.as_slice(), *command].concat(),
|
||||
[*command, flags.as_slice()].concat(),
|
||||
] {
|
||||
let args = parse_kinovi(&args);
|
||||
assert_eq!(args.kinovi_account, account);
|
||||
assert_eq!(args.maybe_cookies_env.as_deref(), Some("CUSTOM_COOKIES"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliases_and_underscore_normalization_preserve_account_selection() {
|
||||
for alias in ["kinovi_web", "kinoviweb", "kinovi", "seedance2pro", "seedance2_pro"] {
|
||||
let args = ["support-tool", alias, "accountinfo", "--kinovi-account", "BYTEPLUSULTRA"];
|
||||
let args = normalize_subcommand_args(args.map(String::from), &all_canonical_names());
|
||||
let cli = Cli::try_parse_from(args).unwrap();
|
||||
let TopLevelCommand::KinoviWeb(args) = cli.command else {
|
||||
panic!("expected Kinovi command");
|
||||
};
|
||||
assert_eq!(args.kinovi_account, KinoviAccount::BytePlusUltra);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_unknown_accounts() {
|
||||
let err = Cli::try_parse_from([
|
||||
"support-tool", "kinovi_web", "account_info", "--kinovi-account", "unknown",
|
||||
]).err().expect("unknown accounts should be rejected");
|
||||
assert_eq!(err.kind(), ErrorKind::InvalidValue);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_kinovi(args: &[&str]) -> KinoviWebArgs {
|
||||
let cli = Cli::try_parse_from([&["support-tool", "kinovi_web"], args].concat()).unwrap();
|
||||
match cli.command {
|
||||
TopLevelCommand::KinoviWeb(args) => args,
|
||||
_ => panic!("expected Kinovi command"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
mod account;
|
||||
pub mod dispatch;
|
||||
mod state;
|
||||
mod subcommands;
|
||||
|
||||
pub use dispatch::KinoviWebCommand;
|
||||
pub use dispatch::KinoviWebArgs;
|
||||
pub use dispatch::run;
|
||||
@@ -1,3 +1,131 @@
|
||||
use anyhow::anyhow;
|
||||
|
||||
use super::account::KinoviAccount;
|
||||
|
||||
pub struct KinoviWebState {
|
||||
pub cookies: String,
|
||||
pub cookies_env: String,
|
||||
}
|
||||
|
||||
impl KinoviWebState {
|
||||
pub fn from_env(account: KinoviAccount, maybe_cookies_env: Option<&str>) -> anyhow::Result<Self> {
|
||||
Self::from_env_lookup(account, maybe_cookies_env, easyenv::get_env_string_optional)
|
||||
}
|
||||
|
||||
fn from_env_lookup(
|
||||
account: KinoviAccount,
|
||||
maybe_cookies_env: Option<&str>,
|
||||
mut lookup: impl FnMut(&str) -> Option<String>,
|
||||
) -> anyhow::Result<Self> {
|
||||
// An explicit override must resolve on its own, without requiring or falling
|
||||
// back to credentials for the selected account.
|
||||
let override_vars;
|
||||
let cookie_env_vars = match maybe_cookies_env {
|
||||
Some(var) => {
|
||||
override_vars = [var];
|
||||
&override_vars[..]
|
||||
},
|
||||
None => account.cookie_env_vars(),
|
||||
};
|
||||
|
||||
for &var in cookie_env_vars {
|
||||
if let Some(cookies) = lookup(var) {
|
||||
return Ok(Self {
|
||||
cookies,
|
||||
cookies_env: var.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("Missing Kinovi cookies: set {}", cookie_env_vars.join(" or ")))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const COOKIES: &[(&str, &str)] = &[
|
||||
("SEEDANCE2PRO_VOLCENGINE_COOKIES", "volcengine-cookies"),
|
||||
("SEEDANCE2PRO_BYTEPLUS_COOKIES", "byteplus-cookies"),
|
||||
("SEEDANCE2PRO_BYTEPLUS_ULTRA_COOKIES", "ultra-cookies"),
|
||||
("SEEDANCE2PRO_COOKIES", "legacy-cookies"),
|
||||
("SEEDANCE2PRO_ALT_COOKIES", "legacy-alt-cookies"),
|
||||
("CUSTOM_COOKIES", "override-cookies"),
|
||||
];
|
||||
|
||||
mod account_selection {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn selects_each_account_even_when_legacy_cookies_are_set() {
|
||||
for (account, var, cookies) in [
|
||||
(KinoviAccount::Volcengine, "SEEDANCE2PRO_VOLCENGINE_COOKIES", "volcengine-cookies"),
|
||||
(KinoviAccount::BytePlus, "SEEDANCE2PRO_BYTEPLUS_COOKIES", "byteplus-cookies"),
|
||||
(KinoviAccount::BytePlusUltra, "SEEDANCE2PRO_BYTEPLUS_ULTRA_COOKIES", "ultra-cookies"),
|
||||
] {
|
||||
for env in [COOKIES, &[(var, cookies)]] {
|
||||
let state = resolve(account, None, env).unwrap();
|
||||
assert_eq!(state.cookies, cookies);
|
||||
assert_eq!(state.cookies_env, var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn falls_back_to_the_matching_legacy_variable() {
|
||||
let env = &COOKIES[3..5];
|
||||
for (account, var, cookies) in [
|
||||
(KinoviAccount::Volcengine, "SEEDANCE2PRO_COOKIES", "legacy-cookies"),
|
||||
(KinoviAccount::BytePlus, "SEEDANCE2PRO_ALT_COOKIES", "legacy-alt-cookies"),
|
||||
] {
|
||||
let state = resolve(account, None, env).unwrap();
|
||||
assert_eq!(state.cookies, cookies);
|
||||
assert_eq!(state.cookies_env, var);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_account_cookies_never_use_another_accounts_credentials() {
|
||||
for (account, expected_var) in [
|
||||
(KinoviAccount::BytePlus, "SEEDANCE2PRO_BYTEPLUS_COOKIES"),
|
||||
(KinoviAccount::BytePlusUltra, "SEEDANCE2PRO_BYTEPLUS_ULTRA_COOKIES"),
|
||||
] {
|
||||
let err = resolve(account, None, &COOKIES[3..4]).err().expect("account cookies are missing");
|
||||
assert!(err.to_string().contains(expected_var));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod cookie_overrides {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn explicit_override_wins_with_or_without_account_cookies() {
|
||||
for account in [KinoviAccount::Volcengine, KinoviAccount::BytePlus, KinoviAccount::BytePlusUltra] {
|
||||
for env in [COOKIES, &COOKIES[5..]] {
|
||||
let state = resolve(account, Some("CUSTOM_COOKIES"), env).unwrap();
|
||||
assert_eq!(state.cookies, "override-cookies");
|
||||
assert_eq!(state.cookies_env, "CUSTOM_COOKIES");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_explicit_override_errors_instead_of_using_account_cookies() {
|
||||
let err = resolve(KinoviAccount::Volcengine, Some("MISSING_COOKIES"), COOKIES)
|
||||
.err().expect("explicit override must exist");
|
||||
assert_eq!(err.to_string(), "Missing Kinovi cookies: set MISSING_COOKIES");
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve(
|
||||
account: KinoviAccount,
|
||||
maybe_override: Option<&str>,
|
||||
env: &[(&str, &str)],
|
||||
) -> anyhow::Result<KinoviWebState> {
|
||||
KinoviWebState::from_env_lookup(account, maybe_override, |var| {
|
||||
env.iter().find(|(name, _)| *name == var).map(|(_, value)| value.to_string())
|
||||
})
|
||||
}
|
||||
}
|
||||
+2
-16
@@ -11,28 +11,14 @@ use super::super::state::KinoviWebState;
|
||||
/// single CSV line: account,email,credits,available_credits.
|
||||
#[derive(Args)]
|
||||
pub struct AccountInfoArgs {
|
||||
/// Read session cookies from this env var instead of SEEDANCE2PRO_COOKIES.
|
||||
#[arg(long)]
|
||||
pub cookies_env: Option<String>,
|
||||
|
||||
/// Account label for the output line. Defaults to the cookies env var name.
|
||||
#[arg(long)]
|
||||
pub account: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn run(state: &KinoviWebState, args: AccountInfoArgs) -> anyhow::Result<()> {
|
||||
let (cookies, account_label) = match &args.cookies_env {
|
||||
Some(var) => (
|
||||
easyenv::get_env_string_required(var)
|
||||
.map_err(|err| anyhow!("Missing {} env var: {:?}", var, err))?,
|
||||
args.account.clone().unwrap_or_else(|| var.clone()),
|
||||
),
|
||||
None => (
|
||||
state.cookies.clone(),
|
||||
args.account.clone().unwrap_or_else(|| "SEEDANCE2PRO_COOKIES".to_string()),
|
||||
),
|
||||
};
|
||||
let session = KinoviWebSession::from_cookies_string(cookies);
|
||||
let account_label = args.account.as_deref().unwrap_or(&state.cookies_env);
|
||||
let session = KinoviWebSession::from_cookies_string(state.cookies.clone());
|
||||
|
||||
let details = get_user_auth_details(GetUserAuthDetailsArgs {
|
||||
session: &session,
|
||||
|
||||
+3
-17
@@ -44,10 +44,6 @@ pub struct AuditCreditsArgs {
|
||||
#[arg(long, default_value_t = 250)]
|
||||
pub delay_ms: u64,
|
||||
|
||||
/// Read session cookies from this env var instead of SEEDANCE2PRO_COOKIES.
|
||||
#[arg(long)]
|
||||
pub cookies_env: Option<String>,
|
||||
|
||||
/// Account label written into each CSV row. Defaults to the cookies env
|
||||
/// var name.
|
||||
#[arg(long)]
|
||||
@@ -55,18 +51,8 @@ pub struct AuditCreditsArgs {
|
||||
}
|
||||
|
||||
pub async fn run(state: &KinoviWebState, args: AuditCreditsArgs) -> anyhow::Result<()> {
|
||||
let (cookies, account_label) = match &args.cookies_env {
|
||||
Some(var) => (
|
||||
easyenv::get_env_string_required(var)
|
||||
.map_err(|err| anyhow!("Missing {} env var: {:?}", var, err))?,
|
||||
args.account.clone().unwrap_or_else(|| var.clone()),
|
||||
),
|
||||
None => (
|
||||
state.cookies.clone(),
|
||||
args.account.clone().unwrap_or_else(|| "SEEDANCE2PRO_COOKIES".to_string()),
|
||||
),
|
||||
};
|
||||
let session = KinoviWebSession::from_cookies_string(cookies);
|
||||
let account_label = args.account.as_deref().unwrap_or(&state.cookies_env);
|
||||
let session = KinoviWebSession::from_cookies_string(state.cookies.clone());
|
||||
|
||||
let file_is_new = std::fs::metadata(&args.out).map(|m| m.len() == 0).unwrap_or(true);
|
||||
let mut out = OpenOptions::new().create(true).append(true).open(&args.out)
|
||||
@@ -101,7 +87,7 @@ pub async fn run(state: &KinoviWebState, args: AuditCreditsArgs) -> anyhow::Resu
|
||||
let oldest_day = page.entries.last().map(|e| day_of(e)).unwrap_or_default();
|
||||
|
||||
for entry in &page.entries {
|
||||
write_entry_row(&mut out, &account_label, entry)?;
|
||||
write_entry_row(&mut out, account_label, entry)?;
|
||||
rows += 1;
|
||||
let tally = type_tallies.entry(entry.entry_type.as_str().to_string()).or_insert((0, 0));
|
||||
tally.0 += 1;
|
||||
|
||||
+5
-21
@@ -43,31 +43,15 @@ pub struct AuditOrdersArgs {
|
||||
#[arg(long, default_value_t = 250)]
|
||||
pub delay_ms: u64,
|
||||
|
||||
/// Read session cookies from this env var instead of SEEDANCE2PRO_COOKIES
|
||||
/// (e.g. SEEDANCE2PRO_VOLCENGINE_COOKIES / SEEDANCE2PRO_BYTEPLUS_COOKIES /
|
||||
/// SEEDANCE2PRO_BYTEPLUS_ULTRA_COOKIES to audit each Kinovi account).
|
||||
#[arg(long)]
|
||||
pub cookies_env: Option<String>,
|
||||
|
||||
/// Account label written into each CSV row. Defaults to the cookies env
|
||||
/// var name (or "SEEDANCE2PRO_COOKIES").
|
||||
/// var name.
|
||||
#[arg(long)]
|
||||
pub account: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn run(state: &KinoviWebState, args: AuditOrdersArgs) -> anyhow::Result<()> {
|
||||
let (cookies, account_label) = match &args.cookies_env {
|
||||
Some(var) => (
|
||||
easyenv::get_env_string_required(var)
|
||||
.map_err(|err| anyhow!("Missing {} env var: {:?}", var, err))?,
|
||||
args.account.clone().unwrap_or_else(|| var.clone()),
|
||||
),
|
||||
None => (
|
||||
state.cookies.clone(),
|
||||
args.account.clone().unwrap_or_else(|| "SEEDANCE2PRO_COOKIES".to_string()),
|
||||
),
|
||||
};
|
||||
let session = KinoviWebSession::from_cookies_string(cookies);
|
||||
let account_label = args.account.as_deref().unwrap_or(&state.cookies_env);
|
||||
let session = KinoviWebSession::from_cookies_string(state.cookies.clone());
|
||||
|
||||
let file_is_new = std::fs::metadata(&args.out).map(|m| m.len() == 0).unwrap_or(true);
|
||||
let mut out = OpenOptions::new().create(true).append(true).open(&args.out)
|
||||
@@ -106,7 +90,7 @@ pub async fn run(state: &KinoviWebState, args: AuditOrdersArgs) -> anyhow::Resul
|
||||
stats.skipped_before_window += 1;
|
||||
continue;
|
||||
}
|
||||
write_order_row(&mut out, &account_label, order)?;
|
||||
write_order_row(&mut out, account_label, order)?;
|
||||
stats.record(order);
|
||||
}
|
||||
out.flush()?;
|
||||
@@ -133,7 +117,7 @@ pub async fn run(state: &KinoviWebState, args: AuditOrdersArgs) -> anyhow::Resul
|
||||
tokio::time::sleep(Duration::from_millis(args.delay_ms)).await;
|
||||
}
|
||||
|
||||
stats.print_summary(&account_label, cursor);
|
||||
stats.print_summary(account_label, cursor);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+3
-17
@@ -29,10 +29,6 @@ pub struct AuditPaymentsArgs {
|
||||
#[arg(long, default_value_t = 250)]
|
||||
pub delay_ms: u64,
|
||||
|
||||
/// Read session cookies from this env var instead of SEEDANCE2PRO_COOKIES.
|
||||
#[arg(long)]
|
||||
pub cookies_env: Option<String>,
|
||||
|
||||
/// Account label written into each CSV row. Defaults to the cookies env
|
||||
/// var name.
|
||||
#[arg(long)]
|
||||
@@ -40,18 +36,8 @@ pub struct AuditPaymentsArgs {
|
||||
}
|
||||
|
||||
pub async fn run(state: &KinoviWebState, args: AuditPaymentsArgs) -> anyhow::Result<()> {
|
||||
let (cookies, account_label) = match &args.cookies_env {
|
||||
Some(var) => (
|
||||
easyenv::get_env_string_required(var)
|
||||
.map_err(|err| anyhow!("Missing {} env var: {:?}", var, err))?,
|
||||
args.account.clone().unwrap_or_else(|| var.clone()),
|
||||
),
|
||||
None => (
|
||||
state.cookies.clone(),
|
||||
args.account.clone().unwrap_or_else(|| "SEEDANCE2PRO_COOKIES".to_string()),
|
||||
),
|
||||
};
|
||||
let session = KinoviWebSession::from_cookies_string(cookies);
|
||||
let account_label = args.account.as_deref().unwrap_or(&state.cookies_env);
|
||||
let session = KinoviWebSession::from_cookies_string(state.cookies.clone());
|
||||
|
||||
let file_is_new = std::fs::metadata(&args.out).map(|m| m.len() == 0).unwrap_or(true);
|
||||
let mut out = OpenOptions::new().create(true).append(true).open(&args.out)
|
||||
@@ -82,7 +68,7 @@ pub async fn run(state: &KinoviWebState, args: AuditPaymentsArgs) -> anyhow::Res
|
||||
}
|
||||
|
||||
for payment in &page.payments {
|
||||
write_payment_row(&mut out, &account_label, payment)?;
|
||||
write_payment_row(&mut out, account_label, payment)?;
|
||||
rows += 1;
|
||||
total_amount += payment.amount_usd;
|
||||
total_credits += payment.credits_earned;
|
||||
|
||||
@@ -6,7 +6,7 @@ use super::kinovi_web;
|
||||
/// All canonical subcommand names across all modules.
|
||||
/// Used by the underscore-insensitive arg normalizer.
|
||||
pub fn all_canonical_names() -> Vec<&'static str> {
|
||||
let mut names: Vec<&str> = vec!["kinovi_web", "artcraft"];
|
||||
let mut names: Vec<&str> = vec!["kinovi_web", "kinovi", "seedance2pro", "artcraft"];
|
||||
names.extend_from_slice(kinovi_web::dispatch::SUBCOMMAND_NAMES);
|
||||
names.extend_from_slice(artcraft::dispatch::SUBCOMMAND_NAMES);
|
||||
names
|
||||
@@ -23,10 +23,8 @@ pub struct Cli {
|
||||
#[command(rename_all = "snake_case")]
|
||||
pub enum TopLevelCommand {
|
||||
/// Seedance2 Pro support commands (direct Kinovi API)
|
||||
KinoviWeb {
|
||||
#[command(subcommand)]
|
||||
command: kinovi_web::KinoviWebCommand,
|
||||
},
|
||||
#[command(visible_aliases = ["kinovi", "seedance2pro"])]
|
||||
KinoviWeb(kinovi_web::KinoviWebArgs),
|
||||
|
||||
/// ArtCraft support commands (omni API)
|
||||
Artcraft(artcraft::dispatch::ArtcraftArgs),
|
||||
@@ -34,8 +32,8 @@ pub enum TopLevelCommand {
|
||||
|
||||
pub async fn run(cli: Cli) -> anyhow::Result<()> {
|
||||
match cli.command {
|
||||
TopLevelCommand::KinoviWeb { command } => {
|
||||
kinovi_web::run(command).await
|
||||
TopLevelCommand::KinoviWeb(args) => {
|
||||
kinovi_web::run(args).await
|
||||
}
|
||||
TopLevelCommand::Artcraft(args) => {
|
||||
artcraft::dispatch(args).await
|
||||
|
||||
Reference in new issue
Block a user