smooth-operator-mcp 2.4.1 → 2.4.3

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.
@@ -364,6 +364,7 @@ function linuxCandidates() {
364
364
  ["google-chrome-beta", "Google Chrome Beta", "beta"],
365
365
  ["google-chrome-unstable", "Google Chrome Dev", "dev"],
366
366
  ["brave-browser", "Brave", "stable"],
367
+ ["brave", "Brave", "stable"],
367
368
  ["microsoft-edge", "Microsoft Edge", "stable"],
368
369
  ["microsoft-edge-stable", "Microsoft Edge", "stable"],
369
370
  ["chromium", "Chromium", "stable"],
@@ -1210,7 +1211,7 @@ async function runWizard(harness, opts) {
1210
1211
  });
1211
1212
  const session = tolerantQuestion(rl);
1212
1213
  try {
1213
- ui.banner("SmoothOperator Setup", `Give ${harness} a real Chrome it can drive`, opts.version ?? "2.4.1");
1214
+ ui.banner("SmoothOperator Setup", `Give ${harness} a real Chrome it can drive`, opts.version ?? "2.4.3");
1214
1215
  ui.note(`Configuring: ${harness}`);
1215
1216
  ui.note("Answer each question, or press Enter to accept the recommended default.");
1216
1217
  ui.note(`You can re-run \`smooth-operator install ${harness}\` at any time to change these.`);
@@ -1620,9 +1621,11 @@ function readConfigFile(configPath) {
1620
1621
  if (uid !== void 0 && stats.uid !== uid) {
1621
1622
  throw new AppError("CONFIG_INSECURE", "Configuration files must be owned by the current user.");
1622
1623
  }
1623
- const mode = stats.mode & 511;
1624
- if ((mode & 63) !== 0) {
1625
- throw new AppError("CONFIG_INSECURE", "Configuration files must use owner-only permissions (for example, chmod 600).");
1624
+ if (process2.platform !== "win32") {
1625
+ const mode = stats.mode & 511;
1626
+ if ((mode & 63) !== 0) {
1627
+ throw new AppError("CONFIG_INSECURE", "Configuration files must use owner-only permissions (for example, chmod 600).");
1628
+ }
1626
1629
  }
1627
1630
  const parsed = JSON.parse(readFileSync(descriptor, "utf8"));
1628
1631
  const result = RawConfigSchema.safeParse(parsed);
@@ -2442,7 +2445,7 @@ init_errors();
2442
2445
  init_logger();
2443
2446
 
2444
2447
  // src/server/version.ts
2445
- var SERVER_VERSION = "2.4.1";
2448
+ var SERVER_VERSION = "2.4.3";
2446
2449
 
2447
2450
  // src/server/mcp.ts
2448
2451
  var EmptyInputSchema = z3.object({}).strict();
@@ -6031,6 +6034,7 @@ var BrowserService = class {
6031
6034
  throw new AppError("BROWSER_GUARD_FAILED", "The browser navigation policy could not be installed.", { retryable: true, cause: error });
6032
6035
  }
6033
6036
  }
6037
+ await this.releaseTargetGuardForPage(state.page);
6034
6038
  }
6035
6039
  async handleRequest(state, request) {
6036
6040
  let requestUrl = "";
@@ -8180,6 +8184,9 @@ function isWithinRoot(root, candidate) {
8180
8184
  const relativePath = relative2(root, candidate);
8181
8185
  return relativePath === "" || !relativePath.startsWith(`..${sep2}`) && relativePath !== ".." && !isAbsolute2(relativePath);
8182
8186
  }
8187
+ function hasNoSymlinkSegments(path) {
8188
+ return !hasSymlinkSegment(path);
8189
+ }
8183
8190
  var SecurityPolicy = class {
8184
8191
  constructor(config) {
8185
8192
  this.config = config;
@@ -8282,7 +8289,13 @@ var SecurityPolicy = class {
8282
8289
  const root = this.config.security.allowedFileRoots.find((candidate) => {
8283
8290
  const lexicalRoot = resolve3(candidate);
8284
8291
  const canonicalRoot = canonicalPath(lexicalRoot) ?? lexicalRoot;
8285
- return canonicalCandidate !== void 0 ? isWithinRoot(canonicalRoot, canonicalCandidate) : isWithinRoot(lexicalRoot, path);
8292
+ if (canonicalCandidate !== void 0 && isWithinRoot(canonicalRoot, canonicalCandidate)) {
8293
+ return true;
8294
+ }
8295
+ if (canonicalCandidate === void 0 && isWithinRoot(lexicalRoot, path)) {
8296
+ return true;
8297
+ }
8298
+ return canonicalCandidate !== void 0 && hasNoSymlinkSegments(lexicalRoot) && hasNoSymlinkSegments(path) && isWithinRoot(lexicalRoot, path);
8286
8299
  });
8287
8300
  if (!root) {
8288
8301
  throw new AppError("FILE_PATH_BLOCKED", "The file path is outside the configured file roots.");
@@ -8816,19 +8829,21 @@ async function ensurePrivateDirectory(path) {
8816
8829
  }
8817
8830
  await assertPrivateDirectoryComponent(currentPath, path);
8818
8831
  }
8819
- let info = await lstat2(target);
8832
+ const info = await lstat2(target);
8820
8833
  if (info.isSymbolicLink() || !info.isDirectory()) {
8821
8834
  throw new AppError("CONFIG_INSECURE", `The runtime path '${path}' must be a real directory, not a symbolic link or file.`);
8822
8835
  }
8823
- let permissions = Number(info.mode) & 511;
8824
- if ((permissions & 63) !== 0) {
8825
- await chmod(target, 448);
8826
- info = await lstat2(target);
8827
- await assertPrivateDirectoryComponent(target, path);
8828
- permissions = Number(info.mode) & 511;
8829
- }
8830
- if ((permissions & 63) !== 0) {
8831
- throw new AppError("CONFIG_INSECURE", `The runtime directory '${path}' must not be group/world-readable.`);
8836
+ if (process3.platform !== "win32") {
8837
+ let permissions = Number(info.mode) & 511;
8838
+ if ((permissions & 63) !== 0) {
8839
+ await chmod(target, 448);
8840
+ const tightened = await lstat2(target);
8841
+ await assertPrivateDirectoryComponent(target, path);
8842
+ permissions = Number(tightened.mode) & 511;
8843
+ }
8844
+ if ((permissions & 63) !== 0) {
8845
+ throw new AppError("CONFIG_INSECURE", `The runtime directory '${path}' must not be group/world-readable.`);
8846
+ }
8832
8847
  }
8833
8848
  }
8834
8849
  async function assertPrivateDirectoryComponent(componentPath, originalPath) {