chrome-relay 0.5.0 → 0.5.2
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 +93 -3
- package/dist/index.js +1 -1
- package/dist/native-host.js +43 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
import { writeFileSync } from "fs";
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
|
-
var CHROME_RELAY_VERSION = true ? "0.5.
|
|
8
|
+
var CHROME_RELAY_VERSION = true ? "0.5.2" : "0.0.0-dev";
|
|
9
9
|
|
|
10
10
|
// src/install/install.ts
|
|
11
11
|
import os from "os";
|
|
@@ -130,7 +130,14 @@ async function runDoctor() {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// src/client/call.ts
|
|
133
|
-
|
|
133
|
+
var noticePrinted = false;
|
|
134
|
+
function emitNoticeOnce(notice) {
|
|
135
|
+
if (noticePrinted) return;
|
|
136
|
+
noticePrinted = true;
|
|
137
|
+
process.stderr.write(`[chrome-relay] ${notice}
|
|
138
|
+
`);
|
|
139
|
+
}
|
|
140
|
+
async function callToolWithMeta(name, args) {
|
|
134
141
|
const response = await fetch(`http://127.0.0.1:${DEFAULT_HTTP_PORT}/call`, {
|
|
135
142
|
method: "POST",
|
|
136
143
|
headers: {
|
|
@@ -143,12 +150,58 @@ async function callTool(name, args) {
|
|
|
143
150
|
});
|
|
144
151
|
const payload = await response.json().catch(() => null);
|
|
145
152
|
if (!response.ok) {
|
|
153
|
+
if (payload?.notice) emitNoticeOnce(payload.notice);
|
|
146
154
|
throw new Error(payload?.error || `Bridge request failed with ${response.status}`);
|
|
147
155
|
}
|
|
148
156
|
if (!payload?.ok) {
|
|
157
|
+
if (payload?.notice) emitNoticeOnce(payload.notice);
|
|
149
158
|
throw new Error(payload?.error || "Bridge call failed.");
|
|
150
159
|
}
|
|
151
|
-
|
|
160
|
+
if (payload.notice) emitNoticeOnce(payload.notice);
|
|
161
|
+
return { data: payload.data, notice: payload.notice };
|
|
162
|
+
}
|
|
163
|
+
async function callTool(name, args) {
|
|
164
|
+
const { data } = await callToolWithMeta(name, args);
|
|
165
|
+
return data;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/release-notes.ts
|
|
169
|
+
var RELEASE_NOTES = {
|
|
170
|
+
"0.5.2": [
|
|
171
|
+
"Strict input parsers (code-quality-hardening PR 0). Invalid console levels, network status filters, tab-group colors, and tab-id lists now throw instead of being silently dropped \u2014 an agent that asks for `errors` (typo of `error`) gets a precise error rather than all levels back.",
|
|
172
|
+
"Affected tools: chrome_console (levels), chrome_network (status), chrome_group (color, tabIds), chrome_screencast (format, action), chrome_network (action).",
|
|
173
|
+
"Parsers moved to apps/extension/src/browser/parsers.ts (pure module, no chrome runtime imports) so they're directly unit-testable. 24 new tests cover the strict paths."
|
|
174
|
+
],
|
|
175
|
+
"0.5.1": [
|
|
176
|
+
"Tool results now carry a `notice` field when the CLI is older than the connected extension \u2014 agents (or humans) get a structured nudge to run `chrome-relay update`.",
|
|
177
|
+
"New subcommand: `chrome-relay update` \u2014 installs the latest CLI via your package manager and prints what changed.",
|
|
178
|
+
"New subcommand: `chrome-relay release-notes --since <version>` \u2014 query the same change log without updating."
|
|
179
|
+
],
|
|
180
|
+
"0.5.0": [
|
|
181
|
+
"New tool: `chrome_hover` \u2014 `Input.dispatchMouseEvent mouseMoved` at a selector or x,y. Fires :hover, :focus-within, tooltips, dropdown openers without clicking.",
|
|
182
|
+
"New tool: `chrome_screencast` \u2014 paint-driven CDP recording. Catches CSS transitions, fade-ins, and animation mid-states that screenshot polling misses. Requires the tab to be active.",
|
|
183
|
+
"`chrome-relay screencast {start,stop}` CLI with default-on SHA-256 dedupe (--no-dedupe to keep raw frames) and --gif/--mp4 ffmpeg post-step.",
|
|
184
|
+
"JPEG default quality bumped 60 \u2192 80 for max precision. See docs/recording.md."
|
|
185
|
+
],
|
|
186
|
+
"0.4.0": [
|
|
187
|
+
"BREAKING: `chrome_group` repurposed for Chrome's native tab-groups (the colored, collapsible folders). Old isolation-window semantics moved to `chrome_workspace`.",
|
|
188
|
+
"New CLI: `chrome-relay workspace {create,list,close}` for parallel agent isolation (named background windows).",
|
|
189
|
+
"New CLI: `chrome-relay group {create,list,close,add,remove}` for visual tab-grouping inside a single window."
|
|
190
|
+
]
|
|
191
|
+
};
|
|
192
|
+
function compareSemver(a, b) {
|
|
193
|
+
const pa = a.split(".").map((n) => Number(n) || 0);
|
|
194
|
+
const pb = b.split(".").map((n) => Number(n) || 0);
|
|
195
|
+
for (let i = 0; i < 3; i++) {
|
|
196
|
+
const ai = pa[i] ?? 0;
|
|
197
|
+
const bi = pb[i] ?? 0;
|
|
198
|
+
if (ai > bi) return 1;
|
|
199
|
+
if (ai < bi) return -1;
|
|
200
|
+
}
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
function listReleaseNotesSince(since) {
|
|
204
|
+
return Object.keys(RELEASE_NOTES).filter((v) => compareSemver(v, since) > 0).sort((a, b) => compareSemver(a, b)).map((version) => ({ version, bullets: RELEASE_NOTES[version] }));
|
|
152
205
|
}
|
|
153
206
|
|
|
154
207
|
// src/program.ts
|
|
@@ -181,6 +234,43 @@ Notes:
|
|
|
181
234
|
const ok = await runDoctor();
|
|
182
235
|
process.exit(ok ? 0 : 1);
|
|
183
236
|
});
|
|
237
|
+
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) => {
|
|
238
|
+
const fromVersion = CHROME_RELAY_VERSION;
|
|
239
|
+
const { spawnSync } = await import("child_process");
|
|
240
|
+
if (!opts.dryRun) {
|
|
241
|
+
const argv0 = process.argv[1] ?? "";
|
|
242
|
+
const pm = /[\\/](pnpm|\.pnpm)[\\/]/.test(argv0) ? "pnpm" : /[\\/]bun[\\/]/.test(argv0) ? "bun" : "npm";
|
|
243
|
+
const cmd = pm === "pnpm" ? ["pnpm", ["add", "-g", "chrome-relay@latest"]] : pm === "bun" ? ["bun", ["add", "-g", "chrome-relay@latest"]] : ["npm", ["install", "-g", "chrome-relay@latest"]];
|
|
244
|
+
process.stderr.write(`[chrome-relay] updating from ${fromVersion} via ${pm}...
|
|
245
|
+
`);
|
|
246
|
+
const install = spawnSync(cmd[0], cmd[1], { stdio: "inherit" });
|
|
247
|
+
if (install.status !== 0) {
|
|
248
|
+
process.stderr.write(`[chrome-relay] install failed (${pm} exited ${install.status}). Try manually: ${pm} ${cmd[1].join(" ")}
|
|
249
|
+
`);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
const which = spawnSync("which", ["chrome-relay"]);
|
|
253
|
+
const newBin = which.stdout?.toString().trim();
|
|
254
|
+
if (which.status === 0 && newBin && newBin !== argv0) {
|
|
255
|
+
spawnSync(newBin, ["release-notes", "--since", fromVersion], { stdio: "inherit" });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const changes = listReleaseNotesSince(fromVersion);
|
|
260
|
+
process.stdout.write(JSON.stringify({
|
|
261
|
+
updatedFrom: fromVersion,
|
|
262
|
+
updatedTo: CHROME_RELAY_VERSION,
|
|
263
|
+
changes
|
|
264
|
+
}, null, 2) + "\n");
|
|
265
|
+
});
|
|
266
|
+
program.command("release-notes").description("Print release notes since a version (no install). JSON output for agents.").option("--since <version>", "show release notes for versions newer than this", "0.0.0").action((opts) => {
|
|
267
|
+
const changes = listReleaseNotesSince(opts.since);
|
|
268
|
+
process.stdout.write(JSON.stringify({
|
|
269
|
+
currentVersion: CHROME_RELAY_VERSION,
|
|
270
|
+
since: opts.since,
|
|
271
|
+
changes
|
|
272
|
+
}, null, 2) + "\n");
|
|
273
|
+
});
|
|
184
274
|
async function run(name, args) {
|
|
185
275
|
try {
|
|
186
276
|
const result = await callTool(name, args);
|
package/dist/index.js
CHANGED
package/dist/native-host.js
CHANGED
|
@@ -9,7 +9,29 @@ import Fastify from "fastify";
|
|
|
9
9
|
// ../protocol/dist/index.js
|
|
10
10
|
var DEFAULT_HTTP_PORT = 12122;
|
|
11
11
|
|
|
12
|
+
// src/index.ts
|
|
13
|
+
var CHROME_RELAY_VERSION = true ? "0.5.2" : "0.0.0-dev";
|
|
14
|
+
|
|
15
|
+
// src/release-notes.ts
|
|
16
|
+
function compareSemver(a, b) {
|
|
17
|
+
const pa = a.split(".").map((n) => Number(n) || 0);
|
|
18
|
+
const pb = b.split(".").map((n) => Number(n) || 0);
|
|
19
|
+
for (let i = 0; i < 3; i++) {
|
|
20
|
+
const ai = pa[i] ?? 0;
|
|
21
|
+
const bi = pb[i] ?? 0;
|
|
22
|
+
if (ai > bi) return 1;
|
|
23
|
+
if (ai < bi) return -1;
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
// src/http/server.ts
|
|
29
|
+
function buildOutdatedNotice(bridge2) {
|
|
30
|
+
const extVersion = bridge2.getExtensionVersion();
|
|
31
|
+
if (!extVersion) return void 0;
|
|
32
|
+
if (compareSemver(CHROME_RELAY_VERSION, extVersion) >= 0) return void 0;
|
|
33
|
+
return `cli-outdated: ${CHROME_RELAY_VERSION} < extension ${extVersion}; run \`chrome-relay update\``;
|
|
34
|
+
}
|
|
13
35
|
var RelayHttpServer = class {
|
|
14
36
|
constructor(bridge2, port = DEFAULT_HTTP_PORT) {
|
|
15
37
|
this.bridge = bridge2;
|
|
@@ -19,7 +41,12 @@ var RelayHttpServer = class {
|
|
|
19
41
|
port;
|
|
20
42
|
app = Fastify({ logger: false });
|
|
21
43
|
async start() {
|
|
22
|
-
this.app.get("/ping", async () => ({
|
|
44
|
+
this.app.get("/ping", async () => ({
|
|
45
|
+
ok: true,
|
|
46
|
+
port: this.port,
|
|
47
|
+
cliVersion: CHROME_RELAY_VERSION,
|
|
48
|
+
extensionVersion: this.bridge.getExtensionVersion() ?? null
|
|
49
|
+
}));
|
|
23
50
|
this.app.post("/call", async (request, reply) => {
|
|
24
51
|
if (request.headers.origin) {
|
|
25
52
|
reply.code(403).send({ error: "Browser-origin bridge requests are not accepted." });
|
|
@@ -35,12 +62,16 @@ var RelayHttpServer = class {
|
|
|
35
62
|
body.name,
|
|
36
63
|
body.args ?? {}
|
|
37
64
|
);
|
|
38
|
-
|
|
65
|
+
const notice = buildOutdatedNotice(this.bridge);
|
|
66
|
+
reply.send(notice ? { ok: true, data, notice } : { ok: true, data });
|
|
39
67
|
} catch (error) {
|
|
40
|
-
|
|
68
|
+
const notice = buildOutdatedNotice(this.bridge);
|
|
69
|
+
const body2 = {
|
|
41
70
|
ok: false,
|
|
42
71
|
error: error instanceof Error ? error.message : String(error)
|
|
43
|
-
}
|
|
72
|
+
};
|
|
73
|
+
if (notice) body2.notice = notice;
|
|
74
|
+
reply.code(500).send(body2);
|
|
44
75
|
}
|
|
45
76
|
});
|
|
46
77
|
await this.app.listen({ port: this.port, host: "127.0.0.1" });
|
|
@@ -60,6 +91,12 @@ var ExtensionBridge = class {
|
|
|
60
91
|
pending = /* @__PURE__ */ new Map();
|
|
61
92
|
readyWaiters = /* @__PURE__ */ new Set();
|
|
62
93
|
ready = false;
|
|
94
|
+
// Extension version captured from `bridge.ready`. Read by the HTTP server
|
|
95
|
+
// to compute the cli-outdated notice on each tool call.
|
|
96
|
+
extensionVersion;
|
|
97
|
+
getExtensionVersion() {
|
|
98
|
+
return this.extensionVersion;
|
|
99
|
+
}
|
|
63
100
|
handleMessage(message) {
|
|
64
101
|
if (message.type === "bridge.ready") {
|
|
65
102
|
this.handleReady(message);
|
|
@@ -79,8 +116,9 @@ var ExtensionBridge = class {
|
|
|
79
116
|
pending.resolve(true);
|
|
80
117
|
}
|
|
81
118
|
}
|
|
82
|
-
handleReady(
|
|
119
|
+
handleReady(message) {
|
|
83
120
|
this.ready = true;
|
|
121
|
+
this.extensionVersion = message.payload?.version;
|
|
84
122
|
for (const notify of this.readyWaiters) {
|
|
85
123
|
notify();
|
|
86
124
|
}
|