bridgerator 1.1.6 → 1.1.8

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/LICENSE CHANGED
@@ -19,6 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
-
23
- Bundled dependency: Desktop Commander (https://github.com/wonderwhy-er/DesktopCommanderMCP),
24
- MIT License, Copyright (c) 2024-2025 Eduard Ruzga and Desktop Commander Contributors.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "bridgerator",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "bridgerator",
9
- "version": "1.1.6",
9
+ "version": "1.1.8",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@modelcontextprotocol/sdk": "^1.29.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bridgerator",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "description": "Connect ChatGPT to your Windows PC: files, search and terminal through an outbound-only bridge with folder limits. No inbound ports, no admin rights.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/bridge.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // Bridgerator bridge: runs on the user's PC.
2
- // - starts the bundled open-source Desktop Commander MCP server as a child over stdio;
2
+ // - starts the bundled MCP engine as a child over stdio;
3
3
  // - pushes this PC's limits (allowedDirectories, blockedCommands) into it at startup;
4
4
  // - opens ONE outbound WebSocket to the relay (no inbound port is opened on this PC);
5
5
  // - forwards each relay `call` through the policy (excluded / read-only folders, disabled tools)
@@ -78,7 +78,7 @@ export async function localDrivesAsync() {
78
78
  }
79
79
  const DRIVES_MS = 60000;
80
80
 
81
- export async function startDesktopCommander(cfg, log = console.log, { ask: askArg, recycle: recycleArg, drives: drivesArg, delegate: delegateOpts } = {}) {
81
+ export async function startEngine(cfg, log = console.log, { ask: askArg, recycle: recycleArg, drives: drivesArg, delegate: delegateOpts } = {}) {
82
82
  const ask0 = askArg || askOnPc;
83
83
  const recycle = recycleArg || recycleOnPc;
84
84
  // Drive list: refreshed in the background once a minute (new / removed drives are seen); only the
@@ -115,7 +115,7 @@ export async function startDesktopCommander(cfg, log = console.log, { ask: askAr
115
115
  const absPath = (p) => path.resolve(engineDir, String(p).replace(/^~(?=[\\/]|$)/, os.homedir()));
116
116
  const transport = new StdioClientTransport({
117
117
  command: process.execPath,
118
- // Own Desktop Commander config (dc-isolate.mjs): a DC the user runs elsewhere keeps its limits.
118
+ // The engine's own config folder (dc-isolate.mjs): a copy of the engine the user runs elsewhere keeps its limits.
119
119
  args: ['--import', ISOLATE, DC_ENTRY],
120
120
  // No calls home: telemetry kill-switch, and remote feature flags (onboarding page, promo text
121
121
  // injected into tool results, A/B tests) replaced by an empty local set.
@@ -133,7 +133,7 @@ export async function startDesktopCommander(cfg, log = console.log, { ask: askAr
133
133
  // Engine gone (crash, killed): nothing works without it — exit, the autostart starts us again.
134
134
  let closing = false;
135
135
  client.onclose = () => { if (closing) return; log(`[bridgerator] engine stopped — exiting (autostart restarts the bridge)${engineErr()}`); process.exit(1); };
136
- // The PC owner's limits go into Desktop Commander's own config; the model can't change them
136
+ // The PC owner's limits go into the engine's own config; the model can't change them
137
137
  // (set_config_value is never exposed to the relay, see policy.ALWAYS_HIDDEN).
138
138
  const set = (key, value) => client.callTool({ name: 'set_config_value', arguments: { key, value } });
139
139
  await set('allowedDirectories', cfg.allowedDirectories || []);
@@ -492,7 +492,7 @@ export async function startDesktopCommander(cfg, log = console.log, { ask: askAr
492
492
  return { ...rest, confirm: { enabled: confirm.enabled, ...confirm.ask, timeoutSeconds: confirm.timeoutSec }, telemetryEnabled: telemetry };
493
493
  };
494
494
  async function readResource(uri) {
495
- // The old ui://desktop-commander/… still works: ChatGPT and the relay's offline list may have cached it.
495
+ // The old widget URI still works: ChatGPT and the relay's offline list may have cached it.
496
496
  if (!resourceUris.has(ourUi(String(uri)))) return { error: 'unknown resource' };
497
497
  try { return { result: ourUi(debrand(await client.readResource({ uri: engineUi(uri) }))) }; } catch (e) { return { error: String(e?.message || e) }; }
498
498
  }
package/src/cli.mjs CHANGED
@@ -10,7 +10,7 @@ import crypto from 'node:crypto';
10
10
  import { spawn, execFileSync } from 'node:child_process';
11
11
  import { fileURLToPath } from 'node:url';
12
12
  import { isSea } from 'node:sea';
13
- import { startDesktopCommander, connect, checkRelay } from './bridge.mjs';
13
+ import { startEngine, connect, checkRelay } from './bridge.mjs';
14
14
  import { pair, siteOf, checkKey, machineFp } from './pair.mjs';
15
15
  import { ACCOUNT_URL } from './notice.mjs';
16
16
  import { send, VERSION } from './telemetry.mjs';
@@ -393,7 +393,7 @@ async function main() {
393
393
  protectSensitive: prev.protectSensitive === true,
394
394
  // Readable, never changed by the file tools (the terminal is not bound by folders).
395
395
  readOnlyDirectories: o['read-only'] ? abs(o['read-only']) : prev.readOnlyDirectories || [],
396
- // blockedCommands: absent = Desktop Commander's own default list (format, diskpart, reg, net, sc, runas, …).
396
+ // blockedCommands: absent = the engine's own default list (format, diskpart, reg, net, sc, runas, …).
397
397
  ...(prev.blockedCommands ? { blockedCommands: prev.blockedCommands } : {}),
398
398
  disabledTools: prev.disabledTools || [],
399
399
  installId: prev.installId || crypto.randomUUID(),
@@ -471,7 +471,7 @@ async function main() {
471
471
  plan: b.kind === 'none' ? null : { label: b.action === 'renew' ? 'Renew Pro…' : 'Upgrade to Pro…', url: b.url, badge: b.tone },
472
472
  });
473
473
  };
474
- dc = await startDesktopCommander({ ...cfg, dcConfigDir: path.join(DIR, 'dc'), configDir: DIR, confirmIni: INI, configFile: CONFIG, snapshotsDir: SNAP_DIR, scriptsDir: SCRIPTS_DIR }, log, { ask: tray ? (q) => tray.ask(q) : undefined });
474
+ dc = await startEngine({ ...cfg, dcConfigDir: path.join(DIR, 'dc'), configDir: DIR, confirmIni: INI, configFile: CONFIG, snapshotsDir: SNAP_DIR, scriptsDir: SCRIPTS_DIR }, log, { ask: tray ? (q) => tray.ask(q) : undefined });
475
475
  dc.cfgName = () => readConfig()?.deviceName;
476
476
  dc.openWindow = (page) => tray?.open(page);
477
477
  dc.onPlan = pushTray;
@@ -25,7 +25,7 @@ const PATCHES = [
25
25
  { file: /[\\/]dist[\\/]terminal-manager\.js$/, from: "args: ['-Command', command],", to: "args: ['-Command', '[Console]::OutputEncoding=[Text.Encoding]::UTF8;' + command]," },
26
26
  { file: /[\\/]dist[\\/]index\.js$/, from: ' ensureChromeAvailable();\n', to: ' // bridgerator: no Chrome pre-download\n' },
27
27
  ];
28
- const ENGINE = /[\\/]bridgerator-engine[\\/]dist[\\/]/; // the bundled engine (Desktop Commander, MIT) installed under our own name (npm alias, owner 26.10)
28
+ const ENGINE = /[\\/]bridgerator-engine[\\/]dist[\\/]/; // the bundled engine, installed under our own name (owner 26.10)
29
29
 
30
30
  export async function load(url, context, nextLoad) {
31
31
  const r = await nextLoad(url, context);
@@ -1,4 +1,4 @@
1
- // Preload for the Desktop Commander child: registers dc-config-hooks.mjs (see there).
1
+ // Preload for the engine child: registers dc-config-hooks.mjs (see there).
2
2
  import { register } from 'node:module';
3
3
 
4
4
  if (!process.env.BRIDGERATOR_DC_CONFIG_DIR) throw new Error('bridgerator: BRIDGERATOR_DC_CONFIG_DIR not set');
package/src/policy.mjs CHANGED
@@ -1,5 +1,5 @@
1
- // Bridge-side access policy, applied BEFORE a call reaches Desktop Commander and to its results.
2
- // Desktop Commander itself only knows allowedDirectories; it has no "except these folders" rule,
1
+ // Bridge-side access policy, applied BEFORE a call reaches the engine and to its results.
2
+ // The engine itself only knows allowedDirectories; it has no "except these folders" rule,
3
3
  // so deniedDirectories is enforced here: input paths are resolved (symlinks/junctions included)
4
4
  // and refused when they fall inside a denied folder; result lines that mention a denied path are
5
5
  // dropped (search hits, recursive listings).
@@ -53,7 +53,7 @@ const expandHome = (p) => (p === '~' || p.startsWith('~/') || p.startsWith('~\\'
53
53
  // base: the folder a relative path is resolved against (default: this process's cwd).
54
54
  export const norm = (p, base) => path.resolve(base || '.', expandHome(String(p))).replace(/[\\/]+$/, '').toLowerCase();
55
55
 
56
- // Resolve symlinks/junctions on the deepest existing ancestor, like Desktop Commander does.
56
+ // Resolve symlinks/junctions on the deepest existing ancestor, like the engine does.
57
57
  export function realish(p, base) {
58
58
  const start = path.resolve(base || '.', expandHome(String(p)));
59
59
  let cur = start;
@@ -157,6 +157,8 @@ const SELF_CHANGE = [
157
157
  /\b(?:taskkill|tskill|pskill|kill|stop-process|wmic|schtasks|(?:start|stop|disable|enable|unregister|set)-scheduledtask)\b[^|;&\n]*\bbridgerator/i,
158
158
  /\bbridgerator[^;&\n]*\|\s*(?:stop-process|kill|(?:stop|disable|unregister|set)-scheduledtask)\b/i,
159
159
  ];
160
+ // Tools whose result is the text of files that passed the folder check (shown whole, never filtered).
161
+ const FILE_TEXT = new Set(['read_file', 'read_multiple_files']);
160
162
  export function createPolicy(cfg = {}) {
161
163
  const base = cfg.cwd || process.cwd();
162
164
  // protectSensitive absent = on (callers that don't say); the bridge passes the person's setting.
@@ -249,8 +251,10 @@ export function createPolicy(cfg = {}) {
249
251
  },
250
252
  // Drop result lines that reveal anything inside a denied folder. `args` lets a listing of a
251
253
  // parent folder hide the excluded child, whose entries are printed relative to that parent.
254
+ // Not for the text of a file that was allowed to be read: a line that merely NAMES an excluded folder is the
255
+ // person's own content (E2E 26.10: .md notes lost lines, a later full rewrite would delete them from disk).
252
256
  filter(result, name, args) {
253
- if (!denied.length || !result || !Array.isArray(result.content)) return result;
257
+ if (!denied.length || !result || !Array.isArray(result.content) || FILE_TEXT.has(name)) return result;
254
258
  const rel = [];
255
259
  if (name === 'list_directory' && typeof args?.path === 'string') {
256
260
  const at = norm(realish(args.path, base));
@@ -265,7 +269,7 @@ export function createPolicy(cfg = {}) {
265
269
  dropped += lines.length - kept.length;
266
270
  return { ...c, text: kept.join('\n') };
267
271
  });
268
- if (dropped) content.push({ type: 'text', text: `[Bridgerator: ${dropped} line(s) hidden — excluded folder]` });
272
+ if (dropped) content.push({ type: 'text', text: `[Bridgerator: ${dropped} line(s) naming an excluded folder were left out; everything else above is complete — not an error]` }); // E2E 26.10: "hidden" read as a refusal
269
273
  return { ...result, content };
270
274
  },
271
275
  };
@@ -292,7 +296,7 @@ export function debrand(v) {
292
296
  }
293
297
 
294
298
  // Two results are the engine's own text, not user data: the prompt catalog (get_prompts says
295
- // "Desktop Commander Prompt Categories") and get_config, whose `version` is the engine's and is
299
+ // its own brand in the heading) and get_config, whose `version` is the engine's and is
296
300
  // shown as "Bridgerator version" — replaced by the bridge's own version.
297
301
  export function debrandResult(name, result, version) {
298
302
  if (!result || typeof result !== 'object') return result;
package/ui/app.js CHANGED
@@ -172,7 +172,7 @@ function awakeLine() {
172
172
  return `<div class="awake-line ${a.on ? 'on' : ''}"><span class="dot ${a.on ? 'online' : 'offline'}"></span>${esc(t(k))}</div>`;
173
173
  }
174
174
  // The Bridgerator app's "Permission" page in ChatGPT (App ID of the store app). Owner 26.10: on first use ChatGPT
175
- // asks before most actions ("Allow low-risk"); one switch to "Allow all" stops that, like Desktop Commander.
175
+ // asks before most actions ("Allow low-risk"); one switch to "Allow all" stops that.
176
176
  const CHATGPT_PERMISSIONS = 'https://chatgpt.com/settings/plugins-settings/plugin_asdk_app_6ad78d4a3c408191a9215fd304394c4a/permissions';
177
177
  P.home = () => {
178
178
  const st = state.state || 'offline';