autosh 0.0.1__py3-none-any.whl → 0.0.2__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.py +10 -4
- autosh/md.py +14 -0
- autosh/plugins/cli.py +42 -0
- autosh/session.py +58 -5
- {autosh-0.0.1.dist-info → autosh-0.0.2.dist-info}/METADATA +1 -1
- {autosh-0.0.1.dist-info → autosh-0.0.2.dist-info}/RECORD +9 -9
- {autosh-0.0.1.dist-info → autosh-0.0.2.dist-info}/WHEEL +0 -0
- {autosh-0.0.1.dist-info → autosh-0.0.2.dist-info}/entry_points.txt +0 -0
- {autosh-0.0.1.dist-info → autosh-0.0.2.dist-info}/licenses/LICENSE +0 -0
autosh/config.py
CHANGED
@@ -3,6 +3,8 @@ from pydantic import BaseModel, Field
|
|
3
3
|
from pathlib import Path
|
4
4
|
import tomllib
|
5
5
|
|
6
|
+
import rich
|
7
|
+
|
6
8
|
USER_CONFIG_PATH = Path.home() / ".config" / "autosh" / "config.toml"
|
7
9
|
|
8
10
|
|
@@ -42,10 +44,14 @@ class Config(BaseModel):
|
|
42
44
|
@staticmethod
|
43
45
|
def load() -> "Config":
|
44
46
|
if USER_CONFIG_PATH.is_file():
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
47
|
+
try:
|
48
|
+
doc = tomllib.loads(USER_CONFIG_PATH.read_text())
|
49
|
+
main = doc.get("autosh", {})
|
50
|
+
plugins = Plugins(**doc.get("plugins", {}))
|
51
|
+
config = Config.model_validate({**main, "plugins": plugins})
|
52
|
+
except tomllib.TOMLDecodeError as e:
|
53
|
+
rich.print(f"[bold red]Error:[/bold red] invalid config file: {e}")
|
54
|
+
sys.exit(1)
|
49
55
|
else:
|
50
56
|
config = Config()
|
51
57
|
return config
|
autosh/md.py
CHANGED
@@ -142,6 +142,7 @@ class MarkdowmPrinter:
|
|
142
142
|
case "`":
|
143
143
|
await self.next()
|
144
144
|
if (i := find("code")) is not None:
|
145
|
+
self.print(c)
|
145
146
|
if not outer_is_dim:
|
146
147
|
self.print("\x1b[22m")
|
147
148
|
if find("bold") is not None or outer_is_bold:
|
@@ -150,6 +151,7 @@ class MarkdowmPrinter:
|
|
150
151
|
else:
|
151
152
|
self.print("\x1b[2m")
|
152
153
|
styles.append("code")
|
154
|
+
self.print(c)
|
153
155
|
# Bold
|
154
156
|
case "*" if (
|
155
157
|
not_code and await self.check("**") and not find_italic_first()
|
@@ -158,24 +160,32 @@ class MarkdowmPrinter:
|
|
158
160
|
await self.next()
|
159
161
|
# print(">", styles, find("bold"))
|
160
162
|
if (i := find("bold")) is not None:
|
163
|
+
self.print(c)
|
164
|
+
self.print(c)
|
161
165
|
if not outer_is_bold:
|
162
166
|
self.print("\x1b[22m")
|
163
167
|
styles = styles[:i]
|
164
168
|
else:
|
165
169
|
self.print("\x1b[1m")
|
166
170
|
styles.append("bold")
|
171
|
+
self.print(c)
|
172
|
+
self.print(c)
|
167
173
|
case "_" if (
|
168
174
|
not_code and await self.check("__") and not find_italic_first()
|
169
175
|
):
|
170
176
|
await self.next()
|
171
177
|
await self.next()
|
172
178
|
if (i := find("bold")) is not None:
|
179
|
+
self.print(c)
|
180
|
+
self.print(c)
|
173
181
|
if not outer_is_bold:
|
174
182
|
self.print("\x1b[22m")
|
175
183
|
styles = styles[:i]
|
176
184
|
else:
|
177
185
|
self.print("\x1b[1m")
|
178
186
|
styles.append("bold")
|
187
|
+
self.print(c)
|
188
|
+
self.print(c)
|
179
189
|
# Italic
|
180
190
|
case "*" | "_" if (
|
181
191
|
not_code
|
@@ -184,6 +194,7 @@ class MarkdowmPrinter:
|
|
184
194
|
):
|
185
195
|
await self.next()
|
186
196
|
if (i := find("italic")) is not None:
|
197
|
+
self.print(c)
|
187
198
|
if not outer_is_italic:
|
188
199
|
self.print("\x1b[23m")
|
189
200
|
styles = styles[:i]
|
@@ -191,16 +202,19 @@ class MarkdowmPrinter:
|
|
191
202
|
else:
|
192
203
|
self.print("\x1b[3m")
|
193
204
|
styles.append("italic")
|
205
|
+
self.print(c)
|
194
206
|
# Strike through
|
195
207
|
case "~" if not_code and await self.check("~~"):
|
196
208
|
await self.next()
|
197
209
|
await self.next()
|
198
210
|
if (i := find("strike")) is not None:
|
211
|
+
self.print("~~")
|
199
212
|
self.print("\x1b[29m")
|
200
213
|
styles = styles[:i]
|
201
214
|
else:
|
202
215
|
self.print("\x1b[9m")
|
203
216
|
styles.append("strike")
|
217
|
+
self.print("~~")
|
204
218
|
case _:
|
205
219
|
self.print(c)
|
206
220
|
await self.next()
|
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"""
|
@@ -90,7 +92,7 @@ class Session:
|
|
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,6 +110,7 @@ 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():
|
@@ -119,8 +122,11 @@ class Session:
|
|
119
122
|
)
|
120
123
|
completion = self.agent.chat_completion(prompt, stream=True)
|
121
124
|
async for stream in completion:
|
122
|
-
if
|
125
|
+
if not loading:
|
126
|
+
loading = self.__create_loading_indicator()
|
127
|
+
if await self.__render_streamed_markdown(stream, loading=loading):
|
123
128
|
print()
|
129
|
+
loading = None
|
124
130
|
|
125
131
|
async def exec_from_stdin(self):
|
126
132
|
if sys.stdin.isatty():
|
@@ -141,19 +147,60 @@ class Session:
|
|
141
147
|
console = rich.console.Console()
|
142
148
|
while True:
|
143
149
|
try:
|
144
|
-
prompt = console.input("[bold]>[/bold] ").strip()
|
150
|
+
prompt = console.input("[bold blue]>[/bold blue] ").strip()
|
145
151
|
if prompt in ["exit", "quit"]:
|
146
152
|
break
|
147
153
|
if len(prompt) == 0:
|
148
154
|
continue
|
155
|
+
loading = self.__create_loading_indicator(newline=True)
|
149
156
|
completion = self.agent.chat_completion(prompt, stream=True)
|
150
157
|
async for stream in completion:
|
151
|
-
if
|
158
|
+
if not loading:
|
159
|
+
loading = self.__create_loading_indicator()
|
160
|
+
if await self.__render_streamed_markdown(stream, loading=loading):
|
152
161
|
print()
|
162
|
+
loading = None
|
153
163
|
except KeyboardInterrupt:
|
154
164
|
break
|
155
165
|
|
156
|
-
|
166
|
+
def __create_loading_indicator(self, newline: bool = False):
|
167
|
+
return (
|
168
|
+
asyncio.create_task(self.__loading(newline))
|
169
|
+
if sys.stdout.isatty()
|
170
|
+
else None
|
171
|
+
)
|
172
|
+
|
173
|
+
async def __loading(self, newline: bool = False):
|
174
|
+
chars = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
175
|
+
char_width = 1
|
176
|
+
msg = "Loading..."
|
177
|
+
count = 0
|
178
|
+
print("\x1b[2m", end="", flush=True)
|
179
|
+
while True:
|
180
|
+
try:
|
181
|
+
print(chars[count], end="", flush=True)
|
182
|
+
print(" " + msg, end="", flush=True)
|
183
|
+
count += 1
|
184
|
+
await asyncio.sleep(0.1)
|
185
|
+
length = char_width + len(msg) + 1
|
186
|
+
print("\b" * length, end="", flush=True)
|
187
|
+
print(" " * length, end="", flush=True)
|
188
|
+
print("\b" * length, end="", flush=True)
|
189
|
+
if count == len(chars):
|
190
|
+
count = 0
|
191
|
+
except asyncio.CancelledError:
|
192
|
+
length = char_width + len(msg) + 1
|
193
|
+
print("\b" * length, end="", flush=True)
|
194
|
+
print(" " * length, end="", flush=True)
|
195
|
+
print("\b" * length, end="", flush=True)
|
196
|
+
print("\x1b[0m", end="", flush=True)
|
197
|
+
if newline:
|
198
|
+
print()
|
199
|
+
break
|
200
|
+
|
201
|
+
async def __render_streamed_markdown(
|
202
|
+
self, stream: MessageStream, loading: asyncio.Task[None] | None = None
|
203
|
+
):
|
157
204
|
if sys.stdout.isatty():
|
158
205
|
# buffer first few chars so we don't need to launch glow if there is no output
|
159
206
|
chunks = aiter(stream)
|
@@ -163,8 +210,14 @@ class Session:
|
|
163
210
|
buf += await anext(chunks)
|
164
211
|
except StopAsyncIteration:
|
165
212
|
if len(buf) == 0:
|
213
|
+
if loading:
|
214
|
+
loading.cancel()
|
215
|
+
await loading
|
166
216
|
return False
|
167
217
|
break
|
218
|
+
if loading:
|
219
|
+
loading.cancel()
|
220
|
+
await loading
|
168
221
|
|
169
222
|
content = {"v": ""}
|
170
223
|
|
@@ -1,17 +1,17 @@
|
|
1
1
|
autosh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
autosh/config.py,sha256=
|
2
|
+
autosh/config.py,sha256=KPuXr_MF5Pbdn5pCCgq9AcyDSDlOnCJ4vBetKmhodic,2348
|
3
3
|
autosh/main.py,sha256=myRolKqfHyQEgujqmUndDSm9J0C21w3zqA9-SYNX1kY,4961
|
4
|
-
autosh/md.py,sha256=
|
5
|
-
autosh/session.py,sha256=
|
4
|
+
autosh/md.py,sha256=qXg5ZFVUwek3rUXb-oEti1soRRbIq8EbDq88lEYRoO4,14267
|
5
|
+
autosh/session.py,sha256=kCaD-7tZpozz56sWlMGp0njqwYRf3cZOau1ulWRKJEI,8885
|
6
6
|
autosh/plugins/__init__.py,sha256=yOTobuyYFpUWl5BCowGzJG8rZX2Whlagail_QyHVlo4,2487
|
7
7
|
autosh/plugins/calc.py,sha256=qo0EajIpNPv9PtLNLygyEjVaxo1F6_S62kmoJZq5oLM,581
|
8
|
-
autosh/plugins/cli.py,sha256=
|
8
|
+
autosh/plugins/cli.py,sha256=D6S_QHPmjBBB9gwgXeJrwxUs3u0TNty_tHVICbEPGbs,8522
|
9
9
|
autosh/plugins/clock.py,sha256=GGi0HAG6f6-FP1qqGoyCcUj11q_VnkaGArumsMk0CkY,542
|
10
10
|
autosh/plugins/code.py,sha256=0JwFzq6ejgbisCqBm_RG1r1WEVNou64ue-siVIpvZqs,2291
|
11
11
|
autosh/plugins/search.py,sha256=1d3Gqq6uXu0ntTBpw44Ab_haAySvZLMj3e2MQd3DHO0,2736
|
12
12
|
autosh/plugins/web.py,sha256=lmD2JnsqVI1qKgSFrk39851jCZoPyPRaVvHeEFYXylA,2597
|
13
|
-
autosh-0.0.
|
14
|
-
autosh-0.0.
|
15
|
-
autosh-0.0.
|
16
|
-
autosh-0.0.
|
17
|
-
autosh-0.0.
|
13
|
+
autosh-0.0.2.dist-info/METADATA,sha256=y9pbK71nuueA11LCv6AsR24MSsnqNut-7-zljpOC5_Y,1720
|
14
|
+
autosh-0.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
15
|
+
autosh-0.0.2.dist-info/entry_points.txt,sha256=BV7bzUnxG6Z5InEkrfajGCxjooYORC5tZDDZctOPenQ,67
|
16
|
+
autosh-0.0.2.dist-info/licenses/LICENSE,sha256=BnLDJsIJe-Dm18unR9DOoSv7QOfAz6LeIQc1yHAjxp0,1066
|
17
|
+
autosh-0.0.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|