deepy-cli 0.1.12__tar.gz → 0.1.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/PKG-INFO +2 -3
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/README.md +1 -1
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/pyproject.toml +1 -2
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/__init__.py +1 -1
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/local_command.py +82 -54
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/__main__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/cli.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/config/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/config/settings.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/AskUserQuestion.md +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/WebFetch.md +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/WebSearch.md +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/edit.md +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/modify.md +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/read.md +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/shell.md +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/data/tools/write.md +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/errors.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/agent.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/compaction.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/context.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/events.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/model_capabilities.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/provider.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/replay.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/runner.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/llm/thinking.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/prompts/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/prompts/compact.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/prompts/rules.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/prompts/runtime_context.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/prompts/system.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/prompts/tool_docs.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/sessions/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/sessions/jsonl.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/sessions/manager.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/skills.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/status.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/tools/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/tools/agents.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/tools/builtin.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/tools/file_state.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/tools/result.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/tools/shell_utils.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/app.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/ask_user_question.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/exit_summary.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/file_mentions.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/loading_text.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/markdown.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/message_view.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/model_picker.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/prompt_buffer.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/prompt_input.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/session_list.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/session_picker.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/slash_commands.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/styles.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/terminal.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/theme_picker.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/thinking_state.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/ui/welcome.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/update_check.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/usage.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/utils/__init__.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/utils/debug_logger.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/utils/error_logger.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/utils/json.py +0 -0
- {deepy_cli-0.1.12 → deepy_cli-0.1.13}/src/deepy/utils/notify.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: deepy-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: Deepy - Vibe coding for DeepSeek models in your terminal
|
|
5
5
|
Keywords: deepseek,coding-agent,terminal,cli,agents
|
|
6
6
|
Author: kirineko
|
|
@@ -17,7 +17,6 @@ Requires-Dist: openai>=2.26,<3
|
|
|
17
17
|
Requires-Dist: orjson>=3.10,<4
|
|
18
18
|
Requires-Dist: pydantic>=2.12,<3
|
|
19
19
|
Requires-Dist: prompt-toolkit>=3.0,<4
|
|
20
|
-
Requires-Dist: pywinpty>=2.0,<3 ; sys_platform == 'win32'
|
|
21
20
|
Requires-Dist: rich>=13.9,<15
|
|
22
21
|
Requires-Dist: tiktoken>=0.9,<1
|
|
23
22
|
Requires-Dist: tomli-w>=1
|
|
@@ -79,7 +78,7 @@ of hiding tool calls behind chat text.
|
|
|
79
78
|
- **Local command mode**: type `!cmd` to run a non-interactive local shell command
|
|
80
79
|
without sending it to the model; the result is still saved as context.
|
|
81
80
|
- **Cross-platform shell handling**: POSIX shell, PowerShell, cmd, Windows paths,
|
|
82
|
-
UTF-8 output, CRLF editing, and
|
|
81
|
+
UTF-8 output, CRLF editing, and non-interactive Windows local command mode.
|
|
83
82
|
|
|
84
83
|
## See It Work
|
|
85
84
|
|
|
@@ -50,7 +50,7 @@ of hiding tool calls behind chat text.
|
|
|
50
50
|
- **Local command mode**: type `!cmd` to run a non-interactive local shell command
|
|
51
51
|
without sending it to the model; the result is still saved as context.
|
|
52
52
|
- **Cross-platform shell handling**: POSIX shell, PowerShell, cmd, Windows paths,
|
|
53
|
-
UTF-8 output, CRLF editing, and
|
|
53
|
+
UTF-8 output, CRLF editing, and non-interactive Windows local command mode.
|
|
54
54
|
|
|
55
55
|
## See It Work
|
|
56
56
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "deepy-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.13"
|
|
4
4
|
description = "Deepy - Vibe coding for DeepSeek models in your terminal"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -23,7 +23,6 @@ dependencies = [
|
|
|
23
23
|
"orjson>=3.10,<4",
|
|
24
24
|
"pydantic>=2.12,<3",
|
|
25
25
|
"prompt-toolkit>=3.0,<4",
|
|
26
|
-
"pywinpty>=2.0,<3; sys_platform == 'win32'",
|
|
27
26
|
"rich>=13.9,<15",
|
|
28
27
|
"tiktoken>=0.9,<1",
|
|
29
28
|
"tomli-w>=1",
|
|
@@ -4,6 +4,7 @@ import errno
|
|
|
4
4
|
import contextlib
|
|
5
5
|
import os
|
|
6
6
|
import queue
|
|
7
|
+
import re
|
|
7
8
|
import select
|
|
8
9
|
import shutil
|
|
9
10
|
import signal
|
|
@@ -29,6 +30,21 @@ DEFAULT_LOCAL_COMMAND_TIMEOUT_MS = 120_000
|
|
|
29
30
|
DEFAULT_DISPLAY_OUTPUT_LIMIT = 30_000
|
|
30
31
|
DEFAULT_CONTEXT_OUTPUT_LIMIT = 8_000
|
|
31
32
|
_TRUNCATED_MARKER = "\n... output truncated ...\n"
|
|
33
|
+
_ANSI_CONTROL_RE = re.compile(
|
|
34
|
+
r"""
|
|
35
|
+
\x1b
|
|
36
|
+
(?:
|
|
37
|
+
\[[0-?]*[ -/]*[@-~]
|
|
38
|
+
|\][^\x07\x1b]*(?:\x07|\x1b\\)
|
|
39
|
+
|P[^\x1b]*(?:\x1b\\)
|
|
40
|
+
|_[^\x1b]*(?:\x1b\\)
|
|
41
|
+
|\^[^\x1b]*(?:\x1b\\)
|
|
42
|
+
|[@-Z\\-_]
|
|
43
|
+
)
|
|
44
|
+
""",
|
|
45
|
+
re.VERBOSE,
|
|
46
|
+
)
|
|
47
|
+
_TERMINAL_CONTROL_CHAR_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]")
|
|
32
48
|
|
|
33
49
|
|
|
34
50
|
@dataclass(frozen=True)
|
|
@@ -97,7 +113,7 @@ def run_local_command(
|
|
|
97
113
|
capture_limit = max(display_limit, context_limit) + len(_TRUNCATED_MARKER)
|
|
98
114
|
|
|
99
115
|
if runtime.os_family == "windows":
|
|
100
|
-
return
|
|
116
|
+
return _run_windows_pipes(
|
|
101
117
|
command,
|
|
102
118
|
cwd=cwd,
|
|
103
119
|
env=process_env,
|
|
@@ -155,6 +171,8 @@ def shell_tool_result_json(
|
|
|
155
171
|
output: str | None = None,
|
|
156
172
|
) -> str:
|
|
157
173
|
rendered_output = result.context_output if output is None else output
|
|
174
|
+
if result.os_family == "windows":
|
|
175
|
+
rendered_output = _sanitize_terminal_output(rendered_output)
|
|
158
176
|
metadata = _shell_metadata(result)
|
|
159
177
|
if result.ok:
|
|
160
178
|
return ToolResult.ok_result("shell", rendered_output, metadata=metadata).to_json()
|
|
@@ -270,7 +288,7 @@ def _run_posix_pty(
|
|
|
270
288
|
)
|
|
271
289
|
|
|
272
290
|
|
|
273
|
-
def
|
|
291
|
+
def _run_windows_pipes(
|
|
274
292
|
command: str,
|
|
275
293
|
*,
|
|
276
294
|
cwd: Path,
|
|
@@ -284,70 +302,56 @@ def _run_windows_pty(
|
|
|
284
302
|
started_at: float,
|
|
285
303
|
should_interrupt: Callable[[], bool] | None,
|
|
286
304
|
) -> LocalCommandResult:
|
|
287
|
-
|
|
288
|
-
from winpty import PtyProcess # type: ignore[import-not-found]
|
|
289
|
-
except Exception:
|
|
290
|
-
error = "Windows local command mode requires the pywinpty package."
|
|
291
|
-
return _error_result(
|
|
292
|
-
command,
|
|
293
|
-
cwd=cwd,
|
|
294
|
-
runtime=runtime,
|
|
295
|
-
shell_path=shell_path,
|
|
296
|
-
timeout_ms=timeout_ms,
|
|
297
|
-
started_at=started_at,
|
|
298
|
-
tty_mode="unavailable",
|
|
299
|
-
error=error,
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
process = None
|
|
305
|
+
process: subprocess.Popen[bytes] | None = None
|
|
303
306
|
timed_out = False
|
|
304
307
|
interrupted = False
|
|
305
|
-
captured =
|
|
308
|
+
captured = bytearray()
|
|
306
309
|
capture_truncated = False
|
|
307
|
-
output_queue: queue.Queue[
|
|
308
|
-
stop_reader = threading.Event()
|
|
310
|
+
output_queue: queue.Queue[bytes] = queue.Queue()
|
|
309
311
|
try:
|
|
310
|
-
process =
|
|
312
|
+
process = subprocess.Popen(
|
|
311
313
|
[shell_path, *_shell_args(runtime, command)],
|
|
312
314
|
cwd=str(cwd),
|
|
313
315
|
env=env,
|
|
316
|
+
stdin=subprocess.DEVNULL,
|
|
317
|
+
stdout=subprocess.PIPE,
|
|
318
|
+
stderr=subprocess.STDOUT,
|
|
319
|
+
close_fds=True,
|
|
314
320
|
)
|
|
315
|
-
reader = threading.Thread(
|
|
316
|
-
target=_read_windows_pty_output,
|
|
317
|
-
args=(process, output_queue, stop_reader),
|
|
318
|
-
daemon=True,
|
|
319
|
-
)
|
|
321
|
+
reader = threading.Thread(target=_read_pipe_output, args=(process, output_queue), daemon=True)
|
|
320
322
|
reader.start()
|
|
321
323
|
deadline = started_at + timeout_ms / 1000
|
|
322
324
|
while True:
|
|
323
|
-
|
|
325
|
+
drained_truncated = _drain_bytes_queue(
|
|
324
326
|
output_queue,
|
|
325
327
|
captured,
|
|
326
328
|
capture_limit,
|
|
327
329
|
)
|
|
328
330
|
capture_truncated = capture_truncated or drained_truncated
|
|
329
|
-
if
|
|
331
|
+
if process.poll() is not None:
|
|
330
332
|
break
|
|
331
333
|
if callable(should_interrupt) and should_interrupt():
|
|
332
334
|
interrupted = True
|
|
333
|
-
process
|
|
335
|
+
_terminate_windows_process(process)
|
|
334
336
|
break
|
|
335
337
|
remaining = deadline - time.monotonic()
|
|
336
338
|
if remaining <= 0:
|
|
337
339
|
timed_out = True
|
|
338
|
-
process
|
|
340
|
+
_terminate_windows_process(process)
|
|
339
341
|
break
|
|
340
342
|
time.sleep(min(0.05, remaining))
|
|
341
|
-
stop_reader.set()
|
|
342
343
|
reader.join(timeout=0.2)
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
344
|
+
capture_truncated = (
|
|
345
|
+
capture_truncated or _drain_bytes_queue(output_queue, captured, capture_limit)
|
|
346
|
+
)
|
|
347
|
+
if len(captured) >= capture_limit:
|
|
348
|
+
capture_truncated = True
|
|
349
|
+
exit_code = process.poll()
|
|
350
|
+
output = _sanitize_terminal_output(_decode_output(bytes(captured)))
|
|
347
351
|
error = _command_error(exit_code, timed_out=timed_out, interrupted=interrupted)
|
|
348
352
|
except Exception as exc:
|
|
349
353
|
exit_code = None
|
|
350
|
-
output = captured
|
|
354
|
+
output = _sanitize_terminal_output(_decode_output(bytes(captured)))
|
|
351
355
|
error = str(exc)
|
|
352
356
|
|
|
353
357
|
display_output, display_truncated = _limit_output(output, display_limit)
|
|
@@ -364,7 +368,7 @@ def _run_windows_pty(
|
|
|
364
368
|
command_dialect=runtime.command_dialect,
|
|
365
369
|
path_style=runtime.path_style,
|
|
366
370
|
os_family=runtime.os_family,
|
|
367
|
-
tty_mode="
|
|
371
|
+
tty_mode="pipe",
|
|
368
372
|
duration_ms=_elapsed_ms(started_at),
|
|
369
373
|
timeout_ms=timeout_ms,
|
|
370
374
|
timed_out=timed_out,
|
|
@@ -376,36 +380,37 @@ def _run_windows_pty(
|
|
|
376
380
|
)
|
|
377
381
|
|
|
378
382
|
|
|
379
|
-
def
|
|
380
|
-
process:
|
|
381
|
-
output_queue: queue.Queue[
|
|
382
|
-
stop_reader: threading.Event,
|
|
383
|
+
def _read_pipe_output(
|
|
384
|
+
process: subprocess.Popen[bytes],
|
|
385
|
+
output_queue: queue.Queue[bytes],
|
|
383
386
|
) -> None:
|
|
384
|
-
|
|
387
|
+
stream = process.stdout
|
|
388
|
+
if stream is None:
|
|
389
|
+
return
|
|
390
|
+
while True:
|
|
385
391
|
try:
|
|
386
|
-
chunk =
|
|
392
|
+
chunk = stream.read1(4096)
|
|
387
393
|
except Exception:
|
|
388
394
|
return
|
|
389
|
-
if chunk:
|
|
390
|
-
output_queue.put(chunk)
|
|
391
|
-
elif not process.isalive():
|
|
395
|
+
if not chunk:
|
|
392
396
|
return
|
|
397
|
+
output_queue.put(chunk)
|
|
393
398
|
|
|
394
399
|
|
|
395
|
-
def
|
|
396
|
-
output_queue: queue.Queue[
|
|
397
|
-
captured:
|
|
400
|
+
def _drain_bytes_queue(
|
|
401
|
+
output_queue: queue.Queue[bytes],
|
|
402
|
+
captured: bytearray,
|
|
398
403
|
capture_limit: int,
|
|
399
|
-
) ->
|
|
404
|
+
) -> bool:
|
|
400
405
|
truncated = False
|
|
401
406
|
while True:
|
|
402
407
|
try:
|
|
403
408
|
chunk = output_queue.get_nowait()
|
|
404
409
|
except queue.Empty:
|
|
405
|
-
return
|
|
410
|
+
return truncated
|
|
406
411
|
if len(captured) < capture_limit:
|
|
407
412
|
remaining = capture_limit - len(captured)
|
|
408
|
-
captured
|
|
413
|
+
captured.extend(chunk[:remaining])
|
|
409
414
|
if len(chunk) > remaining:
|
|
410
415
|
truncated = True
|
|
411
416
|
else:
|
|
@@ -472,6 +477,20 @@ def _terminate_process(process: subprocess.Popen[bytes]) -> None:
|
|
|
472
477
|
process.wait(timeout=0.2)
|
|
473
478
|
|
|
474
479
|
|
|
480
|
+
def _terminate_windows_process(process: subprocess.Popen[bytes]) -> None:
|
|
481
|
+
if process.poll() is not None:
|
|
482
|
+
return
|
|
483
|
+
with contextlib.suppress(OSError):
|
|
484
|
+
process.terminate()
|
|
485
|
+
try:
|
|
486
|
+
process.wait(timeout=0.2)
|
|
487
|
+
except subprocess.TimeoutExpired:
|
|
488
|
+
with contextlib.suppress(OSError):
|
|
489
|
+
process.kill()
|
|
490
|
+
with contextlib.suppress(OSError, subprocess.TimeoutExpired):
|
|
491
|
+
process.wait(timeout=0.2)
|
|
492
|
+
|
|
493
|
+
|
|
475
494
|
def _limit_output(output: str, limit: int) -> tuple[str, bool]:
|
|
476
495
|
if limit <= 0:
|
|
477
496
|
return (_TRUNCATED_MARKER if output else ""), bool(output)
|
|
@@ -485,7 +504,16 @@ def _limit_output(output: str, limit: int) -> tuple[str, bool]:
|
|
|
485
504
|
|
|
486
505
|
|
|
487
506
|
def _decode_output(output: bytes) -> str:
|
|
488
|
-
return output.
|
|
507
|
+
return _normalize_line_endings(output.decode("utf-8", errors="replace"))
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def _sanitize_terminal_output(output: str) -> str:
|
|
511
|
+
normalized = _normalize_line_endings(output)
|
|
512
|
+
return _TERMINAL_CONTROL_CHAR_RE.sub("", _ANSI_CONTROL_RE.sub("", normalized))
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def _normalize_line_endings(output: str) -> str:
|
|
516
|
+
return output.replace("\r\n", "\n").replace("\r", "\n")
|
|
489
517
|
|
|
490
518
|
|
|
491
519
|
def _command_error(exit_code: int | None, *, timed_out: bool, interrupted: bool) -> str | None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|