akashcli 4.5.5__tar.gz → 4.5.7__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.
- {akashcli-4.5.5 → akashcli-4.5.7}/PKG-INFO +1 -1
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/relay.py +95 -25
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/PKG-INFO +1 -1
- {akashcli-4.5.5 → akashcli-4.5.7}/pyproject.toml +1 -1
- {akashcli-4.5.5 → akashcli-4.5.7}/README.md +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/__init__.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/_net.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/auth.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/cli.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/client.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/config.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/crypto.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/env_manager.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/exceptions.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/exec.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/integrity.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/logger.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/models.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/p2p.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/packages.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/restore.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/snapshot_crypto.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/storage.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/terminal.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/uploader.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/utils.py +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/SOURCES.txt +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/dependency_links.txt +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/entry_points.txt +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/requires.txt +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/top_level.txt +0 -0
- {akashcli-4.5.5 → akashcli-4.5.7}/setup.cfg +0 -0
|
@@ -193,27 +193,59 @@ def _handle_run(ws, frame: dict) -> None:
|
|
|
193
193
|
|
|
194
194
|
|
|
195
195
|
def _handle_pty(ws, frame: dict) -> None:
|
|
196
|
-
"""Run a
|
|
197
|
-
import pty, select, struct, fcntl, termios
|
|
196
|
+
"""Run a shell in a properly configured PTY and relay I/O."""
|
|
197
|
+
import pty, select, struct, fcntl, termios
|
|
198
198
|
|
|
199
199
|
command = frame.get("command", "")
|
|
200
200
|
rows = int(frame.get("rows", 24))
|
|
201
201
|
cols = int(frame.get("cols", 80))
|
|
202
202
|
|
|
203
203
|
master_fd, slave_fd = pty.openpty()
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
204
|
+
# Set terminal window size on both fds
|
|
205
|
+
winsz = struct.pack("HHHH", rows, cols, 0, 0)
|
|
206
|
+
for fd in (master_fd, slave_fd):
|
|
207
|
+
try:
|
|
208
|
+
fcntl.ioctl(fd, termios.TIOCSWINSZ, winsz)
|
|
209
|
+
except Exception:
|
|
210
|
+
pass
|
|
211
|
+
|
|
212
|
+
def _child_setup():
|
|
213
|
+
"""Run inside the child process before exec — creates a new session
|
|
214
|
+
and attaches the slave PTY as the controlling terminal so bash gets
|
|
215
|
+
full job control (no 'cannot set terminal process group' errors)."""
|
|
216
|
+
os.setsid()
|
|
217
|
+
try:
|
|
218
|
+
fcntl.ioctl(0, termios.TIOCSCTTY, 0)
|
|
219
|
+
except Exception:
|
|
220
|
+
pass
|
|
209
221
|
|
|
210
222
|
import subprocess as _sp
|
|
223
|
+
shell = command or os.environ.get("SHELL", "/bin/bash")
|
|
224
|
+
|
|
225
|
+
# DevOS-style PS1 injected into every relay shell session.
|
|
226
|
+
# Uses only bash builtins + git (optional). No external deps.
|
|
227
|
+
_AKASH_PS1 = (
|
|
228
|
+
r'__akash_ps1() { '
|
|
229
|
+
r' local b; b=$(git branch --show-current 2>/dev/null); '
|
|
230
|
+
r' [ -n "$b" ] && echo -e " \033[90m(\033[1;35m$b\033[90m)\033[0m"; '
|
|
231
|
+
r'}; '
|
|
232
|
+
r'PS1="\n\033[1;36m╭─\033[0m \033[1;32m\u\033[90m@\033[1;32m\h\033[0m'
|
|
233
|
+
r' \033[1;34m\w\033[0m\$(__akash_ps1)\n'
|
|
234
|
+
r'\033[1;36m╰─❯\033[0m "; '
|
|
235
|
+
r'export PS1'
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Run the shell directly (not wrapped in bash -c) so it IS the login shell
|
|
239
|
+
# and gets a proper controlling terminal from slave_fd.
|
|
211
240
|
try:
|
|
212
241
|
proc = _sp.Popen(
|
|
213
|
-
[
|
|
242
|
+
[shell],
|
|
214
243
|
stdin=slave_fd, stdout=slave_fd, stderr=slave_fd,
|
|
215
|
-
close_fds=True,
|
|
216
|
-
|
|
244
|
+
close_fds=True,
|
|
245
|
+
preexec_fn=_child_setup,
|
|
246
|
+
cwd=os.path.expanduser("~"),
|
|
247
|
+
env={**os.environ,
|
|
248
|
+
"TERM": "xterm-256color",
|
|
217
249
|
"COLUMNS": str(cols), "LINES": str(rows)},
|
|
218
250
|
)
|
|
219
251
|
except Exception as e:
|
|
@@ -225,15 +257,25 @@ def _handle_pty(ws, frame: dict) -> None:
|
|
|
225
257
|
return
|
|
226
258
|
|
|
227
259
|
os.close(slave_fd)
|
|
260
|
+
|
|
261
|
+
# Give bash ~120 ms to finish sourcing .bashrc, then inject our PS1.
|
|
262
|
+
import time as _time
|
|
263
|
+
_time.sleep(0.12)
|
|
264
|
+
try:
|
|
265
|
+
os.write(master_fd, (_AKASH_PS1 + "\n").encode())
|
|
266
|
+
except Exception:
|
|
267
|
+
pass
|
|
268
|
+
|
|
228
269
|
stop = threading.Event()
|
|
229
270
|
|
|
230
|
-
# Thread: master fd → WebSocket
|
|
271
|
+
# Thread: master fd → WebSocket (drain as fast as possible)
|
|
231
272
|
def _out():
|
|
232
273
|
while not stop.is_set():
|
|
233
274
|
try:
|
|
234
|
-
r, _, _ = select.select([master_fd], [], [], 0.
|
|
275
|
+
r, _, _ = select.select([master_fd], [], [], 0.02)
|
|
235
276
|
if r:
|
|
236
|
-
|
|
277
|
+
# Read up to 16 KiB at a time for snappy bulk output
|
|
278
|
+
data = os.read(master_fd, 16384)
|
|
237
279
|
if not data:
|
|
238
280
|
break
|
|
239
281
|
ws.send(json.dumps({
|
|
@@ -241,6 +283,18 @@ def _handle_pty(ws, frame: dict) -> None:
|
|
|
241
283
|
"data": base64.b64encode(data).decode(),
|
|
242
284
|
}))
|
|
243
285
|
elif proc.poll() is not None:
|
|
286
|
+
# Drain any remaining output
|
|
287
|
+
try:
|
|
288
|
+
r2, _, _ = select.select([master_fd], [], [], 0.1)
|
|
289
|
+
if r2:
|
|
290
|
+
data = os.read(master_fd, 16384)
|
|
291
|
+
if data:
|
|
292
|
+
ws.send(json.dumps({
|
|
293
|
+
"type": "pty_out",
|
|
294
|
+
"data": base64.b64encode(data).decode(),
|
|
295
|
+
}))
|
|
296
|
+
except Exception:
|
|
297
|
+
pass
|
|
244
298
|
break
|
|
245
299
|
except Exception:
|
|
246
300
|
break
|
|
@@ -269,9 +323,9 @@ def _handle_pty(ws, frame: dict) -> None:
|
|
|
269
323
|
elif it == "resize":
|
|
270
324
|
r = int(inf.get("rows", rows))
|
|
271
325
|
c = int(inf.get("cols", cols))
|
|
326
|
+
winsz = struct.pack("HHHH", r, c, 0, 0)
|
|
272
327
|
try:
|
|
273
|
-
fcntl.ioctl(master_fd, termios.TIOCSWINSZ,
|
|
274
|
-
struct.pack("HHHH", r, c, 0, 0))
|
|
328
|
+
fcntl.ioctl(master_fd, termios.TIOCSWINSZ, winsz)
|
|
275
329
|
except Exception:
|
|
276
330
|
pass
|
|
277
331
|
elif it == "close":
|
|
@@ -279,10 +333,14 @@ def _handle_pty(ws, frame: dict) -> None:
|
|
|
279
333
|
|
|
280
334
|
stop.set()
|
|
281
335
|
try:
|
|
282
|
-
proc.
|
|
336
|
+
proc.terminate()
|
|
283
337
|
except Exception:
|
|
284
338
|
pass
|
|
285
|
-
|
|
339
|
+
try:
|
|
340
|
+
proc.wait(timeout=2)
|
|
341
|
+
except Exception:
|
|
342
|
+
try: proc.kill()
|
|
343
|
+
except Exception: pass
|
|
286
344
|
t_out.join(timeout=1)
|
|
287
345
|
try:
|
|
288
346
|
os.close(master_fd)
|
|
@@ -404,10 +462,10 @@ def connect_shell(api_key: str, base_url: str,
|
|
|
404
462
|
try:
|
|
405
463
|
while not stop.is_set():
|
|
406
464
|
if _stdin_fd >= 0:
|
|
407
|
-
r = _sel.select([_stdin_fd], [], [], 0.
|
|
465
|
+
r = _sel.select([_stdin_fd], [], [], 0.02)[0]
|
|
408
466
|
if not r:
|
|
409
467
|
continue
|
|
410
|
-
chunk = os.read(_stdin_fd,
|
|
468
|
+
chunk = os.read(_stdin_fd, 4096)
|
|
411
469
|
else:
|
|
412
470
|
chunk = sys.stdin.buffer.read(1)
|
|
413
471
|
if not chunk:
|
|
@@ -702,11 +760,10 @@ def guest_connect_shell(base_url: str, code: str) -> int:
|
|
|
702
760
|
if ftype == "waiting_approval":
|
|
703
761
|
fp = first.get("fingerprint", "")
|
|
704
762
|
_restore() # restore terminal so we can print cleanly
|
|
705
|
-
print(f" {
|
|
763
|
+
print(f"\n {_C}◈{_RS} {_DM}waiting for host to approve…{_RS}")
|
|
706
764
|
if fp:
|
|
707
|
-
print(f" {_DM}
|
|
708
|
-
print(f" {_DM}
|
|
709
|
-
print()
|
|
765
|
+
print(f" {_DM}fingerprint {_C}{fp}{_RS}")
|
|
766
|
+
print(f" {_DM}host has 30 s to respond{_RS}\n")
|
|
710
767
|
# Wait for next frame: connected or denied
|
|
711
768
|
try:
|
|
712
769
|
second = json.loads(ws.recv(timeout=35))
|
|
@@ -728,6 +785,18 @@ def guest_connect_shell(base_url: str, code: str) -> int:
|
|
|
728
785
|
_restore()
|
|
729
786
|
raise RuntimeError(first.get("message", "unexpected server response"))
|
|
730
787
|
|
|
788
|
+
# Print a clean DevOS-style session header before entering raw mode
|
|
789
|
+
_restore()
|
|
790
|
+
print(f"\n {_G}◉{_RS} {_C}akash shell{_RS} {_DM}· code {code} · Ctrl-C to disconnect{_RS}\n")
|
|
791
|
+
# Re-enter raw mode
|
|
792
|
+
try:
|
|
793
|
+
import tty as _tty3, termios as _term3
|
|
794
|
+
_old_settings = _term3.tcgetattr(_stdin_fd)
|
|
795
|
+
_tty3.setraw(_stdin_fd)
|
|
796
|
+
_raw_ok = True
|
|
797
|
+
except Exception:
|
|
798
|
+
pass
|
|
799
|
+
|
|
731
800
|
ws.send(json.dumps({
|
|
732
801
|
"type": "pty_run",
|
|
733
802
|
"command": os.environ.get("SHELL", "/bin/bash"),
|
|
@@ -740,10 +809,11 @@ def guest_connect_shell(base_url: str, code: str) -> int:
|
|
|
740
809
|
try:
|
|
741
810
|
while not stop.is_set():
|
|
742
811
|
if _stdin_fd >= 0:
|
|
743
|
-
r = _sel.select([_stdin_fd], [], [], 0.
|
|
812
|
+
r = _sel.select([_stdin_fd], [], [], 0.02)[0]
|
|
744
813
|
if not r:
|
|
745
814
|
continue
|
|
746
|
-
|
|
815
|
+
# Read up to 4 KiB — handles paste bursts snappily
|
|
816
|
+
chunk = os.read(_stdin_fd, 4096)
|
|
747
817
|
else:
|
|
748
818
|
chunk = sys.stdin.buffer.read(1)
|
|
749
819
|
if not chunk:
|
|
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
|