conductor-remote 1.64.0 → 1.65.1

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,8 +13,8 @@
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-C8s68scA.js"></script>
17
- <link rel="stylesheet" crossorigin href="/assets/index-BhxPTIyU.css">
16
+ <script type="module" crossorigin src="/assets/index-fu-yJ_gW.js"></script>
17
+ <link rel="stylesheet" crossorigin href="/assets/index-87r9u-0C.css">
18
18
  <link rel="manifest" href="/manifest.webmanifest"></head>
19
19
  <body>
20
20
  <div id="root"></div>
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:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"0cc53b4390ac109ed647b9f747869331"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-C8s68scA.js",revision:null},{url:"assets/index-BhxPTIyU.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:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"96dea2763a1c11d8262211107568a7e1"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-fu-yJ_gW.js",revision:null},{url:"assets/index-87r9u-0C.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\//]}))});
@@ -714,7 +714,39 @@ on normalizeNewlines(s)
714
714
  return joined
715
715
  end normalizeNewlines
716
716
 
717
- on fillComposer(promptText)
717
+ on composerField()
718
+ -- The composer's AXTextArea: an AXGroup named "composer" in the chat pane, one
719
+ -- text area inside it. Resolved once and handed to both the fill and the send
720
+ -- confirm below, because reaching it means walking the pane from the tab strip
721
+ -- and that walk measured ~0.5s on a live window. Returns missing value when the
722
+ -- composer isn't there at all, which is the caller's cue to fall back to pasting.
723
+ set strips to my tabGroups()
724
+ if (count of strips) is 0 then return missing value
725
+ try
726
+ tell application "System Events" to tell process "Conductor"
727
+ set pane to value of attribute "AXParent" of (item 1 of strips)
728
+ set composerBox to item 1 of (UI elements of pane whose name is "composer")
729
+ return item 1 of (UI elements of composerBox whose role is "AXTextArea")
730
+ end tell
731
+ end try
732
+ return missing value
733
+ end composerField
734
+
735
+ on composerTextOf(textBox)
736
+ -- What the box holds now, or missing value when it cannot be read at all. A pane
737
+ -- that re-rendered leaves the old reference pointing at a dead element, and
738
+ -- "can't read it" must never be reported as "still holding the prompt": that is
739
+ -- the one state that presses Enter again, and it would do so at the moment the
740
+ -- box may already be empty.
741
+ try
742
+ tell application "System Events" to tell process "Conductor"
743
+ return (value of textBox) as text
744
+ end tell
745
+ end try
746
+ return missing value
747
+ end composerTextOf
748
+
749
+ on fillComposer(textBox, promptText)
718
750
  -- Write the prompt straight into the composer's AXTextArea instead of
719
751
  -- stashing the clipboard, pressing Cmd+L and pasting. AXFocused and AXValue
720
752
  -- are both settable, so this needs no keystrokes and no clipboard hijack.
@@ -722,13 +754,9 @@ on fillComposer(promptText)
722
754
  -- *clears whatever it wrote first*: leaving half a prompt behind would make
723
755
  -- the fallback paste append to it and send a garbled prompt.
724
756
  if promptText is "" then return false
725
- set strips to my tabGroups()
726
- if (count of strips) is 0 then return false
757
+ if textBox is missing value then return false
727
758
  try
728
759
  tell application "System Events" to tell process "Conductor"
729
- set pane to value of attribute "AXParent" of (item 1 of strips)
730
- set composerBox to item 1 of (UI elements of pane whose name is "composer")
731
- set textBox to item 1 of (UI elements of composerBox whose role is "AXTextArea")
732
760
  set value of attribute "AXFocused" of textBox to true
733
761
  set value of textBox to promptText
734
762
  delay 0.25
@@ -739,23 +767,75 @@ on fillComposer(promptText)
739
767
  end tell
740
768
  on error
741
769
  try
742
- my clearComposer()
770
+ my clearComposer(textBox)
743
771
  end try
744
772
  return false
745
773
  end try
746
774
  return true
747
775
  end fillComposer
748
776
 
