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.
Files changed (32) hide show
  1. {akashcli-4.5.5 → akashcli-4.5.7}/PKG-INFO +1 -1
  2. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/relay.py +95 -25
  3. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/PKG-INFO +1 -1
  4. {akashcli-4.5.5 → akashcli-4.5.7}/pyproject.toml +1 -1
  5. {akashcli-4.5.5 → akashcli-4.5.7}/README.md +0 -0
  6. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/__init__.py +0 -0
  7. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/_net.py +0 -0
  8. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/auth.py +0 -0
  9. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/cli.py +0 -0
  10. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/client.py +0 -0
  11. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/config.py +0 -0
  12. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/crypto.py +0 -0
  13. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/env_manager.py +0 -0
  14. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/exceptions.py +0 -0
  15. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/exec.py +0 -0
  16. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/integrity.py +0 -0
  17. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/logger.py +0 -0
  18. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/models.py +0 -0
  19. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/p2p.py +0 -0
  20. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/packages.py +0 -0
  21. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/restore.py +0 -0
  22. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/snapshot_crypto.py +0 -0
  23. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/storage.py +0 -0
  24. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/terminal.py +0 -0
  25. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/uploader.py +0 -0
  26. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli/utils.py +0 -0
  27. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/SOURCES.txt +0 -0
  28. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/dependency_links.txt +0 -0
  29. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/entry_points.txt +0 -0
  30. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/requires.txt +0 -0
  31. {akashcli-4.5.5 → akashcli-4.5.7}/akashcli.egg-info/top_level.txt +0 -0
  32. {akashcli-4.5.5 → akashcli-4.5.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.5.5
3
+ Version: 4.5.7
4
4
  Summary: Akash CLI — secure remote shell relay, file vault, and device management
5
5
  License: MIT
6
6
  Keywords: akash,cli,relay,remote-shell,vault
@@ -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 command in a PTY and relay I/O until the process exits."""
197
- import pty, select, struct, fcntl, termios, threading as _th
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
- try:
205
- fcntl.ioctl(master_fd, termios.TIOCSWINSZ,
206
- struct.pack("HHHH", rows, cols, 0, 0))
207
- except Exception:
208
- pass
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
- ["/bin/bash", "-c", command],
242
+ [shell],
214
243
  stdin=slave_fd, stdout=slave_fd, stderr=slave_fd,
215
- close_fds=True, cwd=os.path.expanduser("~"),
216
- env={**os.environ, "TERM": "xterm-256color",
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.05)
275
+ r, _, _ = select.select([master_fd], [], [], 0.02)
235
276
  if r:
236
- data = os.read(master_fd, 4096)
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.kill()
336
+ proc.terminate()
283
337
  except Exception:
284
338
  pass
285
- proc.wait()
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.05)[0]
465
+ r = _sel.select([_stdin_fd], [], [], 0.02)[0]
408
466
  if not r:
409
467
  continue
410
- chunk = os.read(_stdin_fd, 256)
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" {_Y}…{_RS} waiting for host to approve this connection…")
763
+ print(f"\n {_C}◈{_RS} {_DM}waiting for host to approve…{_RS}")
706
764
  if fp:
707
- print(f" {_DM}Your fingerprint: {_C}{fp}{_RS}")
708
- print(f" {_DM}(host has 30 seconds to respond){_RS}")
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.05)[0]
812
+ r = _sel.select([_stdin_fd], [], [], 0.02)[0]
744
813
  if not r:
745
814
  continue
746
- chunk = os.read(_stdin_fd, 256)
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.5.5
3
+ Version: 4.5.7
4
4
  Summary: Akash CLI — secure remote shell relay, file vault, and device management
5
5
  License: MIT
6
6
  Keywords: akash,cli,relay,remote-shell,vault
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "akashcli"
3
- version = "4.5.5"
3
+ version = "4.5.7"
4
4
  description = "Akash CLI — secure remote shell relay, file vault, and device management"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
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