conductor-remote 1.53.0 → 1.53.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,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-Dz1kQTBe.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:"f0d77cb2af6cdaeed1b4528603d90245"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-Dz1kQTBe.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/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.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.",
|