749
- on clearComposer()
750
- set strips to my tabGroups()
751
- if (count of strips) is 0 then return
777
+ on clearComposer(textBox)
778
+ if textBox is missing value then return
752
779
  tell application "System Events" to tell process "Conductor"
753
- set pane to value of attribute "AXParent" of (item 1 of strips)
754
- set composerBox to item 1 of (UI elements of pane whose name is "composer")
755
- set value of (item 1 of (UI elements of composerBox whose role is "AXTextArea")) to ""
780
+ set value of textBox to ""
756
781
  end tell
757
782
  end clearComposer
758
783
 
784
+ on pressSend(queueIt)
785
+ -- Return sends, Command-Return queues the prompt behind the running answer.
786
+ -- The choice is made out here because inside a System Events tell an ordinary
787
+ -- parameter name can resolve as one of its own terms.
788
+ if queueIt then
789
+ tell application "System Events" to key code 36 using {command down}
790
+ else
791
+ tell application "System Events" to key code 36
792
+ end if
793
+ end pressSend
794
+
795
+ on submitComposer(textBox, promptText, queueIt)
796
+ -- Press Enter, then read the composer back. Conductor consumes the draft when it
797
+ -- accepts a prompt, so a box still holding the text is a keystroke that did
798
+ -- nothing — and that read is the only receipt a run can collect about its own
799
+ -- send.
800
+ --
801
+ -- Why it earns its place: the caller's other receipt is the transcript row, which
802
+ -- costs a 6s watch (CONFIRM_WINDOW_MS in server.ts) and then a whole second run
803
+ -- through activate, focus, tab and fill. Measured from this Mac's own relay logs,
804
+ -- a send whose single fault was a first Enter Conductor ignored took ~10s from the
805
+ -- prompt appearing in the composer to it being sent, with the text sitting on
806
+ -- screen the whole way. Exactly one user row was written, so the keystroke was
807
+ -- lost rather than delayed.
808
+ --
809
+ -- Pressing again is safe *because* of the read, not in spite of it: the prompt is
810
+ -- still in the box, so nothing was consumed, so there is nothing to duplicate.
811
+ -- A Conductor too busy to have handled the first Enter is also too busy to answer
812
+ -- an AX read, which blocks here rather than handing back stale text. Bounded at
813
+ -- three presses — a box that survives those is stuck on something this script
814
+ -- cannot see, and the caller's own retry, which re-focuses and re-fills, is the
815
+ -- better lever than a fourth keystroke.
816
+ --
817
+ -- Returns the press that cleared the box, or 0 for a box that never cleared. The
818
+ -- count is the point: a rescued send is otherwise indistinguishable from one that
819
+ -- worked first time, so the failure this handler exists for would stop appearing
820
+ -- in the log the day it stopped costing 10s, and nobody could tell whether it was
821
+ -- fixed or merely hidden.
822
+ if promptText is "" then
823
+ my pressSend(queueIt)
824
+ return 1
825
+ end if
826
+ repeat with pressCount from 1 to 3
827
+ my pressSend(queueIt)
828
+ if textBox is missing value then return pressCount
829
+ repeat with tick from 1 to 6
830
+ delay 0.2
831
+ set seen to my composerTextOf(textBox)
832
+ if seen is missing value then return pressCount
833
+ if seen does not contain promptText then return pressCount
834
+ end repeat
835
+ end repeat
836
+ return 0
837
+ end submitComposer
838
+
759
839
  on pasteComposer()
760
840
  -- Fallback for when the composer isn't reachable: Cmd+L focuses it (after the
761
841
  -- palette, focus sits on a button, not the text box), then paste.
@@ -807,6 +887,106 @@ on selectChatTab()
807
887
  end tell
808
888
  end selectChatTab
809
889
 
