Jarvis2345 commited on
Commit
122042d
Β·
verified Β·
1 Parent(s): 2e7cef7

deploy(S4): Blender headless pipeline + WebAR client + backend fixes

Browse files
backend/routes/mobile_bridge_routes.py CHANGED
@@ -162,6 +162,57 @@ async def caps() -> dict[str, Any]:
162
  return {"ok": True, "capabilities": labels}
163
 
164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  @router.get("/link_info")
166
  async def link_info(request: Request) -> dict[str, Any]:
167
  base = _base_url(request)
 
162
  return {"ok": True, "capabilities": labels}
163
 
164
 
165
+ @router.get("/link_status")
166
+ async def link_status() -> dict[str, Any]:
167
+ """The truth about the phone -> cloud -> PC path, in one call.
168
+
169
+ Guardian used to build its status cards from two unrelated signals: a /health
170
+ ping (proves the Space is up) and a stored boolean. Neither said anything
171
+ about whether a command could actually REACH the desktop, so the app happily
172
+ rendered "PC CONTROL: CLOUD online" next to "NO ACTIVE LINK" while the phone
173
+ could not send a single command. A status light that does not test the path
174
+ it claims to describe is worse than no light at all.
175
+
176
+ Reaching this endpoint at all proves the cloud leg. `pc_connected` is the
177
+ number of desktops holding a live, registered WebSocket β€” the exact list
178
+ send_to_pcs() delivers to β€” so it is the same fact the command path uses,
179
+ not a parallel guess. `pc_unlocked` comes from the PC's own periodic report.
180
+ """
181
+ pc_count = 0
182
+ status: dict[str, Any] = {}
183
+ age: float | None = None
184
+ queued = 0
185
+ try:
186
+ from backend.ws.agent_ws import ws_manager as _ws
187
+ pc_count = len(_ws.pc_connections)
188
+ status = dict(_ws.last_pc_status or {})
189
+ queued = len(_ws.pending_commands)
190
+ if _ws.last_pc_status_at:
191
+ age = round(time.time() - _ws.last_pc_status_at, 1)
192
+ except Exception as exc:
193
+ log.warning("link_status unavailable: %s", exc)
194
+
195
+ unlocked = bool(status.get("unlocked"))
196
+ if pc_count <= 0:
197
+ state, detail = "offline", "Your PC is not connected."
198
+ elif not unlocked:
199
+ state, detail = "locked", "Connected, but locked. Send: unlock pc <password>"
200
+ else:
201
+ state, detail = "ready", "Connected and ready for commands."
202
+
203
+ return {
204
+ "ok": True,
205
+ "cloud": True, # answering at all proves this leg
206
+ "pc_connected": pc_count,
207
+ "pc_unlocked": unlocked,
208
+ "state": state, # offline | locked | ready
209
+ "detail": detail,
210
+ "queued_commands": queued,
211
+ "pc_status_age_seconds": age,
212
+ "pc_status": status,
213
+ }
214
+
215
+
216
  @router.get("/link_info")
217
  async def link_info(request: Request) -> dict[str, Any]:
218
  base = _base_url(request)
backend/ws/agent_ws.py CHANGED
@@ -69,6 +69,14 @@ class ConnectionManager:
69
  # simply dropped β€” the user had to remember it and send it again once the
70
  # PC woke up.
71
  self.pending_commands: list[dict] = []
 
 
 
 
 
 
 
 
72
  from backend.agent.react_agent import Tool
73
  self.tools = []
74
  for name, func in TOOL_REGISTRY.items():
@@ -280,6 +288,18 @@ class ConnectionManager:
280
  # by the command_id the cloud minted. This closes the loop that used to
281
  # be open-ended: without it the phone was told "Sent to your PC." and
282
  # never learned that the desktop had refused the command.
 
 
 
 
 
 
 
 
 
 
 
 
283
  if data.get("event") == "relay:result":
284
  payload = data.get("payload") or {}
285
  command_id = str(payload.get("command_id") or "")
 
69
  # simply dropped β€” the user had to remember it and send it again once the
70
  # PC woke up.
71
  self.pending_commands: list[dict] = []
72
+ # Last status the PC relay pushed, and when. The relay has always sent
73
+ # `relay:status` every 10 seconds and NOTHING read it β€” there was no
74
+ # branch for it in handle_client_event, so it fell through and was
75
+ # dropped. That is why the phone had no honest way to know anything about
76
+ # the desktop and fell back to showing cloud health in a card labelled
77
+ # "PC CONTROL".
78
+ self.last_pc_status: dict = {}
79
+ self.last_pc_status_at: float = 0.0
80
  from backend.agent.react_agent import Tool
81
  self.tools = []
82
  for name, func in TOOL_REGISTRY.items():
 
288
  # by the command_id the cloud minted. This closes the loop that used to
289
  # be open-ended: without it the phone was told "Sent to your PC." and
290
  # never learned that the desktop had refused the command.
291
+ # ── THE PC'S OWN REPORT ─────────────────────────────────────────────────
292
+ # Received every 10s from pc_relay_client.status_pusher and, until now,
293
+ # silently discarded. Storing it is what lets /api/link_status tell the
294
+ # phone something TRUE about the desktop instead of guessing from cloud
295
+ # health.
296
+ if data.get("event") == "relay:status":
297
+ payload = data.get("payload")
298
+ if isinstance(payload, dict):
299
+ self.last_pc_status = payload
300
+ self.last_pc_status_at = time.time()
301
+ return
302
+
303
  if data.get("event") == "relay:result":
304
  payload = data.get("payload") or {}
305
  command_id = str(payload.get("command_id") or "")
dist/app-release.apk CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:9f6ff4329c1d9ab621312b87542baa2a90f5020db9131cb1601b5058ab50853d
3
  size 116538395
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:aea6e0ae1cd4ee73eae1ba552343ab583401abf760f44e5966a156af9e38d4ff
3
  size 116538395
modules/activation_state.py CHANGED
@@ -12,9 +12,13 @@ UNLOCK_JSON = ROOT / "data" / "session_unlocked.json"
12
  # One wording for the refusal, shared by every caller that has to explain it β€”
13
  # core.commands (voice/GUI) and phone.command_runner (phone and cloud relay) β€”
14
  # so a remote user is never told something different from a local one.
 
 
 
 
15
  LOCKED_MESSAGE = (
16
- "This PC is locked. Activate JARVIS on the desktop with your "
17
- "activation key, then send the command again."
18
  )
19
 
20
 
 
12
  # One wording for the refusal, shared by every caller that has to explain it β€”
13
  # core.commands (voice/GUI) and phone.command_runner (phone and cloud relay) β€”
14
  # so a remote user is never told something different from a local one.
15
+ # Tells the user what to do from WHERE THEY ARE. The old wording sent them to
16
+ # the desktop for an activation key, which is useless advice to someone holding
17
+ # a phone in another country β€” and that is the only situation in which a relayed
18
+ # command hits this message at all.
19
  LOCKED_MESSAGE = (
20
+ "This PC is locked. Send: unlock pc <your password> β€” then this command "
21
+ "will run."
22
  )
23
 
24