codespeak-cli 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codespeak_cli-0.2.0.dist-info/METADATA +11 -0
- codespeak_cli-0.2.0.dist-info/RECORD +19 -0
- codespeak_cli-0.2.0.dist-info/WHEEL +4 -0
- codespeak_cli-0.2.0.dist-info/entry_points.txt +3 -0
- console_client/__init__.py +1 -0
- console_client/auth/__init__.py +1 -0
- console_client/auth/auth_manager.py +156 -0
- console_client/auth/callback_server.py +170 -0
- console_client/auth/exceptions.py +83 -0
- console_client/auth/oauth_pkce.py +134 -0
- console_client/auth/token_storage.py +122 -0
- console_client/build_client.py +318 -0
- console_client/build_client_event_converter.py +156 -0
- console_client/client_feature_flags.py +23 -0
- console_client/console_client_logging.py +220 -0
- console_client/console_client_main.py +348 -0
- console_client/os_environment_servicer.py +676 -0
- console_client/sequence_reorder_buffer.py +93 -0
- console_client/version.py +10 -0
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
from collections.abc import Iterator
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import api_stubs.os_environment_pb2 as os_env_pb2
|
|
5
|
+
from api_stubs.operation_logging import OsEnvRequestLogging
|
|
6
|
+
from api_stubs.os_environment_pb2 import (
|
|
7
|
+
AppendToFileRequest,
|
|
8
|
+
AppendToFileResponse,
|
|
9
|
+
ChildProcessOutcome,
|
|
10
|
+
CopyRequest,
|
|
11
|
+
CopyResponse,
|
|
12
|
+
DeleteFileRequest,
|
|
13
|
+
DeleteFileResponse,
|
|
14
|
+
DeleteRecursivelyRequest,
|
|
15
|
+
DeleteRecursivelyResponse,
|
|
16
|
+
ErrorInfo,
|
|
17
|
+
ErrorType,
|
|
18
|
+
ExistsRequest,
|
|
19
|
+
ExistsResponse,
|
|
20
|
+
GetAttributesRequest,
|
|
21
|
+
GetAttributesResponse,
|
|
22
|
+
GlobRequest,
|
|
23
|
+
GlobResponse,
|
|
24
|
+
GrepRequest,
|
|
25
|
+
GrepResponse,
|
|
26
|
+
IsDirectoryRequest,
|
|
27
|
+
IsDirectoryResponse,
|
|
28
|
+
IsFileRequest,
|
|
29
|
+
IsFileResponse,
|
|
30
|
+
ListDirectoryRequest,
|
|
31
|
+
ListDirectoryResponse,
|
|
32
|
+
ListTreeRequest,
|
|
33
|
+
ListTreeResponse,
|
|
34
|
+
MkdirsRequest,
|
|
35
|
+
MkdirsResponse,
|
|
36
|
+
OperationRequest,
|
|
37
|
+
OperationResponse,
|
|
38
|
+
ReadFileRequest,
|
|
39
|
+
ReadFileResponse,
|
|
40
|
+
ResolvePathRequest,
|
|
41
|
+
ResolvePathResponse,
|
|
42
|
+
ResolveUserNameRequest,
|
|
43
|
+
ResolveUserNameResponse,
|
|
44
|
+
RunChildProcessRequest,
|
|
45
|
+
RunChildProcessResponse,
|
|
46
|
+
RunDjangoDevServerRequest,
|
|
47
|
+
RunDjangoDevServerResponse,
|
|
48
|
+
TraverseRequest,
|
|
49
|
+
TraverseResponse,
|
|
50
|
+
WriteFileRequest,
|
|
51
|
+
WriteFileResponse,
|
|
52
|
+
)
|
|
53
|
+
from api_stubs.os_environment_pb2 import (
|
|
54
|
+
ChildProcessResult as ChildProcessResultProto,
|
|
55
|
+
)
|
|
56
|
+
from api_stubs.os_environment_pb2 import (
|
|
57
|
+
DjangoServerRunMode as DjangoServerRunModeProto,
|
|
58
|
+
)
|
|
59
|
+
from api_stubs.os_environment_pb2 import (
|
|
60
|
+
EntryAttributes as EntryAttributesProto,
|
|
61
|
+
)
|
|
62
|
+
from api_stubs.os_environment_pb2 import GrepFileCount as GrepFileCountProto
|
|
63
|
+
from api_stubs.os_environment_pb2 import GrepMatch as GrepMatchProto
|
|
64
|
+
from api_stubs.os_environment_pb2 import (
|
|
65
|
+
GrepOutputMode as GrepOutputModeProto,
|
|
66
|
+
)
|
|
67
|
+
from api_stubs.os_environment_pb2 import TreeEntry as TreeEntryProto
|
|
68
|
+
from api_stubs.os_environment_pb2_grpc import OsEnvironmentServiceServicer
|
|
69
|
+
from codespeak_shared.logging import LoggingUtil
|
|
70
|
+
from codespeak_shared.os_environment.os_environment import (
|
|
71
|
+
ChildProcessFailedToStartException,
|
|
72
|
+
ChildProcessTimedOutException,
|
|
73
|
+
DjangoServerRunMode,
|
|
74
|
+
EntryAttributes,
|
|
75
|
+
ExistsWithLastModifiedTimestamp,
|
|
76
|
+
FileNotFoundException,
|
|
77
|
+
FileState,
|
|
78
|
+
FileStateExpectationNotMet,
|
|
79
|
+
GrepOutputMode,
|
|
80
|
+
MaxFileSizeExceededException,
|
|
81
|
+
Missing,
|
|
82
|
+
OsEnvironment,
|
|
83
|
+
OsEnvironmentException,
|
|
84
|
+
)
|
|
85
|
+
from codespeak_shared.project_path import ProjectPath
|
|
86
|
+
from codespeak_shared.utils.process_util import OutputType
|
|
87
|
+
from rich.console import Console
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class OsEnvironmentServicer(OsEnvironmentServiceServicer):
|
|
91
|
+
"""
|
|
92
|
+
gRPC server implementation that delegates all operations to OsEnvironment using bidirectional streaming.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__(self, os_env: OsEnvironment, console: Console | None = None):
|
|
96
|
+
self._os_env = os_env
|
|
97
|
+
self._console = console
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def create_error_info_from_exception(exception: OsEnvironmentException) -> ErrorInfo:
|
|
101
|
+
message = str(exception)
|
|
102
|
+
if isinstance(exception, FileNotFoundException):
|
|
103
|
+
return ErrorInfo(message=message, type=ErrorType.FILE_NOT_FOUND)
|
|
104
|
+
if isinstance(exception, MaxFileSizeExceededException):
|
|
105
|
+
return ErrorInfo(message=message, type=ErrorType.MAX_FILE_SIZE_EXCEEDED)
|
|
106
|
+
if isinstance(exception, FileStateExpectationNotMet):
|
|
107
|
+
return ErrorInfo(message=message, type=ErrorType.FILE_STATE_EXPECTATION_NOT_MET)
|
|
108
|
+
return ErrorInfo(message=message)
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def _to_proto_attributes(attributes: EntryAttributes) -> EntryAttributesProto:
|
|
112
|
+
return EntryAttributesProto(
|
|
113
|
+
last_modified=attributes.last_modified,
|
|
114
|
+
size=attributes.size,
|
|
115
|
+
mode=attributes.mode,
|
|
116
|
+
nlink=attributes.nlink,
|
|
117
|
+
user_name=attributes.user_name,
|
|
118
|
+
group_name=attributes.group_name,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def StreamOperations(
|
|
122
|
+
self, request_iterator: Iterator[OperationRequest], context: Any
|
|
123
|
+
) -> Iterator[OperationResponse]:
|
|
124
|
+
"""Handle bidirectional streaming of operations."""
|
|
125
|
+
for request in request_iterator:
|
|
126
|
+
request_id = request.request_id
|
|
127
|
+
|
|
128
|
+
# Format operation string once for logging
|
|
129
|
+
request_description = OsEnvRequestLogging.format(request)
|
|
130
|
+
|
|
131
|
+
# Determine which operation was requested
|
|
132
|
+
which = request.WhichOneof("operation")
|
|
133
|
+
|
|
134
|
+
if which == "get_attributes":
|
|
135
|
+
yield self._handle_get_attributes(request_id, request.get_attributes, request_description)
|
|
136
|
+
elif which == "read_file":
|
|
137
|
+
yield self._handle_read_file(request_id, request.read_file, request_description)
|
|
138
|
+
elif which == "write_file":
|
|
139
|
+
yield self._handle_write_file(request_id, request.write_file, request_description)
|
|
140
|
+
elif which == "append_to_file":
|
|
141
|
+
yield self._handle_append_to_file(request_id, request.append_to_file, request_description)
|
|
142
|
+
elif which == "mkdirs":
|
|
143
|
+
yield self._handle_mkdirs(request_id, request.mkdirs, request_description)
|
|
144
|
+
elif which == "is_file":
|
|
145
|
+
yield self._handle_is_file(request_id, request.is_file, request_description)
|
|
146
|
+
elif which == "is_directory":
|
|
147
|
+
yield self._handle_is_directory(request_id, request.is_directory, request_description)
|
|
148
|
+
elif which == "exists":
|
|
149
|
+
yield self._handle_exists(request_id, request.exists, request_description)
|
|
150
|
+
elif which == "copy":
|
|
151
|
+
yield self._handle_copy(request_id, request.copy, request_description)
|
|
152
|
+
elif which == "delete_recursively":
|
|
153
|
+
yield self._handle_delete_recursively(request_id, request.delete_recursively, request_description)
|
|
154
|
+
elif which == "delete_file":
|
|
155
|
+
yield self._handle_delete_file(request_id, request.delete_file, request_description)
|
|
156
|
+
elif which == "list_directory":
|
|
157
|
+
yield self._handle_list_directory(request_id, request.list_directory, request_description)
|
|
158
|
+
elif which == "glob":
|
|
159
|
+
yield self._handle_glob(request_id, request.glob, request_description)
|
|
160
|
+
elif which == "resolve_user_name":
|
|
161
|
+
yield self._handle_resolve_user_name(request_id, request.resolve_user_name, request_description)
|
|
162
|
+
elif which == "run_child_process":
|
|
163
|
+
yield self._handle_run_child_process(request_id, request.run_child_process, request_description)
|
|
164
|
+
elif which == "resolve_path":
|
|
165
|
+
yield self._handle_resolve_path(request_id, request.resolve_path, request_description)
|
|
166
|
+
elif which == "traverse":
|
|
167
|
+
yield self._handle_traverse(request_id, request.traverse, request_description)
|
|
168
|
+
elif which == "run_django_dev_server":
|
|
169
|
+
yield self._handle_run_django_dev_server(request_id, request.run_django_dev_server, request_description)
|
|
170
|
+
elif which == "grep":
|
|
171
|
+
yield self._handle_grep(request_id, request.grep, request_description)
|
|
172
|
+
elif which == "list_tree":
|
|
173
|
+
yield self._handle_list_tree(request_id, request.list_tree, request_description)
|
|
174
|
+
|
|
175
|
+
def _handle_get_attributes(
|
|
176
|
+
self, request_id: str, request: GetAttributesRequest, operation_details_to_log: str
|
|
177
|
+
) -> OperationResponse:
|
|
178
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
179
|
+
try:
|
|
180
|
+
attributes = self._os_env.get_attributes(ProjectPath.from_string(request.path))
|
|
181
|
+
proto_attributes = self._to_proto_attributes(attributes)
|
|
182
|
+
return OperationResponse(
|
|
183
|
+
request_id=request_id,
|
|
184
|
+
get_attributes=GetAttributesResponse(attributes=proto_attributes),
|
|
185
|
+
)
|
|
186
|
+
except OsEnvironmentException as e:
|
|
187
|
+
return OperationResponse(
|
|
188
|
+
request_id=request_id,
|
|
189
|
+
get_attributes=GetAttributesResponse(error=self.create_error_info_from_exception(e)),
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def _handle_read_file(
|
|
193
|
+
self, request_id: str, request: ReadFileRequest, operation_details_to_log: str
|
|
194
|
+
) -> OperationResponse:
|
|
195
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
196
|
+
try:
|
|
197
|
+
max_allowed = (
|
|
198
|
+
request.max_allowed_file_size_bytes if request.max_allowed_file_size_bytes > 0 else 20 * 1024 * 1024
|
|
199
|
+
)
|
|
200
|
+
offset_line = request.offset_line if request.HasField("offset_line") else None
|
|
201
|
+
limit_lines = request.limit_lines if request.HasField("limit_lines") else None
|
|
202
|
+
content, attributes, truncated = self._os_env.read_file_ex(
|
|
203
|
+
ProjectPath.from_string(request.path),
|
|
204
|
+
max_allowed_file_size_bytes=max_allowed,
|
|
205
|
+
offset_line=offset_line,
|
|
206
|
+
limit_lines=limit_lines,
|
|
207
|
+
)
|
|
208
|
+
proto_attributes = self._to_proto_attributes(attributes)
|
|
209
|
+
return OperationResponse(
|
|
210
|
+
request_id=request_id,
|
|
211
|
+
read_file=ReadFileResponse(content=content, attributes=proto_attributes, truncated=truncated),
|
|
212
|
+
)
|
|
213
|
+
except MaxFileSizeExceededException as e:
|
|
214
|
+
# Include file size in attributes so client can construct proper exception
|
|
215
|
+
attrs_with_size = EntryAttributesProto(
|
|
216
|
+
size=e.file_size,
|
|
217
|
+
last_modified=0,
|
|
218
|
+
mode=0,
|
|
219
|
+
nlink=0,
|
|
220
|
+
user_name="",
|
|
221
|
+
group_name="",
|
|
222
|
+
)
|
|
223
|
+
return OperationResponse(
|
|
224
|
+
request_id=request_id,
|
|
225
|
+
read_file=ReadFileResponse(
|
|
226
|
+
error=self.create_error_info_from_exception(e),
|
|
227
|
+
attributes=attrs_with_size,
|
|
228
|
+
content="",
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
except OsEnvironmentException as e:
|
|
232
|
+
return OperationResponse(
|
|
233
|
+
request_id=request_id,
|
|
234
|
+
read_file=ReadFileResponse(
|
|
235
|
+
error=self.create_error_info_from_exception(e),
|
|
236
|
+
attributes=EntryAttributesProto(),
|
|
237
|
+
content="",
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def _handle_write_file(
|
|
242
|
+
self, request_id: str, request: WriteFileRequest, operation_details_to_log: str
|
|
243
|
+
) -> OperationResponse:
|
|
244
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
245
|
+
try:
|
|
246
|
+
path = ProjectPath.from_string(request.path)
|
|
247
|
+
|
|
248
|
+
expected_state = (
|
|
249
|
+
self._proto_to_file_state(request.expected_state) if request.HasField("expected_state") else None
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
new_last_modified = self._os_env.write_file(path, request.content, expected_state=expected_state)
|
|
253
|
+
|
|
254
|
+
return OperationResponse(
|
|
255
|
+
request_id=request_id,
|
|
256
|
+
write_file=WriteFileResponse(new_last_modified=new_last_modified),
|
|
257
|
+
)
|
|
258
|
+
except OsEnvironmentException as e:
|
|
259
|
+
return OperationResponse(
|
|
260
|
+
request_id=request_id,
|
|
261
|
+
write_file=WriteFileResponse(error=self.create_error_info_from_exception(e)),
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
def _handle_append_to_file(
|
|
265
|
+
self, request_id: str, request: AppendToFileRequest, operation_details_to_log: str
|
|
266
|
+
) -> OperationResponse:
|
|
267
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
268
|
+
try:
|
|
269
|
+
path = ProjectPath.from_string(request.path)
|
|
270
|
+
|
|
271
|
+
expected_state = (
|
|
272
|
+
self._proto_to_file_state(request.expected_state) if request.HasField("expected_state") else None
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
new_last_modified = self._os_env.append_to_file(path, request.content, expected_state=expected_state)
|
|
276
|
+
|
|
277
|
+
return OperationResponse(
|
|
278
|
+
request_id=request_id,
|
|
279
|
+
append_to_file=AppendToFileResponse(new_last_modified=new_last_modified),
|
|
280
|
+
)
|
|
281
|
+
except OsEnvironmentException as e:
|
|
282
|
+
return OperationResponse(
|
|
283
|
+
request_id=request_id,
|
|
284
|
+
append_to_file=AppendToFileResponse(error=self.create_error_info_from_exception(e)),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
def _handle_mkdirs(
|
|
288
|
+
self, request_id: str, request: MkdirsRequest, operation_details_to_log: str
|
|
289
|
+
) -> OperationResponse:
|
|
290
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
291
|
+
try:
|
|
292
|
+
self._os_env.mkdirs(ProjectPath.from_string(request.path))
|
|
293
|
+
return OperationResponse(
|
|
294
|
+
request_id=request_id,
|
|
295
|
+
mkdirs=MkdirsResponse(),
|
|
296
|
+
)
|
|
297
|
+
except OsEnvironmentException as e:
|
|
298
|
+
return OperationResponse(
|
|
299
|
+
request_id=request_id,
|
|
300
|
+
mkdirs=MkdirsResponse(error=self.create_error_info_from_exception(e)),
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
def _handle_is_file(
|
|
304
|
+
self, request_id: str, request: IsFileRequest, operation_details_to_log: str
|
|
305
|
+
) -> OperationResponse:
|
|
306
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
307
|
+
try:
|
|
308
|
+
result = self._os_env.is_file(ProjectPath.from_string(request.path))
|
|
309
|
+
return OperationResponse(
|
|
310
|
+
request_id=request_id,
|
|
311
|
+
is_file=IsFileResponse(result=result),
|
|
312
|
+
)
|
|
313
|
+
except OsEnvironmentException as e:
|
|
314
|
+
return OperationResponse(
|
|
315
|
+
request_id=request_id,
|
|
316
|
+
is_file=IsFileResponse(result=False, error=self.create_error_info_from_exception(e)),
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
def _handle_is_directory(
|
|
320
|
+
self, request_id: str, request: IsDirectoryRequest, operation_details_to_log: str
|
|
321
|
+
) -> OperationResponse:
|
|
322
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
323
|
+
try:
|
|
324
|
+
result = self._os_env.is_directory(ProjectPath.from_string(request.path))
|
|
325
|
+
return OperationResponse(
|
|
326
|
+
request_id=request_id,
|
|
327
|
+
is_directory=IsDirectoryResponse(result=result),
|
|
328
|
+
)
|
|
329
|
+
except OsEnvironmentException as e:
|
|
330
|
+
return OperationResponse(
|
|
331
|
+
request_id=request_id,
|
|
332
|
+
is_directory=IsDirectoryResponse(result=False, error=self.create_error_info_from_exception(e)),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def _handle_exists(
|
|
336
|
+
self, request_id: str, request: ExistsRequest, operation_details_to_log: str
|
|
337
|
+
) -> OperationResponse:
|
|
338
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
339
|
+
try:
|
|
340
|
+
result = self._os_env.exists(ProjectPath.from_string(request.path))
|
|
341
|
+
return OperationResponse(
|
|
342
|
+
request_id=request_id,
|
|
343
|
+
exists=ExistsResponse(result=result),
|
|
344
|
+
)
|
|
345
|
+
except OsEnvironmentException as e:
|
|
346
|
+
return OperationResponse(
|
|
347
|
+
request_id=request_id,
|
|
348
|
+
exists=ExistsResponse(result=False, error=self.create_error_info_from_exception(e)),
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
def _handle_copy(self, request_id: str, request: CopyRequest, operation_details_to_log: str) -> OperationResponse:
|
|
352
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
353
|
+
try:
|
|
354
|
+
self._os_env.copy(
|
|
355
|
+
ProjectPath.from_string(request.source_path), ProjectPath.from_string(request.dest_path)
|
|
356
|
+
)
|
|
357
|
+
return OperationResponse(
|
|
358
|
+
request_id=request_id,
|
|
359
|
+
copy=CopyResponse(),
|
|
360
|
+
)
|
|
361
|
+
except OsEnvironmentException as e:
|
|
362
|
+
return OperationResponse(
|
|
363
|
+
request_id=request_id,
|
|
364
|
+
copy=CopyResponse(error=self.create_error_info_from_exception(e)),
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def _handle_delete_recursively(
|
|
368
|
+
self, request_id: str, request: DeleteRecursivelyRequest, operation_details_to_log: str
|
|
369
|
+
) -> OperationResponse:
|
|
370
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
371
|
+
try:
|
|
372
|
+
self._os_env.delete_recursively(ProjectPath.from_string(request.path))
|
|
373
|
+
return OperationResponse(
|
|
374
|
+
request_id=request_id,
|
|
375
|
+
delete_recursively=DeleteRecursivelyResponse(),
|
|
376
|
+
)
|
|
377
|
+
except OsEnvironmentException as e:
|
|
378
|
+
return OperationResponse(
|
|
379
|
+
request_id=request_id,
|
|
380
|
+
delete_recursively=DeleteRecursivelyResponse(error=self.create_error_info_from_exception(e)),
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def _handle_delete_file(
|
|
384
|
+
self, request_id: str, request: DeleteFileRequest, operation_details_to_log: str
|
|
385
|
+
) -> OperationResponse:
|
|
386
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
387
|
+
try:
|
|
388
|
+
self._os_env.delete_file(ProjectPath.from_string(request.path))
|
|
389
|
+
return OperationResponse(
|
|
390
|
+
request_id=request_id,
|
|
391
|
+
delete_file=DeleteFileResponse(),
|
|
392
|
+
)
|
|
393
|
+
except OsEnvironmentException as e:
|
|
394
|
+
return OperationResponse(
|
|
395
|
+
request_id=request_id,
|
|
396
|
+
delete_file=DeleteFileResponse(error=self.create_error_info_from_exception(e)),
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
def _handle_list_directory(
|
|
400
|
+
self, request_id: str, request: ListDirectoryRequest, operation_details_to_log: str
|
|
401
|
+
) -> OperationResponse:
|
|
402
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
403
|
+
try:
|
|
404
|
+
entries = self._os_env.list_directory(ProjectPath.from_string(request.path))
|
|
405
|
+
return OperationResponse(
|
|
406
|
+
request_id=request_id,
|
|
407
|
+
list_directory=ListDirectoryResponse(entries=entries),
|
|
408
|
+
)
|
|
409
|
+
except OsEnvironmentException as e:
|
|
410
|
+
return OperationResponse(
|
|
411
|
+
request_id=request_id,
|
|
412
|
+
list_directory=ListDirectoryResponse(error=self.create_error_info_from_exception(e)),
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
def _handle_glob(self, request_id: str, request: GlobRequest, operation_details_to_log: str) -> OperationResponse:
|
|
416
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
417
|
+
try:
|
|
418
|
+
path = ProjectPath.from_string(request.path) if request.path else None
|
|
419
|
+
paths = self._os_env.glob(request.pattern, path)
|
|
420
|
+
return OperationResponse(
|
|
421
|
+
request_id=request_id,
|
|
422
|
+
glob=GlobResponse(paths=[str(p) for p in paths]),
|
|
423
|
+
)
|
|
424
|
+
except OsEnvironmentException as e:
|
|
425
|
+
return OperationResponse(
|
|
426
|
+
request_id=request_id,
|
|
427
|
+
glob=GlobResponse(error=self.create_error_info_from_exception(e)),
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
def _handle_grep(self, request_id: str, request: GrepRequest, operation_details_to_log: str) -> OperationResponse:
|
|
431
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
432
|
+
try:
|
|
433
|
+
# Convert proto output mode to enum
|
|
434
|
+
output_mode_map = {
|
|
435
|
+
GrepOutputModeProto.GREP_OUTPUT_MODE_FILES_WITH_MATCHES: GrepOutputMode.FILES_WITH_MATCHES,
|
|
436
|
+
GrepOutputModeProto.GREP_OUTPUT_MODE_CONTENT: GrepOutputMode.CONTENT,
|
|
437
|
+
GrepOutputModeProto.GREP_OUTPUT_MODE_COUNT: GrepOutputMode.COUNT,
|
|
438
|
+
}
|
|
439
|
+
output_mode = output_mode_map.get(request.output_mode, GrepOutputMode.FILES_WITH_MATCHES)
|
|
440
|
+
|
|
441
|
+
path = ProjectPath.from_string(request.path) if request.path else None
|
|
442
|
+
|
|
443
|
+
result = self._os_env.grep(
|
|
444
|
+
pattern=request.pattern,
|
|
445
|
+
path=path,
|
|
446
|
+
glob_filter=request.glob if request.glob else None,
|
|
447
|
+
output_mode=output_mode,
|
|
448
|
+
context_before=request.context_before if request.HasField("context_before") else None,
|
|
449
|
+
context_after=request.context_after if request.HasField("context_after") else None,
|
|
450
|
+
context_both=request.context_both if request.HasField("context_both") else None,
|
|
451
|
+
show_line_numbers=request.show_line_numbers if request.HasField("show_line_numbers") else True,
|
|
452
|
+
case_insensitive=request.case_insensitive if request.HasField("case_insensitive") else False,
|
|
453
|
+
file_type=request.file_type if request.file_type else None,
|
|
454
|
+
head_limit=request.head_limit if request.HasField("head_limit") else None,
|
|
455
|
+
offset=request.offset if request.HasField("offset") else None,
|
|
456
|
+
multiline=request.multiline if request.HasField("multiline") else False,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
return OperationResponse(
|
|
460
|
+
request_id=request_id,
|
|
461
|
+
grep=GrepResponse(
|
|
462
|
+
matching_files=result.matching_files,
|
|
463
|
+
matches=[
|
|
464
|
+
GrepMatchProto(
|
|
465
|
+
file_path=m.file_path,
|
|
466
|
+
line_number=m.line_number,
|
|
467
|
+
line_content=m.line_content,
|
|
468
|
+
)
|
|
469
|
+
for m in result.matches
|
|
470
|
+
],
|
|
471
|
+
file_counts=[
|
|
472
|
+
GrepFileCountProto(
|
|
473
|
+
file_path=fc.file_path,
|
|
474
|
+
match_count=fc.match_count,
|
|
475
|
+
)
|
|
476
|
+
for fc in result.file_counts
|
|
477
|
+
],
|
|
478
|
+
),
|
|
479
|
+
)
|
|
480
|
+
except OsEnvironmentException as e:
|
|
481
|
+
return OperationResponse(
|
|
482
|
+
request_id=request_id,
|
|
483
|
+
grep=GrepResponse(error=self.create_error_info_from_exception(e)),
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
def _handle_resolve_user_name(
|
|
487
|
+
self, request_id: str, request: ResolveUserNameRequest, operation_details_to_log: str
|
|
488
|
+
) -> OperationResponse:
|
|
489
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
490
|
+
try:
|
|
491
|
+
username = self._os_env.resolve_user_name()
|
|
492
|
+
return OperationResponse(
|
|
493
|
+
request_id=request_id,
|
|
494
|
+
resolve_user_name=ResolveUserNameResponse(username=username),
|
|
495
|
+
)
|
|
496
|
+
except OsEnvironmentException as e:
|
|
497
|
+
return OperationResponse(
|
|
498
|
+
request_id=request_id,
|
|
499
|
+
resolve_user_name=ResolveUserNameResponse(
|
|
500
|
+
username="", error=self.create_error_info_from_exception(e)
|
|
501
|
+
),
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
def _handle_run_child_process(
|
|
505
|
+
self, request_id: str, request: RunChildProcessRequest, operation_details_to_log: str
|
|
506
|
+
) -> OperationResponse:
|
|
507
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
508
|
+
try:
|
|
509
|
+
redirected_output = (
|
|
510
|
+
ProjectPath.from_string(request.redirected_output_path) if request.redirected_output_path else None
|
|
511
|
+
)
|
|
512
|
+
output_limit_chars = request.output_limit_chars if request.HasField("output_limit_chars") else None
|
|
513
|
+
result = self._os_env.run_child_process(
|
|
514
|
+
args=list(request.args),
|
|
515
|
+
cwd=ProjectPath.from_string(request.cwd_relative),
|
|
516
|
+
timeout=request.timeout,
|
|
517
|
+
check=request.check,
|
|
518
|
+
redirected_output_path=redirected_output,
|
|
519
|
+
shell=request.shell,
|
|
520
|
+
output_limit_chars=output_limit_chars,
|
|
521
|
+
interactive_mode=request.interactive_mode,
|
|
522
|
+
)
|
|
523
|
+
proto_result = ChildProcessResultProto(
|
|
524
|
+
exit_code=result.exit_code,
|
|
525
|
+
stdout=result.stdout,
|
|
526
|
+
stderr=result.stderr,
|
|
527
|
+
)
|
|
528
|
+
return OperationResponse(
|
|
529
|
+
request_id=request_id,
|
|
530
|
+
run_child_process=RunChildProcessResponse(
|
|
531
|
+
result=proto_result, outcome=ChildProcessOutcome.PROCESS_FINISHED
|
|
532
|
+
),
|
|
533
|
+
)
|
|
534
|
+
except ChildProcessFailedToStartException as e:
|
|
535
|
+
return OperationResponse(
|
|
536
|
+
request_id=request_id,
|
|
537
|
+
run_child_process=RunChildProcessResponse(
|
|
538
|
+
error=self.create_error_info_from_exception(e),
|
|
539
|
+
outcome=ChildProcessOutcome.FAILED_TO_START_PROCESS,
|
|
540
|
+
),
|
|
541
|
+
)
|
|
542
|
+
except ChildProcessTimedOutException as e:
|
|
543
|
+
return OperationResponse(
|
|
544
|
+
request_id=request_id,
|
|
545
|
+
run_child_process=RunChildProcessResponse(
|
|
546
|
+
result=ChildProcessResultProto(exit_code=-1, stdout=e.stdout, stderr=e.stderr),
|
|
547
|
+
error=self.create_error_info_from_exception(e),
|
|
548
|
+
outcome=ChildProcessOutcome.PROCESS_TIMED_OUT,
|
|
549
|
+
),
|
|
550
|
+
)
|
|
551
|
+
except OsEnvironmentException as e:
|
|
552
|
+
# This catches other generic OsEnvironmentException errors that are not specifically
|
|
553
|
+
# ChildProcessFailedToStartException or ChildProcessTimedOutException.
|
|
554
|
+
# Since we don't know what happened, we use UNKNOWN outcome.
|
|
555
|
+
return OperationResponse(
|
|
556
|
+
request_id=request_id,
|
|
557
|
+
run_child_process=RunChildProcessResponse(
|
|
558
|
+
error=self.create_error_info_from_exception(e),
|
|
559
|
+
outcome=ChildProcessOutcome.UNKNOWN,
|
|
560
|
+
),
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
def _handle_resolve_path(
|
|
564
|
+
self, request_id: str, request: ResolvePathRequest, operation_details_to_log: str
|
|
565
|
+
) -> OperationResponse:
|
|
566
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
567
|
+
try:
|
|
568
|
+
resolved = self._os_env.resolve_path(ProjectPath.from_string(request.path))
|
|
569
|
+
return OperationResponse(
|
|
570
|
+
request_id=request_id,
|
|
571
|
+
resolve_path=ResolvePathResponse(resolved_path=str(resolved)),
|
|
572
|
+
)
|
|
573
|
+
except OsEnvironmentException as e:
|
|
574
|
+
return OperationResponse(
|
|
575
|
+
request_id=request_id,
|
|
576
|
+
resolve_path=ResolvePathResponse(resolved_path="", error=self.create_error_info_from_exception(e)),
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
def _handle_traverse(
|
|
580
|
+
self, request_id: str, request: TraverseRequest, operation_details_to_log: str
|
|
581
|
+
) -> OperationResponse:
|
|
582
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
583
|
+
try:
|
|
584
|
+
paths = self._os_env.traverse(
|
|
585
|
+
prune_dirnames=set(request.prune_dirnames),
|
|
586
|
+
skip_file_basenames=set(request.skip_file_basenames),
|
|
587
|
+
required_extension=request.required_extension,
|
|
588
|
+
)
|
|
589
|
+
return OperationResponse(
|
|
590
|
+
request_id=request_id,
|
|
591
|
+
traverse=TraverseResponse(paths=[str(p) for p in paths]),
|
|
592
|
+
)
|
|
593
|
+
except OsEnvironmentException as e:
|
|
594
|
+
return OperationResponse(
|
|
595
|
+
request_id=request_id,
|
|
596
|
+
traverse=TraverseResponse(error=self.create_error_info_from_exception(e)),
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
def _handle_list_tree(
|
|
600
|
+
self, request_id: str, request: ListTreeRequest, operation_details_to_log: str
|
|
601
|
+
) -> OperationResponse:
|
|
602
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
603
|
+
try:
|
|
604
|
+
path = ProjectPath.from_string(request.path) if request.path else None
|
|
605
|
+
max_depth = request.max_depth if request.HasField("max_depth") else None
|
|
606
|
+
max_entries = request.max_entries if request.HasField("max_entries") else None
|
|
607
|
+
entries, truncated = self._os_env.list_tree(path=path, max_depth=max_depth, max_entries=max_entries)
|
|
608
|
+
return OperationResponse(
|
|
609
|
+
request_id=request_id,
|
|
610
|
+
list_tree=ListTreeResponse(
|
|
611
|
+
entries=[TreeEntryProto(path=e.path, is_directory=e.is_directory) for e in entries],
|
|
612
|
+
truncated=truncated,
|
|
613
|
+
),
|
|
614
|
+
)
|
|
615
|
+
except OsEnvironmentException as e:
|
|
616
|
+
return OperationResponse(
|
|
617
|
+
request_id=request_id,
|
|
618
|
+
list_tree=ListTreeResponse(error=self.create_error_info_from_exception(e)),
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
def _handle_run_django_dev_server(
|
|
622
|
+
self, request_id: str, request: RunDjangoDevServerRequest, operation_details_to_log: str
|
|
623
|
+
) -> OperationResponse:
|
|
624
|
+
with LoggingUtil.Span(operation_details_to_log):
|
|
625
|
+
try:
|
|
626
|
+
# Convert proto enum to Python enum
|
|
627
|
+
run_mode = (
|
|
628
|
+
DjangoServerRunMode.SHUTDOWN_WHEN_READY
|
|
629
|
+
if request.run_mode == DjangoServerRunModeProto.DJANGO_SERVER_RUN_MODE_SHUTDOWN_WHEN_READY
|
|
630
|
+
else DjangoServerRunMode.INTERACTIVE
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
local_console_printer = None
|
|
634
|
+
if self._console and run_mode == DjangoServerRunMode.INTERACTIVE:
|
|
635
|
+
console = self._console
|
|
636
|
+
|
|
637
|
+
def _print(line: str, _output_type: OutputType) -> None:
|
|
638
|
+
console.print(line, markup=False, highlight=False)
|
|
639
|
+
|
|
640
|
+
local_console_printer = _print
|
|
641
|
+
|
|
642
|
+
success, error_output = self._os_env.run_django_dev_server(
|
|
643
|
+
server_readiness_timeout=request.server_readiness_timeout_sec,
|
|
644
|
+
run_mode=run_mode,
|
|
645
|
+
output_printer=local_console_printer,
|
|
646
|
+
)
|
|
647
|
+
return OperationResponse(
|
|
648
|
+
request_id=request_id,
|
|
649
|
+
run_django_dev_server=RunDjangoDevServerResponse(
|
|
650
|
+
success=success,
|
|
651
|
+
error_output=error_output if error_output else None,
|
|
652
|
+
),
|
|
653
|
+
)
|
|
654
|
+
except OsEnvironmentException as e:
|
|
655
|
+
return OperationResponse(
|
|
656
|
+
request_id=request_id,
|
|
657
|
+
run_django_dev_server=RunDjangoDevServerResponse(
|
|
658
|
+
success=False, error=self.create_error_info_from_exception(e)
|
|
659
|
+
),
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
@staticmethod
|
|
663
|
+
def _proto_to_file_state(proto_state: "os_env_pb2.FileState | None") -> FileState | None:
|
|
664
|
+
if proto_state is None:
|
|
665
|
+
return None
|
|
666
|
+
|
|
667
|
+
state_type = proto_state.WhichOneof("state")
|
|
668
|
+
match state_type:
|
|
669
|
+
case "exists_with_last_modified_timestamp":
|
|
670
|
+
return ExistsWithLastModifiedTimestamp(
|
|
671
|
+
timestamp=proto_state.exists_with_last_modified_timestamp.timestamp
|
|
672
|
+
)
|
|
673
|
+
case "missing":
|
|
674
|
+
return Missing()
|
|
675
|
+
case _:
|
|
676
|
+
return None
|