browsentic 0.4.6 → 0.4.9
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 +202 -19
- package/dist/daemon-main.js +242 -23
- package/extension/chrome-mv3/assets/globals-wMwvQr8A.css +2 -0
- package/extension/chrome-mv3/background.js +70 -19
- package/extension/chrome-mv3/chunks/globals-C7iS-PkW.js +177 -0
- package/extension/chrome-mv3/chunks/popup-B_-2m0P9.js +1 -0
- package/extension/chrome-mv3/chunks/sidepanel-Bv8WKKVC.js +11 -0
- package/extension/chrome-mv3/content-scripts/content.js +22 -22
- package/extension/chrome-mv3/manifest.json +1 -1
- package/extension/chrome-mv3/popup.html +3 -3
- package/extension/chrome-mv3/sidepanel.html +3 -3
- package/package.json +1 -1
- package/skills/a-eye.md +3 -3
- package/skills/page-scripting.md +50 -0
- package/extension/chrome-mv3/assets/globals-BOLwDXwQ.css +0 -2
- package/extension/chrome-mv3/chunks/globals-DIBegMWk.js +0 -137
- package/extension/chrome-mv3/chunks/popup-yL0itqnF.js +0 -1
- package/extension/chrome-mv3/chunks/sidepanel-WyGwqwXh.js +0 -11
package/dist/cli.js
CHANGED
|
@@ -28508,6 +28508,28 @@ var hoverElement = defineAction({
|
|
|
28508
28508
|
}
|
|
28509
28509
|
});
|
|
28510
28510
|
|
|
28511
|
+
// ../lib/actions/page/inject-code.ts
|
|
28512
|
+
var MAX_CODE_LENGTH = 32768;
|
|
28513
|
+
var injectCode = defineAction({
|
|
28514
|
+
name: "page.injectCode",
|
|
28515
|
+
description: "Install a small toolkit of JavaScript functions into the page, to be called later with page.runCode. Reach for it only when the ordinary tools are the wrong shape: a step sequence you are about to repeat three or more times with different inputs (create 20 tags, delete every row), or a capability no tool covers (seek a video, read a canvas, drive a bespoke editor API). The user reviews and approves the code before it runs \u2014 one approval covers every later page.runCode call and survives page reloads, so batch work needs no further prompts. The toolkit is bound to the tab and origin it was approved on; navigating to another site voids it. Installing goes through Chrome\u2019s debugger, so the browser shows a \u201CBrowsentic is debugging this browser\u201D bar for the moment it takes, it cannot install on a tab that has DevTools open, and it is unavailable on Firefox \u2014 the calls afterwards are cheap and show nothing. For a one-off click or fill, the ordinary tools are always the better choice.",
|
|
28516
|
+
input: external_exports.object({
|
|
28517
|
+
purpose: external_exports.string().min(1).max(200).describe(
|
|
28518
|
+
"One plain sentence saying what this toolkit does and why it is needed \u2014 shown to the user on the approval prompt, so write it for them, not for the page."
|
|
28519
|
+
),
|
|
28520
|
+
code: external_exports.string().min(1).max(MAX_CODE_LENGTH).describe(
|
|
28521
|
+
"JavaScript source that assigns each entry point onto the provided `tools` object: `tools.addTag = async (name) => {\u2026}`. It runs once in the page\u2019s main world after the user approves it, so it can use the page\u2019s DOM, globals and same-origin fetch, but nothing of the extension. Keep it pure of data: anything that varies per call arrives as arguments through page.runCode, and secrets never belong in it. Async functions are awaited; return values must be JSON-serializable."
|
|
28522
|
+
),
|
|
28523
|
+
call: external_exports.object({
|
|
28524
|
+
function: external_exports.string().min(1).describe("Name of a function the code assigns onto `tools`."),
|
|
28525
|
+
args: external_exports.array(external_exports.unknown()).default([]).describe("Arguments for that first call, JSON values only.")
|
|
28526
|
+
}).optional().describe("Call one of the new functions immediately after installing, saving a round trip when the first use is already known.")
|
|
28527
|
+
}),
|
|
28528
|
+
execute() {
|
|
28529
|
+
throw new ActionError("page.injectCode is resolved by the Browsentic extension, not in the page", "UNSUPPORTED");
|
|
28530
|
+
}
|
|
28531
|
+
});
|
|
28532
|
+
|
|
28511
28533
|
// ../lib/actions/page/list-downloads.ts
|
|
28512
28534
|
var listDownloads = defineAction({
|
|
28513
28535
|
name: "page.listDownloads",
|
|
@@ -28636,12 +28658,9 @@ var CURSOR_PATHS = [
|
|
|
28636
28658
|
];
|
|
28637
28659
|
var CURSOR2 = `url("data:image/svg+xml,${encodeURIComponent(cursorSvg())}") 14 14, crosshair`;
|
|
28638
28660
|
var DEFAULT_HINT = "Click the element you mean";
|
|
28639
|
-
var
|
|
28640
|
-
function lensIsUp() {
|
|
28641
|
-
return picking;
|
|
28642
|
-
}
|
|
28661
|
+
var dismissCurrent = null;
|
|
28643
28662
|
function pickWithLens({ hint, timeoutMs }) {
|
|
28644
|
-
|
|
28663
|
+
dismissCurrent?.();
|
|
28645
28664
|
const host = document.createElement("div");
|
|
28646
28665
|
host.id = HOST_ID;
|
|
28647
28666
|
host.style.cssText = "all: initial; position: static;";
|
|
@@ -28656,6 +28675,8 @@ function pickWithLens({ hint, timeoutMs }) {
|
|
|
28656
28675
|
const chip = root.querySelector(".chip");
|
|
28657
28676
|
let hovered = null;
|
|
28658
28677
|
return new Promise((resolve) => {
|
|
28678
|
+
const dismiss = () => settle2({ cancelled: true });
|
|
28679
|
+
dismissCurrent = dismiss;
|
|
28659
28680
|
const timer = setTimeout(() => settle2({ timedOut: true }), timeoutMs);
|
|
28660
28681
|
const mute = (event) => {
|
|
28661
28682
|
event.stopPropagation();
|
|
@@ -28720,7 +28741,7 @@ function pickWithLens({ hint, timeoutMs }) {
|
|
|
28720
28741
|
}
|
|
28721
28742
|
host.remove();
|
|
28722
28743
|
cursor.remove();
|
|
28723
|
-
|
|
28744
|
+
if (dismissCurrent === dismiss) dismissCurrent = null;
|
|
28724
28745
|
resolve(outcome);
|
|
28725
28746
|
}
|
|
28726
28747
|
});
|
|
@@ -28799,18 +28820,16 @@ function styles() {
|
|
|
28799
28820
|
|
|
28800
28821
|
// ../lib/actions/page/pick-element.ts
|
|
28801
28822
|
var MAX_CONTENT = 2e4;
|
|
28823
|
+
var PICK_DEFAULT_TIMEOUT_MS = 6e4;
|
|
28802
28824
|
var pickElement = defineAction({
|
|
28803
28825
|
name: "page.pickElement",
|
|
28804
|
-
description: "Ask the user to point at an element \u2014 A-Eye. Their cursor becomes a lens, whatever they hover is outlined, and the element they click comes back with its selector, its role
|
|
28826
|
+
description: "Ask the user to point at an element \u2014 A-Eye. Their cursor becomes a lens, whatever they hover is outlined, and the element they click comes back with its selector, its role, its rendered text and a screenshot of it exactly as they saw it. Use it when a target is genuinely ambiguous \u2014 several things share a label, or the user said \u201Cthis one\u201D about something you cannot see \u2014 and pointing is faster than describing. It takes over the page and waits for a person, so never call it to explore; a new call dismisses a pick already waiting.",
|
|
28805
28827
|
input: external_exports.object({
|
|
28806
28828
|
hint: external_exports.string().max(120).optional().describe("One line shown over the page saying what to point at, e.g. \u201CPoint at the price you mean\u201D"),
|
|
28807
28829
|
maxContentLength: external_exports.number().int().positive().max(MAX_CONTENT).default(2e3).describe('Characters of the element\u2019s rendered text to return; past that it is cut and "truncated" comes back true'),
|
|
28808
|
-
timeoutMs: external_exports.number().int().min(5e3).max(3e5).default(
|
|
28830
|
+
timeoutMs: external_exports.number().int().min(5e3).max(3e5).default(PICK_DEFAULT_TIMEOUT_MS).describe("How long to wait for the user to click before giving up")
|
|
28809
28831
|
}),
|
|
28810
28832
|
async execute({ hint, maxContentLength, timeoutMs }) {
|
|
28811
|
-
if (lensIsUp()) {
|
|
28812
|
-
throw new ActionError("A-Eye is already waiting for the user to point at something", "ACTION_FAILED");
|
|
28813
|
-
}
|
|
28814
28833
|
const outcome = await pickWithLens({ hint, timeoutMs });
|
|
28815
28834
|
if ("timedOut" in outcome) {
|
|
28816
28835
|
throw new ActionError(
|
|
@@ -28824,12 +28843,18 @@ var pickElement = defineAction({
|
|
|
28824
28843
|
const element = outcome.picked;
|
|
28825
28844
|
const rendered2 = element instanceof HTMLElement ? element.innerText : element.textContent ?? "";
|
|
28826
28845
|
const content = rendered2.replace(/\n{3,}/g, "\n\n").trim() || accessibleText(element);
|
|
28846
|
+
const rect = element.getBoundingClientRect();
|
|
28827
28847
|
return {
|
|
28828
28848
|
element: describeElement(element),
|
|
28829
28849
|
content: content.slice(0, maxContentLength),
|
|
28830
28850
|
truncated: content.length > maxContentLength,
|
|
28831
28851
|
url: location.href,
|
|
28832
|
-
title: document.title
|
|
28852
|
+
title: document.title,
|
|
28853
|
+
capture: {
|
|
28854
|
+
region: { x: rect.x, y: rect.y, w: rect.width, h: rect.height },
|
|
28855
|
+
viewport: { w: window.innerWidth, h: window.innerHeight },
|
|
28856
|
+
dpr: window.devicePixelRatio || 1
|
|
28857
|
+
}
|
|
28833
28858
|
};
|
|
28834
28859
|
}
|
|
28835
28860
|
});
|
|
@@ -29141,6 +29166,64 @@ function renderDiagram2(tree) {
|
|
|
29141
29166
|
return lines.join("\n");
|
|
29142
29167
|
}
|
|
29143
29168
|
|
|
29169
|
+
// ../lib/actions/page/toolkit.ts
|
|
29170
|
+
var TOOLKIT_ATTRIBUTE = "data-browsentic-toolkit";
|
|
29171
|
+
var TOOLKIT_CALL_EVENT = "browsentic:toolkit:call";
|
|
29172
|
+
var TOOLKIT_RESULT_EVENT = "browsentic:toolkit:result";
|
|
29173
|
+
var TOOLKIT_MISSING = "TOOLKIT_MISSING";
|
|
29174
|
+
var CODE_ERROR = "CODE_ERROR";
|
|
29175
|
+
function callToolkit(fn, args, timeoutMs) {
|
|
29176
|
+
if (!document.documentElement.hasAttribute(TOOLKIT_ATTRIBUTE)) {
|
|
29177
|
+
throw new ActionError("No toolkit is installed in this page.", TOOLKIT_MISSING);
|
|
29178
|
+
}
|
|
29179
|
+
const callId = crypto.randomUUID();
|
|
29180
|
+
return new Promise((resolve, reject) => {
|
|
29181
|
+
const onResult = (event) => {
|
|
29182
|
+
const reply = parseReply(event.detail);
|
|
29183
|
+
if (reply?.id !== callId) return;
|
|
29184
|
+
stop2();
|
|
29185
|
+
if (reply.ok) resolve(reply.value ?? null);
|
|
29186
|
+
else reject(new ActionError(reply.error ?? "The function threw.", CODE_ERROR));
|
|
29187
|
+
};
|
|
29188
|
+
const timer = setTimeout(() => {
|
|
29189
|
+
stop2();
|
|
29190
|
+
reject(new ActionError(`\u201C${fn}\u201D did not finish within ${timeoutMs}ms.`, "TIMEOUT"));
|
|
29191
|
+
}, timeoutMs);
|
|
29192
|
+
function stop2() {
|
|
29193
|
+
clearTimeout(timer);
|
|
29194
|
+
window.removeEventListener(TOOLKIT_RESULT_EVENT, onResult);
|
|
29195
|
+
}
|
|
29196
|
+
window.addEventListener(TOOLKIT_RESULT_EVENT, onResult);
|
|
29197
|
+
window.dispatchEvent(
|
|
29198
|
+
new CustomEvent(TOOLKIT_CALL_EVENT, { detail: JSON.stringify({ id: callId, fn, args }) })
|
|
29199
|
+
);
|
|
29200
|
+
});
|
|
29201
|
+
}
|
|
29202
|
+
function parseReply(detail) {
|
|
29203
|
+
if (typeof detail !== "string") return null;
|
|
29204
|
+
try {
|
|
29205
|
+
return JSON.parse(detail);
|
|
29206
|
+
} catch {
|
|
29207
|
+
return null;
|
|
29208
|
+
}
|
|
29209
|
+
}
|
|
29210
|
+
|
|
29211
|
+
// ../lib/actions/page/run-code.ts
|
|
29212
|
+
var runCode = defineAction({
|
|
29213
|
+
name: "page.runCode",
|
|
29214
|
+
description: "Call one function from the toolkit page.injectCode installed in this tab, with fresh arguments. This is the cheap, repeatable half of the pair: the user approved the code once, so every call runs without another prompt, and a page reload re-installs the approved toolkit on its own. It refuses if nothing is installed here, or if the tab has moved to a different site than the one the code was approved on \u2014 inject again in either case. The function\u2019s return value comes back as JSON.",
|
|
29215
|
+
input: external_exports.object({
|
|
29216
|
+
function: external_exports.string().min(1).describe("Name of a function the installed toolkit assigned onto `tools`."),
|
|
29217
|
+
args: external_exports.array(external_exports.unknown()).default([]).describe(
|
|
29218
|
+
"Arguments passed to the function, in order. JSON values only \u2014 this is where per-call data like a tag name belongs."
|
|
29219
|
+
),
|
|
29220
|
+
timeoutMs: external_exports.number().int().min(100).max(12e4).default(1e4).describe("How long the call may run before it is abandoned. Raise it for functions that wait on the page.")
|
|
29221
|
+
}),
|
|
29222
|
+
async execute({ function: fn, args, timeoutMs }) {
|
|
29223
|
+
return { function: fn, returned: await callToolkit(fn, args, timeoutMs) };
|
|
29224
|
+
}
|
|
29225
|
+
});
|
|
29226
|
+
|
|
29144
29227
|
// ../lib/actions/page/screenshot.ts
|
|
29145
29228
|
var screenshot = defineAction({
|
|
29146
29229
|
name: "page.screenshot",
|
|
@@ -29846,6 +29929,8 @@ var actions = new Map(
|
|
|
29846
29929
|
pressKey,
|
|
29847
29930
|
submitForm,
|
|
29848
29931
|
waitForElement,
|
|
29932
|
+
injectCode,
|
|
29933
|
+
runCode,
|
|
29849
29934
|
findProgress,
|
|
29850
29935
|
findSearch,
|
|
29851
29936
|
readTheme,
|
|
@@ -29895,7 +29980,8 @@ var AGENTS = {
|
|
|
29895
29980
|
vendor: "Anthropic",
|
|
29896
29981
|
bin: "claude",
|
|
29897
29982
|
install: "npm i -g @anthropic-ai/claude-code",
|
|
29898
|
-
docs: "https://claude.com/claude-code"
|
|
29983
|
+
docs: "https://claude.com/claude-code",
|
|
29984
|
+
models: ["claude-fable-5", "claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5"]
|
|
29899
29985
|
},
|
|
29900
29986
|
codex: {
|
|
29901
29987
|
kind: "codex",
|
|
@@ -29903,7 +29989,8 @@ var AGENTS = {
|
|
|
29903
29989
|
vendor: "OpenAI",
|
|
29904
29990
|
bin: "codex",
|
|
29905
29991
|
install: "npm i -g @openai/codex",
|
|
29906
|
-
docs: "https://developers.openai.com/codex/cli"
|
|
29992
|
+
docs: "https://developers.openai.com/codex/cli",
|
|
29993
|
+
models: ["gpt-5.6-terra", "gpt-5.1-codex-max", "gpt-5.1-codex", "gpt-5.1-codex-mini"]
|
|
29907
29994
|
},
|
|
29908
29995
|
antigravity: {
|
|
29909
29996
|
kind: "antigravity",
|
|
@@ -29911,7 +29998,8 @@ var AGENTS = {
|
|
|
29911
29998
|
vendor: "Google",
|
|
29912
29999
|
bin: "agy",
|
|
29913
30000
|
install: "https://antigravity.google/docs/cli/install",
|
|
29914
|
-
docs: "https://antigravity.google/docs/cli"
|
|
30001
|
+
docs: "https://antigravity.google/docs/cli",
|
|
30002
|
+
models: ["gemini-3-pro", "gemini-3-flash"]
|
|
29915
30003
|
}
|
|
29916
30004
|
};
|
|
29917
30005
|
var AGENT_LIST = AGENT_KINDS.map((kind) => AGENTS[kind]);
|
|
@@ -29925,11 +30013,13 @@ var SAVE_SITE_MAP_ACTION = `${RESERVED_PREFIX}saveSiteMap`;
|
|
|
29925
30013
|
var START_RECORDING_ACTION = `${RESERVED_PREFIX}startRecording`;
|
|
29926
30014
|
var STOP_RECORDING_ACTION = `${RESERVED_PREFIX}stopRecording`;
|
|
29927
30015
|
var READ_SITEMAP_ACTION = `${RESERVED_PREFIX}readSitemap`;
|
|
30016
|
+
var FOCUS_SHOT_ACTION = `${RESERVED_PREFIX}focusShot`;
|
|
29928
30017
|
var RESERVED_ACTIONS = [
|
|
29929
30018
|
SAVE_SITE_MAP_ACTION,
|
|
29930
30019
|
START_RECORDING_ACTION,
|
|
29931
30020
|
STOP_RECORDING_ACTION,
|
|
29932
|
-
READ_SITEMAP_ACTION
|
|
30021
|
+
READ_SITEMAP_ACTION,
|
|
30022
|
+
FOCUS_SHOT_ACTION
|
|
29933
30023
|
];
|
|
29934
30024
|
|
|
29935
30025
|
// ../lib/actions/tool-names.ts
|
|
@@ -30243,6 +30333,15 @@ var claudeRunner = {
|
|
|
30243
30333
|
};
|
|
30244
30334
|
},
|
|
30245
30335
|
reader() {
|
|
30336
|
+
let generated = 0;
|
|
30337
|
+
const report = (usage, sink) => {
|
|
30338
|
+
if (!usage) return;
|
|
30339
|
+
generated += usage.output_tokens ?? 0;
|
|
30340
|
+
sink.usage({
|
|
30341
|
+
contextTokens: (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
30342
|
+
outputTokens: generated
|
|
30343
|
+
});
|
|
30344
|
+
};
|
|
30246
30345
|
return (line, sink) => {
|
|
30247
30346
|
const message = parseJsonLine(line);
|
|
30248
30347
|
if (!message) return;
|
|
@@ -30266,10 +30365,14 @@ var claudeRunner = {
|
|
|
30266
30365
|
}
|
|
30267
30366
|
return;
|
|
30268
30367
|
}
|
|
30368
|
+
case "assistant":
|
|
30369
|
+
if (!message.parent_tool_use_id) report(message.message?.usage, sink);
|
|
30370
|
+
return;
|
|
30269
30371
|
case "result":
|
|
30270
30372
|
if (message.is_error) {
|
|
30271
30373
|
return sink.fail("AGENT_FAILED", message.result || message.subtype || "Claude Code reported an error");
|
|
30272
30374
|
}
|
|
30375
|
+
if (!generated) report(message.usage, sink);
|
|
30273
30376
|
return sink.done(message.stop_reason || "end_turn");
|
|
30274
30377
|
}
|
|
30275
30378
|
};
|
|
@@ -30580,6 +30683,17 @@ var codexRunner = {
|
|
|
30580
30683
|
return finish(msg.message, sink);
|
|
30581
30684
|
case "web_search_begin":
|
|
30582
30685
|
return sink.tool(randomUUID3(), WEB_TOOL);
|
|
30686
|
+
case "token_count": {
|
|
30687
|
+
const last = msg.info?.last_token_usage ?? msg.info?.total_token_usage;
|
|
30688
|
+
const total = msg.info?.total_token_usage ?? last;
|
|
30689
|
+
if (last) {
|
|
30690
|
+
sink.usage({
|
|
30691
|
+
contextTokens: (last.input_tokens ?? 0) + (last.output_tokens ?? 0),
|
|
30692
|
+
outputTokens: total?.output_tokens ?? 0
|
|
30693
|
+
});
|
|
30694
|
+
}
|
|
30695
|
+
return;
|
|
30696
|
+
}
|
|
30583
30697
|
case "task_complete":
|
|
30584
30698
|
return sink.done("end_turn");
|
|
30585
30699
|
case "error":
|
|
@@ -30603,8 +30717,16 @@ var codexRunner = {
|
|
|
30603
30717
|
if (kind === "web_search") return sink.tool(item?.id ?? randomUUID3(), WEB_TOOL);
|
|
30604
30718
|
return;
|
|
30605
30719
|
}
|
|
30606
|
-
case "turn.completed":
|
|
30720
|
+
case "turn.completed": {
|
|
30721
|
+
const usage = frame.usage;
|
|
30722
|
+
if (usage) {
|
|
30723
|
+
sink.usage({
|
|
30724
|
+
contextTokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
|
|
30725
|
+
outputTokens: usage.output_tokens ?? 0
|
|
30726
|
+
});
|
|
30727
|
+
}
|
|
30607
30728
|
return sink.done("end_turn");
|
|
30729
|
+
}
|
|
30608
30730
|
case "turn.failed":
|
|
30609
30731
|
return sink.fail("AGENT_FAILED", frame.error?.message || "Codex could not finish the turn");
|
|
30610
30732
|
case "error":
|
|
@@ -31181,6 +31303,26 @@ var DEFAULT_RULES = [
|
|
|
31181
31303
|
title: "Saves a file from the page to disk",
|
|
31182
31304
|
reason: "That writes a file the page chose into the user\u2019s download folder."
|
|
31183
31305
|
},
|
|
31306
|
+
{
|
|
31307
|
+
// The most powerful thing an agent can ask for, and the one gate that has to show
|
|
31308
|
+
// its work: the panel puts the source behind a Review button, because "allow this
|
|
31309
|
+
// action?" is not a question anyone can answer about code they have not read. It
|
|
31310
|
+
// confirms rather than denies because a reviewed function is how twenty repetitions
|
|
31311
|
+
// stop being twenty round trips — and `unattended: deny` keeps an external MCP
|
|
31312
|
+
// client, which has nobody to show the code to, from installing any at all.
|
|
31313
|
+
id: "code-injection",
|
|
31314
|
+
when: "injectsCode",
|
|
31315
|
+
effect: "confirm",
|
|
31316
|
+
title: "Runs code it wrote in the page",
|
|
31317
|
+
reason: "That installs JavaScript the agent wrote into the page, with your logged-in session."
|
|
31318
|
+
},
|
|
31319
|
+
{
|
|
31320
|
+
id: "external-code-execution",
|
|
31321
|
+
when: "runsCodeOutsideThePanel",
|
|
31322
|
+
effect: "deny",
|
|
31323
|
+
title: "Calls injected code from outside the panel",
|
|
31324
|
+
reason: "Code installed by page.injectCode was reviewed and approved for the side-panel conversation that asked for it. It is not available to an MCP client."
|
|
31325
|
+
},
|
|
31184
31326
|
{
|
|
31185
31327
|
id: "leaves-pinned-tab",
|
|
31186
31328
|
when: "leavesPinnedTab",
|
|
@@ -31707,6 +31849,7 @@ function invokeTimeoutFor(action, input2) {
|
|
|
31707
31849
|
const declared = input2?.timeoutMs;
|
|
31708
31850
|
if (typeof declared === "number" && declared > 0) return declared + 1e4;
|
|
31709
31851
|
if (action === awaitMonitor.name) return AWAIT_DEFAULT_TIMEOUT_MS + 1e4;
|
|
31852
|
+
if (action === pickElement.name) return PICK_DEFAULT_TIMEOUT_MS + 1e4;
|
|
31710
31853
|
return REQUEST_TIMEOUT_MS;
|
|
31711
31854
|
}
|
|
31712
31855
|
|
|
@@ -33455,6 +33598,12 @@ var Server = class extends Protocol {
|
|
|
33455
33598
|
// server.ts
|
|
33456
33599
|
var STATUS_TOOL = toolNameFor(`${RESERVED_PREFIX}status`);
|
|
33457
33600
|
var SCREENSHOT_TOOL = "page_screenshot";
|
|
33601
|
+
var PICK_TOOL = "page_pickElement";
|
|
33602
|
+
var FOCUS_SHOT_TOOL = {
|
|
33603
|
+
name: toolNameFor(FOCUS_SHOT_ACTION),
|
|
33604
|
+
description: "Show the screenshot of the element the user pointed at with A-Eye, taken at the instant they picked it. Only answers when the current instruction arrived with a pick attached.",
|
|
33605
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
33606
|
+
};
|
|
33458
33607
|
var RESOURCES = [
|
|
33459
33608
|
{
|
|
33460
33609
|
uri: "browsentic://page/current",
|
|
@@ -33568,7 +33717,7 @@ function createMcpServer(bridge, version2, opts = {}) {
|
|
|
33568
33717
|
description: "Report whether the Browsentic browser extension is connected, its version, and the active tab. Use this first if a page tool fails.",
|
|
33569
33718
|
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
33570
33719
|
},
|
|
33571
|
-
...opts.agentRun ? [SAVE_SITE_MAP_TOOL] : []
|
|
33720
|
+
...opts.agentRun ? [SAVE_SITE_MAP_TOOL, FOCUS_SHOT_TOOL] : []
|
|
33572
33721
|
]
|
|
33573
33722
|
};
|
|
33574
33723
|
});
|
|
@@ -33577,6 +33726,8 @@ function createMcpServer(bridge, version2, opts = {}) {
|
|
|
33577
33726
|
const action = actionNameFor(params.name);
|
|
33578
33727
|
const result = await bridge.invoke(action, params.arguments ?? {});
|
|
33579
33728
|
if (params.name === SCREENSHOT_TOOL) return renderScreenshot(result);
|
|
33729
|
+
if (params.name === FOCUS_SHOT_TOOL.name) return renderFocusShot(result);
|
|
33730
|
+
if (params.name === PICK_TOOL) return renderPick(result, shouldFence(action, policy) ? tag2 : void 0);
|
|
33580
33731
|
return render(result, shouldFence(action, policy) ? tag2 : void 0);
|
|
33581
33732
|
});
|
|
33582
33733
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [...RESOURCES] }));
|
|
@@ -33654,6 +33805,38 @@ function renderScreenshot(result) {
|
|
|
33654
33805
|
]
|
|
33655
33806
|
};
|
|
33656
33807
|
}
|
|
33808
|
+
function renderPick(result, fenceWith) {
|
|
33809
|
+
if (!result.ok) return render(result, fenceWith);
|
|
33810
|
+
const { shot, ...rest } = result.data;
|
|
33811
|
+
if (typeof shot?.dataUrl !== "string") return render(result, fenceWith);
|
|
33812
|
+
const [mimeType, base643] = splitDataUrl(shot.dataUrl);
|
|
33813
|
+
const rendered2 = render({ ok: true, data: rest }, fenceWith);
|
|
33814
|
+
return {
|
|
33815
|
+
content: [
|
|
33816
|
+
...rendered2.content,
|
|
33817
|
+
{ type: "image", data: base643, mimeType },
|
|
33818
|
+
{
|
|
33819
|
+
type: "text",
|
|
33820
|
+
text: `${IMAGE_NOTE} This is the picked element photographed at the instant the user clicked it.`
|
|
33821
|
+
}
|
|
33822
|
+
]
|
|
33823
|
+
};
|
|
33824
|
+
}
|
|
33825
|
+
function renderFocusShot(result) {
|
|
33826
|
+
if (!result.ok) return render(result);
|
|
33827
|
+
const dataUrl = result.data?.dataUrl;
|
|
33828
|
+
if (typeof dataUrl !== "string") return render(result);
|
|
33829
|
+
const [mimeType, base643] = splitDataUrl(dataUrl);
|
|
33830
|
+
return {
|
|
33831
|
+
content: [
|
|
33832
|
+
{ type: "image", data: base643, mimeType },
|
|
33833
|
+
{
|
|
33834
|
+
type: "text",
|
|
33835
|
+
text: `${IMAGE_NOTE} This is the element the user pointed at with A-Eye, as it stood when they picked it.`
|
|
33836
|
+
}
|
|
33837
|
+
]
|
|
33838
|
+
};
|
|
33839
|
+
}
|
|
33657
33840
|
function splitDataUrl(dataUrl) {
|
|
33658
33841
|
const match = /^data:([^;,]+);base64,(.*)$/s.exec(dataUrl);
|
|
33659
33842
|
return match ? [match[1], match[2]] : ["image/png", ""];
|
|
@@ -33679,7 +33862,7 @@ function text2(uri, mimeType, body) {
|
|
|
33679
33862
|
// package.json
|
|
33680
33863
|
var package_default = {
|
|
33681
33864
|
name: "browsentic",
|
|
33682
|
-
version: "0.4.
|
|
33865
|
+
version: "0.4.9",
|
|
33683
33866
|
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.",
|
|
33684
33867
|
type: "module",
|
|
33685
33868
|
license: "MIT",
|