ripperdoc 0.2.9__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ripperdoc/__init__.py +1 -1
- ripperdoc/cli/cli.py +379 -51
- ripperdoc/cli/commands/__init__.py +6 -0
- ripperdoc/cli/commands/agents_cmd.py +128 -5
- ripperdoc/cli/commands/clear_cmd.py +8 -0
- ripperdoc/cli/commands/doctor_cmd.py +29 -0
- ripperdoc/cli/commands/exit_cmd.py +1 -0
- ripperdoc/cli/commands/memory_cmd.py +2 -1
- ripperdoc/cli/commands/models_cmd.py +63 -7
- ripperdoc/cli/commands/resume_cmd.py +5 -0
- ripperdoc/cli/commands/skills_cmd.py +103 -0
- ripperdoc/cli/commands/stats_cmd.py +244 -0
- ripperdoc/cli/commands/status_cmd.py +10 -0
- ripperdoc/cli/commands/tasks_cmd.py +6 -3
- ripperdoc/cli/commands/themes_cmd.py +139 -0
- ripperdoc/cli/ui/file_mention_completer.py +63 -13
- ripperdoc/cli/ui/helpers.py +6 -3
- ripperdoc/cli/ui/interrupt_handler.py +34 -0
- ripperdoc/cli/ui/panels.py +14 -8
- ripperdoc/cli/ui/rich_ui.py +737 -47
- ripperdoc/cli/ui/spinner.py +93 -18
- ripperdoc/cli/ui/thinking_spinner.py +1 -2
- ripperdoc/cli/ui/tool_renderers.py +10 -9
- ripperdoc/cli/ui/wizard.py +24 -19
- ripperdoc/core/agents.py +14 -3
- ripperdoc/core/config.py +238 -6
- ripperdoc/core/default_tools.py +91 -10
- ripperdoc/core/hooks/events.py +4 -0
- ripperdoc/core/hooks/llm_callback.py +58 -0
- ripperdoc/core/hooks/manager.py +6 -0
- ripperdoc/core/permissions.py +160 -9
- ripperdoc/core/providers/openai.py +84 -28
- ripperdoc/core/query.py +489 -87
- ripperdoc/core/query_utils.py +17 -14
- ripperdoc/core/skills.py +1 -0
- ripperdoc/core/theme.py +298 -0
- ripperdoc/core/tool.py +15 -5
- ripperdoc/protocol/__init__.py +14 -0
- ripperdoc/protocol/models.py +300 -0
- ripperdoc/protocol/stdio.py +1453 -0
- ripperdoc/tools/background_shell.py +354 -139
- ripperdoc/tools/bash_tool.py +117 -22
- ripperdoc/tools/file_edit_tool.py +228 -50
- ripperdoc/tools/file_read_tool.py +154 -3
- ripperdoc/tools/file_write_tool.py +53 -11
- ripperdoc/tools/grep_tool.py +98 -8
- ripperdoc/tools/lsp_tool.py +609 -0
- ripperdoc/tools/multi_edit_tool.py +26 -3
- ripperdoc/tools/skill_tool.py +52 -1
- ripperdoc/tools/task_tool.py +539 -65
- ripperdoc/utils/conversation_compaction.py +1 -1
- ripperdoc/utils/file_watch.py +216 -7
- ripperdoc/utils/image_utils.py +125 -0
- ripperdoc/utils/log.py +30 -3
- ripperdoc/utils/lsp.py +812 -0
- ripperdoc/utils/mcp.py +80 -18
- ripperdoc/utils/message_formatting.py +7 -4
- ripperdoc/utils/messages.py +198 -33
- ripperdoc/utils/pending_messages.py +50 -0
- ripperdoc/utils/permissions/shell_command_validation.py +3 -3
- ripperdoc/utils/permissions/tool_permission_utils.py +180 -15
- ripperdoc/utils/platform.py +198 -0
- ripperdoc/utils/session_heatmap.py +242 -0
- ripperdoc/utils/session_history.py +2 -2
- ripperdoc/utils/session_stats.py +294 -0
- ripperdoc/utils/shell_utils.py +8 -5
- ripperdoc/utils/todo.py +0 -6
- {ripperdoc-0.2.9.dist-info → ripperdoc-0.3.0.dist-info}/METADATA +55 -17
- ripperdoc-0.3.0.dist-info/RECORD +136 -0
- {ripperdoc-0.2.9.dist-info → ripperdoc-0.3.0.dist-info}/WHEEL +1 -1
- ripperdoc/sdk/__init__.py +0 -9
- ripperdoc/sdk/client.py +0 -333
- ripperdoc-0.2.9.dist-info/RECORD +0 -123
- {ripperdoc-0.2.9.dist-info → ripperdoc-0.3.0.dist-info}/entry_points.txt +0 -0
- {ripperdoc-0.2.9.dist-info → ripperdoc-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {ripperdoc-0.2.9.dist-info → ripperdoc-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import fnmatch
|
|
5
6
|
from dataclasses import dataclass
|
|
6
7
|
from typing import Callable, Iterable, List, Optional, Set
|
|
7
8
|
|
|
@@ -27,8 +28,25 @@ class PermissionDecision:
|
|
|
27
28
|
rule_suggestions: Optional[List[ToolRule] | List[str]] = None
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
def create_wildcard_rule(rule_name: str) -> str:
|
|
31
|
-
"""Create a wildcard/prefix rule string.
|
|
31
|
+
def create_wildcard_rule(rule_name: str, use_glob_style: bool = False) -> str:
|
|
32
|
+
"""Create a wildcard/prefix rule string.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
rule_name: The command prefix (e.g., "git", "npm")
|
|
36
|
+
use_glob_style: If True, creates "rule_name *" format;
|
|
37
|
+
if False, creates "rule_name:*" format (default for compatibility)
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Wildcard rule string in requested format
|
|
41
|
+
|
|
42
|
+
Examples:
|
|
43
|
+
>>> create_wildcard_rule("git")
|
|
44
|
+
"git:*"
|
|
45
|
+
>>> create_wildcard_rule("git", use_glob_style=True)
|
|
46
|
+
"git *"
|
|
47
|
+
"""
|
|
48
|
+
if use_glob_style:
|
|
49
|
+
return f"{rule_name} *"
|
|
32
50
|
return f"{rule_name}:*"
|
|
33
51
|
|
|
34
52
|
|
|
@@ -36,22 +54,124 @@ def create_tool_rule(rule_content: str) -> List[ToolRule]:
|
|
|
36
54
|
return [ToolRule(tool_name="Bash", rule_content=rule_content)]
|
|
37
55
|
|
|
38
56
|
|
|
39
|
-
def create_wildcard_tool_rule(rule_name: str) -> List[ToolRule]:
|
|
40
|
-
|
|
57
|
+
def create_wildcard_tool_rule(rule_name: str, use_glob_style: bool = False) -> List[ToolRule]:
|
|
58
|
+
"""Create a wildcard tool rule.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
rule_name: The command prefix
|
|
62
|
+
use_glob_style: Whether to use glob-style format
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List containing a single ToolRule with wildcard pattern
|
|
66
|
+
"""
|
|
67
|
+
return [
|
|
68
|
+
ToolRule(tool_name="Bash", rule_content=create_wildcard_rule(rule_name, use_glob_style))
|
|
69
|
+
]
|
|
41
70
|
|
|
42
71
|
|
|
43
72
|
def extract_rule_prefix(rule_string: str) -> Optional[str]:
|
|
44
73
|
return rule_string[:-2] if rule_string.endswith(":*") else None
|
|
45
74
|
|
|
46
75
|
|
|
76
|
+
def _has_unquoted_shell_operators(command: str) -> bool:
|
|
77
|
+
"""Check if command contains shell operators outside of quotes.
|
|
78
|
+
|
|
79
|
+
This prevents wildcard rules from matching commands with shell operators
|
|
80
|
+
like &&, ||, ;, | which could be used to chain dangerous commands.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
command: The command to check
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
True if command contains unquoted shell operators, False otherwise
|
|
87
|
+
|
|
88
|
+
Examples:
|
|
89
|
+
>>> _has_unquoted_shell_operators("git status && rm -rf /")
|
|
90
|
+
True
|
|
91
|
+
>>> _has_unquoted_shell_operators("echo 'foo && bar'")
|
|
92
|
+
False
|
|
93
|
+
>>> _has_unquoted_shell_operators("ls | grep foo")
|
|
94
|
+
True
|
|
95
|
+
>>> _has_unquoted_shell_operators("echo hi; rm -rf /")
|
|
96
|
+
True
|
|
97
|
+
"""
|
|
98
|
+
# Parse the command into tokens, which handles quotes correctly
|
|
99
|
+
tokens = parse_shell_tokens(command)
|
|
100
|
+
|
|
101
|
+
# Check for shell operators in the tokens
|
|
102
|
+
# Note: parse_shell_tokens may attach semicolon to adjacent tokens
|
|
103
|
+
# (e.g., "echo hi; rm" -> ["echo", "hi;", "rm"] or ";echo" -> [";echo"])
|
|
104
|
+
for token in tokens:
|
|
105
|
+
# Check for separate operator tokens
|
|
106
|
+
if token in {"&&", "||", "|"}:
|
|
107
|
+
return True
|
|
108
|
+
# Check for semicolon (may be attached to previous or next token)
|
|
109
|
+
if token.endswith(";") or token.startswith(";"):
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
|
|
47
115
|
def match_rule(command: str, rule: str) -> bool:
|
|
48
|
-
"""Return True if a command matches a rule (exact or
|
|
116
|
+
"""Return True if a command matches a rule (exact, prefix wildcard, or glob pattern).
|
|
117
|
+
|
|
118
|
+
Supports three formats:
|
|
119
|
+
- Exact match: "git status" matches "git status" only
|
|
120
|
+
- Legacy prefix: "git:*" matches any command starting with "git"
|
|
121
|
+
- Glob patterns: "git * main" matches "git push main", "git pull main", etc.
|
|
122
|
+
|
|
123
|
+
Glob patterns support:
|
|
124
|
+
- * (matches any characters)
|
|
125
|
+
- ? (matches single character)
|
|
126
|
+
- [seq] (matches any character in seq)
|
|
127
|
+
- [!seq] (matches any character NOT in seq)
|
|
128
|
+
|
|
129
|
+
Security: Wildcard rules (both legacy and glob) will NOT match commands
|
|
130
|
+
containing shell operators (&&, ||, ;, |) outside of quotes. This prevents
|
|
131
|
+
a rule like "git:*" from matching "git status && rm -rf /". Exact match
|
|
132
|
+
rules still work with operators (user explicitly approved the full command).
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
command: The command to check
|
|
136
|
+
rule: The rule pattern to match against
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
True if command matches rule, False otherwise
|
|
140
|
+
|
|
141
|
+
Examples:
|
|
142
|
+
>>> match_rule("git status", "git:*")
|
|
143
|
+
True
|
|
144
|
+
>>> match_rule("npm install", "npm *")
|
|
145
|
+
True
|
|
146
|
+
>>> match_rule("pip install", "* install")
|
|
147
|
+
True
|
|
148
|
+
>>> match_rule("git push main", "git * main")
|
|
149
|
+
True
|
|
150
|
+
>>> match_rule("git status && rm -rf /", "git:*")
|
|
151
|
+
False
|
|
152
|
+
>>> match_rule("git status && git commit", "git status && git commit")
|
|
153
|
+
True
|
|
154
|
+
"""
|
|
49
155
|
command = command.strip()
|
|
50
156
|
if not command:
|
|
51
157
|
return False
|
|
158
|
+
|
|
159
|
+
# Legacy format: prefix:* (highest priority for backward compatibility)
|
|
52
160
|
prefix = extract_rule_prefix(rule)
|
|
53
161
|
if prefix is not None:
|
|
162
|
+
# For wildcard rules, reject commands with shell operators for security
|
|
163
|
+
if _has_unquoted_shell_operators(command):
|
|
164
|
+
return False
|
|
54
165
|
return command.startswith(prefix)
|
|
166
|
+
|
|
167
|
+
# New format: glob-style patterns with wildcards
|
|
168
|
+
if "*" in rule or "?" in rule or "[" in rule:
|
|
169
|
+
# For wildcard rules, reject commands with shell operators for security
|
|
170
|
+
if _has_unquoted_shell_operators(command):
|
|
171
|
+
return False
|
|
172
|
+
return fnmatch.fnmatch(command, rule)
|
|
173
|
+
|
|
174
|
+
# Exact match - allow even with operators (user explicitly approved full command)
|
|
55
175
|
return command == rule
|
|
56
176
|
|
|
57
177
|
|
|
@@ -131,10 +251,41 @@ def _is_command_read_only(
|
|
|
131
251
|
|
|
132
252
|
|
|
133
253
|
def _collect_rule_suggestions(command: str) -> List[ToolRule]:
|
|
134
|
-
suggestions
|
|
254
|
+
"""Collect rule suggestions for a command.
|
|
255
|
+
|
|
256
|
+
Suggests three options:
|
|
257
|
+
1. Exact command match
|
|
258
|
+
2. Legacy prefix wildcard (git:*)
|
|
259
|
+
3. Glob-style wildcard (git *)
|
|
260
|
+
|
|
261
|
+
This gives users choice between formats while maintaining backward compatibility.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
command: The command to generate suggestions for
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
List of suggested ToolRule objects
|
|
268
|
+
"""
|
|
269
|
+
suggestions: list[ToolRule] = [
|
|
270
|
+
# Exact match
|
|
271
|
+
ToolRule(tool_name="Bash", rule_content=command)
|
|
272
|
+
]
|
|
273
|
+
|
|
135
274
|
tokens = parse_and_clean_shell_tokens(command)
|
|
136
275
|
if tokens:
|
|
137
|
-
|
|
276
|
+
# Legacy prefix format
|
|
277
|
+
suggestions.append(
|
|
278
|
+
ToolRule(
|
|
279
|
+
tool_name="Bash", rule_content=create_wildcard_rule(tokens[0], use_glob_style=False)
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
# New glob-style format
|
|
283
|
+
suggestions.append(
|
|
284
|
+
ToolRule(
|
|
285
|
+
tool_name="Bash", rule_content=create_wildcard_rule(tokens[0], use_glob_style=True)
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
|
|
138
289
|
return suggestions
|
|
139
290
|
|
|
140
291
|
|
|
@@ -144,19 +295,34 @@ def evaluate_shell_command_permissions(
|
|
|
144
295
|
denied_rules: Iterable[str],
|
|
145
296
|
allowed_working_dirs: Set[str] | None = None,
|
|
146
297
|
*,
|
|
147
|
-
|
|
148
|
-
injection_detector: Callable[[str], bool] | None = None,
|
|
298
|
+
danger_detector: Callable[[str], bool] | None = None,
|
|
149
299
|
read_only_detector: Callable[[str, Callable[[str], bool]], bool] | None = None,
|
|
150
300
|
) -> PermissionDecision:
|
|
151
|
-
"""Evaluate whether a bash command should be allowed.
|
|
301
|
+
"""Evaluate whether a bash command should be allowed.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
tool_request: The tool request containing the command.
|
|
305
|
+
allowed_rules: Rules that allow commands.
|
|
306
|
+
denied_rules: Rules that deny commands.
|
|
307
|
+
allowed_working_dirs: Allowed working directories.
|
|
308
|
+
danger_detector: Callable that returns True if command contains dangerous
|
|
309
|
+
shell patterns (injection, destructive commands, etc.).
|
|
310
|
+
read_only_detector: Callable that returns True if command is read-only.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
PermissionDecision indicating whether the command should be allowed.
|
|
314
|
+
"""
|
|
152
315
|
command = tool_request.command if hasattr(tool_request, "command") else str(tool_request)
|
|
153
316
|
trimmed_command = command.strip()
|
|
154
317
|
allowed_working_dirs = allowed_working_dirs or {safe_get_cwd()}
|
|
155
|
-
|
|
318
|
+
danger_detector = danger_detector or (
|
|
156
319
|
lambda cmd: validate_shell_command(cmd).behavior != "passthrough"
|
|
157
320
|
)
|
|
158
321
|
read_only_detector = read_only_detector or _is_command_read_only
|
|
159
322
|
|
|
323
|
+
# Detect dangerous patterns once, reuse the result
|
|
324
|
+
has_dangerous_patterns = danger_detector(trimmed_command)
|
|
325
|
+
|
|
160
326
|
merged_denied = _merge_rules(denied_rules)
|
|
161
327
|
merged_allowed = _merge_rules(allowed_rules)
|
|
162
328
|
|
|
@@ -217,11 +383,10 @@ def evaluate_shell_command_permissions(
|
|
|
217
383
|
merged_allowed,
|
|
218
384
|
merged_denied,
|
|
219
385
|
allowed_working_dirs,
|
|
220
|
-
|
|
221
|
-
injection_detector=injection_detector,
|
|
386
|
+
danger_detector=danger_detector,
|
|
222
387
|
read_only_detector=read_only_detector,
|
|
223
388
|
)
|
|
224
|
-
right_read_only = read_only_detector(right_command,
|
|
389
|
+
right_read_only = read_only_detector(right_command, danger_detector)
|
|
225
390
|
|
|
226
391
|
if left_result.behavior == "deny":
|
|
227
392
|
return left_result
|
|
@@ -245,7 +410,7 @@ def evaluate_shell_command_permissions(
|
|
|
245
410
|
rule_suggestions=_collect_rule_suggestions(trimmed_command),
|
|
246
411
|
)
|
|
247
412
|
|
|
248
|
-
if read_only_detector(trimmed_command,
|
|
413
|
+
if read_only_detector(trimmed_command, danger_detector) and not has_dangerous_patterns:
|
|
249
414
|
return PermissionDecision(
|
|
250
415
|
behavior="allow",
|
|
251
416
|
updated_input=tool_request,
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Platform detection utilities.
|
|
2
|
+
|
|
3
|
+
This module provides a unified interface for detecting the current operating system
|
|
4
|
+
and platform-specific capabilities. It should be used instead of direct checks
|
|
5
|
+
like `sys.platform == "win32"` or `os.name == "nt"`.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from ripperdoc.utils.platform import (
|
|
9
|
+
is_windows,
|
|
10
|
+
is_linux,
|
|
11
|
+
is_macos,
|
|
12
|
+
is_unix,
|
|
13
|
+
Platform,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if is_windows():
|
|
17
|
+
# Windows-specific code
|
|
18
|
+
elif is_macos():
|
|
19
|
+
# macOS-specific code
|
|
20
|
+
else:
|
|
21
|
+
# Linux or other Unix-specific code
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import os
|
|
25
|
+
import sys
|
|
26
|
+
from typing import Final, Literal
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Platform type definitions
|
|
30
|
+
PlatformType = Literal["windows", "linux", "macos", "unknown"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Platform:
|
|
34
|
+
"""Platform detection constants and utilities.
|
|
35
|
+
|
|
36
|
+
This class provides platform detection methods and constants that should
|
|
37
|
+
be used throughout the codebase instead of direct checks.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Platform constants (using sys.platform for consistency)
|
|
41
|
+
WINDOWS: Final = "win32"
|
|
42
|
+
LINUX: Final = "linux"
|
|
43
|
+
MACOS: Final = "darwin"
|
|
44
|
+
FREEBSD: Final = "freebsd"
|
|
45
|
+
OPENBSD: Final = "openbsd"
|
|
46
|
+
NETBSD: Final = "netbsd"
|
|
47
|
+
|
|
48
|
+
# os.name constants
|
|
49
|
+
NAME_NT: Final = "nt" # Windows
|
|
50
|
+
NAME_POSIX: Final = "posix" # Unix-like systems
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def get_system() -> PlatformType:
|
|
54
|
+
"""Get the current operating system name.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
'windows', 'linux', 'macos', or 'unknown'
|
|
58
|
+
"""
|
|
59
|
+
platform = sys.platform.lower()
|
|
60
|
+
|
|
61
|
+
if platform.startswith("win"):
|
|
62
|
+
return "windows"
|
|
63
|
+
elif platform.startswith("darwin"):
|
|
64
|
+
return "macos"
|
|
65
|
+
elif platform.startswith("linux"):
|
|
66
|
+
return "linux"
|
|
67
|
+
elif platform in {"freebsd", "openbsd", "netbsd"}:
|
|
68
|
+
return "linux" # Treat BSD as Linux for most purposes
|
|
69
|
+
else:
|
|
70
|
+
return "unknown"
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def is_windows() -> bool:
|
|
74
|
+
"""Check if running on Windows."""
|
|
75
|
+
return sys.platform == Platform.WINDOWS
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def is_linux() -> bool:
|
|
79
|
+
"""Check if running on Linux."""
|
|
80
|
+
return sys.platform.startswith("linux")
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def is_macos() -> bool:
|
|
84
|
+
"""Check if running on macOS."""
|
|
85
|
+
return sys.platform == Platform.MACOS
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def is_bsd() -> bool:
|
|
89
|
+
"""Check if running on any BSD variant."""
|
|
90
|
+
return sys.platform in {Platform.FREEBSD, Platform.OPENBSD, Platform.NETBSD}
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def is_unix() -> bool:
|
|
94
|
+
"""Check if running on any Unix-like system (Linux, macOS, BSD)."""
|
|
95
|
+
return os.name == Platform.NAME_POSIX
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def is_posix() -> bool:
|
|
99
|
+
"""Check if running on a POSIX-compliant system.
|
|
100
|
+
|
|
101
|
+
This is equivalent to is_unix() but uses os.name for the check.
|
|
102
|
+
"""
|
|
103
|
+
return os.name == Platform.NAME_POSIX
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def get_raw_name() -> str:
|
|
107
|
+
"""Get the raw sys.platform value.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
The raw sys.platform string (e.g., 'win32', 'linux', 'darwin').
|
|
111
|
+
"""
|
|
112
|
+
return sys.platform
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def get_os_name() -> str:
|
|
116
|
+
"""Get the os.name value.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
'nt' for Windows, 'posix' for Unix-like systems.
|
|
120
|
+
"""
|
|
121
|
+
return os.name
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# Convenience functions for direct import
|
|
125
|
+
def is_windows() -> bool:
|
|
126
|
+
"""Check if running on Windows."""
|
|
127
|
+
return Platform.is_windows()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def is_linux() -> bool:
|
|
131
|
+
"""Check if running on Linux."""
|
|
132
|
+
return Platform.is_linux()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def is_macos() -> bool:
|
|
136
|
+
"""Check if running on macOS."""
|
|
137
|
+
return Platform.is_macos()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def is_bsd() -> bool:
|
|
141
|
+
"""Check if running on any BSD variant."""
|
|
142
|
+
return Platform.is_bsd()
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def is_unix() -> bool:
|
|
146
|
+
"""Check if running on any Unix-like system (Linux, macOS, BSD)."""
|
|
147
|
+
return Platform.is_unix()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def is_posix() -> bool:
|
|
151
|
+
"""Check if running on a POSIX-compliant system."""
|
|
152
|
+
return Platform.is_posix()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# Module-level constants for backward compatibility
|
|
156
|
+
IS_WINDOWS: Final = is_windows()
|
|
157
|
+
IS_LINUX: Final = is_linux()
|
|
158
|
+
IS_MACOS: Final = is_macos()
|
|
159
|
+
IS_BSD: Final = is_bsd()
|
|
160
|
+
IS_UNIX: Final = is_unix()
|
|
161
|
+
IS_POSIX: Final = is_posix()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# Platform-specific module availability
|
|
165
|
+
def has_termios() -> bool:
|
|
166
|
+
"""Check if the termios module is available (Unix-like systems only)."""
|
|
167
|
+
try:
|
|
168
|
+
import termios # noqa: F401
|
|
169
|
+
|
|
170
|
+
return True
|
|
171
|
+
except ImportError:
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def has_fcntl() -> bool:
|
|
176
|
+
"""Check if the fcntl module is available (Unix-like systems only)."""
|
|
177
|
+
try:
|
|
178
|
+
import fcntl # noqa: F401
|
|
179
|
+
|
|
180
|
+
return True
|
|
181
|
+
except ImportError:
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def has_tty() -> bool:
|
|
186
|
+
"""Check if the tty module is available (Unix-like systems only)."""
|
|
187
|
+
try:
|
|
188
|
+
import tty # noqa: F401
|
|
189
|
+
|
|
190
|
+
return True
|
|
191
|
+
except ImportError:
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# Module-level constants for module availability
|
|
196
|
+
HAS_TERMIOS: Final = has_termios()
|
|
197
|
+
HAS_FCNTL: Final = has_fcntl()
|
|
198
|
+
HAS_TTY: Final = has_tty()
|