conductor-remote 1.27.0 → 1.27.2

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-5K1FTwMp.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-BK1VlcLX.js"></script>
17
17
  <link rel="stylesheet" crossorigin href="/assets/index-DnE5ulTu.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,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(i[o])return;let l={};const c=e=>n(e,o),t={module:{uri:o},exports:l,require:c};i[o]=Promise.all(s.map(e=>t[e]||c(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"index.html",revision:"6322fd47f12740563120e0cc3b544d1a"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-DnE5ulTu.css",revision:null},{url:"assets/index-5K1FTwMp.js",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,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(i[o])return;let l={};const c=e=>n(e,o),t={module:{uri:o},exports:l,require:c};i[o]=Promise.all(s.map(e=>t[e]||c(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"index.html",revision:"4a324603f0ee46fc5bafbe0e20a1b712"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-DnE5ulTu.css",revision:null},{url:"assets/index-BK1VlcLX.js",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\//]}))});
@@ -8,7 +8,7 @@
8
8
  * KeepAlive restarts it into the freshly-installed code (the plist's baked `bin/cli.js` path is stable
9
9
  * across a global reinstall, so no re-`service install` is needed).
10
10
  *
11
- * Beyond that hourly registry poll, a cheap local check reconciles a disk↔process version skew: if the
11
+ * Beyond that periodic registry poll, a cheap local check reconciles a disk↔process version skew: if the
12
12
  * installed package.json on disk no longer matches the version this process booted as — an out-of-band
13
13
  * `npm i -g`/deploy swapped the global install underneath us — it exit()→restarts to load it. Without
14
14
  * this, a stale backend keeps serving the newer frontend from disk (the API contract drifts, e.g. the
@@ -35,16 +35,16 @@ const execFileP = promisify(execFile);
35
35
  const NAME = 'conductor-remote';
36
36
  const projectDir = packageRoot(import.meta.dirname);
37
37
  const REGISTRY = process.env.NPM_REGISTRY ?? 'https://registry.npmjs.org';
38
- // Registry poll cadence. The poll is a single cheap GET off npm's CDN, so a tight loop costs nothing
39
- // but releases are now infrequent and rarely carry a critical fix, so racing `latest` buys little. A
40
- // coarse hourly poll lands the phone on `latest` within the hour, and an out-of-band `yarn deploy` still
41
- // heals in ~a minute via the disk-skew check below. (The first poll fires ~90s after boot, then on this
42
- // interval, so a restart re-syncs fast regardless of cadence.) `AUTO_UPDATE_INTERVAL_MINUTES` retunes it
43
- // with no code change drop it to e.g. 2 while shipping a burst of releases; values are floored at 1 min
44
- // (below that you're just racing the 60s disk-skew heal and the CDN's tag TTL).
38
+ // Registry poll cadence. The poll is a single cheap GET off npm's CDN, so a tight loop costs nothing, and
39
+ // a fresh release should reach the phone while you still care about it — 5 minutes lands `latest` within
40
+ // one coffee sip, and an out-of-band `yarn deploy` still heals in ~a minute via the disk-skew check below.
41
+ // (The first poll fires ~90s after boot, then on this interval, so a restart re-syncs fast regardless of
42
+ // cadence.) `AUTO_UPDATE_INTERVAL_MINUTES` retunes it with no code change — raise it to e.g. 60 when
43
+ // releases go quiet; values are floored at 1 min (below that you're just racing the 60s disk-skew heal and
44
+ // the CDN's tag TTL).
45
45
  function resolveIntervalMs() {
46
46
  const raw = Number(process.env.AUTO_UPDATE_INTERVAL_MINUTES);
47
- const minutes = Number.isFinite(raw) && raw > 0 ? raw : 60;
47
+ const minutes = Number.isFinite(raw) && raw > 0 ? raw : 5;
48
48
  return Math.max(minutes, 1) * 60 * 1000;
49
49
  }
50
50
  const CHECK_INTERVAL_MS = resolveIntervalMs();
@@ -67,11 +67,68 @@ on splitLines(s)
67
67
  return parts
68
68
  end splitLines
69
69
 
70
+ on hasWindow()
71
+ -- Also false when the process itself is gone: "tell process" errors there.
72
+ try
73
+ tell application "System Events" to tell process "Conductor"
74
+ return (count of windows) > 0
75
+ end tell
76
+ on error
77
+ return false
78
+ end try
79
+ end hasWindow
80
+
81
+ on requireWindow()
82
+ -- ASCII on purpose: this one reaches the phone, and osascript decodes -e by the
83
+ -- caller's locale, which a LaunchAgent doesn't set.
84
+ if not (my hasWindow()) then error "Conductor has no open window - open it on your Mac and try again"
85
+ end requireWindow
86
+
87
+ on activateConductor()
88
+ -- Conductor keeps running with every window closed (standard macOS: the red
89
+ -- button doesn't quit), and a cold launch needs a moment to build one. Both
90
+ -- look identical from here, and both used to surface as "Can't get window 1 of
91
+ -- process Conductor. Invalid index." from whichever handler happened to run
92
+ -- first. So wait for a real window, nudge twice, then say it in words the phone
93
+ -- can act on. "reopen" is the dock-click event — that is what recreates a
94
+ -- closed window; "activate" on its own does not.
95
+ try
96
+ tell application "Conductor" to activate
97
+ on error errText
98
+ error "couldn't bring Conductor to the front: " & errText
99
+ end try
100
+ delay 0.4
101
+ -- ~4s of patience: enough for a reopen to draw, short enough that the caller's
102
+ -- osascript timeout still has room for the send itself. A cold launch slower
103
+ -- than this fails with the message below, and the retry finds a warm app.
104
+ repeat with attempt from 1 to 16
105
+ if my hasWindow() then return
106
+ if attempt is 2 or attempt is 8 then
107
+ try
108
+ tell application "Conductor" to reopen
109
+ tell application "Conductor" to activate
110
+ end try
111
+ end if
112
+ delay 0.25
113
+ end repeat
114
+ my requireWindow()
115
+ end activateConductor
116
+
117
+ on webArea()
118
+ -- The webview root: the window, then four nested containers. Guarded, or a
119
+ -- window that closed mid-run reappears as an "Invalid index" from a caller
120
+ -- that has nothing to do with windows.
121
+ my requireWindow()
122
+ tell application "System Events" to tell process "Conductor"
123
+ return UI element 1 of UI element 1 of UI element 1 of UI element 1 of window 1
124
+ end tell
125
+ end webArea
126
+
70
127
  on sidebarLinks()
71
128
  -- The sidebar rows are AXLinks named "<repo> <title> +adds -dels". They live
72
129
  -- two levels under the web area; collect them wherever they are at that depth.
130
+ set wa to my webArea()
73
131
  tell application "System Events" to tell process "Conductor"
74
- set wa to UI element 1 of UI element 1 of UI element 1 of UI element 1 of window 1
75
132
  set out to {}
76
133
  repeat with a in (UI elements of wa)
77
134
  try
@@ -156,6 +213,7 @@ on tabGroups()
156
213
  -- Level-order search, returning every tab group at the shallowest depth that
157
214
  -- has one (the chat strip and the terminal strip are siblings). Bounded: the
158
215
  -- pane sits ~5 levels down, and we must never descend into the transcript.
216
+ my requireWindow()
159
217
  tell application "System Events" to tell process "Conductor"
160
218
  set level to {window 1}
161
219
  set depth to 0
@@ -483,8 +541,8 @@ on setModel(wanted)
483
541
  -- otherwise also match "Sonnet 4.6 1M"), which must fail rather than guess.
484
542
  set chosen to missing value
485
543
  set loose to {}
544
+ set wa to my webArea()
486
545
  tell application "System Events" to tell process "Conductor"
487
- set wa to UI element 1 of UI element 1 of UI element 1 of UI element 1 of window 1
488
546
  repeat with m in (UI elements of wa whose role is "AXMenu")
489
547
  repeat with mi in (UI elements of m whose role is "AXMenuItem")
490
548
  set label to my firstLine(my tabLabel(mi))
@@ -529,8 +587,8 @@ on listModels()
529
587
  end tell
530
588
  delay 1.0
531
589
  set labels to {}
590
+ set wa to my webArea()
532
591
  tell application "System Events" to tell process "Conductor"
533
- set wa to UI element 1 of UI element 1 of UI element 1 of UI element 1 of window 1
534
592
  repeat with m in (UI elements of wa whose role is "AXMenu")
535
593
  repeat with mi in (UI elements of m whose role is "AXMenuItem")
536
594
  set end of labels to my firstLine(my tabLabel(mi))
@@ -592,6 +650,11 @@ function targetEnv(target) {
592
650
  /** osascript echoes the whole failing script back; keep just the reason for the phone. */
593
651
  function osaError(err) {
594
652
  const raw = err instanceof Error ? err.message : String(err);
653
+ // A timeout kill carries no execution error at all — its first line is
654
+ // "Command failed: osascript -e" plus the whole script, which is useless here.
655
+ if (err && typeof err === 'object' && 'killed' in err && err.killed) {
656
+ return 'Conductor took too long to respond';
657
+ }
595
658
  return raw.match(/execution error: (.+?) \(-?\d+\)/)?.[1] ?? raw.split('\n')[0];
596
659
  }
597
660
  /**
@@ -617,8 +680,7 @@ export class AppleScriptActuator {
617
680
  const script = `
618
681
  ${SELECT_CHAT_TAB_HANDLERS}
619
682
 
620
- tell application "Conductor" to activate
621
- delay 0.4
683
+ my activateConductor()
622
684
  my focusWorkspace()
623
685
  my selectChatTab()
624
686
  set promptText to my normalizeNewlines(do shell script "cat" & " " & quoted form of (system attribute "RELAY_PROMPT_FILE"))
@@ -680,8 +742,7 @@ export async function setAgentOptions(target, opts) {
680
742
  const script = `
681
743
  ${SELECT_CHAT_TAB_HANDLERS}
682
744
 
683
- tell application "Conductor" to activate
684
- delay 0.4
745
+ my activateConductor()
685
746
  my focusWorkspace()
686
747
  my selectChatTab()
687
748
  my applyAgentOptions()
@@ -709,8 +770,7 @@ export async function listAgentModels(target) {
709
770
  const script = `
710
771
  ${SELECT_CHAT_TAB_HANDLERS}
711
772
 
712
- tell application "Conductor" to activate
713
- delay 0.4
773
+ my activateConductor()
714
774
  my focusWorkspace()
715
775
  my selectChatTab()
716
776
  return my listModels()`.trim();
@@ -781,8 +841,7 @@ export async function newChat(workspace) {
781
841
  const script = `
782
842
  ${SELECT_CHAT_TAB_HANDLERS}
783
843
 
784
- tell application "Conductor" to activate
785
- delay 0.4
844
+ my activateConductor()
786
845
  my focusWorkspace()
787
846
  tell application "System Events"
788
847
  keystroke "t" using {command down}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-remote",
3
- "version": "1.27.0",
3
+ "version": "1.27.2",
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.",