dais-shell 0.1.1__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.
dais_shell/__init__.py ADDED
@@ -0,0 +1,62 @@
1
+ import platform
2
+ from typing import TypeAlias
3
+ from .env_builder import EnvBuilder
4
+ from .iostream_reader import IOStreamReaderResult, IOStreamReaderStatus
5
+ from .runtimes import BaseShellRuntime, BashRuntime, PowerShellRuntime
6
+ from .types import CommandStep
7
+ from .types.exceptions import ShellError, CommandNotFoundError, ShellRuntimeNotFoundError, ForbiddenShellTargetError
8
+ from .constants import DEFAULT_COMMAND_BLACKLIST
9
+
10
+ ShellResult: TypeAlias = IOStreamReaderResult
11
+ ShellResultStatus: TypeAlias = IOStreamReaderStatus
12
+
13
+ class AgentShell:
14
+ def __init__(self,
15
+ command_blacklist: set[str] | None = None,
16
+ env_extra: dict[str, str] | None = None,
17
+ max_lines: int = 10000,
18
+ ):
19
+ self._runtime = self._create_runtime(max_lines)
20
+ self._command_blacklist = command_blacklist or DEFAULT_COMMAND_BLACKLIST
21
+ self._env_builder = EnvBuilder(blacklist=None, extra=env_extra)
22
+
23
+ @staticmethod
24
+ def _create_runtime(max_lines: int) -> BaseShellRuntime:
25
+ if platform.system() == "Windows":
26
+ return PowerShellRuntime(max_lines)
27
+ else:
28
+ return BashRuntime(max_lines)
29
+
30
+ def run_sync(self,
31
+ step: CommandStep,
32
+ on_stdout=None,
33
+ on_stderr=None
34
+ ) -> ShellResult:
35
+ step.validate_command(self._command_blacklist)
36
+ step.env = (self._env_builder
37
+ .with_extra(step.env or {})
38
+ .build())
39
+ return self._runtime.run_sync(step, on_stdout, on_stderr)
40
+
41
+ async def run(self,
42
+ step: CommandStep,
43
+ on_stdout=None,
44
+ on_stderr=None
45
+ ) -> ShellResult:
46
+ step.validate_command(self._command_blacklist)
47
+ step.env = (self._env_builder
48
+ .with_extra(step.env or {})
49
+ .build())
50
+ return await self._runtime.run(step, on_stdout, on_stderr)
51
+
52
+ __all__ = [
53
+ "AgentShell",
54
+ "CommandStep",
55
+ "ShellResult",
56
+ "ShellResultStatus",
57
+
58
+ "ShellError",
59
+ "CommandNotFoundError",
60
+ "ShellRuntimeNotFoundError",
61
+ "ForbiddenShellTargetError",
62
+ ]
@@ -0,0 +1,6 @@
1
+ DEFAULT_COMMAND_BLACKLIST = {
2
+ "cmd", "cmd.exe",
3
+ "powershell", "powershell.exe",
4
+ "pwsh", "pwsh.exe",
5
+ "bash", "sh", "zsh", "fish"
6
+ }
@@ -0,0 +1,103 @@
1
+ import os
2
+ import platform
3
+
4
+ WINDOWS_ESSENTIAL_VARS = {
5
+ "SYSTEMROOT",
6
+ "SYSTEMDRIVE",
7
+ "PATHEXT",
8
+ "COMSPEC",
9
+ "WINDIR",
10
+
11
+ "USERPROFILE",
12
+ "USERNAME",
13
+ "COMPUTERNAME",
14
+ "APPDATA",
15
+ "LOCALAPPDATA",
16
+ "PUBLIC",
17
+
18
+ "PROCESSOR_ARCHITECTURE"
19
+ }
20
+
21
+ UNIX_ESSENTIAL_VARS = {
22
+ "HOME",
23
+ "USER",
24
+ "LOGNAME",
25
+ "SHELL",
26
+ "TERM",
27
+ "LINES", "COLUMNS",
28
+ "PWD",
29
+ "XDG_RUNTIME_DIR", "XDG_CONFIG_HOME"
30
+ }
31
+
32
+ MACOS_ESSENTIAL_VARS = UNIX_ESSENTIAL_VARS | {
33
+ "DYLD_LIBRARY_PATH",
34
+ "DYLD_FRAMEWORK_PATH",
35
+ "DYLD_FALLBACK_LIBRARY_PATH",
36
+
37
+ "SSH_AUTH_SOCK",
38
+ "SECURITYSESSIONID",
39
+
40
+ "__CF_USER_TEXT_ENCODING",
41
+ "DEVELOPER_DIR",
42
+
43
+ "TERM_PROGRAM",
44
+ "TERM_PROGRAM_VERSION",
45
+ }
46
+
47
+ UNIVERSAL_ESSENTIAL_VARS = {
48
+ "PATH",
49
+ "TEMP", "TMP", "TMPDIR",
50
+ "LANG", "LC_ALL", "LC_CTYPE",
51
+ }
52
+
53
+ PROGRAM_ESSENTIAL_VARS = {
54
+ "PYTHONIOENCODING",
55
+ "GOPATH",
56
+ "LUA_PATH",
57
+ "JAVA_HOME",
58
+ "RUSTUP_HOME",
59
+ "CARGO_HOME",
60
+ "PNPM_HOME",
61
+ "ANDROID_HOME"
62
+ }
63
+
64
+ SECURITY_ADDITIONS = {
65
+ "SSL_CERT_FILE",
66
+ "SSL_CERT_DIR",
67
+ "REQUESTS_CA_BUNDLE"
68
+ }
69
+
70
+ ESSENTIAL_VARS = UNIVERSAL_ESSENTIAL_VARS | PROGRAM_ESSENTIAL_VARS | SECURITY_ADDITIONS
71
+ if platform.system() == "Windows":
72
+ ESSENTIAL_VARS |= WINDOWS_ESSENTIAL_VARS
73
+ elif platform.system() == "Darwin":
74
+ ESSENTIAL_VARS |= MACOS_ESSENTIAL_VARS
75
+ else:
76
+ ESSENTIAL_VARS |= UNIX_ESSENTIAL_VARS
77
+
78
+ class EnvBuilder:
79
+ def __init__(self,
80
+ blacklist: set[str] | None = None,
81
+ extra: dict[str, str] | None = None,
82
+ ):
83
+ self._blacklist = blacklist
84
+ self._extra = extra
85
+
86
+ def with_extra(self, extra: dict[str, str]) -> "EnvBuilder":
87
+ final_extra = (self._extra or {}).copy()
88
+ final_extra.update(extra)
89
+ return EnvBuilder(self._blacklist, final_extra)
90
+
91
+ def build(self) -> dict[str, str]:
92
+ base_env = os.environ.copy()
93
+ final_env = {}
94
+
95
+ # insert essential vars
96
+ for key, var in base_env.items():
97
+ if key.upper() in ESSENTIAL_VARS:
98
+ final_env[key] = var
99
+
100
+ # insert extra vars
101
+ if self._extra:
102
+ final_env.update(self._extra)
103
+ return final_env
@@ -0,0 +1,171 @@
1
+ import asyncio
2
+ import subprocess
3
+ import threading
4
+ import time
5
+ from enum import Enum
6
+ from dataclasses import dataclass
7
+ from collections import deque
8
+ from typing import Callable, TextIO
9
+
10
+ IOStreamCallback = Callable[[str], None]
11
+ IOStreamBuffer = deque[str]
12
+
13
+ class IOStreamReaderStatus(str, Enum):
14
+ SUCCESS = "success"
15
+ TIMEOUT = "timeout"
16
+ CANCELED = "canceled"
17
+ ERROR = "error"
18
+
19
+ @dataclass
20
+ class IOStreamReaderResult:
21
+ returncode: int
22
+ status: IOStreamReaderStatus
23
+ error: Exception | None
24
+ stdout_buf: IOStreamBuffer
25
+ stderr_buf: IOStreamBuffer
26
+
27
+ @property
28
+ def stdout(self) -> str:
29
+ """Get the full text of stdout"""
30
+ return "\n".join(self.stdout_buf)
31
+
32
+ @property
33
+ def stderr(self) -> str:
34
+ """Get the full text of stderr"""
35
+ return "\n".join(self.stderr_buf)
36
+
37
+
38
+ class IOStreamReaderSync:
39
+ def __init__(self,
40
+ proc: subprocess.Popen,
41
+ on_stdout: IOStreamCallback | None = None,
42
+ on_stderr: IOStreamCallback | None = None,
43
+ max_lines: int = 10000):
44
+ self._proc = proc
45
+ self._max_lines = max_lines
46
+ self._on_stdout = on_stdout
47
+ self._on_stderr = on_stderr
48
+ self._cancel_event = threading.Event()
49
+
50
+ def cancel(self):
51
+ self._cancel_event.set()
52
+
53
+ @staticmethod
54
+ def _consumer(pipe: TextIO, callback: IOStreamCallback | None, buf: IOStreamBuffer):
55
+ for line in iter(pipe.readline, ""):
56
+ buf.append(line)
57
+ if callback: callback(line)
58
+
59
+ def read(self, timeout_sec: int | None = None) -> IOStreamReaderResult:
60
+ if (returncode := self._proc.poll()) is not None:
61
+ return IOStreamReaderResult(
62
+ returncode,
63
+ status=IOStreamReaderStatus.SUCCESS,
64
+ error=None,
65
+ stdout_buf=IOStreamBuffer(),
66
+ stderr_buf=IOStreamBuffer())
67
+
68
+ stdout_buf = IOStreamBuffer(maxlen=self._max_lines)
69
+ stderr_buf = IOStreamBuffer(maxlen=self._max_lines)
70
+
71
+ stdout_consumer = threading.Thread(
72
+ target=IOStreamReaderSync._consumer,
73
+ args=(self._proc.stdout, self._on_stdout, stdout_buf))
74
+ stderr_consumer = threading.Thread(
75
+ target=IOStreamReaderSync._consumer,
76
+ args=(self._proc.stderr, self._on_stderr, stderr_buf))
77
+
78
+ stdout_consumer.start()
79
+ stderr_consumer.start()
80
+
81
+ start_time = time.monotonic()
82
+ status = IOStreamReaderStatus.SUCCESS
83
+ error: Exception | None = None
84
+ try:
85
+ while self._proc.poll() is None:
86
+ if self._cancel_event.is_set():
87
+ self._proc.kill()
88
+ status = IOStreamReaderStatus.CANCELED
89
+ break
90
+
91
+ if (timeout_sec is not None and
92
+ (time.monotonic() - start_time) > timeout_sec):
93
+ self._proc.kill()
94
+ status = IOStreamReaderStatus.TIMEOUT
95
+ break
96
+ time.sleep(0.1)
97
+ except Exception as exc:
98
+ self._proc.kill()
99
+ status = IOStreamReaderStatus.ERROR
100
+ error = exc
101
+
102
+ stdout_consumer.join(timeout=3)
103
+ stderr_consumer.join(timeout=3)
104
+
105
+ returncode = self._proc.returncode
106
+ if returncode is None:
107
+ returncode = self._proc.wait()
108
+ return IOStreamReaderResult(returncode, status, error, stdout_buf, stderr_buf)
109
+
110
+ class IOStreamReader:
111
+ def __init__(self,
112
+ proc: asyncio.subprocess.Process,
113
+ max_lines: int,
114
+ on_stdout: IOStreamCallback | None = None,
115
+ on_stderr: IOStreamCallback | None = None,
116
+ ):
117
+ self._proc = proc
118
+ self._max_lines = max_lines
119
+ self._on_stdout = on_stdout
120
+ self._on_stderr = on_stderr
121
+
122
+ @staticmethod
123
+ async def _consumer(stream: asyncio.StreamReader, callback: IOStreamCallback | None, buf: IOStreamBuffer):
124
+ while not stream.at_eof():
125
+ line = await stream.readline()
126
+ if not line: break
127
+ text = line.decode("utf-8", errors="replace")
128
+ buf.append(text)
129
+ if callback: callback(text)
130
+
131
+ async def read(self, timeout_sec: int | None = None) -> IOStreamReaderResult:
132
+ stdout_buf = IOStreamBuffer(maxlen=self._max_lines)
133
+ stderr_buf = IOStreamBuffer(maxlen=self._max_lines)
134
+
135
+ assert self._proc.stdout is not None
136
+ assert self._proc.stderr is not None
137
+ consumer_task = [
138
+ asyncio.create_task(IOStreamReader._consumer(self._proc.stdout, self._on_stdout, stdout_buf)),
139
+ asyncio.create_task(IOStreamReader._consumer(self._proc.stderr, self._on_stderr, stderr_buf))
140
+ ]
141
+
142
+ status = IOStreamReaderStatus.SUCCESS
143
+ error: Exception | None = None
144
+ try:
145
+ if timeout_sec is not None:
146
+ returncode = await asyncio.wait_for(self._proc.wait(), timeout=timeout_sec)
147
+ else:
148
+ returncode = await self._proc.wait()
149
+ except asyncio.TimeoutError:
150
+ self._proc.kill()
151
+ returncode = await self._proc.wait()
152
+ status = IOStreamReaderStatus.TIMEOUT
153
+ except asyncio.CancelledError:
154
+ self._proc.kill()
155
+ returncode = await self._proc.wait()
156
+ status = IOStreamReaderStatus.CANCELED
157
+ except Exception as exc:
158
+ self._proc.kill()
159
+ returncode = await self._proc.wait()
160
+ status = IOStreamReaderStatus.ERROR
161
+ error = exc
162
+ finally:
163
+ try:
164
+ await asyncio.wait_for(
165
+ asyncio.gather(*consumer_task, return_exceptions=True),
166
+ timeout=2)
167
+ except asyncio.TimeoutError:
168
+ for task in consumer_task: task.cancel()
169
+ await asyncio.gather(*consumer_task, return_exceptions=True)
170
+
171
+ return IOStreamReaderResult(returncode, status, error, stdout_buf, stderr_buf)
dais_shell/py.typed ADDED
File without changes
@@ -0,0 +1,18 @@
1
+ from abc import ABC, abstractmethod
2
+ from ..types import CommandStep
3
+ from ..iostream_reader import IOStreamReaderResult
4
+
5
+ class BaseShellRuntime(ABC):
6
+ @abstractmethod
7
+ def run_sync(self,
8
+ step: CommandStep,
9
+ on_stdout=None,
10
+ on_stderr=None,
11
+ ) -> IOStreamReaderResult: ...
12
+
13
+ @abstractmethod
14
+ async def run(self,
15
+ step: CommandStep,
16
+ on_stdout=None,
17
+ on_stderr=None
18
+ ) -> IOStreamReaderResult: ...
@@ -0,0 +1,90 @@
1
+ import asyncio
2
+ import shutil
3
+ import subprocess
4
+ from dataclasses import dataclass
5
+ from .BaseShellRuntime import BaseShellRuntime
6
+ from ..types import CommandStep
7
+ from ..types.exceptions import ShellRuntimeNotFoundError
8
+ from ..iostream_reader import IOStreamReaderResult, IOStreamReader, IOStreamReaderSync
9
+
10
+ @dataclass
11
+ class BashCommandStep(CommandStep):
12
+ @classmethod
13
+ def from_command_step(cls, step: CommandStep):
14
+ return cls(
15
+ command=step.command,
16
+ args=step.args,
17
+ env=step.env,
18
+ cwd=step.cwd,
19
+ timeout=step.timeout
20
+ )
21
+
22
+ def to_wrapper_script(self):
23
+ return 'exec "$1" "${@:2}"'
24
+
25
+ # --- --- --- --- --- ---
26
+
27
+ class BashRuntime(BaseShellRuntime):
28
+ def __init__(self, max_lines: int):
29
+ self._shell = self._detect_shell()
30
+ self._max_lines = max_lines
31
+
32
+ def _detect_shell(self) -> str:
33
+ if bash := shutil.which("bash"):
34
+ return bash
35
+ if sh := shutil.which("sh"):
36
+ return sh
37
+ raise ShellRuntimeNotFoundError("Bash")
38
+
39
+ def _make_bash_commands(self, step: BashCommandStep):
40
+ return [
41
+ self._shell,
42
+ "-c",
43
+ step.to_wrapper_script(),
44
+ "--",
45
+ step.command,
46
+ *step.args
47
+ ]
48
+
49
+ def _prepare_cmd(self, step: CommandStep) -> list[str]:
50
+ step = BashCommandStep.from_command_step(step)
51
+ resolved = shutil.which(step.command)
52
+ is_shell_command = resolved is None
53
+ if is_shell_command:
54
+ return self._make_bash_commands(step)
55
+ else:
56
+ return [resolved, *step.args]
57
+
58
+ def run_sync(self,
59
+ step: CommandStep,
60
+ on_stdout=None,
61
+ on_stderr=None,
62
+ ) -> IOStreamReaderResult:
63
+ proc = subprocess.Popen(
64
+ self._prepare_cmd(step),
65
+ cwd=step.cwd,
66
+ env=step.env,
67
+ stdout=subprocess.PIPE,
68
+ stderr=subprocess.PIPE,
69
+ text=True,
70
+ bufsize=1,
71
+ )
72
+
73
+ reader = IOStreamReaderSync(proc, on_stdout, on_stderr, self._max_lines)
74
+ return reader.read(step.timeout)
75
+
76
+ async def run(self,
77
+ step: CommandStep,
78
+ on_stdout=None,
79
+ on_stderr=None
80
+ ) -> IOStreamReaderResult:
81
+ proc = await asyncio.create_subprocess_exec(
82
+ *self._prepare_cmd(step),
83
+ cwd=step.cwd,
84
+ env=step.env,
85
+ stdout=asyncio.subprocess.PIPE,
86
+ stderr=asyncio.subprocess.PIPE,
87
+ )
88
+
89
+ reader = IOStreamReader(proc, self._max_lines, on_stdout, on_stderr)
90
+ return await reader.read(step.timeout)
@@ -0,0 +1,114 @@
1
+ import asyncio
2
+ import base64
3
+ import os
4
+ import json
5
+ import shutil
6
+ import subprocess
7
+ from dataclasses import dataclass
8
+ from .BaseShellRuntime import BaseShellRuntime
9
+ from ..iostream_reader import IOStreamReaderResult, IOStreamReader, IOStreamReaderSync
10
+ from ..types import CommandStep
11
+ from ..types.exceptions import ShellRuntimeNotFoundError
12
+
13
+ CREATE_NO_WINDOW = 0x08000000 if os.name == "nt" else 0
14
+
15
+ @dataclass
16
+ class PowerShellCommandStep(CommandStep):
17
+ @classmethod
18
+ def from_command_step(cls, step: CommandStep):
19
+ return cls(
20
+ command=step.command,
21
+ args=step.args,
22
+ env=step.env,
23
+ cwd=step.cwd,
24
+ timeout=step.timeout
25
+ )
26
+
27
+ def to_wrapper_script(self):
28
+ cmd_json = json.dumps(self.command)
29
+ args_json = json.dumps(self.args)
30
+ script = f"""
31
+ $ErrorActionPreference = "Stop"
32
+ $PSNativeCommandArgumentPassing = "Standard"
33
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
34
+
35
+ $command = ConvertFrom-Json '{cmd_json}'
36
+ $arguments = ,(ConvertFrom-Json '{args_json}')
37
+
38
+ & $command @arguments
39
+ exit $LASTEXITCODE"""
40
+ return script.strip()
41
+
42
+ # --- --- --- --- --- ---
43
+
44
+ class PowerShellRuntime(BaseShellRuntime):
45
+ def __init__(self, max_lines: int):
46
+ self._shell = self._detect_shell()
47
+ self._max_lines = max_lines
48
+
49
+ @staticmethod
50
+ def _detect_shell() -> str:
51
+ if pwsh := shutil.which("pwsh"):
52
+ return pwsh
53
+ if powershell := shutil.which("powershell"):
54
+ return powershell
55
+ raise ShellRuntimeNotFoundError("PowerShell")
56
+
57
+ @staticmethod
58
+ def _encode(source: str) -> str:
59
+ return base64.b64encode(
60
+ source.encode("utf-16-le")
61
+ ).decode("ascii")
62
+
63
+ def _make_powershell_commands(self, encoded: str):
64
+ return [
65
+ self._shell,
66
+ "-NoProfile",
67
+ "-NonInteractive",
68
+ "-ExecutionPolicy", "Bypass",
69
+ "-EncodedCommand", encoded
70
+ ]
71
+
72
+ def _prepare_cmd(self, step: CommandStep) -> list[str]:
73
+ step = PowerShellCommandStep.from_command_step(step)
74
+ script = step.to_wrapper_script()
75
+ encoded = self._encode(script)
76
+ return self._make_powershell_commands(encoded)
77
+
78
+ def run_sync(
79
+ self,
80
+ step: CommandStep,
81
+ on_stdout=None,
82
+ on_stderr=None,
83
+ ) -> IOStreamReaderResult:
84
+ proc = subprocess.Popen(
85
+ self._prepare_cmd(step),
86
+ cwd=step.cwd,
87
+ env=step.env,
88
+ stdout=subprocess.PIPE,
89
+ stderr=subprocess.PIPE,
90
+ text=True,
91
+ bufsize=1,
92
+ creationflags=CREATE_NO_WINDOW
93
+ )
94
+
95
+ reader = IOStreamReaderSync(proc, on_stdout, on_stderr, self._max_lines)
96
+ return reader.read(step.timeout)
97
+
98
+ async def run(
99
+ self,
100
+ step: CommandStep,
101
+ on_stdout=None,
102
+ on_stderr=None
103
+ ) -> IOStreamReaderResult:
104
+ proc = await asyncio.create_subprocess_exec(
105
+ *self._prepare_cmd(step),
106
+ cwd=step.cwd,
107
+ env=step.env,
108
+ stdout=asyncio.subprocess.PIPE,
109
+ stderr=asyncio.subprocess.PIPE,
110
+ creationflags=CREATE_NO_WINDOW
111
+ )
112
+
113
+ reader = IOStreamReader(proc, self._max_lines, on_stdout, on_stderr)
114
+ return await reader.read(step.timeout)
@@ -0,0 +1,3 @@
1
+ from .BaseShellRuntime import BaseShellRuntime
2
+ from .BashRuntime import BashRuntime
3
+ from .PowershellRuntime import PowerShellRuntime
@@ -0,0 +1,26 @@
1
+ import os
2
+ import shutil
3
+ from abc import abstractmethod
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from .exceptions import CommandNotFoundError, ForbiddenShellTargetError
7
+
8
+ @dataclass
9
+ class CommandStep:
10
+ command: str
11
+ args: list[str]
12
+ cwd: str | Path
13
+ env: dict[str, str] | None = None
14
+ timeout: int | None = None
15
+
16
+ @abstractmethod
17
+ def to_wrapper_script(self) -> str: ...
18
+
19
+ def validate_command(self, filter: set[str] | None = None):
20
+ if filter is not None:
21
+ name = os.path.basename(self.command).lower()
22
+ if name in filter:
23
+ raise ForbiddenShellTargetError(name)
24
+ resolved = shutil.which(self.command)
25
+ if resolved is None:
26
+ raise CommandNotFoundError(self.command)
@@ -0,0 +1,16 @@
1
+ class ShellError(Exception): ...
2
+
3
+ class CommandNotFoundError(ShellError):
4
+ def __init__(self, command: str):
5
+ self.command = command
6
+ super().__init__(f"Command not found: {command}")
7
+
8
+ class ShellRuntimeNotFoundError(ShellError):
9
+ def __init__(self, runtime: str):
10
+ self.runtime = runtime
11
+ super().__init__(f"Shell runtime not found: {runtime}")
12
+
13
+ class ForbiddenShellTargetError(ShellError):
14
+ def __init__(self, command: str):
15
+ self.command = command
16
+ super().__init__(f"Refusing to execute shell program as target: {command}")
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.3
2
+ Name: dais-shell
3
+ Version: 0.1.1
4
+ Summary: The shell tool for Dais.
5
+ Author: BHznJNs
6
+ Author-email: BHznJNs <441768875@qq.com>
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+
10
+ # Dais Shell
11
+
12
+ The shell tool for [Dais](https://github.com/Dais-Project/Dais).
@@ -0,0 +1,14 @@
1
+ dais_shell/__init__.py,sha256=GIEykP4yW5bC31JR1km2_fOVJGyxU_kPWgZd02fDobY,2202
2
+ dais_shell/constants.py,sha256=G5_Bo503U7CB9l5b7cjiQe2gNrGsn4HIQFnBokBrpQo,146
3
+ dais_shell/env_builder.py,sha256=ZIJlxZeOhngU1_nD8xJcvfC2CTLYkv7G8tewI7zppJk,2208
4
+ dais_shell/iostream_reader.py,sha256=IZEXgxHseDUyFNr8OGr4EAtUyohHuMdvkGSv_iaWcKg,6044
5
+ dais_shell/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ dais_shell/runtimes/BaseShellRuntime.py,sha256=997cIoDK866hbvOhDVLO2IAXUZ3TIUV552VnrQi91w0,540
7
+ dais_shell/runtimes/BashRuntime.py,sha256=UGQa_yl_9I9IkG_lWA1h-ApImONKWuKyFJlaEOhNPyE,2741
8
+ dais_shell/runtimes/PowershellRuntime.py,sha256=8G9OhGJYUu12P1H-Mibjs_14IDy_E8EsfFPxSgQXo-c,3342
9
+ dais_shell/runtimes/__init__.py,sha256=VfiKA1QYRYFpdFJDRzzwYitxKuoK86OadjO8wh4Zd4U,133
10
+ dais_shell/types/__init__.py,sha256=6GFk9q51LQfOnIWwd-lgI9w85SodNLzVXbLiwzVhB-k,775
11
+ dais_shell/types/exceptions.py,sha256=F4x3Q9MTK9V_6XbVRHcfzAUahXvDv6YEO1mVa7uSGhs,579
12
+ dais_shell-0.1.1.dist-info/WHEEL,sha256=iHtWm8nRfs0VRdCYVXocAWFW8ppjHL-uTJkAdZJKOBM,80
13
+ dais_shell-0.1.1.dist-info/METADATA,sha256=bNu9cxvfuPEo6NBbZZ7ZFxbumpj4sbqQS4ncEHkT4Mc,289
14
+ dais_shell-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.30
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any