acontext 0.1.2__py3-none-any.whl → 0.1.4__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.
- acontext/__init__.py +0 -8
- acontext/agent/__init__.py +2 -0
- acontext/agent/base.py +2 -1
- acontext/agent/disk.py +25 -18
- acontext/agent/prompts.py +96 -0
- acontext/agent/sandbox.py +532 -0
- acontext/agent/skill.py +35 -44
- acontext/agent/text_editor.py +436 -0
- acontext/async_client.py +6 -5
- acontext/client.py +6 -5
- acontext/client_types.py +2 -0
- acontext/resources/__init__.py +4 -8
- acontext/resources/async_disks.py +92 -0
- acontext/resources/async_sandboxes.py +85 -0
- acontext/resources/async_sessions.py +0 -41
- acontext/resources/async_skills.py +40 -0
- acontext/resources/async_users.py +2 -2
- acontext/resources/disks.py +131 -33
- acontext/resources/sandboxes.py +85 -0
- acontext/resources/sessions.py +0 -41
- acontext/resources/skills.py +40 -0
- acontext/resources/users.py +2 -2
- acontext/types/__init__.py +15 -22
- acontext/types/disk.py +6 -3
- acontext/types/sandbox.py +47 -0
- acontext/types/session.py +0 -16
- acontext/types/skill.py +11 -0
- acontext/types/tool.py +0 -6
- acontext/types/user.py +0 -1
- {acontext-0.1.2.dist-info → acontext-0.1.4.dist-info}/METADATA +1 -1
- acontext-0.1.4.dist-info/RECORD +41 -0
- acontext/resources/async_blocks.py +0 -164
- acontext/resources/async_spaces.py +0 -200
- acontext/resources/blocks.py +0 -163
- acontext/resources/spaces.py +0 -198
- acontext/types/block.py +0 -26
- acontext/types/space.py +0 -70
- acontext-0.1.2.dist-info/RECORD +0 -41
- {acontext-0.1.2.dist-info → acontext-0.1.4.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sandboxes endpoints (async).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .._utils import build_params
|
|
6
|
+
from ..client_types import AsyncRequesterProtocol
|
|
7
|
+
from ..types.sandbox import (
|
|
8
|
+
GetSandboxLogsOutput,
|
|
9
|
+
SandboxCommandOutput,
|
|
10
|
+
SandboxRuntimeInfo,
|
|
11
|
+
)
|
|
12
|
+
from ..types.tool import FlagResponse
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AsyncSandboxesAPI:
|
|
16
|
+
def __init__(self, requester: AsyncRequesterProtocol) -> None:
|
|
17
|
+
self._requester = requester
|
|
18
|
+
|
|
19
|
+
async def create(self) -> SandboxRuntimeInfo:
|
|
20
|
+
"""Create and start a new sandbox.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
SandboxRuntimeInfo containing the sandbox ID, status, and timestamps.
|
|
24
|
+
"""
|
|
25
|
+
data = await self._requester.request("POST", "/sandbox")
|
|
26
|
+
return SandboxRuntimeInfo.model_validate(data)
|
|
27
|
+
|
|
28
|
+
async def exec_command(
|
|
29
|
+
self,
|
|
30
|
+
*,
|
|
31
|
+
sandbox_id: str,
|
|
32
|
+
command: str,
|
|
33
|
+
timeout: float | None = None,
|
|
34
|
+
) -> SandboxCommandOutput:
|
|
35
|
+
"""Execute a shell command in the sandbox.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
sandbox_id: The UUID of the sandbox.
|
|
39
|
+
command: The shell command to execute.
|
|
40
|
+
timeout: Optional timeout in seconds for this command.
|
|
41
|
+
If not provided, uses the client's default timeout.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
SandboxCommandOutput containing stdout, stderr, and exit code.
|
|
45
|
+
"""
|
|
46
|
+
data = await self._requester.request(
|
|
47
|
+
"POST",
|
|
48
|
+
f"/sandbox/{sandbox_id}/exec",
|
|
49
|
+
json_data={"command": command},
|
|
50
|
+
timeout=timeout,
|
|
51
|
+
)
|
|
52
|
+
return SandboxCommandOutput.model_validate(data)
|
|
53
|
+
|
|
54
|
+
async def kill(self, sandbox_id: str) -> FlagResponse:
|
|
55
|
+
"""Kill a running sandbox.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
sandbox_id: The UUID of the sandbox to kill.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
FlagResponse with status and error message.
|
|
62
|
+
"""
|
|
63
|
+
data = await self._requester.request("DELETE", f"/sandbox/{sandbox_id}")
|
|
64
|
+
return FlagResponse.model_validate(data)
|
|
65
|
+
|
|
66
|
+
async def get_logs(
|
|
67
|
+
self,
|
|
68
|
+
*,
|
|
69
|
+
limit: int | None = None,
|
|
70
|
+
cursor: str | None = None,
|
|
71
|
+
time_desc: bool | None = None,
|
|
72
|
+
) -> GetSandboxLogsOutput:
|
|
73
|
+
"""Get sandbox logs for the project with cursor-based pagination.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
limit: Maximum number of logs to return (default 20, max 200).
|
|
77
|
+
cursor: Cursor for pagination. Use the cursor from the previous response to get the next page.
|
|
78
|
+
time_desc: Order by created_at descending if True, ascending if False (default False).
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
GetSandboxLogsOutput containing the list of sandbox logs and pagination information.
|
|
82
|
+
"""
|
|
83
|
+
params = build_params(limit=limit, cursor=cursor, time_desc=time_desc)
|
|
84
|
+
data = await self._requester.request("GET", "/sandbox/logs", params=params or None)
|
|
85
|
+
return GetSandboxLogsOutput.model_validate(data)
|
|
@@ -12,7 +12,6 @@ from ..types.session import (
|
|
|
12
12
|
EditStrategy,
|
|
13
13
|
GetMessagesOutput,
|
|
14
14
|
GetTasksOutput,
|
|
15
|
-
LearningStatus,
|
|
16
15
|
ListSessionsOutput,
|
|
17
16
|
Message,
|
|
18
17
|
MessageObservingStatus,
|
|
@@ -38,8 +37,6 @@ class AsyncSessionsAPI:
|
|
|
38
37
|
self,
|
|
39
38
|
*,
|
|
40
39
|
user: str | None = None,
|
|
41
|
-
space_id: str | None = None,
|
|
42
|
-
not_connected: bool | None = None,
|
|
43
40
|
limit: int | None = None,
|
|
44
41
|
cursor: str | None = None,
|
|
45
42
|
time_desc: bool | None = None,
|
|
@@ -48,8 +45,6 @@ class AsyncSessionsAPI:
|
|
|
48
45
|
|
|
49
46
|
Args:
|
|
50
47
|
user: Filter by user identifier. Defaults to None.
|
|
51
|
-
space_id: Filter sessions by space ID. Defaults to None.
|
|
52
|
-
not_connected: Filter sessions that are not connected to a space. Defaults to None.
|
|
53
48
|
limit: Maximum number of sessions to return. Defaults to None.
|
|
54
49
|
cursor: Cursor for pagination. Defaults to None.
|
|
55
50
|
time_desc: Order by created_at descending if True, ascending if False. Defaults to None.
|
|
@@ -60,11 +55,8 @@ class AsyncSessionsAPI:
|
|
|
60
55
|
params: dict[str, Any] = {}
|
|
61
56
|
if user:
|
|
62
57
|
params["user"] = user
|
|
63
|
-
if space_id:
|
|
64
|
-
params["space_id"] = space_id
|
|
65
58
|
params.update(
|
|
66
59
|
build_params(
|
|
67
|
-
not_connected=not_connected,
|
|
68
60
|
limit=limit,
|
|
69
61
|
cursor=cursor,
|
|
70
62
|
time_desc=time_desc,
|
|
@@ -77,7 +69,6 @@ class AsyncSessionsAPI:
|
|
|
77
69
|
self,
|
|
78
70
|
*,
|
|
79
71
|
user: str | None = None,
|
|
80
|
-
space_id: str | None = None,
|
|
81
72
|
disable_task_tracking: bool | None = None,
|
|
82
73
|
configs: Mapping[str, Any] | None = None,
|
|
83
74
|
) -> Session:
|
|
@@ -85,7 +76,6 @@ class AsyncSessionsAPI:
|
|
|
85
76
|
|
|
86
77
|
Args:
|
|
87
78
|
user: Optional user identifier string. Defaults to None.
|
|
88
|
-
space_id: Optional space ID to associate with the session. Defaults to None.
|
|
89
79
|
disable_task_tracking: Whether to disable task tracking for this session. Defaults to None (server default: False).
|
|
90
80
|
configs: Optional session configuration dictionary. Defaults to None.
|
|
91
81
|
|
|
@@ -95,8 +85,6 @@ class AsyncSessionsAPI:
|
|
|
95
85
|
payload: dict[str, Any] = {}
|
|
96
86
|
if user:
|
|
97
87
|
payload["user"] = user
|
|
98
|
-
if space_id:
|
|
99
|
-
payload["space_id"] = space_id
|
|
100
88
|
if disable_task_tracking is not None:
|
|
101
89
|
payload["disable_task_tracking"] = disable_task_tracking
|
|
102
90
|
if configs is not None:
|
|
@@ -141,18 +129,6 @@ class AsyncSessionsAPI:
|
|
|
141
129
|
data = await self._requester.request("GET", f"/session/{session_id}/configs")
|
|
142
130
|
return Session.model_validate(data)
|
|
143
131
|
|
|
144
|
-
async def connect_to_space(self, session_id: str, *, space_id: str) -> None:
|
|
145
|
-
"""Connect a session to a space.
|
|
146
|
-
|
|
147
|
-
Args:
|
|
148
|
-
session_id: The UUID of the session.
|
|
149
|
-
space_id: The UUID of the space to connect to.
|
|
150
|
-
"""
|
|
151
|
-
payload = {"space_id": space_id}
|
|
152
|
-
await self._requester.request(
|
|
153
|
-
"POST", f"/session/{session_id}/connect_to_space", json_data=payload
|
|
154
|
-
)
|
|
155
|
-
|
|
156
132
|
async def get_tasks(
|
|
157
133
|
self,
|
|
158
134
|
session_id: str,
|
|
@@ -382,23 +358,6 @@ class AsyncSessionsAPI:
|
|
|
382
358
|
data = await self._requester.request("POST", f"/session/{session_id}/flush")
|
|
383
359
|
return data # type: ignore
|
|
384
360
|
|
|
385
|
-
async def get_learning_status(self, session_id: str) -> LearningStatus:
|
|
386
|
-
"""Get learning status for a session.
|
|
387
|
-
|
|
388
|
-
Returns the count of space digested tasks and not space digested tasks.
|
|
389
|
-
If the session is not connected to a space, returns 0 and 0.
|
|
390
|
-
|
|
391
|
-
Args:
|
|
392
|
-
session_id: The UUID of the session.
|
|
393
|
-
|
|
394
|
-
Returns:
|
|
395
|
-
LearningStatus object containing space_digested_count and not_space_digested_count.
|
|
396
|
-
"""
|
|
397
|
-
data = await self._requester.request(
|
|
398
|
-
"GET", f"/session/{session_id}/get_learning_status"
|
|
399
|
-
)
|
|
400
|
-
return LearningStatus.model_validate(data)
|
|
401
|
-
|
|
402
361
|
async def get_token_counts(self, session_id: str) -> TokenCounts:
|
|
403
362
|
"""Get total token counts for all text and tool-call parts in a session.
|
|
404
363
|
|
|
@@ -9,6 +9,7 @@ from typing import Any, BinaryIO, cast
|
|
|
9
9
|
from .._utils import build_params
|
|
10
10
|
from ..client_types import AsyncRequesterProtocol
|
|
11
11
|
from ..types.skill import (
|
|
12
|
+
DownloadSkillToSandboxResp,
|
|
12
13
|
GetSkillFileResp,
|
|
13
14
|
ListSkillsOutput,
|
|
14
15
|
Skill,
|
|
@@ -133,3 +134,42 @@ class AsyncSkillsAPI:
|
|
|
133
134
|
|
|
134
135
|
data = await self._requester.request("GET", endpoint, params=params)
|
|
135
136
|
return GetSkillFileResp.model_validate(data)
|
|
137
|
+
|
|
138
|
+
async def download_to_sandbox(
|
|
139
|
+
self,
|
|
140
|
+
*,
|
|
141
|
+
skill_id: str,
|
|
142
|
+
sandbox_id: str,
|
|
143
|
+
) -> DownloadSkillToSandboxResp:
|
|
144
|
+
"""Download all files from a skill to a sandbox environment.
|
|
145
|
+
|
|
146
|
+
Files are placed at /skills/{skill_name}/.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
skill_id: The UUID of the skill to download.
|
|
150
|
+
sandbox_id: The UUID of the target sandbox.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
DownloadSkillToSandboxResp containing success status, the directory path
|
|
154
|
+
where the skill was installed, and the skill's name and description.
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
```python
|
|
158
|
+
result = await client.skills.download_to_sandbox(
|
|
159
|
+
skill_id="skill-uuid",
|
|
160
|
+
sandbox_id="sandbox-uuid"
|
|
161
|
+
)
|
|
162
|
+
print(f"Success: {result.success}")
|
|
163
|
+
print(f"Skill installed at: {result.dir_path}")
|
|
164
|
+
print(f"Skill name: {result.name}")
|
|
165
|
+
print(f"Description: {result.description}")
|
|
166
|
+
```
|
|
167
|
+
"""
|
|
168
|
+
payload: dict[str, Any] = {"sandbox_id": sandbox_id}
|
|
169
|
+
|
|
170
|
+
data = await self._requester.request(
|
|
171
|
+
"POST",
|
|
172
|
+
f"/agent_skills/{skill_id}/download_to_sandbox",
|
|
173
|
+
json_data=payload,
|
|
174
|
+
)
|
|
175
|
+
return DownloadSkillToSandboxResp.model_validate(data)
|
|
@@ -41,7 +41,7 @@ class AsyncUsersAPI:
|
|
|
41
41
|
identifier: The user identifier string.
|
|
42
42
|
|
|
43
43
|
Returns:
|
|
44
|
-
GetUserResourcesOutput containing counts for
|
|
44
|
+
GetUserResourcesOutput containing counts for Sessions, Disks, and Skills.
|
|
45
45
|
"""
|
|
46
46
|
data = await self._requester.request(
|
|
47
47
|
"GET", f"/user/{quote(identifier, safe='')}/resources"
|
|
@@ -49,7 +49,7 @@ class AsyncUsersAPI:
|
|
|
49
49
|
return GetUserResourcesOutput.model_validate(data)
|
|
50
50
|
|
|
51
51
|
async def delete(self, identifier: str) -> None:
|
|
52
|
-
"""Delete a user and cascade delete all associated resources (
|
|
52
|
+
"""Delete a user and cascade delete all associated resources (Session, Disk, Skill).
|
|
53
53
|
|
|
54
54
|
Args:
|
|
55
55
|
identifier: The user identifier string.
|
acontext/resources/disks.py
CHANGED
|
@@ -37,26 +37,28 @@ class DisksAPI:
|
|
|
37
37
|
time_desc: bool | None = None,
|
|
38
38
|
) -> ListDisksOutput:
|
|
39
39
|
"""List all disks in the project.
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
Args:
|
|
42
42
|
user: Filter by user identifier. Defaults to None.
|
|
43
43
|
limit: Maximum number of disks to return. Defaults to None.
|
|
44
44
|
cursor: Cursor for pagination. Defaults to None.
|
|
45
45
|
time_desc: Order by created_at descending if True, ascending if False. Defaults to None.
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
Returns:
|
|
48
48
|
ListDisksOutput containing the list of disks and pagination information.
|
|
49
49
|
"""
|
|
50
|
-
params = build_params(
|
|
50
|
+
params = build_params(
|
|
51
|
+
user=user, limit=limit, cursor=cursor, time_desc=time_desc
|
|
52
|
+
)
|
|
51
53
|
data = self._requester.request("GET", "/disk", params=params or None)
|
|
52
54
|
return ListDisksOutput.model_validate(data)
|
|
53
55
|
|
|
54
56
|
def create(self, *, user: str | None = None) -> Disk:
|
|
55
57
|
"""Create a new disk.
|
|
56
|
-
|
|
58
|
+
|
|
57
59
|
Args:
|
|
58
60
|
user: Optional user identifier string. Defaults to None.
|
|
59
|
-
|
|
61
|
+
|
|
60
62
|
Returns:
|
|
61
63
|
The created Disk object.
|
|
62
64
|
"""
|
|
@@ -68,7 +70,7 @@ class DisksAPI:
|
|
|
68
70
|
|
|
69
71
|
def delete(self, disk_id: str) -> None:
|
|
70
72
|
"""Delete a disk by its ID.
|
|
71
|
-
|
|
73
|
+
|
|
72
74
|
Args:
|
|
73
75
|
disk_id: The UUID of the disk to delete.
|
|
74
76
|
"""
|
|
@@ -83,20 +85,22 @@ class DiskArtifactsAPI:
|
|
|
83
85
|
self,
|
|
84
86
|
disk_id: str,
|
|
85
87
|
*,
|
|
86
|
-
file:
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
file: (
|
|
89
|
+
FileUpload
|
|
90
|
+
| tuple[str, BinaryIO | bytes]
|
|
91
|
+
| tuple[str, BinaryIO | bytes, str]
|
|
92
|
+
),
|
|
89
93
|
file_path: str | None = None,
|
|
90
94
|
meta: Mapping[str, Any] | None = None,
|
|
91
95
|
) -> Artifact:
|
|
92
96
|
"""Upload a file to create or update an artifact.
|
|
93
|
-
|
|
97
|
+
|
|
94
98
|
Args:
|
|
95
99
|
disk_id: The UUID of the disk.
|
|
96
100
|
file: The file to upload (FileUpload object or tuple format).
|
|
97
101
|
file_path: Directory path (not including filename), defaults to "/".
|
|
98
102
|
meta: Custom metadata as JSON-serializable dict, defaults to None.
|
|
99
|
-
|
|
103
|
+
|
|
100
104
|
Returns:
|
|
101
105
|
Artifact containing the created/updated artifact information.
|
|
102
106
|
"""
|
|
@@ -126,7 +130,7 @@ class DiskArtifactsAPI:
|
|
|
126
130
|
expire: int | None = None,
|
|
127
131
|
) -> GetArtifactResp:
|
|
128
132
|
"""Get an artifact by disk ID, file path, and filename.
|
|
129
|
-
|
|
133
|
+
|
|
130
134
|
Args:
|
|
131
135
|
disk_id: The UUID of the disk.
|
|
132
136
|
file_path: Directory path (not including filename).
|
|
@@ -134,7 +138,7 @@ class DiskArtifactsAPI:
|
|
|
134
138
|
with_public_url: Whether to include a presigned public URL. Defaults to None.
|
|
135
139
|
with_content: Whether to include file content. Defaults to None.
|
|
136
140
|
expire: URL expiration time in seconds. Defaults to None.
|
|
137
|
-
|
|
141
|
+
|
|
138
142
|
Returns:
|
|
139
143
|
GetArtifactResp containing the artifact and optionally public URL and content.
|
|
140
144
|
"""
|
|
@@ -145,7 +149,9 @@ class DiskArtifactsAPI:
|
|
|
145
149
|
with_content=with_content,
|
|
146
150
|
expire=expire,
|
|
147
151
|
)
|
|
148
|
-
data = self._requester.request(
|
|
152
|
+
data = self._requester.request(
|
|
153
|
+
"GET", f"/disk/{disk_id}/artifact", params=params
|
|
154
|
+
)
|
|
149
155
|
return GetArtifactResp.model_validate(data)
|
|
150
156
|
|
|
151
157
|
def update(
|
|
@@ -157,13 +163,13 @@ class DiskArtifactsAPI:
|
|
|
157
163
|
meta: Mapping[str, Any],
|
|
158
164
|
) -> UpdateArtifactResp:
|
|
159
165
|
"""Update an artifact's metadata.
|
|
160
|
-
|
|
166
|
+
|
|
161
167
|
Args:
|
|
162
168
|
disk_id: The UUID of the disk.
|
|
163
169
|
file_path: Directory path (not including filename).
|
|
164
170
|
filename: The filename of the artifact.
|
|
165
171
|
meta: Custom metadata as JSON-serializable dict.
|
|
166
|
-
|
|
172
|
+
|
|
167
173
|
Returns:
|
|
168
174
|
UpdateArtifactResp containing the updated artifact information.
|
|
169
175
|
"""
|
|
@@ -172,7 +178,9 @@ class DiskArtifactsAPI:
|
|
|
172
178
|
"file_path": full_path,
|
|
173
179
|
"meta": json.dumps(cast(Mapping[str, Any], meta)),
|
|
174
180
|
}
|
|
175
|
-
data = self._requester.request(
|
|
181
|
+
data = self._requester.request(
|
|
182
|
+
"PUT", f"/disk/{disk_id}/artifact", json_data=payload
|
|
183
|
+
)
|
|
176
184
|
return UpdateArtifactResp.model_validate(data)
|
|
177
185
|
|
|
178
186
|
def delete(
|
|
@@ -183,7 +191,7 @@ class DiskArtifactsAPI:
|
|
|
183
191
|
filename: str,
|
|
184
192
|
) -> None:
|
|
185
193
|
"""Delete an artifact by disk ID, file path, and filename.
|
|
186
|
-
|
|
194
|
+
|
|
187
195
|
Args:
|
|
188
196
|
disk_id: The UUID of the disk.
|
|
189
197
|
file_path: Directory path (not including filename).
|
|
@@ -200,18 +208,20 @@ class DiskArtifactsAPI:
|
|
|
200
208
|
path: str | None = None,
|
|
201
209
|
) -> ListArtifactsResp:
|
|
202
210
|
"""List artifacts in a disk at a specific path.
|
|
203
|
-
|
|
211
|
+
|
|
204
212
|
Args:
|
|
205
213
|
disk_id: The UUID of the disk.
|
|
206
214
|
path: Directory path to list. Defaults to None (root).
|
|
207
|
-
|
|
215
|
+
|
|
208
216
|
Returns:
|
|
209
217
|
ListArtifactsResp containing the list of artifacts.
|
|
210
218
|
"""
|
|
211
219
|
params: dict[str, Any] = {}
|
|
212
220
|
if path is not None:
|
|
213
221
|
params["path"] = path
|
|
214
|
-
data = self._requester.request(
|
|
222
|
+
data = self._requester.request(
|
|
223
|
+
"GET", f"/disk/{disk_id}/artifact/ls", params=params or None
|
|
224
|
+
)
|
|
215
225
|
return ListArtifactsResp.model_validate(data)
|
|
216
226
|
|
|
217
227
|
def grep_artifacts(
|
|
@@ -222,15 +232,15 @@ class DiskArtifactsAPI:
|
|
|
222
232
|
limit: int = 100,
|
|
223
233
|
) -> list[Artifact]:
|
|
224
234
|
"""Search artifact content using regex pattern.
|
|
225
|
-
|
|
235
|
+
|
|
226
236
|
Args:
|
|
227
237
|
disk_id: The disk ID to search in
|
|
228
238
|
query: Regex pattern to search for in file content
|
|
229
239
|
limit: Maximum number of results (default 100, max 1000)
|
|
230
|
-
|
|
240
|
+
|
|
231
241
|
Returns:
|
|
232
242
|
List of matching artifacts
|
|
233
|
-
|
|
243
|
+
|
|
234
244
|
Example:
|
|
235
245
|
```python
|
|
236
246
|
# Search for TODO comments in code
|
|
@@ -242,9 +252,7 @@ class DiskArtifactsAPI:
|
|
|
242
252
|
"""
|
|
243
253
|
params = build_params(query=query, limit=limit)
|
|
244
254
|
data = self._requester.request(
|
|
245
|
-
"GET",
|
|
246
|
-
f"/disk/{disk_id}/artifact/grep",
|
|
247
|
-
params=params
|
|
255
|
+
"GET", f"/disk/{disk_id}/artifact/grep", params=params
|
|
248
256
|
)
|
|
249
257
|
return TypeAdapter(list[Artifact]).validate_python(data)
|
|
250
258
|
|
|
@@ -256,15 +264,15 @@ class DiskArtifactsAPI:
|
|
|
256
264
|
limit: int = 100,
|
|
257
265
|
) -> list[Artifact]:
|
|
258
266
|
"""Search artifact paths using glob pattern.
|
|
259
|
-
|
|
267
|
+
|
|
260
268
|
Args:
|
|
261
269
|
disk_id: The disk ID to search in
|
|
262
270
|
query: Glob pattern (e.g., '**/*.py', '*.txt')
|
|
263
271
|
limit: Maximum number of results (default 100, max 1000)
|
|
264
|
-
|
|
272
|
+
|
|
265
273
|
Returns:
|
|
266
274
|
List of matching artifacts
|
|
267
|
-
|
|
275
|
+
|
|
268
276
|
Example:
|
|
269
277
|
```python
|
|
270
278
|
# Find all Python files
|
|
@@ -276,8 +284,98 @@ class DiskArtifactsAPI:
|
|
|
276
284
|
"""
|
|
277
285
|
params = build_params(query=query, limit=limit)
|
|
278
286
|
data = self._requester.request(
|
|
279
|
-
"GET",
|
|
280
|
-
f"/disk/{disk_id}/artifact/glob",
|
|
281
|
-
params=params
|
|
287
|
+
"GET", f"/disk/{disk_id}/artifact/glob", params=params
|
|
282
288
|
)
|
|
283
289
|
return TypeAdapter(list[Artifact]).validate_python(data)
|
|
290
|
+
|
|
291
|
+
def download_to_sandbox(
|
|
292
|
+
self,
|
|
293
|
+
disk_id: str,
|
|
294
|
+
*,
|
|
295
|
+
file_path: str,
|
|
296
|
+
filename: str,
|
|
297
|
+
sandbox_id: str,
|
|
298
|
+
sandbox_path: str,
|
|
299
|
+
) -> bool:
|
|
300
|
+
"""Download an artifact from disk storage to a sandbox environment.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
disk_id: The UUID of the disk containing the artifact.
|
|
304
|
+
file_path: Directory path of the artifact (not including filename).
|
|
305
|
+
filename: The filename of the artifact.
|
|
306
|
+
sandbox_id: The UUID of the target sandbox.
|
|
307
|
+
sandbox_path: Destination directory in the sandbox.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
True if the download was successful.
|
|
311
|
+
|
|
312
|
+
Example:
|
|
313
|
+
```python
|
|
314
|
+
success = client.disks.artifacts.download_to_sandbox(
|
|
315
|
+
disk_id="disk-uuid",
|
|
316
|
+
file_path="/documents/",
|
|
317
|
+
filename="report.pdf",
|
|
318
|
+
sandbox_id="sandbox-uuid",
|
|
319
|
+
sandbox_path="/home/user/"
|
|
320
|
+
)
|
|
321
|
+
print(f"Success: {success}")
|
|
322
|
+
```
|
|
323
|
+
"""
|
|
324
|
+
payload = {
|
|
325
|
+
"file_path": file_path,
|
|
326
|
+
"filename": filename,
|
|
327
|
+
"sandbox_id": sandbox_id,
|
|
328
|
+
"sandbox_path": sandbox_path,
|
|
329
|
+
}
|
|
330
|
+
data = self._requester.request(
|
|
331
|
+
"POST",
|
|
332
|
+
f"/disk/{disk_id}/artifact/download_to_sandbox",
|
|
333
|
+
json_data=payload,
|
|
334
|
+
)
|
|
335
|
+
return bool(data.get("success", False))
|
|
336
|
+
|
|
337
|
+
def upload_from_sandbox(
|
|
338
|
+
self,
|
|
339
|
+
disk_id: str,
|
|
340
|
+
*,
|
|
341
|
+
sandbox_id: str,
|
|
342
|
+
sandbox_path: str,
|
|
343
|
+
sandbox_filename: str,
|
|
344
|
+
file_path: str,
|
|
345
|
+
) -> Artifact:
|
|
346
|
+
"""Upload a file from a sandbox environment to disk storage as an artifact.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
disk_id: The UUID of the target disk.
|
|
350
|
+
sandbox_id: The UUID of the source sandbox.
|
|
351
|
+
sandbox_path: Source directory in the sandbox (not including filename).
|
|
352
|
+
sandbox_filename: Filename in the sandbox.
|
|
353
|
+
file_path: Destination directory path on the disk.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Artifact containing the created artifact information.
|
|
357
|
+
|
|
358
|
+
Example:
|
|
359
|
+
```python
|
|
360
|
+
artifact = client.disks.artifacts.upload_from_sandbox(
|
|
361
|
+
disk_id="disk-uuid",
|
|
362
|
+
sandbox_id="sandbox-uuid",
|
|
363
|
+
sandbox_path="/home/user/",
|
|
364
|
+
sandbox_filename="output.txt",
|
|
365
|
+
file_path="/results/"
|
|
366
|
+
)
|
|
367
|
+
print(f"Created: {artifact.path}{artifact.filename}")
|
|
368
|
+
```
|
|
369
|
+
"""
|
|
370
|
+
payload = {
|
|
371
|
+
"sandbox_id": sandbox_id,
|
|
372
|
+
"sandbox_path": sandbox_path,
|
|
373
|
+
"sandbox_filename": sandbox_filename,
|
|
374
|
+
"file_path": file_path,
|
|
375
|
+
}
|
|
376
|
+
data = self._requester.request(
|
|
377
|
+
"POST",
|
|
378
|
+
f"/disk/{disk_id}/artifact/upload_from_sandbox",
|
|
379
|
+
json_data=payload,
|
|
380
|
+
)
|
|
381
|
+
return Artifact.model_validate(data)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sandboxes endpoints.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .._utils import build_params
|
|
6
|
+
from ..client_types import RequesterProtocol
|
|
7
|
+
from ..types.sandbox import (
|
|
8
|
+
GetSandboxLogsOutput,
|
|
9
|
+
SandboxCommandOutput,
|
|
10
|
+
SandboxRuntimeInfo,
|
|
11
|
+
)
|
|
12
|
+
from ..types.tool import FlagResponse
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SandboxesAPI:
|
|
16
|
+
def __init__(self, requester: RequesterProtocol) -> None:
|
|
17
|
+
self._requester = requester
|
|
18
|
+
|
|
19
|
+
def create(self) -> SandboxRuntimeInfo:
|
|
20
|
+
"""Create and start a new sandbox.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
SandboxRuntimeInfo containing the sandbox ID, status, and timestamps.
|
|
24
|
+
"""
|
|
25
|
+
data = self._requester.request("POST", "/sandbox")
|
|
26
|
+
return SandboxRuntimeInfo.model_validate(data)
|
|
27
|
+
|
|
28
|
+
def exec_command(
|
|
29
|
+
self,
|
|
30
|
+
*,
|
|
31
|
+
sandbox_id: str,
|
|
32
|
+
command: str,
|
|
33
|
+
timeout: float | None = None,
|
|
34
|
+
) -> SandboxCommandOutput:
|
|
35
|
+
"""Execute a shell command in the sandbox.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
sandbox_id: The UUID of the sandbox.
|
|
39
|
+
command: The shell command to execute.
|
|
40
|
+
timeout: Optional timeout in seconds for this command.
|
|
41
|
+
If not provided, uses the client's default timeout.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
SandboxCommandOutput containing stdout, stderr, and exit code.
|
|
45
|
+
"""
|
|
46
|
+
data = self._requester.request(
|
|
47
|
+
"POST",
|
|
48
|
+
f"/sandbox/{sandbox_id}/exec",
|
|
49
|
+
json_data={"command": command},
|
|
50
|
+
timeout=timeout,
|
|
51
|
+
)
|
|
52
|
+
return SandboxCommandOutput.model_validate(data)
|
|
53
|
+
|
|
54
|
+
def kill(self, sandbox_id: str) -> FlagResponse:
|
|
55
|
+
"""Kill a running sandbox.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
sandbox_id: The UUID of the sandbox to kill.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
FlagResponse with status and error message.
|
|
62
|
+
"""
|
|
63
|
+
data = self._requester.request("DELETE", f"/sandbox/{sandbox_id}")
|
|
64
|
+
return FlagResponse.model_validate(data)
|
|
65
|
+
|
|
66
|
+
def get_logs(
|
|
67
|
+
self,
|
|
68
|
+
*,
|
|
69
|
+
limit: int | None = None,
|
|
70
|
+
cursor: str | None = None,
|
|
71
|
+
time_desc: bool | None = None,
|
|
72
|
+
) -> GetSandboxLogsOutput:
|
|
73
|
+
"""Get sandbox logs for the project with cursor-based pagination.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
limit: Maximum number of logs to return (default 20, max 200).
|
|
77
|
+
cursor: Cursor for pagination. Use the cursor from the previous response to get the next page.
|
|
78
|
+
time_desc: Order by created_at descending if True, ascending if False (default False).
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
GetSandboxLogsOutput containing the list of sandbox logs and pagination information.
|
|
82
|
+
"""
|
|
83
|
+
params = build_params(limit=limit, cursor=cursor, time_desc=time_desc)
|
|
84
|
+
data = self._requester.request("GET", "/sandbox/logs", params=params or None)
|
|
85
|
+
return GetSandboxLogsOutput.model_validate(data)
|