floorp-mcp 1.5.0
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/LICENSE +21 -0
- package/README.md +270 -0
- package/dist/floorp-client.d.ts +133 -0
- package/dist/floorp-client.js +273 -0
- package/dist/floorp-client.js.map +1 -0
- package/dist/guards.d.ts +22 -0
- package/dist/guards.js +111 -0
- package/dist/guards.js.map +1 -0
- package/dist/html-find.d.ts +22 -0
- package/dist/html-find.js +95 -0
- package/dist/html-find.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +681 -0
- package/dist/index.js.map +1 -0
- package/dist/launch.d.ts +9 -0
- package/dist/launch.js +43 -0
- package/dist/launch.js.map +1 -0
- package/dist/os-input.d.ts +48 -0
- package/dist/os-input.js +339 -0
- package/dist/os-input.js.map +1 -0
- package/dist/probe.d.ts +5 -0
- package/dist/probe.js +31 -0
- package/dist/probe.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client for Floorp's built-in automation API.
|
|
3
|
+
*
|
|
4
|
+
* Floorp exposes this API on http://127.0.0.1:58261 once `floorp.mcp.enabled`
|
|
5
|
+
* is set to `true` in about:config. The model is instance-based: to operate on
|
|
6
|
+
* a tab you first obtain an `instanceId` (by attaching to an existing tab or by
|
|
7
|
+
* creating a new one), then issue per-instance commands.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle (verified against a live Floorp):
|
|
10
|
+
* - attach(browserId) -> ephemeral handle to an EXISTING tab
|
|
11
|
+
* - createTab(url) -> opens a NEW tab, returns a handle
|
|
12
|
+
* - detach(instanceId) [DELETE] -> releases the handle, tab stays open
|
|
13
|
+
* - closeTab(instanceId) [close] -> actually closes the tab
|
|
14
|
+
*/
|
|
15
|
+
const DATA_URL_PREFIX = /^data:image\/[a-z]+;base64,/;
|
|
16
|
+
export class FloorpClient {
|
|
17
|
+
baseUrl;
|
|
18
|
+
token;
|
|
19
|
+
constructor(port = Number(process.env.FLOORP_MCP_PORT) || 58261, token = process.env.FLOORP_MCP_TOKEN ?? "") {
|
|
20
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
21
|
+
throw new Error(`Invalid Floorp API port "${port}" — must be an integer 1–65535 (check FLOORP_MCP_PORT).`);
|
|
22
|
+
}
|
|
23
|
+
this.baseUrl = `http://127.0.0.1:${port}`;
|
|
24
|
+
this.token = token;
|
|
25
|
+
}
|
|
26
|
+
// -- low level --------------------------------------------------------------
|
|
27
|
+
async request(method, path, body) {
|
|
28
|
+
const headers = {};
|
|
29
|
+
if (this.token)
|
|
30
|
+
headers["Authorization"] = `Bearer ${this.token}`;
|
|
31
|
+
if (body !== undefined)
|
|
32
|
+
headers["Content-Type"] = "application/json";
|
|
33
|
+
let res;
|
|
34
|
+
try {
|
|
35
|
+
res = await fetch(`${this.baseUrl}${path}`, {
|
|
36
|
+
method,
|
|
37
|
+
headers,
|
|
38
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
throw new Error(`Cannot reach Floorp at ${this.baseUrl}. Is Floorp running with ` +
|
|
43
|
+
`'floorp.mcp.enabled' set to true in about:config? (${err.message})`);
|
|
44
|
+
}
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
const raw = await res.text().catch(() => "");
|
|
47
|
+
// Truncate so a large/unexpected error body can't dump page data into the model.
|
|
48
|
+
const text = raw.length > 500 ? raw.slice(0, 500) + "…[truncated]" : raw;
|
|
49
|
+
throw new Error(`Floorp API ${res.status} on ${path}: ${text}`);
|
|
50
|
+
}
|
|
51
|
+
return (await res.json());
|
|
52
|
+
}
|
|
53
|
+
static stripImagePrefix(data) {
|
|
54
|
+
return data.replace(DATA_URL_PREFIX, "");
|
|
55
|
+
}
|
|
56
|
+
// -- browser-level ----------------------------------------------------------
|
|
57
|
+
async health() {
|
|
58
|
+
try {
|
|
59
|
+
const r = await this.request("GET", "/health");
|
|
60
|
+
return r.status === "ok";
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async listTabs() {
|
|
67
|
+
return this.request("GET", "/tabs/list");
|
|
68
|
+
}
|
|
69
|
+
/** The currently selected tab. Throws if none is reported. */
|
|
70
|
+
async activeTab() {
|
|
71
|
+
const tabs = await this.listTabs();
|
|
72
|
+
const active = tabs.find((t) => t.selected);
|
|
73
|
+
if (!active)
|
|
74
|
+
throw new Error("No active tab reported by Floorp.");
|
|
75
|
+
return active;
|
|
76
|
+
}
|
|
77
|
+
// -- instance lifecycle -----------------------------------------------------
|
|
78
|
+
/** Open a NEW tab and return its instance handle. */
|
|
79
|
+
async createTab(url, opts = {}) {
|
|
80
|
+
const r = await this.request("POST", "/tabs/instances", {
|
|
81
|
+
url,
|
|
82
|
+
inBackground: opts.background ?? false,
|
|
83
|
+
waitForLoad: opts.waitForLoad ?? true,
|
|
84
|
+
});
|
|
85
|
+
return r.instanceId;
|
|
86
|
+
}
|
|
87
|
+
/** Resolve the live browserId behind an instance handle. */
|
|
88
|
+
async getInstanceBrowserId(instanceId) {
|
|
89
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}`);
|
|
90
|
+
return r.browserId ?? null;
|
|
91
|
+
}
|
|
92
|
+
/** Attach an ephemeral handle to an EXISTING tab. */
|
|
93
|
+
async attach(browserId) {
|
|
94
|
+
const r = await this.request("POST", "/tabs/attach", { browserId: String(browserId) });
|
|
95
|
+
return r.instanceId;
|
|
96
|
+
}
|
|
97
|
+
/** Release a handle WITHOUT closing the tab. */
|
|
98
|
+
async detach(instanceId) {
|
|
99
|
+
await this.request("DELETE", `/tabs/instances/${encodeURIComponent(instanceId)}`);
|
|
100
|
+
}
|
|
101
|
+
/** Actually close the tab behind a handle. */
|
|
102
|
+
async closeTab(instanceId) {
|
|
103
|
+
await this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}/close`);
|
|
104
|
+
}
|
|
105
|
+
// -- per-instance reads / actions ------------------------------------------
|
|
106
|
+
async navigate(instanceId, url) {
|
|
107
|
+
await this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}/navigate`, { url });
|
|
108
|
+
}
|
|
109
|
+
async getUri(instanceId) {
|
|
110
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/uri`);
|
|
111
|
+
return r.uri;
|
|
112
|
+
}
|
|
113
|
+
async getTitle(instanceId) {
|
|
114
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/title`);
|
|
115
|
+
return r.title ?? null;
|
|
116
|
+
}
|
|
117
|
+
/** Page content as clean Markdown. */
|
|
118
|
+
async getText(instanceId, mode = "full") {
|
|
119
|
+
const r = await this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}/text`, { mode, enableFingerprints: false, includeSelectorMap: false });
|
|
120
|
+
return r.text ?? "";
|
|
121
|
+
}
|
|
122
|
+
async getHtml(instanceId, selector) {
|
|
123
|
+
const qs = selector ? `?selector=${encodeURIComponent(selector)}` : "";
|
|
124
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/html${qs}`);
|
|
125
|
+
return r.html ?? "";
|
|
126
|
+
}
|
|
127
|
+
async getAccessibilityTree(instanceId) {
|
|
128
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/ax-tree?interestingOnly=true`);
|
|
129
|
+
return r.tree ?? null;
|
|
130
|
+
}
|
|
131
|
+
/** Viewport screenshot as base64 PNG (no data-URL prefix). */
|
|
132
|
+
async screenshot(instanceId) {
|
|
133
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/screenshot`);
|
|
134
|
+
return r.image ? FloorpClient.stripImagePrefix(r.image) : null;
|
|
135
|
+
}
|
|
136
|
+
/** Full-page screenshot as base64 PNG (no data-URL prefix). */
|
|
137
|
+
async fullPageScreenshot(instanceId) {
|
|
138
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/fullPageScreenshot`);
|
|
139
|
+
return r.image ? FloorpClient.stripImagePrefix(r.image) : null;
|
|
140
|
+
}
|
|
141
|
+
// -- interactions -----------------------------------------------------------
|
|
142
|
+
/** POST a per-instance action; treat an explicit `{ ok: false }` as failure. */
|
|
143
|
+
async action(instanceId, suffix, body, what) {
|
|
144
|
+
const r = await this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}${suffix}`, body);
|
|
145
|
+
if (r.ok === false) {
|
|
146
|
+
throw new Error(`${what} failed — element not found or not actionable.`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Scroll an element (by selector or fingerprint) into view. */
|
|
150
|
+
async scrollTo(instanceId, selector, fingerprint) {
|
|
151
|
+
await this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}/scrollTo`, { selector, fingerprint });
|
|
152
|
+
}
|
|
153
|
+
async click(instanceId, selector, opts = {}) {
|
|
154
|
+
// Auto scroll-into-view first so off-screen elements are actionable.
|
|
155
|
+
await this.scrollTo(instanceId, selector, opts.fingerprint).catch(() => { });
|
|
156
|
+
await this.action(instanceId, "/click", {
|
|
157
|
+
selector,
|
|
158
|
+
fingerprint: opts.fingerprint,
|
|
159
|
+
button: opts.button,
|
|
160
|
+
clickCount: opts.clickCount,
|
|
161
|
+
force: opts.force,
|
|
162
|
+
}, `Click "${selector ?? opts.fingerprint ?? "?"}"`);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Structured page snapshot: clean Markdown text with inline fingerprint refs
|
|
166
|
+
* (`<!--fp:...-->`) plus an "Element Selector Map" (fp | tag | text). Lets an
|
|
167
|
+
* agent locate elements without grepping raw HTML, then act via a `ref`.
|
|
168
|
+
*/
|
|
169
|
+
async snapshot(instanceId, mode = "full") {
|
|
170
|
+
const r = await this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}/text`, { mode, enableFingerprints: true, includeSelectorMap: true });
|
|
171
|
+
return r.text ?? "";
|
|
172
|
+
}
|
|
173
|
+
/** Set the value of an input/textarea. */
|
|
174
|
+
async input(instanceId, selector, value, opts = {}) {
|
|
175
|
+
await this.action(instanceId, "/input", { selector, value, typingMode: opts.typingMode, typingDelayMs: opts.typingDelayMs }, `Type into "${selector}"`);
|
|
176
|
+
}
|
|
177
|
+
async clearInput(instanceId, selector) {
|
|
178
|
+
await this.action(instanceId, "/clearInput", { selector }, `Clear "${selector}"`);
|
|
179
|
+
}
|
|
180
|
+
/** Fill several fields at once: keys are CSS selectors, values are strings. */
|
|
181
|
+
async fillForm(instanceId, formData) {
|
|
182
|
+
await this.action(instanceId, "/fillForm", { formData }, "Fill form");
|
|
183
|
+
}
|
|
184
|
+
async pressKey(instanceId, key) {
|
|
185
|
+
await this.action(instanceId, "/pressKey", { key }, `Press "${key}"`);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Insert text into a rich / contenteditable editor (Slate, ProseMirror, Lexical…)
|
|
189
|
+
* by dispatching a real text-input event. Use this when `input` fails because the
|
|
190
|
+
* element has no `.value` (i.e. it is not a plain <input>/<textarea>).
|
|
191
|
+
*/
|
|
192
|
+
async dispatchTextInput(instanceId, selector, text) {
|
|
193
|
+
await this.action(instanceId, "/dispatchTextInput", { selector, text }, `Type into "${selector}"`);
|
|
194
|
+
}
|
|
195
|
+
/** Read the current value of an input/textarea/select. */
|
|
196
|
+
async getValue(instanceId, selector) {
|
|
197
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/value?selector=${encodeURIComponent(selector)}`);
|
|
198
|
+
return r.value ?? null;
|
|
199
|
+
}
|
|
200
|
+
async waitForElement(instanceId, selector, state = "visible", timeout = 5000) {
|
|
201
|
+
const r = await this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}/waitForElement`, { selector, state, timeout });
|
|
202
|
+
return r.found ?? r.ok ?? false;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Evaluate JavaScript in the page context (supports async/await).
|
|
206
|
+
* NOTE: not exposed in all Floorp builds — older ones return HTTP 404.
|
|
207
|
+
*/
|
|
208
|
+
async evaluate(instanceId, script) {
|
|
209
|
+
return this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}/evaluate`, { script });
|
|
210
|
+
}
|
|
211
|
+
// -- more interactions (v0.6.0) ---------------------------------------------
|
|
212
|
+
async hover(instanceId, selector, fingerprint) {
|
|
213
|
+
await this.scrollTo(instanceId, selector, fingerprint).catch(() => { });
|
|
214
|
+
await this.action(instanceId, "/hover", { selector, fingerprint }, `Hover "${selector ?? fingerprint}"`);
|
|
215
|
+
}
|
|
216
|
+
async doubleClick(instanceId, selector, fingerprint) {
|
|
217
|
+
await this.scrollTo(instanceId, selector, fingerprint).catch(() => { });
|
|
218
|
+
await this.action(instanceId, "/doubleClick", { selector, fingerprint }, `Double-click "${selector ?? fingerprint}"`);
|
|
219
|
+
}
|
|
220
|
+
async rightClick(instanceId, selector, fingerprint) {
|
|
221
|
+
await this.scrollTo(instanceId, selector, fingerprint).catch(() => { });
|
|
222
|
+
await this.action(instanceId, "/rightClick", { selector, fingerprint }, `Right-click "${selector ?? fingerprint}"`);
|
|
223
|
+
}
|
|
224
|
+
/** Choose an option in a <select> by value. */
|
|
225
|
+
async selectOption(instanceId, selector, value) {
|
|
226
|
+
await this.action(instanceId, "/selectOption", { selector, value }, `Select "${value}" in "${selector}"`);
|
|
227
|
+
}
|
|
228
|
+
/** Check/uncheck a checkbox or radio. */
|
|
229
|
+
async setChecked(instanceId, selector, checked) {
|
|
230
|
+
await this.action(instanceId, "/setChecked", { selector, checked }, `Set checked=${checked} on "${selector}"`);
|
|
231
|
+
}
|
|
232
|
+
/** Submit a form (by a selector inside/of the form). */
|
|
233
|
+
async submitForm(instanceId, selector) {
|
|
234
|
+
await this.action(instanceId, "/submit", { selector }, `Submit "${selector ?? "form"}"`);
|
|
235
|
+
}
|
|
236
|
+
/** Set a file input's file by absolute path. */
|
|
237
|
+
async uploadFile(instanceId, selector, filePath) {
|
|
238
|
+
await this.action(instanceId, "/uploadFile", { selector, filePath }, `Upload to "${selector}"`);
|
|
239
|
+
}
|
|
240
|
+
// -- more reads (v0.6.0) ----------------------------------------------------
|
|
241
|
+
async getAttribute(instanceId, name, selector, fingerprint) {
|
|
242
|
+
const p = new URLSearchParams({ name });
|
|
243
|
+
if (selector)
|
|
244
|
+
p.set("selector", selector);
|
|
245
|
+
if (fingerprint)
|
|
246
|
+
p.set("fingerprint", fingerprint);
|
|
247
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/attribute?${p.toString()}`);
|
|
248
|
+
return r.value ?? null;
|
|
249
|
+
}
|
|
250
|
+
/** Readability-extracted main article (title, byline, markdown). */
|
|
251
|
+
async getArticle(instanceId) {
|
|
252
|
+
return this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/article`);
|
|
253
|
+
}
|
|
254
|
+
async getCookies(instanceId) {
|
|
255
|
+
const r = await this.request("GET", `/tabs/instances/${encodeURIComponent(instanceId)}/cookies`);
|
|
256
|
+
return r.cookies ?? [];
|
|
257
|
+
}
|
|
258
|
+
/** Wait until network activity settles (good after navigation/SPA loads). */
|
|
259
|
+
async waitForNetworkIdle(instanceId, timeout = 8000) {
|
|
260
|
+
const r = await this.request("POST", `/tabs/instances/${encodeURIComponent(instanceId)}/waitForNetworkIdle`, { timeout });
|
|
261
|
+
return r.ok ?? false;
|
|
262
|
+
}
|
|
263
|
+
// -- workspaces (Floorp-specific, browser-level) (v0.6.0) -------------------
|
|
264
|
+
async listWorkspaces() {
|
|
265
|
+
const r = await this.request("GET", "/workspaces/");
|
|
266
|
+
return r.workspaces ?? [];
|
|
267
|
+
}
|
|
268
|
+
async switchWorkspace(id) {
|
|
269
|
+
const r = await this.request("POST", `/workspaces/${encodeURIComponent(id)}/switch`);
|
|
270
|
+
return r.ok ?? false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=floorp-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floorp-client.js","sourceRoot":"","sources":["../src/floorp-client.ts"],"names":[],"mappings":"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"}
|
package/dist/guards.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure security guards for URL navigation and file uploads. Kept in their own
|
|
3
|
+
* module (no side effects, no server) so they can be unit-tested without a live
|
|
4
|
+
* Floorp or starting the MCP server.
|
|
5
|
+
*/
|
|
6
|
+
/** Browser-internal pages (about:, chrome:, …) cannot be screenshotted. */
|
|
7
|
+
export declare const PRIVILEGED_SCHEME: RegExp;
|
|
8
|
+
/** True for loopback / link-local / RFC-1918 private hosts. Best-effort literal
|
|
9
|
+
* check (does not resolve DNS — that rebinding case is documented in README). */
|
|
10
|
+
export declare function isInternalHost(host: string): boolean;
|
|
11
|
+
/** Gate URLs before open/navigate. By default: only http(s) (and about:blank),
|
|
12
|
+
* and NOT loopback/private hosts — so a prompt-injected agent can't pivot the
|
|
13
|
+
* browser onto Floorp's own automation API (127.0.0.1:58261) or your LAN, then
|
|
14
|
+
* read the response back. Optional FLOORP_MCP_ALLOW_DOMAINS restricts to a
|
|
15
|
+
* domain allowlist. FLOORP_MCP_ALLOW_PRIVILEGED_URLS=1 lifts scheme+host gates. */
|
|
16
|
+
export declare function assertNavigableUrl(url: string): void;
|
|
17
|
+
/** If FLOORP_MCP_ALLOW_UPLOAD_DIRS is set (';'-separated on Windows), uploads are
|
|
18
|
+
* restricted to files inside those directories. Paths are canonicalised with
|
|
19
|
+
* realpath (resolving symlinks) and checked with path.relative so neither a
|
|
20
|
+
* symlink, a "..", nor a same-prefix sibling dir (C:\\a vs C:\\ab) can escape.
|
|
21
|
+
* UNC paths are rejected when an allowlist is set. Unset = any path (default). */
|
|
22
|
+
export declare function assertUploadAllowed(filePath: string): string;
|
package/dist/guards.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure security guards for URL navigation and file uploads. Kept in their own
|
|
3
|
+
* module (no side effects, no server) so they can be unit-tested without a live
|
|
4
|
+
* Floorp or starting the MCP server.
|
|
5
|
+
*/
|
|
6
|
+
import { resolve, relative, isAbsolute, delimiter } from "node:path";
|
|
7
|
+
import { realpathSync } from "node:fs";
|
|
8
|
+
/** Browser-internal pages (about:, chrome:, …) cannot be screenshotted. */
|
|
9
|
+
export const PRIVILEGED_SCHEME = /^(about|chrome|resource|view-source|moz-extension):/i;
|
|
10
|
+
/** True for loopback / link-local / RFC-1918 private hosts. Best-effort literal
|
|
11
|
+
* check (does not resolve DNS — that rebinding case is documented in README). */
|
|
12
|
+
export function isInternalHost(host) {
|
|
13
|
+
const h = host.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
|
|
14
|
+
if (h === "localhost" || h.endsWith(".localhost") || h === "" || h === "::" || h === "::1")
|
|
15
|
+
return true;
|
|
16
|
+
const m = h.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
|
|
17
|
+
if (m) {
|
|
18
|
+
const a = Number(m[1]), b = Number(m[2]);
|
|
19
|
+
if (a === 0 || a === 127 || a === 10)
|
|
20
|
+
return true; // this-host, loopback, private
|
|
21
|
+
if (a === 169 && b === 254)
|
|
22
|
+
return true; // link-local
|
|
23
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
24
|
+
return true; // private
|
|
25
|
+
if (a === 192 && b === 168)
|
|
26
|
+
return true; // private
|
|
27
|
+
}
|
|
28
|
+
// IPv6 loopback / unique-local (fc00::/7) / link-local (fe80::/10)
|
|
29
|
+
if (h.startsWith("fc") || h.startsWith("fd") || h.startsWith("fe8") || h.startsWith("fe9") ||
|
|
30
|
+
h.startsWith("fea") || h.startsWith("feb"))
|
|
31
|
+
return true;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
/** Gate URLs before open/navigate. By default: only http(s) (and about:blank),
|
|
35
|
+
* and NOT loopback/private hosts — so a prompt-injected agent can't pivot the
|
|
36
|
+
* browser onto Floorp's own automation API (127.0.0.1:58261) or your LAN, then
|
|
37
|
+
* read the response back. Optional FLOORP_MCP_ALLOW_DOMAINS restricts to a
|
|
38
|
+
* domain allowlist. FLOORP_MCP_ALLOW_PRIVILEGED_URLS=1 lifts scheme+host gates. */
|
|
39
|
+
export function assertNavigableUrl(url) {
|
|
40
|
+
const bypass = process.env.FLOORP_MCP_ALLOW_PRIVILEGED_URLS === "1";
|
|
41
|
+
if (url.trim().toLowerCase() === "about:blank")
|
|
42
|
+
return;
|
|
43
|
+
const scheme = url.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/)?.[1]?.toLowerCase();
|
|
44
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
45
|
+
if (bypass)
|
|
46
|
+
return;
|
|
47
|
+
throw new Error(`Refusing to open "${url}" — only http(s) URLs are allowed by default ` +
|
|
48
|
+
`(blocks file:// and browser-internal pages). ` +
|
|
49
|
+
`Set FLOORP_MCP_ALLOW_PRIVILEGED_URLS=1 to allow other schemes.`);
|
|
50
|
+
}
|
|
51
|
+
let host;
|
|
52
|
+
try {
|
|
53
|
+
host = new URL(url).hostname;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new Error(`Refusing to open "${url}" — not a valid URL.`);
|
|
57
|
+
}
|
|
58
|
+
if (!bypass && isInternalHost(host)) {
|
|
59
|
+
throw new Error(`Refusing to navigate to internal/loopback host "${host}" — this could reach ` +
|
|
60
|
+
`Floorp's own automation API or your private network. ` +
|
|
61
|
+
`Set FLOORP_MCP_ALLOW_PRIVILEGED_URLS=1 to override.`);
|
|
62
|
+
}
|
|
63
|
+
const allow = process.env.FLOORP_MCP_ALLOW_DOMAINS;
|
|
64
|
+
if (allow) {
|
|
65
|
+
const h = host.toLowerCase();
|
|
66
|
+
const ok = allow
|
|
67
|
+
.split(",")
|
|
68
|
+
.map((d) => d.trim().toLowerCase())
|
|
69
|
+
.filter(Boolean)
|
|
70
|
+
.some((d) => h === d || h.endsWith("." + d));
|
|
71
|
+
if (!ok) {
|
|
72
|
+
throw new Error(`Refusing to navigate to "${host}" — not in FLOORP_MCP_ALLOW_DOMAINS allowlist.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** If FLOORP_MCP_ALLOW_UPLOAD_DIRS is set (';'-separated on Windows), uploads are
|
|
77
|
+
* restricted to files inside those directories. Paths are canonicalised with
|
|
78
|
+
* realpath (resolving symlinks) and checked with path.relative so neither a
|
|
79
|
+
* symlink, a "..", nor a same-prefix sibling dir (C:\\a vs C:\\ab) can escape.
|
|
80
|
+
* UNC paths are rejected when an allowlist is set. Unset = any path (default). */
|
|
81
|
+
export function assertUploadAllowed(filePath) {
|
|
82
|
+
const canon = (p) => {
|
|
83
|
+
try {
|
|
84
|
+
return realpathSync(p);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return resolve(p);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const resolved = canon(filePath);
|
|
91
|
+
const allow = process.env.FLOORP_MCP_ALLOW_UPLOAD_DIRS;
|
|
92
|
+
if (!allow)
|
|
93
|
+
return resolved;
|
|
94
|
+
if (process.platform === "win32" && /^\\\\/.test(resolved)) {
|
|
95
|
+
throw new Error(`Upload of "${resolved}" blocked — UNC paths are not allowed. Use a local absolute path.`);
|
|
96
|
+
}
|
|
97
|
+
const ok = allow
|
|
98
|
+
.split(delimiter)
|
|
99
|
+
.map((d) => d.trim())
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
.some((d) => {
|
|
102
|
+
const rel = relative(canon(d), resolved);
|
|
103
|
+
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
104
|
+
});
|
|
105
|
+
if (!ok) {
|
|
106
|
+
throw new Error(`Upload of "${resolved}" blocked — outside FLOORP_MCP_ALLOW_UPLOAD_DIRS. ` +
|
|
107
|
+
`Add its directory to that variable (';'-separated) to allow it.`);
|
|
108
|
+
}
|
|
109
|
+
return resolved;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../src/guards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side element locator. Searches a page's raw HTML by visible text and/or
|
|
3
|
+
* tag and returns compact, clickable CSS selectors — so the full page never has
|
|
4
|
+
* to reach the model. Pure (no I/O) so it can be unit-tested directly.
|
|
5
|
+
*/
|
|
6
|
+
/** Escape a raw attribute value for use inside a double-quoted CSS string. */
|
|
7
|
+
export declare function cssString(v: string): string;
|
|
8
|
+
/** Build a clickable CSS selector from an element's opening tag, preferring the
|
|
9
|
+
* most stable identifier available. */
|
|
10
|
+
export declare function suggestSelector(openTag: string, tag: string): string;
|
|
11
|
+
export interface FoundEl {
|
|
12
|
+
tag: string;
|
|
13
|
+
selector: string;
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
/** Locate elements in raw HTML by visible text and/or tag — runs server-side so
|
|
17
|
+
* the full page never reaches the model. Returns compact, clickable matches. */
|
|
18
|
+
export declare function findInHtml(html: string, opts: {
|
|
19
|
+
text?: string;
|
|
20
|
+
tag?: string;
|
|
21
|
+
limit: number;
|
|
22
|
+
}): FoundEl[];
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side element locator. Searches a page's raw HTML by visible text and/or
|
|
3
|
+
* tag and returns compact, clickable CSS selectors — so the full page never has
|
|
4
|
+
* to reach the model. Pure (no I/O) so it can be unit-tested directly.
|
|
5
|
+
*/
|
|
6
|
+
/** Escape a raw attribute value for use inside a double-quoted CSS string. */
|
|
7
|
+
export function cssString(v) {
|
|
8
|
+
return v.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
9
|
+
}
|
|
10
|
+
/** Build a clickable CSS selector from an element's opening tag, preferring the
|
|
11
|
+
* most stable identifier available. */
|
|
12
|
+
export function suggestSelector(openTag, tag) {
|
|
13
|
+
const id = openTag.match(/\sid="([^"]+)"/)?.[1];
|
|
14
|
+
if (id && /^[A-Za-z_][\w-]*$/.test(id))
|
|
15
|
+
return `#${id}`;
|
|
16
|
+
const name = openTag.match(/\sname="([^"]+)"/)?.[1];
|
|
17
|
+
if (name)
|
|
18
|
+
return `${tag}[name="${cssString(name)}"]`;
|
|
19
|
+
const href = openTag.match(/\shref="([^"]+)"/)?.[1];
|
|
20
|
+
if (href && href !== "#" && !href.startsWith("javascript:"))
|
|
21
|
+
return `${tag}[href="${cssString(href)}"]`;
|
|
22
|
+
const cls = openTag
|
|
23
|
+
.match(/\sclass="([^"]+)"/)?.[1]
|
|
24
|
+
?.split(/\s+/)
|
|
25
|
+
.find((c) => /^[A-Za-z_][\w-]{1,}$/.test(c));
|
|
26
|
+
if (cls)
|
|
27
|
+
return `${tag}.${cls}`;
|
|
28
|
+
const type = openTag.match(/\stype="([^"]+)"/)?.[1];
|
|
29
|
+
if (type)
|
|
30
|
+
return `${tag}[type="${cssString(type)}"]`;
|
|
31
|
+
return tag;
|
|
32
|
+
}
|
|
33
|
+
/** Locate elements in raw HTML by visible text and/or tag — runs server-side so
|
|
34
|
+
* the full page never reaches the model. Returns compact, clickable matches. */
|
|
35
|
+
export function findInHtml(html, opts) {
|
|
36
|
+
// Strip Floorp's injected automation overlay so it can't pollute matches.
|
|
37
|
+
html = html
|
|
38
|
+
.replace(/<style id="nr-webscraper[\s\S]*?<\/style>/gi, " ")
|
|
39
|
+
.replace(/<div[^>]*nr-webscraper[\s\S]*?<\/div>/gi, " ");
|
|
40
|
+
const out = [];
|
|
41
|
+
const tagFilter = opts.tag?.toLowerCase();
|
|
42
|
+
const strip = (s) => s.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
43
|
+
// Text nodes never live in these — skip them so a text search returns visible elements.
|
|
44
|
+
const INVISIBLE = new Set(["title", "meta", "script", "style", "head", "link", "noscript", "html"]);
|
|
45
|
+
// Skip elements an attacker can hide to lure the agent into clicking them. Best
|
|
46
|
+
// effort over raw HTML: catches inline hiding, the hidden attr, type=hidden,
|
|
47
|
+
// aria-hidden (not CSS-class-based hiding, which needs computed styles).
|
|
48
|
+
const isHidden = (openTag) => /\stype\s*=\s*["']?hidden/i.test(openTag) ||
|
|
49
|
+
/\shidden(\s|>|=|\/)/i.test(openTag) ||
|
|
50
|
+
/\saria-hidden\s*=\s*["']?true/i.test(openTag) ||
|
|
51
|
+
/\sstyle\s*=\s*["'][^"']*(display\s*:\s*none|visibility\s*:\s*hidden)/i.test(openTag);
|
|
52
|
+
if (opts.text) {
|
|
53
|
+
const lc = html.toLowerCase();
|
|
54
|
+
const q = opts.text.toLowerCase();
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
let from = 0;
|
|
57
|
+
while (out.length < opts.limit) {
|
|
58
|
+
const idx = lc.indexOf(q, from);
|
|
59
|
+
if (idx < 0)
|
|
60
|
+
break;
|
|
61
|
+
from = idx + q.length;
|
|
62
|
+
const open = html.lastIndexOf("<", idx);
|
|
63
|
+
if (open < 0 || seen.has(open))
|
|
64
|
+
continue;
|
|
65
|
+
seen.add(open);
|
|
66
|
+
const tm = html.slice(open).match(/^<([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>/);
|
|
67
|
+
if (!tm)
|
|
68
|
+
continue;
|
|
69
|
+
const tg = tm[1].toLowerCase();
|
|
70
|
+
if (INVISIBLE.has(tg))
|
|
71
|
+
continue;
|
|
72
|
+
if (isHidden(tm[0]))
|
|
73
|
+
continue;
|
|
74
|
+
if (tagFilter && tg !== tagFilter)
|
|
75
|
+
continue;
|
|
76
|
+
// Preview = the matched text node only; cut at the next tag so it can't bleed.
|
|
77
|
+
const seg = html.slice(idx, idx + 120);
|
|
78
|
+
const cut = seg.indexOf("<");
|
|
79
|
+
const text = (cut >= 0 ? seg.slice(0, cut) : seg).replace(/\s+/g, " ").trim();
|
|
80
|
+
out.push({ tag: tg, selector: suggestSelector(tm[0], tg), text });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (tagFilter) {
|
|
84
|
+
const re = new RegExp(`<${tagFilter}\\b[^>]*>`, "gi");
|
|
85
|
+
let m;
|
|
86
|
+
while ((m = re.exec(html)) && out.length < opts.limit) {
|
|
87
|
+
if (isHidden(m[0]))
|
|
88
|
+
continue;
|
|
89
|
+
const after = html.slice(m.index + m[0].length, m.index + m[0].length + 90);
|
|
90
|
+
out.push({ tag: tagFilter, selector: suggestSelector(m[0], tagFilter), text: strip(after) });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=html-find.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-find.js","sourceRoot":"","sources":["../src/html-find.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* floorp-mcp — an MCP server that drives the Floorp browser through its
|
|
4
|
+
* built-in automation API (http://127.0.0.1:58261, gated by
|
|
5
|
+
* `floorp.mcp.enabled` in about:config).
|
|
6
|
+
*
|
|
7
|
+
* MVP tool surface: tab management, page reading, and screenshots, operating on
|
|
8
|
+
* the user's real, logged-in session.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|