conductor-remote 1.53.0 → 1.53.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-CQuR_IG3.js"></script>
|
|
17
17
|
<link rel="stylesheet" crossorigin href="/assets/index-CNdG7lFR.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 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:"
|
|
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:"6fd3f4c8b836fbae3791d6d273888b16"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-CQuR_IG3.js",revision:null},{url:"assets/index-CNdG7lFR.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,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
const SOURCE_EXTENSIONS = new Set([
|
|
2
3
|
'.bash',
|
|
3
4
|
'.c',
|
|
@@ -40,6 +41,22 @@ function sourceExtension(filePath) {
|
|
|
40
41
|
export function isPreviewableSource(filePath) {
|
|
41
42
|
return SOURCE_EXTENSIONS.has(sourceExtension(filePath));
|
|
42
43
|
}
|
|
44
|
+
/** True only for a descendant. A prefix test would let `/workspaces-old` escape `/workspaces`. */
|
|
45
|
+
function insideDirectory(filePath, root) {
|
|
46
|
+
const relative = path.relative(root, filePath);
|
|
47
|
+
return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Public Funnel clients may preview source under Conductor's workspaces only.
|
|
51
|
+
* A tailnet relay is limited to trusted devices, so it can also show project
|
|
52
|
+
* notes and supporting files from the signed-in user's home directory.
|
|
53
|
+
*
|
|
54
|
+
* Call this only with real paths. That ensures a symlink cannot point outside
|
|
55
|
+
* an allowed directory after this check.
|
|
56
|
+
*/
|
|
57
|
+
export function isAllowedPreviewPath(filePath, workspaceRoot, homeRoot, exposeMode) {
|
|
58
|
+
return insideDirectory(filePath, workspaceRoot) || (exposeMode === 'tailnet' && insideDirectory(filePath, homeRoot));
|
|
59
|
+
}
|
|
43
60
|
/**
|
|
44
61
|
* Parse a source link without treating ordinary PWA routes as file references.
|
|
45
62
|
* The extension allowlist keeps links such as `/w/a-workspace` in the browser,
|
package/dist-node/src/notify.js
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
* Accessibility, no AppleScript, no window has to exist. A Conductor update can
|
|
9
9
|
* rename every UI string and notifications keep working.
|
|
10
10
|
*
|
|
11
|
-
* What counts as news is deliberately narrow
|
|
12
|
-
* signal Conductor records
|
|
13
|
-
* - `working → idle`
|
|
14
|
-
* "asked you a question" and "
|
|
15
|
-
* three end the turn.
|
|
11
|
+
* What counts as news is deliberately narrow. `sessions.status` is the only
|
|
12
|
+
* signal Conductor records. A normal turn ends in one of three states:
|
|
13
|
+
* - `working → idle | needs_plan_response | needs_user_input` means the turn ended.
|
|
14
|
+
* This is *the* event: it covers "done", "asked you a question" and "plan ready"
|
|
15
|
+
* alike, because all three end the turn.
|
|
16
16
|
* - `→ error` — the agent stopped badly.
|
|
17
17
|
* There is no permission-request table to watch (verified against the schema), so
|
|
18
18
|
* don't go looking for a finer-grained trigger; there isn't one.
|
|
@@ -247,6 +247,10 @@ function askedBy(state) {
|
|
|
247
247
|
return '';
|
|
248
248
|
return `${state.turnStartedAt ?? ''}|${state.lastUserMessageAt ?? ''}`;
|
|
249
249
|
}
|
|
250
|
+
/** Statuses that keep a completed turn open until the person responds. */
|
|
251
|
+
function turnEnded(status) {
|
|
252
|
+
return status === 'idle' || status === 'needs_plan_response' || status === 'needs_user_input';
|
|
253
|
+
}
|
|
250
254
|
/**
|
|
251
255
|
* The status state machine, kept apart from the store and the network so it can be
|
|
252
256
|
* driven a tick at a time by `scripts/check-notify.ts`. Every rule in it is a rule
|
|
@@ -307,7 +311,7 @@ export class TurnWatcher {
|
|
|
307
311
|
if (pendingKind) {
|
|
308
312
|
this.armed.delete(state.sessionId);
|
|
309
313
|
// Confirmed only if the new status held for a second step; a flap just drops the arm.
|
|
310
|
-
const held = (pendingKind === 'done' && now
|
|
314
|
+
const held = (pendingKind === 'done' && turnEnded(now)) || (pendingKind === 'error' && now === 'error');
|
|
311
315
|
if (held && !(pendingKind === 'done' && this.selfScheduled(state))) {
|
|
312
316
|
const asked = askedBy(state);
|
|
313
317
|
if (pendingKind === 'done' && asked)
|
|
@@ -320,7 +324,7 @@ export class TurnWatcher {
|
|
|
320
324
|
// contributes its status to the snapshot but is never itself news.
|
|
321
325
|
if (before === undefined)
|
|
322
326
|
continue;
|
|
323
|
-
if (before === 'working' && now
|
|
327
|
+
if (before === 'working' && turnEnded(now))
|
|
324
328
|
this.armed.set(state.sessionId, 'done');
|
|
325
329
|
else if (before !== 'error' && now === 'error')
|
|
326
330
|
this.armed.set(state.sessionId, 'error');
|
package/dist-node/src/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import { writeAttachment } from "./attachments.js";
|
|
|
8
8
|
import { startAutoUpdate, updateStatus } from "./autoupdate.js";
|
|
9
9
|
import { loadConfig, stateDir } from "./config.js";
|
|
10
10
|
import { ConductorDb } from "./db.js";
|
|
11
|
-
import { parseFileReference } from "./file-preview.js";
|
|
11
|
+
import { isAllowedPreviewPath, parseFileReference } from "./file-preview.js";
|
|
12
12
|
import { FirstPromptQueue } from "./firstprompt.js";
|
|
13
13
|
import { startFunnelWatchdog } from "./funnel-watchdog.js";
|
|
14
14
|
import { workspaceDiff } from "./git.js";
|
|
@@ -25,7 +25,7 @@ import { foldHits, queryTokens, SearchIndex } from "./search.js";
|
|
|
25
25
|
import { SendOnce } from "./sendonce.js";
|
|
26
26
|
import { readSettings, writeSettings } from "./settings.js";
|
|
27
27
|
import { discardStagedAttachment, materializeStagedAttachments, stageAttachment, stagedAttachments } from "./staged-attachments.js";
|
|
28
|
-
import { driftWarningLines, tailscaleBin } from "./tailscale.js";
|
|
28
|
+
import { driftWarningLines, readExposeMode, tailscaleBin } from "./tailscale.js";
|
|
29
29
|
import { renderTranscript } from "./transcript.js";
|
|
30
30
|
import { autoJoinHotspotMode, currentSsid, looksLikeHotspot, preferredNetworks } from "./wifi.js";
|
|
31
31
|
import { createWorkspace, describeActuator, EFFORT_LABELS, listAgentModels, lockBlocked, newChat, pickActuator, retryWontHelp, screenLocked, setAgentOptions, setRestartGuard, setWorkspaceStatus, stopTurn, UiBusyError, uiQueueDepth, WORKSPACE_STATUS_LABELS, withUiPriority } from "./writes.js";
|
|
@@ -452,16 +452,11 @@ async function serveLocalImage(req, res, requestedPath) {
|
|
|
452
452
|
const FILE_PREVIEW_MAX_BYTES = 512 * 1024;
|
|
453
453
|
const FILE_PREVIEW_CONTEXT_LINES = 100;
|
|
454
454
|
const FILE_PREVIEW_FIRST_LINES = 500;
|
|
455
|
-
/** True only for a descendant. A prefix test would let `/workspaces-old` escape `/workspaces`. */
|
|
456
|
-
function insideDirectory(filePath, root) {
|
|
457
|
-
const relative = path.relative(root, filePath);
|
|
458
|
-
return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
459
|
-
}
|
|
460
455
|
/**
|
|
461
456
|
* Serve source that an agent linked in its Markdown. The link format comes from
|
|
462
|
-
* coding-agent file references, but its path still arrives from
|
|
463
|
-
*
|
|
464
|
-
*
|
|
457
|
+
* coding-agent file references, but its path still arrives from a remote client.
|
|
458
|
+
* Public Funnel clients stay within Conductor workspaces. Tailnet-only relays may
|
|
459
|
+
* also read supporting source files from the signed-in user's home directory.
|
|
465
460
|
*/
|
|
466
461
|
async function serveFilePreview(req, res, reference) {
|
|
467
462
|
const target = parseFileReference(reference);
|
|
@@ -469,15 +464,18 @@ async function serveFilePreview(req, res, reference) {
|
|
|
469
464
|
return json(req, res, 404, { error: 'source file not found' });
|
|
470
465
|
let filePath;
|
|
471
466
|
let workspaceRoot;
|
|
467
|
+
let homeRoot;
|
|
472
468
|
let size;
|
|
473
469
|
try {
|
|
474
470
|
;
|
|
475
|
-
[filePath, workspaceRoot] = await Promise.all([
|
|
471
|
+
[filePath, workspaceRoot, homeRoot] = await Promise.all([
|
|
476
472
|
fs.promises.realpath(target.path),
|
|
477
|
-
fs.promises.realpath(cfg.workspacesRoot)
|
|
473
|
+
fs.promises.realpath(cfg.workspacesRoot),
|
|
474
|
+
fs.promises.realpath(os.homedir())
|
|
478
475
|
]);
|
|
479
|
-
if (!
|
|
476
|
+
if (!isAllowedPreviewPath(filePath, workspaceRoot, homeRoot, readExposeMode())) {
|
|
480
477
|
return json(req, res, 404, { error: 'source file not found' });
|
|
478
|
+
}
|
|
481
479
|
const info = await fs.promises.stat(filePath);
|
|
482
480
|
if (!info.isFile())
|
|
483
481
|
return json(req, res, 404, { error: 'source file not found' });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-remote",
|
|
3
|
-
"version": "1.53.
|
|
3
|
+
"version": "1.53.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.",
|