conductor-remote 1.97.0 → 1.98.0
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
|
@@ -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-CWS0Qyzc.js"></script>
|
|
29
|
+
<link rel="stylesheet" crossorigin href="/assets/index-0UqQA-X1.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:"51edeb5f6522b16d55023377170a1bec"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-CWS0Qyzc.js",revision:null},{url:"assets/index-0UqQA-X1.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\//]}))});
|
|
@@ -33,8 +33,10 @@ const exec = promisify(execFile);
|
|
|
33
33
|
// Keep the receipt additive and version-1-readable: an installed relay and a
|
|
34
34
|
// source `yarn dev` relay can briefly share this file during an update.
|
|
35
35
|
const STORE_VERSION = 1;
|
|
36
|
+
const PREVIEW_ADVERTISEMENT_VERSION = 1;
|
|
36
37
|
const PORT_WAIT_MS = 15_000;
|
|
37
38
|
const PORT_SNAPSHOT_TTL_MS = 5000;
|
|
39
|
+
const MAX_PREVIEW_ADVERTISEMENT_BYTES = 64 * 1024;
|
|
38
40
|
function recordKey(workspaceId, targetPort) {
|
|
39
41
|
return `${workspaceId}:${targetPort}`;
|
|
40
42
|
}
|
|
@@ -323,6 +325,7 @@ function forwardUrl(record, previewUrl) {
|
|
|
323
325
|
}
|
|
324
326
|
export class DevServerController {
|
|
325
327
|
storeFile;
|
|
328
|
+
advertisementDir;
|
|
326
329
|
bin;
|
|
327
330
|
host;
|
|
328
331
|
records = new Map();
|
|
@@ -332,12 +335,15 @@ export class DevServerController {
|
|
|
332
335
|
actions = new Map();
|
|
333
336
|
constructor(storeFile = path.join(stateDir(), 'dev-forwards.json')) {
|
|
334
337
|
this.storeFile = storeFile;
|
|
338
|
+
this.advertisementDir = path.join(path.dirname(storeFile), 'dev-preview-advertisements');
|
|
335
339
|
this.bin = tailscaleBin();
|
|
336
340
|
this.host = this.bin ? magicDnsName(this.bin) : null;
|
|
337
341
|
this.load();
|
|
338
342
|
}
|
|
339
343
|
/**
|
|
340
|
-
* Let a running application publish the exact URLs it wants opened.
|
|
344
|
+
* Let a running application publish the exact URLs it wants opened. A source
|
|
345
|
+
* relay and the installed relay are separate processes, so the advertisement
|
|
346
|
+
* is also written to a private workspace-scoped manifest they can both read.
|
|
341
347
|
*
|
|
342
348
|
* The producer owns every path, query parameter and fragment — including any
|
|
343
349
|
* bootstrap credential. The forwarding layer only accepts loopback HTTP URLs
|
|
@@ -348,9 +354,73 @@ export class DevServerController {
|
|
|
348
354
|
return;
|
|
349
355
|
if (!previews.length) {
|
|
350
356
|
this.advertisedPreviews.delete(workspaceId);
|
|
357
|
+
this.removeOwnPreviewAdvertisement(workspaceId);
|
|
351
358
|
return;
|
|
352
359
|
}
|
|
353
|
-
|
|
360
|
+
const published = previews.slice(0, 10).map(preview => ({ name: preview.name, url: preview.url }));
|
|
361
|
+
this.advertisedPreviews.set(workspaceId, published);
|
|
362
|
+
const advertisement = {
|
|
363
|
+
version: PREVIEW_ADVERTISEMENT_VERSION,
|
|
364
|
+
workspaceId,
|
|
365
|
+
ownerPid: process.pid,
|
|
366
|
+
previews: published
|
|
367
|
+
};
|
|
368
|
+
const file = this.previewAdvertisementFile(workspaceId);
|
|
369
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
370
|
+
try {
|
|
371
|
+
fs.mkdirSync(this.advertisementDir, { recursive: true, mode: 0o700 });
|
|
372
|
+
fs.writeFileSync(temporary, `${JSON.stringify(advertisement, null, 2)}\n`, { mode: 0o600 });
|
|
373
|
+
fs.chmodSync(temporary, 0o600);
|
|
374
|
+
fs.renameSync(temporary, file);
|
|
375
|
+
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
try {
|
|
378
|
+
fs.unlinkSync(temporary);
|
|
379
|
+
}
|
|
380
|
+
catch { }
|
|
381
|
+
console.warn(`⚠ could not publish dev-server previews (${err instanceof Error ? err.message : err})`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
previewAdvertisementFile(workspaceId) {
|
|
385
|
+
const key = crypto.createHash('sha256').update(workspaceId).digest('hex');
|
|
386
|
+
return path.join(this.advertisementDir, `${key}.json`);
|
|
387
|
+
}
|
|
388
|
+
readPreviewAdvertisement(workspaceId) {
|
|
389
|
+
try {
|
|
390
|
+
const file = this.previewAdvertisementFile(workspaceId);
|
|
391
|
+
if (fs.statSync(file).size > MAX_PREVIEW_ADVERTISEMENT_BYTES)
|
|
392
|
+
return [];
|
|
393
|
+
const value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
394
|
+
if (value.version !== PREVIEW_ADVERTISEMENT_VERSION ||
|
|
395
|
+
value.workspaceId !== workspaceId ||
|
|
396
|
+
!Number.isInteger(value.ownerPid) ||
|
|
397
|
+
(value.ownerPid ?? 0) <= 0 ||
|
|
398
|
+
!processAlive(value.ownerPid) ||
|
|
399
|
+
!Array.isArray(value.previews))
|
|
400
|
+
return [];
|
|
401
|
+
return value.previews
|
|
402
|
+
.flatMap(preview => preview &&
|
|
403
|
+
typeof preview.url === 'string' &&
|
|
404
|
+
preview.url.length <= 8192 &&
|
|
405
|
+
(preview.name === undefined || (typeof preview.name === 'string' && preview.name.length <= 256))
|
|
406
|
+
? [{ name: preview.name, url: preview.url }]
|
|
407
|
+
: [])
|
|
408
|
+
.slice(0, 10);
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
removeOwnPreviewAdvertisement(workspaceId) {
|
|
415
|
+
const file = this.previewAdvertisementFile(workspaceId);
|
|
416
|
+
try {
|
|
417
|
+
const value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
418
|
+
if (value.workspaceId === workspaceId && value.ownerPid === process.pid)
|
|
419
|
+
fs.unlinkSync(file);
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
// Missing, malformed or owned by another producer: there is nothing of ours to remove.
|
|
423
|
+
}
|
|
354
424
|
}
|
|
355
425
|
refreshTailscale() {
|
|
356
426
|
this.bin ??= tailscaleBin();
|
|
@@ -410,7 +480,7 @@ export class DevServerController {
|
|
|
410
480
|
const configured = resolvePreviewTargets(previewUrlSettings(workspace), basePort);
|
|
411
481
|
if (configured.length)
|
|
412
482
|
return configured;
|
|
413
|
-
const advertised = resolvePreviewTargets(this.advertisedPreviews.get(workspace.id) ??
|
|
483
|
+
const advertised = resolvePreviewTargets(this.advertisedPreviews.get(workspace.id) ?? this.readPreviewAdvertisement(workspace.id), basePort);
|
|
414
484
|
if (advertised.length)
|
|
415
485
|
return advertised;
|
|
416
486
|
const detected = resolvePreviewTargets(detectedUrls.map(url => ({ url })), basePort);
|
package/dist-node/src/server.js
CHANGED
|
@@ -55,8 +55,9 @@ if (cfg.devWebPort !== undefined && process.env.CONDUCTOR_WORKSPACE_ID) {
|
|
|
55
55
|
const preview = new URL(`http://localhost:${cfg.devWebPort}/`);
|
|
56
56
|
preview.hash = new URLSearchParams({ token: cfg.token }).toString();
|
|
57
57
|
// `yarn dev` is itself one application behind Conductor's Run button. Publish
|
|
58
|
-
// the same canonical URL printed below
|
|
59
|
-
//
|
|
58
|
+
// the same canonical URL printed below. The private live advertisement crosses
|
|
59
|
+
// into the installed relay that normally serves the phone; its forwarding code
|
|
60
|
+
// still treats this exactly like any full URL and knows nothing about the token.
|
|
60
61
|
devServers.advertisePreviewUrls(process.env.CONDUCTOR_WORKSPACE_ID, [{ name: 'Conductor Remote', url: preview.href }]);
|
|
61
62
|
}
|
|
62
63
|
const STAGED_ATTACHMENTS_DIR = path.join(stateDir(), 'attachment-staging');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-remote",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.98.0",
|
|
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.",
|