browsentic 0.4.12 → 0.4.14
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 +448 -92
- package/dist/daemon-main.js +56 -20
- package/extension/chrome-mv3/assets/globals-DEu7-bvL.css +2 -0
- package/extension/chrome-mv3/background.js +1 -1
- package/extension/chrome-mv3/chunks/globals-B4qbR8F8.js +177 -0
- package/extension/chrome-mv3/chunks/popup-CBHYDoU5.js +1 -0
- package/extension/chrome-mv3/chunks/sidepanel-CdI2mAPj.js +11 -0
- package/extension/chrome-mv3/content-scripts/content.js +35 -35
- package/extension/chrome-mv3/manifest.json +1 -1
- package/extension/chrome-mv3/popup.html +4 -4
- package/extension/chrome-mv3/sidepanel.html +4 -4
- package/package.json +1 -1
- package/extension/chrome-mv3/assets/globals-DfMsVuFd.css +0 -2
- package/extension/chrome-mv3/chunks/globals-B0U7yIPs.js +0 -177
- package/extension/chrome-mv3/chunks/popup-oBlyX-3-.js +0 -1
- package/extension/chrome-mv3/chunks/sidepanel-BypynwRp.js +0 -11
package/dist/cli.js
CHANGED
|
@@ -2276,7 +2276,7 @@ var require_websocket = __commonJS({
|
|
|
2276
2276
|
var http = __require("http");
|
|
2277
2277
|
var net = __require("net");
|
|
2278
2278
|
var tls = __require("tls");
|
|
2279
|
-
var { randomBytes:
|
|
2279
|
+
var { randomBytes: randomBytes4, createHash: createHash3 } = __require("crypto");
|
|
2280
2280
|
var { Duplex, Readable } = __require("stream");
|
|
2281
2281
|
var { URL: URL2 } = __require("url");
|
|
2282
2282
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2814,7 +2814,7 @@ var require_websocket = __commonJS({
|
|
|
2814
2814
|
}
|
|
2815
2815
|
}
|
|
2816
2816
|
const defaultPort = isSecure ? 443 : 80;
|
|
2817
|
-
const key =
|
|
2817
|
+
const key = randomBytes4(16).toString("base64");
|
|
2818
2818
|
const request = isSecure ? https.request : http.request;
|
|
2819
2819
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2820
2820
|
let perMessageDeflate;
|
|
@@ -6658,7 +6658,7 @@ var require_compile = __commonJS({
|
|
|
6658
6658
|
const schOrFunc = root.refs[ref];
|
|
6659
6659
|
if (schOrFunc)
|
|
6660
6660
|
return schOrFunc;
|
|
6661
|
-
let _sch =
|
|
6661
|
+
let _sch = resolve3.call(this, root, ref);
|
|
6662
6662
|
if (_sch === void 0) {
|
|
6663
6663
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
6664
6664
|
const { schemaId } = this.opts;
|
|
@@ -6685,7 +6685,7 @@ var require_compile = __commonJS({
|
|
|
6685
6685
|
function sameSchemaEnv(s1, s2) {
|
|
6686
6686
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
6687
6687
|
}
|
|
6688
|
-
function
|
|
6688
|
+
function resolve3(root, ref) {
|
|
6689
6689
|
let sch;
|
|
6690
6690
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
6691
6691
|
ref = sch;
|
|
@@ -7316,55 +7316,55 @@ var require_fast_uri = __commonJS({
|
|
|
7316
7316
|
}
|
|
7317
7317
|
return uri;
|
|
7318
7318
|
}
|
|
7319
|
-
function
|
|
7319
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
7320
7320
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
7321
7321
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
7322
7322
|
schemelessOptions.skipEscape = true;
|
|
7323
7323
|
return serialize(resolved, schemelessOptions);
|
|
7324
7324
|
}
|
|
7325
|
-
function resolveComponent(base,
|
|
7325
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
7326
7326
|
const target = {};
|
|
7327
7327
|
if (!skipNormalization) {
|
|
7328
7328
|
base = parse3(serialize(base, options), options);
|
|
7329
|
-
|
|
7329
|
+
relative3 = parse3(serialize(relative3, options), options);
|
|
7330
7330
|
}
|
|
7331
7331
|
options = options || {};
|
|
7332
|
-
if (!options.tolerant &&
|
|
7333
|
-
target.scheme =
|
|
7334
|
-
target.userinfo =
|
|
7335
|
-
target.host =
|
|
7336
|
-
target.port =
|
|
7337
|
-
target.path = removeDotSegments(
|
|
7338
|
-
target.query =
|
|
7332
|
+
if (!options.tolerant && relative3.scheme) {
|
|
7333
|
+
target.scheme = relative3.scheme;
|
|
7334
|
+
target.userinfo = relative3.userinfo;
|
|
7335
|
+
target.host = relative3.host;
|
|
7336
|
+
target.port = relative3.port;
|
|
7337
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
7338
|
+
target.query = relative3.query;
|
|
7339
7339
|
} else {
|
|
7340
|
-
if (
|
|
7341
|
-
target.userinfo =
|
|
7342
|
-
target.host =
|
|
7343
|
-
target.port =
|
|
7344
|
-
target.path = removeDotSegments(
|
|
7345
|
-
target.query =
|
|
7340
|
+
if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
|
|
7341
|
+
target.userinfo = relative3.userinfo;
|
|
7342
|
+
target.host = relative3.host;
|
|
7343
|
+
target.port = relative3.port;
|
|
7344
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
7345
|
+
target.query = relative3.query;
|
|
7346
7346
|
} else {
|
|
7347
|
-
if (!
|
|
7347
|
+
if (!relative3.path) {
|
|
7348
7348
|
target.path = base.path;
|
|
7349
|
-
if (
|
|
7350
|
-
target.query =
|
|
7349
|
+
if (relative3.query !== void 0) {
|
|
7350
|
+
target.query = relative3.query;
|
|
7351
7351
|
} else {
|
|
7352
7352
|
target.query = base.query;
|
|
7353
7353
|
}
|
|
7354
7354
|
} else {
|
|
7355
|
-
if (
|
|
7356
|
-
target.path = removeDotSegments(
|
|
7355
|
+
if (relative3.path[0] === "/") {
|
|
7356
|
+
target.path = removeDotSegments(relative3.path);
|
|
7357
7357
|
} else {
|
|
7358
7358
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
7359
|
-
target.path = "/" +
|
|
7359
|
+
target.path = "/" + relative3.path;
|
|
7360
7360
|
} else if (!base.path) {
|
|
7361
|
-
target.path =
|
|
7361
|
+
target.path = relative3.path;
|
|
7362
7362
|
} else {
|
|
7363
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
7363
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
7364
7364
|
}
|
|
7365
7365
|
target.path = removeDotSegments(target.path);
|
|
7366
7366
|
}
|
|
7367
|
-
target.query =
|
|
7367
|
+
target.query = relative3.query;
|
|
7368
7368
|
}
|
|
7369
7369
|
target.userinfo = base.userinfo;
|
|
7370
7370
|
target.host = base.host;
|
|
@@ -7372,7 +7372,7 @@ var require_fast_uri = __commonJS({
|
|
|
7372
7372
|
}
|
|
7373
7373
|
target.scheme = base.scheme;
|
|
7374
7374
|
}
|
|
7375
|
-
target.fragment =
|
|
7375
|
+
target.fragment = relative3.fragment;
|
|
7376
7376
|
return target;
|
|
7377
7377
|
}
|
|
7378
7378
|
function equal(uriA, uriB, options) {
|
|
@@ -7580,7 +7580,7 @@ var require_fast_uri = __commonJS({
|
|
|
7580
7580
|
var fastUri = {
|
|
7581
7581
|
SCHEMES,
|
|
7582
7582
|
normalize,
|
|
7583
|
-
resolve,
|
|
7583
|
+
resolve: resolve3,
|
|
7584
7584
|
resolveComponent,
|
|
7585
7585
|
equal,
|
|
7586
7586
|
serialize,
|
|
@@ -10570,7 +10570,8 @@ var require_dist = __commonJS({
|
|
|
10570
10570
|
});
|
|
10571
10571
|
|
|
10572
10572
|
// cli.ts
|
|
10573
|
-
import { readFileSync as
|
|
10573
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
10574
|
+
import { createInterface } from "readline/promises";
|
|
10574
10575
|
|
|
10575
10576
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
10576
10577
|
import process3 from "process";
|
|
@@ -26686,12 +26687,12 @@ var StdioServerTransport = class {
|
|
|
26686
26687
|
this.onclose?.();
|
|
26687
26688
|
}
|
|
26688
26689
|
send(message) {
|
|
26689
|
-
return new Promise((
|
|
26690
|
+
return new Promise((resolve3) => {
|
|
26690
26691
|
const json2 = serializeMessage(message);
|
|
26691
26692
|
if (this._stdout.write(json2)) {
|
|
26692
|
-
|
|
26693
|
+
resolve3();
|
|
26693
26694
|
} else {
|
|
26694
|
-
this._stdout.once("drain",
|
|
26695
|
+
this._stdout.once("drain", resolve3);
|
|
26695
26696
|
}
|
|
26696
26697
|
});
|
|
26697
26698
|
}
|
|
@@ -27680,7 +27681,7 @@ function hoverSequence(el, at) {
|
|
|
27680
27681
|
|
|
27681
27682
|
// ../lib/actions/page/drag-element.ts
|
|
27682
27683
|
var FRAME_MS = 16;
|
|
27683
|
-
var settle = (ms) => new Promise((
|
|
27684
|
+
var settle = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
27684
27685
|
function mouseInit(at, buttons) {
|
|
27685
27686
|
return {
|
|
27686
27687
|
clientX: at.x,
|
|
@@ -28392,8 +28393,8 @@ function renderDiagram(regions) {
|
|
|
28392
28393
|
const walk2 = (nodes, indent) => {
|
|
28393
28394
|
nodes.forEach((region, i) => {
|
|
28394
28395
|
const last = i === nodes.length - 1;
|
|
28395
|
-
const { bounds, contains } = region;
|
|
28396
|
-
const counts = ["links", "buttons", "fields"].filter((kind) =>
|
|
28396
|
+
const { bounds, contains: contains2 } = region;
|
|
28397
|
+
const counts = ["links", "buttons", "fields"].filter((kind) => contains2[kind] > 0).map((kind) => ` \xB7 ${contains2[kind]} ${kind}`).join("");
|
|
28397
28398
|
lines.push(
|
|
28398
28399
|
`${indent}${last ? "\u2514" : "\u251C"} ${regionName(region)} \xB7 ${bounds.width}\xD7${bounds.height} @ (${bounds.x},${bounds.y})${counts}`
|
|
28399
28400
|
);
|
|
@@ -28698,7 +28699,7 @@ function pickWithLens({ hint, timeoutMs }) {
|
|
|
28698
28699
|
const box = root.querySelector(".box");
|
|
28699
28700
|
const chip = root.querySelector(".chip");
|
|
28700
28701
|
let hovered = null;
|
|
28701
|
-
return new Promise((
|
|
28702
|
+
return new Promise((resolve3) => {
|
|
28702
28703
|
const dismiss = () => settle2({ cancelled: true });
|
|
28703
28704
|
dismissCurrent = dismiss;
|
|
28704
28705
|
const timer = setTimeout(() => settle2({ timedOut: true }), timeoutMs);
|
|
@@ -28766,7 +28767,7 @@ function pickWithLens({ hint, timeoutMs }) {
|
|
|
28766
28767
|
host.remove();
|
|
28767
28768
|
cursor.remove();
|
|
28768
28769
|
if (dismissCurrent === dismiss) dismissCurrent = null;
|
|
28769
|
-
|
|
28770
|
+
resolve3(outcome);
|
|
28770
28771
|
}
|
|
28771
28772
|
});
|
|
28772
28773
|
}
|
|
@@ -29201,12 +29202,12 @@ function callToolkit(fn, args, timeoutMs) {
|
|
|
29201
29202
|
throw new ActionError("No toolkit is installed in this page.", TOOLKIT_MISSING);
|
|
29202
29203
|
}
|
|
29203
29204
|
const callId = crypto.randomUUID();
|
|
29204
|
-
return new Promise((
|
|
29205
|
+
return new Promise((resolve3, reject) => {
|
|
29205
29206
|
const onResult = (event) => {
|
|
29206
29207
|
const reply = parseReply(event.detail);
|
|
29207
29208
|
if (reply?.id !== callId) return;
|
|
29208
29209
|
stop2();
|
|
29209
|
-
if (reply.ok)
|
|
29210
|
+
if (reply.ok) resolve3(reply.value ?? null);
|
|
29210
29211
|
else reject(new ActionError(reply.error ?? "The function threw.", CODE_ERROR));
|
|
29211
29212
|
};
|
|
29212
29213
|
const timer = setTimeout(() => {
|
|
@@ -29330,11 +29331,11 @@ var scrollTo = defineAction({
|
|
|
29330
29331
|
}
|
|
29331
29332
|
});
|
|
29332
29333
|
function scrollSettled() {
|
|
29333
|
-
return new Promise((
|
|
29334
|
+
return new Promise((resolve3) => {
|
|
29334
29335
|
const done = () => {
|
|
29335
29336
|
window.removeEventListener("scrollend", done);
|
|
29336
29337
|
clearTimeout(timer);
|
|
29337
|
-
|
|
29338
|
+
resolve3();
|
|
29338
29339
|
};
|
|
29339
29340
|
const timer = setTimeout(done, 1e3);
|
|
29340
29341
|
window.addEventListener("scrollend", done);
|
|
@@ -29748,7 +29749,7 @@ var LINGER_AFTER = {
|
|
|
29748
29749
|
var MAX_DURATION_MS = 12e4;
|
|
29749
29750
|
var keyDelays = (text3, base) => [...text3].map((char) => base * (LINGER_AFTER[char] ?? 1));
|
|
29750
29751
|
var budgetFor = (delays, jitter) => Math.round(delays.reduce((total, ms) => total + ms, 0) * (1 + jitter));
|
|
29751
|
-
var pause = (ms) => new Promise((
|
|
29752
|
+
var pause = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
29752
29753
|
var typeText = defineAction({
|
|
29753
29754
|
name: "page.typeText",
|
|
29754
29755
|
description: "Stream text into a field one keystroke at a time, at a human pace \u2014 a real key event per character, pauses that vary, and longer breaths after punctuation. Use page.fillInput when you just need the value in the field; use this when the page should watch someone type.",
|
|
@@ -29898,12 +29899,12 @@ var waitForElement = defineAction({
|
|
|
29898
29899
|
if (satisfied(element)) {
|
|
29899
29900
|
return { state, elapsedMs: 0, element: element ? describeElement(element) : void 0 };
|
|
29900
29901
|
}
|
|
29901
|
-
await new Promise((
|
|
29902
|
+
await new Promise((resolve3, reject) => {
|
|
29902
29903
|
const check2 = () => {
|
|
29903
29904
|
element = find();
|
|
29904
29905
|
if (satisfied(element)) {
|
|
29905
29906
|
cleanup();
|
|
29906
|
-
|
|
29907
|
+
resolve3();
|
|
29907
29908
|
}
|
|
29908
29909
|
};
|
|
29909
29910
|
const observer = new MutationObserver(check2);
|
|
@@ -30078,7 +30079,7 @@ function assertToolNamesRoundTrip(actionNames) {
|
|
|
30078
30079
|
}
|
|
30079
30080
|
|
|
30080
30081
|
// cli.ts
|
|
30081
|
-
import { basename as
|
|
30082
|
+
import { basename as basename3, join as join18 } from "path";
|
|
30082
30083
|
|
|
30083
30084
|
// agent/agent-skills.ts
|
|
30084
30085
|
import { createHash } from "crypto";
|
|
@@ -31033,6 +31034,10 @@ var failure = (code, message) => ({
|
|
|
31033
31034
|
error: { code, message }
|
|
31034
31035
|
});
|
|
31035
31036
|
|
|
31037
|
+
// guardrails/scope.ts
|
|
31038
|
+
var PROBES = ["https://one.probe.invalid/", "https://two.probe.invalid/"];
|
|
31039
|
+
var PROBE_HOSTS = PROBES.map((base) => new URL(base).hostname);
|
|
31040
|
+
|
|
31036
31041
|
// ../lib/recordings/events.ts
|
|
31037
31042
|
var MAX_RECORDING_MS = 15 * 6e4;
|
|
31038
31043
|
var WARN_AT_MS = 13 * 6e4;
|
|
@@ -31291,6 +31296,18 @@ var DEFAULT_RULES = [
|
|
|
31291
31296
|
title: "Non-http navigation",
|
|
31292
31297
|
reason: "Only http(s) URLs can be opened."
|
|
31293
31298
|
},
|
|
31299
|
+
{
|
|
31300
|
+
// A URL is a destination or it is nothing, and this is the rule that makes that true.
|
|
31301
|
+
// `//evil.com/x` used to arrive here as a null every url condition skipped, while the
|
|
31302
|
+
// page it was typed on resolved it and left the site. Classifying fixed that spelling;
|
|
31303
|
+
// refusing what still will not classify is what stops the next one, without anybody
|
|
31304
|
+
// having to think of it first.
|
|
31305
|
+
id: "unreadable-navigation",
|
|
31306
|
+
when: "unreadableNavigation",
|
|
31307
|
+
effect: "deny",
|
|
31308
|
+
title: "URL with no readable destination",
|
|
31309
|
+
reason: "That URL does not resolve to a destination Browsentic can check. Pass an absolute https:// URL."
|
|
31310
|
+
},
|
|
31294
31311
|
{
|
|
31295
31312
|
id: "off-scope-navigation",
|
|
31296
31313
|
when: "navigatesOffScope",
|
|
@@ -31655,6 +31672,38 @@ async function probeExisting() {
|
|
|
31655
31672
|
}
|
|
31656
31673
|
return null;
|
|
31657
31674
|
}
|
|
31675
|
+
async function runningDaemons() {
|
|
31676
|
+
const found = /* @__PURE__ */ new Map();
|
|
31677
|
+
for (const port of DAEMON_PORTS) {
|
|
31678
|
+
const pid = await healthyPid(port);
|
|
31679
|
+
if (pid !== null && !found.has(pid)) found.set(pid, port);
|
|
31680
|
+
}
|
|
31681
|
+
return [...found].map(([pid, port]) => ({ pid, port }));
|
|
31682
|
+
}
|
|
31683
|
+
async function stopDaemons(graceMs = 5e3) {
|
|
31684
|
+
const daemons = await runningDaemons();
|
|
31685
|
+
for (const { pid } of daemons) kill(pid, "SIGTERM");
|
|
31686
|
+
const remaining = await waitForExit(daemons, graceMs);
|
|
31687
|
+
for (const { pid } of remaining) kill(pid, "SIGKILL");
|
|
31688
|
+
const stubborn = await waitForExit(remaining, 2e3);
|
|
31689
|
+
clearLockfile();
|
|
31690
|
+
return { stopped: daemons.filter((daemon) => !stubborn.includes(daemon)), stubborn };
|
|
31691
|
+
}
|
|
31692
|
+
function kill(pid, signal) {
|
|
31693
|
+
try {
|
|
31694
|
+
process.kill(pid, signal);
|
|
31695
|
+
} catch {
|
|
31696
|
+
}
|
|
31697
|
+
}
|
|
31698
|
+
async function waitForExit(daemons, timeoutMs) {
|
|
31699
|
+
const deadline = Date.now() + timeoutMs;
|
|
31700
|
+
let alive = daemons.filter((daemon) => isRunning(daemon.pid));
|
|
31701
|
+
while (alive.length && Date.now() < deadline) {
|
|
31702
|
+
await delay(POLL_INTERVAL_MS);
|
|
31703
|
+
alive = alive.filter((daemon) => isRunning(daemon.pid));
|
|
31704
|
+
}
|
|
31705
|
+
return alive;
|
|
31706
|
+
}
|
|
31658
31707
|
async function healthyPid(port) {
|
|
31659
31708
|
try {
|
|
31660
31709
|
const response = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
@@ -31668,7 +31717,7 @@ async function healthyPid(port) {
|
|
|
31668
31717
|
}
|
|
31669
31718
|
}
|
|
31670
31719
|
function delay(ms) {
|
|
31671
|
-
return new Promise((
|
|
31720
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
31672
31721
|
}
|
|
31673
31722
|
|
|
31674
31723
|
// install.ts
|
|
@@ -31781,6 +31830,71 @@ function install(dir, force = false) {
|
|
|
31781
31830
|
return { dir, version: version2, source: packaged.source, files: sources.length, changed, alreadyCurrent: false };
|
|
31782
31831
|
}
|
|
31783
31832
|
|
|
31833
|
+
// npx.ts
|
|
31834
|
+
import { existsSync as existsSync5, readFileSync as readFileSync9, readdirSync as readdirSync5, realpathSync } from "fs";
|
|
31835
|
+
import { homedir as homedir7 } from "os";
|
|
31836
|
+
import { dirname as dirname6, join as join15, resolve, sep as sep2 } from "path";
|
|
31837
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
31838
|
+
var packageRoot = resolve(dirname6(fileURLToPath5(import.meta.url)), "..");
|
|
31839
|
+
var inNpxCache = (path) => path.split(sep2).includes("_npx");
|
|
31840
|
+
function installKind() {
|
|
31841
|
+
if (inNpxCache(packageRoot)) return "npx";
|
|
31842
|
+
if (existsSync5(join15(packageRoot, "tsup.config.ts"))) return "repo";
|
|
31843
|
+
return "global";
|
|
31844
|
+
}
|
|
31845
|
+
function real(path) {
|
|
31846
|
+
try {
|
|
31847
|
+
return realpathSync(path);
|
|
31848
|
+
} catch {
|
|
31849
|
+
return path;
|
|
31850
|
+
}
|
|
31851
|
+
}
|
|
31852
|
+
function cacheRoots() {
|
|
31853
|
+
const roots = [
|
|
31854
|
+
process.env.npm_config_cache,
|
|
31855
|
+
process.platform === "win32" ? join15(process.env.LOCALAPPDATA ?? homedir7(), "npm-cache") : null,
|
|
31856
|
+
join15(homedir7(), ".npm")
|
|
31857
|
+
].filter((root) => !!root);
|
|
31858
|
+
return [...new Set(roots.map((root) => join15(root, "_npx")))];
|
|
31859
|
+
}
|
|
31860
|
+
function readJson(path) {
|
|
31861
|
+
try {
|
|
31862
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
31863
|
+
} catch {
|
|
31864
|
+
return null;
|
|
31865
|
+
}
|
|
31866
|
+
}
|
|
31867
|
+
function npxEntries() {
|
|
31868
|
+
const own = inNpxCache(packageRoot) ? real(resolve(packageRoot, "..", "..")) : null;
|
|
31869
|
+
const scanned = cacheRoots().flatMap((root) => {
|
|
31870
|
+
try {
|
|
31871
|
+
return readdirSync5(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => real(join15(root, entry.name)));
|
|
31872
|
+
} catch {
|
|
31873
|
+
return [];
|
|
31874
|
+
}
|
|
31875
|
+
});
|
|
31876
|
+
const entries = /* @__PURE__ */ new Map();
|
|
31877
|
+
for (const dir of [...scanned, ...own ? [own] : []]) {
|
|
31878
|
+
if (entries.has(dir)) continue;
|
|
31879
|
+
const manifest = readJson(join15(dir, "node_modules", "browsentic", "package.json"));
|
|
31880
|
+
if (!manifest) continue;
|
|
31881
|
+
const requested = readJson(join15(dir, "package.json"))?._npx;
|
|
31882
|
+
entries.set(dir, {
|
|
31883
|
+
dir,
|
|
31884
|
+
version: typeof manifest.version === "string" ? manifest.version : null,
|
|
31885
|
+
packages: Array.isArray(requested?.packages) ? requested.packages : [],
|
|
31886
|
+
running: dir === own
|
|
31887
|
+
});
|
|
31888
|
+
}
|
|
31889
|
+
return [...entries.values()];
|
|
31890
|
+
}
|
|
31891
|
+
function pinnedVersion() {
|
|
31892
|
+
const own = npxEntries().find((entry) => entry.running);
|
|
31893
|
+
const spec = own?.packages.find((name) => name === "browsentic" || name.startsWith("browsentic@"));
|
|
31894
|
+
const requested = spec?.slice("browsentic@".length);
|
|
31895
|
+
return requested && /^\d+\.\d+\.\d+/.test(requested) ? requested : null;
|
|
31896
|
+
}
|
|
31897
|
+
|
|
31784
31898
|
// remote-bridge.ts
|
|
31785
31899
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
31786
31900
|
|
|
@@ -31807,11 +31921,11 @@ var RemoteBridge = class _RemoteBridge {
|
|
|
31807
31921
|
pending = /* @__PURE__ */ new Map();
|
|
31808
31922
|
manifestListeners = /* @__PURE__ */ new Set();
|
|
31809
31923
|
static connect(port, token, runId) {
|
|
31810
|
-
return new Promise((
|
|
31924
|
+
return new Promise((resolve3, reject) => {
|
|
31811
31925
|
const socket = new import_websocket.default(`ws://127.0.0.1:${port}/control`, {
|
|
31812
31926
|
headers: { authorization: `Bearer ${token}` }
|
|
31813
31927
|
});
|
|
31814
|
-
socket.once("open", () =>
|
|
31928
|
+
socket.once("open", () => resolve3(new _RemoteBridge(socket, runId)));
|
|
31815
31929
|
socket.once("error", reject);
|
|
31816
31930
|
});
|
|
31817
31931
|
}
|
|
@@ -31858,14 +31972,14 @@ var RemoteBridge = class _RemoteBridge {
|
|
|
31858
31972
|
}
|
|
31859
31973
|
request(request, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
31860
31974
|
if (this.socket.readyState !== import_websocket.default.OPEN) return Promise.resolve(null);
|
|
31861
|
-
return new Promise((
|
|
31975
|
+
return new Promise((resolve3) => {
|
|
31862
31976
|
const timer = setTimeout(() => {
|
|
31863
31977
|
this.pending.delete(request.id);
|
|
31864
|
-
|
|
31978
|
+
resolve3(null);
|
|
31865
31979
|
}, timeoutMs);
|
|
31866
31980
|
this.pending.set(request.id, (message) => {
|
|
31867
31981
|
clearTimeout(timer);
|
|
31868
|
-
|
|
31982
|
+
resolve3(message);
|
|
31869
31983
|
});
|
|
31870
31984
|
this.socket.send(JSON.stringify(request));
|
|
31871
31985
|
});
|
|
@@ -31896,6 +32010,83 @@ function invokeTimeoutFor(action, input2) {
|
|
|
31896
32010
|
return REQUEST_TIMEOUT_MS;
|
|
31897
32011
|
}
|
|
31898
32012
|
|
|
32013
|
+
// self-update.ts
|
|
32014
|
+
import { spawnSync } from "child_process";
|
|
32015
|
+
import { rmSync as rmSync5 } from "fs";
|
|
32016
|
+
var REGISTRY = process.env.BROWSENTIC_REGISTRY ?? "https://registry.npmjs.org";
|
|
32017
|
+
async function publishedVersion(timeoutMs = 4e3) {
|
|
32018
|
+
try {
|
|
32019
|
+
const response = await fetch(`${REGISTRY}/browsentic/latest`, {
|
|
32020
|
+
headers: { accept: "application/vnd.npm.install-v1+json" },
|
|
32021
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
32022
|
+
});
|
|
32023
|
+
if (!response.ok) return null;
|
|
32024
|
+
const manifest = await response.json();
|
|
32025
|
+
return typeof manifest.version === "string" ? manifest.version : null;
|
|
32026
|
+
} catch {
|
|
32027
|
+
return null;
|
|
32028
|
+
}
|
|
32029
|
+
}
|
|
32030
|
+
function isNewer(candidate, current) {
|
|
32031
|
+
const release = (version2) => version2.split("-")[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
32032
|
+
const [a, b] = [release(candidate), release(current)];
|
|
32033
|
+
for (let i = 0; i < 3; i++) if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);
|
|
32034
|
+
const [pre, currentPre] = [candidate.split("-")[1], current.split("-")[1]];
|
|
32035
|
+
if (pre === currentPre) return false;
|
|
32036
|
+
if (!pre) return true;
|
|
32037
|
+
if (!currentPre) return false;
|
|
32038
|
+
return pre > currentPre;
|
|
32039
|
+
}
|
|
32040
|
+
async function upgradeCli(current, forward) {
|
|
32041
|
+
const pinned = pinnedVersion();
|
|
32042
|
+
if (pinned) return null;
|
|
32043
|
+
const latest = await publishedVersion();
|
|
32044
|
+
if (!latest || !isNewer(latest, current)) return null;
|
|
32045
|
+
const kind = installKind();
|
|
32046
|
+
if (kind === "repo") {
|
|
32047
|
+
console.error(`
|
|
32048
|
+
! browsentic ${latest} is published; this is ${current}, from a source checkout.`);
|
|
32049
|
+
console.error(` Update it with "git pull && yarn setup".
|
|
32050
|
+
`);
|
|
32051
|
+
return null;
|
|
32052
|
+
}
|
|
32053
|
+
console.error(`
|
|
32054
|
+
\u21BB Updating the command itself, ${current} \u2192 ${latest}
|
|
32055
|
+
`);
|
|
32056
|
+
const rerun = [...forward, "--no-self-update"];
|
|
32057
|
+
if (kind === "npx") {
|
|
32058
|
+
for (const entry of npxEntries()) rmSync5(entry.dir, { recursive: true, force: true });
|
|
32059
|
+
const code = run("npx", ["-y", `browsentic@${latest}`, ...rerun]);
|
|
32060
|
+
if (code !== 0) {
|
|
32061
|
+
console.error(`
|
|
32062
|
+
That did not run, but nothing is broken: the cache is cleared, so a plain`);
|
|
32063
|
+
console.error(` \`npx browsentic ${forward[0] ?? "setup"}\` fetches ${latest} fresh next time.
|
|
32064
|
+
`);
|
|
32065
|
+
}
|
|
32066
|
+
return code;
|
|
32067
|
+
}
|
|
32068
|
+
const installed = run("npm", ["install", "-g", `browsentic@${latest}`]);
|
|
32069
|
+
if (installed !== 0) {
|
|
32070
|
+
console.error(`
|
|
32071
|
+
Could not replace the global install. Run this yourself, then try again:
|
|
32072
|
+
`);
|
|
32073
|
+
console.error(` npm install -g browsentic@${latest}
|
|
32074
|
+
`);
|
|
32075
|
+
return installed;
|
|
32076
|
+
}
|
|
32077
|
+
return run("browsentic", rerun);
|
|
32078
|
+
}
|
|
32079
|
+
function run(command2, args) {
|
|
32080
|
+
const result = spawnSync(command2, args, { stdio: "inherit", shell: process.platform === "win32" });
|
|
32081
|
+
if (result.error) {
|
|
32082
|
+
console.error(`
|
|
32083
|
+
Could not run \`${command2}\`: ${result.error.message}
|
|
32084
|
+
`);
|
|
32085
|
+
return 1;
|
|
32086
|
+
}
|
|
32087
|
+
return result.status ?? 1;
|
|
32088
|
+
}
|
|
32089
|
+
|
|
31899
32090
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
31900
32091
|
function isZ4Schema(s) {
|
|
31901
32092
|
const schema = s;
|
|
@@ -32488,7 +32679,7 @@ var Protocol = class {
|
|
|
32488
32679
|
return;
|
|
32489
32680
|
}
|
|
32490
32681
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
32491
|
-
await new Promise((
|
|
32682
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
32492
32683
|
options?.signal?.throwIfAborted();
|
|
32493
32684
|
}
|
|
32494
32685
|
} catch (error51) {
|
|
@@ -32505,7 +32696,7 @@ var Protocol = class {
|
|
|
32505
32696
|
*/
|
|
32506
32697
|
request(request, resultSchema, options) {
|
|
32507
32698
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
32508
|
-
return new Promise((
|
|
32699
|
+
return new Promise((resolve3, reject) => {
|
|
32509
32700
|
const earlyReject = (error51) => {
|
|
32510
32701
|
reject(error51);
|
|
32511
32702
|
};
|
|
@@ -32583,7 +32774,7 @@ var Protocol = class {
|
|
|
32583
32774
|
if (!parseResult.success) {
|
|
32584
32775
|
reject(parseResult.error);
|
|
32585
32776
|
} else {
|
|
32586
|
-
|
|
32777
|
+
resolve3(parseResult.data);
|
|
32587
32778
|
}
|
|
32588
32779
|
} catch (error51) {
|
|
32589
32780
|
reject(error51);
|
|
@@ -32844,12 +33035,12 @@ var Protocol = class {
|
|
|
32844
33035
|
}
|
|
32845
33036
|
} catch {
|
|
32846
33037
|
}
|
|
32847
|
-
return new Promise((
|
|
33038
|
+
return new Promise((resolve3, reject) => {
|
|
32848
33039
|
if (signal.aborted) {
|
|
32849
33040
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
32850
33041
|
return;
|
|
32851
33042
|
}
|
|
32852
|
-
const timeoutId = setTimeout(
|
|
33043
|
+
const timeoutId = setTimeout(resolve3, interval);
|
|
32853
33044
|
signal.addEventListener("abort", () => {
|
|
32854
33045
|
clearTimeout(timeoutId);
|
|
32855
33046
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -33902,10 +34093,98 @@ function text2(uri, mimeType, body) {
|
|
|
33902
34093
|
return { contents: [{ uri, mimeType, text: body }] };
|
|
33903
34094
|
}
|
|
33904
34095
|
|
|
34096
|
+
// uninstall.ts
|
|
34097
|
+
import { existsSync as existsSync6, readdirSync as readdirSync6, rmSync as rmSync6 } from "fs";
|
|
34098
|
+
import { isAbsolute as isAbsolute4, join as join17, relative as relative2 } from "path";
|
|
34099
|
+
|
|
34100
|
+
// screenshots.ts
|
|
34101
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
34102
|
+
import { chmodSync as chmodSync5, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
34103
|
+
import { homedir as homedir8 } from "os";
|
|
34104
|
+
import { basename as basename2, isAbsolute as isAbsolute3, join as join16, resolve as resolve2, sep as sep3 } from "path";
|
|
34105
|
+
function screenshotDir() {
|
|
34106
|
+
const configured = readAgentConfig().screenshotDir;
|
|
34107
|
+
if (typeof configured === "string" && configured.trim()) return expandHome3(configured.trim());
|
|
34108
|
+
return join16(homedir8(), "browsentic", "screenshot");
|
|
34109
|
+
}
|
|
34110
|
+
function expandHome3(p) {
|
|
34111
|
+
if (p === "~") return homedir8();
|
|
34112
|
+
if (p.startsWith("~/")) return join16(homedir8(), p.slice(2));
|
|
34113
|
+
return isAbsolute3(p) ? p : join16(homedir8(), p);
|
|
34114
|
+
}
|
|
34115
|
+
|
|
34116
|
+
// uninstall.ts
|
|
34117
|
+
function contains(root, path) {
|
|
34118
|
+
const inside = relative2(root, path);
|
|
34119
|
+
return !inside.startsWith("..") && !isAbsolute4(inside);
|
|
34120
|
+
}
|
|
34121
|
+
function planUninstall(options = {}) {
|
|
34122
|
+
const config2 = readAgentConfig();
|
|
34123
|
+
const extension2 = extensionDir(config2.extensionDir);
|
|
34124
|
+
const roots = [stateDir, userDir];
|
|
34125
|
+
const removals = [];
|
|
34126
|
+
if (!roots.some((root) => contains(root, extension2)) && existsSync6(extension2)) {
|
|
34127
|
+
removals.push({ label: "extension", path: extension2, holds: "the unpacked build Chrome loads" });
|
|
34128
|
+
}
|
|
34129
|
+
removals.push({
|
|
34130
|
+
label: "state",
|
|
34131
|
+
path: stateDir,
|
|
34132
|
+
holds: "pairing keys, config, approvals, logs",
|
|
34133
|
+
keep: options.keepSkills ? ["skills"] : void 0
|
|
34134
|
+
});
|
|
34135
|
+
removals.push({
|
|
34136
|
+
label: "files",
|
|
34137
|
+
path: userDir,
|
|
34138
|
+
holds: "the extension, skills, site maps, screenshots, captured downloads",
|
|
34139
|
+
keep: options.keepSkills ? ["skills"] : void 0
|
|
34140
|
+
});
|
|
34141
|
+
const elsewhere = [];
|
|
34142
|
+
const outside = (label2, path, holds) => {
|
|
34143
|
+
if (!roots.some((root) => contains(root, path)) && existsSync6(path)) elsewhere.push({ label: label2, path, holds });
|
|
34144
|
+
};
|
|
34145
|
+
outside("skills", uploadedSkillsDir(), "site maps and uploaded skills");
|
|
34146
|
+
outside("screenshots", screenshotDir(), "captures taken with save: true");
|
|
34147
|
+
outside("downloads", downloadDir(), "files captured from pages");
|
|
34148
|
+
return {
|
|
34149
|
+
removals: removals.filter((removal) => existsSync6(removal.path)),
|
|
34150
|
+
elsewhere,
|
|
34151
|
+
npx: npxEntries()
|
|
34152
|
+
};
|
|
34153
|
+
}
|
|
34154
|
+
function removeAll(removals) {
|
|
34155
|
+
return removals.map((removal) => {
|
|
34156
|
+
try {
|
|
34157
|
+
const kept = removal.keep?.length ? keepingSome(removal.path, removal.keep) : [];
|
|
34158
|
+
if (!kept.length) rmSync6(removal.path, { recursive: true, force: true });
|
|
34159
|
+
return { removal, removed: true, kept };
|
|
34160
|
+
} catch (error51) {
|
|
34161
|
+
return { removal, removed: false, kept: [], error: error51.message };
|
|
34162
|
+
}
|
|
34163
|
+
});
|
|
34164
|
+
}
|
|
34165
|
+
function keepingSome(dir, keep) {
|
|
34166
|
+
const kept = [];
|
|
34167
|
+
for (const entry of readdirSync6(dir)) {
|
|
34168
|
+
if (keep.includes(entry)) kept.push(entry);
|
|
34169
|
+
else rmSync6(join17(dir, entry), { recursive: true, force: true });
|
|
34170
|
+
}
|
|
34171
|
+
return kept;
|
|
34172
|
+
}
|
|
34173
|
+
function purgeNpxCache(entries) {
|
|
34174
|
+
return entries.map((entry) => {
|
|
34175
|
+
try {
|
|
34176
|
+
rmSync6(entry.dir, { recursive: true, force: true });
|
|
34177
|
+
return { entry, removed: true };
|
|
34178
|
+
} catch (error51) {
|
|
34179
|
+
return { entry, removed: false, error: error51.message };
|
|
34180
|
+
}
|
|
34181
|
+
});
|
|
34182
|
+
}
|
|
34183
|
+
|
|
33905
34184
|
// package.json
|
|
33906
34185
|
var package_default = {
|
|
33907
34186
|
name: "browsentic",
|
|
33908
|
-
version: "0.4.
|
|
34187
|
+
version: "0.4.14",
|
|
33909
34188
|
description: "Hand your real, logged-in browser to the AI agent you already run. Installs the browser extension, runs the local daemon, and speaks MCP.",
|
|
33910
34189
|
type: "module",
|
|
33911
34190
|
license: "MIT",
|
|
@@ -33962,7 +34241,8 @@ var package_default = {
|
|
|
33962
34241
|
var USAGE = `browsentic ${package_default.version} \u2014 hand your real browser to the agent you already run
|
|
33963
34242
|
|
|
33964
34243
|
browsentic setup install the extension, start the daemon, print a pairing code
|
|
33965
|
-
browsentic update
|
|
34244
|
+
browsentic update pull the newest build \u2014 the command itself, then the extension
|
|
34245
|
+
browsentic uninstall stop the daemon and remove everything Browsentic wrote
|
|
33966
34246
|
browsentic pair issue a one-time code to type into the extension
|
|
33967
34247
|
browsentic status daemon, extension and agent state
|
|
33968
34248
|
browsentic sessions list paired browsers
|
|
@@ -33990,7 +34270,7 @@ For MCP clients
|
|
|
33990
34270
|
browsentic mcp serve MCP over stdio \u2014 what a client runs, not what you type
|
|
33991
34271
|
claude mcp add browsentic -- browsentic mcp
|
|
33992
34272
|
`;
|
|
33993
|
-
var invokedAs =
|
|
34273
|
+
var invokedAs = basename3(process.argv[1] ?? "").replace(/\.(?:js|cjs|mjs|exe|cmd|ps1)$/i, "");
|
|
33994
34274
|
var servesBare = invokedAs === "browsentic-mcp" || !!process.env.BROWSENTIC_AGENT_RUN;
|
|
33995
34275
|
var [command] = process.argv.slice(2);
|
|
33996
34276
|
switch (command) {
|
|
@@ -34007,6 +34287,9 @@ switch (command) {
|
|
|
34007
34287
|
case "update":
|
|
34008
34288
|
await setup(["--no-pair", "--restart", ...process.argv.slice(3)]);
|
|
34009
34289
|
break;
|
|
34290
|
+
case "uninstall":
|
|
34291
|
+
await uninstall(process.argv.slice(3));
|
|
34292
|
+
break;
|
|
34010
34293
|
case "pair":
|
|
34011
34294
|
await pair();
|
|
34012
34295
|
break;
|
|
@@ -34026,7 +34309,7 @@ switch (command) {
|
|
|
34026
34309
|
await showStatus();
|
|
34027
34310
|
break;
|
|
34028
34311
|
case "stop":
|
|
34029
|
-
stop();
|
|
34312
|
+
await stop();
|
|
34030
34313
|
break;
|
|
34031
34314
|
case "restart":
|
|
34032
34315
|
await restart();
|
|
@@ -34102,7 +34385,7 @@ function printSkills() {
|
|
|
34102
34385
|
].filter(Boolean);
|
|
34103
34386
|
console.log(`${skill.name} (${tags.join(" \xB7 ")})`);
|
|
34104
34387
|
if (skill.description) console.log(` ${skill.description}`);
|
|
34105
|
-
if (skill.provenance === "generated") console.log(` ${
|
|
34388
|
+
if (skill.provenance === "generated") console.log(` ${join18(uploadedSkillsDir(), skill.name)}/`);
|
|
34106
34389
|
}
|
|
34107
34390
|
console.log(`
|
|
34108
34391
|
Read in order: ${skillDirNames().join(" \u2192 ")} (a later one shadows an earlier one by name)`);
|
|
@@ -34148,34 +34431,27 @@ async function showStatus() {
|
|
|
34148
34431
|
);
|
|
34149
34432
|
if (!status2.pairedBrowsers) console.log('\nRun "browsentic setup" to install the extension, or "browsentic pair" if it is already loaded.');
|
|
34150
34433
|
}
|
|
34151
|
-
function stop() {
|
|
34152
|
-
const
|
|
34153
|
-
|
|
34154
|
-
|
|
34155
|
-
|
|
34156
|
-
console.log(`Stopped daemon (pid ${lock.pid}).`);
|
|
34157
|
-
} catch {
|
|
34158
|
-
console.log(`Daemon (pid ${lock.pid}) was not running; clearing the lockfile.`);
|
|
34159
|
-
clearLockfile();
|
|
34434
|
+
async function stop() {
|
|
34435
|
+
const { stopped, stubborn } = await stopDaemons();
|
|
34436
|
+
for (const daemon of stopped) console.log(`Stopped daemon (pid ${daemon.pid}) on 127.0.0.1:${daemon.port}.`);
|
|
34437
|
+
for (const daemon of stubborn) {
|
|
34438
|
+
console.error(`Daemon (pid ${daemon.pid}) on 127.0.0.1:${daemon.port} would not exit \u2014 kill it by hand.`);
|
|
34160
34439
|
}
|
|
34440
|
+
if (!stopped.length && !stubborn.length) console.log("No daemon is answering; nothing to stop.");
|
|
34441
|
+
if (stubborn.length) process.exitCode = 1;
|
|
34161
34442
|
}
|
|
34162
34443
|
async function restart() {
|
|
34163
|
-
const
|
|
34164
|
-
|
|
34165
|
-
|
|
34166
|
-
|
|
34167
|
-
if (Date.now() > deadline) {
|
|
34168
|
-
console.error(`Daemon (pid ${lock.pid}) is still exiting \u2014 try again in a moment.`);
|
|
34169
|
-
process.exit(1);
|
|
34170
|
-
}
|
|
34171
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
34444
|
+
const { stubborn } = await stopDaemons();
|
|
34445
|
+
if (stubborn.length) {
|
|
34446
|
+
console.error(`Daemon (pid ${stubborn[0].pid}) is still exiting \u2014 try again in a moment.`);
|
|
34447
|
+
process.exit(1);
|
|
34172
34448
|
}
|
|
34173
34449
|
const fresh = await ensureDaemon();
|
|
34174
34450
|
console.log(`Daemon running on 127.0.0.1:${fresh.port} (pid ${fresh.pid}, v${fresh.daemonVersion}).`);
|
|
34175
34451
|
}
|
|
34176
34452
|
function showLogs() {
|
|
34177
34453
|
try {
|
|
34178
|
-
process.stdout.write(
|
|
34454
|
+
process.stdout.write(readFileSync10(logPath, "utf8"));
|
|
34179
34455
|
} catch {
|
|
34180
34456
|
console.log(`No log at ${logPath} yet.`);
|
|
34181
34457
|
}
|
|
@@ -34206,6 +34482,10 @@ async function setup(argv) {
|
|
|
34206
34482
|
const at = argv.indexOf(`--${name}`);
|
|
34207
34483
|
return at === -1 ? void 0 : argv[at + 1];
|
|
34208
34484
|
};
|
|
34485
|
+
if (!flag("no-self-update")) {
|
|
34486
|
+
const code2 = await upgradeCli(package_default.version, process.argv.slice(2));
|
|
34487
|
+
if (code2 !== null) process.exit(code2);
|
|
34488
|
+
}
|
|
34209
34489
|
const browser = valueOf("browser") ?? "chrome";
|
|
34210
34490
|
if (browser === "firefox") {
|
|
34211
34491
|
console.log(`
|
|
@@ -34244,15 +34524,11 @@ async function setup(argv) {
|
|
|
34244
34524
|
}
|
|
34245
34525
|
if (flag("restart")) await restart();
|
|
34246
34526
|
const lock = await ensureDaemon();
|
|
34247
|
-
|
|
34248
|
-
|
|
34249
|
-
|
|
34250
|
-
|
|
34251
|
-
|
|
34252
|
-
alreadyPaired = sessions.some((session) => session.origin.startsWith("chrome-extension://"));
|
|
34253
|
-
if (!alreadyPaired) code = (await bridge.pair()).code;
|
|
34254
|
-
await bridge.close();
|
|
34255
|
-
}
|
|
34527
|
+
const bridge = await RemoteBridge.connect(lock.port, lock.token);
|
|
34528
|
+
const sessions = await bridge.sessions();
|
|
34529
|
+
const alreadyPaired = sessions.some((session) => session.origin.startsWith("chrome-extension://"));
|
|
34530
|
+
const code = alreadyPaired || flag("no-pair") ? void 0 : (await bridge.pair()).code;
|
|
34531
|
+
await bridge.close();
|
|
34256
34532
|
if (json2) {
|
|
34257
34533
|
console.log(
|
|
34258
34534
|
JSON.stringify(
|
|
@@ -34292,11 +34568,91 @@ async function setup(argv) {
|
|
|
34292
34568
|
console.log(` ${groupCode(code)}
|
|
34293
34569
|
`);
|
|
34294
34570
|
console.log(` Single use, expires in 10 minutes. Need another? "browsentic pair"
|
|
34571
|
+
`);
|
|
34572
|
+
} else {
|
|
34573
|
+
console.log(` 2. Run "browsentic pair" and paste the code into the Browsentic popup.
|
|
34295
34574
|
`);
|
|
34296
34575
|
}
|
|
34297
34576
|
console.log(` Then open the side panel and say what you want.
|
|
34298
34577
|
`);
|
|
34299
34578
|
}
|
|
34579
|
+
async function uninstall(argv) {
|
|
34580
|
+
const flag = (name) => argv.includes(`--${name}`);
|
|
34581
|
+
const plan = planUninstall({ keepSkills: flag("keep-skills") });
|
|
34582
|
+
const daemons = await runningDaemons();
|
|
34583
|
+
const kind = installKind();
|
|
34584
|
+
console.log(`
|
|
34585
|
+
Browsentic ${package_default.version} \u2014 uninstall
|
|
34586
|
+
`);
|
|
34587
|
+
if (!daemons.length && !plan.removals.length && !plan.npx.length) {
|
|
34588
|
+
console.log(" Nothing to remove; this machine is already clean.\n");
|
|
34589
|
+
return;
|
|
34590
|
+
}
|
|
34591
|
+
console.log(" This removes:\n");
|
|
34592
|
+
for (const daemon of daemons) console.log(` daemon 127.0.0.1:${daemon.port}, pid ${daemon.pid}`);
|
|
34593
|
+
for (const removal of plan.removals) {
|
|
34594
|
+
console.log(` ${removal.label.padEnd(11)} ${removal.path}`);
|
|
34595
|
+
console.log(` ${removal.holds}${removal.keep ? " \u2014 keeping skills/" : ""}`);
|
|
34596
|
+
}
|
|
34597
|
+
for (const entry of plan.npx) {
|
|
34598
|
+
const running = entry.running ? ", the copy running right now" : "";
|
|
34599
|
+
console.log(` npx cache ${entry.dir}`);
|
|
34600
|
+
console.log(` browsentic ${entry.version ?? "unknown"}${running}`);
|
|
34601
|
+
}
|
|
34602
|
+
if (plan.elsewhere.length) {
|
|
34603
|
+
console.log("\n Left alone \u2014 configuration put these outside the two roots:\n");
|
|
34604
|
+
for (const dir of plan.elsewhere) console.log(` ${dir.label.padEnd(11)} ${dir.path} (${dir.holds})`);
|
|
34605
|
+
}
|
|
34606
|
+
console.log("\n You will have to remove these yourself:\n");
|
|
34607
|
+
console.log(" the Browsentic card at chrome://extensions. Do that first \u2014 remove the");
|
|
34608
|
+
console.log(" directory while the card is loaded and the browser is left holding a");
|
|
34609
|
+
console.log(" broken one. It is also what clears recordings and held secrets, which");
|
|
34610
|
+
console.log(" live in extension storage rather than on disk.");
|
|
34611
|
+
if (kind === "global") console.log("\n the command itself: npm rm -g browsentic");
|
|
34612
|
+
if (kind === "repo") console.log("\n the global link: yarn daemon:unlink");
|
|
34613
|
+
console.log("\n the entry in your MCP client, e.g. claude mcp remove browsentic");
|
|
34614
|
+
if (flag("dry-run")) {
|
|
34615
|
+
console.log("\n --dry-run: nothing was removed.\n");
|
|
34616
|
+
return;
|
|
34617
|
+
}
|
|
34618
|
+
if (!flag("yes") && !argv.includes("-y")) {
|
|
34619
|
+
if (!process.stdin.isTTY) {
|
|
34620
|
+
console.error("\n Nothing was removed \u2014 this is not a terminal, so there is nobody to ask.");
|
|
34621
|
+
console.error(" Re-run it with --yes.\n");
|
|
34622
|
+
process.exit(1);
|
|
34623
|
+
}
|
|
34624
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
34625
|
+
const answer = await rl.question("\n Remove all of it? [y/N] ");
|
|
34626
|
+
rl.close();
|
|
34627
|
+
if (!/^y(es)?$/i.test(answer.trim())) return console.log("\n Nothing was removed.\n");
|
|
34628
|
+
}
|
|
34629
|
+
console.log();
|
|
34630
|
+
const lock = await probeExisting();
|
|
34631
|
+
if (lock) {
|
|
34632
|
+
try {
|
|
34633
|
+
const bridge = await RemoteBridge.connect(lock.port, lock.token);
|
|
34634
|
+
const revoked = await bridge.revoke();
|
|
34635
|
+
await bridge.close();
|
|
34636
|
+
if (revoked) console.log(` \u2713 Unpaired ${revoked} browser${revoked === 1 ? "" : "s"}`);
|
|
34637
|
+
} catch {
|
|
34638
|
+
console.log(" \xB7 Unpair skipped, the daemon did not answer");
|
|
34639
|
+
}
|
|
34640
|
+
}
|
|
34641
|
+
const { stopped, stubborn } = await stopDaemons();
|
|
34642
|
+
if (stopped.length) console.log(` \u2713 Daemon stopped (pid ${stopped.map((daemon) => daemon.pid).join(", ")})`);
|
|
34643
|
+
for (const daemon of stubborn) console.log(` \u2717 Daemon pid ${daemon.pid} would not exit \u2014 kill it by hand`);
|
|
34644
|
+
for (const outcome of removeAll(plan.removals)) {
|
|
34645
|
+
if (!outcome.removed) console.log(` \u2717 ${outcome.removal.path} \u2014 ${outcome.error}`);
|
|
34646
|
+
else if (outcome.kept.length) console.log(` \u2713 Emptied ${outcome.removal.path} \u2014 kept ${outcome.kept.join(", ")}/`);
|
|
34647
|
+
else console.log(` \u2713 Removed ${outcome.removal.path}`);
|
|
34648
|
+
}
|
|
34649
|
+
for (const purge of purgeNpxCache(plan.npx)) {
|
|
34650
|
+
if (purge.removed) console.log(` \u2713 Cleared ${purge.entry.dir}`);
|
|
34651
|
+
else console.log(` \u2717 ${purge.entry.dir} \u2014 ${purge.error}`);
|
|
34652
|
+
}
|
|
34653
|
+
console.log("\n Done. Remove the card at chrome://extensions if you have not.\n");
|
|
34654
|
+
if (stubborn.length) process.exitCode = 1;
|
|
34655
|
+
}
|
|
34300
34656
|
async function showSessions() {
|
|
34301
34657
|
const bridge = await connect();
|
|
34302
34658
|
const sessions = await bridge.sessions();
|