pairing moves in, and the extension download stops being a snapshot
Two halves of the same coupling, both left behind by this morning's extraction. The pairing UI was Settings/IntegrationsSettings/BrowserRelay.tsx — a PLATFORM screen driving this plugin's pairing, calling /browser/relay-token, and working only because the plugin happens to serve that path. It is a panel here now, beside the tab list rather than buried in settings: it shows whether the extension is connected and how many tabs it holds, which is the first thing anyone looks at when a capture does not work, and that is not configuration you set once. The download link pointed at public/browser-relay-extension.zip, a static file. That file was 58 lines behind the source it claimed to be — built in August from something that predated a February commit, never rebuilt, and serving an extension missing the multi-session recovery path that the same February commit added. Anyone downloading it got the version that cannot recover from exactly the failure they were about to hit. So the archive is built from the extension directory per request. There is no second copy to fall behind, which is the only durable fix for that class. zip.ts is hand-rolled and stored rather than deflated. The route's own history says it used to shell out to `zip`; this manifest's "nothing to declare" is worth more than 30 KB on a download that happens once, and Chrome accepts stored entries. Timestamps are fixed at the format's zero so the same directory always produces identical bytes — which is what makes the ETag meaningful and "did this change" answerable by comparing two files. Verified by unzipping the output and diffing background.js against the source. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+25
-2
@@ -1,4 +1,6 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { join } from 'node:path';
|
||||
import { zipDirectory } from './zip';
|
||||
import { createRouter } from '@@/create-router';
|
||||
import * as errors from '@@/custom-errors';
|
||||
import { sendSidecarCommand } from '@@/sidecar-registry';
|
||||
@@ -44,8 +46,29 @@ async function getOpts(userId: number) {
|
||||
return { relayPort: RELAY_PORT, userToken: deriveRelayToken(userId, RELAY_PORT, salt) };
|
||||
}
|
||||
|
||||
// GET /extension-download used to zip the extension directory on the fly, shelling out to `zip`. It had
|
||||
// no caller: BrowserRelay.tsx links straight at /browser-relay-extension.zip, a real file in public/.
|
||||
// ── The extension, zipped from the directory on every request ──
|
||||
//
|
||||
// This route existed once, shelling out to `zip`, and was removed as having no caller: the UI linked at
|
||||
// `/browser-relay-extension.zip`, a static file in public/. That file was then found to be 58 lines
|
||||
// behind the source it claimed to be — built from something older and never rebuilt.
|
||||
//
|
||||
// So it is back, without the host binary (see zip.ts) and with the caller inside this plugin. There is
|
||||
// no second copy to fall behind, which is the only durable fix for that.
|
||||
|
||||
router.get('/extension.zip', async (ctx) => {
|
||||
const dir = join(import.meta.dir, '..', 'extension');
|
||||
const bytes = zipDirectory(dir);
|
||||
// `.buffer` because Bun's BodyInit does not accept a Uint8Array view directly.
|
||||
return new Response(bytes.buffer as ArrayBuffer, {
|
||||
headers: {
|
||||
'content-type': 'application/zip',
|
||||
'content-disposition': 'attachment; filename="officer-browser-relay.zip"',
|
||||
// Deterministic bytes for an unchanged directory, so a repeat download is a 304 rather than 50 KB.
|
||||
etag: `"${Bun.hash(bytes).toString(16)}"`,
|
||||
'cache-control': 'private, no-cache',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/status', async (ctx) => {
|
||||
const user = ctx.get('user');
|
||||
|
||||
Reference in New Issue
Block a user