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-
|
|
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:"
|
|
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
|
|
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
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
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 :
|
|
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();
|
package/dist-node/src/writes.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.",
|