chrome-relay 0.5.19 → 0.5.21
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/cli.js +67 -15
- package/dist/index.js +1 -1
- package/dist/native-host.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1101,7 +1101,7 @@ var init_dist = __esm({
|
|
|
1101
1101
|
import { Command } from "commander";
|
|
1102
1102
|
|
|
1103
1103
|
// src/index.ts
|
|
1104
|
-
var CHROME_RELAY_VERSION = true ? "0.5.
|
|
1104
|
+
var CHROME_RELAY_VERSION = true ? "0.5.21" : "0.0.0-dev";
|
|
1105
1105
|
|
|
1106
1106
|
// src/commands/shared.ts
|
|
1107
1107
|
init_dist();
|
|
@@ -1237,6 +1237,7 @@ function isToolName(name) {
|
|
|
1237
1237
|
init_dist();
|
|
1238
1238
|
import os from "os";
|
|
1239
1239
|
import path from "path";
|
|
1240
|
+
import { spawnSync } from "child_process";
|
|
1240
1241
|
import { chmod, mkdir, readFile, stat, writeFile } from "fs/promises";
|
|
1241
1242
|
import { fileURLToPath } from "url";
|
|
1242
1243
|
var APP_DIR = path.join(os.homedir(), ".chrome-relay");
|
|
@@ -1294,16 +1295,43 @@ async function writeManifest(wrapperPath) {
|
|
|
1294
1295
|
`, "utf8");
|
|
1295
1296
|
return manifestPath;
|
|
1296
1297
|
}
|
|
1298
|
+
function killStaleNativeHosts() {
|
|
1299
|
+
if (process.platform !== "darwin" && process.platform !== "linux") {
|
|
1300
|
+
return { killed: 0 };
|
|
1301
|
+
}
|
|
1302
|
+
const ps = spawnSync("ps", ["-A", "-o", "pid=,command="], { encoding: "utf8" });
|
|
1303
|
+
if (ps.status !== 0 || !ps.stdout) return { killed: 0 };
|
|
1304
|
+
let killed = 0;
|
|
1305
|
+
for (const raw of ps.stdout.split("\n")) {
|
|
1306
|
+
const line = raw.trim();
|
|
1307
|
+
if (!line) continue;
|
|
1308
|
+
if (!line.includes("chrome-relay") || !line.includes("native-host.js")) continue;
|
|
1309
|
+
const m = line.match(/^(\d+)\s/);
|
|
1310
|
+
if (!m) continue;
|
|
1311
|
+
const pid = Number.parseInt(m[1], 10);
|
|
1312
|
+
if (pid === process.pid) continue;
|
|
1313
|
+
try {
|
|
1314
|
+
process.kill(pid, "SIGTERM");
|
|
1315
|
+
killed++;
|
|
1316
|
+
} catch {
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return { killed };
|
|
1320
|
+
}
|
|
1297
1321
|
async function runInstall() {
|
|
1298
1322
|
const distDir = getDistDir();
|
|
1299
1323
|
const hostPath = path.join(distDir, "native-host.js");
|
|
1300
1324
|
const wrapperPath = await writeWrapperScript(hostPath);
|
|
1301
1325
|
const manifestPath = await writeManifest(wrapperPath);
|
|
1326
|
+
const { killed } = killStaleNativeHosts();
|
|
1302
1327
|
console.log(`Installed Chrome Relay native host.`);
|
|
1303
1328
|
console.log(`Wrapper: ${wrapperPath}`);
|
|
1304
1329
|
console.log(`Manifest: ${manifestPath}`);
|
|
1305
1330
|
console.log(`Local bridge port: ${DEFAULT_HTTP_PORT}`);
|
|
1306
1331
|
console.log(`Allowed extension IDs: ${formatKnownExtensionIds()}`);
|
|
1332
|
+
if (killed > 0) {
|
|
1333
|
+
console.log(`Reaped ${killed} stale native-host process${killed === 1 ? "" : "es"}; Chrome will respawn from the new manifest.`);
|
|
1334
|
+
}
|
|
1307
1335
|
}
|
|
1308
1336
|
async function runDoctor() {
|
|
1309
1337
|
try {
|
|
@@ -1344,6 +1372,18 @@ async function runDoctor() {
|
|
|
1344
1372
|
|
|
1345
1373
|
// src/release-notes.ts
|
|
1346
1374
|
var RELEASE_NOTES = {
|
|
1375
|
+
"0.5.21": [
|
|
1376
|
+
"Fix: `chrome-relay update` and `chrome-relay install` now SIGTERM any running native-host.js process before exiting, and `update` re-runs `install` from the freshly-installed binary. Chrome respawns the host from the new manifest on its next native-messaging request.",
|
|
1377
|
+
"Why this matters: Chrome's native messaging keeps the host process alive for the session. Pre-0.5.21, `chrome-relay update` refreshed the on-disk package but Chrome kept routing through the OLD host. The HTTP bridge served by that old host then reported its own embedded `CHROME_RELAY_VERSION`, which falsely tripped the cli-outdated nudge against the newer extension. Users running the very command the nudge told them to run found the nudge still firing afterwards \u2014 the worst kind of UX bug.",
|
|
1378
|
+
"Best-effort and silent on failure: kill is only attempted on darwin/linux, only matches `native-host.js` paths that also contain `chrome-relay`, and ignores individual kill errors (already gone, no permission). Won't kill the running CLI itself."
|
|
1379
|
+
],
|
|
1380
|
+
"0.5.20": [
|
|
1381
|
+
"BREAKING \u2014 `chrome-relay navigate` no longer steals focus by default. Background is now the implicit behavior; agents pass `--active` when they actually want the user looking at the new tab. The whole product pitch is 'operate without stealing focus' \u2014 the default needed to match.",
|
|
1382
|
+
"`--inactive` flag removed entirely. It was the opt-in for the previous (wrong) default; now it'd be a no-op, and no-op flags are dead code. Agents that were passing `--inactive` should drop it (the behavior is now the default). Commander will reject the unknown flag \u2014 that's the right signal to update.",
|
|
1383
|
+
"Workspace `chrome.windows.create({focused: false})` was already correct from earlier work, so workspaces are unaffected.",
|
|
1384
|
+
'Click now dispatches as a pointer event. Added `pointerType: "mouse"` to all three `Input.dispatchMouseEvent` calls in the CLICK handler. Without it, CDP only fires mouse events \u2014 modern UI libs (Radix, React-Aria, Headless UI) listen on `pointerdown` and silently ignore mouse-only clicks, so dropdowns / menu triggers / select widgets stayed closed even though the click registered. Discovered while dogfooding `chrome-relay click --x N --y N` against npm\'s token-creation form; verified by completing the full token-creation flow end-to-end (radio click, combobox open, submit) via chrome-relay tools.',
|
|
1385
|
+
"Tests: navigate test updated to assert no `active` field in default request, plus a new test verifying `--active` opts in to focus."
|
|
1386
|
+
],
|
|
1347
1387
|
"0.5.19": [
|
|
1348
1388
|
"Coordinate click. `chrome-relay click --x N --y N --tab N` dispatches a trusted Input.dispatchMouseEvent at the given pixel coordinates \u2014 no selector required. The selector positional became optional; the protocol parser collapses click args into a discriminated union (`kind: 'selector'` | `kind: 'coords'`) and rejects partial coords (`--x` without `--y`) with `invalid_arguments`.",
|
|
1349
1389
|
"Intentionally NOT shipping `click-text`. Once coord-click exists, finding text + clicking is fully composable with `js` (TreeWalker \u2192 getBoundingClientRect) \u2192 `click --x/--y`. Per the CLI philosophy, that's a smart wrapper, not a primitive. The two-step recipe lives in docs/clicking-strategies.md as the documented pattern.",
|
|
@@ -1504,7 +1544,7 @@ function registerInstallUpdate(program) {
|
|
|
1504
1544
|
});
|
|
1505
1545
|
program.command("update").description("Update chrome-relay CLI to the latest version and print what changed (agent-readable JSON).").option("--dry-run", "skip the install; just show what changed since the current version").action(async (opts) => {
|
|
1506
1546
|
const fromVersion = CHROME_RELAY_VERSION;
|
|
1507
|
-
const { spawnSync } = await import("child_process");
|
|
1547
|
+
const { spawnSync: spawnSync2 } = await import("child_process");
|
|
1508
1548
|
const out = {
|
|
1509
1549
|
updatedFrom: fromVersion,
|
|
1510
1550
|
updatedTo: fromVersion,
|
|
@@ -1524,7 +1564,7 @@ function registerInstallUpdate(program) {
|
|
|
1524
1564
|
};
|
|
1525
1565
|
process.stderr.write(`[chrome-relay] updating from ${fromVersion} via ${pm}...
|
|
1526
1566
|
`);
|
|
1527
|
-
const install =
|
|
1567
|
+
const install = spawnSync2(cmd[0], cmd[1], { stdio: "inherit" });
|
|
1528
1568
|
out.install.status = install.status;
|
|
1529
1569
|
if (install.status !== 0) {
|
|
1530
1570
|
process.stderr.write(`[chrome-relay] install failed (${pm} exited ${install.status}). Try manually: ${cmd[0]} ${cmd[1].join(" ")}
|
|
@@ -1536,15 +1576,22 @@ function registerInstallUpdate(program) {
|
|
|
1536
1576
|
process.stdout.write(JSON.stringify(out, null, 2) + "\n");
|
|
1537
1577
|
process.exit(1);
|
|
1538
1578
|
}
|
|
1539
|
-
const which =
|
|
1579
|
+
const which = spawnSync2("which", ["chrome-relay"]);
|
|
1540
1580
|
const newBin = which.stdout?.toString().trim();
|
|
1541
1581
|
if (which.status === 0 && newBin) {
|
|
1542
|
-
const versionOut =
|
|
1582
|
+
const versionOut = spawnSync2(newBin, ["--version"]);
|
|
1543
1583
|
const newVersion = (versionOut.stdout?.toString() ?? "").trim();
|
|
1544
1584
|
out.binary.path = newBin;
|
|
1545
1585
|
if (newVersion && newVersion !== fromVersion) {
|
|
1546
1586
|
out.updatedTo = newVersion;
|
|
1547
|
-
const
|
|
1587
|
+
const install2 = spawnSync2(newBin, ["install"], { stdio: "inherit" });
|
|
1588
|
+
if (install2.status !== 0) {
|
|
1589
|
+
out.warnings.push({
|
|
1590
|
+
code: "install_refresh_failed",
|
|
1591
|
+
message: `Update installed the new package but \`${newBin} install\` exited ${install2.status}. Run it manually to refresh the native host manifest.`
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
const rn = spawnSync2(newBin, ["release-notes", "--since", fromVersion]);
|
|
1548
1595
|
try {
|
|
1549
1596
|
const parsed = JSON.parse(rn.stdout?.toString() ?? "");
|
|
1550
1597
|
if (Array.isArray(parsed.changes)) {
|
|
@@ -1597,14 +1644,19 @@ function registerNavigation(ctx) {
|
|
|
1597
1644
|
await run("get_windows_and_tabs", {});
|
|
1598
1645
|
});
|
|
1599
1646
|
tabOpt(
|
|
1600
|
-
program.command("navigate <url>").description("Navigate a tab to a URL. Use --tab <id> to target an existing tab.").option("--new", "open in a new tab").option("--
|
|
1647
|
+
program.command("navigate <url>").description("Navigate a tab to a URL. Use --tab <id> to target an existing tab.").option("--new", "open in a new tab").option("--active", "activate the tab after navigating (default: background \u2014 no focus theft)").addHelpText(
|
|
1601
1648
|
"after",
|
|
1602
1649
|
`
|
|
1603
1650
|
|
|
1604
1651
|
Examples:
|
|
1605
|
-
chrome-relay navigate "https://example.com"
|
|
1606
|
-
chrome-relay navigate --tab
|
|
1607
|
-
chrome-relay navigate "https://example.com" --new
|
|
1652
|
+
chrome-relay navigate "https://example.com" # navigate current tab
|
|
1653
|
+
chrome-relay navigate --tab 123 "https://example.com" # navigate an existing tab
|
|
1654
|
+
chrome-relay navigate "https://example.com" --new # open in a new background tab
|
|
1655
|
+
chrome-relay navigate "https://example.com" --new --active # open new tab AND show it to the user
|
|
1656
|
+
|
|
1657
|
+
By default chrome-relay never steals focus \u2014 navigated tabs (new or
|
|
1658
|
+
existing) stay in whatever state they're in. Pass --active when you
|
|
1659
|
+
actually want the user looking at the page.
|
|
1608
1660
|
`
|
|
1609
1661
|
)
|
|
1610
1662
|
).action(async (url, opts) => {
|
|
@@ -1618,7 +1670,7 @@ Use "chrome-relay switch ${url}" to activate that tab, or "chrome-relay navigate
|
|
|
1618
1670
|
}
|
|
1619
1671
|
const extras = { url };
|
|
1620
1672
|
if (opts.new) extras.newTab = true;
|
|
1621
|
-
if (opts.
|
|
1673
|
+
if (opts.active) extras.active = true;
|
|
1622
1674
|
await run("chrome_navigate", withBase(opts, extras));
|
|
1623
1675
|
});
|
|
1624
1676
|
program.command("switch <tabId>").description("Activate a tab by ID.").action(async (tabId) => {
|
|
@@ -1928,8 +1980,8 @@ Notes:
|
|
|
1928
1980
|
}
|
|
1929
1981
|
if (opts.gif || opts.mp4) {
|
|
1930
1982
|
const fps = typeof opts.fps === "number" ? opts.fps : 15;
|
|
1931
|
-
const { spawnSync } = await import("child_process");
|
|
1932
|
-
const which =
|
|
1983
|
+
const { spawnSync: spawnSync2 } = await import("child_process");
|
|
1984
|
+
const which = spawnSync2("which", ["ffmpeg"]);
|
|
1933
1985
|
if (which.status !== 0) {
|
|
1934
1986
|
if (opts.allowMissingFfmpeg) {
|
|
1935
1987
|
process.stderr.write("[chrome-relay] ffmpeg not on PATH \u2014 skipping --gif/--mp4 (allow-missing-ffmpeg).\n");
|
|
@@ -1950,7 +2002,7 @@ Notes:
|
|
|
1950
2002
|
}
|
|
1951
2003
|
if (opts.gif) {
|
|
1952
2004
|
const gifOut = `${opts.out.replace(/\/$/, "")}.gif`;
|
|
1953
|
-
const r =
|
|
2005
|
+
const r = spawnSync2("ffmpeg", [
|
|
1954
2006
|
"-y",
|
|
1955
2007
|
"-framerate",
|
|
1956
2008
|
String(fps),
|
|
@@ -1967,7 +2019,7 @@ Notes:
|
|
|
1967
2019
|
}
|
|
1968
2020
|
if (opts.mp4) {
|
|
1969
2021
|
const mp4Out = `${opts.out.replace(/\/$/, "")}.mp4`;
|
|
1970
|
-
const r =
|
|
2022
|
+
const r = spawnSync2("ffmpeg", [
|
|
1971
2023
|
"-y",
|
|
1972
2024
|
"-framerate",
|
|
1973
2025
|
String(fps),
|
package/dist/index.js
CHANGED
package/dist/native-host.js
CHANGED
|
@@ -56,7 +56,7 @@ function toBridgeError(unknownErr, fallbackTool) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// src/index.ts
|
|
59
|
-
var CHROME_RELAY_VERSION = true ? "0.5.
|
|
59
|
+
var CHROME_RELAY_VERSION = true ? "0.5.21" : "0.0.0-dev";
|
|
60
60
|
|
|
61
61
|
// src/release-notes.ts
|
|
62
62
|
function compareSemver(a, b) {
|