main
py 144 lines 4.71 KB
Raw
1 import getpass
2 import hashlib
3 import os
4 import socket
5
6 from flaredantic import MicrosoftConfig, MicrosoftTunnel, NotifyEvent
7
8 try:
9 from flaredantic.core.exceptions import MicrosoftTunnelError
10 except Exception: # pragma: no cover - keeps tests independent from package internals
11 MicrosoftTunnelError = RuntimeError
12
13 from helpers import files
14 from helpers.tunnel_common import FlaredanticTunnelHelper
15
16
17 MICROSOFT_TUNNEL_ID_ENV_KEYS = (
18 "A0_MICROSOFT_DEV_TUNNEL_ID",
19 "MICROSOFT_DEV_TUNNEL_ID",
20 )
21 MICROSOFT_TUNNEL_TIMEOUT = 120
22
23
24 def default_microsoft_tunnel_id():
25 for env_key in MICROSOFT_TUNNEL_ID_ENV_KEYS:
26 configured = (os.environ.get(env_key) or "").strip()
27 if configured:
28 return configured
29
30 seed = "|".join([
31 getpass.getuser(),
32 socket.gethostname(),
33 files.get_abs_path("usr"),
34 ])
35 digest = hashlib.sha256(seed.encode("utf-8")).hexdigest()[:10]
36 return f"agent-zero-{digest}"
37
38
39 class AgentZeroMicrosoftTunnel(MicrosoftTunnel):
40 def notify(self, event, message, data=None):
41 try:
42 return super().notify(event, message, data)
43 except AttributeError:
44 self.agent_zero_notifications.append({
45 "event": event.value if hasattr(event, "value") else event,
46 "message": message,
47 "data": data,
48 })
49 return None
50
51 @property
52 def agent_zero_notifications(self):
53 if not hasattr(self, "_agent_zero_notifications"):
54 self._agent_zero_notifications = []
55 return self._agent_zero_notifications
56
57 def _notify_progress(self, message, data=None):
58 self.notify(NotifyEvent.INFO, message, data)
59
60 def _ensure_logged_in(self):
61 parent = getattr(super(), "_ensure_logged_in", None)
62 if callable(parent):
63 parent()
64 self._notify_progress(
65 "Microsoft Dev Tunnels login confirmed. Preparing your tunnel..."
66 )
67
68 def _ensure_tunnel(self):
69 tunnel_id = self.config.tunnel_id
70 port = str(self.config.port)
71
72 self._notify_progress(
73 f"Checking Microsoft Dev Tunnel `{tunnel_id}`...",
74 {"tunnel_id": tunnel_id},
75 )
76 show = self._run_cmd(["show", tunnel_id])
77 if show.returncode != 0:
78 self._notify_progress(
79 f"Creating Microsoft Dev Tunnel `{tunnel_id}`...",
80 {"tunnel_id": tunnel_id},
81 )
82 create = self._run_cmd(["create", tunnel_id])
83 if create.returncode != 0:
84 raise MicrosoftTunnelError(f"Failed to create tunnel: {create.stdout}")
85 else:
86 self._notify_progress(
87 f"Microsoft Dev Tunnel `{tunnel_id}` already exists. Checking port {port}...",
88 {"tunnel_id": tunnel_id, "port": port},
89 )
90
91 self._notify_progress(
92 f"Checking Microsoft Dev Tunnel port {port}...",
93 {"tunnel_id": tunnel_id, "port": port},
94 )
95 port_show = self._run_cmd(["port", "show", tunnel_id, "-p", port])
96 if port_show.returncode != 0:
97 self._notify_progress(
98 f"Creating Microsoft Dev Tunnel port {port}...",
99 {"tunnel_id": tunnel_id, "port": port},
100 )
101 port_create = self._run_cmd([
102 "port",
103 "create",
104 tunnel_id,
105 "-p",
106 port,
107 "--protocol",
108 "http",
109 ])
110 if port_create.returncode != 0:
111 raise MicrosoftTunnelError(
112 f"Failed to create port: {port_create.stdout}"
113 )
114
115 self._notify_progress(
116 "Microsoft Dev Tunnel setup is ready. Starting the secure host..."
117 )
118
119
120 class MicrosoftDevTunnel(FlaredanticTunnelHelper):
121 label = "Microsoft Dev Tunnels"
122
123 def build_tunnel(self):
124 config = MicrosoftConfig(
125 port=self.port,
126 verbose=True,
127 timeout=MICROSOFT_TUNNEL_TIMEOUT,
128 tunnel_id=default_microsoft_tunnel_id(),
129 )
130 return AgentZeroMicrosoftTunnel(config)
131
132 def start(self):
133 try:
134 return super().start()
135 except Exception as e:
136 if "Timeout waiting for Microsoft Dev Tunnels URL" not in str(e):
137 raise
138 tunnel_id = default_microsoft_tunnel_id()
139 raise RuntimeError(
140 "Microsoft Dev Tunnels did not return a URL. Agent Zero uses "
141 f"the tunnel id `{tunnel_id}` to avoid flaredantic's global "
142 "`flaredantic` tunnel-id collision. If this still fails, set "
143 "`A0_MICROSOFT_DEV_TUNNEL_ID` to a fresh unique value and try again."
144 ) from e