smooth-operator-mcp 2.4.2 → 2.4.4
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/smooth-operator.mjs +60 -45
- package/dist/smooth-operator.mjs.map +1 -1
- package/package.json +1 -1
package/dist/smooth-operator.mjs
CHANGED
|
@@ -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.
|
|
1214
|
+
ui.banner("SmoothOperator Setup", `Give ${harness} a real Chrome it can drive`, opts.version ?? "2.4.4");
|
|
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
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
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.
|
|
2448
|
+
var SERVER_VERSION = "2.4.4";
|
|
2446
2449
|
|
|
2447
2450
|
// src/server/mcp.ts
|
|
2448
2451
|
var EmptyInputSchema = z3.object({}).strict();
|
|
@@ -5733,17 +5736,14 @@ var BrowserService = class {
|
|
|
5733
5736
|
}
|
|
5734
5737
|
}
|
|
5735
5738
|
targetGuardForPage(page) {
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
sessionId = target._session?.()?.id();
|
|
5740
|
-
} catch {
|
|
5741
|
-
sessionId = void 0;
|
|
5739
|
+
const identity = pageTargetIdentity(page);
|
|
5740
|
+
if (identity.sessionId) {
|
|
5741
|
+
return this.targetGuardSessions.get(identity.sessionId);
|
|
5742
5742
|
}
|
|
5743
|
-
if (
|
|
5744
|
-
return
|
|
5743
|
+
if (identity.targetId) {
|
|
5744
|
+
return [...this.targetGuardSessions.values()].find((guard) => guard.targetId === identity.targetId);
|
|
5745
5745
|
}
|
|
5746
|
-
return
|
|
5746
|
+
return void 0;
|
|
5747
5747
|
}
|
|
5748
5748
|
async waitForTargetGuardDrain(page, signal) {
|
|
5749
5749
|
const guard = this.targetGuardForPage(page);
|
|
@@ -5752,18 +5752,12 @@ var BrowserService = class {
|
|
|
5752
5752
|
}
|
|
5753
5753
|
}
|
|
5754
5754
|
async releaseTargetGuardForPage(page) {
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
sessionId = void 0;
|
|
5761
|
-
}
|
|
5762
|
-
if (!sessionId) {
|
|
5763
|
-
return;
|
|
5755
|
+
const identity = pageTargetIdentity(page);
|
|
5756
|
+
const guard = this.targetGuardForPage(page);
|
|
5757
|
+
if (!identity.sessionId && !guard) return;
|
|
5758
|
+
if (identity.sessionId) {
|
|
5759
|
+
this.unguardedTargetSessions.delete(identity.sessionId);
|
|
5764
5760
|
}
|
|
5765
|
-
this.unguardedTargetSessions.delete(sessionId);
|
|
5766
|
-
const guard = this.targetGuardSessions.get(sessionId);
|
|
5767
5761
|
if (!guard) {
|
|
5768
5762
|
return;
|
|
5769
5763
|
}
|
|
@@ -5774,7 +5768,7 @@ var BrowserService = class {
|
|
|
5774
5768
|
removeCdpListener(guard.session, "Fetch.requestPaused", guard.requestPausedListener);
|
|
5775
5769
|
removeCdpListener(guard.session, "disconnected", guard.disconnectedListener);
|
|
5776
5770
|
restoreGuardSend(guard);
|
|
5777
|
-
this.targetGuardSessions.delete(
|
|
5771
|
+
this.targetGuardSessions.delete(guard.session.id());
|
|
5778
5772
|
}
|
|
5779
5773
|
async prepareTarget(target) {
|
|
5780
5774
|
const generation = this.lifecycleGeneration;
|
|
@@ -5804,13 +5798,8 @@ var BrowserService = class {
|
|
|
5804
5798
|
}
|
|
5805
5799
|
}
|
|
5806
5800
|
isUnguardedTargetPage(page) {
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
const sessionId = target._session?.()?.id();
|
|
5810
|
-
return Boolean(sessionId && this.unguardedTargetSessions.has(sessionId));
|
|
5811
|
-
} catch {
|
|
5812
|
-
return false;
|
|
5813
|
-
}
|
|
5801
|
+
const identity = pageTargetIdentity(page);
|
|
5802
|
+
return Boolean(identity.sessionId && this.unguardedTargetSessions.has(identity.sessionId));
|
|
5814
5803
|
}
|
|
5815
5804
|
async newPageState(signal) {
|
|
5816
5805
|
const generation = this.lifecycleGeneration;
|
|
@@ -7537,6 +7526,21 @@ function isPageClosed(page) {
|
|
|
7537
7526
|
return true;
|
|
7538
7527
|
}
|
|
7539
7528
|
}
|
|
7529
|
+
function pageTargetIdentity(page) {
|
|
7530
|
+
try {
|
|
7531
|
+
const target = page.target();
|
|
7532
|
+
let sessionId;
|
|
7533
|
+
try {
|
|
7534
|
+
sessionId = target._session?.()?.id();
|
|
7535
|
+
} catch {
|
|
7536
|
+
sessionId = void 0;
|
|
7537
|
+
}
|
|
7538
|
+
const targetId = typeof target._targetId === "string" ? target._targetId : void 0;
|
|
7539
|
+
return { ...sessionId ? { sessionId } : {}, ...targetId ? { targetId } : {} };
|
|
7540
|
+
} catch {
|
|
7541
|
+
return {};
|
|
7542
|
+
}
|
|
7543
|
+
}
|
|
7540
7544
|
async function rejectSymlink(path) {
|
|
7541
7545
|
try {
|
|
7542
7546
|
const file = await lstat(path);
|
|
@@ -8135,7 +8139,7 @@ function canonicalPath(path) {
|
|
|
8135
8139
|
let current = path;
|
|
8136
8140
|
while (true) {
|
|
8137
8141
|
try {
|
|
8138
|
-
const canonical = realpathSync(current);
|
|
8142
|
+
const canonical = realpathSync.native(current);
|
|
8139
8143
|
return missingSegments.reduceRight((parent, segment) => join4(parent, segment), canonical);
|
|
8140
8144
|
} catch {
|
|
8141
8145
|
try {
|
|
@@ -8181,6 +8185,9 @@ function isWithinRoot(root, candidate) {
|
|
|
8181
8185
|
const relativePath = relative2(root, candidate);
|
|
8182
8186
|
return relativePath === "" || !relativePath.startsWith(`..${sep2}`) && relativePath !== ".." && !isAbsolute2(relativePath);
|
|
8183
8187
|
}
|
|
8188
|
+
function hasNoSymlinkSegments(path) {
|
|
8189
|
+
return !hasSymlinkSegment(path);
|
|
8190
|
+
}
|
|
8184
8191
|
var SecurityPolicy = class {
|
|
8185
8192
|
constructor(config) {
|
|
8186
8193
|
this.config = config;
|
|
@@ -8283,7 +8290,13 @@ var SecurityPolicy = class {
|
|
|
8283
8290
|
const root = this.config.security.allowedFileRoots.find((candidate) => {
|
|
8284
8291
|
const lexicalRoot = resolve3(candidate);
|
|
8285
8292
|
const canonicalRoot = canonicalPath(lexicalRoot) ?? lexicalRoot;
|
|
8286
|
-
|
|
8293
|
+
if (canonicalCandidate !== void 0 && isWithinRoot(canonicalRoot, canonicalCandidate)) {
|
|
8294
|
+
return true;
|
|
8295
|
+
}
|
|
8296
|
+
if (canonicalCandidate === void 0 && isWithinRoot(lexicalRoot, path)) {
|
|
8297
|
+
return true;
|
|
8298
|
+
}
|
|
8299
|
+
return canonicalCandidate !== void 0 && hasNoSymlinkSegments(lexicalRoot) && hasNoSymlinkSegments(path) && isWithinRoot(lexicalRoot, path);
|
|
8287
8300
|
});
|
|
8288
8301
|
if (!root) {
|
|
8289
8302
|
throw new AppError("FILE_PATH_BLOCKED", "The file path is outside the configured file roots.");
|
|
@@ -8817,19 +8830,21 @@ async function ensurePrivateDirectory(path) {
|
|
|
8817
8830
|
}
|
|
8818
8831
|
await assertPrivateDirectoryComponent(currentPath, path);
|
|
8819
8832
|
}
|
|
8820
|
-
|
|
8833
|
+
const info = await lstat2(target);
|
|
8821
8834
|
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
8822
8835
|
throw new AppError("CONFIG_INSECURE", `The runtime path '${path}' must be a real directory, not a symbolic link or file.`);
|
|
8823
8836
|
}
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8837
|
+
if (process3.platform !== "win32") {
|
|
8838
|
+
let permissions = Number(info.mode) & 511;
|
|
8839
|
+
if ((permissions & 63) !== 0) {
|
|
8840
|
+
await chmod(target, 448);
|
|
8841
|
+
const tightened = await lstat2(target);
|
|
8842
|
+
await assertPrivateDirectoryComponent(target, path);
|
|
8843
|
+
permissions = Number(tightened.mode) & 511;
|
|
8844
|
+
}
|
|
8845
|
+
if ((permissions & 63) !== 0) {
|
|
8846
|
+
throw new AppError("CONFIG_INSECURE", `The runtime directory '${path}' must not be group/world-readable.`);
|
|
8847
|
+
}
|
|
8833
8848
|
}
|
|
8834
8849
|
}
|
|
8835
8850
|
async function assertPrivateDirectoryComponent(componentPath, originalPath) {
|