inspect-swe 0.2.1__py3-none-any.whl → 0.2.3__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.
- inspect_swe/__init__.py +3 -1
- inspect_swe/_claude_code/claude_code.py +35 -4
- inspect_swe/_claude_code/install/__init__.py +0 -0
- inspect_swe/_claude_code/install/cache.py +58 -0
- inspect_swe/_claude_code/install/download.py +111 -0
- inspect_swe/_claude_code/install/install.py +58 -0
- inspect_swe/_util/__init__.py +0 -0
- inspect_swe/_util/_async.py +54 -0
- inspect_swe/_util/appdirs.py +20 -0
- inspect_swe/_util/checksum.py +6 -0
- inspect_swe/_util/constants.py +1 -0
- inspect_swe/_util/download.py +12 -0
- inspect_swe/_util/platform.py +15 -0
- inspect_swe/_util/sandbox.py +59 -0
- inspect_swe/_util/trace.py +7 -0
- inspect_swe/_version.py +2 -2
- {inspect_swe-0.2.1.dist-info → inspect_swe-0.2.3.dist-info}/METADATA +6 -1
- inspect_swe-0.2.3.dist-info/RECORD +24 -0
- inspect_swe/_claude_code/install_claude.py +0 -341
- inspect_swe-0.2.1.dist-info/RECORD +0 -12
- {inspect_swe-0.2.1.dist-info → inspect_swe-0.2.3.dist-info}/WHEEL +0 -0
- {inspect_swe-0.2.1.dist-info → inspect_swe-0.2.3.dist-info}/entry_points.txt +0 -0
- {inspect_swe-0.2.1.dist-info → inspect_swe-0.2.3.dist-info}/licenses/LICENSE +0 -0
inspect_swe/__init__.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
from ._claude_code.claude_code import claude_code
|
2
|
+
from ._claude_code.install.download import download_claude_code
|
3
|
+
from ._util.sandbox import SandboxPlatform
|
2
4
|
|
3
5
|
try:
|
4
6
|
from ._version import __version__
|
@@ -6,4 +8,4 @@ except ImportError:
|
|
6
8
|
__version__ = "unknown"
|
7
9
|
|
8
10
|
|
9
|
-
__all__ = ["claude_code", "__version__"]
|
11
|
+
__all__ = ["claude_code", "download_claude_code", "SandboxPlatform", "__version__"]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import Literal
|
2
|
+
|
1
3
|
from inspect_ai.agent import (
|
2
4
|
Agent,
|
3
5
|
AgentState,
|
@@ -5,16 +7,44 @@ from inspect_ai.agent import (
|
|
5
7
|
sandbox_agent_bridge,
|
6
8
|
)
|
7
9
|
from inspect_ai.model import ChatMessageSystem, ChatMessageUser
|
8
|
-
from inspect_ai.util import sandbox
|
10
|
+
from inspect_ai.util import sandbox as sandbox_env
|
11
|
+
|
12
|
+
from inspect_swe._claude_code.install.install import ensure_claude_code_installed
|
9
13
|
|
10
14
|
|
11
15
|
@agent
|
12
|
-
def claude_code(
|
16
|
+
def claude_code(
|
17
|
+
version: Literal["auto", "sandbox", "stable", "latest"] | str = "auto",
|
18
|
+
user: str | None = None,
|
19
|
+
sandbox: str | None = None,
|
20
|
+
) -> Agent:
|
21
|
+
"""Claude Code agent.
|
22
|
+
|
23
|
+
Agent that uses [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) running in a sandbox.
|
24
|
+
|
25
|
+
The agent can either use a version of Claude Code installed in the sandbox, or can download a version and install it in the sandbox (see docs on `version` option below for details).
|
26
|
+
|
27
|
+
Args:
|
28
|
+
version: Version of claude code to use. One of:
|
29
|
+
- "auto": Use any available version of claude code in the sandbox, otherwise download the current stable version.
|
30
|
+
- "sandbox": Use the version of claude code in the sandbox (raises `RuntimeError` if claude is not available in the sandbox)
|
31
|
+
- "stable": Download and use the current stable version of claude code.
|
32
|
+
- "latest": Download and use the very latest version of claude code.
|
33
|
+
- "x.x.x": Download and use a specific version of claude code.
|
34
|
+
user: User to execute claude code with.
|
35
|
+
sandbox: Optional sandbox environment name.
|
36
|
+
"""
|
37
|
+
|
13
38
|
async def execute(state: AgentState) -> AgentState:
|
14
39
|
async with sandbox_agent_bridge(state) as bridge:
|
40
|
+
# ensure claude is installed and get binary location
|
41
|
+
claude_binary = await ensure_claude_code_installed(
|
42
|
+
version, user, sandbox_env(sandbox)
|
43
|
+
)
|
44
|
+
|
15
45
|
# base options
|
16
46
|
cmd = [
|
17
|
-
|
47
|
+
claude_binary,
|
18
48
|
"--print", # run without interactions
|
19
49
|
"--dangerously-skip-permissions",
|
20
50
|
"--model", # use current inspect model
|
@@ -35,7 +65,7 @@ def claude_code() -> Agent:
|
|
35
65
|
cmd.append(prompt)
|
36
66
|
|
37
67
|
# execute the agent
|
38
|
-
result = await sandbox
|
68
|
+
result = await sandbox_env(sandbox).exec(
|
39
69
|
cmd=cmd,
|
40
70
|
env={
|
41
71
|
"ANTHROPIC_BASE_URL": f"http://localhost:{bridge.port}",
|
@@ -44,6 +74,7 @@ def claude_code() -> Agent:
|
|
44
74
|
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
|
45
75
|
"IS_SANDBOX": "1",
|
46
76
|
},
|
77
|
+
user=user,
|
47
78
|
)
|
48
79
|
|
49
80
|
if result.success:
|
File without changes
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
from inspect_swe._util.sandbox import SandboxPlatform
|
4
|
+
|
5
|
+
from ..._util.appdirs import package_cache_dir
|
6
|
+
from ..._util.checksum import verify_checksum
|
7
|
+
|
8
|
+
|
9
|
+
def read_cached_claude_code_binary(
|
10
|
+
version: str, platform: SandboxPlatform, expected_checksum: str | None
|
11
|
+
) -> bytes | None:
|
12
|
+
# no cached binary
|
13
|
+
cache_path = _claude_code_cached_binary(version, platform)
|
14
|
+
if not cache_path.exists():
|
15
|
+
return None
|
16
|
+
|
17
|
+
# read binary
|
18
|
+
with open(cache_path, "rb") as f:
|
19
|
+
binary_data = f.read()
|
20
|
+
|
21
|
+
if expected_checksum is None or verify_checksum(binary_data, expected_checksum):
|
22
|
+
cache_path.touch()
|
23
|
+
return binary_data
|
24
|
+
else:
|
25
|
+
cache_path.unlink()
|
26
|
+
return None
|
27
|
+
|
28
|
+
|
29
|
+
def write_cached_claude_code_binary(
|
30
|
+
binary_data: bytes, version: str, platform: SandboxPlatform
|
31
|
+
) -> None:
|
32
|
+
binary_path = _claude_code_cached_binary(version, platform)
|
33
|
+
|
34
|
+
with open(binary_path, "wb") as f:
|
35
|
+
f.write(binary_data)
|
36
|
+
|
37
|
+
_cleanup_claude_code_binary_cache(keep_count=3)
|
38
|
+
|
39
|
+
|
40
|
+
def _cleanup_claude_code_binary_cache(keep_count: int = 5) -> None:
|
41
|
+
# get all cached binaries
|
42
|
+
cache_files = list(_claude_code_cached_binary_dir().glob("claude-*"))
|
43
|
+
if len(cache_files) <= keep_count:
|
44
|
+
return
|
45
|
+
|
46
|
+
# remove oldest
|
47
|
+
cache_files.sort(key=lambda f: f.stat().st_atime)
|
48
|
+
files_to_remove = cache_files[:-keep_count]
|
49
|
+
for file_path in files_to_remove:
|
50
|
+
file_path.unlink()
|
51
|
+
|
52
|
+
|
53
|
+
def _claude_code_cached_binary_dir() -> Path:
|
54
|
+
return package_cache_dir("claude-code-downloads")
|
55
|
+
|
56
|
+
|
57
|
+
def _claude_code_cached_binary(version: str, platform: SandboxPlatform) -> Path:
|
58
|
+
return _claude_code_cached_binary_dir() / f"claude-{version}-{platform}"
|
@@ -0,0 +1,111 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Literal
|
3
|
+
|
4
|
+
from pydantic import BaseModel
|
5
|
+
|
6
|
+
from ..._util._async import run_coroutine
|
7
|
+
from ..._util.checksum import verify_checksum
|
8
|
+
from ..._util.download import download_file, download_text_file
|
9
|
+
from ..._util.sandbox import SandboxPlatform
|
10
|
+
from ..._util.trace import trace
|
11
|
+
from .cache import (
|
12
|
+
read_cached_claude_code_binary,
|
13
|
+
write_cached_claude_code_binary,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
def download_claude_code(
|
18
|
+
version: Literal["stable", "latest"] | str, platform: SandboxPlatform
|
19
|
+
) -> None:
|
20
|
+
"""Download Claude Code.
|
21
|
+
|
22
|
+
Download a version of Claude Code. This version will be added to the cache of downloaded versions (which retains the 5 most recently downloaded versions).
|
23
|
+
|
24
|
+
Use this if you need to ensure that a specific version of Claude Code is downloaded in advance (e.g. if you are going to run your evaluations offline). After downloading, explicit requests for the downloaded version (e.g. `claude_code(version="1.0.98")`) will not require network access.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
version: Version to download ("stable", "latest", or an explicit version number).
|
28
|
+
platform: Target platform ("linux-x64", "linux-arm64", "linux-x64-musl", or "linux-arm64-musl")
|
29
|
+
"""
|
30
|
+
run_coroutine(download_claude_code_async(version, platform))
|
31
|
+
|
32
|
+
|
33
|
+
async def download_claude_code_async(
|
34
|
+
version: Literal["stable", "latest"] | str, platform: SandboxPlatform
|
35
|
+
) -> bytes:
|
36
|
+
# determine version and checksum
|
37
|
+
gcs_bucket = await _claude_code_gcs_bucket()
|
38
|
+
version = await _claude_code_version(gcs_bucket, version)
|
39
|
+
manifest = await _claude_code_manifest(gcs_bucket, version)
|
40
|
+
expected_checksum = _checksum_for_platform(manifest, platform)
|
41
|
+
|
42
|
+
# check the cache
|
43
|
+
binary_data = read_cached_claude_code_binary(version, platform, expected_checksum)
|
44
|
+
if binary_data is None:
|
45
|
+
# not in cache, download and verify checksum
|
46
|
+
binary_url = f"{gcs_bucket}/{version}/{platform}/claude"
|
47
|
+
binary_data = await download_file(binary_url)
|
48
|
+
if not verify_checksum(binary_data, expected_checksum):
|
49
|
+
raise ValueError("Checksum verification failed")
|
50
|
+
|
51
|
+
# save to cache
|
52
|
+
write_cached_claude_code_binary(binary_data, version, platform)
|
53
|
+
|
54
|
+
# trace
|
55
|
+
trace(f"Downloaded claude code binary: {version} ({platform})")
|
56
|
+
else:
|
57
|
+
trace(f"Used claude code binary from cache: {version} ({platform})")
|
58
|
+
|
59
|
+
# return data
|
60
|
+
return binary_data
|
61
|
+
|
62
|
+
|
63
|
+
async def _claude_code_gcs_bucket() -> str:
|
64
|
+
INSTALL_SCRIPT_URL = "https://claude.ai/install.sh"
|
65
|
+
script_content = await download_text_file(INSTALL_SCRIPT_URL)
|
66
|
+
pattern = r'GCS_BUCKET="(https://storage\.googleapis\.com/[^"]+)"'
|
67
|
+
match = re.search(pattern, script_content)
|
68
|
+
if match is not None:
|
69
|
+
gcs_bucket = match.group(1)
|
70
|
+
return gcs_bucket
|
71
|
+
else:
|
72
|
+
raise RuntimeError("Unable to determine GCS bucket for claude code.")
|
73
|
+
|
74
|
+
|
75
|
+
async def _claude_code_version(gcs_bucket: str, target: str) -> str:
|
76
|
+
# validate target
|
77
|
+
target_pattern = r"^(stable|latest|[0-9]+\.[0-9]+\.[0-9]+(-[^[:space:]]+)?)$"
|
78
|
+
if re.match(target_pattern, target) is None:
|
79
|
+
raise RuntimeError(
|
80
|
+
"Invalid version target (must be 'stable', 'latest', or a semver version number)"
|
81
|
+
)
|
82
|
+
|
83
|
+
# resolve target alias if required
|
84
|
+
if target in ["stable", "latest"]:
|
85
|
+
version_url = f"{gcs_bucket}/{target}"
|
86
|
+
version = await download_text_file(version_url)
|
87
|
+
return version
|
88
|
+
else:
|
89
|
+
return target
|
90
|
+
|
91
|
+
|
92
|
+
class PlatformInfo(BaseModel):
|
93
|
+
checksum: str
|
94
|
+
size: int
|
95
|
+
|
96
|
+
|
97
|
+
class Manifest(BaseModel):
|
98
|
+
version: str
|
99
|
+
platforms: dict[str, PlatformInfo]
|
100
|
+
|
101
|
+
|
102
|
+
async def _claude_code_manifest(gcs_bucket: str, version: str) -> Manifest:
|
103
|
+
manifest_url = f"{gcs_bucket}/{version}/manifest.json"
|
104
|
+
manifest_json = await download_text_file(manifest_url)
|
105
|
+
return Manifest.model_validate_json(manifest_json)
|
106
|
+
|
107
|
+
|
108
|
+
def _checksum_for_platform(manifest: Manifest, platform: SandboxPlatform) -> str:
|
109
|
+
if platform not in manifest.platforms:
|
110
|
+
raise RuntimeError(f"Platform '{platform}' not found in manifest.")
|
111
|
+
return manifest.platforms[platform].checksum
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from typing import Literal
|
2
|
+
|
3
|
+
from inspect_ai.util import SandboxEnvironment, concurrency
|
4
|
+
from inspect_ai.util import sandbox as sandbox_env
|
5
|
+
|
6
|
+
from inspect_swe._claude_code.install.cache import read_cached_claude_code_binary
|
7
|
+
from inspect_swe._util.trace import trace
|
8
|
+
|
9
|
+
from ..._util.sandbox import bash_command, detect_sandbox_platform, sandbox_exec
|
10
|
+
from .download import download_claude_code_async
|
11
|
+
|
12
|
+
|
13
|
+
async def ensure_claude_code_installed(
|
14
|
+
version: Literal["auto", "sandbox", "stable", "latest"] | str = "auto",
|
15
|
+
user: str | None = None,
|
16
|
+
sandbox: SandboxEnvironment | None = None,
|
17
|
+
) -> str:
|
18
|
+
# resolve sandbox
|
19
|
+
sandbox = sandbox or sandbox_env()
|
20
|
+
|
21
|
+
# look in the sandbox first if we need to
|
22
|
+
if version == "auto" or version == "sandbox":
|
23
|
+
result = await sandbox.exec(bash_command("which claude"), user=user)
|
24
|
+
if result.success:
|
25
|
+
claude_binary = result.stdout.strip()
|
26
|
+
trace(f"Using claude code installed in sandbox: {claude_binary}")
|
27
|
+
return claude_binary
|
28
|
+
|
29
|
+
# if version == "sandbox" and we don't find it that's an error
|
30
|
+
if version == "sandbox":
|
31
|
+
raise RuntimeError("unable to locate claude code in sandbox")
|
32
|
+
|
33
|
+
# otherwise set to "stable"
|
34
|
+
version = "stable"
|
35
|
+
|
36
|
+
# detect the sandbox target platform
|
37
|
+
platform = await detect_sandbox_platform(sandbox)
|
38
|
+
|
39
|
+
# use concurrency so multiple samples don't attempt the same download all at once
|
40
|
+
async with concurrency("claude-install", 1, visible=False):
|
41
|
+
# if a specific version is requested, first try to read it directly from the cache
|
42
|
+
if version not in ["stable", "latest"]:
|
43
|
+
claude_binary_bytes: bytes | None = read_cached_claude_code_binary(
|
44
|
+
version, platform, None
|
45
|
+
)
|
46
|
+
if claude_binary_bytes is not None:
|
47
|
+
trace(f"Used claude code binary from cache: {version} ({platform})")
|
48
|
+
|
49
|
+
# download the binary
|
50
|
+
if claude_binary_bytes is None:
|
51
|
+
claude_binary_bytes = await download_claude_code_async(version, platform)
|
52
|
+
|
53
|
+
# write it into the container and return it
|
54
|
+
claude_binary = f"/opt/claude-{version}-{platform}"
|
55
|
+
await sandbox.write_file(claude_binary, claude_binary_bytes)
|
56
|
+
await sandbox_exec(sandbox, f"chmod +x {claude_binary}")
|
57
|
+
await sandbox_exec(sandbox, f"{claude_binary} config list", user=user)
|
58
|
+
return claude_binary
|
File without changes
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import asyncio
|
2
|
+
from typing import Coroutine, Literal, TypeVar, cast
|
3
|
+
|
4
|
+
import nest_asyncio # type: ignore
|
5
|
+
import sniffio
|
6
|
+
|
7
|
+
from .platform import running_in_notebook
|
8
|
+
|
9
|
+
T = TypeVar("T")
|
10
|
+
|
11
|
+
|
12
|
+
def run_coroutine(coroutine: Coroutine[None, None, T]) -> T:
|
13
|
+
if current_async_backend() == "trio":
|
14
|
+
raise RuntimeError("run_coroutine cannot be used with trio")
|
15
|
+
|
16
|
+
if running_in_notebook():
|
17
|
+
init_nest_asyncio()
|
18
|
+
return asyncio.run(coroutine)
|
19
|
+
else:
|
20
|
+
try:
|
21
|
+
# this will throw if there is no running loop
|
22
|
+
asyncio.get_running_loop()
|
23
|
+
|
24
|
+
# initialiase nest_asyncio then we are clear to run
|
25
|
+
init_nest_asyncio()
|
26
|
+
return asyncio.run(coroutine)
|
27
|
+
|
28
|
+
except RuntimeError:
|
29
|
+
# No running event loop so we are clear to run
|
30
|
+
return asyncio.run(coroutine)
|
31
|
+
|
32
|
+
|
33
|
+
_initialised_nest_asyncio: bool = False
|
34
|
+
|
35
|
+
|
36
|
+
def init_nest_asyncio() -> None:
|
37
|
+
global _initialised_nest_asyncio
|
38
|
+
if not _initialised_nest_asyncio:
|
39
|
+
nest_asyncio.apply()
|
40
|
+
_initialised_nest_asyncio = True
|
41
|
+
|
42
|
+
|
43
|
+
def current_async_backend() -> Literal["asyncio", "trio"] | None:
|
44
|
+
try:
|
45
|
+
return _validate_backend(sniffio.current_async_library().lower())
|
46
|
+
except sniffio.AsyncLibraryNotFoundError:
|
47
|
+
return None
|
48
|
+
|
49
|
+
|
50
|
+
def _validate_backend(backend: str) -> Literal["asyncio", "trio"]:
|
51
|
+
if backend in ["asyncio", "trio"]:
|
52
|
+
return cast(Literal["asyncio", "trio"], backend)
|
53
|
+
else:
|
54
|
+
raise RuntimeError(f"Unknown async backend: {backend}")
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
from inspect_ai._util.constants import PKG_NAME
|
4
|
+
from platformdirs import user_cache_path, user_data_path
|
5
|
+
|
6
|
+
|
7
|
+
def package_data_dir(subdir: str | None) -> Path:
|
8
|
+
data_dir = user_data_path(PKG_NAME)
|
9
|
+
if subdir:
|
10
|
+
data_dir = data_dir / subdir
|
11
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
12
|
+
return data_dir
|
13
|
+
|
14
|
+
|
15
|
+
def package_cache_dir(subdir: str | None) -> Path:
|
16
|
+
cache_dir = user_cache_path(PKG_NAME)
|
17
|
+
if subdir:
|
18
|
+
cache_dir = cache_dir / subdir
|
19
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
20
|
+
return cache_dir
|
@@ -0,0 +1 @@
|
|
1
|
+
PKG_NAME = "inspect_swe"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import httpx
|
2
|
+
|
3
|
+
|
4
|
+
async def download_file(url: str) -> bytes:
|
5
|
+
async with httpx.AsyncClient() as client:
|
6
|
+
response = await client.get(url, follow_redirects=True)
|
7
|
+
response.raise_for_status()
|
8
|
+
return response.content
|
9
|
+
|
10
|
+
|
11
|
+
async def download_text_file(url: str) -> str:
|
12
|
+
return (await download_file(url)).decode("utf-8")
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from typing import no_type_check
|
2
|
+
|
3
|
+
|
4
|
+
@no_type_check
|
5
|
+
def running_in_notebook() -> bool:
|
6
|
+
try:
|
7
|
+
from IPython import get_ipython # type: ignore
|
8
|
+
|
9
|
+
if "IPKernelApp" not in get_ipython().config:
|
10
|
+
return False
|
11
|
+
except ImportError:
|
12
|
+
return False
|
13
|
+
except AttributeError:
|
14
|
+
return False
|
15
|
+
return True
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from typing import Literal, TypeAlias, cast
|
2
|
+
|
3
|
+
from inspect_ai.util import SandboxEnvironment
|
4
|
+
|
5
|
+
SandboxPlatform: TypeAlias = Literal[
|
6
|
+
"linux-x64", "linux-arm64", "linux-x64-musl", "linux-arm64-musl"
|
7
|
+
]
|
8
|
+
|
9
|
+
|
10
|
+
async def detect_sandbox_platform(sandbox: SandboxEnvironment) -> SandboxPlatform:
|
11
|
+
# Get OS
|
12
|
+
os_name = await sandbox_exec(sandbox, "uname -s")
|
13
|
+
if os_name == "Linux":
|
14
|
+
os_type = "linux"
|
15
|
+
else:
|
16
|
+
raise ValueError(f"Unsupported OS: {os_name}")
|
17
|
+
|
18
|
+
# Get architecture
|
19
|
+
arch = await sandbox_exec(sandbox, "uname -m")
|
20
|
+
if arch in ["x86_64", "amd64"]:
|
21
|
+
arch_type = "x64"
|
22
|
+
elif arch in ["arm64", "aarch64"]:
|
23
|
+
arch_type = "arm64"
|
24
|
+
else:
|
25
|
+
raise ValueError(f"Unsupported architecture: {arch}")
|
26
|
+
|
27
|
+
# Check for musl on Linux
|
28
|
+
if os_type == "linux":
|
29
|
+
# Check for musl libc
|
30
|
+
musl_check_cmd = (
|
31
|
+
"if [ -f /lib/libc.musl-x86_64.so.1 ] || "
|
32
|
+
"[ -f /lib/libc.musl-aarch64.so.1 ] || "
|
33
|
+
"ldd /bin/ls 2>&1 | grep -q musl; then "
|
34
|
+
"echo 'musl'; else echo 'glibc'; fi"
|
35
|
+
)
|
36
|
+
libc_type = await sandbox_exec(sandbox, musl_check_cmd)
|
37
|
+
if libc_type == "musl":
|
38
|
+
platform = f"linux-{arch_type}-musl"
|
39
|
+
else:
|
40
|
+
platform = f"linux-{arch_type}"
|
41
|
+
else:
|
42
|
+
platform = f"{os_type}-{arch_type}"
|
43
|
+
|
44
|
+
return cast(SandboxPlatform, platform)
|
45
|
+
|
46
|
+
|
47
|
+
def bash_command(cmd: str) -> list[str]:
|
48
|
+
return ["bash", "--login", "-c", cmd]
|
49
|
+
|
50
|
+
|
51
|
+
async def sandbox_exec(
|
52
|
+
sandbox: SandboxEnvironment, cmd: str, user: str | None = None
|
53
|
+
) -> str:
|
54
|
+
result = await sandbox.exec(bash_command(cmd), user=user)
|
55
|
+
if not result.success:
|
56
|
+
raise RuntimeError(
|
57
|
+
f"Error executing sandbox command {','.join(cmd)}: {result.stderr}"
|
58
|
+
)
|
59
|
+
return result.stdout.strip()
|
inspect_swe/_version.py
CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
28
28
|
commit_id: COMMIT_ID
|
29
29
|
__commit_id__: COMMIT_ID
|
30
30
|
|
31
|
-
__version__ = version = '0.2.
|
32
|
-
__version_tuple__ = version_tuple = (0, 2,
|
31
|
+
__version__ = version = '0.2.3'
|
32
|
+
__version_tuple__ = version_tuple = (0, 2, 3)
|
33
33
|
|
34
34
|
__commit_id__ = commit_id = None
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: inspect_swe
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.3
|
4
4
|
Summary: Software engineering agents for Inspect AI.
|
5
5
|
Project-URL: Documentation, https://meridianlabs-ai.github.io/inspect_swe/
|
6
6
|
Project-URL: Source Code, https://github.com/meridianlabs-ai/inspect_swe
|
@@ -9,7 +9,12 @@ Author: Meridian Labs
|
|
9
9
|
License: MIT License
|
10
10
|
License-File: LICENSE
|
11
11
|
Requires-Python: >=3.10
|
12
|
+
Requires-Dist: httpx
|
12
13
|
Requires-Dist: inspect-ai>=0.3.125
|
14
|
+
Requires-Dist: nest-asyncio
|
15
|
+
Requires-Dist: platformdirs
|
16
|
+
Requires-Dist: pydantic>=2.11.4
|
17
|
+
Requires-Dist: sniffio
|
13
18
|
Requires-Dist: typing-extensions>=4.9.0
|
14
19
|
Provides-Extra: dev
|
15
20
|
Requires-Dist: anthropic; extra == 'dev'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
inspect_swe/__init__.py,sha256=Jg2VYr_eK8_fOXA4Oj0UAQj-g-RxDJuXrIhxKhassko,335
|
2
|
+
inspect_swe/_registry.py,sha256=jM37ysrY39Ufd67GRKbiwfSViOLlm-82lm_JEaWKshw,97
|
3
|
+
inspect_swe/_version.py,sha256=kBRz0P2plw1eVdIpt70W6m1LMbEIhLY3RyOfVGdubaI,704
|
4
|
+
inspect_swe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
inspect_swe/_claude_code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
inspect_swe/_claude_code/claude_code.py,sha256=YfxNLgohMMhAohLdclgGyLsfcjocwgmMyOxl2-HlepA,3297
|
7
|
+
inspect_swe/_claude_code/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
inspect_swe/_claude_code/install/cache.py,sha256=k08bCxGq-iYVpO16LNQhPjxTM9p2iecpqMjqYd2WBss,1708
|
9
|
+
inspect_swe/_claude_code/install/download.py,sha256=QKlFuDqCV55coTumIjyTXt2MU-vUQg8qPL3z3LHIUq8,4132
|
10
|
+
inspect_swe/_claude_code/install/install.py,sha256=cJP2JOUZNfPphz0eWbzrY7ULjSUU_SbSlPy3QecBltw,2430
|
11
|
+
inspect_swe/_util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
inspect_swe/_util/_async.py,sha256=cL8_Smmj2Es41TefceGDYLyVaO7gZ56VJcA4oByrWfQ,1520
|
13
|
+
inspect_swe/_util/appdirs.py,sha256=V3o1ERdSYLjKP-m4O1T_Hvkx0UsP2HdfvsshLSQgP6E,562
|
14
|
+
inspect_swe/_util/checksum.py,sha256=i-_GhtgCFd5eFj3PPJiGSCHDhZdPcIPNwiqddX93Sls,186
|
15
|
+
inspect_swe/_util/constants.py,sha256=xKvGgaJ0MwNbdzaken5HMbxYyKBEw_3VrBwCgkvAIWo,25
|
16
|
+
inspect_swe/_util/download.py,sha256=cCUau4ZBOKezpotJV5-v3JY_5CuYDZ-VcWlLf_EyNL0,340
|
17
|
+
inspect_swe/_util/platform.py,sha256=wm4efIFfdyTeaV2oxOXVvYl1u22MHX3jQMERHJMgv7A,339
|
18
|
+
inspect_swe/_util/sandbox.py,sha256=RixiEY1asFHa8HTsAHAxYXcPL-mUMgprQke1-TRbWYE,1812
|
19
|
+
inspect_swe/_util/trace.py,sha256=mFHmBKn2F8iJP9PpTHaCseMHnTMz3ErRx6RCKV83rZk,139
|
20
|
+
inspect_swe-0.2.3.dist-info/METADATA,sha256=yod5MyJGNjnpnlPCPczXyXMfx5BXhBrHJDoIkcTGpDI,1658
|
21
|
+
inspect_swe-0.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
22
|
+
inspect_swe-0.2.3.dist-info/entry_points.txt,sha256=OzpvUhd7M3T2Rog4MjwJAxIKeX5ljiR0mVYM9GefBKg,49
|
23
|
+
inspect_swe-0.2.3.dist-info/licenses/LICENSE,sha256=Hi3UDcbD6yCKZ1mcgt7pprzSG0rDEnSrbrm3XinyiDA,1070
|
24
|
+
inspect_swe-0.2.3.dist-info/RECORD,,
|
@@ -1,341 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
|
3
|
-
import hashlib
|
4
|
-
import json
|
5
|
-
import os
|
6
|
-
import re
|
7
|
-
import subprocess
|
8
|
-
import tempfile
|
9
|
-
import urllib.request
|
10
|
-
from pathlib import Path
|
11
|
-
from typing import Optional, cast
|
12
|
-
|
13
|
-
# Constants
|
14
|
-
INSTALL_SCRIPT_URL = "https://claude.ai/install.sh"
|
15
|
-
CACHE_DIR = Path.home() / ".claude" / "downloads"
|
16
|
-
# Fallback GCS bucket in case we can't fetch from install.sh
|
17
|
-
FALLBACK_GCS_BUCKET = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases"
|
18
|
-
|
19
|
-
|
20
|
-
def run_docker_exec(container_name: str, command: str) -> str:
|
21
|
-
"""Execute a command in the Docker container and return output."""
|
22
|
-
cmd = ["docker", "exec", container_name, "bash", "-c", command]
|
23
|
-
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
24
|
-
return result.stdout.strip()
|
25
|
-
|
26
|
-
|
27
|
-
def detect_platform(container_name: str) -> str:
|
28
|
-
"""Detect the platform (OS and architecture) of the container."""
|
29
|
-
# Get OS
|
30
|
-
os_name = run_docker_exec(container_name, "uname -s")
|
31
|
-
if os_name == "Darwin":
|
32
|
-
os_type = "darwin"
|
33
|
-
elif os_name == "Linux":
|
34
|
-
os_type = "linux"
|
35
|
-
else:
|
36
|
-
raise ValueError(f"Unsupported OS: {os_name}")
|
37
|
-
|
38
|
-
# Get architecture
|
39
|
-
arch = run_docker_exec(container_name, "uname -m")
|
40
|
-
if arch in ["x86_64", "amd64"]:
|
41
|
-
arch_type = "x64"
|
42
|
-
elif arch in ["arm64", "aarch64"]:
|
43
|
-
arch_type = "arm64"
|
44
|
-
else:
|
45
|
-
raise ValueError(f"Unsupported architecture: {arch}")
|
46
|
-
|
47
|
-
# Check for musl on Linux
|
48
|
-
if os_type == "linux":
|
49
|
-
# Check for musl libc
|
50
|
-
musl_check_cmd = (
|
51
|
-
"if [ -f /lib/libc.musl-x86_64.so.1 ] || "
|
52
|
-
"[ -f /lib/libc.musl-aarch64.so.1 ] || "
|
53
|
-
"ldd /bin/ls 2>&1 | grep -q musl; then "
|
54
|
-
"echo 'musl'; else echo 'glibc'; fi"
|
55
|
-
)
|
56
|
-
libc_type = run_docker_exec(container_name, musl_check_cmd)
|
57
|
-
if libc_type == "musl":
|
58
|
-
platform = f"linux-{arch_type}-musl"
|
59
|
-
else:
|
60
|
-
platform = f"linux-{arch_type}"
|
61
|
-
else:
|
62
|
-
platform = f"{os_type}-{arch_type}"
|
63
|
-
|
64
|
-
return platform
|
65
|
-
|
66
|
-
|
67
|
-
def download_file(url: str) -> bytes:
|
68
|
-
"""Download a file from the given URL and return its contents."""
|
69
|
-
with urllib.request.urlopen(url) as response:
|
70
|
-
return cast(bytes, response.read())
|
71
|
-
|
72
|
-
|
73
|
-
def get_gcs_bucket_from_install_script() -> str:
|
74
|
-
"""Fetch the install.sh script and extract the GCS_BUCKET URL.
|
75
|
-
|
76
|
-
Falls back to hardcoded URL if extraction fails.
|
77
|
-
"""
|
78
|
-
try:
|
79
|
-
print("Fetching install script to discover GCS bucket...")
|
80
|
-
script_content = download_file(INSTALL_SCRIPT_URL).decode("utf-8")
|
81
|
-
|
82
|
-
# Look for GCS_BUCKET= line in the script
|
83
|
-
# Pattern matches: GCS_BUCKET="https://storage.googleapis.com/..."
|
84
|
-
pattern = r'GCS_BUCKET="(https://storage\.googleapis\.com/[^"]+)"'
|
85
|
-
match = re.search(pattern, script_content)
|
86
|
-
|
87
|
-
if match:
|
88
|
-
gcs_bucket = match.group(1)
|
89
|
-
print(f"Discovered GCS bucket: {gcs_bucket}")
|
90
|
-
return gcs_bucket
|
91
|
-
else:
|
92
|
-
print("Could not extract GCS bucket from install script, using fallback")
|
93
|
-
return FALLBACK_GCS_BUCKET
|
94
|
-
|
95
|
-
except Exception as e:
|
96
|
-
print(f"Error fetching install script: {e}, using fallback")
|
97
|
-
return FALLBACK_GCS_BUCKET
|
98
|
-
|
99
|
-
|
100
|
-
def validate_target(target: str) -> bool:
|
101
|
-
"""Validate the target parameter format."""
|
102
|
-
pattern = r"^(stable|latest|[0-9]+\.[0-9]+\.[0-9]+(-[^[:space:]]+)?)$"
|
103
|
-
return bool(re.match(pattern, target))
|
104
|
-
|
105
|
-
|
106
|
-
def get_version(gcs_bucket: str, target: str = "stable") -> str:
|
107
|
-
"""Get the actual version to install based on the target."""
|
108
|
-
if not validate_target(target):
|
109
|
-
raise ValueError(f"Invalid target: {target}")
|
110
|
-
|
111
|
-
# Always download stable version first (it has the most up-to-date installer)
|
112
|
-
stable_url = f"{gcs_bucket}/stable"
|
113
|
-
stable_version = download_file(stable_url).decode("utf-8").strip()
|
114
|
-
|
115
|
-
if target == "stable" or target == stable_version:
|
116
|
-
return stable_version
|
117
|
-
elif target == "latest":
|
118
|
-
# For latest, we'd need to check the latest version
|
119
|
-
# For now, we'll use stable as the implementation
|
120
|
-
return stable_version
|
121
|
-
else:
|
122
|
-
# Specific version requested
|
123
|
-
return target
|
124
|
-
|
125
|
-
|
126
|
-
def get_checksum_from_manifest(manifest_json: str, platform: str) -> str:
|
127
|
-
"""Extract the checksum for the given platform from the manifest."""
|
128
|
-
manifest = json.loads(manifest_json)
|
129
|
-
|
130
|
-
if "platforms" not in manifest:
|
131
|
-
raise ValueError("Invalid manifest: missing platforms")
|
132
|
-
|
133
|
-
if platform not in manifest["platforms"]:
|
134
|
-
raise ValueError(f"Platform {platform} not found in manifest")
|
135
|
-
|
136
|
-
checksum = manifest["platforms"][platform].get("checksum")
|
137
|
-
|
138
|
-
if not checksum or not re.match(r"^[a-f0-9]{64}$", checksum):
|
139
|
-
raise ValueError(f"Invalid checksum for platform {platform}")
|
140
|
-
|
141
|
-
return str(checksum)
|
142
|
-
|
143
|
-
|
144
|
-
def verify_checksum(data: bytes, expected_checksum: str) -> bool:
|
145
|
-
"""Verify the SHA256 checksum of the data."""
|
146
|
-
actual_checksum = hashlib.sha256(data).hexdigest()
|
147
|
-
return actual_checksum == expected_checksum
|
148
|
-
|
149
|
-
|
150
|
-
def get_cached_binary_path(version: str, platform: str) -> Path:
|
151
|
-
"""Get the path where a binary would be cached."""
|
152
|
-
return CACHE_DIR / f"claude-{version}-{platform}"
|
153
|
-
|
154
|
-
|
155
|
-
def get_cached_binary(
|
156
|
-
version: str, platform: str, expected_checksum: str
|
157
|
-
) -> Optional[bytes]:
|
158
|
-
"""
|
159
|
-
Check if we have a cached binary and verify its checksum.
|
160
|
-
|
161
|
-
Returns the binary data if valid, None otherwise.
|
162
|
-
"""
|
163
|
-
cache_path = get_cached_binary_path(version, platform)
|
164
|
-
|
165
|
-
if not cache_path.exists():
|
166
|
-
return None
|
167
|
-
|
168
|
-
try:
|
169
|
-
with open(cache_path, "rb") as f:
|
170
|
-
binary_data = f.read()
|
171
|
-
|
172
|
-
# Verify the cached binary still has the correct checksum
|
173
|
-
if verify_checksum(binary_data, expected_checksum):
|
174
|
-
# Update access time so this file is considered "recently used"
|
175
|
-
cache_path.touch()
|
176
|
-
print(f"Using cached binary from {cache_path}")
|
177
|
-
return binary_data
|
178
|
-
else:
|
179
|
-
print("Cached binary checksum mismatch, will re-download")
|
180
|
-
cache_path.unlink() # Remove invalid cache file
|
181
|
-
return None
|
182
|
-
except Exception as e:
|
183
|
-
print(f"Error reading cached binary: {e}")
|
184
|
-
return None
|
185
|
-
|
186
|
-
|
187
|
-
def cleanup_old_cache_files(keep_count: int = 3) -> None:
|
188
|
-
"""
|
189
|
-
Remove old cached binaries, keeping only the most recent ones.
|
190
|
-
|
191
|
-
Keeps the specified number of most recently accessed files.
|
192
|
-
"""
|
193
|
-
if not CACHE_DIR.exists():
|
194
|
-
return
|
195
|
-
|
196
|
-
# Get all claude binary files in cache
|
197
|
-
cache_files = list(CACHE_DIR.glob("claude-*"))
|
198
|
-
|
199
|
-
if len(cache_files) <= keep_count:
|
200
|
-
return # Nothing to clean up
|
201
|
-
|
202
|
-
# Sort by access time (most recently accessed last)
|
203
|
-
cache_files.sort(key=lambda f: f.stat().st_atime)
|
204
|
-
|
205
|
-
# Remove oldest files
|
206
|
-
files_to_remove = cache_files[:-keep_count]
|
207
|
-
for file_path in files_to_remove:
|
208
|
-
try:
|
209
|
-
file_size_mb = file_path.stat().st_size / (1024 * 1024)
|
210
|
-
file_path.unlink()
|
211
|
-
print(f"Removed old cache file: {file_path.name} ({file_size_mb:.1f} MB)")
|
212
|
-
except Exception as e:
|
213
|
-
print(f"Error removing cache file {file_path}: {e}")
|
214
|
-
|
215
|
-
|
216
|
-
def save_to_cache(binary_data: bytes, version: str, platform: str) -> None:
|
217
|
-
"""Save a binary to the cache directory and clean up old files."""
|
218
|
-
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
219
|
-
cache_path = get_cached_binary_path(version, platform)
|
220
|
-
|
221
|
-
with open(cache_path, "wb") as f:
|
222
|
-
f.write(binary_data)
|
223
|
-
|
224
|
-
print(f"Saved binary to cache: {cache_path}")
|
225
|
-
|
226
|
-
# Clean up old cache files, keeping only the 3 most recent
|
227
|
-
cleanup_old_cache_files(keep_count=3)
|
228
|
-
|
229
|
-
|
230
|
-
def transfer_binary(container_name: str, binary_data: bytes, target_path: str) -> None:
|
231
|
-
"""Transfer binary data to the container."""
|
232
|
-
# Use a temporary file and docker cp
|
233
|
-
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
234
|
-
tmp_file.write(binary_data)
|
235
|
-
tmp_file_path = tmp_file.name
|
236
|
-
|
237
|
-
try:
|
238
|
-
# Copy file to container
|
239
|
-
subprocess.run(
|
240
|
-
["docker", "cp", tmp_file_path, f"{container_name}:{target_path}"],
|
241
|
-
check=True,
|
242
|
-
)
|
243
|
-
finally:
|
244
|
-
# Clean up temporary file
|
245
|
-
os.unlink(tmp_file_path)
|
246
|
-
|
247
|
-
|
248
|
-
def install_claude(container_name: str, binary_path: str) -> None:
|
249
|
-
"""Install claude binary and verify it works."""
|
250
|
-
# Copy binary to /usr/local/bin for system-wide access
|
251
|
-
run_docker_exec(container_name, f"cp {binary_path} /usr/local/bin/claude")
|
252
|
-
run_docker_exec(container_name, "chmod +x /usr/local/bin/claude")
|
253
|
-
|
254
|
-
# Clean up the temporary binary
|
255
|
-
run_docker_exec(container_name, f"rm -f {binary_path}")
|
256
|
-
|
257
|
-
# Verify installation and initialize config
|
258
|
-
try:
|
259
|
-
# Check version
|
260
|
-
version_output = run_docker_exec(container_name, "claude --version")
|
261
|
-
print(f"Claude installed successfully: {version_output}")
|
262
|
-
|
263
|
-
# Initialize config files/directories by running config list
|
264
|
-
run_docker_exec(container_name, "claude config list")
|
265
|
-
print("Claude configuration initialized")
|
266
|
-
|
267
|
-
except subprocess.CalledProcessError as e:
|
268
|
-
print(f"Warning: Could not verify claude installation: {e}")
|
269
|
-
raise ValueError("Claude installation verification failed") from e
|
270
|
-
|
271
|
-
|
272
|
-
def main(container_name: str, target: str = "stable") -> None:
|
273
|
-
"""Main function to orchestrate the Claude installation."""
|
274
|
-
print(f"Installing Claude Code in container: {container_name}")
|
275
|
-
print(f"Target: {target}")
|
276
|
-
|
277
|
-
# Step 0: Get GCS bucket URL
|
278
|
-
gcs_bucket = get_gcs_bucket_from_install_script()
|
279
|
-
|
280
|
-
# Step 1: Detect platform
|
281
|
-
print("Detecting platform...")
|
282
|
-
platform = detect_platform(container_name)
|
283
|
-
print(f"Platform: {platform}")
|
284
|
-
|
285
|
-
# Step 2: Get version
|
286
|
-
print("Determining version...")
|
287
|
-
version = get_version(gcs_bucket, target)
|
288
|
-
print(f"Version: {version}")
|
289
|
-
|
290
|
-
# Step 3: Download and parse manifest
|
291
|
-
print("Downloading manifest...")
|
292
|
-
manifest_url = f"{gcs_bucket}/{version}/manifest.json"
|
293
|
-
manifest_json = download_file(manifest_url).decode("utf-8")
|
294
|
-
|
295
|
-
# Step 4: Get checksum for platform
|
296
|
-
print("Extracting checksum...")
|
297
|
-
expected_checksum = get_checksum_from_manifest(manifest_json, platform)
|
298
|
-
|
299
|
-
# Step 5: Check cache or download binary
|
300
|
-
binary_data = get_cached_binary(version, platform, expected_checksum)
|
301
|
-
|
302
|
-
if binary_data is None:
|
303
|
-
# Not in cache or invalid, need to download
|
304
|
-
print(f"Downloading Claude binary for {platform}...")
|
305
|
-
binary_url = f"{gcs_bucket}/{version}/{platform}/claude"
|
306
|
-
binary_data = download_file(binary_url)
|
307
|
-
|
308
|
-
# Step 6: Verify checksum
|
309
|
-
print("Verifying checksum...")
|
310
|
-
if not verify_checksum(binary_data, expected_checksum):
|
311
|
-
raise ValueError("Checksum verification failed")
|
312
|
-
print("Checksum verified successfully")
|
313
|
-
|
314
|
-
# Save to cache for future use
|
315
|
-
save_to_cache(binary_data, version, platform)
|
316
|
-
else:
|
317
|
-
print("Checksum already verified for cached binary")
|
318
|
-
|
319
|
-
# Step 7: Transfer binary to container
|
320
|
-
print("Transferring binary to container...")
|
321
|
-
binary_path = f"/tmp/claude-{version}-{platform}"
|
322
|
-
transfer_binary(container_name, binary_data, binary_path)
|
323
|
-
|
324
|
-
# Step 8: Install
|
325
|
-
print("Installing Claude Code...")
|
326
|
-
install_claude(container_name, binary_path)
|
327
|
-
|
328
|
-
print("\n✅ Installation complete!")
|
329
|
-
|
330
|
-
|
331
|
-
if __name__ == "__main__":
|
332
|
-
# Test code - replace with your actual container name
|
333
|
-
test_container = "inspect-intervention-izedw74-default-1"
|
334
|
-
|
335
|
-
# You can test with different targets
|
336
|
-
# main(test_container, "stable")
|
337
|
-
# main(test_container, "latest")
|
338
|
-
# main(test_container, "1.0.0")
|
339
|
-
|
340
|
-
# Default test
|
341
|
-
main(test_container, "stable")
|
@@ -1,12 +0,0 @@
|
|
1
|
-
inspect_swe/__init__.py,sha256=6F52dddUoPvJA7RugtyDaswUqliDGgeaTK_OXWplvI0,185
|
2
|
-
inspect_swe/_registry.py,sha256=jM37ysrY39Ufd67GRKbiwfSViOLlm-82lm_JEaWKshw,97
|
3
|
-
inspect_swe/_version.py,sha256=vYqoJTG51NOUmYyL0xt8asRK8vUT4lGAdal_EZ59mvw,704
|
4
|
-
inspect_swe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
inspect_swe/_claude_code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
inspect_swe/_claude_code/claude_code.py,sha256=iE_-6Wv0m7hO1Tj-d21K8iZHgBIcTcfSqLHPVS1whMM,1788
|
7
|
-
inspect_swe/_claude_code/install_claude.py,sha256=g5nHIY-JVKDQFgm0IIhpCsCX5B6MadYj8-CtKpKU4YE,11796
|
8
|
-
inspect_swe-0.2.1.dist-info/METADATA,sha256=56Fjle-9IWwx-k0AHCbxPPH7443KE1JqdSX4_s_dFCc,1526
|
9
|
-
inspect_swe-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
-
inspect_swe-0.2.1.dist-info/entry_points.txt,sha256=OzpvUhd7M3T2Rog4MjwJAxIKeX5ljiR0mVYM9GefBKg,49
|
11
|
-
inspect_swe-0.2.1.dist-info/licenses/LICENSE,sha256=Hi3UDcbD6yCKZ1mcgt7pprzSG0rDEnSrbrm3XinyiDA,1070
|
12
|
-
inspect_swe-0.2.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|