indent 0.0.8__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.
Potentially problematic release.
This version of indent might be problematic. Click here for more details.
- exponent/__init__.py +1 -0
- exponent/cli.py +112 -0
- exponent/commands/cloud_commands.py +85 -0
- exponent/commands/common.py +434 -0
- exponent/commands/config_commands.py +581 -0
- exponent/commands/github_app_commands.py +211 -0
- exponent/commands/listen_commands.py +96 -0
- exponent/commands/run_commands.py +208 -0
- exponent/commands/settings.py +56 -0
- exponent/commands/shell_commands.py +2840 -0
- exponent/commands/theme.py +246 -0
- exponent/commands/types.py +111 -0
- exponent/commands/upgrade.py +29 -0
- exponent/commands/utils.py +236 -0
- exponent/core/config.py +180 -0
- exponent/core/graphql/__init__.py +0 -0
- exponent/core/graphql/client.py +59 -0
- exponent/core/graphql/cloud_config_queries.py +77 -0
- exponent/core/graphql/get_chats_query.py +47 -0
- exponent/core/graphql/github_config_queries.py +56 -0
- exponent/core/graphql/mutations.py +75 -0
- exponent/core/graphql/queries.py +110 -0
- exponent/core/graphql/subscriptions.py +452 -0
- exponent/core/remote_execution/checkpoints.py +212 -0
- exponent/core/remote_execution/cli_rpc_types.py +214 -0
- exponent/core/remote_execution/client.py +545 -0
- exponent/core/remote_execution/code_execution.py +58 -0
- exponent/core/remote_execution/command_execution.py +105 -0
- exponent/core/remote_execution/error_info.py +45 -0
- exponent/core/remote_execution/exceptions.py +10 -0
- exponent/core/remote_execution/file_write.py +410 -0
- exponent/core/remote_execution/files.py +415 -0
- exponent/core/remote_execution/git.py +268 -0
- exponent/core/remote_execution/languages/python_execution.py +239 -0
- exponent/core/remote_execution/languages/shell_streaming.py +221 -0
- exponent/core/remote_execution/languages/types.py +20 -0
- exponent/core/remote_execution/session.py +128 -0
- exponent/core/remote_execution/system_context.py +54 -0
- exponent/core/remote_execution/tool_execution.py +289 -0
- exponent/core/remote_execution/truncation.py +284 -0
- exponent/core/remote_execution/types.py +670 -0
- exponent/core/remote_execution/utils.py +600 -0
- exponent/core/types/__init__.py +0 -0
- exponent/core/types/command_data.py +206 -0
- exponent/core/types/event_types.py +89 -0
- exponent/core/types/generated/__init__.py +0 -0
- exponent/core/types/generated/strategy_info.py +225 -0
- exponent/migration-docs/login.md +112 -0
- exponent/py.typed +4 -0
- exponent/utils/__init__.py +0 -0
- exponent/utils/colors.py +92 -0
- exponent/utils/version.py +289 -0
- indent-0.0.8.dist-info/METADATA +36 -0
- indent-0.0.8.dist-info/RECORD +56 -0
- indent-0.0.8.dist-info/WHEEL +4 -0
- indent-0.0.8.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
from os import PathLike
|
|
6
|
+
from pathlib import Path, PurePath
|
|
7
|
+
from typing import (
|
|
8
|
+
Annotated,
|
|
9
|
+
Any,
|
|
10
|
+
ClassVar,
|
|
11
|
+
Generic,
|
|
12
|
+
Literal,
|
|
13
|
+
TypeVar,
|
|
14
|
+
Union,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from anyio import Path as AsyncPath
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
from exponent.core.remote_execution.error_info import SerializableErrorInfo
|
|
21
|
+
from exponent.core.types.command_data import (
|
|
22
|
+
CommandDataType,
|
|
23
|
+
FileWriteStrategyName,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
type FilePath = str | PathLike[str]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# DEPRECATED, only around for gql compatibility
|
|
30
|
+
class UseToolsMode(str, Enum):
|
|
31
|
+
read_only = "read_only"
|
|
32
|
+
read_write = "read_write"
|
|
33
|
+
disabled = "disabled"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CreateChatResponse(BaseModel):
|
|
37
|
+
chat_uuid: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RunWorkflowRequest(BaseModel):
|
|
41
|
+
chat_uuid: str
|
|
42
|
+
workflow_id: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ExecutionEndResponse(BaseModel):
|
|
46
|
+
execution_ended: bool
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class SignalType(str, Enum):
|
|
50
|
+
disconnect = "disconnect"
|
|
51
|
+
|
|
52
|
+
def __str__(self) -> str:
|
|
53
|
+
return self.value
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class GitInfo(BaseModel):
|
|
57
|
+
branch: str
|
|
58
|
+
remote: str | None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class PythonEnvInfo(BaseModel):
|
|
62
|
+
interpreter_path: str | None
|
|
63
|
+
interpreter_version: str | None
|
|
64
|
+
name: str | None = "exponent"
|
|
65
|
+
provider: Literal["venv", "pyenv", "pipenv", "conda"] | None = "pyenv"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class SystemInfo(BaseModel):
|
|
69
|
+
name: str
|
|
70
|
+
cwd: str
|
|
71
|
+
os: str
|
|
72
|
+
shell: str
|
|
73
|
+
git: GitInfo | None
|
|
74
|
+
python_env: PythonEnvInfo | None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class HeartbeatInfo(BaseModel):
|
|
78
|
+
exponent_version: str | None = None
|
|
79
|
+
editable_installation: bool = False
|
|
80
|
+
system_info: SystemInfo | None
|
|
81
|
+
timestamp: datetime.datetime = Field(
|
|
82
|
+
default_factory=lambda: datetime.datetime.now(datetime.UTC)
|
|
83
|
+
)
|
|
84
|
+
timestamp_received: datetime.datetime | None = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class RemoteFile(BaseModel):
|
|
88
|
+
file_path: str
|
|
89
|
+
working_directory: str = "."
|
|
90
|
+
|
|
91
|
+
@cached_property
|
|
92
|
+
def pure_path(self) -> PurePath:
|
|
93
|
+
return PurePath(self.working_directory, self.file_path)
|
|
94
|
+
|
|
95
|
+
@cached_property
|
|
96
|
+
def path(self) -> Path:
|
|
97
|
+
return Path(self.working_directory, self.file_path)
|
|
98
|
+
|
|
99
|
+
@cached_property
|
|
100
|
+
def name(self) -> str:
|
|
101
|
+
return self.pure_path.name
|
|
102
|
+
|
|
103
|
+
@cached_property
|
|
104
|
+
def absolute_path(self) -> str:
|
|
105
|
+
return self.path.absolute().as_posix()
|
|
106
|
+
|
|
107
|
+
async def resolve(self, client_working_directory: str) -> str:
|
|
108
|
+
working_directory = AsyncPath(self.working_directory, self.file_path)
|
|
109
|
+
|
|
110
|
+
if not working_directory.is_absolute():
|
|
111
|
+
working_directory = AsyncPath(client_working_directory, working_directory)
|
|
112
|
+
|
|
113
|
+
return str(await working_directory.resolve())
|
|
114
|
+
|
|
115
|
+
def __eq__(self, other: object) -> bool:
|
|
116
|
+
if not isinstance(other, RemoteFile):
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
return self.path.name == other.path.name
|
|
120
|
+
|
|
121
|
+
def __lt__(self, other: "RemoteFile") -> bool:
|
|
122
|
+
# Prefer shorter paths
|
|
123
|
+
if (cmp := self._cmp_path_len(other)) is not None:
|
|
124
|
+
return cmp
|
|
125
|
+
|
|
126
|
+
# Prefer paths sorted by parent directory
|
|
127
|
+
if (cmp := self._cmp_path_str(other)) is not None:
|
|
128
|
+
return cmp
|
|
129
|
+
|
|
130
|
+
# Prefer paths with alphabetical first character
|
|
131
|
+
return self._cmp_first_char(other)
|
|
132
|
+
|
|
133
|
+
def __hash__(self) -> int:
|
|
134
|
+
return hash(self.absolute_path)
|
|
135
|
+
|
|
136
|
+
def _cmp_first_char(self, other: "RemoteFile") -> bool:
|
|
137
|
+
return self._cmp_str(self.path.name, other.path.name)
|
|
138
|
+
|
|
139
|
+
def _cmp_path_len(self, other: "RemoteFile") -> bool | None:
|
|
140
|
+
self_parts = self.path.absolute().parent.parts
|
|
141
|
+
other_parts = other.path.absolute().parent.parts
|
|
142
|
+
|
|
143
|
+
if len(self_parts) == len(other_parts):
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
return len(self_parts) < len(other_parts)
|
|
147
|
+
|
|
148
|
+
def _cmp_path_str(self, other: "RemoteFile") -> bool | None:
|
|
149
|
+
self_parts = self.path.absolute().parent.parts
|
|
150
|
+
other_parts = other.path.absolute().parent.parts
|
|
151
|
+
|
|
152
|
+
if self_parts == other_parts:
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
for a, b in zip(self_parts, other_parts):
|
|
156
|
+
if a != b:
|
|
157
|
+
return self._cmp_str(a, b)
|
|
158
|
+
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def _cmp_str(s1: str, s2: str) -> bool:
|
|
163
|
+
if s1[:1].isalpha() == s2[:1].isalpha():
|
|
164
|
+
return s1 < s2
|
|
165
|
+
|
|
166
|
+
return s1[:1].isalpha()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class URLAttachment(BaseModel):
|
|
170
|
+
attachment_type: Literal["url"] = "url"
|
|
171
|
+
url: str
|
|
172
|
+
content: str
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class FileAttachment(BaseModel):
|
|
176
|
+
attachment_type: Literal["file"] = "file"
|
|
177
|
+
file: RemoteFile
|
|
178
|
+
content: str
|
|
179
|
+
truncated: bool = False
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class TableSchemaAttachment(BaseModel):
|
|
183
|
+
attachment_type: Literal["table_schema"] = "table_schema"
|
|
184
|
+
table_name: str
|
|
185
|
+
table_schema: dict[str, Any]
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class PromptAttachment(BaseModel):
|
|
189
|
+
attachment_type: Literal["prompt"] = "prompt"
|
|
190
|
+
prompt_name: str
|
|
191
|
+
prompt_content: str
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
MessageAttachment = Annotated[
|
|
195
|
+
FileAttachment | URLAttachment | TableSchemaAttachment | PromptAttachment,
|
|
196
|
+
Field(discriminator="attachment_type"),
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
Direction = Literal[
|
|
201
|
+
"request",
|
|
202
|
+
"response",
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
Namespace = Literal[
|
|
206
|
+
"code_execution",
|
|
207
|
+
"streaming_code_execution",
|
|
208
|
+
"streaming_code_execution_chunk",
|
|
209
|
+
"file_write",
|
|
210
|
+
"command",
|
|
211
|
+
"list_files",
|
|
212
|
+
"get_file_attachment",
|
|
213
|
+
"get_file_attachments",
|
|
214
|
+
"get_matching_files",
|
|
215
|
+
"system_context",
|
|
216
|
+
"get_all_tracked_files",
|
|
217
|
+
"halt",
|
|
218
|
+
"switch_cli_chat",
|
|
219
|
+
"error",
|
|
220
|
+
"create_checkpoint",
|
|
221
|
+
"rollback_to_checkpoint",
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
ErrorType = Literal["unknown_request_type", "request_error"]
|
|
225
|
+
|
|
226
|
+
SupportedLanguage = Literal[
|
|
227
|
+
"python",
|
|
228
|
+
"shell",
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
SUPPORTED_LANGUAGES: list[SupportedLanguage] = ["python", "shell"]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class RemoteExecutionMessageData(BaseModel):
|
|
235
|
+
namespace: Namespace
|
|
236
|
+
direction: Direction
|
|
237
|
+
message_data: str
|
|
238
|
+
|
|
239
|
+
def message_type(self) -> str:
|
|
240
|
+
return f"{self.namespace}.{self.direction}"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class RemoteExecutionMessage(BaseModel):
|
|
244
|
+
direction: ClassVar[Direction]
|
|
245
|
+
namespace: ClassVar[Namespace]
|
|
246
|
+
correlation_id: str
|
|
247
|
+
|
|
248
|
+
@classmethod
|
|
249
|
+
def message_type(cls) -> str:
|
|
250
|
+
return f"{cls.namespace}.{cls.direction}"
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def result_key(self) -> str:
|
|
254
|
+
return f"{self.namespace}:{self.correlation_id}"
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
### Response Types
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class RemoteExecutionResponseData(RemoteExecutionMessageData):
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class RemoteExecutionResponse(RemoteExecutionMessage):
|
|
265
|
+
direction: ClassVar[Direction] = "response"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
ResponseT = TypeVar("ResponseT", bound=RemoteExecutionResponse)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class StreamingCodeExecutionResponseChunk(RemoteExecutionResponse):
|
|
272
|
+
namespace: ClassVar[Namespace] = "streaming_code_execution_chunk"
|
|
273
|
+
|
|
274
|
+
content: str
|
|
275
|
+
truncated: bool = False
|
|
276
|
+
|
|
277
|
+
def add(
|
|
278
|
+
self, new_chunk: "StreamingCodeExecutionResponseChunk"
|
|
279
|
+
) -> "StreamingCodeExecutionResponseChunk":
|
|
280
|
+
"""Aggregates content of this and a new chunk."""
|
|
281
|
+
assert self.correlation_id == new_chunk.correlation_id
|
|
282
|
+
return StreamingCodeExecutionResponseChunk(
|
|
283
|
+
correlation_id=self.correlation_id, content=self.content + new_chunk.content
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class StreamingCodeExecutionResponse(RemoteExecutionResponse):
|
|
288
|
+
namespace: ClassVar[Namespace] = "streaming_code_execution"
|
|
289
|
+
|
|
290
|
+
content: str
|
|
291
|
+
truncated: bool = False
|
|
292
|
+
|
|
293
|
+
# Only present for shell code execution
|
|
294
|
+
cancelled_for_timeout: bool = False
|
|
295
|
+
exit_code: int | None = None
|
|
296
|
+
halted: bool = False
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class CodeExecutionResponse(RemoteExecutionResponse):
|
|
300
|
+
namespace: ClassVar[Namespace] = "code_execution"
|
|
301
|
+
|
|
302
|
+
content: str
|
|
303
|
+
|
|
304
|
+
# Only present for shell code execution
|
|
305
|
+
cancelled_for_timeout: bool = False
|
|
306
|
+
exit_code: int | None = None
|
|
307
|
+
halted: bool = False
|
|
308
|
+
truncated: bool = False
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class FileWriteResponse(RemoteExecutionResponse):
|
|
312
|
+
namespace: ClassVar[Namespace] = "file_write"
|
|
313
|
+
|
|
314
|
+
content: str
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class ListFilesResponse(RemoteExecutionResponse):
|
|
318
|
+
namespace: ClassVar[Namespace] = "list_files"
|
|
319
|
+
|
|
320
|
+
files: list[RemoteFile]
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class GetFileAttachmentResponse(RemoteExecutionResponse, FileAttachment):
|
|
324
|
+
namespace: ClassVar[Namespace] = "get_file_attachment"
|
|
325
|
+
|
|
326
|
+
exists: bool = Field(default=True)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class GetFileAttachmentsResponse(RemoteExecutionResponse):
|
|
330
|
+
namespace: ClassVar[Namespace] = "get_file_attachments"
|
|
331
|
+
|
|
332
|
+
file_attachments: list[FileAttachment]
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class GetMatchingFilesResponse(RemoteExecutionResponse):
|
|
336
|
+
namespace: ClassVar[Namespace] = "get_matching_files"
|
|
337
|
+
|
|
338
|
+
files: list[RemoteFile]
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class GetAllTrackedFilesResponse(RemoteExecutionResponse):
|
|
342
|
+
namespace: ClassVar[Namespace] = "get_all_tracked_files"
|
|
343
|
+
|
|
344
|
+
files: list[RemoteFile]
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class SystemContextResponse(RemoteExecutionResponse):
|
|
348
|
+
namespace: ClassVar[Namespace] = "system_context"
|
|
349
|
+
|
|
350
|
+
exponent_txt: str | None
|
|
351
|
+
system_info: SystemInfo | None
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class HaltResponse(RemoteExecutionResponse):
|
|
355
|
+
namespace: ClassVar[Namespace] = "halt"
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class SwitchCLIChatResponse(RemoteExecutionResponse):
|
|
359
|
+
namespace: ClassVar[Namespace] = "switch_cli_chat"
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class ErrorResponse(RemoteExecutionResponse):
|
|
363
|
+
namespace: ClassVar[Namespace] = "error"
|
|
364
|
+
# The namespace of the request that caused the error.
|
|
365
|
+
# Not a Namespace to avoid deserialization errors
|
|
366
|
+
request_namespace: str
|
|
367
|
+
error_type: ErrorType
|
|
368
|
+
error_info: SerializableErrorInfo | None = None
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def result_key(self) -> str:
|
|
372
|
+
# Match the key of the request that caused the error
|
|
373
|
+
return f"{self.request_namespace}:{self.correlation_id}"
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
class GitFileChange(BaseModel):
|
|
377
|
+
path: str
|
|
378
|
+
lines_added: int
|
|
379
|
+
lines_deleted: int
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class GitDiff(BaseModel):
|
|
383
|
+
files: list[GitFileChange]
|
|
384
|
+
truncated: bool = False # True if there were more files than the limit
|
|
385
|
+
total_files: int # Total number of files changed, even if truncated
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class GitCommitMetadata(BaseModel):
|
|
389
|
+
author_name: str
|
|
390
|
+
author_email: str
|
|
391
|
+
author_date: str
|
|
392
|
+
commit_date: str
|
|
393
|
+
commit_message: str
|
|
394
|
+
branch: str
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class CreateCheckpointResponse(RemoteExecutionResponse):
|
|
398
|
+
namespace: ClassVar[Namespace] = "create_checkpoint"
|
|
399
|
+
|
|
400
|
+
correlation_id: str
|
|
401
|
+
head_commit_hash: str
|
|
402
|
+
head_commit_metadata: GitCommitMetadata
|
|
403
|
+
uncommitted_changes_commit_hash: str | None = None
|
|
404
|
+
diff_versus_last_checkpoint: GitDiff | None = None
|
|
405
|
+
|
|
406
|
+
debug_info: dict[str, Any] | None = None
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class RollbackToCheckpointResponse(RemoteExecutionResponse):
|
|
410
|
+
namespace: ClassVar[Namespace] = "rollback_to_checkpoint"
|
|
411
|
+
|
|
412
|
+
debug_info: dict[str, Any] | None = None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
### Request Types
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class RemoteExecutionRequestData(RemoteExecutionMessageData):
|
|
419
|
+
pass
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class RemoteExecutionRequest(RemoteExecutionMessage, Generic[ResponseT]):
|
|
423
|
+
direction: ClassVar[Direction] = "request"
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class CodeExecutionRequest(RemoteExecutionRequest[CodeExecutionResponse]):
|
|
427
|
+
namespace: ClassVar[Namespace] = "code_execution"
|
|
428
|
+
|
|
429
|
+
language: SupportedLanguage
|
|
430
|
+
content: str
|
|
431
|
+
timeout: int
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
class StreamingCodeExecutionRequest(
|
|
435
|
+
RemoteExecutionRequest[
|
|
436
|
+
Union[StreamingCodeExecutionResponseChunk, StreamingCodeExecutionResponse]
|
|
437
|
+
]
|
|
438
|
+
):
|
|
439
|
+
namespace: ClassVar[Namespace] = "streaming_code_execution"
|
|
440
|
+
|
|
441
|
+
language: SupportedLanguage
|
|
442
|
+
content: str
|
|
443
|
+
timeout: int
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class HaltRequest(RemoteExecutionRequest[HaltResponse]):
|
|
447
|
+
namespace: ClassVar[Namespace] = "halt"
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class SwitchCLIChatRequest(RemoteExecutionRequest[SwitchCLIChatResponse]):
|
|
451
|
+
namespace: ClassVar[Namespace] = "switch_cli_chat"
|
|
452
|
+
new_chat_uuid: str
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
class FileWriteRequest(RemoteExecutionRequest[FileWriteResponse]):
|
|
456
|
+
namespace: ClassVar[Namespace] = "file_write"
|
|
457
|
+
|
|
458
|
+
file_path: str
|
|
459
|
+
# Note we don't use SupportedLanguage here because we don't
|
|
460
|
+
# require language-specific execution support for file writes
|
|
461
|
+
language: str
|
|
462
|
+
write_strategy: FileWriteStrategyName
|
|
463
|
+
content: str
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
class ListFilesRequest(RemoteExecutionRequest[ListFilesResponse]):
|
|
467
|
+
namespace: ClassVar[Namespace] = "list_files"
|
|
468
|
+
|
|
469
|
+
directory: str
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
class GetFileAttachmentRequest(RemoteExecutionRequest[GetFileAttachmentResponse]):
|
|
473
|
+
namespace: ClassVar[Namespace] = "get_file_attachment"
|
|
474
|
+
|
|
475
|
+
file: RemoteFile
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
class GetFileAttachmentsRequest(RemoteExecutionRequest[GetFileAttachmentsResponse]):
|
|
479
|
+
namespace: ClassVar[Namespace] = "get_file_attachments"
|
|
480
|
+
|
|
481
|
+
files: list[RemoteFile]
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
class GetMatchingFilesRequest(RemoteExecutionRequest[GetMatchingFilesResponse]):
|
|
485
|
+
namespace: ClassVar[Namespace] = "get_matching_files"
|
|
486
|
+
|
|
487
|
+
search_term: str
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
class GetAllTrackedFilesRequest(RemoteExecutionRequest[GetAllTrackedFilesResponse]):
|
|
491
|
+
namespace: ClassVar[Namespace] = "get_all_tracked_files"
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
class SystemContextRequest(RemoteExecutionRequest[SystemContextResponse]):
|
|
495
|
+
namespace: ClassVar[Namespace] = "system_context"
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
class CreateCheckpointRequest(RemoteExecutionRequest[CreateCheckpointResponse]):
|
|
499
|
+
namespace: ClassVar[Namespace] = "create_checkpoint"
|
|
500
|
+
|
|
501
|
+
last_checkpoint_head_commit: str | None = None
|
|
502
|
+
last_checkpoint_uncommitted_changes_commit: str | None = None
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
class RollbackToCheckpointRequest(RemoteExecutionRequest[RollbackToCheckpointResponse]):
|
|
506
|
+
namespace: ClassVar[Namespace] = "rollback_to_checkpoint"
|
|
507
|
+
|
|
508
|
+
head_commit: str
|
|
509
|
+
uncommitted_changes_commit: str | None
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
### Commands
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
### Command Response Types
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
class CommandResponse(RemoteExecutionResponse):
|
|
519
|
+
namespace: ClassVar[Namespace] = "command"
|
|
520
|
+
|
|
521
|
+
content: str
|
|
522
|
+
content_json: dict[str, Any] = Field(default_factory=dict)
|
|
523
|
+
subcommand: str = "unknown"
|
|
524
|
+
truncated: bool = False
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
### Command Request Types
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
class CommandRequest(RemoteExecutionRequest[CommandResponse]):
|
|
531
|
+
namespace: ClassVar[Namespace] = "command"
|
|
532
|
+
|
|
533
|
+
data: CommandDataType = Field(..., discriminator="type")
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
RemoteExecutionRequestType = Union[
|
|
537
|
+
CodeExecutionRequest,
|
|
538
|
+
FileWriteRequest,
|
|
539
|
+
ListFilesRequest,
|
|
540
|
+
GetFileAttachmentRequest,
|
|
541
|
+
GetFileAttachmentsRequest,
|
|
542
|
+
GetMatchingFilesRequest,
|
|
543
|
+
SystemContextRequest,
|
|
544
|
+
GetAllTrackedFilesRequest,
|
|
545
|
+
CommandRequest,
|
|
546
|
+
HaltRequest,
|
|
547
|
+
StreamingCodeExecutionRequest,
|
|
548
|
+
SwitchCLIChatRequest,
|
|
549
|
+
CreateCheckpointRequest,
|
|
550
|
+
RollbackToCheckpointRequest,
|
|
551
|
+
]
|
|
552
|
+
|
|
553
|
+
RemoteExecutionResponseType = Union[
|
|
554
|
+
CodeExecutionResponse,
|
|
555
|
+
StreamingCodeExecutionResponseChunk,
|
|
556
|
+
StreamingCodeExecutionResponse,
|
|
557
|
+
FileWriteResponse,
|
|
558
|
+
ListFilesResponse,
|
|
559
|
+
GetFileAttachmentResponse,
|
|
560
|
+
GetFileAttachmentsResponse,
|
|
561
|
+
GetMatchingFilesResponse,
|
|
562
|
+
GetAllTrackedFilesResponse,
|
|
563
|
+
SystemContextResponse,
|
|
564
|
+
CommandResponse,
|
|
565
|
+
HaltResponse,
|
|
566
|
+
SwitchCLIChatResponse,
|
|
567
|
+
ErrorResponse,
|
|
568
|
+
CreateCheckpointResponse,
|
|
569
|
+
RollbackToCheckpointResponse,
|
|
570
|
+
]
|
|
571
|
+
|
|
572
|
+
StreamingResponseType = Union[
|
|
573
|
+
StreamingCodeExecutionResponseChunk,
|
|
574
|
+
StreamingCodeExecutionResponse,
|
|
575
|
+
ErrorResponse,
|
|
576
|
+
]
|
|
577
|
+
|
|
578
|
+
STREAMING_NAMESPACES = [
|
|
579
|
+
"streaming_code_execution",
|
|
580
|
+
"streaming_code_execution_chunk",
|
|
581
|
+
]
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
class ChatMode(str, Enum):
|
|
585
|
+
DEFAULT = "DEFAULT" # chat just with model
|
|
586
|
+
CLI = "CLI"
|
|
587
|
+
CLOUD = "CLOUD" # chat with cloud devbox
|
|
588
|
+
CODEBASE = "CODEBASE" # chat with codebase
|
|
589
|
+
PYTHON_INTERPRETER = "PYTHON_INTERPRETER"
|
|
590
|
+
DATABASE = "DATABASE" # chat with database connection
|
|
591
|
+
WORKFLOW = "WORKFLOW"
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
DEVBOX_CHAT_MODES = [
|
|
595
|
+
ChatMode.CLOUD,
|
|
596
|
+
ChatMode.CODEBASE,
|
|
597
|
+
]
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
class ChatSource(str, Enum):
|
|
601
|
+
CLI_SHELL = "CLI_SHELL"
|
|
602
|
+
CLI_RUN = "CLI_RUN"
|
|
603
|
+
WEB = "WEB"
|
|
604
|
+
DESKTOP_APP = "DESKTOP_APP"
|
|
605
|
+
VSCODE_EXTENSION = "VSCODE_EXTENSION"
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
class CLIConnectedState(BaseModel):
|
|
609
|
+
chat_uuid: str
|
|
610
|
+
connected: bool
|
|
611
|
+
last_connected_at: datetime.datetime | None
|
|
612
|
+
connection_latency_ms: int | None
|
|
613
|
+
system_info: SystemInfo | None
|
|
614
|
+
exponent_version: str | None = None
|
|
615
|
+
editable_installation: bool = False
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
class DevboxConnectedState(str, Enum):
|
|
619
|
+
# TODO: Only needed if we create devbox async
|
|
620
|
+
INITIALIZED = "INITIALIZED"
|
|
621
|
+
# The chat has been initialized, but the devbox is still loading
|
|
622
|
+
DEVBOX_LOADING = "DEVBOX_LOADING"
|
|
623
|
+
# Devbox is ready to use but not tied to a chat
|
|
624
|
+
DEVBOX_READY = "DEVBOX_READY"
|
|
625
|
+
# CLI is connected and running on devbox
|
|
626
|
+
CONNECTED = "CONNECTED"
|
|
627
|
+
# CLI has disconnected
|
|
628
|
+
# TODO: what condition?
|
|
629
|
+
CLI_DISCONNECTED = "CLI_DISCONNECTED"
|
|
630
|
+
# CLI has an error, devbox is running
|
|
631
|
+
CLI_ERROR = "CLI_ERROR"
|
|
632
|
+
# Devbox has an error
|
|
633
|
+
DEVBOX_ERROR = "DEVBOX_ERROR"
|
|
634
|
+
# Devbox is going to idle
|
|
635
|
+
GOING_TO_IDLE = "GOING_TO_IDLE"
|
|
636
|
+
# Devbox is idle
|
|
637
|
+
IDLE = "IDLE"
|
|
638
|
+
# Devbox is going to idle
|
|
639
|
+
RESUMING_FROM_IDLE = "RESUMING_FROM_IDLE"
|
|
640
|
+
# Devbox_shutdown
|
|
641
|
+
# TODO: In theory our terminal state, do we want to name something different?
|
|
642
|
+
DEVBOX_SHUTDOWN = "DEVBOX_SHUTDOWN"
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
class CloudConnectedState(BaseModel):
|
|
646
|
+
chat_uuid: str
|
|
647
|
+
connected_state: DevboxConnectedState
|
|
648
|
+
last_connected_at: datetime.datetime | None
|
|
649
|
+
system_info: SystemInfo | None
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
class CLIErrorLog(BaseModel):
|
|
653
|
+
event_data: str
|
|
654
|
+
timestamp: datetime.datetime = datetime.datetime.now()
|
|
655
|
+
attachment_data: str | None = None
|
|
656
|
+
version: str | None = None
|
|
657
|
+
chat_uuid: str | None = None
|
|
658
|
+
|
|
659
|
+
@property
|
|
660
|
+
def loaded_event_data(self) -> Any | None:
|
|
661
|
+
try:
|
|
662
|
+
return json.loads(self.event_data)
|
|
663
|
+
except json.JSONDecodeError:
|
|
664
|
+
return None
|
|
665
|
+
|
|
666
|
+
@property
|
|
667
|
+
def attachment_bytes(self) -> bytes | None:
|
|
668
|
+
if not self.attachment_data:
|
|
669
|
+
return None
|
|
670
|
+
return self.attachment_data.encode()
|