detect_agent 0.1.0__tar.gz
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.
- detect_agent-0.1.0/.gitignore +10 -0
- detect_agent-0.1.0/.python-version +1 -0
- detect_agent-0.1.0/CHANGELOG.md +5 -0
- detect_agent-0.1.0/PKG-INFO +118 -0
- detect_agent-0.1.0/README.md +105 -0
- detect_agent-0.1.0/detect_agent/__init__.py +121 -0
- detect_agent-0.1.0/detect_agent/py.typed +0 -0
- detect_agent-0.1.0/pyproject.toml +41 -0
- detect_agent-0.1.0/tests/__init__.py +0 -0
- detect_agent-0.1.0/tests/test_detect_agent.py +367 -0
- detect_agent-0.1.0/uv.lock +228 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.9
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: detect_agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Detect if code is running in an AI agent or automated development environment
|
|
5
|
+
Project-URL: Homepage, https://github.com/togethercomputer/detect_agent
|
|
6
|
+
Project-URL: Repository, https://github.com/togethercomputer/detect_agent
|
|
7
|
+
Project-URL: Changelog, https://github.com/togethercomputer/detect_agent/blob/main/CHANGELOG.md
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# detect_agent
|
|
15
|
+
|
|
16
|
+
> This is a Python Port of Vercels NPM package
|
|
17
|
+
|
|
18
|
+
A lightweight utility for detecting if code is being executed by an AI agent or automated development environment.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv add detect_agent
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from detect_agent import determine_agent
|
|
30
|
+
|
|
31
|
+
result = determine_agent()
|
|
32
|
+
|
|
33
|
+
if result["is_agent"]:
|
|
34
|
+
print(f"Running in {result["agent"]["name"]} environment");
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Supported Agents
|
|
38
|
+
|
|
39
|
+
This package can detect the following AI agents and development environments:
|
|
40
|
+
|
|
41
|
+
- **Custom agents** via `AI_AGENT` environment variable
|
|
42
|
+
- **Cursor** (cursor editor and cursor-cli)
|
|
43
|
+
- **Claude Code** (Anthropic's Claude)
|
|
44
|
+
- **Devin** (Cognition Labs)
|
|
45
|
+
- **Gemini CLI** (Google)
|
|
46
|
+
- **Codex** (OpenAI)
|
|
47
|
+
- **Antigravity** (Google DeepMind)
|
|
48
|
+
- **GitHub Copilot** (via `AI_AGENT=github-copilot|github-copilot-cli`, `COPILOT_MODEL`, `COPILOT_ALLOW_ALL`, or `COPILOT_GITHUB_TOKEN`)
|
|
49
|
+
- **Replit** (online IDE)
|
|
50
|
+
|
|
51
|
+
## The AI_AGENT Standard
|
|
52
|
+
|
|
53
|
+
We're promoting `AI_AGENT` as a universal environment variable standard for AI development tools. This allows any tool or library to easily detect when it's running in an AI-driven environment.
|
|
54
|
+
|
|
55
|
+
### For AI Tool Developers
|
|
56
|
+
|
|
57
|
+
Set the `AI_AGENT` environment variable to identify your tool:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
export AI_AGENT="your-tool-name"
|
|
61
|
+
# or
|
|
62
|
+
AI_AGENT="your-tool-name" your-command
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Recommended Naming Convention
|
|
66
|
+
|
|
67
|
+
- Use lowercase with hyphens for multi-word names
|
|
68
|
+
- Include version information if needed, separated by an `@` symbol
|
|
69
|
+
- Examples: `claude-code`, `cursor-cli`, `devin@1`, `custom-agent@2.0`
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
uv sync --extra dev
|
|
75
|
+
uv run pytest
|
|
76
|
+
# Lint and format check (CI)
|
|
77
|
+
uv run ruff check . && uv run ruff format --check .
|
|
78
|
+
# Fix and format
|
|
79
|
+
uv run ruff check . --fix && uv run ruff format .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Use Cases
|
|
83
|
+
|
|
84
|
+
### Adaptive Behavior
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from detect_agent import determine_agent
|
|
88
|
+
import os
|
|
89
|
+
|
|
90
|
+
def setup_environment():
|
|
91
|
+
result = determine_agent()
|
|
92
|
+
|
|
93
|
+
if (result["is_agent"]) {
|
|
94
|
+
# Running in AI environment - adjust behavior
|
|
95
|
+
os.environ.setdefault("TOGETHER_LOG", "debug")
|
|
96
|
+
print(f"🤖 Detected AI agent: {result["agent"]["name"]}");
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Telemetry and Analytics
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from detect_agent import determine_agent
|
|
103
|
+
|
|
104
|
+
def track_usage(event: string):
|
|
105
|
+
result = determine_agent();
|
|
106
|
+
|
|
107
|
+
analytics.track(event, {
|
|
108
|
+
"agent": result["agent"]["name"] if result["is_agent"] else "human",
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
### Adding New Agent Support
|
|
112
|
+
|
|
113
|
+
To add support for a new AI agent:
|
|
114
|
+
|
|
115
|
+
1. Add detection logic to `main.py`
|
|
116
|
+
2. Add comprehensive test cases in `test.py`
|
|
117
|
+
3. Update this README with the new agent information
|
|
118
|
+
4. Follow the existing priority order pattern
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# detect_agent
|
|
2
|
+
|
|
3
|
+
> This is a Python Port of Vercels NPM package
|
|
4
|
+
|
|
5
|
+
A lightweight utility for detecting if code is being executed by an AI agent or automated development environment.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv add detect_agent
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from detect_agent import determine_agent
|
|
17
|
+
|
|
18
|
+
result = determine_agent()
|
|
19
|
+
|
|
20
|
+
if result["is_agent"]:
|
|
21
|
+
print(f"Running in {result["agent"]["name"]} environment");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Supported Agents
|
|
25
|
+
|
|
26
|
+
This package can detect the following AI agents and development environments:
|
|
27
|
+
|
|
28
|
+
- **Custom agents** via `AI_AGENT` environment variable
|
|
29
|
+
- **Cursor** (cursor editor and cursor-cli)
|
|
30
|
+
- **Claude Code** (Anthropic's Claude)
|
|
31
|
+
- **Devin** (Cognition Labs)
|
|
32
|
+
- **Gemini CLI** (Google)
|
|
33
|
+
- **Codex** (OpenAI)
|
|
34
|
+
- **Antigravity** (Google DeepMind)
|
|
35
|
+
- **GitHub Copilot** (via `AI_AGENT=github-copilot|github-copilot-cli`, `COPILOT_MODEL`, `COPILOT_ALLOW_ALL`, or `COPILOT_GITHUB_TOKEN`)
|
|
36
|
+
- **Replit** (online IDE)
|
|
37
|
+
|
|
38
|
+
## The AI_AGENT Standard
|
|
39
|
+
|
|
40
|
+
We're promoting `AI_AGENT` as a universal environment variable standard for AI development tools. This allows any tool or library to easily detect when it's running in an AI-driven environment.
|
|
41
|
+
|
|
42
|
+
### For AI Tool Developers
|
|
43
|
+
|
|
44
|
+
Set the `AI_AGENT` environment variable to identify your tool:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
export AI_AGENT="your-tool-name"
|
|
48
|
+
# or
|
|
49
|
+
AI_AGENT="your-tool-name" your-command
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Recommended Naming Convention
|
|
53
|
+
|
|
54
|
+
- Use lowercase with hyphens for multi-word names
|
|
55
|
+
- Include version information if needed, separated by an `@` symbol
|
|
56
|
+
- Examples: `claude-code`, `cursor-cli`, `devin@1`, `custom-agent@2.0`
|
|
57
|
+
|
|
58
|
+
## Development
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv sync --extra dev
|
|
62
|
+
uv run pytest
|
|
63
|
+
# Lint and format check (CI)
|
|
64
|
+
uv run ruff check . && uv run ruff format --check .
|
|
65
|
+
# Fix and format
|
|
66
|
+
uv run ruff check . --fix && uv run ruff format .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Use Cases
|
|
70
|
+
|
|
71
|
+
### Adaptive Behavior
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from detect_agent import determine_agent
|
|
75
|
+
import os
|
|
76
|
+
|
|
77
|
+
def setup_environment():
|
|
78
|
+
result = determine_agent()
|
|
79
|
+
|
|
80
|
+
if (result["is_agent"]) {
|
|
81
|
+
# Running in AI environment - adjust behavior
|
|
82
|
+
os.environ.setdefault("TOGETHER_LOG", "debug")
|
|
83
|
+
print(f"🤖 Detected AI agent: {result["agent"]["name"]}");
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Telemetry and Analytics
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from detect_agent import determine_agent
|
|
90
|
+
|
|
91
|
+
def track_usage(event: string):
|
|
92
|
+
result = determine_agent();
|
|
93
|
+
|
|
94
|
+
analytics.track(event, {
|
|
95
|
+
"agent": result["agent"]["name"] if result["is_agent"] else "human",
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
### Adding New Agent Support
|
|
99
|
+
|
|
100
|
+
To add support for a new AI agent:
|
|
101
|
+
|
|
102
|
+
1. Add detection logic to `main.py`
|
|
103
|
+
2. Add comprehensive test cases in `test.py`
|
|
104
|
+
3. Update this README with the new agent information
|
|
105
|
+
4. Follow the existing priority order pattern
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Literal, TypedDict, Union
|
|
4
|
+
|
|
5
|
+
DEVIN_LOCAL_PATH = "/opt/.devin"
|
|
6
|
+
|
|
7
|
+
CURSOR: Literal["cursor"] = "cursor"
|
|
8
|
+
CURSOR_CLI: Literal["cursor-cli"] = "cursor-cli"
|
|
9
|
+
CLAUDE: Literal["claude"] = "claude"
|
|
10
|
+
COWORK: Literal["cowork"] = "cowork"
|
|
11
|
+
DEVIN: Literal["devin"] = "devin"
|
|
12
|
+
REPLIT: Literal["replit"] = "replit"
|
|
13
|
+
GEMINI: Literal["gemini"] = "gemini"
|
|
14
|
+
CODEX: Literal["codex"] = "codex"
|
|
15
|
+
ANTIGRAVITY: Literal["antigravity"] = "antigravity"
|
|
16
|
+
AUGMENT_CLI: Literal["augment-cli"] = "augment-cli"
|
|
17
|
+
OPENCODE: Literal["opencode"] = "opencode"
|
|
18
|
+
GITHUB_COPILOT: Literal["github-copilot"] = "github-copilot"
|
|
19
|
+
GITHUB_COPILOT_CLI: Literal["github-copilot-cli"] = "github-copilot-cli"
|
|
20
|
+
|
|
21
|
+
KnownAgentNames = Literal[
|
|
22
|
+
"cursor",
|
|
23
|
+
"cursor-cli",
|
|
24
|
+
"claude",
|
|
25
|
+
"cowork",
|
|
26
|
+
"devin",
|
|
27
|
+
"replit",
|
|
28
|
+
"gemini",
|
|
29
|
+
"codex",
|
|
30
|
+
"antigravity",
|
|
31
|
+
"augment-cli",
|
|
32
|
+
"opencode",
|
|
33
|
+
"github-copilot",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class KnownAgentDetails(TypedDict):
|
|
38
|
+
name: KnownAgentNames
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AgentResultAgent(TypedDict):
|
|
42
|
+
is_agent: Literal[True]
|
|
43
|
+
agent: KnownAgentDetails
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AgentResultNone(TypedDict):
|
|
47
|
+
is_agent: Literal[False]
|
|
48
|
+
agent: None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
AgentResult = Union[AgentResultAgent, AgentResultNone]
|
|
52
|
+
|
|
53
|
+
KNOWN_AGENTS = {
|
|
54
|
+
"CURSOR": CURSOR,
|
|
55
|
+
"CURSOR_CLI": CURSOR_CLI,
|
|
56
|
+
"CLAUDE": CLAUDE,
|
|
57
|
+
"COWORK": COWORK,
|
|
58
|
+
"DEVIN": DEVIN,
|
|
59
|
+
"REPLIT": REPLIT,
|
|
60
|
+
"GEMINI": GEMINI,
|
|
61
|
+
"CODEX": CODEX,
|
|
62
|
+
"ANTIGRAVITY": ANTIGRAVITY,
|
|
63
|
+
"AUGMENT_CLI": AUGMENT_CLI,
|
|
64
|
+
"OPENCODE": OPENCODE,
|
|
65
|
+
"GITHUB_COPILOT": GITHUB_COPILOT,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def determine_agent() -> AgentResult:
|
|
70
|
+
ai_agent = os.environ.get("AI_AGENT")
|
|
71
|
+
if ai_agent:
|
|
72
|
+
name = ai_agent.strip()
|
|
73
|
+
if name:
|
|
74
|
+
if name in (GITHUB_COPILOT, GITHUB_COPILOT_CLI):
|
|
75
|
+
return {"is_agent": True, "agent": {"name": GITHUB_COPILOT}}
|
|
76
|
+
return {"is_agent": True, "agent": {"name": name}} # type: ignore[return-value]
|
|
77
|
+
|
|
78
|
+
if os.environ.get("CURSOR_TRACE_ID"):
|
|
79
|
+
return {"is_agent": True, "agent": {"name": CURSOR}}
|
|
80
|
+
|
|
81
|
+
if os.environ.get("CURSOR_AGENT"):
|
|
82
|
+
return {"is_agent": True, "agent": {"name": CURSOR_CLI}}
|
|
83
|
+
|
|
84
|
+
if os.environ.get("GEMINI_CLI"):
|
|
85
|
+
return {"is_agent": True, "agent": {"name": GEMINI}}
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
os.environ.get("CODEX_SANDBOX")
|
|
89
|
+
or os.environ.get("CODEX_CI")
|
|
90
|
+
or os.environ.get("CODEX_THREAD_ID")
|
|
91
|
+
):
|
|
92
|
+
return {"is_agent": True, "agent": {"name": CODEX}}
|
|
93
|
+
|
|
94
|
+
if os.environ.get("ANTIGRAVITY_AGENT"):
|
|
95
|
+
return {"is_agent": True, "agent": {"name": ANTIGRAVITY}}
|
|
96
|
+
|
|
97
|
+
if os.environ.get("AUGMENT_AGENT"):
|
|
98
|
+
return {"is_agent": True, "agent": {"name": AUGMENT_CLI}}
|
|
99
|
+
|
|
100
|
+
if os.environ.get("OPENCODE_CLIENT"):
|
|
101
|
+
return {"is_agent": True, "agent": {"name": OPENCODE}}
|
|
102
|
+
|
|
103
|
+
if os.environ.get("CLAUDECODE") or os.environ.get("CLAUDE_CODE"):
|
|
104
|
+
if os.environ.get("CLAUDE_CODE_IS_COWORK"):
|
|
105
|
+
return {"is_agent": True, "agent": {"name": COWORK}}
|
|
106
|
+
return {"is_agent": True, "agent": {"name": CLAUDE}}
|
|
107
|
+
|
|
108
|
+
if os.environ.get("REPL_ID"):
|
|
109
|
+
return {"is_agent": True, "agent": {"name": REPLIT}}
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
os.environ.get("COPILOT_MODEL")
|
|
113
|
+
or os.environ.get("COPILOT_ALLOW_ALL")
|
|
114
|
+
or os.environ.get("COPILOT_GITHUB_TOKEN")
|
|
115
|
+
):
|
|
116
|
+
return {"is_agent": True, "agent": {"name": GITHUB_COPILOT}}
|
|
117
|
+
|
|
118
|
+
if Path(DEVIN_LOCAL_PATH).exists():
|
|
119
|
+
return {"is_agent": True, "agent": {"name": DEVIN}}
|
|
120
|
+
|
|
121
|
+
return {"is_agent": False, "agent": None}
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.build.targets.wheel]
|
|
6
|
+
packages = ["detect_agent"]
|
|
7
|
+
|
|
8
|
+
[project]
|
|
9
|
+
name = "detect_agent"
|
|
10
|
+
version = "0.1.0"
|
|
11
|
+
description = "Detect if code is running in an AI agent or automated development environment"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
dependencies = []
|
|
15
|
+
packages = ["detect_agent"]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
dev = ["pytest>=7.0", "ruff>=0.8.0"]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/togethercomputer/detect_agent"
|
|
22
|
+
Repository = "https://github.com/togethercomputer/detect_agent"
|
|
23
|
+
Changelog = "https://github.com/togethercomputer/detect_agent/blob/main/CHANGELOG.md"
|
|
24
|
+
|
|
25
|
+
[tool.pytest.ini_options]
|
|
26
|
+
testpaths = ["tests"]
|
|
27
|
+
xfail_strict = true
|
|
28
|
+
filterwarnings = [
|
|
29
|
+
"error"
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[tool.ruff]
|
|
33
|
+
line-length = 100
|
|
34
|
+
target-version = "py39"
|
|
35
|
+
|
|
36
|
+
[tool.ruff.lint]
|
|
37
|
+
select = ["E", "F", "I", "UP"]
|
|
38
|
+
ignore = []
|
|
39
|
+
|
|
40
|
+
[tool.ruff.lint.per-file-ignores]
|
|
41
|
+
"tests/**/*.py" = ["S101"] # allow assert in tests
|
|
File without changes
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"""Tests for determine_agent."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from detect_agent import (
|
|
9
|
+
DEVIN_LOCAL_PATH,
|
|
10
|
+
KNOWN_AGENTS,
|
|
11
|
+
determine_agent,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# Env vars we reset so tests don't leak into each other
|
|
15
|
+
_AGENT_ENV_VARS = (
|
|
16
|
+
"AI_AGENT",
|
|
17
|
+
"CURSOR_TRACE_ID",
|
|
18
|
+
"CURSOR_AGENT",
|
|
19
|
+
"GEMINI_CLI",
|
|
20
|
+
"CODEX_SANDBOX",
|
|
21
|
+
"CODEX_CI",
|
|
22
|
+
"CODEX_THREAD_ID",
|
|
23
|
+
"ANTIGRAVITY_AGENT",
|
|
24
|
+
"AUGMENT_AGENT",
|
|
25
|
+
"OPENCODE_CLIENT",
|
|
26
|
+
"CLAUDECODE",
|
|
27
|
+
"CLAUDE_CODE",
|
|
28
|
+
"CLAUDE_CODE_IS_COWORK",
|
|
29
|
+
"REPL_ID",
|
|
30
|
+
"COPILOT_MODEL",
|
|
31
|
+
"COPILOT_ALLOW_ALL",
|
|
32
|
+
"COPILOT_GITHUB_TOKEN",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.fixture(autouse=True)
|
|
37
|
+
def _clear_agent_env(monkeypatch):
|
|
38
|
+
for key in _AGENT_ENV_VARS:
|
|
39
|
+
monkeypatch.delenv(key, raising=False)
|
|
40
|
+
yield
|
|
41
|
+
for key in _AGENT_ENV_VARS:
|
|
42
|
+
monkeypatch.delenv(key, raising=False)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TestCustomAgentFromAI_AGENT:
|
|
46
|
+
"""Custom agent detection from AI_AGENT."""
|
|
47
|
+
|
|
48
|
+
def test_ai_agent_not_set_returns_no_agent(self):
|
|
49
|
+
result = determine_agent()
|
|
50
|
+
assert result == {"is_agent": False, "agent": None}
|
|
51
|
+
|
|
52
|
+
def test_ai_agent_set_detects_custom_agent(self, monkeypatch):
|
|
53
|
+
monkeypatch.setenv("AI_AGENT", "custom-agent")
|
|
54
|
+
result = determine_agent()
|
|
55
|
+
assert result == {"is_agent": True, "agent": {"name": "custom-agent"}}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class TestGitHubCopilotDetection:
|
|
59
|
+
"""GitHub Copilot detection."""
|
|
60
|
+
|
|
61
|
+
def test_from_ai_agent_github_copilot(self, monkeypatch):
|
|
62
|
+
monkeypatch.setenv("AI_AGENT", "github-copilot")
|
|
63
|
+
result = determine_agent()
|
|
64
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["GITHUB_COPILOT"]}}
|
|
65
|
+
|
|
66
|
+
def test_from_ai_agent_github_copilot_cli(self, monkeypatch):
|
|
67
|
+
monkeypatch.setenv("AI_AGENT", "github-copilot-cli")
|
|
68
|
+
result = determine_agent()
|
|
69
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["GITHUB_COPILOT"]}}
|
|
70
|
+
|
|
71
|
+
def test_from_copilot_model(self, monkeypatch):
|
|
72
|
+
monkeypatch.setenv("COPILOT_MODEL", "gpt-5")
|
|
73
|
+
result = determine_agent()
|
|
74
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["GITHUB_COPILOT"]}}
|
|
75
|
+
|
|
76
|
+
def test_from_copilot_allow_all(self, monkeypatch):
|
|
77
|
+
monkeypatch.setenv("COPILOT_ALLOW_ALL", "true")
|
|
78
|
+
result = determine_agent()
|
|
79
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["GITHUB_COPILOT"]}}
|
|
80
|
+
|
|
81
|
+
def test_from_copilot_github_token(self, monkeypatch):
|
|
82
|
+
monkeypatch.setenv("COPILOT_GITHUB_TOKEN", "ghp_xxx")
|
|
83
|
+
result = determine_agent()
|
|
84
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["GITHUB_COPILOT"]}}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class TestCursorDetection:
|
|
88
|
+
"""Cursor detection."""
|
|
89
|
+
|
|
90
|
+
def test_cursor_trace_id_not_set_returns_no_agent(self):
|
|
91
|
+
result = determine_agent()
|
|
92
|
+
assert result == {"is_agent": False, "agent": None}
|
|
93
|
+
|
|
94
|
+
def test_cursor_trace_id_set_detects_cursor(self, monkeypatch):
|
|
95
|
+
monkeypatch.setenv("CURSOR_TRACE_ID", "some-uuid")
|
|
96
|
+
result = determine_agent()
|
|
97
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CURSOR"]}}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class TestCursorCliDetection:
|
|
101
|
+
"""Cursor CLI detection."""
|
|
102
|
+
|
|
103
|
+
def test_cursor_agent_not_set_returns_no_agent(self):
|
|
104
|
+
result = determine_agent()
|
|
105
|
+
assert result == {"is_agent": False, "agent": None}
|
|
106
|
+
|
|
107
|
+
def test_cursor_agent_set_detects_cursor_cli(self, monkeypatch):
|
|
108
|
+
monkeypatch.setenv("CURSOR_AGENT", "1")
|
|
109
|
+
result = determine_agent()
|
|
110
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CURSOR_CLI"]}}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class TestGeminiDetection:
|
|
114
|
+
"""Gemini detection."""
|
|
115
|
+
|
|
116
|
+
def test_gemini_cli_not_set_returns_no_agent(self):
|
|
117
|
+
result = determine_agent()
|
|
118
|
+
assert result == {"is_agent": False, "agent": None}
|
|
119
|
+
|
|
120
|
+
def test_gemini_cli_set_detects_gemini(self, monkeypatch):
|
|
121
|
+
monkeypatch.setenv("GEMINI_CLI", "1")
|
|
122
|
+
result = determine_agent()
|
|
123
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["GEMINI"]}}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TestCodexDetection:
|
|
127
|
+
"""Codex detection."""
|
|
128
|
+
|
|
129
|
+
def test_codex_sandbox_not_set_returns_no_agent(self):
|
|
130
|
+
result = determine_agent()
|
|
131
|
+
assert result == {"is_agent": False, "agent": None}
|
|
132
|
+
|
|
133
|
+
def test_codex_sandbox_set_detects_codex(self, monkeypatch):
|
|
134
|
+
monkeypatch.setenv("CODEX_SANDBOX", "seatbelt")
|
|
135
|
+
result = determine_agent()
|
|
136
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CODEX"]}}
|
|
137
|
+
|
|
138
|
+
def test_codex_ci_set_detects_codex(self, monkeypatch):
|
|
139
|
+
monkeypatch.setenv("CODEX_CI", "1")
|
|
140
|
+
result = determine_agent()
|
|
141
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CODEX"]}}
|
|
142
|
+
|
|
143
|
+
def test_codex_thread_id_set_detects_codex(self, monkeypatch):
|
|
144
|
+
monkeypatch.setenv("CODEX_THREAD_ID", "thread-123")
|
|
145
|
+
result = determine_agent()
|
|
146
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CODEX"]}}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class TestAntigravityDetection:
|
|
150
|
+
"""Antigravity detection."""
|
|
151
|
+
|
|
152
|
+
def test_antigravity_agent_not_set_returns_no_agent(self):
|
|
153
|
+
result = determine_agent()
|
|
154
|
+
assert result == {"is_agent": False, "agent": None}
|
|
155
|
+
|
|
156
|
+
def test_antigravity_agent_set_detects_antigravity(self, monkeypatch):
|
|
157
|
+
monkeypatch.setenv("ANTIGRAVITY_AGENT", "1")
|
|
158
|
+
result = determine_agent()
|
|
159
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["ANTIGRAVITY"]}}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class TestAugmentCliDetection:
|
|
163
|
+
"""Augment CLI detection."""
|
|
164
|
+
|
|
165
|
+
def test_augment_agent_not_set_returns_no_agent(self):
|
|
166
|
+
result = determine_agent()
|
|
167
|
+
assert result == {"is_agent": False, "agent": None}
|
|
168
|
+
|
|
169
|
+
def test_augment_agent_set_detects_augment_cli(self, monkeypatch):
|
|
170
|
+
monkeypatch.setenv("AUGMENT_AGENT", "1")
|
|
171
|
+
result = determine_agent()
|
|
172
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["AUGMENT_CLI"]}}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class TestOpencodeDetection:
|
|
176
|
+
"""Opencode detection."""
|
|
177
|
+
|
|
178
|
+
def test_opencode_client_not_set_returns_no_agent(self):
|
|
179
|
+
result = determine_agent()
|
|
180
|
+
assert result == {"is_agent": False, "agent": None}
|
|
181
|
+
|
|
182
|
+
def test_opencode_client_set_detects_opencode(self, monkeypatch):
|
|
183
|
+
monkeypatch.setenv("OPENCODE_CLIENT", "opencode")
|
|
184
|
+
result = determine_agent()
|
|
185
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["OPENCODE"]}}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class TestClaudeDetection:
|
|
189
|
+
"""Claude detection."""
|
|
190
|
+
|
|
191
|
+
def test_claude_code_not_set_returns_no_agent(self):
|
|
192
|
+
result = determine_agent()
|
|
193
|
+
assert result == {"is_agent": False, "agent": None}
|
|
194
|
+
|
|
195
|
+
def test_claude_code_set_detects_claude(self, monkeypatch):
|
|
196
|
+
monkeypatch.setenv("CLAUDE_CODE", "1")
|
|
197
|
+
result = determine_agent()
|
|
198
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CLAUDE"]}}
|
|
199
|
+
|
|
200
|
+
def test_claudecode_set_detects_claude(self, monkeypatch):
|
|
201
|
+
monkeypatch.setenv("CLAUDECODE", "1")
|
|
202
|
+
result = determine_agent()
|
|
203
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CLAUDE"]}}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class TestCoworkDetection:
|
|
207
|
+
"""Cowork detection."""
|
|
208
|
+
|
|
209
|
+
def test_claude_code_is_cowork_not_set_detects_claude(self, monkeypatch):
|
|
210
|
+
monkeypatch.setenv("CLAUDECODE", "1")
|
|
211
|
+
result = determine_agent()
|
|
212
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CLAUDE"]}}
|
|
213
|
+
|
|
214
|
+
def test_claude_code_is_cowork_set_with_claudecode_detects_cowork(self, monkeypatch):
|
|
215
|
+
monkeypatch.setenv("CLAUDECODE", "1")
|
|
216
|
+
monkeypatch.setenv("CLAUDE_CODE_IS_COWORK", "1")
|
|
217
|
+
result = determine_agent()
|
|
218
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["COWORK"]}}
|
|
219
|
+
|
|
220
|
+
def test_claude_code_is_cowork_set_with_claude_code_detects_cowork(self, monkeypatch):
|
|
221
|
+
monkeypatch.setenv("CLAUDE_CODE", "1")
|
|
222
|
+
monkeypatch.setenv("CLAUDE_CODE_IS_COWORK", "1")
|
|
223
|
+
result = determine_agent()
|
|
224
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["COWORK"]}}
|
|
225
|
+
|
|
226
|
+
def test_claude_code_is_cowork_set_without_claudecode_or_claude_code_returns_no_agent(
|
|
227
|
+
self, monkeypatch
|
|
228
|
+
):
|
|
229
|
+
monkeypatch.setenv("CLAUDE_CODE_IS_COWORK", "1")
|
|
230
|
+
result = determine_agent()
|
|
231
|
+
assert result == {"is_agent": False, "agent": None}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class TestDevinDetection:
|
|
235
|
+
"""Devin detection."""
|
|
236
|
+
|
|
237
|
+
def test_devin_path_does_not_exist_returns_no_agent(self):
|
|
238
|
+
with patch.object(Path, "exists", return_value=False):
|
|
239
|
+
result = determine_agent()
|
|
240
|
+
assert result == {"is_agent": False, "agent": None}
|
|
241
|
+
|
|
242
|
+
def test_devin_path_exists_detects_devin(self):
|
|
243
|
+
with patch.object(Path, "exists", return_value=True):
|
|
244
|
+
result = determine_agent()
|
|
245
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["DEVIN"]}}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class TestReplitDetection:
|
|
249
|
+
"""Replit detection."""
|
|
250
|
+
|
|
251
|
+
def test_repl_id_not_set_returns_no_agent(self):
|
|
252
|
+
result = determine_agent()
|
|
253
|
+
assert result == {"is_agent": False, "agent": None}
|
|
254
|
+
|
|
255
|
+
def test_repl_id_set_detects_replit(self, monkeypatch):
|
|
256
|
+
monkeypatch.setenv("REPL_ID", "1")
|
|
257
|
+
result = determine_agent()
|
|
258
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["REPLIT"]}}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class TestPriorityOrderDetection:
|
|
262
|
+
"""Priority order detection."""
|
|
263
|
+
|
|
264
|
+
def test_ai_agent_takes_highest_priority(self, monkeypatch):
|
|
265
|
+
monkeypatch.setenv("AI_AGENT", "custom-priority")
|
|
266
|
+
monkeypatch.setenv("CURSOR_TRACE_ID", "some-uuid")
|
|
267
|
+
monkeypatch.setenv("CURSOR_AGENT", "1")
|
|
268
|
+
monkeypatch.setenv("GEMINI_CLI", "1")
|
|
269
|
+
monkeypatch.setenv("CODEX_SANDBOX", "seatbelt")
|
|
270
|
+
monkeypatch.setenv("ANTIGRAVITY_AGENT", "1")
|
|
271
|
+
monkeypatch.setenv("AUGMENT_AGENT", "1")
|
|
272
|
+
monkeypatch.setenv("OPENCODE_CLIENT", "opencode")
|
|
273
|
+
monkeypatch.setenv("CLAUDE_CODE", "1")
|
|
274
|
+
monkeypatch.setenv("REPL_ID", "1")
|
|
275
|
+
monkeypatch.setenv("COPILOT_MODEL", "gpt-5")
|
|
276
|
+
monkeypatch.setenv("COPILOT_ALLOW_ALL", "true")
|
|
277
|
+
monkeypatch.setenv("COPILOT_GITHUB_TOKEN", "ghp_xxx")
|
|
278
|
+
with patch.object(Path, "exists") as mock_exists:
|
|
279
|
+
mock_exists.side_effect = lambda self: str(self) == DEVIN_LOCAL_PATH
|
|
280
|
+
result = determine_agent()
|
|
281
|
+
assert result == {"is_agent": True, "agent": {"name": "custom-priority"}}
|
|
282
|
+
|
|
283
|
+
def test_cursor_trace_id_takes_priority_over_other_agents(self, monkeypatch):
|
|
284
|
+
monkeypatch.setenv("CURSOR_TRACE_ID", "some-uuid")
|
|
285
|
+
monkeypatch.setenv("CURSOR_AGENT", "1")
|
|
286
|
+
monkeypatch.setenv("GEMINI_CLI", "1")
|
|
287
|
+
monkeypatch.setenv("CODEX_SANDBOX", "seatbelt")
|
|
288
|
+
monkeypatch.setenv("ANTIGRAVITY_AGENT", "1")
|
|
289
|
+
monkeypatch.setenv("AUGMENT_AGENT", "1")
|
|
290
|
+
monkeypatch.setenv("OPENCODE_CLIENT", "opencode")
|
|
291
|
+
monkeypatch.setenv("CLAUDE_CODE", "1")
|
|
292
|
+
monkeypatch.setenv("REPL_ID", "1")
|
|
293
|
+
monkeypatch.setenv("COPILOT_MODEL", "gpt-5")
|
|
294
|
+
monkeypatch.setenv("COPILOT_ALLOW_ALL", "true")
|
|
295
|
+
monkeypatch.setenv("COPILOT_GITHUB_TOKEN", "ghp_xxx")
|
|
296
|
+
with patch.object(Path, "exists") as mock_exists:
|
|
297
|
+
mock_exists.side_effect = lambda self: str(self) == DEVIN_LOCAL_PATH
|
|
298
|
+
result = determine_agent()
|
|
299
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CURSOR"]}}
|
|
300
|
+
|
|
301
|
+
def test_cursor_agent_takes_priority_over_remaining_agents(self, monkeypatch):
|
|
302
|
+
monkeypatch.setenv("CURSOR_AGENT", "1")
|
|
303
|
+
monkeypatch.setenv("GEMINI_CLI", "1")
|
|
304
|
+
monkeypatch.setenv("CODEX_SANDBOX", "seatbelt")
|
|
305
|
+
monkeypatch.setenv("ANTIGRAVITY_AGENT", "1")
|
|
306
|
+
monkeypatch.setenv("AUGMENT_AGENT", "1")
|
|
307
|
+
monkeypatch.setenv("OPENCODE_CLIENT", "opencode")
|
|
308
|
+
monkeypatch.setenv("CLAUDE_CODE", "1")
|
|
309
|
+
monkeypatch.setenv("REPL_ID", "1")
|
|
310
|
+
monkeypatch.setenv("COPILOT_MODEL", "gpt-5")
|
|
311
|
+
monkeypatch.setenv("COPILOT_ALLOW_ALL", "true")
|
|
312
|
+
monkeypatch.setenv("COPILOT_GITHUB_TOKEN", "ghp_xxx")
|
|
313
|
+
with patch.object(Path, "exists") as mock_exists:
|
|
314
|
+
mock_exists.side_effect = lambda self: str(self) == DEVIN_LOCAL_PATH
|
|
315
|
+
result = determine_agent()
|
|
316
|
+
assert result == {"is_agent": True, "agent": {"name": KNOWN_AGENTS["CURSOR_CLI"]}}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class TestEdgeCases:
|
|
320
|
+
"""Edge cases."""
|
|
321
|
+
|
|
322
|
+
def test_empty_string_env_vars(self, monkeypatch):
|
|
323
|
+
monkeypatch.setenv("AI_AGENT", "")
|
|
324
|
+
monkeypatch.setenv("CURSOR_TRACE_ID", "")
|
|
325
|
+
result = determine_agent()
|
|
326
|
+
assert result == {"is_agent": False, "agent": None}
|
|
327
|
+
|
|
328
|
+
def test_whitespace_only_ai_agent(self, monkeypatch):
|
|
329
|
+
monkeypatch.setenv("AI_AGENT", " ")
|
|
330
|
+
result = determine_agent()
|
|
331
|
+
assert result == {"is_agent": False, "agent": None}
|
|
332
|
+
|
|
333
|
+
def test_special_characters_in_ai_agent(self, monkeypatch):
|
|
334
|
+
monkeypatch.setenv("AI_AGENT", "my-custom-agent@v1.0")
|
|
335
|
+
result = determine_agent()
|
|
336
|
+
assert result == {"is_agent": True, "agent": {"name": "my-custom-agent@v1.0"}}
|
|
337
|
+
|
|
338
|
+
def test_trims_whitespace_from_ai_agent(self, monkeypatch):
|
|
339
|
+
monkeypatch.setenv("AI_AGENT", " custom-agent ")
|
|
340
|
+
result = determine_agent()
|
|
341
|
+
assert result == {"is_agent": True, "agent": {"name": "custom-agent"}}
|
|
342
|
+
|
|
343
|
+
def test_devin_path_not_accessible_returns_no_agent(self):
|
|
344
|
+
with patch.object(Path, "exists", return_value=False):
|
|
345
|
+
result = determine_agent()
|
|
346
|
+
assert result == {"is_agent": False, "agent": None}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class TestConvenienceMethods:
|
|
350
|
+
"""Convenience methods."""
|
|
351
|
+
|
|
352
|
+
def test_is_agent_boolean(self, monkeypatch):
|
|
353
|
+
monkeypatch.setenv("AI_AGENT", "test-agent")
|
|
354
|
+
result = determine_agent()
|
|
355
|
+
assert result["is_agent"] is True
|
|
356
|
+
|
|
357
|
+
def test_agent_details_when_detected(self, monkeypatch):
|
|
358
|
+
monkeypatch.setenv("CURSOR_TRACE_ID", "some-id")
|
|
359
|
+
result = determine_agent()
|
|
360
|
+
assert result["is_agent"] is True
|
|
361
|
+
assert result.get("agent") is not None
|
|
362
|
+
assert result["agent"]["name"] == KNOWN_AGENTS["CURSOR"]
|
|
363
|
+
|
|
364
|
+
def test_no_agent_details_when_not_detected(self):
|
|
365
|
+
result = determine_agent()
|
|
366
|
+
assert result["is_agent"] is False
|
|
367
|
+
assert result.get("agent") is None
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.9"
|
|
4
|
+
resolution-markers = [
|
|
5
|
+
"python_full_version >= '3.10'",
|
|
6
|
+
"python_full_version < '3.10'",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
[[package]]
|
|
10
|
+
name = "colorama"
|
|
11
|
+
version = "0.4.6"
|
|
12
|
+
source = { registry = "https://pypi.org/simple" }
|
|
13
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
|
14
|
+
wheels = [
|
|
15
|
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[[package]]
|
|
19
|
+
name = "detect-agent"
|
|
20
|
+
version = "0.1.0"
|
|
21
|
+
source = { editable = "." }
|
|
22
|
+
|
|
23
|
+
[package.optional-dependencies]
|
|
24
|
+
dev = [
|
|
25
|
+
{ name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
|
26
|
+
{ name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
|
27
|
+
{ name = "ruff" },
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[package.metadata]
|
|
31
|
+
requires-dist = [
|
|
32
|
+
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0" },
|
|
33
|
+
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8.0" },
|
|
34
|
+
]
|
|
35
|
+
provides-extras = ["dev"]
|
|
36
|
+
|
|
37
|
+
[[package]]
|
|
38
|
+
name = "exceptiongroup"
|
|
39
|
+
version = "1.3.1"
|
|
40
|
+
source = { registry = "https://pypi.org/simple" }
|
|
41
|
+
dependencies = [
|
|
42
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
43
|
+
]
|
|
44
|
+
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
|
45
|
+
wheels = [
|
|
46
|
+
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[[package]]
|
|
50
|
+
name = "iniconfig"
|
|
51
|
+
version = "2.1.0"
|
|
52
|
+
source = { registry = "https://pypi.org/simple" }
|
|
53
|
+
resolution-markers = [
|
|
54
|
+
"python_full_version < '3.10'",
|
|
55
|
+
]
|
|
56
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
|
|
57
|
+
wheels = [
|
|
58
|
+
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[[package]]
|
|
62
|
+
name = "iniconfig"
|
|
63
|
+
version = "2.3.0"
|
|
64
|
+
source = { registry = "https://pypi.org/simple" }
|
|
65
|
+
resolution-markers = [
|
|
66
|
+
"python_full_version >= '3.10'",
|
|
67
|
+
]
|
|
68
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
|
69
|
+
wheels = [
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[[package]]
|
|
74
|
+
name = "packaging"
|
|
75
|
+
version = "26.0"
|
|
76
|
+
source = { registry = "https://pypi.org/simple" }
|
|
77
|
+
sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
|
|
78
|
+
wheels = [
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[[package]]
|
|
83
|
+
name = "pluggy"
|
|
84
|
+
version = "1.6.0"
|
|
85
|
+
source = { registry = "https://pypi.org/simple" }
|
|
86
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
|
87
|
+
wheels = [
|
|
88
|
+
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[[package]]
|
|
92
|
+
name = "pygments"
|
|
93
|
+
version = "2.19.2"
|
|
94
|
+
source = { registry = "https://pypi.org/simple" }
|
|
95
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
|
|
96
|
+
wheels = [
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
[[package]]
|
|
101
|
+
name = "pytest"
|
|
102
|
+
version = "8.4.2"
|
|
103
|
+
source = { registry = "https://pypi.org/simple" }
|
|
104
|
+
resolution-markers = [
|
|
105
|
+
"python_full_version < '3.10'",
|
|
106
|
+
]
|
|
107
|
+
dependencies = [
|
|
108
|
+
{ name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" },
|
|
109
|
+
{ name = "exceptiongroup", marker = "python_full_version < '3.10'" },
|
|
110
|
+
{ name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
|
111
|
+
{ name = "packaging", marker = "python_full_version < '3.10'" },
|
|
112
|
+
{ name = "pluggy", marker = "python_full_version < '3.10'" },
|
|
113
|
+
{ name = "pygments", marker = "python_full_version < '3.10'" },
|
|
114
|
+
{ name = "tomli", marker = "python_full_version < '3.10'" },
|
|
115
|
+
]
|
|
116
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
|
|
117
|
+
wheels = [
|
|
118
|
+
{ url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "pytest"
|
|
123
|
+
version = "9.0.2"
|
|
124
|
+
source = { registry = "https://pypi.org/simple" }
|
|
125
|
+
resolution-markers = [
|
|
126
|
+
"python_full_version >= '3.10'",
|
|
127
|
+
]
|
|
128
|
+
dependencies = [
|
|
129
|
+
{ name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
|
|
130
|
+
{ name = "exceptiongroup", marker = "python_full_version == '3.10.*'" },
|
|
131
|
+
{ name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
|
132
|
+
{ name = "packaging", marker = "python_full_version >= '3.10'" },
|
|
133
|
+
{ name = "pluggy", marker = "python_full_version >= '3.10'" },
|
|
134
|
+
{ name = "pygments", marker = "python_full_version >= '3.10'" },
|
|
135
|
+
{ name = "tomli", marker = "python_full_version == '3.10.*'" },
|
|
136
|
+
]
|
|
137
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
|
138
|
+
wheels = [
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
[[package]]
|
|
143
|
+
name = "ruff"
|
|
144
|
+
version = "0.15.6"
|
|
145
|
+
source = { registry = "https://pypi.org/simple" }
|
|
146
|
+
sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" }
|
|
147
|
+
wheels = [
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" },
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" },
|
|
150
|
+
{ url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" },
|
|
151
|
+
{ url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" },
|
|
152
|
+
{ url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" },
|
|
153
|
+
{ url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" },
|
|
154
|
+
{ url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" },
|
|
155
|
+
{ url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" },
|
|
156
|
+
{ url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" },
|
|
157
|
+
{ url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" },
|
|
158
|
+
{ url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" },
|
|
159
|
+
{ url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" },
|
|
160
|
+
{ url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" },
|
|
161
|
+
{ url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" },
|
|
162
|
+
{ url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" },
|
|
163
|
+
{ url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" },
|
|
164
|
+
{ url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" },
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
[[package]]
|
|
168
|
+
name = "tomli"
|
|
169
|
+
version = "2.4.0"
|
|
170
|
+
source = { registry = "https://pypi.org/simple" }
|
|
171
|
+
sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" }
|
|
172
|
+
wheels = [
|
|
173
|
+
{ url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" },
|
|
174
|
+
{ url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" },
|
|
175
|
+
{ url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" },
|
|
176
|
+
{ url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" },
|
|
177
|
+
{ url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" },
|
|
178
|
+
{ url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" },
|
|
179
|
+
{ url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" },
|
|
180
|
+
{ url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" },
|
|
181
|
+
{ url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" },
|
|
182
|
+
{ url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" },
|
|
183
|
+
{ url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" },
|
|
184
|
+
{ url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" },
|
|
185
|
+
{ url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" },
|
|
186
|
+
{ url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" },
|
|
187
|
+
{ url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" },
|
|
188
|
+
{ url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" },
|
|
189
|
+
{ url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" },
|
|
190
|
+
{ url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" },
|
|
191
|
+
{ url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" },
|
|
192
|
+
{ url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" },
|
|
193
|
+
{ url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" },
|
|
194
|
+
{ url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" },
|
|
195
|
+
{ url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" },
|
|
196
|
+
{ url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" },
|
|
197
|
+
{ url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" },
|
|
198
|
+
{ url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" },
|
|
199
|
+
{ url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" },
|
|
200
|
+
{ url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" },
|
|
201
|
+
{ url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" },
|
|
202
|
+
{ url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" },
|
|
203
|
+
{ url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" },
|
|
204
|
+
{ url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" },
|
|
205
|
+
{ url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" },
|
|
206
|
+
{ url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" },
|
|
207
|
+
{ url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" },
|
|
208
|
+
{ url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" },
|
|
209
|
+
{ url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" },
|
|
210
|
+
{ url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" },
|
|
211
|
+
{ url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" },
|
|
212
|
+
{ url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" },
|
|
213
|
+
{ url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" },
|
|
214
|
+
{ url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" },
|
|
215
|
+
{ url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" },
|
|
216
|
+
{ url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" },
|
|
217
|
+
{ url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" },
|
|
218
|
+
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
[[package]]
|
|
222
|
+
name = "typing-extensions"
|
|
223
|
+
version = "4.15.0"
|
|
224
|
+
source = { registry = "https://pypi.org/simple" }
|
|
225
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
|
226
|
+
wheels = [
|
|
227
|
+
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
|
228
|
+
]
|