autosh 0.0.1__py3-none-any.whl → 0.0.3__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.
- autosh/config-template.toml +13 -0
- autosh/config.py +15 -4
- autosh/main.py +7 -8
- autosh/md/__init__.py +8 -0
- autosh/md/inline_text.py +214 -0
- autosh/md/printer.py +301 -0
- autosh/md/state.py +136 -0
- autosh/md/stream.py +107 -0
- autosh/plugins/__init__.py +2 -2
- autosh/plugins/cli.py +42 -0
- autosh/session.py +74 -7
- {autosh-0.0.1.dist-info → autosh-0.0.3.dist-info}/METADATA +8 -4
- autosh-0.0.3.dist-info/RECORD +22 -0
- autosh/md.py +0 -394
- autosh-0.0.1.dist-info/RECORD +0 -17
- {autosh-0.0.1.dist-info → autosh-0.0.3.dist-info}/WHEEL +0 -0
- {autosh-0.0.1.dist-info → autosh-0.0.3.dist-info}/entry_points.txt +0 -0
- {autosh-0.0.1.dist-info → autosh-0.0.3.dist-info}/licenses/LICENSE +0 -0
autosh/md/state.py
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from contextlib import contextmanager
|
3
|
+
from enum import Enum
|
4
|
+
|
5
|
+
HIGHLIGHT_COLOR_START = "35"
|
6
|
+
HIGHLIGHT_COLOR_END = "0"
|
7
|
+
ESC = "\x1b"
|
8
|
+
|
9
|
+
|
10
|
+
class Color(Enum):
|
11
|
+
BLACK = 30, 40
|
12
|
+
RED = 31, 41
|
13
|
+
GREEN = 32, 42
|
14
|
+
YELLOW = 33, 43
|
15
|
+
BLUE = 34, 44
|
16
|
+
MAGENTA = 35, 45
|
17
|
+
CYAN = 36, 46
|
18
|
+
WHITE = 37, 47
|
19
|
+
|
20
|
+
BRIGHT_BLACK = 90, 100
|
21
|
+
BRIGHT_RED = 91, 101
|
22
|
+
BRIGHT_GREEN = 92, 102
|
23
|
+
BRIGHT_YELLOW = 93, 103
|
24
|
+
BRIGHT_BLUE = 94, 104
|
25
|
+
BRIGHT_MAGENTA = 95, 105
|
26
|
+
BRIGHT_CYAN = 96, 106
|
27
|
+
BRIGHT_WHITE = 97, 107
|
28
|
+
|
29
|
+
def __init__(self, foreground: int, background: int):
|
30
|
+
self.foreground = foreground
|
31
|
+
self.background = background
|
32
|
+
|
33
|
+
|
34
|
+
@dataclass
|
35
|
+
class State:
|
36
|
+
is_bold: bool = False
|
37
|
+
is_dim: bool = False
|
38
|
+
is_italic: bool = False
|
39
|
+
is_underline: bool = False
|
40
|
+
is_strike: bool = False
|
41
|
+
foreground_color: Color | None = None
|
42
|
+
background_color: Color | None = None
|
43
|
+
|
44
|
+
def emit(self, text: str):
|
45
|
+
print(text, end="", flush=True)
|
46
|
+
|
47
|
+
def _enter_scope(
|
48
|
+
self,
|
49
|
+
bold: bool | None = None,
|
50
|
+
dim: bool | None = None,
|
51
|
+
italic: bool | None = None,
|
52
|
+
underline: bool | None = None,
|
53
|
+
strike: bool | None = None,
|
54
|
+
color: Color | None = None,
|
55
|
+
bg: Color | None = None,
|
56
|
+
) -> "State":
|
57
|
+
old_state = State(
|
58
|
+
is_bold=self.is_bold,
|
59
|
+
is_dim=self.is_dim,
|
60
|
+
is_italic=self.is_italic,
|
61
|
+
is_underline=self.is_underline,
|
62
|
+
is_strike=self.is_strike,
|
63
|
+
foreground_color=self.foreground_color,
|
64
|
+
background_color=self.background_color,
|
65
|
+
)
|
66
|
+
if bold is not None:
|
67
|
+
self.is_bold = bold
|
68
|
+
if dim is not None:
|
69
|
+
self.is_dim = dim
|
70
|
+
if italic is not None:
|
71
|
+
self.is_italic = italic
|
72
|
+
if underline is not None:
|
73
|
+
self.is_underline = underline
|
74
|
+
if strike is not None:
|
75
|
+
self.is_strike = strike
|
76
|
+
if color is not None:
|
77
|
+
self.foreground_color = color
|
78
|
+
if bg is not None:
|
79
|
+
self.background_color = bg
|
80
|
+
self.__apply_all()
|
81
|
+
return old_state
|
82
|
+
|
83
|
+
def _exit_scope(self, old_state: "State"):
|
84
|
+
self.is_bold = old_state.is_bold
|
85
|
+
self.is_dim = old_state.is_dim
|
86
|
+
self.is_italic = old_state.is_italic
|
87
|
+
self.is_underline = old_state.is_underline
|
88
|
+
self.is_strike = old_state.is_strike
|
89
|
+
self.foreground_color = old_state.foreground_color
|
90
|
+
self.background_color = old_state.background_color
|
91
|
+
self.__apply_all()
|
92
|
+
|
93
|
+
@contextmanager
|
94
|
+
def style(
|
95
|
+
self,
|
96
|
+
bold: bool | None = None,
|
97
|
+
dim: bool | None = None,
|
98
|
+
italic: bool | None = None,
|
99
|
+
underline: bool | None = None,
|
100
|
+
strike: bool | None = None,
|
101
|
+
color: Color | None = None,
|
102
|
+
bg: Color | None = None,
|
103
|
+
):
|
104
|
+
old_state = self._enter_scope(
|
105
|
+
bold=bold,
|
106
|
+
dim=dim,
|
107
|
+
italic=italic,
|
108
|
+
underline=underline,
|
109
|
+
strike=strike,
|
110
|
+
color=color,
|
111
|
+
bg=bg,
|
112
|
+
)
|
113
|
+
yield
|
114
|
+
self._exit_scope(old_state)
|
115
|
+
|
116
|
+
def __apply_all(self):
|
117
|
+
self.emit(f"{ESC}[0m")
|
118
|
+
codes = []
|
119
|
+
if self.is_bold:
|
120
|
+
codes.append(1)
|
121
|
+
if self.is_dim:
|
122
|
+
codes.append(2)
|
123
|
+
if self.is_italic:
|
124
|
+
codes.append(3)
|
125
|
+
if self.is_underline:
|
126
|
+
codes.append(4)
|
127
|
+
if self.is_strike:
|
128
|
+
codes.append(9)
|
129
|
+
if self.foreground_color:
|
130
|
+
codes.append(self.foreground_color.foreground)
|
131
|
+
if self.background_color:
|
132
|
+
codes.append(self.background_color.background)
|
133
|
+
|
134
|
+
codes = ";".join(map(str, codes))
|
135
|
+
if len(codes) > 0:
|
136
|
+
self.emit(f"{ESC}[{codes}m")
|
autosh/md/stream.py
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
from typing import AsyncGenerator
|
2
|
+
|
3
|
+
|
4
|
+
class TextStream:
|
5
|
+
def __init__(self, gen: AsyncGenerator[str, None]):
|
6
|
+
self.__stream = gen
|
7
|
+
self.__buf: str = ""
|
8
|
+
self.__eof = False
|
9
|
+
|
10
|
+
async def init(self):
|
11
|
+
try:
|
12
|
+
self.__buf = await anext(self.__stream)
|
13
|
+
except StopAsyncIteration:
|
14
|
+
self.__eof = True
|
15
|
+
|
16
|
+
async def __ensure_length(self, n: int):
|
17
|
+
while (not self.__eof) and len(self.__buf) < n:
|
18
|
+
try:
|
19
|
+
self.__buf += await anext(self.__stream)
|
20
|
+
except StopAsyncIteration:
|
21
|
+
self.__eof = True
|
22
|
+
|
23
|
+
def peek(self):
|
24
|
+
c = self.__buf[0] if len(self.__buf) > 0 else None
|
25
|
+
return c
|
26
|
+
|
27
|
+
async def check(self, s: str, eof: bool | None = None) -> bool:
|
28
|
+
if len(s) == 0:
|
29
|
+
return True
|
30
|
+
await self.__ensure_length(len(s) + 1)
|
31
|
+
if len(self.__buf) < len(s):
|
32
|
+
return False
|
33
|
+
matched = self.__buf[0 : len(s)] == s
|
34
|
+
if matched:
|
35
|
+
if eof is not None:
|
36
|
+
if eof:
|
37
|
+
# return false if there is more data
|
38
|
+
if len(self.__buf) > len(s):
|
39
|
+
return False
|
40
|
+
else:
|
41
|
+
# return false if there is no more data
|
42
|
+
if len(self.__buf) == len(s):
|
43
|
+
return False
|
44
|
+
return matched
|
45
|
+
|
46
|
+
async def consume(self, n: int = 1):
|
47
|
+
await self.__ensure_length(n)
|
48
|
+
if len(self.__buf) < n:
|
49
|
+
raise ValueError("Not enough data to consume")
|
50
|
+
s = self.__buf[:n]
|
51
|
+
self.__buf = self.__buf[n:]
|
52
|
+
await self.__ensure_length(1)
|
53
|
+
return s
|
54
|
+
|
55
|
+
async def unordered_list_label(self) -> bool:
|
56
|
+
if self.__eof:
|
57
|
+
return False
|
58
|
+
await self.__ensure_length(2)
|
59
|
+
buf = self.__buf
|
60
|
+
if len(buf) < 2:
|
61
|
+
return False
|
62
|
+
if buf[0] in ["-", "+", "*"] and buf[1] == " ":
|
63
|
+
return True
|
64
|
+
return False
|
65
|
+
|
66
|
+
async def ordered_list_label(self) -> bool:
|
67
|
+
if self.__eof:
|
68
|
+
return False
|
69
|
+
await self.__ensure_length(5)
|
70
|
+
buf = self.__buf
|
71
|
+
# \d+\.
|
72
|
+
if len(buf) == 0:
|
73
|
+
return False
|
74
|
+
if not buf[0].isnumeric():
|
75
|
+
return False
|
76
|
+
has_dot = False
|
77
|
+
for i in range(1, 5):
|
78
|
+
if i >= len(buf):
|
79
|
+
return False
|
80
|
+
c = buf[i]
|
81
|
+
if c == ".":
|
82
|
+
if has_dot:
|
83
|
+
return False
|
84
|
+
has_dot = True
|
85
|
+
continue
|
86
|
+
if c == " ":
|
87
|
+
if has_dot:
|
88
|
+
return True
|
89
|
+
return False
|
90
|
+
if c.isnumeric():
|
91
|
+
continue
|
92
|
+
return False
|
93
|
+
|
94
|
+
async def non_paragraph_block_start(self):
|
95
|
+
await self.__ensure_length(3)
|
96
|
+
buf = self.__buf[:3] if len(self.__buf) >= 3 else self.__buf
|
97
|
+
if buf.startswith("```"):
|
98
|
+
return True
|
99
|
+
if buf.startswith("---"):
|
100
|
+
return True
|
101
|
+
if buf.startswith("> "):
|
102
|
+
return True
|
103
|
+
if await self.ordered_list_label():
|
104
|
+
return True
|
105
|
+
if await self.unordered_list_label():
|
106
|
+
return True
|
107
|
+
return False
|
autosh/plugins/__init__.py
CHANGED
@@ -29,8 +29,8 @@ def confirm(message: str):
|
|
29
29
|
|
30
30
|
|
31
31
|
def cmd_preview_panel(title: str, content: RenderableType, short: str | None = None):
|
32
|
-
if CLI_OPTIONS.quiet
|
33
|
-
if short:
|
32
|
+
if CLI_OPTIONS.quiet:
|
33
|
+
if short and not CLI_OPTIONS.yes:
|
34
34
|
rich.print(f"[magenta]{short}[/magenta]\n")
|
35
35
|
return
|
36
36
|
panel = Panel.fit(content, title=f"[magenta]{title}[/magenta]", title_align="left")
|
autosh/plugins/cli.py
CHANGED
@@ -82,6 +82,48 @@ class CLIPlugin(Plugin):
|
|
82
82
|
"args": CLI_OPTIONS.args,
|
83
83
|
}
|
84
84
|
|
85
|
+
@tool
|
86
|
+
def get_env(self, key: Annotated[str, "The environment variable to get"]):
|
87
|
+
"""
|
88
|
+
Get an environment variable.
|
89
|
+
"""
|
90
|
+
banner("GET ENV", key)
|
91
|
+
if key not in os.environ:
|
92
|
+
raise KeyError(f"Environment variable `{key}` does not exist.")
|
93
|
+
return os.environ[key]
|
94
|
+
|
95
|
+
@tool
|
96
|
+
def get_all_envs(self):
|
97
|
+
"""
|
98
|
+
Get all environment variables.
|
99
|
+
"""
|
100
|
+
banner("GET ALL ENVS")
|
101
|
+
envs = {}
|
102
|
+
for key, value in os.environ.items():
|
103
|
+
envs[key] = value
|
104
|
+
return {"envs": envs}
|
105
|
+
|
106
|
+
@tool
|
107
|
+
def update_env(
|
108
|
+
self,
|
109
|
+
key: Annotated[str, "The environment variable to set"],
|
110
|
+
value: Annotated[
|
111
|
+
str | None,
|
112
|
+
"The value to set the environment variable to, or None to delete it",
|
113
|
+
],
|
114
|
+
):
|
115
|
+
"""
|
116
|
+
Set or delete an environment variable.
|
117
|
+
"""
|
118
|
+
if value is None:
|
119
|
+
banner("DEL ENV", key)
|
120
|
+
if key in os.environ:
|
121
|
+
del os.environ[key]
|
122
|
+
else:
|
123
|
+
banner("SET ENV", key, value)
|
124
|
+
os.environ[key] = value
|
125
|
+
return f"DONE"
|
126
|
+
|
85
127
|
@tool
|
86
128
|
def read(
|
87
129
|
self,
|
autosh/session.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import asyncio
|
1
2
|
from pathlib import Path
|
2
3
|
import sys
|
3
4
|
from agentia import Agent
|
@@ -10,6 +11,7 @@ from autosh.md import stream_md
|
|
10
11
|
from .plugins import create_plugins
|
11
12
|
import rich
|
12
13
|
import platform
|
14
|
+
from rich.prompt import Prompt
|
13
15
|
|
14
16
|
|
15
17
|
INSTRUCTIONS = f"""
|
@@ -86,11 +88,11 @@ class Session:
|
|
86
88
|
def _get_argv_message(self):
|
87
89
|
args = str(CLI_OPTIONS.args)
|
88
90
|
if not CLI_OPTIONS.script:
|
89
|
-
cmd =
|
91
|
+
cmd = Path(sys.argv[0]).name
|
90
92
|
else:
|
91
93
|
cmd = CLI_OPTIONS.script.name
|
92
94
|
return UserMessage(
|
93
|
-
content=f"PROGRAM NAME: {cmd}\n\nCOMMAND LINE ARGS: {args}",
|
95
|
+
content=f"PROGRAM NAME: {cmd}\n\nCOMMAND LINE ARGS: {args}\n\nCWD: {str(Path.cwd())}",
|
94
96
|
role="user",
|
95
97
|
)
|
96
98
|
|
@@ -108,19 +110,37 @@ class Session:
|
|
108
110
|
):
|
109
111
|
await self._print_help_and_exit(prompt)
|
110
112
|
# Execute the prompt
|
113
|
+
loading = self.__create_loading_indicator()
|
111
114
|
CLI_OPTIONS.prompt = prompt
|
112
115
|
self.agent.history.add(self._get_argv_message())
|
113
116
|
if CLI_OPTIONS.stdin_has_data():
|
114
117
|
self.agent.history.add(
|
115
118
|
UserMessage(
|
116
|
-
content="IMPORTANT:
|
119
|
+
content="IMPORTANT: You are acting as an intermediate tool of a workflow. Input data is fed to you through piped stdin. Please use tools to read when necessary.",
|
120
|
+
role="user",
|
121
|
+
)
|
122
|
+
)
|
123
|
+
if not sys.stdout.isatty():
|
124
|
+
self.agent.history.add(
|
125
|
+
UserMessage(
|
126
|
+
content="IMPORTANT: You are acting as an intermediate tool of a workflow. Your output should only contain the user expected output, nothing else. Don't ask user questions or print anything else since the user cannot see it.",
|
127
|
+
role="user",
|
128
|
+
)
|
129
|
+
)
|
130
|
+
else:
|
131
|
+
self.agent.history.add(
|
132
|
+
UserMessage(
|
133
|
+
content="IMPORTANT: This is a one-off run, so don't ask user questions since the user cannot reply.",
|
117
134
|
role="user",
|
118
135
|
)
|
119
136
|
)
|
120
137
|
completion = self.agent.chat_completion(prompt, stream=True)
|
121
138
|
async for stream in completion:
|
122
|
-
if
|
139
|
+
if not loading:
|
140
|
+
loading = self.__create_loading_indicator()
|
141
|
+
if await self.__render_streamed_markdown(stream, loading=loading):
|
123
142
|
print()
|
143
|
+
loading = None
|
124
144
|
|
125
145
|
async def exec_from_stdin(self):
|
126
146
|
if sys.stdin.isatty():
|
@@ -141,19 +161,60 @@ class Session:
|
|
141
161
|
console = rich.console.Console()
|
142
162
|
while True:
|
143
163
|
try:
|
144
|
-
prompt = console.input("[bold]>[/bold] ").strip()
|
164
|
+
prompt = console.input("[bold blue]>[/bold blue] ").strip()
|
145
165
|
if prompt in ["exit", "quit"]:
|
146
166
|
break
|
147
167
|
if len(prompt) == 0:
|
148
168
|
continue
|
169
|
+
loading = self.__create_loading_indicator(newline=True)
|
149
170
|
completion = self.agent.chat_completion(prompt, stream=True)
|
150
171
|
async for stream in completion:
|
151
|
-
if
|
172
|
+
if not loading:
|
173
|
+
loading = self.__create_loading_indicator()
|
174
|
+
if await self.__render_streamed_markdown(stream, loading=loading):
|
152
175
|
print()
|
176
|
+
loading = None
|
153
177
|
except KeyboardInterrupt:
|
154
178
|
break
|
155
179
|
|
156
|
-
|
180
|
+
def __create_loading_indicator(self, newline: bool = False):
|
181
|
+
return (
|
182
|
+
asyncio.create_task(self.__loading(newline))
|
183
|
+
if sys.stdout.isatty()
|
184
|
+
else None
|
185
|
+
)
|
186
|
+
|
187
|
+
async def __loading(self, newline: bool = False):
|
188
|
+
chars = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
189
|
+
char_width = 1
|
190
|
+
msg = "Loading..."
|
191
|
+
count = 0
|
192
|
+
print("\x1b[2m", end="", flush=True)
|
193
|
+
while True:
|
194
|
+
try:
|
195
|
+
print(chars[count], end="", flush=True)
|
196
|
+
print(" " + msg, end="", flush=True)
|
197
|
+
count += 1
|
198
|
+
await asyncio.sleep(0.1)
|
199
|
+
length = char_width + len(msg) + 1
|
200
|
+
print("\b" * length, end="", flush=True)
|
201
|
+
print(" " * length, end="", flush=True)
|
202
|
+
print("\b" * length, end="", flush=True)
|
203
|
+
if count == len(chars):
|
204
|
+
count = 0
|
205
|
+
except asyncio.CancelledError:
|
206
|
+
length = char_width + len(msg) + 1
|
207
|
+
print("\b" * length, end="", flush=True)
|
208
|
+
print(" " * length, end="", flush=True)
|
209
|
+
print("\b" * length, end="", flush=True)
|
210
|
+
print("\x1b[0m", end="", flush=True)
|
211
|
+
if newline:
|
212
|
+
print()
|
213
|
+
break
|
214
|
+
|
215
|
+
async def __render_streamed_markdown(
|
216
|
+
self, stream: MessageStream, loading: asyncio.Task[None] | None = None
|
217
|
+
):
|
157
218
|
if sys.stdout.isatty():
|
158
219
|
# buffer first few chars so we don't need to launch glow if there is no output
|
159
220
|
chunks = aiter(stream)
|
@@ -163,8 +224,14 @@ class Session:
|
|
163
224
|
buf += await anext(chunks)
|
164
225
|
except StopAsyncIteration:
|
165
226
|
if len(buf) == 0:
|
227
|
+
if loading:
|
228
|
+
loading.cancel()
|
229
|
+
await loading
|
166
230
|
return False
|
167
231
|
break
|
232
|
+
if loading:
|
233
|
+
loading.cancel()
|
234
|
+
await loading
|
168
235
|
|
169
236
|
content = {"v": ""}
|
170
237
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: autosh
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.3
|
4
4
|
Summary: Add your description here
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.13
|
@@ -31,7 +31,9 @@ As an interactive shell: `ash` (alternatively, `autosh`)
|
|
31
31
|
|
32
32
|
Execute a single prompt: `ash "list current directory"`
|
33
33
|
|
34
|
-
Process piped data:
|
34
|
+
Process piped data:
|
35
|
+
* `cat README.md | ash -y "summarise"`
|
36
|
+
* `cat in.csv | ash -y -q "double the first numeric column" > out.csv`
|
35
37
|
|
36
38
|
## Scripting
|
37
39
|
|
@@ -72,6 +74,8 @@ Write "Hello, world" to _test.log
|
|
72
74
|
|
73
75
|
# TODO
|
74
76
|
|
75
|
-
- [ ] Image generation
|
76
|
-
- [ ] Image input
|
77
|
+
- [ ] Image input, generation, and editing
|
77
78
|
- [ ] RAG for non-text files
|
79
|
+
- [ ] Plugin system
|
80
|
+
- [ ] MCP support
|
81
|
+
- [ ] A better input widget with history and auto completion
|
@@ -0,0 +1,22 @@
|
|
1
|
+
autosh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
autosh/config-template.toml,sha256=iLdCBHIK0czWgNHtwAgvuhV670aiNc-IOmaPHP12i0Y,269
|
3
|
+
autosh/config.py,sha256=7DXUSsqHm8dPsXln-NTu8D0_Uwcs2h4sT8pFtQM8tso,2654
|
4
|
+
autosh/main.py,sha256=ov8lBOUb7GOa3-WBW00gqKaYrTG3NFYQgIXP3-6Eki4,4975
|
5
|
+
autosh/session.py,sha256=pU3AAqI3vEEFdtsXK_1ZV6uXzjkzIG8q3Yo01K-Q6dw,9615
|
6
|
+
autosh/md/__init__.py,sha256=0SK4rkynUwR77E8IU-ORJmWou_aTxC6xzQQo-IfAsaM,213
|
7
|
+
autosh/md/inline_text.py,sha256=I8CaxksvOpTr1mlUtnk50TVVgF9CLnLsth2ux0cdTM4,6422
|
8
|
+
autosh/md/printer.py,sha256=qD3AcGosnPMRAf-KiRw1PUixtP-kS5xFIPFjzGLLT0g,11099
|
9
|
+
autosh/md/state.py,sha256=OEp0kUb63HrAN5BPcPx9mxWogofayumH9GZGqLdOoEA,3792
|
10
|
+
autosh/md/stream.py,sha256=zaVJR6Kog6EZcuVaGszwgywpysHcn8fRZ0KItmKRhiA,3224
|
11
|
+
autosh/plugins/__init__.py,sha256=NXq27wvS97NEH6hTtWRyp-ut0BE9fq5QyKBjXOj39zY,2487
|
12
|
+
autosh/plugins/calc.py,sha256=qo0EajIpNPv9PtLNLygyEjVaxo1F6_S62kmoJZq5oLM,581
|
13
|
+
autosh/plugins/cli.py,sha256=D6S_QHPmjBBB9gwgXeJrwxUs3u0TNty_tHVICbEPGbs,8522
|
14
|
+
autosh/plugins/clock.py,sha256=GGi0HAG6f6-FP1qqGoyCcUj11q_VnkaGArumsMk0CkY,542
|
15
|
+
autosh/plugins/code.py,sha256=0JwFzq6ejgbisCqBm_RG1r1WEVNou64ue-siVIpvZqs,2291
|
16
|
+
autosh/plugins/search.py,sha256=1d3Gqq6uXu0ntTBpw44Ab_haAySvZLMj3e2MQd3DHO0,2736
|
17
|
+
autosh/plugins/web.py,sha256=lmD2JnsqVI1qKgSFrk39851jCZoPyPRaVvHeEFYXylA,2597
|
18
|
+
autosh-0.0.3.dist-info/METADATA,sha256=M2wVPjOWoiCXWbYzs216oQ4eUSE_s5-b67-qTufmnRE,1897
|
19
|
+
autosh-0.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
20
|
+
autosh-0.0.3.dist-info/entry_points.txt,sha256=BV7bzUnxG6Z5InEkrfajGCxjooYORC5tZDDZctOPenQ,67
|
21
|
+
autosh-0.0.3.dist-info/licenses/LICENSE,sha256=BnLDJsIJe-Dm18unR9DOoSv7QOfAz6LeIQc1yHAjxp0,1066
|
22
|
+
autosh-0.0.3.dist-info/RECORD,,
|