chrome-relay 0.4.0 → 0.5.1
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 +253 -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.
|
|
8
|
+
var CHROME_RELAY_VERSION = true ? "0.5.1" : "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,53 @@ 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.1": [
|
|
171
|
+
"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`.",
|
|
172
|
+
"New subcommand: `chrome-relay update` \u2014 installs the latest CLI via your package manager and prints what changed.",
|
|
173
|
+
"New subcommand: `chrome-relay release-notes --since <version>` \u2014 query the same change log without updating."
|
|
174
|
+
],
|
|
175
|
+
"0.5.0": [
|
|
176
|
+
"New tool: `chrome_hover` \u2014 `Input.dispatchMouseEvent mouseMoved` at a selector or x,y. Fires :hover, :focus-within, tooltips, dropdown openers without clicking.",
|
|
177
|
+
"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.",
|
|
178
|
+
"`chrome-relay screencast {start,stop}` CLI with default-on SHA-256 dedupe (--no-dedupe to keep raw frames) and --gif/--mp4 ffmpeg post-step.",
|
|
179
|
+
"JPEG default quality bumped 60 \u2192 80 for max precision. See docs/recording.md."
|
|
180
|
+
],
|
|
181
|
+
"0.4.0": [
|
|
182
|
+
"BREAKING: `chrome_group` repurposed for Chrome's native tab-groups (the colored, collapsible folders). Old isolation-window semantics moved to `chrome_workspace`.",
|
|
183
|
+
"New CLI: `chrome-relay workspace {create,list,close}` for parallel agent isolation (named background windows).",
|
|
184
|
+
"New CLI: `chrome-relay group {create,list,close,add,remove}` for visual tab-grouping inside a single window."
|
|
185
|
+
]
|
|
186
|
+
};
|
|
187
|
+
function compareSemver(a, b) {
|
|
188
|
+
const pa = a.split(".").map((n) => Number(n) || 0);
|
|
189
|
+
const pb = b.split(".").map((n) => Number(n) || 0);
|
|
190
|
+
for (let i = 0; i < 3; i++) {
|
|
191
|
+
const ai = pa[i] ?? 0;
|
|
192
|
+
const bi = pb[i] ?? 0;
|
|
193
|
+
if (ai > bi) return 1;
|
|
194
|
+
if (ai < bi) return -1;
|
|
195
|
+
}
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
function listReleaseNotesSince(since) {
|
|
199
|
+
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
200
|
}
|
|
153
201
|
|
|
154
202
|
// src/program.ts
|
|
@@ -181,6 +229,43 @@ Notes:
|
|
|
181
229
|
const ok = await runDoctor();
|
|
182
230
|
process.exit(ok ? 0 : 1);
|
|
183
231
|
});
|
|
232
|
+
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) => {
|
|
233
|
+
const fromVersion = CHROME_RELAY_VERSION;
|
|
234
|
+
const { spawnSync } = await import("child_process");
|
|
235
|
+
if (!opts.dryRun) {
|
|
236
|
+
const argv0 = process.argv[1] ?? "";
|
|
237
|
+
const pm = /[\\/](pnpm|\.pnpm)[\\/]/.test(argv0) ? "pnpm" : /[\\/]bun[\\/]/.test(argv0) ? "bun" : "npm";
|
|
238
|
+
const cmd = pm === "pnpm" ? ["pnpm", ["add", "-g", "chrome-relay@latest"]] : pm === "bun" ? ["bun", ["add", "-g", "chrome-relay@latest"]] : ["npm", ["install", "-g", "chrome-relay@latest"]];
|
|
239
|
+
process.stderr.write(`[chrome-relay] updating from ${fromVersion} via ${pm}...
|
|
240
|
+
`);
|
|
241
|
+
const install = spawnSync(cmd[0], cmd[1], { stdio: "inherit" });
|
|
242
|
+
if (install.status !== 0) {
|
|
243
|
+
process.stderr.write(`[chrome-relay] install failed (${pm} exited ${install.status}). Try manually: ${pm} ${cmd[1].join(" ")}
|
|
244
|
+
`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
const which = spawnSync("which", ["chrome-relay"]);
|
|
248
|
+
const newBin = which.stdout?.toString().trim();
|
|
249
|
+
if (which.status === 0 && newBin && newBin !== argv0) {
|
|
250
|
+
spawnSync(newBin, ["release-notes", "--since", fromVersion], { stdio: "inherit" });
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const changes = listReleaseNotesSince(fromVersion);
|
|
255
|
+
process.stdout.write(JSON.stringify({
|
|
256
|
+
updatedFrom: fromVersion,
|
|
257
|
+
updatedTo: CHROME_RELAY_VERSION,
|
|
258
|
+
changes
|
|
259
|
+
}, null, 2) + "\n");
|
|
260
|
+
});
|
|
261
|
+
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) => {
|
|
262
|
+
const changes = listReleaseNotesSince(opts.since);
|
|
263
|
+
process.stdout.write(JSON.stringify({
|
|
264
|
+
currentVersion: CHROME_RELAY_VERSION,
|
|
265
|
+
since: opts.since,
|
|
266
|
+
changes
|
|
267
|
+
}, null, 2) + "\n");
|
|
268
|
+
});
|
|
184
269
|
async function run(name, args) {
|
|
185
270
|
try {
|
|
186
271
|
const result = await callTool(name, args);
|
|
@@ -644,6 +729,171 @@ Notes:
|
|
|
644
729
|
if (typeof opts.limit === "number") args.limit = opts.limit;
|
|
645
730
|
await run("chrome_console", args);
|
|
646
731
|
});
|
|
732
|
+
tabOpt(
|
|
733
|
+
program.command("hover [selector]").description("Move the pointer over an element or coordinates. Fires :hover styles.").option("--x <px>", "explicit x coordinate (CSS pixels)", (v) => Number(v)).option("--y <px>", "explicit y coordinate (CSS pixels)", (v) => Number(v)).addHelpText(
|
|
734
|
+
"after",
|
|
735
|
+
`
|
|
736
|
+
|
|
737
|
+
Examples:
|
|
738
|
+
chrome-relay hover --tab 123 'button[title="Install runner"]'
|
|
739
|
+
chrome-relay hover --tab 123 --x 1327 --y 771
|
|
740
|
+
|
|
741
|
+
Use before screencast to capture hover-driven micro-states (button glow,
|
|
742
|
+
tooltip appearance, etc.) that a bare click would skip past too quickly.
|
|
743
|
+
`
|
|
744
|
+
)
|
|
745
|
+
).action(async (selector, opts) => {
|
|
746
|
+
const args = {};
|
|
747
|
+
Object.assign(args, baseArgs(opts));
|
|
748
|
+
if (selector) args.selector = selector;
|
|
749
|
+
if (typeof opts.x === "number" && typeof opts.y === "number") {
|
|
750
|
+
args.x = opts.x;
|
|
751
|
+
args.y = opts.y;
|
|
752
|
+
}
|
|
753
|
+
await run("chrome_hover", args);
|
|
754
|
+
});
|
|
755
|
+
const screencast = program.command("screencast").description("Record a tab via CDP (paint-driven). Requires an active tab.").addHelpText(
|
|
756
|
+
"after",
|
|
757
|
+
`
|
|
758
|
+
|
|
759
|
+
Examples:
|
|
760
|
+
chrome-relay screencast start --tab 123 --quality 80 --max-width 900
|
|
761
|
+
# ... drive the interaction (hover, click, etc.) ...
|
|
762
|
+
chrome-relay screencast stop --tab 123 --out /tmp/recording
|
|
763
|
+
|
|
764
|
+
# The --out path becomes a directory of frame_NNNN.jpg files. If ffmpeg
|
|
765
|
+
# is on PATH and --gif is also passed, an animated GIF is written next to
|
|
766
|
+
# the frames at /tmp/recording.gif.
|
|
767
|
+
|
|
768
|
+
Notes:
|
|
769
|
+
Frames buffer in the extension service worker. A 10-second capture at
|
|
770
|
+
default settings (jpeg q=60, ~15fps, full viewport) lands ~2-3 MB.
|
|
771
|
+
Pass --max-width to downscale and lighten the buffer.
|
|
772
|
+
Each frame is base64 JPEG; the CLI decodes them when --out is given.
|
|
773
|
+
`
|
|
774
|
+
);
|
|
775
|
+
tabOpt(
|
|
776
|
+
screencast.command("start").description("Begin screencast capture on a tab.").option("--format <fmt>", "jpeg | png (default jpeg)").option("--quality <n>", "jpeg quality 0-100 (default 80)", (v) => Number(v)).option("--max-width <px>", "downscale; aspect preserved", (v) => Number(v)).option("--max-height <px>", "downscale; aspect preserved", (v) => Number(v)).option("--every-nth <n>", "throttle: keep 1 in N frames (default 1)", (v) => Number(v))
|
|
777
|
+
).action(async (opts) => {
|
|
778
|
+
const args = { action: "start" };
|
|
779
|
+
Object.assign(args, baseArgs(opts));
|
|
780
|
+
if (opts.format) args.format = opts.format;
|
|
781
|
+
if (typeof opts.quality === "number") args.quality = opts.quality;
|
|
782
|
+
if (typeof opts.maxWidth === "number") args.maxWidth = opts.maxWidth;
|
|
783
|
+
if (typeof opts.maxHeight === "number") args.maxHeight = opts.maxHeight;
|
|
784
|
+
if (typeof opts.everyNth === "number") args.everyNthFrame = opts.everyNth;
|
|
785
|
+
await run("chrome_screencast", args);
|
|
786
|
+
});
|
|
787
|
+
tabOpt(
|
|
788
|
+
screencast.command("stop").description("Stop the screencast and emit frames (or write to disk).").option("-o, --out <dir>", "write frames as JPEGs into this directory (created if missing)").option("--gif", "after writing frames, ffmpeg them into <dir>.gif").option("--mp4", "after writing frames, ffmpeg them into <dir>.mp4").option("--fps <n>", "assumed framerate when invoking ffmpeg (default 15)", (v) => Number(v)).option("--no-dedupe", "keep raw frames; default collapses consecutive identical frames via SHA-256")
|
|
789
|
+
).action(async (opts) => {
|
|
790
|
+
const args = { action: "stop" };
|
|
791
|
+
Object.assign(args, baseArgs(opts));
|
|
792
|
+
try {
|
|
793
|
+
const result = await callTool("chrome_screencast", args);
|
|
794
|
+
if (!opts.out) {
|
|
795
|
+
const { frames, ...summary } = result;
|
|
796
|
+
process.stdout.write(JSON.stringify({ ...summary, framesOmitted: frames.length, hint: "pass --out <dir> to save" }, null, 2) + "\n");
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
const { mkdirSync, writeFileSync: writeFileSync2, renameSync, unlinkSync } = await import("fs");
|
|
800
|
+
const path2 = await import("path");
|
|
801
|
+
const { createHash } = await import("crypto");
|
|
802
|
+
mkdirSync(opts.out, { recursive: true });
|
|
803
|
+
result.frames.forEach((f, i) => {
|
|
804
|
+
const name = `frame_${String(i + 1).padStart(4, "0")}.jpg`;
|
|
805
|
+
writeFileSync2(path2.join(opts.out, name), Buffer.from(f.data, "base64"));
|
|
806
|
+
});
|
|
807
|
+
process.stdout.write(`Wrote ${result.frames.length} frames to ${opts.out}
|
|
808
|
+
`);
|
|
809
|
+
const dedupeOn = opts.dedupe !== false;
|
|
810
|
+
if (dedupeOn && result.frames.length > 1) {
|
|
811
|
+
const hashes = result.frames.map(
|
|
812
|
+
(f) => createHash("sha256").update(Buffer.from(f.data, "base64")).digest("hex")
|
|
813
|
+
);
|
|
814
|
+
const kept = [];
|
|
815
|
+
let prev = "";
|
|
816
|
+
hashes.forEach((h, i) => {
|
|
817
|
+
if (h !== prev) kept.push(i);
|
|
818
|
+
prev = h;
|
|
819
|
+
});
|
|
820
|
+
const dropped = result.frames.length - kept.length;
|
|
821
|
+
if (dropped > 0) {
|
|
822
|
+
for (let i = 0; i < result.frames.length; i++) {
|
|
823
|
+
const src = path2.join(opts.out, `frame_${String(i + 1).padStart(4, "0")}.jpg`);
|
|
824
|
+
try {
|
|
825
|
+
unlinkSync(src);
|
|
826
|
+
} catch {
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
kept.forEach((srcIdx, newIdx) => {
|
|
830
|
+
const tmp = path2.join(opts.out, `tmp_${String(newIdx + 1).padStart(4, "0")}.jpg`);
|
|
831
|
+
writeFileSync2(tmp, Buffer.from(result.frames[srcIdx].data, "base64"));
|
|
832
|
+
});
|
|
833
|
+
kept.forEach((_, newIdx) => {
|
|
834
|
+
const tmp = path2.join(opts.out, `tmp_${String(newIdx + 1).padStart(4, "0")}.jpg`);
|
|
835
|
+
const final = path2.join(opts.out, `frame_${String(newIdx + 1).padStart(4, "0")}.jpg`);
|
|
836
|
+
renameSync(tmp, final);
|
|
837
|
+
});
|
|
838
|
+
process.stdout.write(`Deduped: dropped ${dropped} identical frames, ${kept.length} remain.
|
|
839
|
+
`);
|
|
840
|
+
} else {
|
|
841
|
+
process.stdout.write(`Deduped: no consecutive duplicates found.
|
|
842
|
+
`);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
if (opts.gif || opts.mp4) {
|
|
846
|
+
const fps = typeof opts.fps === "number" ? opts.fps : 15;
|
|
847
|
+
const { spawnSync } = await import("child_process");
|
|
848
|
+
const which = spawnSync("which", ["ffmpeg"]);
|
|
849
|
+
if (which.status !== 0) {
|
|
850
|
+
process.stderr.write("[chrome-relay] ffmpeg not on PATH \u2014 skipping --gif/--mp4.\n");
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (opts.gif) {
|
|
854
|
+
const gifOut = `${opts.out.replace(/\/$/, "")}.gif`;
|
|
855
|
+
const r = spawnSync("ffmpeg", [
|
|
856
|
+
"-y",
|
|
857
|
+
"-framerate",
|
|
858
|
+
String(fps),
|
|
859
|
+
"-i",
|
|
860
|
+
path2.join(opts.out, "frame_%04d.jpg"),
|
|
861
|
+
"-vf",
|
|
862
|
+
`fps=${fps},split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse`,
|
|
863
|
+
"-loop",
|
|
864
|
+
"0",
|
|
865
|
+
gifOut
|
|
866
|
+
], { stdio: "inherit" });
|
|
867
|
+
if (r.status === 0) process.stdout.write(`Wrote ${gifOut}
|
|
868
|
+
`);
|
|
869
|
+
}
|
|
870
|
+
if (opts.mp4) {
|
|
871
|
+
const mp4Out = `${opts.out.replace(/\/$/, "")}.mp4`;
|
|
872
|
+
const r = spawnSync("ffmpeg", [
|
|
873
|
+
"-y",
|
|
874
|
+
"-framerate",
|
|
875
|
+
String(fps),
|
|
876
|
+
"-i",
|
|
877
|
+
path2.join(opts.out, "frame_%04d.jpg"),
|
|
878
|
+
"-c:v",
|
|
879
|
+
"libx264",
|
|
880
|
+
"-pix_fmt",
|
|
881
|
+
"yuv420p",
|
|
882
|
+
"-crf",
|
|
883
|
+
"20",
|
|
884
|
+
mp4Out
|
|
885
|
+
], { stdio: "inherit" });
|
|
886
|
+
if (r.status === 0) process.stdout.write(`Wrote ${mp4Out}
|
|
887
|
+
`);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
} catch (error) {
|
|
891
|
+
process.stderr.write(
|
|
892
|
+
(error instanceof Error ? error.message : String(error)) + "\n"
|
|
893
|
+
);
|
|
894
|
+
process.exit(1);
|
|
895
|
+
}
|
|
896
|
+
});
|
|
647
897
|
return program;
|
|
648
898
|
}
|
|
649
899
|
|
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.1" : "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
|
}
|