conductor-remote 1.34.3 → 1.34.4

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.
package/dist/index.html CHANGED
@@ -13,7 +13,7 @@
13
13
  <title>Conductor Remote</title>
14
14
  <!-- Runs before the module bundle so it can catch a stale shell that fails to boot. -->
15
15
  <script src="/self-heal.js"></script>
16
- <script type="module" crossorigin src="/assets/index-Coq6xLXi.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-CUGYKt7R.js"></script>
17
17
  <link rel="stylesheet" crossorigin href="/assets/index--7Dp2aq8.css">
18
18
  <link rel="manifest" href="/manifest.webmanifest"></head>
19
19
  <body>
package/dist/sw.js CHANGED
@@ -1 +1 @@
1
- if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const c=e=>i(e,o),t={module:{uri:o},exports:l,require:c};s[o]=Promise.all(n.map(e=>t[e]||c(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"890f6a70e97d144f4d025254e90892a1"},{url:"index.html",revision:"7f1d085ec2fac185f0af409edd3d81c5"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-Coq6xLXi.js",revision:null},{url:"assets/index--7Dp2aq8.css",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
1
+ if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"890f6a70e97d144f4d025254e90892a1"},{url:"index.html",revision:"aa7c74f617001021bcfa69ef22c8a2b4"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-CUGYKt7R.js",revision:null},{url:"assets/index--7Dp2aq8.css",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
@@ -73,10 +73,17 @@ on windowProbe()
73
73
  -- count is -1 when macOS refused the read at all. Everything that wants to
74
74
  -- know about windows goes through here, because "returned 0" and "refused to
75
75
  -- answer" are different facts and only this handler still has both.
76
+ -- The timeout is load-bearing: a wedged Conductor (seen live after a restart
77
+ -- collided with a relaunch) answers deep links but hangs every AX read, and
78
+ -- without a cap this call blocks until the node-side ceiling kills the whole
79
+ -- run as "took too long" — 28s of silence instead of a named cause. 6s turns
80
+ -- that into error -1712, which refusalReason maps to words.
76
81
  try
77
- tell application "System Events" to tell process "Conductor"
78
- set winCount to (count of windows)
79
- end tell
82
+ with timeout of 6 seconds
83
+ tell application "System Events" to tell process "Conductor"
84
+ set winCount to (count of windows)
85
+ end tell
86
+ end timeout
80
87
  return {winCount, 0, ""}
81
88
  on error errText number errNum
82
89
  return {-1, errNum, errText}
@@ -99,6 +106,7 @@ on refusalReason(probe)
99
106
  set errText to item 3 of probe
100
107
  if errText contains "assistive access" then return "the relay is not trusted for Accessibility - grant it to the node binary running the relay in System Settings > Privacy & Security > Accessibility, then restart the relay (upgrading node silently revokes it). macOS said: " & errText
101
108
  if errNum is -1743 or errText contains "Not authorized" then return "macOS blocked the relay from controlling the UI - grant Automation permission to the node binary running the relay in System Settings > Privacy & Security > Automation. macOS said: " & errText
109
+ if errNum is -1712 then return "Conductor stopped answering UI reads (the Accessibility query timed out) - the app looks wedged even though it may still process deep links. Force-quit Conductor on your Mac and open it again; if it repeats with a healthy Conductor, System Events is stuck - run: killall 'System Events'"
102
110
  return ""
103
111
  end refusalReason
104
112
 
@@ -143,14 +151,50 @@ on menuTitles()
143
151
  end try
144
152
  end menuTitles
145
153
 
154
+ on serverWindowCount()
155
+ -- Second opinion from the window server, because the Accessibility tree only
156
+ -- shows windows on the *current* Space: a full-screen Conductor (which owns its
157
+ -- own Space) or a Mac sitting at the lock screen reads as "no window" from AX
158
+ -- while the window very much exists — and acting on the AX answer alone once
159
+ -- quit a Conductor whose window the user had left open all along.
160
+ -- CGWindowListCopyWindowInfo(kCGWindowListOptionAll) sees every Space; owner
161
+ -- name and layer are readable without the Screen Recording grant (only window
162
+ -- *titles* are gated). Layer 0 keeps it to real windows. -1 = the probe itself
163
+ -- failed, which callers must treat as "no opinion", never as "no window".
164
+ set jxa to "ObjC.import('CoreGraphics'); const w = ObjC.deepUnwrap($.CFBridgingRelease($.CGWindowListCopyWindowInfo(0, 0))) || []; w.filter(x => x.kCGWindowOwnerName === 'Conductor' && x.kCGWindowLayer === 0).length"
165
+ try
166
+ return (do shell script "osascript -l JavaScript -e " & quoted form of jxa) as integer
167
+ on error
168
+ return -1
169
+ end try
170
+ end serverWindowCount
171
+
172
+ on screenLocked()
173
+ -- "locked" / "unlocked" / "unknown". CGSessionCopyCurrentDictionary carries
174
+ -- CGSSessionScreenIsLocked = 1 while the lock screen is up and omits the key
175
+ -- entirely when it is not — same stdlib JXA channel as serverWindowCount.
176
+ -- This matters because the lock screen is the product's home ground: sends
177
+ -- come from a phone precisely when nobody is at the Mac, so "is the lock up"
178
+ -- is the difference between a state a restart can fix and one it provably
179
+ -- makes worse (see the restart gate in activateConductor).
180
+ set jxa to "ObjC.import('CoreGraphics'); const d = ObjC.deepUnwrap($.CFBridgingRelease($.CGSessionCopyCurrentDictionary())) || {}; d.CGSSessionScreenIsLocked ? 'locked' : 'unlocked'"
181
+ try
182
+ return do shell script "osascript -l JavaScript -e " & quoted form of jxa
183
+ on error
184
+ return "unknown"
185
+ end try
186
+ end screenLocked
187
+
146
188
  on windowEvidence()
147
189
  -- Zero windows is a dead end without knowing what the AX tree *does* show, and
148
190
  -- guessing the next thing to press has already cost a round trip (there is no
149
191
  -- "New Window" item — Conductor is single-window). So the error carries the
150
- -- evidence instead: who macOS thinks Conductor is, and what its menus offer.
192
+ -- evidence instead: who macOS thinks Conductor is, what its menus offer, how
193
+ -- many windows the window server counts across all Spaces (AX only sees the
194
+ -- current one), and whether the lock screen is up.
151
195
  set procs to my processWindowCounts()
152
196
  set titles to my menuTitles()
153
- set ev to ""
197
+ set ev to " [window server: " & my serverWindowCount() & "; screen: " & my screenLocked() & "]"
154
198
  if (count of procs) > 0 then set ev to ev & " [processes: " & my joinList(procs, ", ") & "]"
155
199
  if (count of titles) > 0 then set ev to ev & " [menus: " & my joinList(titles, ", ") & "]"
156
200
  return ev
@@ -282,6 +326,22 @@ on activateConductor()
282
326
  -- used to drop it, which made a repeating no-window failure undebuggable
283
327
  -- from the log alone.
284
328
  set ev to my windowEvidence()
329
+ set lockState to my screenLocked()
330
+ -- AX saying "no window" is not the same as there being no window: full
331
+ -- screen on another Space and the lock screen both hide a live window from
332
+ -- AX, and restarting then destroys a window the user left open (measured
333
+ -- live: exactly that loop wedged Conductor for a whole evening). Only the
334
+ -- window server can tell the difference, so a window it CAN see blocks the
335
+ -- restart — this is a human's problem, said in words, not the relay's.
336
+ if my serverWindowCount() > 0 then
337
+ if lockState is "locked" then error "The Mac is locked - Conductor's window is open behind the lock screen, where the relay can't reach it. Unlock the Mac and send again." & ev
338
+ error "Conductor's window exists but is not reachable - it is probably full screen on another Space. Take Conductor out of full screen, then send again." & ev
339
+ end if
340
+ -- No window anywhere and the lock screen up: don't restart. Every relaunch
341
+ -- fired behind the lock screen in the live incident came up windowless, and
342
+ -- the last one came up wedged — answering deep links, hanging every AX
343
+ -- read. A restart here is not a lever, it is how the wreckage happened.
344
+ if lockState is "locked" then error "The Mac is locked and Conductor has no window. Relaunching behind the lock screen is what leaves it wedged, so the relay won't - unlock the Mac and send again." & ev
285
345
  set restartError to my restartConductor()
286
346
  if restartError is not "" then error restartError & ev
287
347
  error "Conductor was running with no window and ignored both reopen and a Dock click, so the relay restarted it. Give it a few seconds and send again." & ev
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-remote",
3
- "version": "1.34.3",
3
+ "version": "1.34.4",
4
4
  "type": "module",
5
5
  "packageManager": "yarn@4.15.0",
6
6
  "description": "Phone control panel for local Conductor agents. Reads ride SQLite + git; prompts ride Conductor's own dispatch path.",