akashcli 4.5.0__tar.gz → 4.5.1__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.0 → akashcli-4.5.1}/PKG-INFO +1 -1
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/relay.py +96 -11
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli.egg-info/PKG-INFO +1 -1
- {akashcli-4.5.0 → akashcli-4.5.1}/pyproject.toml +1 -1
- {akashcli-4.5.0 → akashcli-4.5.1}/README.md +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/__init__.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/auth.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/cli.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/client.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/config.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/crypto.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/env_manager.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/exceptions.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/exec.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/integrity.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/logger.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/models.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/p2p.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/packages.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/restore.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/snapshot_crypto.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/storage.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/terminal.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/uploader.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli/utils.py +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli.egg-info/SOURCES.txt +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli.egg-info/dependency_links.txt +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli.egg-info/entry_points.txt +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli.egg-info/requires.txt +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/akashcli.egg-info/top_level.txt +0 -0
- {akashcli-4.5.0 → akashcli-4.5.1}/setup.cfg +0 -0
|
@@ -554,6 +554,9 @@ def guest_serve(base_url: str) -> None:
|
|
|
554
554
|
Host a shell session with no API key.
|
|
555
555
|
Requests a pairing code from the server, prints it, then waits for
|
|
556
556
|
someone to connect with: akash shell --code <CODE>
|
|
557
|
+
|
|
558
|
+
Security: every incoming connection shows the guest IP + fingerprint
|
|
559
|
+
and asks the host to approve before the shell is bridged.
|
|
557
560
|
"""
|
|
558
561
|
import httpx
|
|
559
562
|
import websockets.sync.client as _ws
|
|
@@ -571,15 +574,15 @@ def guest_serve(base_url: str) -> None:
|
|
|
571
574
|
print()
|
|
572
575
|
print(f" {_G}◉{_RS} guest relay ready")
|
|
573
576
|
print()
|
|
574
|
-
print(f"
|
|
577
|
+
print(f" {_DM}Pairing code:{_RS} {_C}{code}{_RS}")
|
|
575
578
|
print()
|
|
576
|
-
print(f" {_DM}On another machine
|
|
577
|
-
print(f" {_C}akash shell --code {code}{_RS}")
|
|
579
|
+
print(f" {_DM}On another machine:{_RS} {_C}akash shell --code {code}{_RS}")
|
|
578
580
|
print()
|
|
579
581
|
print(f" {_DM}Code expires in {ttl // 60} min · Ctrl-C to stop{_RS}")
|
|
582
|
+
print(f" {_DM}You will be asked to approve each connection before it gets shell access.{_RS}")
|
|
580
583
|
print()
|
|
581
584
|
|
|
582
|
-
# 2. Connect to host endpoint
|
|
585
|
+
# 2. Connect to host endpoint — handle knock/allow/deny then serve shell
|
|
583
586
|
while True:
|
|
584
587
|
try:
|
|
585
588
|
ws_url = _guest_host_url(base_url, code)
|
|
@@ -588,20 +591,70 @@ def guest_serve(base_url: str) -> None:
|
|
|
588
591
|
if ack.get("type") == "error":
|
|
589
592
|
print(f" {_R}✗{_RS} {ack.get('message')}", file=sys.stderr)
|
|
590
593
|
return
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
+
|
|
595
|
+
print(f" {_DM}waiting for connection…{_RS}", flush=True)
|
|
596
|
+
|
|
597
|
+
# Wait for either a knock (someone wants in) or a ping
|
|
598
|
+
while True:
|
|
599
|
+
try:
|
|
600
|
+
raw = ws.recv(timeout=35)
|
|
601
|
+
except Exception:
|
|
602
|
+
# Timeout or disconnect — break to reconnect loop
|
|
603
|
+
break
|
|
604
|
+
frame = json.loads(raw)
|
|
605
|
+
ftype = frame.get("type")
|
|
606
|
+
|
|
607
|
+
if ftype == "ping":
|
|
608
|
+
ws.send(json.dumps({"type": "pong"}))
|
|
609
|
+
continue
|
|
610
|
+
|
|
611
|
+
if ftype == "knock":
|
|
612
|
+
guest_ip = frame.get("ip", "unknown")
|
|
613
|
+
fp = frame.get("fingerprint", "????????")
|
|
614
|
+
print()
|
|
615
|
+
print(f" {_Y}⚡{_RS} Incoming connection request")
|
|
616
|
+
print(f" {_DM}From:{_RS} {_C}{guest_ip}{_RS}")
|
|
617
|
+
print(f" {_DM}Fingerprint:{_RS} {_C}{fp}{_RS}")
|
|
618
|
+
print()
|
|
619
|
+
|
|
620
|
+
# Prompt host — default is N (deny)
|
|
621
|
+
try:
|
|
622
|
+
sys.stdout.write(f" Allow? [y/N]: ")
|
|
623
|
+
sys.stdout.flush()
|
|
624
|
+
answer = sys.stdin.readline().strip().lower()
|
|
625
|
+
except (EOFError, KeyboardInterrupt):
|
|
626
|
+
answer = "n"
|
|
627
|
+
|
|
628
|
+
if answer == "y":
|
|
629
|
+
ws.send(json.dumps({"type": "allow"}))
|
|
630
|
+
print(f" {_G}✓{_RS} connection allowed — starting shell…")
|
|
631
|
+
print()
|
|
632
|
+
# Now serve shell frames
|
|
633
|
+
_run_relay_device(ws, f"guest:{code}")
|
|
634
|
+
print(f"\n {_DM}session ended — waiting for next connection…{_RS}\n")
|
|
635
|
+
else:
|
|
636
|
+
ws.send(json.dumps({"type": "deny"}))
|
|
637
|
+
print(f" {_R}✗{_RS} connection denied")
|
|
638
|
+
print()
|
|
639
|
+
# Loop back — still waiting on same code
|
|
640
|
+
continue
|
|
641
|
+
|
|
642
|
+
# Any other frame — ignore and keep waiting
|
|
643
|
+
continue
|
|
644
|
+
|
|
594
645
|
except KeyboardInterrupt:
|
|
595
646
|
print(f"\n {_DM}guest serve stopped{_RS}")
|
|
596
647
|
break
|
|
597
648
|
except Exception as exc:
|
|
598
|
-
print(f" {_Y}!{_RS} relay disconnected ({exc}) — reconnecting…")
|
|
649
|
+
print(f" {_Y}!{_RS} relay disconnected ({exc}) — reconnecting in 3s…")
|
|
599
650
|
time.sleep(3)
|
|
600
651
|
|
|
601
652
|
|
|
602
653
|
def guest_connect_shell(base_url: str, code: str) -> int:
|
|
603
654
|
"""
|
|
604
655
|
Connect to a guest host by pairing code — no API key needed.
|
|
656
|
+
Handles the approval handshake: shows a spinner while the host decides,
|
|
657
|
+
then opens a full PTY shell if approved.
|
|
605
658
|
Returns the remote exit code.
|
|
606
659
|
"""
|
|
607
660
|
import websockets.sync.client as _ws
|
|
@@ -636,10 +689,42 @@ def guest_connect_shell(base_url: str, code: str) -> int:
|
|
|
636
689
|
exit_code = 1
|
|
637
690
|
try:
|
|
638
691
|
with _ws.connect(ws_url, open_timeout=15, close_timeout=10) as ws:
|
|
639
|
-
|
|
640
|
-
|
|
692
|
+
# First frame: could be error, waiting_approval, or connected
|
|
693
|
+
first = json.loads(ws.recv())
|
|
694
|
+
ftype = first.get("type")
|
|
695
|
+
|
|
696
|
+
if ftype == "error":
|
|
641
697
|
_restore()
|
|
642
|
-
raise RuntimeError(
|
|
698
|
+
raise RuntimeError(first.get("message", "relay error"))
|
|
699
|
+
|
|
700
|
+
if ftype == "waiting_approval":
|
|
701
|
+
fp = first.get("fingerprint", "")
|
|
702
|
+
_restore() # restore terminal so we can print cleanly
|
|
703
|
+
print(f" {_Y}…{_RS} waiting for host to approve this connection…")
|
|
704
|
+
if fp:
|
|
705
|
+
print(f" {_DM}Your fingerprint: {_C}{fp}{_RS}")
|
|
706
|
+
print(f" {_DM}(host has 30 seconds to respond){_RS}")
|
|
707
|
+
print()
|
|
708
|
+
# Wait for next frame: connected or denied
|
|
709
|
+
try:
|
|
710
|
+
second = json.loads(ws.recv(timeout=35))
|
|
711
|
+
except Exception:
|
|
712
|
+
raise RuntimeError("Timed out waiting for host approval.")
|
|
713
|
+
if second.get("type") == "denied":
|
|
714
|
+
raise RuntimeError("Host declined the connection.")
|
|
715
|
+
if second.get("type") != "connected":
|
|
716
|
+
raise RuntimeError(second.get("message", "unexpected response from host"))
|
|
717
|
+
# Re-enter raw mode for the PTY session
|
|
718
|
+
try:
|
|
719
|
+
import tty as _tty2, termios as _term2
|
|
720
|
+
_old_settings = _term2.tcgetattr(_stdin_fd)
|
|
721
|
+
_tty2.setraw(_stdin_fd)
|
|
722
|
+
_raw_ok = True
|
|
723
|
+
except Exception:
|
|
724
|
+
pass
|
|
725
|
+
elif ftype != "connected":
|
|
726
|
+
_restore()
|
|
727
|
+
raise RuntimeError(first.get("message", "unexpected server response"))
|
|
643
728
|
|
|
644
729
|
ws.send(json.dumps({
|
|
645
730
|
"type": "pty_run",
|
|
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
|