Skip to main content

lean_ctx/shell/
command_text.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Pure command/output text predicates shared by the shell executor, the
3//! compression engine and `ctx_shell`. Moved down from `tools/ctx_shell.rs`.
4
5/// On Windows cmd.exe, `;` is not a valid command separator.
6/// Convert `cmd1; cmd2` to `cmd1 && cmd2` when running under cmd.exe.
7pub fn normalize_command_for_shell(command: &str) -> String {
8    if !cfg!(windows) {
9        return command.to_string();
10    }
11    let (_, flag) = crate::shell::shell_and_flag();
12    if flag != "/C" {
13        return command.to_string();
14    }
15    let bytes = command.as_bytes();
16    let mut result = Vec::with_capacity(bytes.len() + 16);
17    let mut in_single = false;
18    let mut in_double = false;
19    for (i, &b) in bytes.iter().enumerate() {
20        if b == b'\'' && !in_double {
21            in_single = !in_single;
22        } else if b == b'"' && !in_single {
23            in_double = !in_double;
24        } else if b == b';' && !in_single && !in_double {
25            result.extend_from_slice(b" && ");
26            continue;
27        }
28        result.push(b);
29        let _ = i;
30    }
31    String::from_utf8(result).unwrap_or_else(|_| command.to_string())
32}
33
34/// Detects OAuth device code flow output that must not be compressed.
35/// Uses a two-tier approach: strong signals match alone (very specific to
36/// device code flows), weak signals require a URL/domain in the same output.
37pub fn contains_auth_flow(output: &str) -> bool {
38    let lower = output.to_lowercase();
39
40    const STRONG_SIGNALS: &[&str] = &[
41        "devicelogin",
42        "deviceauth",
43        "device_code",
44        "device code",
45        "device-code",
46        "verification_uri",
47        "user_code",
48        "one-time code",
49    ];
50
51    if STRONG_SIGNALS.iter().any(|s| lower.contains(s)) {
52        return true;
53    }
54
55    const WEAK_SIGNALS: &[&str] = &[
56        "enter the code",
57        "enter this code",
58        "enter code:",
59        "use the code",
60        "use a web browser to open",
61        "open the page",
62        "authenticate by visiting",
63        "sign in with the code",
64        "sign in using a code",
65        "verification code",
66        "authorize this device",
67        "waiting for authentication",
68        "waiting for login",
69        "waiting for you to authenticate",
70        "open your browser",
71        "open in your browser",
72    ];
73
74    let has_weak_signal = WEAK_SIGNALS.iter().any(|s| lower.contains(s));
75    if !has_weak_signal {
76        return false;
77    }
78
79    lower.contains("http://") || lower.contains("https://")
80}