premanmcp 1.1.2 → 1.1.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.
package/bin/eval.js CHANGED
@@ -1057,7 +1057,9 @@ async function runWithToken(
1057
1057
  // printed a URL and waited out the full timeout for a window it never asked
1058
1058
  // anything to open.
1059
1059
  const resolved = resolveEvalSurface(args, { runId: runs[0].id });
1060
- const surface = resolved ? { ...resolved, open: () => openUrl(resolved.href) } : null;
1060
+ const surface = resolved
1061
+ ? { ...resolved, open: () => openUrl(resolved.href, { app: resolved.app }) }
1062
+ : null;
1061
1063
  if (surface?.webUrl) say(`Watch it at ${surface.webUrl}`);
1062
1064
  say("");
1063
1065
 
package/bin/link.js CHANGED
@@ -14,7 +14,7 @@
14
14
  * a `git push` is exactly the behaviour that gets a tool uninstalled.
15
15
  */
16
16
 
17
- import { desktopAppInstalled, installedDesktopVersion } from "./desktop.js";
17
+ import { desktopAppInstalled, installedAppPath, installedDesktopVersion } from "./desktop.js";
18
18
  import { DEFAULT_FRONTEND, frontendUrl, pairedFrontendUrl } from "./shared.js";
19
19
 
20
20
  export { installedDesktopVersion };
@@ -158,6 +158,10 @@ export function resolveEvalSurface(args, { runId = "", destination = "/Applicati
158
158
  * safe when routing is not: no build can misread it, and no dashboard can be
159
159
  * the wrong one, because none is named. It cannot land you on the run, and the
160
160
  * caller says so rather than implying otherwise.
161
+ *
162
+ * Which is why it now ranks below a browser link rather than above one: "cannot
163
+ * land you on the run" is a real cost, and it is only worth paying when the
164
+ * alternative is nothing at all. See the ordering in `resolveSurface`.
161
165
  */
162
166
  function resolveSurface(
163
167
  args,
@@ -167,23 +171,32 @@ function resolveSurface(
167
171
  const base = requirePairing ? pairedFrontendUrl(args) : frontendUrl(args);
168
172
  const webUrl = base ? `${base}${route}` : "";
169
173
  const installed = desktopAppInstalled(destination);
174
+ const app = installed ? installedAppPath(destination) : "";
170
175
 
171
176
  if (base === DEFAULT_FRONTEND && installed && desktopSupportsRouting(destination)) {
172
177
  return {
173
178
  href: `preman://open?source=${source}&route=${encodeURIComponent(route)}`,
174
179
  webUrl,
180
+ app,
175
181
  surface: "desktop",
176
182
  };
177
183
  }
178
184
 
179
- // Deliberately ahead of the "no dashboard is known" return below: not knowing
180
- // which browser page holds the run says nothing about whether the app is
181
- // installed, and raising it is still the most useful thing available.
182
- if (preferApp && installed) {
183
- return { href: `preman://open?source=${source}`, webUrl, surface: "desktop-raise" };
185
+ // A raise is now the last resort rather than the second choice, and only when
186
+ // there is no page to send anyone to instead.
187
+ //
188
+ // It was ahead of the web branch on the reasoning that somebody watching an
189
+ // eval is already sitting in front of the app. True, but it trades a link
190
+ // that lands on the run for a window that lands wherever it was left --
191
+ // which, for an app too old to route, is the one thing it can never be. What
192
+ // that bought in practice was a stale view and the question "where is my
193
+ // run", while the browser would have shown it. Raising still beats nothing,
194
+ // so it survives for the case where no dashboard is known at all.
195
+ if (!base) {
196
+ return preferApp && installed
197
+ ? { href: `preman://open?source=${source}`, webUrl, app, surface: "desktop-raise" }
198
+ : null;
184
199
  }
185
-
186
- if (!base) return null;
187
200
  if (base !== DEFAULT_FRONTEND) return { href: webUrl, webUrl, surface: "web-local" };
188
201
  return { href: webUrl, webUrl, surface: "web" };
189
202
  }
package/bin/runner.js CHANGED
@@ -467,7 +467,7 @@ export async function runLeasedEval(args, state, job, { log = () => {}, headless
467
467
  log,
468
468
  headless,
469
469
  projectPath: state.project_path || process.cwd(),
470
- surface: { ...surface, open: () => openUrl(surface.href) },
470
+ surface: { ...surface, open: () => openUrl(surface.href, { app: surface.app }) },
471
471
  });
472
472
  if (result.reason === "lease_lost") log(`eval ${job.id} was taken over or cancelled`);
473
473
  else log(`eval ${job.id} finished: ${result.ok ? "ok" : "failed"}`);
package/bin/shared.js CHANGED
@@ -340,15 +340,25 @@ export function pairedFrontendUrl(args) {
340
340
  * ever a convenience — a suite that exercises these flows should not be able to
341
341
  * throw tabs at whoever is running it.
342
342
  */
343
- export function openUrl(url) {
343
+ export function openUrl(url, { app = "" } = {}) {
344
344
  const optOut = (process.env.PREMAN_NO_BROWSER || "").trim().toLowerCase();
345
345
  if (optOut && !["0", "false", "no"].includes(optOut)) return false;
346
346
  if (!process.stdout.isTTY) return false;
347
347
 
348
348
  const opener =
349
349
  process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
350
+ // `app` names the bundle that must receive the URL, and is the difference
351
+ // between "a PreMan window" and "the PreMan window we checked". A custom
352
+ // scheme is claimed, not owned: every build that ever ran on this machine
353
+ // registers `preman:`, including one in the Trash and any dev copy started
354
+ // from a checkout, and LaunchServices picks among them by its own rules. So
355
+ // the CLI inspected the app in /Applications, decided from its version what
356
+ // the link could do, and then handed the URL to whichever claimant won --
357
+ // which on this machine was a dev build pointed at a local backend that was
358
+ // not running. `open -a` addresses the bundle directly and skips the auction.
359
+ const argv = app && process.platform === "darwin" ? ["-a", app, url] : [url];
350
360
  try {
351
- const child = spawn(opener, [url], {
361
+ const child = spawn(opener, argv, {
352
362
  stdio: "ignore",
353
363
  detached: true,
354
364
  shell: process.platform === "win32",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "premanmcp",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "PreMan CLI and stdio proxy for PreMan's hosted MCP server",
5
5
  "type": "module",
6
6
  "bin": {