conductor-remote 1.91.0 → 1.92.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/README.md +3 -1
- package/dist/assets/{index-yWUBiiEb.css → index-DFclyO59.css} +1 -1
- package/dist/assets/{index-BmTEaKny.js → index-DUZ2ZaW9.js} +30 -30
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist-node/src/conductor.applescript +72 -0
- package/dist-node/src/routes.js +2 -0
- package/dist-node/src/server.js +51 -1
- package/dist-node/src/writes.js +37 -0
- package/package.json +1 -1
package/dist/index.html
CHANGED
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
<title>Conductor Remote</title>
|
|
26
26
|
<!-- Runs before the module bundle so it can catch a stale shell that fails to boot. -->
|
|
27
27
|
<script src="/self-heal.js"></script>
|
|
28
|
-
<script type="module" crossorigin src="/assets/index-
|
|
29
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/assets/index-DUZ2ZaW9.js"></script>
|
|
29
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DFclyO59.css">
|
|
30
30
|
<link rel="manifest" href="/manifest.webmanifest"></head>
|
|
31
31
|
<body>
|
|
32
32
|
<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
|
|
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:"e7ef44deca46c0539e6ff7bba5eb815e"},{url:"index.html",revision:"c37a7b1ba1860e09f2d4ea15c7b8423f"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-DUZ2ZaW9.js",revision:null},{url:"assets/index-DFclyO59.css",revision:null},{url:"apple-touch-icon.png",revision:"1127bb396b4648add53dce3f22c92aee"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a9b0d962686287452216492cd2247499"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
|
|
@@ -1336,6 +1336,78 @@ on buttonsUnder(root, maxDepth)
|
|
|
1336
1336
|
return acc
|
|
1337
1337
|
end buttonsUnder
|
|
1338
1338
|
|
|
1339
|
+
on shallowButtonsNamed(root, maxDepth, wanted)
|
|
1340
|
+
-- Exact-name buttons at the first depth that contains one. Continue lives in
|
|
1341
|
+
-- Conductor's workspace action bar, while a chat transcript hangs much deeper
|
|
1342
|
+
-- off the same web area. Stopping at the shallowest hit both avoids walking a
|
|
1343
|
+
-- long transcript and keeps a similarly named control inside a message from
|
|
1344
|
+
-- impersonating the workspace action.
|
|
1345
|
+
set level to {root}
|
|
1346
|
+
set depth to 0
|
|
1347
|
+
tell application "System Events" to tell process "Conductor"
|
|
1348
|
+
repeat while (count of level) > 0 and depth < maxDepth
|
|
1349
|
+
set found to {}
|
|
1350
|
+
set nextLevel to {}
|
|
1351
|
+
repeat with entry in level
|
|
1352
|
+
set node to contents of entry
|
|
1353
|
+
try
|
|
1354
|
+
repeat with candidate in (get UI elements of node whose role is "AXButton")
|
|
1355
|
+
set controlEl to contents of candidate
|
|
1356
|
+
if (my axName(controlEl)) is wanted then set end of found to controlEl
|
|
1357
|
+
end repeat
|
|
1358
|
+
set nextLevel to nextLevel & (get UI elements of node)
|
|
1359
|
+
end try
|
|
1360
|
+
end repeat
|
|
1361
|
+
if (count of found) > 0 then return found
|
|
1362
|
+
set level to nextLevel
|
|
1363
|
+
set depth to depth + 1
|
|
1364
|
+
end repeat
|
|
1365
|
+
end tell
|
|
1366
|
+
return {}
|
|
1367
|
+
end shallowButtonsNamed
|
|
1368
|
+
|
|
1369
|
+
on hasSiblingButtonNamed(controlEl, wanted)
|
|
1370
|
+
-- The live merged action is the pair Continue + Archive. Requiring that pair
|
|
1371
|
+
-- prevents a generic Continue in a modal from impersonating it while the
|
|
1372
|
+
-- asserted workspace pane remains visible behind the modal.
|
|
1373
|
+
try
|
|
1374
|
+
tell application "System Events" to tell process "Conductor"
|
|
1375
|
+
set actionRow to value of attribute "AXParent" of controlEl
|
|
1376
|
+
repeat with peer in (get UI elements of actionRow whose role is "AXButton")
|
|
1377
|
+
if (my axName(contents of peer)) is wanted then return true
|
|
1378
|
+
end repeat
|
|
1379
|
+
end tell
|
|
1380
|
+
end try
|
|
1381
|
+
return false
|
|
1382
|
+
end hasSiblingButtonNamed
|
|
1383
|
+
|
|
1384
|
+
on continueWorkspaceOnNewBranch()
|
|
1385
|
+
-- Press Conductor's own merged-workspace action. Its handler owns all of the
|
|
1386
|
+
-- state that cannot be reproduced safely from the relay: it checks out a fresh
|
|
1387
|
+
-- branch at the PR's target, updates the workspace row, keeps every chat, and
|
|
1388
|
+
-- stages Branch continued.md in the selected chat. The relay never writes the
|
|
1389
|
+
-- database; server.ts watches `workspaces.branch` for the receipt.
|
|
1390
|
+
set strips to my tabGroups()
|
|
1391
|
+
if (count of strips) is 0 then error "couldn't find the chat pane to continue from"
|
|
1392
|
+
my assertWorkspace(item 1 of strips)
|
|
1393
|
+
set found to my shallowButtonsNamed(my webArea(), 10, "Continue")
|
|
1394
|
+
if (count of found) is 0 then error "Conductor isn't offering Continue for this workspace - it appears after its pull request is merged and refreshed"
|
|
1395
|
+
if (count of found) > 1 then error "more than one Continue button is visible - close any dialog and try again"
|
|
1396
|
+
set continueEl to item 1 of found
|
|
1397
|
+
if not (my hasSiblingButtonNamed(continueEl, "Archive")) then error "couldn't verify Conductor's merged-workspace Continue action - close any dialog and try again"
|
|
1398
|
+
set canPress to true
|
|
1399
|
+
try
|
|
1400
|
+
tell application "System Events" to tell process "Conductor"
|
|
1401
|
+
set canPress to (value of attribute "AXEnabled" of continueEl) as boolean
|
|
1402
|
+
end tell
|
|
1403
|
+
end try
|
|
1404
|
+
if not canPress then error "Conductor's Continue button is still busy - try again shortly"
|
|
1405
|
+
tell application "System Events" to tell process "Conductor"
|
|
1406
|
+
perform action "AXPress" of continueEl
|
|
1407
|
+
end tell
|
|
1408
|
+
delay 0.4
|
|
1409
|
+
end continueWorkspaceOnNewBranch
|
|
1410
|
+
|
|
1339
1411
|
on dialogButtons()
|
|
1340
1412
|
-- Both places Conductor could be drawing a confirmation, each at the depth that
|
|
1341
1413
|
-- shape actually sits at. A real sheet hangs off the window a level or two down;
|
package/dist-node/src/routes.js
CHANGED
|
@@ -96,6 +96,8 @@ export const routes = {
|
|
|
96
96
|
/** The worktree's own file list, which is what makes a chat's `src/foo.ts` a link. */
|
|
97
97
|
workspaceFiles: param('GET', '/api/workspaces/:workspaceId/files'),
|
|
98
98
|
merge: param('POST', '/api/workspaces/:workspaceId/merge'),
|
|
99
|
+
/** Keep this live workspace and its chats, but move its merged worktree onto a fresh branch. */
|
|
100
|
+
continueWorkspace: param('POST', '/api/workspaces/:workspaceId/continue'),
|
|
99
101
|
workspaceStatus: param('POST', '/api/workspaces/:workspaceId/status'),
|
|
100
102
|
/** Put the workspace away (Conductor's ⌘⇧A) — the one write that deletes a worktree. */
|
|
101
103
|
archiveWorkspace: param('POST', '/api/workspaces/:workspaceId/archive'),
|
package/dist-node/src/server.js
CHANGED
|
@@ -35,7 +35,7 @@ import { discardStagedAttachment, materializeStagedAttachments, pruneStagedAttac
|
|
|
35
35
|
import { driftWarningLines, readExposeMode, tailscaleBin } from "./tailscale.js";
|
|
36
36
|
import { renderTranscript, transcriptMessage, transcriptThrough } from "./transcript.js";
|
|
37
37
|
import { autoJoinHotspotMode, currentSsid, looksLikeHotspot, preferredNetworks } from "./wifi.js";
|
|
38
|
-
import { archiveWorkspace, closeChat, createWorkspace, describeActuator, EFFORT_LABELS, listAgentModels, lockBlocked, newChat, pickActuator, planSettingForUi, restartConductorApp, retryWontHelp, screenLocked, sendNeverStarted, setAgentOptions, setDefaultModel, setRestartGuard, setWorkspaceStatus, stopTurn, UiBusyError, uiQueueDepth, WORKSPACE_STATUS_LABELS, withUiPriority } from "./writes.js";
|
|
38
|
+
import { archiveWorkspace, closeChat, continueWorkspace, createWorkspace, describeActuator, EFFORT_LABELS, listAgentModels, lockBlocked, newChat, pickActuator, planSettingForUi, restartConductorApp, retryWontHelp, screenLocked, sendNeverStarted, setAgentOptions, setDefaultModel, setRestartGuard, setWorkspaceStatus, stopTurn, UiBusyError, uiQueueDepth, WORKSPACE_STATUS_LABELS, withUiPriority } from "./writes.js";
|
|
39
39
|
// Before anything that logs: from here on every console line is also kept in memory for
|
|
40
40
|
// `GET /api/logs`, so the phone can read why a send failed without ssh-ing into the Mac.
|
|
41
41
|
installLogCapture();
|
|
@@ -1331,6 +1331,56 @@ const server = http.createServer(async (req, res) => {
|
|
|
1331
1331
|
const result = await mergePr(ws);
|
|
1332
1332
|
return json(req, res, result.ok ? 200 : 409, result);
|
|
1333
1333
|
}
|
|
1334
|
+
// POST /api/workspaces/:id/continue { sessionId? } — press the Continue action
|
|
1335
|
+
// Conductor draws for a merged PR. The native handler checks out a fresh branch,
|
|
1336
|
+
// updates its own workspace record and stages Branch continued.md in the selected
|
|
1337
|
+
// chat. Only it can do all three consistently; this relay keeps its DB read-only.
|
|
1338
|
+
const continueOf = routeParam(routes.continueWorkspace, req.method, pathname);
|
|
1339
|
+
if (continueOf) {
|
|
1340
|
+
const workspaceId = continueOf;
|
|
1341
|
+
const ws = reads.getWorkspace(workspaceId);
|
|
1342
|
+
if (!ws) {
|
|
1343
|
+
const known = reads.getAnyWorkspace(workspaceId);
|
|
1344
|
+
if (known?.archived) {
|
|
1345
|
+
return json(req, res, 409, { ok: false, error: 'Archived workspaces cannot be continued.' });
|
|
1346
|
+
}
|
|
1347
|
+
return json(req, res, 404, { error: 'workspace not found' });
|
|
1348
|
+
}
|
|
1349
|
+
const body = JSON.parse((await readBody(req)) || '{}');
|
|
1350
|
+
const requestedSession = body.sessionId || ws.active_session_id;
|
|
1351
|
+
let tab;
|
|
1352
|
+
if (requestedSession) {
|
|
1353
|
+
const located = locateChat(ws, requestedSession);
|
|
1354
|
+
if ('error' in located)
|
|
1355
|
+
return json(req, res, 409, { ok: false, error: located.error });
|
|
1356
|
+
if (body.sessionId && !located.session) {
|
|
1357
|
+
return json(req, res, 409, { ok: false, error: 'chat is no longer one of the workspace’s tabs' });
|
|
1358
|
+
}
|
|
1359
|
+
tab = located.tab;
|
|
1360
|
+
}
|
|
1361
|
+
const previousBranch = ws.branch;
|
|
1362
|
+
if (!previousBranch)
|
|
1363
|
+
return json(req, res, 409, { ok: false, error: 'workspace has no branch to continue' });
|
|
1364
|
+
const result = await continueWorkspace({ workspace: ws, sessionId: requestedSession, tab });
|
|
1365
|
+
if (!result.ok)
|
|
1366
|
+
return json(req, res, 502, result);
|
|
1367
|
+
// AXPress only proves the button accepted a click. Conductor then fetches the
|
|
1368
|
+
// target and changes the worktree asynchronously, so the branch column is the
|
|
1369
|
+
// receipt. A generous wait covers the fetch without ever writing that column.
|
|
1370
|
+
let continued = reads.getWorkspace(workspaceId);
|
|
1371
|
+
for (let i = 0; i < 60 && (!continued?.branch || continued.branch === previousBranch); i++) {
|
|
1372
|
+
await sleep(500);
|
|
1373
|
+
continued = reads.getWorkspace(workspaceId);
|
|
1374
|
+
}
|
|
1375
|
+
if (!continued?.branch || continued.branch === previousBranch) {
|
|
1376
|
+
return json(req, res, 502, {
|
|
1377
|
+
ok: false,
|
|
1378
|
+
strategy: result.strategy,
|
|
1379
|
+
error: 'Conductor did not record a new branch within 30 seconds. Check it on your Mac before retrying.'
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
return json(req, res, 200, { ok: true, previousBranch, workspace: continued });
|
|
1383
|
+
}
|
|
1334
1384
|
// POST /api/workspaces/:id/status { status } — move it between the sidebar's status groups.
|
|
1335
1385
|
// Conductor derives that status from a PR it sometimes never links (a PR merged inside its
|
|
1336
1386
|
// poll window is invisible to it afterwards), which strands finished work in "In progress"
|
package/dist-node/src/writes.js
CHANGED
|
@@ -706,6 +706,43 @@ return "ok"`.trim();
|
|
|
706
706
|
return { ok: false, strategy: 'applescript', error: osaError(err) };
|
|
707
707
|
}
|
|
708
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* Continue a merged workspace on a new branch through Conductor's own Continue
|
|
711
|
+
* button. This deliberately does not perform `git checkout` itself: the native
|
|
712
|
+
* action also updates Conductor's workspace identity, preserves the existing
|
|
713
|
+
* sessions, clears the merged PR metadata, and stages its Branch continued.md
|
|
714
|
+
* context in the selected chat. Reproducing only the git half would leave the DB
|
|
715
|
+
* and UI describing the branch we just moved away from; writing the DB is forbidden.
|
|
716
|
+
*
|
|
717
|
+
* The exact chat matters even though every chat survives. Conductor attaches the
|
|
718
|
+
* continuation note to the selected one, so the target carries the phone's current
|
|
719
|
+
* session and `selectChatTab` asserts it before the button is found. The button is
|
|
720
|
+
* available only in Conductor's merged action bar; an absent or ambiguous control
|
|
721
|
+
* fails closed. server.ts confirms success from the resulting branch change.
|
|
722
|
+
*/
|
|
723
|
+
export async function continueWorkspace(target) {
|
|
724
|
+
if (!target.workspace.branch) {
|
|
725
|
+
return { ok: false, strategy: 'applescript', error: 'workspace has no branch to focus' };
|
|
726
|
+
}
|
|
727
|
+
const script = `
|
|
728
|
+
${CONDUCTOR_HANDLERS}
|
|
729
|
+
|
|
730
|
+
my activateConductor()
|
|
731
|
+
my focusWorkspace()
|
|
732
|
+
my selectChatTab()
|
|
733
|
+
my continueWorkspaceOnNewBranch()
|
|
734
|
+
return "ok"`.trim();
|
|
735
|
+
try {
|
|
736
|
+
await withTargetEnvironment(target, targetEnvironment => uiTurn(() => exec('osascript', ['-e', script], {
|
|
737
|
+
env: { ...process.env, ...targetEnvironment },
|
|
738
|
+
timeout: SEND_ATTEMPT_MS
|
|
739
|
+
})));
|
|
740
|
+
return { ok: true, strategy: 'applescript' };
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
return { ok: false, strategy: 'applescript', error: osaError(err) };
|
|
744
|
+
}
|
|
745
|
+
}
|
|
709
746
|
/**
|
|
710
747
|
* Quit Conductor and start it again because the phone asked — the one write here
|
|
711
748
|
* whose subject is the app rather than anything inside it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-remote",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.92.1",
|
|
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.",
|