890
+ -- The workspace toolbar's run process. This is deliberately separate from the
891
+ -- chat-strip targeting above: the terminal strip is the sibling AXTabGroup whose
892
+ -- direct radio buttons include Setup / Run / Terminal, and its task button is
893
+ -- named "Run <task>" or "Stop <task>". Conductor may add more terminal tabs, so
894
+ -- identify the strip by the Run radio rather than by its position.
895
+ on runStrip()
896
+ set matches to {}
897
+ repeat with entry in my tabGroups()
898
+ set tg to contents of entry
899
+ set hasRunTab to false
900
+ repeat with r in my stripRadios(tg)
901
+ if (my axName(contents of r)) is "Run" then set hasRunTab to true
902
+ end repeat
903
+ if hasRunTab then set end of matches to tg
904
+ end repeat
905
+ if (count of matches) is 0 then error "couldn't find Conductor's Run panel"
906
+ if (count of matches) > 1 then error "can't tell which panel holds Conductor's Run task"
907
+ return item 1 of matches
908
+ end runStrip
909
+
910
+ on runTaskButton(tg)
911
+ set matches to {}
912
+ tell application "System Events" to tell process "Conductor"
913
+ repeat with entry in (UI elements of tg whose role is "AXButton")
914
+ set node to contents of entry
915
+ set label to my axName(node)
916
+ if label starts with "Run " or label starts with "Stop " then set end of matches to node
917
+ end repeat
918
+ end tell
919
+ if (count of matches) is 0 then error "this workspace has no selected Run task"
920
+ if (count of matches) > 1 then error "Conductor exposed more than one selected Run task"
921
+ return item 1 of matches
922
+ end runTaskButton
923
+
924
+ on runTaskState(btn)
925
+ set label to my axName(btn)
926
+ if label starts with "Stop " then return "running"
927
+ if label starts with "Run " then return "stopped"
928
+ error "couldn't read Conductor's Run task state"
929
+ end runTaskState
930
+
931
+ on runTaskName(btn)
932
+ set label to my axName(btn)
933
+ if label starts with "Stop " then return text 6 thru -1 of label
934
+ if label starts with "Run " then return text 5 thru -1 of label
935
+ return label
936
+ end runTaskName
937
+
938
+ on openRunPorts(tg)
939
+ set found to {}
940
+ tell application "System Events" to tell process "Conductor"
941
+ repeat with entry in (UI elements of tg whose role is "AXButton")
942
+ set label to my axName(contents of entry)
943
+ if label starts with "Open :" then set end of found to text 7 thru -1 of label
944
+ end repeat
945
+ end tell
946
+ return found
947
+ end openRunPorts
948
+
949
+ on setRunTask(wantRunning)
950
+ my activateConductor()
951
+ my focusWorkspace()
952
+ set tg to my runStrip()
953
+ my assertWorkspace(tg)
954
+ set btn to my runTaskButton(tg)
955
+ set beforeState to my runTaskState(btn)
956
+ set taskName to my runTaskName(btn)
957
+ set wantedState to "stopped"
958
+ if wantRunning then set wantedState to "running"
959
+ set changed to "false"
960
+
961
+ if beforeState is not wantedState then
962
+ tell application "System Events" to tell process "Conductor"
963
+ perform action "AXPress" of btn
964
+ end tell
965
+ set changed to "true"
966
+ end if
967
+
968
+ -- The panel re-renders after the press, invalidating every old AX handle.
969
+ -- Re-find it on each poll and require the opposite label before reporting the
970
+ -- action as complete. A started web server gets a short second window in which
971
+ -- Conductor can surface its "Open :<port>" button; non-server tasks simply
972
+ -- return with an empty port list.
973
+ set currentState to beforeState
974
+ set ports to {}
975
+ repeat with attempt from 1 to 44
976
+ set tg to my runStrip()
977
+ set btn to my runTaskButton(tg)
978
+ set currentState to my runTaskState(btn)
979
+ set taskName to my runTaskName(btn)
980
+ if currentState is wantedState then
981
+ set ports to my openRunPorts(tg)
982
+ if not wantRunning or (count of ports) > 0 or attempt > 20 then exit repeat
983
+ end if
984
+ delay 0.25
985
+ end repeat
986
+ if currentState is not wantedState then error "Conductor's Run task did not switch to " & wantedState
987
+ return currentState & tab & taskName & tab & changed & tab & my joinList(ports, ",")
988
+ end setRunTask
989
+
810
990
  on cancelAgent()
811
991
  -- Stop the answer this chat is streaming: Conductor's own "Cancel agent"
812
992
  -- command, Command-Shift-Delete (key code 51). Taken from its Keyboard