bookmark-bridge 2.0.0 → 2.0.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/README.md CHANGED
@@ -129,6 +129,7 @@ The v2 server is available on the npm `native` channel. The standard
129
129
  permission prompt. If setup fails, the extension stays in Legacy mode.
130
130
  3. Run `bridge_status` from your MCP client to confirm the connection.
131
131
 
132
+ The Native npm package contains the prebuilt servers and their checksums.
132
133
  The first server startup installs the native host in a user data directory
133
134
  outside the npm cache and registers it with Chrome. Chrome launches that host
134
135
  when the extension connects. The host and the MCP process communicate over an
@@ -1,13 +1,11 @@
1
1
  #!/usr/bin/env node
2
- // Fetches the prebuilt server for this platform on first run, then hands over to
3
- // it. An MCP client launches this over stdio, so nothing may be written to
2
+ // Verifies and unpacks the bundled server, then hands over to it.
3
+ // An MCP client launches this over stdio, so nothing may be written to
4
4
  // stdout that is not protocol: progress goes to stderr.
5
5
  import {spawn} from 'node:child_process';
6
6
  import {createHash} from 'node:crypto';
7
- import {createWriteStream} from 'node:fs';
8
7
  import {chmod, mkdir, mkdtemp, readFile, rename, rm, stat} from 'node:fs/promises';
9
8
  import {dirname, join} from 'node:path';
10
- import {pipeline} from 'node:stream/promises';
11
9
  import {fileURLToPath} from 'node:url';
12
10
 
13
11
  const here = dirname(fileURLToPath(import.meta.url));
@@ -35,7 +33,7 @@ if (!target) {
35
33
  die(`no prebuilt server for ${key}. Build from source: https://github.com/${REPO}`);
36
34
  }
37
35
 
38
- // Cached per version, so upgrading the package fetches a matching server rather
36
+ // Cached per version, so upgrading the package unpacks a matching server rather
39
37
  // than silently reusing an old one.
40
38
  const cacheDir = join(here, '..', '.bin', pkg.version);
41
39
  const binary = join(cacheDir, `bookmark-bridge${EXE}`);
@@ -44,38 +42,20 @@ async function exists(path) {
44
42
  try { await stat(path); return true; } catch { return false; }
45
43
  }
46
44
 
47
- async function fetchOrDie(url, what) {
48
- const res = await fetch(url, {redirect: 'follow'});
49
- if (!res.ok) die(`could not download ${what} (HTTP ${res.status})\n ${url}`);
50
- return res;
51
- }
52
-
53
45
  async function install() {
54
- const tag = `v${pkg.version}`;
46
+ const assets = join(here, '..', 'release-assets');
55
47
  const name = `bookmark-bridge-${target}.tar.gz`;
56
- const base = `https://github.com/${REPO}/releases/download/${tag}`;
57
-
58
- process.stderr.write(`bookmark-bridge: fetching ${tag} for ${key}\n`);
59
- const sums = (await (await fetchOrDie(`${base}/SHA256SUMS`, 'checksums')).text());
60
- const line = sums.split('\n').find((l) => l.trim().endsWith(name));
61
- if (!line) die(`no checksum published for ${name} in ${tag}`);
62
- const expected = line.trim().split(/\s+/)[0];
48
+ const sums = await readFile(join(assets, 'SHA256SUMS'), 'utf8');
49
+ const expected = sums.split('\n').map(line => line.trim().split(/\s+/))
50
+ .find(([, file]) => file === name)?.[0];
51
+ if (!expected) throw new Error(`no checksum bundled for ${name}`);
52
+ const archive = join(assets, name);
53
+ const actual = createHash('sha256').update(await readFile(archive)).digest('hex');
54
+ if (actual !== expected) throw new Error(`checksum mismatch for ${name}`);
63
55
 
64
56
  await mkdir(dirname(cacheDir), {recursive: true});
65
57
  const staging = await mkdtemp(join(dirname(cacheDir), '.install-'));
66
- const archive = join(staging, name);
67
58
  try {
68
- await pipeline(
69
- (await fetchOrDie(`${base}/${name}`, 'the server')).body,
70
- createWriteStream(archive),
71
- );
72
-
73
- // Never run a binary that does not match what the release published.
74
- const actual = createHash('sha256').update(await readFile(archive)).digest('hex');
75
- if (actual !== expected) {
76
- die(`checksum mismatch for ${name}\n expected ${expected}\n got ${actual}`);
77
- }
78
-
79
59
  await new Promise((resolve, reject) => {
80
60
  const tar = spawn('tar', ['-xzf', archive, '-C', staging], {stdio: 'inherit'});
81
61
  tar.on('error', reject);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bookmark-bridge",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Chrome bookmarks as MCP tools, with every change recorded in git so it can be rolled back",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,10 +13,11 @@
13
13
  },
14
14
  "files": [
15
15
  "bin/",
16
+ "release-assets/",
16
17
  "README.md"
17
18
  ],
18
19
  "scripts": {
19
- "prepublishOnly": "node -e \"if (process.env.npm_config_tag !== 'native') { console.error('Publish this version with npm publish --tag native.'); process.exit(1); }\""
20
+ "prepublishOnly": "node -e \"if (process.env.npm_config_tag !== 'native' || !require('fs').existsSync('release-assets/SHA256SUMS')) { console.error('Publish the bundled release with npm publish --tag native.'); process.exit(1); }\""
20
21
  },
21
22
  "publishConfig": {
22
23
  "tag": "native"
@@ -0,0 +1,5 @@
1
+ af0dc501199feb693a68cc1d433499e67a633f495fbce1e6f43c5e165f6b99a3 bookmark-bridge-aarch64-apple-darwin.tar.gz
2
+ 35968f8114af290d81638e97d5482c23457b0b72c5a6004eabec78a3739a13c8 bookmark-bridge-aarch64-unknown-linux-gnu.tar.gz
3
+ 67fae0592491c25bbb8caca61630dd4213d26fe83e09e2f03bf75a130c70efd6 bookmark-bridge-x86_64-apple-darwin.tar.gz
4
+ d443279e1a1380f442b69df9c86072d40bb5980d550aba8050e6c84aa3fcf184 bookmark-bridge-x86_64-pc-windows-msvc.tar.gz
5
+ 3cc3b1480fd7fb6a39d7407d48bf528debe1216791bade50f1e95cfd5dd237b9 bookmark-bridge-x86_64-unknown-linux-gnu.tar.gz