mirror of
https://github.com/openai/codex.git
synced 2026-11-11 21:10:39 +00:00
feat(tui): rename token activity command to usage
This commit is contained in:
@@ -739,7 +739,7 @@ impl App {
|
||||
.finish_token_activity_refresh(request_id, result)
|
||||
{
|
||||
// Commit synchronously so an already queued /clear cannot overtake this card.
|
||||
// Do not route through ChatWidget::add_to_history: /tokens may complete during
|
||||
// Do not route through ChatWidget::add_to_history: /usage may complete during
|
||||
// active work, and flushing an in-progress tool cell would corrupt its lifecycle.
|
||||
// If an answer stream is active, keep the settled card transient until its
|
||||
// provisional transcript cells have been consolidated.
|
||||
|
||||
@@ -28,7 +28,7 @@ impl App {
|
||||
.history_wrap_width(tui.terminal.last_known_screen_size.width),
|
||||
);
|
||||
}
|
||||
// A committed cell can unblock a settled /tokens card that was waiting
|
||||
// A committed cell can unblock a settled /usage card that was waiting
|
||||
// behind a transient active cell or a provisional stream tail.
|
||||
self.chat_widget
|
||||
.request_completed_token_activity_output_insertion();
|
||||
|
||||
@@ -293,7 +293,7 @@ pub(crate) enum AppEvent {
|
||||
result: Result<Vec<RateLimitSnapshot>, String>,
|
||||
},
|
||||
|
||||
/// Fetch account-wide token activity for a `/tokens` history card.
|
||||
/// Fetch account-wide token activity for a `/usage` history card.
|
||||
RefreshTokenActivity {
|
||||
request_id: u64,
|
||||
},
|
||||
|
||||
@@ -76,7 +76,7 @@ pub(crate) fn builtins_for_input(flags: BuiltinCommandFlags) -> Vec<(&'static st
|
||||
.filter(|(_, cmd)| flags.collaboration_modes_enabled || *cmd != SlashCommand::Plan)
|
||||
.filter(|(_, cmd)| flags.connectors_enabled || *cmd != SlashCommand::Apps)
|
||||
.filter(|(_, cmd)| flags.plugins_command_enabled || *cmd != SlashCommand::Plugins)
|
||||
.filter(|(_, cmd)| flags.token_activity_command_enabled || *cmd != SlashCommand::Tokens)
|
||||
.filter(|(_, cmd)| flags.token_activity_command_enabled || *cmd != SlashCommand::Usage)
|
||||
.filter(|(_, cmd)| flags.goal_command_enabled || *cmd != SlashCommand::Goal)
|
||||
.filter(|(_, cmd)| flags.personality_command_enabled || *cmd != SlashCommand::Personality)
|
||||
.filter(|(_, cmd)| flags.realtime_conversation_enabled || *cmd != SlashCommand::Realtime)
|
||||
@@ -262,24 +262,24 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tokens_command_is_hidden_from_input_when_account_token_activity_is_disabled() {
|
||||
fn usage_command_is_hidden_from_input_when_account_token_activity_is_disabled() {
|
||||
let mut flags = all_enabled_flags();
|
||||
flags.token_activity_command_enabled = false;
|
||||
assert_eq!(
|
||||
builtins_for_input(flags)
|
||||
.into_iter()
|
||||
.find(|(_, command)| *command == SlashCommand::Tokens),
|
||||
.find(|(_, command)| *command == SlashCommand::Usage),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tokens_command_exact_lookup_still_resolves_when_account_token_activity_is_disabled() {
|
||||
fn usage_command_exact_lookup_still_resolves_when_account_token_activity_is_disabled() {
|
||||
let mut flags = all_enabled_flags();
|
||||
flags.token_activity_command_enabled = false;
|
||||
assert_eq!(
|
||||
find_builtin_command("tokens", flags),
|
||||
Some(SlashCommand::Tokens)
|
||||
find_builtin_command("usage", flags),
|
||||
Some(SlashCommand::Usage)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ mod tests {
|
||||
SlashCommand::Diff,
|
||||
SlashCommand::Mention,
|
||||
SlashCommand::Status,
|
||||
SlashCommand::Tokens,
|
||||
SlashCommand::Usage,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const SIDE_SLASH_COMMAND_UNAVAILABLE_HINT: &str =
|
||||
const GOAL_USAGE: &str = "Usage: /goal <objective>";
|
||||
const GOAL_USAGE_HINT: &str = "Example: /goal improve benchmark coverage";
|
||||
const RAW_USAGE: &str = "Usage: /raw [on|off]";
|
||||
const TOKENS_CHATGPT_LOGIN_REQUIRED: &str = "Sign in with ChatGPT to use /tokens.";
|
||||
const USAGE_CHATGPT_LOGIN_REQUIRED: &str = "Sign in with ChatGPT to use /usage.";
|
||||
|
||||
impl ChatWidget {
|
||||
/// Dispatch a bare slash command and record its staged local-history entry.
|
||||
@@ -415,7 +415,7 @@ impl ChatWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
SlashCommand::Tokens => {
|
||||
SlashCommand::Usage => {
|
||||
if self.ensure_token_activity_command_available() {
|
||||
self.add_token_activity_output(tokens::TokenActivityView::Daily);
|
||||
}
|
||||
@@ -620,12 +620,12 @@ impl ChatWidget {
|
||||
} = prepared;
|
||||
let trimmed = args.trim();
|
||||
match cmd {
|
||||
SlashCommand::Tokens => {
|
||||
SlashCommand::Usage => {
|
||||
if self.ensure_token_activity_command_available() {
|
||||
match tokens::TokenActivityView::parse(trimmed) {
|
||||
Some(view) => self.add_token_activity_output(view),
|
||||
None => self.add_error_message(
|
||||
"Usage: /tokens [daily|weekly|cumulative]".to_string(),
|
||||
"Usage: /usage [daily|weekly|cumulative]".to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -975,7 +975,7 @@ impl ChatWidget {
|
||||
if self.has_chatgpt_account {
|
||||
return true;
|
||||
}
|
||||
self.add_error_message(TOKENS_CHATGPT_LOGIN_REQUIRED.to_string());
|
||||
self.add_error_message(USAGE_CHATGPT_LOGIN_REQUIRED.to_string());
|
||||
false
|
||||
}
|
||||
|
||||
@@ -986,7 +986,7 @@ impl ChatWidget {
|
||||
match cmd {
|
||||
SlashCommand::Ide
|
||||
| SlashCommand::Status
|
||||
| SlashCommand::Tokens
|
||||
| SlashCommand::Usage
|
||||
| SlashCommand::DebugConfig
|
||||
| SlashCommand::Ps
|
||||
| SlashCommand::Stop
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ source: tui/src/chatwidget/tests/slash_commands.rs
|
||||
expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
---
|
||||
|
||||
/tokens daily
|
||||
/usage daily
|
||||
|
||||
Token activity
|
||||
Loading...
|
||||
|
||||
@@ -1137,10 +1137,10 @@ async fn usage_error_slash_command_is_available_from_local_recall() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signed_out_tokens_command_reports_chatgpt_login_requirement() {
|
||||
async fn signed_out_usage_command_reports_chatgpt_login_requirement() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
|
||||
submit_composer_text(&mut chat, "/tokens");
|
||||
submit_composer_text(&mut chat, "/usage");
|
||||
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
let rendered = cells
|
||||
@@ -1149,17 +1149,17 @@ async fn signed_out_tokens_command_reports_chatgpt_login_requirement() {
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(
|
||||
rendered.contains("Sign in with ChatGPT to use /tokens."),
|
||||
rendered.contains("Sign in with ChatGPT to use /usage."),
|
||||
"expected ChatGPT login requirement, got: {rendered:?}"
|
||||
);
|
||||
assert_eq!(recall_latest_after_clearing(&mut chat), "/tokens");
|
||||
assert_eq!(recall_latest_after_clearing(&mut chat), "/usage");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signed_out_tokens_command_with_args_reports_chatgpt_login_requirement() {
|
||||
async fn signed_out_usage_command_with_args_reports_chatgpt_login_requirement() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
|
||||
submit_composer_text(&mut chat, "/tokens weekly");
|
||||
submit_composer_text(&mut chat, "/usage weekly");
|
||||
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
let rendered = cells
|
||||
@@ -1168,10 +1168,10 @@ async fn signed_out_tokens_command_with_args_reports_chatgpt_login_requirement()
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(
|
||||
rendered.contains("Sign in with ChatGPT to use /tokens."),
|
||||
rendered.contains("Sign in with ChatGPT to use /usage."),
|
||||
"expected ChatGPT login requirement, got: {rendered:?}"
|
||||
);
|
||||
assert_eq!(recall_latest_after_clearing(&mut chat), "/tokens weekly");
|
||||
assert_eq!(recall_latest_after_clearing(&mut chat), "/usage weekly");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1179,7 +1179,7 @@ async fn clearing_pending_token_activity_refreshes_discards_late_result() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
|
||||
let request_id = match rx.try_recv() {
|
||||
Ok(AppEvent::RefreshTokenActivity { request_id }) => request_id,
|
||||
@@ -1188,12 +1188,12 @@ async fn clearing_pending_token_activity_refreshes_discards_late_result() {
|
||||
assert_eq!(
|
||||
chat.pending_token_activity_output()
|
||||
.map(|cell| lines_to_single_string(&cell.display_lines(u16::MAX))),
|
||||
Some("/tokens daily\n\n Token activity\n Loading...\n".to_string()),
|
||||
Some("/usage daily\n\n Token activity\n Loading...\n".to_string()),
|
||||
);
|
||||
assert_eq!(
|
||||
chat.active_cell_transcript_lines(u16::MAX)
|
||||
.map(|lines| lines_to_single_string(&lines)),
|
||||
Some("/tokens daily\n\n Token activity\n Loading...\n".to_string()),
|
||||
Some("/usage daily\n\n Token activity\n Loading...\n".to_string()),
|
||||
);
|
||||
|
||||
chat.clear_pending_token_activity_refreshes();
|
||||
@@ -1211,7 +1211,7 @@ async fn pending_token_activity_refresh_renders_above_composer_snapshot() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
assert_matches!(
|
||||
rx.try_recv(),
|
||||
Ok(AppEvent::RefreshTokenActivity { .. }),
|
||||
@@ -1238,7 +1238,7 @@ async fn completed_token_activity_refresh_returns_one_history_cell() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
|
||||
let request_id = match rx.try_recv() {
|
||||
Ok(AppEvent::RefreshTokenActivity { request_id }) => request_id,
|
||||
@@ -1260,7 +1260,7 @@ async fn completed_token_activity_refresh_returns_one_history_cell() {
|
||||
assert!(chat.pending_token_activity_output().is_none());
|
||||
assert_eq!(
|
||||
lines_to_single_string(&cell.display_lines(u16::MAX)),
|
||||
"/tokens daily\n\n Token activity\n Token activity unavailable\n",
|
||||
"/usage daily\n\n Token activity\n Token activity unavailable\n",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1269,7 +1269,7 @@ async fn completed_token_activity_refresh_waits_for_active_stream() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
let request_id = match rx.try_recv() {
|
||||
Ok(AppEvent::RefreshTokenActivity { request_id }) => request_id,
|
||||
other => panic!("expected token activity refresh request, got {other:?}"),
|
||||
@@ -1286,7 +1286,7 @@ async fn completed_token_activity_refresh_waits_for_active_stream() {
|
||||
assert_eq!(
|
||||
chat.pending_token_activity_output()
|
||||
.map(|cell| lines_to_single_string(&cell.display_lines(u16::MAX))),
|
||||
Some("/tokens daily\n\n Token activity\n Token activity unavailable\n".to_string()),
|
||||
Some("/usage daily\n\n Token activity\n Token activity unavailable\n".to_string()),
|
||||
);
|
||||
|
||||
chat.finalize_turn();
|
||||
@@ -1303,7 +1303,7 @@ async fn completed_token_activity_refresh_waits_for_queued_stream_consolidation(
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
let request_id = match rx.try_recv() {
|
||||
Ok(AppEvent::RefreshTokenActivity { request_id }) => request_id,
|
||||
other => panic!("expected token activity refresh request, got {other:?}"),
|
||||
@@ -1330,7 +1330,7 @@ async fn completed_token_activity_refresh_waits_for_active_history_cell() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
let request_id = match rx.try_recv() {
|
||||
Ok(AppEvent::RefreshTokenActivity { request_id }) => request_id,
|
||||
other => panic!("expected token activity refresh request, got {other:?}"),
|
||||
@@ -1360,7 +1360,7 @@ async fn completed_token_activity_refresh_retries_after_plan_item_completion() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
let request_id = match rx.try_recv() {
|
||||
Ok(AppEvent::RefreshTokenActivity { request_id }) => request_id,
|
||||
other => panic!("expected token activity refresh request, got {other:?}"),
|
||||
@@ -1395,7 +1395,7 @@ async fn pending_token_activity_refresh_keeps_composer_visible_in_short_viewport
|
||||
std::iter::repeat_n(Line::from("active output"), /*n*/ 20).collect(),
|
||||
)));
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
assert_matches!(
|
||||
rx.try_recv(),
|
||||
Ok(AppEvent::RefreshTokenActivity { .. }),
|
||||
@@ -1426,13 +1426,13 @@ async fn repeated_token_activity_refreshes_keep_only_latest_card() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.dispatch_command(SlashCommand::Tokens);
|
||||
chat.dispatch_command(SlashCommand::Usage);
|
||||
let first_request_id = match rx.try_recv() {
|
||||
Ok(AppEvent::RefreshTokenActivity { request_id }) => request_id,
|
||||
other => panic!("expected first token activity refresh request, got {other:?}"),
|
||||
};
|
||||
|
||||
chat.dispatch_command_with_args(SlashCommand::Tokens, "weekly".to_string(), Vec::new());
|
||||
chat.dispatch_command_with_args(SlashCommand::Usage, "weekly".to_string(), Vec::new());
|
||||
let second_request_id = match rx.try_recv() {
|
||||
Ok(AppEvent::RefreshTokenActivity { request_id }) => request_id,
|
||||
other => panic!("expected second token activity refresh request, got {other:?}"),
|
||||
@@ -1441,7 +1441,7 @@ async fn repeated_token_activity_refreshes_keep_only_latest_card() {
|
||||
assert_eq!(
|
||||
chat.pending_token_activity_output()
|
||||
.map(|cell| lines_to_single_string(&cell.display_lines(u16::MAX))),
|
||||
Some("/tokens weekly\n\n Token activity\n Loading...\n".to_string()),
|
||||
Some("/usage weekly\n\n Token activity\n Loading...\n".to_string()),
|
||||
);
|
||||
assert!(!chat.finish_token_activity_refresh(
|
||||
first_request_id,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Renders account token activity and coordinates asynchronous `/tokens` cards.
|
||||
//! Renders account token activity and coordinates asynchronous `/usage` cards.
|
||||
//!
|
||||
//! The slash command builds a composite history cell immediately, but the widget
|
||||
//! keeps that cell transient while the account request runs. The transient card is
|
||||
@@ -72,9 +72,9 @@ pub(super) enum TokenActivityView {
|
||||
}
|
||||
|
||||
impl TokenActivityView {
|
||||
/// Parses the optional `/tokens` argument into a supported chart view.
|
||||
/// Parses the optional `/usage` argument into a supported chart view.
|
||||
///
|
||||
/// An empty argument selects the daily view so `/tokens` and `/tokens daily`
|
||||
/// An empty argument selects the daily view so `/usage` and `/usage daily`
|
||||
/// behave identically. Returning `None` lets the slash-command dispatcher
|
||||
/// report unsupported arguments instead of silently choosing a view.
|
||||
pub(super) fn parse(value: &str) -> Option<Self> {
|
||||
@@ -115,7 +115,7 @@ pub(super) struct TokenActivityHandle {
|
||||
|
||||
/// Holds the one transient token activity card waiting on its background response.
|
||||
///
|
||||
/// The request ID prevents late results from mutating a newer `/tokens` card. The
|
||||
/// The request ID prevents late results from mutating a newer `/usage` card. The
|
||||
/// cell stays out of transcript history until the matching response completes and
|
||||
/// the widget confirms that active output no longer blocks insertion.
|
||||
pub(super) struct PendingTokenActivityOutput {
|
||||
@@ -141,14 +141,14 @@ impl TokenActivityHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders one `/tokens` card from shared asynchronous state.
|
||||
/// Renders one `/usage` card from shared asynchronous state.
|
||||
#[derive(Debug)]
|
||||
struct TokenActivityHistoryCell {
|
||||
view: TokenActivityView,
|
||||
state: Arc<RwLock<TokenActivityState>>,
|
||||
}
|
||||
|
||||
/// Creates the card contents and completion handle for one `/tokens` invocation.
|
||||
/// Creates the card contents and completion handle for one `/usage` invocation.
|
||||
///
|
||||
/// The composite cell includes the echoed slash command and a loading card from
|
||||
/// the start. Callers must retain the returned handle and complete it when the
|
||||
@@ -157,7 +157,7 @@ pub(super) fn new_token_activity_output(
|
||||
view: TokenActivityView,
|
||||
) -> (CompositeHistoryCell, TokenActivityHandle) {
|
||||
let command = PlainHistoryCell::new(vec![
|
||||
format!("/tokens {}", view.label().to_lowercase())
|
||||
format!("/usage {}", view.label().to_lowercase())
|
||||
.magenta()
|
||||
.into(),
|
||||
]);
|
||||
@@ -464,7 +464,7 @@ fn bar_caption(view: TokenActivityView, values: &[i64]) -> Line<'static> {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Dim footer that surfaces the other `/tokens` views and emphasizes the
|
||||
/// Dim footer that surfaces the other `/usage` views and emphasizes the
|
||||
/// active one, so the weekly/cumulative modes are discoverable from the card.
|
||||
fn view_footer(active: TokenActivityView) -> Line<'static> {
|
||||
let mut spans = vec![Span::styled(" ", label_style())];
|
||||
@@ -743,7 +743,7 @@ impl ChatWidget {
|
||||
///
|
||||
/// Returns `true` when the pending request matched and moved into the completed
|
||||
/// slot. Late responses return `false`, including responses for cards replaced
|
||||
/// by a newer `/tokens` invocation or cleared during transcript changes.
|
||||
/// by a newer `/usage` invocation or cleared during transcript changes.
|
||||
pub(crate) fn finish_token_activity_refresh(
|
||||
&mut self,
|
||||
request_id: u64,
|
||||
|
||||
@@ -45,7 +45,7 @@ pub enum SlashCommand {
|
||||
Diff,
|
||||
Mention,
|
||||
Status,
|
||||
Tokens,
|
||||
Usage,
|
||||
DebugConfig,
|
||||
Title,
|
||||
Statusline,
|
||||
@@ -99,7 +99,7 @@ impl SlashCommand {
|
||||
SlashCommand::Skills => "use skills to improve how Codex performs specific tasks",
|
||||
SlashCommand::Hooks => "view and manage lifecycle hooks",
|
||||
SlashCommand::Status => "show current session configuration and token usage",
|
||||
SlashCommand::Tokens => "show account token activity",
|
||||
SlashCommand::Usage => "show account usage activity",
|
||||
SlashCommand::DebugConfig => "show config layers and requirement sources for debugging",
|
||||
SlashCommand::Title => "configure which items appear in the terminal title",
|
||||
SlashCommand::Statusline => "configure which items appear in the status line",
|
||||
@@ -159,7 +159,7 @@ impl SlashCommand {
|
||||
| SlashCommand::Keymap
|
||||
| SlashCommand::Mcp
|
||||
| SlashCommand::Raw
|
||||
| SlashCommand::Tokens
|
||||
| SlashCommand::Usage
|
||||
| SlashCommand::Pets
|
||||
| SlashCommand::Side
|
||||
| SlashCommand::Btw
|
||||
@@ -177,7 +177,7 @@ impl SlashCommand {
|
||||
| SlashCommand::Diff
|
||||
| SlashCommand::Mention
|
||||
| SlashCommand::Status
|
||||
| SlashCommand::Tokens
|
||||
| SlashCommand::Usage
|
||||
| SlashCommand::Ide
|
||||
)
|
||||
}
|
||||
@@ -214,7 +214,7 @@ impl SlashCommand {
|
||||
| SlashCommand::Skills
|
||||
| SlashCommand::Hooks
|
||||
| SlashCommand::Status
|
||||
| SlashCommand::Tokens
|
||||
| SlashCommand::Usage
|
||||
| SlashCommand::DebugConfig
|
||||
| SlashCommand::Ps
|
||||
| SlashCommand::Stop
|
||||
|
||||
Reference in New Issue
Block a user