@mrrlin-dev/mcp 0.4.22 → 0.4.23

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.
Files changed (2) hide show
  1. package/dist/bin.cjs +78 -1
  2. package/package.json +1 -1
package/dist/bin.cjs CHANGED
@@ -20815,6 +20815,66 @@ function browserServeArgs(homeDir = import_node_os.default.homedir()) {
20815
20815
  env: { PLAYWRIGHT_BROWSERS_PATH: browsersPath }
20816
20816
  };
20817
20817
  }
20818
+ function defaultListProcesses() {
20819
+ const r = (0, import_node_child_process.spawnSync)("ps", ["-ww", "-eo", "pid,ppid,command"], { encoding: "utf8" });
20820
+ const lines2 = (r.stdout ?? "").split("\n").slice(1);
20821
+ const out = [];
20822
+ for (const line of lines2) {
20823
+ const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
20824
+ if (!m || m[1] === void 0 || m[2] === void 0 || m[3] === void 0) continue;
20825
+ out.push({ pid: Number(m[1]), ppid: Number(m[2]), command: m[3] });
20826
+ }
20827
+ return out;
20828
+ }
20829
+ function defaultIsAlive(pid) {
20830
+ try {
20831
+ process.kill(pid, 0);
20832
+ return true;
20833
+ } catch (err) {
20834
+ return err.code === "EPERM";
20835
+ }
20836
+ }
20837
+ function defaultKill(pid, signal) {
20838
+ try {
20839
+ process.kill(pid, signal);
20840
+ return true;
20841
+ } catch (err) {
20842
+ if (err.code === "ESRCH") return true;
20843
+ return false;
20844
+ }
20845
+ }
20846
+ function safeKill(kill, pid, signal) {
20847
+ try {
20848
+ return kill(pid, signal);
20849
+ } catch {
20850
+ return false;
20851
+ }
20852
+ }
20853
+ function reapOrphanedBrowserProcesses(opts = {}) {
20854
+ const homeDir = opts.homeDir ?? import_node_os.default.homedir();
20855
+ const { profilePath } = browserPaths(homeDir);
20856
+ const listProcesses = opts.listProcesses ?? defaultListProcesses;
20857
+ const isAlive = opts.isAlive ?? defaultIsAlive;
20858
+ const kill = opts.kill ?? defaultKill;
20859
+ const selfPid = opts.selfPid ?? process.pid;
20860
+ const allProcesses = listProcesses();
20861
+ const candidates = allProcesses.filter(
20862
+ (p) => p.pid !== selfPid && p.command.includes("@playwright/mcp") && p.command.includes(profilePath)
20863
+ );
20864
+ const killedPids = [];
20865
+ const killedChildPids = [];
20866
+ for (const proc of candidates) {
20867
+ if (!isAlive(proc.ppid)) {
20868
+ for (const child of allProcesses) {
20869
+ if (child.ppid === proc.pid) {
20870
+ if (safeKill(kill, child.pid, "SIGKILL")) killedChildPids.push(child.pid);
20871
+ }
20872
+ }
20873
+ if (safeKill(kill, proc.pid, "SIGTERM")) killedPids.push(proc.pid);
20874
+ }
20875
+ }
20876
+ return { checked: candidates.length, killedPids, killedChildPids };
20877
+ }
20818
20878
  function externalAgentsServeArgs() {
20819
20879
  return {
20820
20880
  command: process.execPath,
@@ -41664,7 +41724,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
41664
41724
  }
41665
41725
 
41666
41726
  // src/_generated/version.ts
41667
- var PKG_VERSION = "0.4.22";
41727
+ var PKG_VERSION = "0.4.23";
41668
41728
 
41669
41729
  // src/api-base-url-migrate.ts
41670
41730
  var import_node_fs3 = __toESM(require("node:fs"), 1);
@@ -54052,6 +54112,7 @@ function buildThreadStartParams(msg, config2, desiredModel) {
54052
54112
  "- When creating a task, classify and supply the `domain` field explicitly based on the nature of the work: pass `domain:'code'` for software engineering work, `domain:'seo'` for SEO/optimization, `domain:'marketing'` for marketing/growth, or `domain:'content'` for content creation/writing \u2014 the same way you select category/subcategory. A follow-up task spawned with `parentTaskId` inherits its parent's domain automatically and requires no explicit domain.",
54053
54113
  '- When a task needs a browser (login flows, UI verification/clicks, scraping), pass `prerequisites:[{mcp:"mrrlin-browser",reason:"..."}]` to `create_execution_run`. If that run later fails into a handoff, the operator gets a one-click "Launch browser & resume" that brings up the dedicated browser as the turn starts.',
54054
54114
  "- When you actually drive a browser (calling `browser_navigate`, `browser_click`, `browser_snapshot`, `browser_type`, etc.), invoke those tools from the **`mrrlin-browser`** MCP server ONLY. Do NOT use tools from any other browser MCP (`playwright`, `chrome-devtools`, `puppeteer`, etc.) even if they are also connected on this operator's box \u2014 those use a shared/default Playwright profile that is fragile across runs. Only `mrrlin-browser` uses this project's dedicated Chromium + persistent operator profile (`~/.mrrlin/browser/profile`). If your host exposes multiple browser MCPs with overlapping tool names, prefer the `mrrlin-browser` server unconditionally.",
54115
+ "- Exactly one browser session exists for this operator, shared across every concurrent chat and task on their box \u2014 a deliberate tradeoff, not a bug, so a second concurrent user of the browser is expected, not exceptional. If a `mrrlin-browser` call fails with `Browser is already in use for ...`, that means another one of the operator's own chats or tasks is driving the browser RIGHT NOW. Do not present this as a broken tool or a mysterious error: tell the operator plainly that the browser is busy with another task, ask them to wait for it to finish (or close it), and offer to retry shortly. Never retry-loop this silently.",
54055
54116
  "",
54056
54117
  'Research playbook \u2014 when the user asks about a topic, an area, or "what do we have on X":',
54057
54118
  "- Tasks are the source of truth. Start with `list_tasks` using `q=<keyword>`; the filter is a",
@@ -54146,6 +54207,14 @@ function buildThreadStartParams(msg, config2, desiredModel) {
54146
54207
  "- A failed fetch is a signal to change tool, never a finding. NEVER report that you could not",
54147
54208
  " check, verify, or reach a publicly reachable target until you have actually tried the",
54148
54209
  " browser on it in this turn.",
54210
+ "- An internal Mrrlin route (`/deliverables/...`, `/wiki/...`, `/specs/...`, `/items/...`,",
54211
+ " `/memory/...`) is NEVER a browser target, even when it appears as a literal URL/path in a",
54212
+ " message or a screenshot. Resolve it through the typed tools instead: `list_wiki_pages` (by",
54213
+ " `folder`/`q`) or `search_wiki_pages` to find the page, then `get_wiki_page` for its content;",
54214
+ " `get_task`/`list_tasks` for `/items/<id>`. Opening these in `mrrlin-browser` is slower, can",
54215
+ " hit an operator login/consent wall the automation cannot clear on its own, and needlessly",
54216
+ " occupies the one shared browser session. Only reach for the browser on a Mrrlin route after a",
54217
+ " typed tool call for it has actually failed.",
54149
54218
  "",
54150
54219
  "Initiative contract \u2014 do the obvious next step instead of offering it:",
54151
54220
  "- When a next step is cheap, clearly wanted, and finishable in this turn, just do it and",
@@ -64620,6 +64689,14 @@ async function main() {
64620
64689
  return;
64621
64690
  }
64622
64691
  case "browser-serve": {
64692
+ const reaped = reapOrphanedBrowserProcesses();
64693
+ if (reaped.killedPids.length > 0) {
64694
+ const childNote = reaped.killedChildPids.length > 0 ? ` (plus ${reaped.killedChildPids.length} browser child process(es) they had already opened: ${reaped.killedChildPids.join(", ")})` : "";
64695
+ process.stderr.write(
64696
+ `[mrrlin-mcp browser-serve] reaped ${reaped.killedPids.length} orphaned browser process(es) with a dead parent: ${reaped.killedPids.join(", ")}${childNote}
64697
+ `
64698
+ );
64699
+ }
64623
64700
  const { command: command2, args, env } = browserServeArgs();
64624
64701
  const r = (0, import_node_child_process18.spawnSync)(command2, args, { stdio: "inherit", env: { ...process.env, ...env } });
64625
64702
  process.exitCode = r.status ?? 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/mcp",
3
- "version": "0.4.22",
3
+ "version": "0.4.23",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mrrlin-mcp": "dist/bin.cjs"