@elizaos/plugin-browser 2.0.0-beta.1 → 2.0.11-beta.7
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 +106 -64
- package/dist/actions/browser-autofill-login.d.ts.map +1 -1
- package/dist/actions/browser-autofill-login.js.map +1 -1
- package/dist/actions/browser.d.ts +5 -6
- package/dist/actions/browser.d.ts.map +1 -1
- package/dist/actions/browser.js +54 -59
- package/dist/actions/browser.js.map +1 -1
- package/dist/actions/manage-browser-bridge.d.ts.map +1 -1
- package/dist/actions/manage-browser-bridge.js +10 -14
- package/dist/actions/manage-browser-bridge.js.map +1 -1
- package/dist/bridge-policy.d.ts +10 -0
- package/dist/bridge-policy.d.ts.map +1 -0
- package/dist/bridge-policy.js +37 -0
- package/dist/bridge-policy.js.map +1 -0
- package/dist/bridge-readiness.d.ts +16 -0
- package/dist/bridge-readiness.d.ts.map +1 -0
- package/dist/bridge-readiness.js +82 -0
- package/dist/bridge-readiness.js.map +1 -0
- package/dist/bridge-records.d.ts +9 -0
- package/dist/bridge-records.d.ts.map +1 -0
- package/dist/bridge-records.js +37 -0
- package/dist/bridge-records.js.map +1 -0
- package/dist/browser-capture-hooks.d.ts +9 -0
- package/dist/browser-capture-hooks.d.ts.map +1 -0
- package/dist/browser-capture-hooks.js +15 -0
- package/dist/browser-capture-hooks.js.map +1 -0
- package/dist/browser-service.d.ts +22 -4
- package/dist/browser-service.d.ts.map +1 -1
- package/dist/browser-service.js +63 -15
- package/dist/browser-service.js.map +1 -1
- package/dist/browser-workspace-hooks.d.ts +14 -0
- package/dist/browser-workspace-hooks.d.ts.map +1 -0
- package/dist/browser-workspace-hooks.js +15 -0
- package/dist/browser-workspace-hooks.js.map +1 -0
- package/dist/companion-auth.d.ts +34 -0
- package/dist/companion-auth.d.ts.map +1 -0
- package/dist/companion-auth.js +98 -0
- package/dist/companion-auth.js.map +1 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -11
- package/dist/index.js.map +1 -1
- package/dist/message-adapter.d.ts +9 -0
- package/dist/message-adapter.d.ts.map +1 -0
- package/dist/message-adapter.js +104 -0
- package/dist/message-adapter.js.map +1 -0
- package/dist/packaging.d.ts.map +1 -1
- package/dist/packaging.js +2 -0
- package/dist/packaging.js.map +1 -1
- package/dist/password-manager-bridge.d.ts +50 -0
- package/dist/password-manager-bridge.d.ts.map +1 -0
- package/dist/password-manager-bridge.js +437 -0
- package/dist/password-manager-bridge.js.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +8 -4
- package/dist/plugin.js.map +1 -1
- package/dist/providers/workspace.d.ts +1 -1
- package/dist/providers/workspace.js.map +1 -1
- package/dist/routes/bridge.d.ts.map +1 -1
- package/dist/routes/bridge.js +63 -14
- package/dist/routes/bridge.js.map +1 -1
- package/dist/routes/workspace-setup.d.ts.map +1 -1
- package/dist/routes/workspace-setup.js +1 -1
- package/dist/routes/workspace-setup.js.map +1 -1
- package/dist/routes/workspace.d.ts +1 -2
- package/dist/routes/workspace.d.ts.map +1 -1
- package/dist/routes/workspace.js +63 -3
- package/dist/routes/workspace.js.map +1 -1
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/service.d.ts +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js.map +1 -1
- package/dist/targets/bridge-target.d.ts +1 -1
- package/dist/targets/bridge-target.d.ts.map +1 -1
- package/dist/targets/bridge-target.js.map +1 -1
- package/dist/targets/stagehand-target.d.ts +3 -0
- package/dist/targets/stagehand-target.d.ts.map +1 -0
- package/dist/targets/stagehand-target.js +187 -0
- package/dist/targets/stagehand-target.js.map +1 -0
- package/dist/workspace/browser-capture.d.ts +1 -1
- package/dist/workspace/browser-capture.js.map +1 -1
- package/dist/workspace/browser-workspace-desktop.d.ts +1 -1
- package/dist/workspace/browser-workspace-desktop.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-desktop.js +47 -25
- package/dist/workspace/browser-workspace-desktop.js.map +1 -1
- package/dist/workspace/browser-workspace-forms.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-forms.js +1 -1
- package/dist/workspace/browser-workspace-forms.js.map +1 -1
- package/dist/workspace/browser-workspace-helpers.d.ts +7 -0
- package/dist/workspace/browser-workspace-helpers.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-helpers.js +37 -0
- package/dist/workspace/browser-workspace-helpers.js.map +1 -1
- package/dist/workspace/browser-workspace-network.d.ts +1 -1
- package/dist/workspace/browser-workspace-network.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-types.d.ts +15 -0
- package/dist/workspace/browser-workspace-types.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-types.js.map +1 -1
- package/dist/workspace/browser-workspace-web.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-web.js +15 -88
- package/dist/workspace/browser-workspace-web.js.map +1 -1
- package/dist/workspace/browser-workspace.d.ts +1 -1
- package/dist/workspace/browser-workspace.d.ts.map +1 -1
- package/dist/workspace/browser-workspace.js +9 -4
- package/dist/workspace/browser-workspace.js.map +1 -1
- package/package.json +28 -7
- package/dist/actions/browser-autofill-login.d.js +0 -1
- package/dist/actions/browser-autofill-login.d.js.map +0 -1
- package/dist/actions/browser.d.js +0 -1
- package/dist/actions/browser.d.js.map +0 -1
- package/dist/actions/manage-browser-bridge.d.js +0 -1
- package/dist/actions/manage-browser-bridge.d.js.map +0 -1
- package/dist/ambient-jsdom.d.js +0 -1
- package/dist/ambient-jsdom.d.js.map +0 -1
- package/dist/browser-service.d.js +0 -1
- package/dist/browser-service.d.js.map +0 -1
- package/dist/contracts.d.js +0 -1
- package/dist/contracts.d.js.map +0 -1
- package/dist/index.d.js +0 -21
- package/dist/index.d.js.map +0 -1
- package/dist/lifeops-session-contracts.d.js +0 -1
- package/dist/lifeops-session-contracts.d.js.map +0 -1
- package/dist/packaging.d.js +0 -1
- package/dist/packaging.d.js.map +0 -1
- package/dist/plugin.d.js +0 -1
- package/dist/plugin.d.js.map +0 -1
- package/dist/providers/workspace.d.js +0 -1
- package/dist/providers/workspace.d.js.map +0 -1
- package/dist/routes/bridge.d.js +0 -1
- package/dist/routes/bridge.d.js.map +0 -1
- package/dist/routes/workspace-account-gate.d.js +0 -1
- package/dist/routes/workspace-account-gate.d.js.map +0 -1
- package/dist/routes/workspace-setup.d.js +0 -1
- package/dist/routes/workspace-setup.d.js.map +0 -1
- package/dist/routes/workspace.d.js +0 -1
- package/dist/routes/workspace.d.js.map +0 -1
- package/dist/schema.d.js +0 -1
- package/dist/schema.d.js.map +0 -1
- package/dist/service.d.js +0 -1
- package/dist/service.d.js.map +0 -1
- package/dist/targets/bridge-target.d.js +0 -1
- package/dist/targets/bridge-target.d.js.map +0 -1
- package/dist/workspace/browser-capture.d.js +0 -1
- package/dist/workspace/browser-capture.d.js.map +0 -1
- package/dist/workspace/browser-workspace-desktop.d.js +0 -1
- package/dist/workspace/browser-workspace-desktop.d.js.map +0 -1
- package/dist/workspace/browser-workspace-elements.d.js +0 -1
- package/dist/workspace/browser-workspace-elements.d.js.map +0 -1
- package/dist/workspace/browser-workspace-forms.d.js +0 -1
- package/dist/workspace/browser-workspace-forms.d.js.map +0 -1
- package/dist/workspace/browser-workspace-helpers.d.js +0 -1
- package/dist/workspace/browser-workspace-helpers.d.js.map +0 -1
- package/dist/workspace/browser-workspace-jsdom.d.js +0 -1
- package/dist/workspace/browser-workspace-jsdom.d.js.map +0 -1
- package/dist/workspace/browser-workspace-network.d.js +0 -1
- package/dist/workspace/browser-workspace-network.d.js.map +0 -1
- package/dist/workspace/browser-workspace-snapshots.d.js +0 -1
- package/dist/workspace/browser-workspace-snapshots.d.js.map +0 -1
- package/dist/workspace/browser-workspace-state.d.js +0 -1
- package/dist/workspace/browser-workspace-state.d.js.map +0 -1
- package/dist/workspace/browser-workspace-types.d.js +0 -1
- package/dist/workspace/browser-workspace-types.d.js.map +0 -1
- package/dist/workspace/browser-workspace-web.d.js +0 -1
- package/dist/workspace/browser-workspace-web.d.js.map +0 -1
- package/dist/workspace/browser-workspace.d.js +0 -11
- package/dist/workspace/browser-workspace.d.js.map +0 -1
- package/dist/workspace/index.d.js +0 -3
- package/dist/workspace/index.d.js.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertBrowserWorkspaceConnectorSecretsNotExported,
|
|
3
|
+
assertBrowserWorkspaceUserScriptAllowed,
|
|
3
4
|
createBrowserWorkspaceCommandTargetError,
|
|
4
5
|
DEFAULT_TIMEOUT_MS,
|
|
6
|
+
isBrowserWorkspaceUserScriptAllowed,
|
|
5
7
|
normalizeEnvValue,
|
|
6
8
|
resolveBrowserWorkspaceCommandElementRefs
|
|
7
9
|
} from "./browser-workspace-helpers.js";
|
|
@@ -24,13 +26,13 @@ async function readErrorBody(response) {
|
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
function resolveBrowserWorkspaceBridgeConfig(env = process.env) {
|
|
27
|
-
const baseUrl = normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_URL)
|
|
29
|
+
const baseUrl = normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_URL);
|
|
28
30
|
if (!baseUrl) {
|
|
29
31
|
return null;
|
|
30
32
|
}
|
|
31
33
|
return {
|
|
32
34
|
baseUrl: baseUrl.replace(/\/{1,1024}$/, ""),
|
|
33
|
-
token: normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_TOKEN)
|
|
35
|
+
token: normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_TOKEN)
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
38
|
function isBrowserWorkspaceBridgeConfigured(env = process.env) {
|
|
@@ -99,7 +101,25 @@ async function snapshotBrowserWorkspaceTab(id, env = process.env) {
|
|
|
99
101
|
env
|
|
100
102
|
);
|
|
101
103
|
}
|
|
102
|
-
function
|
|
104
|
+
function desktopBrowserWorkspaceWaitScriptBranch(env) {
|
|
105
|
+
if (isBrowserWorkspaceUserScriptAllowed(env)) {
|
|
106
|
+
return `
|
|
107
|
+
if (command.script) {
|
|
108
|
+
const fn = new Function("document", "window", "location", "return (" + command.script + ");");
|
|
109
|
+
if (fn(document, window, location)) {
|
|
110
|
+
resolve({ ok: true, script: true });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}`;
|
|
114
|
+
}
|
|
115
|
+
return `
|
|
116
|
+
if (command.script) {
|
|
117
|
+
reject(new Error("Browser workspace wait script is disabled (GHSA-mhhr-9ph9-64j7)."));
|
|
118
|
+
return;
|
|
119
|
+
}`;
|
|
120
|
+
}
|
|
121
|
+
function createDesktopBrowserWorkspaceCommandScript(command, env = process.env) {
|
|
122
|
+
const waitScriptBranch = desktopBrowserWorkspaceWaitScriptBranch(env);
|
|
103
123
|
return `
|
|
104
124
|
(() => {
|
|
105
125
|
const command = ${JSON.stringify(command)};
|
|
@@ -730,24 +750,23 @@ function createDesktopBrowserWorkspaceCommandScript(command) {
|
|
|
730
750
|
const deadline = Date.now() + (Number(command.timeoutMs) || 4000);
|
|
731
751
|
const check = () => {
|
|
732
752
|
try {
|
|
733
|
-
if (command.selector
|
|
753
|
+
if (command.selector) {
|
|
734
754
|
const found = findTarget();
|
|
735
755
|
const visible =
|
|
736
|
-
command.state === "
|
|
737
|
-
? found
|
|
738
|
-
:
|
|
756
|
+
command.state === "hidden"
|
|
757
|
+
? !found || !isVisible(found)
|
|
758
|
+
: found && isVisible(found);
|
|
739
759
|
if (visible) {
|
|
740
760
|
resolve({ ok: true, selector: command.selector, state: command.state || "visible" });
|
|
741
761
|
return;
|
|
742
762
|
}
|
|
743
763
|
}
|
|
744
|
-
if (
|
|
745
|
-
|
|
746
|
-
(command.state === "
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
return;
|
|
764
|
+
if (command.findBy) {
|
|
765
|
+
const found = findSemantic();
|
|
766
|
+
if (command.state === "hidden" ? !found : found) {
|
|
767
|
+
resolve({ findBy: command.findBy, ok: true });
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
751
770
|
}
|
|
752
771
|
if (command.text && normalize(document.body?.textContent).includes(command.text)) {
|
|
753
772
|
resolve({ ok: true, text: command.text });
|
|
@@ -757,13 +776,7 @@ function createDesktopBrowserWorkspaceCommandScript(command) {
|
|
|
757
776
|
resolve({ ok: true, url: location.href });
|
|
758
777
|
return;
|
|
759
778
|
}
|
|
760
|
-
|
|
761
|
-
const fn = new Function("document", "window", "location", "return (" + command.script + ");");
|
|
762
|
-
if (fn(document, window, location)) {
|
|
763
|
-
resolve({ ok: true, script: true });
|
|
764
|
-
return;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
779
|
+
${waitScriptBranch}
|
|
767
780
|
if (Date.now() >= deadline) {
|
|
768
781
|
reject(new Error("Timed out waiting for browser workspace condition."));
|
|
769
782
|
return;
|
|
@@ -1471,16 +1484,25 @@ async function loadDesktopBrowserWorkspaceSessionState(command, payload, env) {
|
|
|
1471
1484
|
);
|
|
1472
1485
|
}
|
|
1473
1486
|
async function executeDesktopBrowserWorkspaceDomCommand(command, env) {
|
|
1487
|
+
assertBrowserWorkspaceUserScriptAllowed(
|
|
1488
|
+
command.script,
|
|
1489
|
+
"wait",
|
|
1490
|
+
"desktop",
|
|
1491
|
+
env
|
|
1492
|
+
);
|
|
1474
1493
|
const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);
|
|
1475
1494
|
const startedAt = Date.now();
|
|
1476
1495
|
command = resolveBrowserWorkspaceCommandElementRefs(command, "desktop", id);
|
|
1477
1496
|
const result = await evaluateBrowserWorkspaceTab(
|
|
1478
1497
|
{
|
|
1479
1498
|
id,
|
|
1480
|
-
script: createDesktopBrowserWorkspaceCommandScript(
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1499
|
+
script: createDesktopBrowserWorkspaceCommandScript(
|
|
1500
|
+
{
|
|
1501
|
+
...command,
|
|
1502
|
+
id
|
|
1503
|
+
},
|
|
1504
|
+
env
|
|
1505
|
+
)
|
|
1484
1506
|
},
|
|
1485
1507
|
env
|
|
1486
1508
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/workspace/browser-workspace-desktop.ts"],"sourcesContent":["import {\n assertBrowserWorkspaceConnectorSecretsNotExported,\n createBrowserWorkspaceCommandTargetError,\n DEFAULT_TIMEOUT_MS,\n normalizeEnvValue,\n resolveBrowserWorkspaceCommandElementRefs,\n} from \"./browser-workspace-helpers.js\";\nimport {\n appendBrowserWorkspaceProfilerEntry,\n appendBrowserWorkspaceTraceEntry,\n getBrowserWorkspaceRuntimeState,\n registerBrowserWorkspaceElementRefs,\n} from \"./browser-workspace-state.js\";\nimport type {\n BrowserWorkspaceBridgeConfig,\n BrowserWorkspaceCommand,\n BrowserWorkspaceCommandResult,\n BrowserWorkspaceDomElementSummary,\n BrowserWorkspaceSnapshotRecord,\n BrowserWorkspaceTab,\n EvaluateBrowserWorkspaceTabRequest,\n} from \"./browser-workspace-types.js\";\n\nasync function assertDesktopBrowserWorkspaceCanAccessProfileSecrets(\n id: string,\n env: NodeJS.ProcessEnv,\n operation: string,\n): Promise<void> {\n const payload = await requestBrowserWorkspace<{\n tabs?: BrowserWorkspaceTab[];\n }>(\"/tabs\", undefined, env);\n const tab = payload.tabs?.find((entry) => entry.id === id) ?? null;\n assertBrowserWorkspaceConnectorSecretsNotExported(tab?.partition, operation);\n}\n\nasync function readErrorBody(response: Response): Promise<string> {\n try {\n return (await response.text()).trim().slice(0, 240);\n } catch {\n return \"\";\n }\n}\n\nexport function resolveBrowserWorkspaceBridgeConfig(\n env: NodeJS.ProcessEnv = process.env,\n): BrowserWorkspaceBridgeConfig | null {\n const baseUrl =\n normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_URL) ??\n normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_URL);\n if (!baseUrl) {\n return null;\n }\n\n return {\n baseUrl: baseUrl.replace(/\\/{1,1024}$/, \"\"),\n token:\n normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_TOKEN) ??\n normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_TOKEN),\n };\n}\n\nexport function isBrowserWorkspaceBridgeConfigured(\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n return resolveBrowserWorkspaceBridgeConfig(env) !== null;\n}\n\nexport function getBrowserWorkspaceUnavailableMessage(): string {\n return \"Eliza browser workspace desktop bridge is unavailable.\";\n}\n\nexport async function requestBrowserWorkspace<T>(\n path: string,\n init?: RequestInit,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<T> {\n const config = resolveBrowserWorkspaceBridgeConfig(env);\n if (!config) {\n throw new Error(getBrowserWorkspaceUnavailableMessage());\n }\n\n const headers = new Headers(init?.headers ?? {});\n headers.set(\"Accept\", \"application/json\");\n if (!headers.has(\"Content-Type\") && init?.body) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n if (config.token) {\n headers.set(\"Authorization\", `Bearer ${config.token}`);\n }\n\n const response = await fetch(`${config.baseUrl}${path}`, {\n ...init,\n headers,\n signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n const details = await readErrorBody(response);\n throw new Error(\n `Browser workspace request failed (${response.status})${details ? `: ${details}` : \"\"}`,\n );\n }\n\n return (await response.json()) as T;\n}\n\nexport async function evaluateBrowserWorkspaceTab(\n request: EvaluateBrowserWorkspaceTabRequest,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<unknown> {\n if (!isBrowserWorkspaceBridgeConfigured(env)) {\n throw new Error(\n \"Eliza browser workspace eval is only available in the desktop app.\",\n );\n }\n\n const evalBody: { script: string; partition?: string } = {\n script: request.script,\n };\n if (request.partition !== undefined) {\n evalBody.partition = request.partition;\n }\n\n const payload = await requestBrowserWorkspace<{ result: unknown }>(\n `/tabs/${encodeURIComponent(request.id)}/eval`,\n {\n method: \"POST\",\n body: JSON.stringify(evalBody),\n },\n env,\n );\n return payload.result;\n}\n\nexport async function snapshotBrowserWorkspaceTab(\n id: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<{ data: string }> {\n if (!isBrowserWorkspaceBridgeConfigured(env)) {\n throw new Error(\n \"Eliza browser workspace snapshot is only available in the desktop app.\",\n );\n }\n\n return await requestBrowserWorkspace<{ data: string }>(\n `/tabs/${encodeURIComponent(id)}/snapshot`,\n undefined,\n env,\n );\n}\n\nexport function createDesktopBrowserWorkspaceCommandScript(\n command: BrowserWorkspaceCommand,\n): string {\n return `\n(() => {\n const command = ${JSON.stringify(command)};\n const normalize = (value) => String(value ?? \"\").replace(/\\\\s+/g, \" \").trim();\n const textMatches = (candidate, wanted, exact = false) => {\n const left = normalize(candidate).toLowerCase();\n const right = normalize(wanted).toLowerCase();\n if (!left || !right) return false;\n return exact ? left === right : left.includes(right);\n };\n const selectorFor = (element) => {\n if (!element) return \"\";\n if (element.id) return \"#\" + element.id.replace(/[^a-zA-Z0-9_-]/g, \"\\\\\\\\$&\");\n const testId = element.getAttribute?.(\"data-testid\");\n if (testId) return \\`[data-testid=\"\\${testId}\"]\\`;\n const name = element.getAttribute?.(\"name\");\n if (name) return \\`\\${element.tagName.toLowerCase()}[name=\"\\${name}\"]\\`;\n const type = element.getAttribute?.(\"type\");\n if (type) return \\`\\${element.tagName.toLowerCase()}[type=\"\\${type}\"]\\`;\n let index = 1;\n let previous = element.previousElementSibling;\n while (previous) {\n if (previous.tagName === element.tagName) index += 1;\n previous = previous.previousElementSibling;\n }\n return \\`\\${element.tagName.toLowerCase()}:nth-of-type(\\${index})\\`;\n };\n const serialize = (element) => {\n const value =\n element instanceof HTMLInputElement ||\n element instanceof HTMLTextAreaElement ||\n element instanceof HTMLSelectElement\n ? element.value\n : null;\n return {\n selector: selectorFor(element),\n tag: element.tagName.toLowerCase(),\n text: normalize(value ?? element.textContent),\n type: element.getAttribute?.(\"type\"),\n name: element.getAttribute?.(\"name\"),\n href: element.getAttribute?.(\"href\"),\n value: typeof value === \"string\" ? value : null,\n };\n };\n const searchTexts = (element) => {\n const labelText = element.id\n ? Array.from(document.querySelectorAll('label[for=\"' + element.id + '\"]'))\n .map((label) => label.textContent)\n .join(\" \")\n : \"\";\n return [\n element.textContent,\n element.getAttribute?.(\"aria-label\"),\n element.getAttribute?.(\"placeholder\"),\n element.getAttribute?.(\"title\"),\n element.getAttribute?.(\"name\"),\n element.getAttribute?.(\"alt\"),\n element.getAttribute?.(\"data-testid\"),\n labelText,\n element.value,\n ]\n .map((value) => normalize(value))\n .filter(Boolean);\n };\n const isVisible = (element) => {\n if (!element) return false;\n if (element.hasAttribute?.(\"hidden\") || element.getAttribute?.(\"aria-hidden\") === \"true\") {\n return false;\n }\n const style = element.style || {};\n return style.display !== \"none\" && style.visibility !== \"hidden\";\n };\n const nativeRole = (element) => {\n const explicit = element.getAttribute?.(\"role\")?.trim()?.toLowerCase();\n if (explicit) return explicit;\n const tag = element.tagName.toLowerCase();\n if (tag === \"a\" && element.getAttribute?.(\"href\")) return \"link\";\n if (tag === \"button\") return \"button\";\n if (tag === \"select\") return \"combobox\";\n if (tag === \"option\") return \"option\";\n if (tag === \"textarea\") return \"textbox\";\n if (tag === \"form\") return \"form\";\n if (/^h[1-6]$/.test(tag)) return \"heading\";\n if (tag === \"input\") {\n const type = (element.type || \"text\").toLowerCase();\n if (type === \"checkbox\") return \"checkbox\";\n if (type === \"radio\") return \"radio\";\n if ([\"button\", \"submit\", \"reset\", \"image\"].includes(type)) return \"button\";\n return \"textbox\";\n }\n return null;\n };\n const findByText = (wanted) => {\n const needle = normalize(wanted).toLowerCase();\n if (!needle) return null;\n const elements = Array.from(document.querySelectorAll(\n \"a, button, input, textarea, select, option, label, h1, h2, h3, [role='button'], [data-testid]\"\n ));\n for (const element of elements) {\n const haystacks = [\n element.textContent,\n element.getAttribute?.(\"aria-label\"),\n element.getAttribute?.(\"placeholder\"),\n element.getAttribute?.(\"title\"),\n element.getAttribute?.(\"name\"),\n element.value,\n ]\n .map((value) => normalize(value))\n .filter(Boolean)\n .map((value) => value.toLowerCase());\n if (haystacks.some((value) => value.includes(needle))) {\n return element;\n }\n }\n return null;\n };\n const findByLabel = (wanted, exact = false) => {\n const labels = Array.from(document.querySelectorAll(\"label\"));\n for (const label of labels) {\n if (!textMatches(label.textContent, wanted, exact)) continue;\n const forId = label.getAttribute(\"for\");\n if (forId) {\n const explicit = document.getElementById(forId);\n if (explicit) return explicit;\n }\n const nested = label.querySelector(\"input, textarea, select, button\");\n if (nested) return nested;\n }\n return null;\n };\n const findByRole = (role, name, exact = false) => {\n const candidates = Array.from(\n document.querySelectorAll(\n \"a, button, input, textarea, select, option, form, h1, h2, h3, h4, h5, h6, [role], [data-testid]\"\n )\n );\n for (const candidate of candidates) {\n if (nativeRole(candidate) !== role.trim().toLowerCase()) continue;\n if (!name) return candidate;\n if (searchTexts(candidate).some((value) => textMatches(value, name, exact))) {\n return candidate;\n }\n }\n return null;\n };\n const trimQuoted = (value) => {\n const trimmed = String(value || \"\").trim();\n const hasTextMatch = trimmed.match(/^has-text\\\\((?:\"([^\"]*)\"|'([^']*)')\\\\)$/i);\n if (hasTextMatch?.[1] || hasTextMatch?.[2]) {\n return (hasTextMatch[1] || hasTextMatch[2] || \"\").trim();\n }\n if (\n (trimmed.startsWith('\"') && trimmed.endsWith('\"')) ||\n (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\"))\n ) {\n return trimmed.slice(1, -1).trim();\n }\n return trimmed;\n };\n const normalizeSelectorSyntax = (selector) => {\n let normalized = String(selector || \"\").trim();\n normalized = normalized.replace(\n /^role\\\\s*[:=]\\\\s*([a-z0-9_-]+)\\\\s+name\\\\s*[:=]\\\\s*(.+)$/i,\n \"role=$1[name=$2]\"\n );\n normalized = normalized.replace(\n /^((?:label|text|placeholder|alt|title|testid|data-testid)\\\\s*[:=]\\\\s*(?:has-text\\\\((?:\"[^\"]*\"|'[^']*')\\\\)|\"[^\"]+\"|'[^']+'|[^>]+?))\\\\s+((?:input|textarea|select)[\\\\s\\\\S]*)$/i,\n \"$1 >> $2\"\n );\n return normalized;\n };\n const parseSemanticSelector = (selector) => {\n const trimmed = normalizeSelectorSyntax(selector);\n const match = trimmed.match(/^([a-z-]+)\\\\s*[:=]\\\\s*(.+)$/i);\n if (!match) return null;\n const kind = match[1]?.trim()?.toLowerCase();\n const rawValue = match[2]?.trim() || \"\";\n if (!kind || !rawValue) return null;\n switch (kind) {\n case \"alt\":\n return { findBy: \"alt\", text: trimQuoted(rawValue) };\n case \"css\":\n return { selector: trimQuoted(rawValue) };\n case \"data-testid\":\n case \"testid\":\n return { findBy: \"testid\", text: trimQuoted(rawValue) };\n case \"label\":\n return { findBy: \"label\", text: trimQuoted(rawValue) };\n case \"placeholder\":\n return { findBy: \"placeholder\", text: trimQuoted(rawValue) };\n case \"role\": {\n const roleMatch = rawValue.match(\n /^([a-z0-9_-]+)(?:\\\\s*\\\\[\\\\s*name\\\\s*[:=]\\\\s*(.+?)\\\\s*\\\\])?$/i\n );\n if (!roleMatch?.[1]) return null;\n return {\n findBy: \"role\",\n name: roleMatch[2] ? trimQuoted(roleMatch[2]) : undefined,\n role: roleMatch[1].trim().toLowerCase(),\n };\n }\n case \"text\":\n return { findBy: \"text\", text: trimQuoted(rawValue) };\n case \"title\":\n return { findBy: \"title\", text: trimQuoted(rawValue) };\n default:\n return null;\n }\n };\n const mergeSelectorCommand = (selector) => {\n const parsed = parseSemanticSelector(selector);\n if (!parsed) return null;\n return { ...command, ...parsed, selector: parsed.selector };\n };\n const queryOne = (selector) => {\n try {\n return document.querySelector(selector);\n } catch {\n throw new Error(\"Invalid selector \" + selector);\n }\n };\n const queryAll = (selector) => {\n try {\n return Array.from(document.querySelectorAll(selector));\n } catch {\n throw new Error(\"Invalid selector \" + selector);\n }\n };\n const findSemantic = (targetCommand = command) => {\n switch (targetCommand.findBy) {\n case \"alt\":\n return Array.from(document.querySelectorAll(\"[alt]\")).find((element) =>\n textMatches(\n element.getAttribute(\"alt\"),\n targetCommand.text,\n targetCommand.exact\n )\n ) || null;\n case \"first\":\n return targetCommand.selector ? queryOne(targetCommand.selector) : null;\n case \"label\":\n return targetCommand.text\n ? findByLabel(targetCommand.text, targetCommand.exact)\n : null;\n case \"last\":\n return targetCommand.selector\n ? queryAll(targetCommand.selector).at(-1) || null\n : null;\n case \"nth\":\n return targetCommand.selector && Number.isInteger(targetCommand.index)\n ? queryAll(targetCommand.selector).at(targetCommand.index) || null\n : null;\n case \"placeholder\":\n return Array.from(document.querySelectorAll(\"[placeholder]\")).find((element) =>\n textMatches(\n element.getAttribute(\"placeholder\"),\n targetCommand.text,\n targetCommand.exact\n )\n ) || null;\n case \"role\":\n return targetCommand.role\n ? findByRole(\n targetCommand.role,\n targetCommand.name,\n targetCommand.exact\n )\n : null;\n case \"testid\":\n return targetCommand.text\n ? document.querySelector('[data-testid=\"' + targetCommand.text + '\"]')\n : null;\n case \"text\":\n return targetCommand.text ? findByText(targetCommand.text) : null;\n case \"title\":\n return Array.from(document.querySelectorAll(\"[title]\")).find((element) =>\n textMatches(\n element.getAttribute(\"title\"),\n targetCommand.text,\n targetCommand.exact\n )\n ) || null;\n default:\n return null;\n }\n };\n const findTarget = () => {\n if (command.selector) {\n const selectorChain = normalizeSelectorSyntax(command.selector)\n .split(/s*>>s*/)\n .map((segment) => segment.trim())\n .filter(Boolean);\n if (selectorChain.length > 1) {\n let current = queryTarget(selectorChain[0]);\n for (let index = 1; current && index < selectorChain.length; index += 1) {\n const segment = selectorChain[index];\n if (!segment) continue;\n if (typeof current.matches === \"function\" && current.matches(segment)) {\n continue;\n }\n if (\n /^(input|textarea|select)(?:[[^]]+])?$/i.test(segment) &&\n (current.tagName === \"INPUT\" ||\n current.tagName === \"TEXTAREA\" ||\n current.tagName === \"SELECT\")\n ) {\n continue;\n }\n current = queryOneWithin(current, segment);\n }\n return current;\n }\n return queryTarget(command.selector);\n }\n if (command.findBy) return findSemantic();\n if (command.text) return findByText(command.text);\n return null;\n };\n const queryOneWithin = (root, selector) => {\n try {\n return root.querySelector(selector);\n } catch {\n throw new Error(\"Invalid selector \" + selector);\n }\n };\n const queryTarget = (selector) => {\n const semantic = mergeSelectorCommand(selector);\n if (semantic) return findSemantic(semantic);\n return queryOne(selector);\n };\n const inspect = () =>\n Array.from(\n document.querySelectorAll(\n \"a, button, input, textarea, select, form, [role='button'], [data-testid]\"\n )\n )\n .slice(0, 40)\n .map((element) => serialize(element));\n const snapshot = () => ({\n title: document.title,\n url: location.href,\n bodyText: normalize(document.body?.textContent).slice(0, 800),\n elements: inspect(),\n });\n const setInputValue = (appendMode, target) => {\n const element = target || findTarget();\n if (!element) {\n throw new Error(\"Target element was not found.\");\n }\n if (\n !(\n element instanceof HTMLInputElement ||\n element instanceof HTMLTextAreaElement ||\n element instanceof HTMLSelectElement\n )\n ) {\n throw new Error(\"Target element is not an input, textarea, or select.\");\n }\n const nextValue = appendMode ? \\`\\${element.value ?? \"\"}\\${command.value ?? \"\"}\\` : (command.value ?? \"\");\n element.value = nextValue;\n element.dispatchEvent(new Event(\"input\", { bubbles: true }));\n element.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { selector: selectorFor(element), value: element.value };\n };\n const setChecked = (targetValue) => {\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n if (!(element instanceof HTMLInputElement)) {\n throw new Error(\"Target element is not a checkbox or radio input.\");\n }\n const type = (element.type || \"\").toLowerCase();\n if (type !== \"checkbox\" && type !== \"radio\") {\n throw new Error(\"Target element is not a checkbox or radio input.\");\n }\n element.checked = targetValue;\n element.dispatchEvent(new Event(\"input\", { bubbles: true }));\n element.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { checked: element.checked, selector: selectorFor(element) };\n };\n const setSelectValue = () => {\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n if (!(element instanceof HTMLSelectElement)) {\n throw new Error(\"Target element is not a select.\");\n }\n const targetValue = command.value ?? \"\";\n const option = Array.from(element.options).find(\n (entry) =>\n entry.value === targetValue || textMatches(entry.textContent, targetValue, true)\n );\n if (!option) {\n throw new Error(\"Select option was not found.\");\n }\n element.value = option.value;\n option.selected = true;\n element.dispatchEvent(new Event(\"input\", { bubbles: true }));\n element.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { selector: selectorFor(element), value: element.value };\n };\n const focusElement = (element) => {\n if (!element) throw new Error(\"Target element was not found.\");\n if (typeof element.focus === \"function\") {\n element.focus();\n }\n return {\n focused: document.activeElement === element,\n selector: selectorFor(element),\n };\n };\n const hoverElement = (element) => {\n if (!element) throw new Error(\"Target element was not found.\");\n element.setAttribute(\"data-eliza-hover\", \"true\");\n return { hovered: true, selector: selectorFor(element) };\n };\n const activateElement = (subaction, element) => {\n if (!element) throw new Error(\"Target element was not found.\");\n if (subaction === \"dblclick\") {\n element.dispatchEvent(new MouseEvent(\"dblclick\", { bubbles: true }));\n }\n if (typeof element.click === \"function\") {\n element.click();\n }\n return {\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n element: serialize(element),\n url: location.href,\n };\n };\n const ensureTabKit = () => {\n const kit = window.__elizaTabKit;\n if (!kit) {\n throw new Error(\n \"browser tab kit not installed (BROWSER_TAB_PRELOAD_SCRIPT missing)\",\n );\n }\n return kit;\n };\n const runRealisticSubaction = (subaction) => {\n const kit = ensureTabKit();\n const cursorDuration = Number(command.cursorDurationMs) || 220;\n if (subaction === \"cursor-hide\") {\n kit.cursor.hide();\n return { hidden: true };\n }\n if (subaction === \"cursor-move\") {\n const x = Number(command.x);\n const y = Number(command.y);\n if (!Number.isFinite(x) || !Number.isFinite(y)) {\n throw new Error(\"cursor-move requires x and y\");\n }\n kit.cursor.show();\n return Promise.resolve(\n kit.cursor.moveTo({ x: x, y: y }, { durationMs: cursorDuration }),\n ).then(() => ({ x: x, y: y }));\n }\n if (subaction === \"realistic-press\") {\n const target = findTarget() || document.activeElement || document.body;\n const key = command.key || \"Enter\";\n target.dispatchEvent(\n new KeyboardEvent(\"keydown\", {\n key: key,\n bubbles: true,\n cancelable: true,\n composed: true,\n }),\n );\n target.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key: key,\n bubbles: true,\n cancelable: true,\n composed: true,\n }),\n );\n return { key: key, selector: selectorFor(target), url: location.href };\n }\n const element = findTarget();\n if (!element) {\n throw new Error(\"Target element was not found.\");\n }\n kit.cursor.show();\n if (subaction === \"realistic-click\") {\n kit.cursor.highlight(element);\n return Promise.resolve(\n kit.dispatchPointerSequence(element, { button: 0 }),\n ).then(() => ({\n element: serialize(element),\n url: location.href,\n }));\n }\n if (subaction === \"realistic-fill\" || subaction === \"realistic-type\") {\n const value = command.value ?? command.text ?? \"\";\n const replace = subaction === \"realistic-fill\" || command.replace === true;\n const perCharDelayMs = Number(command.perCharDelayMs);\n kit.cursor.highlight(element);\n return Promise.resolve(\n kit\n .dispatchPointerSequence(element, { button: 0 })\n .then(() =>\n kit.typeRealistic(element, value, {\n replace: replace,\n perCharDelayMs: Number.isFinite(perCharDelayMs)\n ? perCharDelayMs\n : undefined,\n }),\n ),\n ).then(() => ({\n element: serialize(element),\n value: element.value,\n }));\n }\n if (subaction === \"realistic-upload\") {\n const url = (command.files && command.files[0]) || command.url || command.value;\n if (!url) {\n throw new Error(\"realistic-upload requires files[0] or url\");\n }\n if (element.tagName !== \"INPUT\" || element.type !== \"file\") {\n throw new Error(\"realistic-upload target must be input[type=file]\");\n }\n kit.cursor.highlight(element);\n return Promise.resolve(kit.setFileInput(element, url, {})).then((info) => ({\n element: serialize(element),\n upload: info,\n }));\n }\n throw new Error(\"Unsupported realistic subaction: \" + subaction);\n };\n const keyboardTarget = () => findTarget() || document.activeElement || document.body;\n const keyboardWrite = (appendMode) => {\n const target = keyboardTarget();\n if (\n !(\n target instanceof HTMLInputElement ||\n target instanceof HTMLTextAreaElement ||\n target instanceof HTMLSelectElement\n )\n ) {\n throw new Error(\"Keyboard text input requires an input, textarea, or select target.\");\n }\n return setInputValue(appendMode, target);\n };\n const keyPhase = (phase) => {\n const target = keyboardTarget();\n const key = command.key || \"Enter\";\n target.dispatchEvent(new KeyboardEvent(phase, { key, bubbles: true }));\n return { key, phase, selector: selectorFor(target) };\n };\n const scrollTarget = () => findTarget();\n const scroll = () => {\n const target = scrollTarget();\n const direction = command.direction || \"down\";\n const pixels = Math.max(1, Math.abs(Number(command.pixels) || 240));\n const axis = direction === \"left\" || direction === \"right\" ? \"x\" : \"y\";\n const delta = direction === \"up\" || direction === \"left\" ? -pixels : pixels;\n if (target instanceof HTMLElement) {\n if (axis === \"y\") {\n target.scrollTop = (target.scrollTop || 0) + delta;\n return { axis, selector: selectorFor(target), value: target.scrollTop };\n }\n target.scrollLeft = (target.scrollLeft || 0) + delta;\n return { axis, selector: selectorFor(target), value: target.scrollLeft };\n }\n if (axis === \"y\") {\n window.scrollBy(0, delta);\n return { axis, selector: null, value: window.scrollY };\n }\n window.scrollBy(delta, 0);\n return { axis, selector: null, value: window.scrollX };\n };\n const getResult = () => {\n if (command.getMode === \"title\") return document.title;\n if (command.getMode === \"url\") return location.href;\n if (command.getMode === \"count\") {\n if (!command.selector) throw new Error(\"count requires selector\");\n const semantic = mergeSelectorCommand(command.selector);\n return semantic ? Number(Boolean(findSemantic(semantic))) : queryAll(command.selector).length;\n }\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n switch (command.getMode) {\n case \"attr\":\n if (!command.attribute) throw new Error(\"attr lookups require attribute\");\n return element.getAttribute(command.attribute);\n case \"box\":\n return element.getBoundingClientRect();\n case \"checked\":\n return element instanceof HTMLInputElement\n ? Boolean(element.checked)\n : element instanceof HTMLOptionElement\n ? Boolean(element.selected)\n : false;\n case \"enabled\":\n return \"disabled\" in element ? !Boolean(element.disabled) : true;\n case \"html\":\n return element.innerHTML;\n case \"styles\": {\n const computed = getComputedStyle(element);\n return {\n display: computed.display || null,\n visibility: computed.visibility || null,\n opacity: computed.opacity || null,\n };\n }\n case \"text\":\n return normalize(element.textContent);\n case \"value\":\n return element.value ?? element.getAttribute?.(\"value\");\n case \"visible\":\n return isVisible(element);\n default:\n return normalize(element.textContent);\n }\n };\n const waitForCondition = () =>\n new Promise((resolve, reject) => {\n if (\n !command.selector &&\n !command.findBy &&\n !command.text &&\n !command.url &&\n !command.script &&\n Number.isFinite(Number(command.timeoutMs))\n ) {\n const waitedMs = Math.max(0, Number(command.timeoutMs) || 0);\n setTimeout(() => resolve({ ok: true, waitedMs }), waitedMs);\n return;\n }\n const deadline = Date.now() + (Number(command.timeoutMs) || 4000);\n const check = () => {\n try {\n if (command.selector && findTarget()) {\n const found = findTarget();\n const visible =\n command.state === \"visible\"\n ? found && isVisible(found)\n : !found || !isVisible(found);\n if (visible) {\n resolve({ ok: true, selector: command.selector, state: command.state || \"visible\" });\n return;\n }\n }\n if (\n command.findBy &&\n (command.state === \"visible\") &&\n findSemantic()\n ) {\n resolve({ findBy: command.findBy, ok: true });\n return;\n }\n if (command.text && normalize(document.body?.textContent).includes(command.text)) {\n resolve({ ok: true, text: command.text });\n return;\n }\n if (command.url && location.href.includes(command.url)) {\n resolve({ ok: true, url: location.href });\n return;\n }\n if (command.script) {\n const fn = new Function(\"document\", \"window\", \"location\", \"return (\" + command.script + \");\");\n if (fn(document, window, location)) {\n resolve({ ok: true, script: true });\n return;\n }\n }\n if (Date.now() >= deadline) {\n reject(new Error(\"Timed out waiting for browser workspace condition.\"));\n return;\n }\n setTimeout(check, 100);\n } catch (error) {\n reject(error);\n }\n };\n check();\n });\n\n switch (command.subaction) {\n case \"inspect\":\n return { title: document.title, url: location.href, elements: inspect() };\n case \"snapshot\":\n return snapshot();\n case \"get\":\n return { value: getResult() };\n case \"find\": {\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n switch (command.action) {\n case \"check\":\n return setChecked(true);\n case \"click\":\n return activateElement(\"click\", element);\n case \"fill\":\n return setInputValue(false, element);\n case \"focus\":\n return focusElement(element);\n case \"hover\":\n return hoverElement(element);\n case \"text\":\n case undefined:\n return { element: serialize(element), value: normalize(element.textContent) };\n case \"type\":\n return setInputValue(true, element);\n case \"uncheck\":\n return setChecked(false);\n default:\n throw new Error(\"Unsupported find action.\");\n }\n }\n case \"click\": {\n const element = findTarget();\n return activateElement(\"click\", element);\n }\n case \"dblclick\": {\n const element = findTarget();\n return activateElement(\"dblclick\", element);\n }\n case \"check\":\n return setChecked(true);\n case \"fill\":\n return setInputValue(false);\n case \"focus\": {\n const element = findTarget();\n return focusElement(element);\n }\n case \"hover\": {\n const element = findTarget();\n return hoverElement(element);\n }\n case \"keyboardinserttext\":\n return keyboardWrite(false);\n case \"keyboardtype\":\n return keyboardWrite(true);\n case \"keydown\":\n return keyPhase(\"keydown\");\n case \"keyup\":\n return keyPhase(\"keyup\");\n case \"type\":\n return setInputValue(true);\n case \"press\": {\n const target = findTarget() ?? document.activeElement ?? document.body;\n const key = command.key || \"Enter\";\n target.dispatchEvent(new KeyboardEvent(\"keydown\", { key, bubbles: true }));\n target.dispatchEvent(new KeyboardEvent(\"keyup\", { key, bubbles: true }));\n return { key, url: location.href };\n }\n case \"realistic-click\":\n case \"realistic-fill\":\n case \"realistic-type\":\n case \"realistic-press\":\n case \"realistic-upload\":\n case \"cursor-move\":\n case \"cursor-hide\":\n return runRealisticSubaction(command.subaction);\n case \"scroll\":\n return scroll();\n case \"scrollinto\": {\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n if (typeof element.scrollIntoView === \"function\") {\n element.scrollIntoView();\n }\n return { scrolled: true, selector: selectorFor(element) };\n }\n case \"select\":\n return setSelectValue();\n case \"uncheck\":\n return setChecked(false);\n case \"wait\":\n return waitForCondition();\n case \"back\":\n history.back();\n return { url: location.href, title: document.title };\n case \"forward\":\n history.forward();\n return { url: location.href, title: document.title };\n case \"reload\":\n location.reload();\n return { url: location.href, title: document.title };\n default:\n throw new Error(\\`Unsupported desktop browser subaction: \\${command.subaction}\\`);\n }\n})()\n`.trim();\n}\n\nexport function createDesktopBrowserWorkspaceUtilityScript(\n command: BrowserWorkspaceCommand,\n): string {\n return `\n(() => {\n const command = ${JSON.stringify(command)};\n const normalize = (value) => String(value ?? \"\").replace(/\\\\s+/g, \" \").trim();\n const state =\n window.__elizaBrowserWorkspaceState ||\n (window.__elizaBrowserWorkspaceState = {\n clipboardText: \"\",\n consoleEntries: [],\n currentFrame: null,\n dialog: null,\n errors: [],\n highlightedSelector: null,\n mouse: { buttons: [], x: 0, y: 0 },\n networkHar: { active: false, entries: [], startedAt: null },\n networkNextRequestId: 1,\n networkRequests: [],\n networkRoutes: [],\n settings: {\n credentials: null,\n device: null,\n geo: null,\n headers: {},\n media: null,\n offline: false,\n viewport: null\n }\n });\n const patternMatches = (pattern, value) => {\n const trimmed = String(pattern ?? \"\").trim();\n if (!trimmed) return false;\n if (!trimmed.includes(\"*\")) return String(value ?? \"\").includes(trimmed);\n let wildcard = \"\";\n for (let i = 0; i < trimmed.length; i += 1) {\n const char = trimmed[i];\n if (char === \"*\") {\n if (trimmed[i + 1] === \"*\") {\n wildcard += \".*\";\n i += 1;\n } else {\n wildcard += \".*\";\n }\n } else {\n wildcard += char.replace(/[|\\\\\\\\{}()[\\\\]^$+?.]/g, \"\\\\\\\\$&\");\n }\n }\n return new RegExp(\"^\" + wildcard + \"$\", \"i\").test(String(value ?? \"\"));\n };\n const buildSelector = (element) => {\n if (!element || !element.tagName) return null;\n const testId = element.getAttribute && element.getAttribute(\"data-testid\");\n if (testId) return '[data-testid=\"' + testId + '\"]';\n const name = element.getAttribute && element.getAttribute(\"name\");\n if (name) return element.tagName.toLowerCase() + '[name=\"' + name + '\"]';\n const title = element.getAttribute && element.getAttribute(\"title\");\n if (title) return element.tagName.toLowerCase() + '[title=\"' + title + '\"]';\n return element.tagName.toLowerCase();\n };\n const activeDocument = (() => {\n if (!state.currentFrame) return document;\n try {\n const frame = document.querySelector(state.currentFrame);\n return frame && frame.contentDocument ? frame.contentDocument : document;\n } catch {\n return document;\n }\n })();\n const queryOne = (selector, root = activeDocument) => {\n try {\n return root.querySelector(selector);\n } catch {\n throw new Error(\"Invalid selector \" + selector);\n }\n };\n const findByText = (needle) => {\n const wanted = normalize(needle).toLowerCase();\n if (!wanted) return null;\n const candidates = Array.from(\n activeDocument.querySelectorAll(\n \"a, button, input, textarea, select, option, label, h1, h2, h3, [role='button'], [data-testid]\"\n )\n );\n return (\n candidates.find((element) => {\n const haystacks = [\n element.textContent,\n element.getAttribute(\"aria-label\"),\n element.getAttribute(\"placeholder\"),\n element.getAttribute(\"title\"),\n element.getAttribute(\"name\"),\n element.value\n ]\n .map((value) => normalize(value).toLowerCase())\n .filter(Boolean);\n return haystacks.some((value) => value.includes(wanted));\n }) || null\n );\n };\n const resolveTarget = () => {\n if (command.selector) return queryOne(command.selector);\n if (command.text) return findByText(command.text);\n return activeDocument.activeElement || activeDocument.body;\n };\n const recordRequest = (request) => {\n const entry = {\n ...request,\n id: \"req_\" + state.networkNextRequestId++,\n timestamp: new Date().toISOString()\n };\n state.networkRequests.push(entry);\n if (state.networkHar.active) state.networkHar.entries.push(entry);\n return entry;\n };\n if (!state.consoleWrapped) {\n for (const level of [\"log\", \"info\", \"warn\", \"error\"]) {\n console[level] = (...args) => {\n state.consoleEntries.push({\n level,\n message: args.map((value) => normalize(value)).join(\" \"),\n timestamp: new Date().toISOString()\n });\n };\n }\n state.consoleWrapped = true;\n }\n if (!state.dialogWrapped) {\n window.alert = (message) => {\n state.dialog = { defaultValue: null, message: String(message ?? \"\"), open: true, type: \"alert\" };\n };\n window.confirm = (message) => {\n state.dialog = { defaultValue: null, message: String(message ?? \"\"), open: true, type: \"confirm\" };\n return false;\n };\n window.prompt = (message, defaultValue) => {\n state.dialog = {\n defaultValue: defaultValue ?? null,\n message: String(message ?? \"\"),\n open: true,\n type: \"prompt\"\n };\n return null;\n };\n state.dialogWrapped = true;\n }\n if (!state.fetchWrapped) {\n state.originalFetch = window.fetch ? window.fetch.bind(window) : null;\n window.fetch = async (input, init = {}) => {\n const inputUrl =\n typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.toString()\n : typeof input?.url === \"string\"\n ? input.url\n : String(input);\n const url = new URL(inputUrl, location.href).toString();\n if (state.settings.offline) {\n recordRequest({\n matchedRoute: null,\n method: String(init.method || \"GET\").toUpperCase(),\n resourceType: \"fetch\",\n responseBody: null,\n responseHeaders: {},\n status: 0,\n url\n });\n throw new Error(\"Browser workspace is offline.\");\n }\n const route = [...state.networkRoutes].reverse().find((entry) => patternMatches(entry.pattern, url)) || null;\n if (route && route.abort) {\n recordRequest({\n matchedRoute: route.pattern,\n method: String(init.method || \"GET\").toUpperCase(),\n resourceType: \"fetch\",\n responseBody: null,\n responseHeaders: route.headers || {},\n status: 0,\n url\n });\n throw new Error(\"Browser workspace network route aborted request: \" + url);\n }\n if (route && (route.body !== null || route.status !== null || Object.keys(route.headers || {}).length > 0)) {\n const response = new Response(route.body || \"\", {\n headers: route.headers || {},\n status: route.status || 200\n });\n recordRequest({\n matchedRoute: route.pattern,\n method: String(init.method || \"GET\").toUpperCase(),\n resourceType: \"fetch\",\n responseBody: route.body || \"\",\n responseHeaders: route.headers || {},\n status: route.status || 200,\n url\n });\n return response;\n }\n const headers = new Headers(init.headers || {});\n for (const [key, value] of Object.entries(state.settings.headers || {})) {\n if (!headers.has(key)) headers.set(key, value);\n }\n if (state.settings.credentials && state.settings.credentials.username && !headers.has(\"Authorization\")) {\n headers.set(\n \"Authorization\",\n \"Basic \" + btoa(state.settings.credentials.username + \":\" + state.settings.credentials.password)\n );\n }\n const response = await state.originalFetch(url, { ...init, headers });\n recordRequest({\n matchedRoute: null,\n method: String(init.method || \"GET\").toUpperCase(),\n resourceType: \"fetch\",\n responseBody: null,\n responseHeaders: Object.fromEntries(response.headers.entries()),\n status: response.status,\n url: response.url || url\n });\n return response;\n };\n state.fetchWrapped = true;\n }\n Object.defineProperty(navigator, \"onLine\", {\n configurable: true,\n get: () => !state.settings.offline\n });\n switch (command.subaction) {\n case \"clipboard\": {\n const action = command.clipboardAction || \"read\";\n if (action === \"read\") return state.clipboardText;\n if (action === \"write\") {\n state.clipboardText = command.value || command.text || \"\";\n return state.clipboardText;\n }\n if (action === \"copy\") {\n const target = resolveTarget();\n state.clipboardText =\n target && typeof target.value === \"string\"\n ? String(target.value || \"\")\n : normalize(target?.textContent || activeDocument.body?.textContent);\n return state.clipboardText;\n }\n const target = resolveTarget();\n if (target && typeof target.value === \"string\") {\n target.value = String(target.value || \"\") + state.clipboardText;\n target.setAttribute(\"value\", target.value);\n return { selector: buildSelector(target), value: target.value };\n }\n return state.clipboardText;\n }\n case \"mouse\": {\n const action = command.mouseAction || \"move\";\n if (action === \"move\") {\n state.mouse.x = typeof command.x === \"number\" ? command.x : state.mouse.x;\n state.mouse.y = typeof command.y === \"number\" ? command.y : state.mouse.y;\n return state.mouse;\n }\n if (action === \"down\") {\n const button = command.button || \"left\";\n state.mouse.buttons = Array.from(new Set([...(state.mouse.buttons || []), button]));\n return state.mouse;\n }\n if (action === \"up\") {\n const button = command.button || \"left\";\n state.mouse.buttons = (state.mouse.buttons || []).filter((entry) => entry !== button);\n return state.mouse;\n }\n window.scrollBy(command.deltaX || 0, command.deltaY || command.pixels || 240);\n return { axis: Math.abs(command.deltaY || 0) >= Math.abs(command.deltaX || 0) ? \"y\" : \"x\", value: window.scrollY };\n }\n case \"drag\": {\n const source = resolveTarget();\n const target = command.value ? queryOne(command.value) : null;\n if (!source || !target) throw new Error(\"Eliza browser workspace drag requires source selector and target selector in value.\");\n source.setAttribute(\"data-eliza-dragging\", \"true\");\n target.setAttribute(\"data-eliza-drop-target\", \"true\");\n return { source: buildSelector(source), target: buildSelector(target) };\n }\n case \"upload\": {\n const target = resolveTarget();\n if (!target || target.tagName !== \"INPUT\") throw new Error(\"Eliza browser workspace upload requires a file input target.\");\n const files = Array.isArray(command.files) ? command.files.map((entry) => String(entry).split(/[\\\\\\\\/]/).pop()) : [];\n target.setAttribute(\"data-eliza-uploaded-files\", files.join(\",\"));\n return { files, selector: buildSelector(target) };\n }\n case \"set\": {\n const action = command.setAction || \"viewport\";\n if (action === \"viewport\") {\n state.settings.viewport = { width: command.width || 1280, height: command.height || 720, scale: command.scale || 1 };\n } else if (action === \"device\") {\n state.settings.device = command.device || null;\n } else if (action === \"geo\") {\n state.settings.geo =\n typeof command.latitude === \"number\" && typeof command.longitude === \"number\"\n ? { latitude: command.latitude, longitude: command.longitude }\n : null;\n } else if (action === \"offline\") {\n state.settings.offline = Boolean(command.offline);\n } else if (action === \"headers\") {\n state.settings.headers = command.headers || {};\n } else if (action === \"credentials\") {\n state.settings.credentials =\n command.username || command.password\n ? { username: command.username || \"\", password: command.password || \"\" }\n : null;\n } else if (action === \"media\") {\n state.settings.media = command.media || null;\n }\n return state.settings;\n }\n case \"cookies\": {\n const action = command.cookieAction || \"get\";\n if (action === \"clear\") {\n const current = document.cookie || \"\";\n current.split(/;\\\\s*/).forEach((entry) => {\n const name = entry.split(\"=\")[0];\n if (name) document.cookie = name + \"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/\";\n });\n return { cleared: true };\n }\n if (action === \"set\") {\n const name = command.name || command.entryKey;\n if (!name) throw new Error(\"Eliza browser workspace cookies set requires name.\");\n document.cookie = name + \"=\" + (command.value || \"\") + \"; path=/\";\n }\n const cookieString = document.cookie || \"\";\n return Object.fromEntries(\n cookieString\n .split(/;\\\\s*/)\n .filter(Boolean)\n .map((entry) => {\n const [name, ...rest] = entry.split(\"=\");\n return [name, rest.join(\"=\")];\n })\n );\n }\n case \"storage\": {\n const storage = command.storageArea === \"session\" ? sessionStorage : localStorage;\n const action = command.storageAction || \"get\";\n if (action === \"clear\") {\n storage.clear();\n return { cleared: true };\n }\n if (action === \"set\") {\n const key = command.entryKey || command.name;\n if (!key) throw new Error(\"Eliza browser workspace storage set requires entryKey.\");\n storage.setItem(key, command.value || \"\");\n }\n if (command.entryKey || command.name) {\n return storage.getItem(command.entryKey || command.name);\n }\n const out = {};\n for (let i = 0; i < storage.length; i += 1) {\n const key = storage.key(i);\n if (key) out[key] = storage.getItem(key) || \"\";\n }\n return out;\n }\n case \"network\": {\n const action = command.networkAction || \"requests\";\n if (action === \"route\") {\n if (!command.url) throw new Error(\"Eliza browser workspace network route requires url pattern.\");\n state.networkRoutes.push({\n abort: Boolean(command.offline),\n body: command.responseBody ?? null,\n headers: command.responseHeaders || {},\n pattern: command.url,\n status: typeof command.responseStatus === \"number\" ? command.responseStatus : null\n });\n return state.networkRoutes;\n }\n if (action === \"unroute\") {\n state.networkRoutes = command.url\n ? state.networkRoutes.filter((entry) => entry.pattern !== command.url)\n : [];\n return state.networkRoutes;\n }\n if (action === \"request\") {\n return state.networkRequests.find((entry) => entry.id === command.requestId) || null;\n }\n if (action === \"harstart\") {\n state.networkHar = { active: true, entries: [], startedAt: new Date().toISOString() };\n return state.networkHar;\n }\n if (action === \"harstop\") {\n state.networkHar.active = false;\n return { log: { entries: state.networkHar.entries, startedAt: state.networkHar.startedAt } };\n }\n let requests = [...state.networkRequests];\n if (command.filter) requests = requests.filter((entry) => entry.url.includes(command.filter));\n if (command.method) requests = requests.filter((entry) => entry.method === String(command.method).toUpperCase());\n if (command.status) requests = requests.filter((entry) => String(entry.status || \"\") === String(command.status));\n return requests;\n }\n case \"dialog\": {\n const action = command.dialogAction || \"status\";\n if (action === \"status\") return state.dialog;\n if (state.dialog) state.dialog.open = false;\n const result =\n action === \"accept\"\n ? { accepted: true, dialog: state.dialog, promptText: command.promptText || command.value || null }\n : { accepted: false, dialog: state.dialog };\n state.dialog = null;\n return result;\n }\n case \"console\":\n if (command.consoleAction === \"clear\") state.consoleEntries = [];\n return state.consoleEntries;\n case \"errors\":\n if (command.consoleAction === \"clear\") state.errors = [];\n return state.errors;\n case \"highlight\": {\n const target = resolveTarget();\n if (!target) throw new Error(\"Target element was not found.\");\n target.setAttribute(\"data-eliza-highlight\", \"true\");\n state.highlightedSelector = buildSelector(target);\n return { selector: state.highlightedSelector };\n }\n case \"frame\": {\n if ((command.frameAction || \"select\") === \"main\") {\n state.currentFrame = null;\n return { frame: null };\n }\n const frame = command.selector ? document.querySelector(command.selector) : null;\n if (!frame || frame.tagName !== \"IFRAME\") throw new Error(\"Eliza browser workspace frame select requires an iframe selector.\");\n state.currentFrame = buildSelector(frame);\n return { frame: state.currentFrame };\n }\n default:\n throw new Error(\"Unsupported desktop browser workspace utility subaction: \" + command.subaction);\n }\n})()\n`.trim();\n}\n\nexport async function executeDesktopBrowserWorkspaceUtilityCommand(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<BrowserWorkspaceCommandResult> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n if (\n command.subaction === \"cookies\" ||\n command.subaction === \"storage\" ||\n (command.subaction === \"set\" &&\n (command.setAction === \"credentials\" || command.setAction === \"headers\"))\n ) {\n await assertDesktopBrowserWorkspaceCanAccessProfileSecrets(\n id,\n env,\n command.subaction,\n );\n }\n const startedAt = Date.now();\n const result = await evaluateBrowserWorkspaceTab(\n {\n id,\n script: createDesktopBrowserWorkspaceUtilityScript({\n ...command,\n id,\n }),\n },\n env,\n );\n const runtime = getBrowserWorkspaceRuntimeState(\"desktop\", id);\n appendBrowserWorkspaceTraceEntry(runtime, {\n subaction: command.subaction,\n type: \"utility\",\n });\n appendBrowserWorkspaceProfilerEntry(runtime, {\n durationMs: Date.now() - startedAt,\n subaction: command.subaction,\n type: \"utility\",\n });\n return {\n mode: \"desktop\",\n subaction: command.subaction,\n value: result,\n };\n}\n\nexport async function getDesktopBrowserWorkspaceSnapshotRecord(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<BrowserWorkspaceSnapshotRecord> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n const result = await evaluateBrowserWorkspaceTab(\n {\n id,\n script: `\n(() => {\n const activeDocument = (() => {\n const state = window.__elizaBrowserWorkspaceState || {};\n if (!state.currentFrame) return document;\n try {\n const frame = document.querySelector(state.currentFrame);\n return frame && frame.contentDocument ? frame.contentDocument : document;\n } catch {\n return document;\n }\n })();\n const normalize = (value) => String(value ?? \"\").replace(/\\\\s+/g, \" \").trim();\n const controlText = Array.from(activeDocument.querySelectorAll(\"input, textarea, select, option:checked\"))\n .map((element) => {\n const name = element.getAttribute(\"name\") || element.getAttribute(\"id\") || element.tagName.toLowerCase();\n const value =\n element.tagName === \"SELECT\"\n ? element.value\n : typeof element.value === \"string\"\n ? element.value\n : element.textContent || \"\";\n return name + \":\" + normalize(value);\n })\n .filter(Boolean)\n .join(\" \");\n return {\n bodyText: normalize((activeDocument.body?.textContent || \"\") + \" \" + controlText),\n title: normalize(document.title),\n url: location.href\n };\n})()\n `.trim(),\n },\n env,\n );\n return result as BrowserWorkspaceSnapshotRecord;\n}\n\nexport async function getDesktopBrowserWorkspaceSessionState(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<Record<string, unknown>> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n await assertDesktopBrowserWorkspaceCanAccessProfileSecrets(id, env, \"state\");\n const result = await evaluateBrowserWorkspaceTab(\n {\n id,\n script: `\n(() => {\n const state = window.__elizaBrowserWorkspaceState || {};\n const readStorage = (storage) => {\n const out = {};\n for (let i = 0; i < storage.length; i += 1) {\n const key = storage.key(i);\n if (key) out[key] = storage.getItem(key) || \"\";\n }\n return out;\n };\n const cookies = Object.fromEntries(\n String(document.cookie || \"\")\n .split(/;\\\\s*/)\n .filter(Boolean)\n .map((entry) => {\n const [name, ...rest] = entry.split(\"=\");\n return [name, rest.join(\"=\")];\n })\n );\n return {\n clipboard: state.clipboardText || \"\",\n cookies,\n localStorage: readStorage(localStorage),\n sessionStorage: readStorage(sessionStorage),\n settings: state.settings || {},\n url: location.href\n };\n})()\n `.trim(),\n },\n env,\n );\n return result as Record<string, unknown>;\n}\n\nexport async function loadDesktopBrowserWorkspaceSessionState(\n command: BrowserWorkspaceCommand,\n payload: Record<string, unknown>,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n await assertDesktopBrowserWorkspaceCanAccessProfileSecrets(id, env, \"state\");\n await evaluateBrowserWorkspaceTab(\n {\n id,\n script: `\n(() => {\n const payload = ${JSON.stringify(payload)};\n const state =\n window.__elizaBrowserWorkspaceState ||\n (window.__elizaBrowserWorkspaceState = { settings: {} });\n localStorage.clear();\n for (const [key, value] of Object.entries(payload.localStorage || {})) {\n localStorage.setItem(key, String(value ?? \"\"));\n }\n sessionStorage.clear();\n for (const [key, value] of Object.entries(payload.sessionStorage || {})) {\n sessionStorage.setItem(key, String(value ?? \"\"));\n }\n for (const [key, value] of Object.entries(payload.cookies || {})) {\n document.cookie = key + \"=\" + String(value ?? \"\") + \"; path=/\";\n }\n state.clipboardText = typeof payload.clipboard === \"string\" ? payload.clipboard : \"\";\n state.settings = typeof payload.settings === \"object\" && payload.settings ? payload.settings : state.settings;\n return { loaded: true };\n})()\n `.trim(),\n },\n env,\n );\n}\n\nexport async function executeDesktopBrowserWorkspaceDomCommand(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<BrowserWorkspaceCommandResult> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n const startedAt = Date.now();\n command = resolveBrowserWorkspaceCommandElementRefs(command, \"desktop\", id);\n const result = await evaluateBrowserWorkspaceTab(\n {\n id,\n script: createDesktopBrowserWorkspaceCommandScript({\n ...command,\n id,\n }),\n },\n env,\n );\n\n if (command.subaction === \"inspect\" || command.subaction === \"snapshot\") {\n const value =\n result && typeof result === \"object\" && !Array.isArray(result)\n ? (result as {\n bodyText?: string;\n elements?: BrowserWorkspaceDomElementSummary[];\n })\n : null;\n const elements = registerBrowserWorkspaceElementRefs(\n \"desktop\",\n id,\n Array.isArray(value?.elements) ? value.elements : [],\n );\n return {\n mode: \"desktop\",\n subaction: command.subaction,\n elements,\n value: result,\n };\n }\n\n const runtime = getBrowserWorkspaceRuntimeState(\"desktop\", id);\n appendBrowserWorkspaceTraceEntry(runtime, {\n subaction: command.subaction,\n type: \"dom\",\n });\n appendBrowserWorkspaceProfilerEntry(runtime, {\n durationMs: Date.now() - startedAt,\n subaction: command.subaction,\n type: \"dom\",\n });\n return {\n mode: \"desktop\",\n subaction: command.subaction,\n value:\n result && typeof result === \"object\" && !Array.isArray(result)\n ? ((result as { value?: unknown }).value ?? result)\n : result,\n };\n}\n\n// --- Desktop tab resolution ---\n\nexport function resolveBrowserWorkspaceCurrentTab(\n tabs: BrowserWorkspaceTab[],\n): BrowserWorkspaceTab | null {\n if (tabs.length === 0) {\n return null;\n }\n\n return (\n tabs.find((tab) => tab.visible) ??\n [...tabs].sort((left, right) => {\n const leftTime = left.lastFocusedAt ?? left.updatedAt ?? \"\";\n const rightTime = right.lastFocusedAt ?? right.updatedAt ?? \"\";\n return (\n rightTime.localeCompare(leftTime) || left.id.localeCompare(right.id)\n );\n })[0] ??\n null\n );\n}\n\nexport async function resolveDesktopBrowserWorkspaceTargetTabId(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<string> {\n if (command.id?.trim()) {\n return command.id.trim();\n }\n\n // This function is only called on desktop-bridge paths where the\n // Electrobun HTTP bridge is configured. Calling `listBrowserWorkspaceTabs`\n // from `browser-workspace.ts` here would create a compile-time circular\n // dependency (browser-workspace → desktop → browser-workspace); the web\n // fallback in that function is also not reachable from these call sites.\n // So hit the bridge directly and skip the detour.\n const payload = await requestBrowserWorkspace<{\n tabs?: BrowserWorkspaceTab[];\n }>(\"/tabs\", undefined, env);\n const tabs = Array.isArray(payload.tabs) ? payload.tabs : [];\n const current = resolveBrowserWorkspaceCurrentTab(tabs);\n if (!current) {\n throw createBrowserWorkspaceCommandTargetError(command.subaction);\n }\n return current.id;\n}\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAWP,eAAe,qDACb,IACA,KACA,WACe;AACf,QAAM,UAAU,MAAM,wBAEnB,SAAS,QAAW,GAAG;AAC1B,QAAM,MAAM,QAAQ,MAAM,KAAK,CAAC,UAAU,MAAM,OAAO,EAAE,KAAK;AAC9D,oDAAkD,KAAK,WAAW,SAAS;AAC7E;AAEA,eAAe,cAAc,UAAqC;AAChE,MAAI;AACF,YAAQ,MAAM,SAAS,KAAK,GAAG,KAAK,EAAE,MAAM,GAAG,GAAG;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oCACd,MAAyB,QAAQ,KACI;AACrC,QAAM,UACJ,kBAAkB,IAAI,2BAA2B,KACjD,kBAAkB,IAAI,2BAA2B;AACnD,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,SAAS,QAAQ,QAAQ,eAAe,EAAE;AAAA,IAC1C,OACE,kBAAkB,IAAI,6BAA6B,KACnD,kBAAkB,IAAI,6BAA6B;AAAA,EACvD;AACF;AAEO,SAAS,mCACd,MAAyB,QAAQ,KACxB;AACT,SAAO,oCAAoC,GAAG,MAAM;AACtD;AAEO,SAAS,wCAAgD;AAC9D,SAAO;AACT;AAEA,eAAsB,wBACpB,MACA,MACA,MAAyB,QAAQ,KACrB;AACZ,QAAM,SAAS,oCAAoC,GAAG;AACtD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,sCAAsC,CAAC;AAAA,EACzD;AAEA,QAAM,UAAU,IAAI,QAAQ,MAAM,WAAW,CAAC,CAAC;AAC/C,UAAQ,IAAI,UAAU,kBAAkB;AACxC,MAAI,CAAC,QAAQ,IAAI,cAAc,KAAK,MAAM,MAAM;AAC9C,YAAQ,IAAI,gBAAgB,kBAAkB;AAAA,EAChD;AACA,MAAI,OAAO,OAAO;AAChB,YAAQ,IAAI,iBAAiB,UAAU,OAAO,KAAK,EAAE;AAAA,EACvD;AAEA,QAAM,WAAW,MAAM,MAAM,GAAG,OAAO,OAAO,GAAG,IAAI,IAAI;AAAA,IACvD,GAAG;AAAA,IACH;AAAA,IACA,QAAQ,YAAY,QAAQ,kBAAkB;AAAA,EAChD,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UAAU,MAAM,cAAc,QAAQ;AAC5C,UAAM,IAAI;AAAA,MACR,qCAAqC,SAAS,MAAM,IAAI,UAAU,KAAK,OAAO,KAAK,EAAE;AAAA,IACvF;AAAA,EACF;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAEA,eAAsB,4BACpB,SACA,MAAyB,QAAQ,KACf;AAClB,MAAI,CAAC,mCAAmC,GAAG,GAAG;AAC5C,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAmD;AAAA,IACvD,QAAQ,QAAQ;AAAA,EAClB;AACA,MAAI,QAAQ,cAAc,QAAW;AACnC,aAAS,YAAY,QAAQ;AAAA,EAC/B;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,SAAS,mBAAmB,QAAQ,EAAE,CAAC;AAAA,IACvC;AAAA,MACE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,QAAQ;AAAA,IAC/B;AAAA,IACA;AAAA,EACF;AACA,SAAO,QAAQ;AACjB;AAEA,eAAsB,4BACpB,IACA,MAAyB,QAAQ,KACN;AAC3B,MAAI,CAAC,mCAAmC,GAAG,GAAG;AAC5C,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM;AAAA,IACX,SAAS,mBAAmB,EAAE,CAAC;AAAA,IAC/B;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,2CACd,SACQ;AACR,SAAO;AAAA;AAAA,oBAEW,KAAK,UAAU,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/workspace/browser-workspace-desktop.ts"],"sourcesContent":["import {\n assertBrowserWorkspaceConnectorSecretsNotExported,\n assertBrowserWorkspaceUserScriptAllowed,\n createBrowserWorkspaceCommandTargetError,\n DEFAULT_TIMEOUT_MS,\n isBrowserWorkspaceUserScriptAllowed,\n normalizeEnvValue,\n resolveBrowserWorkspaceCommandElementRefs,\n} from \"./browser-workspace-helpers.js\";\nimport {\n appendBrowserWorkspaceProfilerEntry,\n appendBrowserWorkspaceTraceEntry,\n getBrowserWorkspaceRuntimeState,\n registerBrowserWorkspaceElementRefs,\n} from \"./browser-workspace-state.js\";\nimport type {\n BrowserWorkspaceBridgeConfig,\n BrowserWorkspaceCommand,\n BrowserWorkspaceCommandResult,\n BrowserWorkspaceDomElementSummary,\n BrowserWorkspaceSnapshotRecord,\n BrowserWorkspaceTab,\n EvaluateBrowserWorkspaceTabRequest,\n} from \"./browser-workspace-types.js\";\n\nasync function assertDesktopBrowserWorkspaceCanAccessProfileSecrets(\n id: string,\n env: NodeJS.ProcessEnv,\n operation: string,\n): Promise<void> {\n const payload = await requestBrowserWorkspace<{\n tabs?: BrowserWorkspaceTab[];\n }>(\"/tabs\", undefined, env);\n const tab = payload.tabs?.find((entry) => entry.id === id) ?? null;\n assertBrowserWorkspaceConnectorSecretsNotExported(tab?.partition, operation);\n}\n\nasync function readErrorBody(response: Response): Promise<string> {\n try {\n return (await response.text()).trim().slice(0, 240);\n } catch {\n return \"\";\n }\n}\n\nexport function resolveBrowserWorkspaceBridgeConfig(\n env: NodeJS.ProcessEnv = process.env,\n): BrowserWorkspaceBridgeConfig | null {\n const baseUrl = normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_URL);\n if (!baseUrl) {\n return null;\n }\n\n return {\n baseUrl: baseUrl.replace(/\\/{1,1024}$/, \"\"),\n token: normalizeEnvValue(env.ELIZA_BROWSER_WORKSPACE_TOKEN),\n };\n}\n\nexport function isBrowserWorkspaceBridgeConfigured(\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n return resolveBrowserWorkspaceBridgeConfig(env) !== null;\n}\n\nexport function getBrowserWorkspaceUnavailableMessage(): string {\n return \"Eliza browser workspace desktop bridge is unavailable.\";\n}\n\nexport async function requestBrowserWorkspace<T>(\n path: string,\n init?: RequestInit,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<T> {\n const config = resolveBrowserWorkspaceBridgeConfig(env);\n if (!config) {\n throw new Error(getBrowserWorkspaceUnavailableMessage());\n }\n\n const headers = new Headers(init?.headers ?? {});\n headers.set(\"Accept\", \"application/json\");\n if (!headers.has(\"Content-Type\") && init?.body) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n if (config.token) {\n headers.set(\"Authorization\", `Bearer ${config.token}`);\n }\n\n const response = await fetch(`${config.baseUrl}${path}`, {\n ...init,\n headers,\n signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n const details = await readErrorBody(response);\n throw new Error(\n `Browser workspace request failed (${response.status})${details ? `: ${details}` : \"\"}`,\n );\n }\n\n return (await response.json()) as T;\n}\n\nexport async function evaluateBrowserWorkspaceTab(\n request: EvaluateBrowserWorkspaceTabRequest,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<unknown> {\n if (!isBrowserWorkspaceBridgeConfigured(env)) {\n throw new Error(\n \"Eliza browser workspace eval is only available in the desktop app.\",\n );\n }\n\n const evalBody: { script: string; partition?: string } = {\n script: request.script,\n };\n if (request.partition !== undefined) {\n evalBody.partition = request.partition;\n }\n\n const payload = await requestBrowserWorkspace<{ result: unknown }>(\n `/tabs/${encodeURIComponent(request.id)}/eval`,\n {\n method: \"POST\",\n body: JSON.stringify(evalBody),\n },\n env,\n );\n return payload.result;\n}\n\nexport async function snapshotBrowserWorkspaceTab(\n id: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<{ data: string }> {\n if (!isBrowserWorkspaceBridgeConfigured(env)) {\n throw new Error(\n \"Eliza browser workspace snapshot is only available in the desktop app.\",\n );\n }\n\n return await requestBrowserWorkspace<{ data: string }>(\n `/tabs/${encodeURIComponent(id)}/snapshot`,\n undefined,\n env,\n );\n}\n\nfunction desktopBrowserWorkspaceWaitScriptBranch(\n env: NodeJS.ProcessEnv,\n): string {\n if (isBrowserWorkspaceUserScriptAllowed(env)) {\n return `\n if (command.script) {\n const fn = new Function(\"document\", \"window\", \"location\", \"return (\" + command.script + \");\");\n if (fn(document, window, location)) {\n resolve({ ok: true, script: true });\n return;\n }\n }`;\n }\n return `\n if (command.script) {\n reject(new Error(\"Browser workspace wait script is disabled (GHSA-mhhr-9ph9-64j7).\"));\n return;\n }`;\n}\n\nexport function createDesktopBrowserWorkspaceCommandScript(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const waitScriptBranch = desktopBrowserWorkspaceWaitScriptBranch(env);\n return `\n(() => {\n const command = ${JSON.stringify(command)};\n const normalize = (value) => String(value ?? \"\").replace(/\\\\s+/g, \" \").trim();\n const textMatches = (candidate, wanted, exact = false) => {\n const left = normalize(candidate).toLowerCase();\n const right = normalize(wanted).toLowerCase();\n if (!left || !right) return false;\n return exact ? left === right : left.includes(right);\n };\n const selectorFor = (element) => {\n if (!element) return \"\";\n if (element.id) return \"#\" + element.id.replace(/[^a-zA-Z0-9_-]/g, \"\\\\\\\\$&\");\n const testId = element.getAttribute?.(\"data-testid\");\n if (testId) return \\`[data-testid=\"\\${testId}\"]\\`;\n const name = element.getAttribute?.(\"name\");\n if (name) return \\`\\${element.tagName.toLowerCase()}[name=\"\\${name}\"]\\`;\n const type = element.getAttribute?.(\"type\");\n if (type) return \\`\\${element.tagName.toLowerCase()}[type=\"\\${type}\"]\\`;\n let index = 1;\n let previous = element.previousElementSibling;\n while (previous) {\n if (previous.tagName === element.tagName) index += 1;\n previous = previous.previousElementSibling;\n }\n return \\`\\${element.tagName.toLowerCase()}:nth-of-type(\\${index})\\`;\n };\n const serialize = (element) => {\n const value =\n element instanceof HTMLInputElement ||\n element instanceof HTMLTextAreaElement ||\n element instanceof HTMLSelectElement\n ? element.value\n : null;\n return {\n selector: selectorFor(element),\n tag: element.tagName.toLowerCase(),\n text: normalize(value ?? element.textContent),\n type: element.getAttribute?.(\"type\"),\n name: element.getAttribute?.(\"name\"),\n href: element.getAttribute?.(\"href\"),\n value: typeof value === \"string\" ? value : null,\n };\n };\n const searchTexts = (element) => {\n const labelText = element.id\n ? Array.from(document.querySelectorAll('label[for=\"' + element.id + '\"]'))\n .map((label) => label.textContent)\n .join(\" \")\n : \"\";\n return [\n element.textContent,\n element.getAttribute?.(\"aria-label\"),\n element.getAttribute?.(\"placeholder\"),\n element.getAttribute?.(\"title\"),\n element.getAttribute?.(\"name\"),\n element.getAttribute?.(\"alt\"),\n element.getAttribute?.(\"data-testid\"),\n labelText,\n element.value,\n ]\n .map((value) => normalize(value))\n .filter(Boolean);\n };\n const isVisible = (element) => {\n if (!element) return false;\n if (element.hasAttribute?.(\"hidden\") || element.getAttribute?.(\"aria-hidden\") === \"true\") {\n return false;\n }\n const style = element.style || {};\n return style.display !== \"none\" && style.visibility !== \"hidden\";\n };\n const nativeRole = (element) => {\n const explicit = element.getAttribute?.(\"role\")?.trim()?.toLowerCase();\n if (explicit) return explicit;\n const tag = element.tagName.toLowerCase();\n if (tag === \"a\" && element.getAttribute?.(\"href\")) return \"link\";\n if (tag === \"button\") return \"button\";\n if (tag === \"select\") return \"combobox\";\n if (tag === \"option\") return \"option\";\n if (tag === \"textarea\") return \"textbox\";\n if (tag === \"form\") return \"form\";\n if (/^h[1-6]$/.test(tag)) return \"heading\";\n if (tag === \"input\") {\n const type = (element.type || \"text\").toLowerCase();\n if (type === \"checkbox\") return \"checkbox\";\n if (type === \"radio\") return \"radio\";\n if ([\"button\", \"submit\", \"reset\", \"image\"].includes(type)) return \"button\";\n return \"textbox\";\n }\n return null;\n };\n const findByText = (wanted) => {\n const needle = normalize(wanted).toLowerCase();\n if (!needle) return null;\n const elements = Array.from(document.querySelectorAll(\n \"a, button, input, textarea, select, option, label, h1, h2, h3, [role='button'], [data-testid]\"\n ));\n for (const element of elements) {\n const haystacks = [\n element.textContent,\n element.getAttribute?.(\"aria-label\"),\n element.getAttribute?.(\"placeholder\"),\n element.getAttribute?.(\"title\"),\n element.getAttribute?.(\"name\"),\n element.value,\n ]\n .map((value) => normalize(value))\n .filter(Boolean)\n .map((value) => value.toLowerCase());\n if (haystacks.some((value) => value.includes(needle))) {\n return element;\n }\n }\n return null;\n };\n const findByLabel = (wanted, exact = false) => {\n const labels = Array.from(document.querySelectorAll(\"label\"));\n for (const label of labels) {\n if (!textMatches(label.textContent, wanted, exact)) continue;\n const forId = label.getAttribute(\"for\");\n if (forId) {\n const explicit = document.getElementById(forId);\n if (explicit) return explicit;\n }\n const nested = label.querySelector(\"input, textarea, select, button\");\n if (nested) return nested;\n }\n return null;\n };\n const findByRole = (role, name, exact = false) => {\n const candidates = Array.from(\n document.querySelectorAll(\n \"a, button, input, textarea, select, option, form, h1, h2, h3, h4, h5, h6, [role], [data-testid]\"\n )\n );\n for (const candidate of candidates) {\n if (nativeRole(candidate) !== role.trim().toLowerCase()) continue;\n if (!name) return candidate;\n if (searchTexts(candidate).some((value) => textMatches(value, name, exact))) {\n return candidate;\n }\n }\n return null;\n };\n const trimQuoted = (value) => {\n const trimmed = String(value || \"\").trim();\n const hasTextMatch = trimmed.match(/^has-text\\\\((?:\"([^\"]*)\"|'([^']*)')\\\\)$/i);\n if (hasTextMatch?.[1] || hasTextMatch?.[2]) {\n return (hasTextMatch[1] || hasTextMatch[2] || \"\").trim();\n }\n if (\n (trimmed.startsWith('\"') && trimmed.endsWith('\"')) ||\n (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\"))\n ) {\n return trimmed.slice(1, -1).trim();\n }\n return trimmed;\n };\n const normalizeSelectorSyntax = (selector) => {\n let normalized = String(selector || \"\").trim();\n normalized = normalized.replace(\n /^role\\\\s*[:=]\\\\s*([a-z0-9_-]+)\\\\s+name\\\\s*[:=]\\\\s*(.+)$/i,\n \"role=$1[name=$2]\"\n );\n normalized = normalized.replace(\n /^((?:label|text|placeholder|alt|title|testid|data-testid)\\\\s*[:=]\\\\s*(?:has-text\\\\((?:\"[^\"]*\"|'[^']*')\\\\)|\"[^\"]+\"|'[^']+'|[^>]+?))\\\\s+((?:input|textarea|select)[\\\\s\\\\S]*)$/i,\n \"$1 >> $2\"\n );\n return normalized;\n };\n const parseSemanticSelector = (selector) => {\n const trimmed = normalizeSelectorSyntax(selector);\n const match = trimmed.match(/^([a-z-]+)\\\\s*[:=]\\\\s*(.+)$/i);\n if (!match) return null;\n const kind = match[1]?.trim()?.toLowerCase();\n const rawValue = match[2]?.trim() || \"\";\n if (!kind || !rawValue) return null;\n switch (kind) {\n case \"alt\":\n return { findBy: \"alt\", text: trimQuoted(rawValue) };\n case \"css\":\n return { selector: trimQuoted(rawValue) };\n case \"data-testid\":\n case \"testid\":\n return { findBy: \"testid\", text: trimQuoted(rawValue) };\n case \"label\":\n return { findBy: \"label\", text: trimQuoted(rawValue) };\n case \"placeholder\":\n return { findBy: \"placeholder\", text: trimQuoted(rawValue) };\n case \"role\": {\n const roleMatch = rawValue.match(\n /^([a-z0-9_-]+)(?:\\\\s*\\\\[\\\\s*name\\\\s*[:=]\\\\s*(.+?)\\\\s*\\\\])?$/i\n );\n if (!roleMatch?.[1]) return null;\n return {\n findBy: \"role\",\n name: roleMatch[2] ? trimQuoted(roleMatch[2]) : undefined,\n role: roleMatch[1].trim().toLowerCase(),\n };\n }\n case \"text\":\n return { findBy: \"text\", text: trimQuoted(rawValue) };\n case \"title\":\n return { findBy: \"title\", text: trimQuoted(rawValue) };\n default:\n return null;\n }\n };\n const mergeSelectorCommand = (selector) => {\n const parsed = parseSemanticSelector(selector);\n if (!parsed) return null;\n return { ...command, ...parsed, selector: parsed.selector };\n };\n const queryOne = (selector) => {\n try {\n return document.querySelector(selector);\n } catch {\n throw new Error(\"Invalid selector \" + selector);\n }\n };\n const queryAll = (selector) => {\n try {\n return Array.from(document.querySelectorAll(selector));\n } catch {\n throw new Error(\"Invalid selector \" + selector);\n }\n };\n const findSemantic = (targetCommand = command) => {\n switch (targetCommand.findBy) {\n case \"alt\":\n return Array.from(document.querySelectorAll(\"[alt]\")).find((element) =>\n textMatches(\n element.getAttribute(\"alt\"),\n targetCommand.text,\n targetCommand.exact\n )\n ) || null;\n case \"first\":\n return targetCommand.selector ? queryOne(targetCommand.selector) : null;\n case \"label\":\n return targetCommand.text\n ? findByLabel(targetCommand.text, targetCommand.exact)\n : null;\n case \"last\":\n return targetCommand.selector\n ? queryAll(targetCommand.selector).at(-1) || null\n : null;\n case \"nth\":\n return targetCommand.selector && Number.isInteger(targetCommand.index)\n ? queryAll(targetCommand.selector).at(targetCommand.index) || null\n : null;\n case \"placeholder\":\n return Array.from(document.querySelectorAll(\"[placeholder]\")).find((element) =>\n textMatches(\n element.getAttribute(\"placeholder\"),\n targetCommand.text,\n targetCommand.exact\n )\n ) || null;\n case \"role\":\n return targetCommand.role\n ? findByRole(\n targetCommand.role,\n targetCommand.name,\n targetCommand.exact\n )\n : null;\n case \"testid\":\n return targetCommand.text\n ? document.querySelector('[data-testid=\"' + targetCommand.text + '\"]')\n : null;\n case \"text\":\n return targetCommand.text ? findByText(targetCommand.text) : null;\n case \"title\":\n return Array.from(document.querySelectorAll(\"[title]\")).find((element) =>\n textMatches(\n element.getAttribute(\"title\"),\n targetCommand.text,\n targetCommand.exact\n )\n ) || null;\n default:\n return null;\n }\n };\n const findTarget = () => {\n if (command.selector) {\n const selectorChain = normalizeSelectorSyntax(command.selector)\n .split(/s*>>s*/)\n .map((segment) => segment.trim())\n .filter(Boolean);\n if (selectorChain.length > 1) {\n let current = queryTarget(selectorChain[0]);\n for (let index = 1; current && index < selectorChain.length; index += 1) {\n const segment = selectorChain[index];\n if (!segment) continue;\n if (typeof current.matches === \"function\" && current.matches(segment)) {\n continue;\n }\n if (\n /^(input|textarea|select)(?:[[^]]+])?$/i.test(segment) &&\n (current.tagName === \"INPUT\" ||\n current.tagName === \"TEXTAREA\" ||\n current.tagName === \"SELECT\")\n ) {\n continue;\n }\n current = queryOneWithin(current, segment);\n }\n return current;\n }\n return queryTarget(command.selector);\n }\n if (command.findBy) return findSemantic();\n if (command.text) return findByText(command.text);\n return null;\n };\n const queryOneWithin = (root, selector) => {\n try {\n return root.querySelector(selector);\n } catch {\n throw new Error(\"Invalid selector \" + selector);\n }\n };\n const queryTarget = (selector) => {\n const semantic = mergeSelectorCommand(selector);\n if (semantic) return findSemantic(semantic);\n return queryOne(selector);\n };\n const inspect = () =>\n Array.from(\n document.querySelectorAll(\n \"a, button, input, textarea, select, form, [role='button'], [data-testid]\"\n )\n )\n .slice(0, 40)\n .map((element) => serialize(element));\n const snapshot = () => ({\n title: document.title,\n url: location.href,\n bodyText: normalize(document.body?.textContent).slice(0, 800),\n elements: inspect(),\n });\n const setInputValue = (appendMode, target) => {\n const element = target || findTarget();\n if (!element) {\n throw new Error(\"Target element was not found.\");\n }\n if (\n !(\n element instanceof HTMLInputElement ||\n element instanceof HTMLTextAreaElement ||\n element instanceof HTMLSelectElement\n )\n ) {\n throw new Error(\"Target element is not an input, textarea, or select.\");\n }\n const nextValue = appendMode ? \\`\\${element.value ?? \"\"}\\${command.value ?? \"\"}\\` : (command.value ?? \"\");\n element.value = nextValue;\n element.dispatchEvent(new Event(\"input\", { bubbles: true }));\n element.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { selector: selectorFor(element), value: element.value };\n };\n const setChecked = (targetValue) => {\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n if (!(element instanceof HTMLInputElement)) {\n throw new Error(\"Target element is not a checkbox or radio input.\");\n }\n const type = (element.type || \"\").toLowerCase();\n if (type !== \"checkbox\" && type !== \"radio\") {\n throw new Error(\"Target element is not a checkbox or radio input.\");\n }\n element.checked = targetValue;\n element.dispatchEvent(new Event(\"input\", { bubbles: true }));\n element.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { checked: element.checked, selector: selectorFor(element) };\n };\n const setSelectValue = () => {\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n if (!(element instanceof HTMLSelectElement)) {\n throw new Error(\"Target element is not a select.\");\n }\n const targetValue = command.value ?? \"\";\n const option = Array.from(element.options).find(\n (entry) =>\n entry.value === targetValue || textMatches(entry.textContent, targetValue, true)\n );\n if (!option) {\n throw new Error(\"Select option was not found.\");\n }\n element.value = option.value;\n option.selected = true;\n element.dispatchEvent(new Event(\"input\", { bubbles: true }));\n element.dispatchEvent(new Event(\"change\", { bubbles: true }));\n return { selector: selectorFor(element), value: element.value };\n };\n const focusElement = (element) => {\n if (!element) throw new Error(\"Target element was not found.\");\n if (typeof element.focus === \"function\") {\n element.focus();\n }\n return {\n focused: document.activeElement === element,\n selector: selectorFor(element),\n };\n };\n const hoverElement = (element) => {\n if (!element) throw new Error(\"Target element was not found.\");\n element.setAttribute(\"data-eliza-hover\", \"true\");\n return { hovered: true, selector: selectorFor(element) };\n };\n const activateElement = (subaction, element) => {\n if (!element) throw new Error(\"Target element was not found.\");\n if (subaction === \"dblclick\") {\n element.dispatchEvent(new MouseEvent(\"dblclick\", { bubbles: true }));\n }\n if (typeof element.click === \"function\") {\n element.click();\n }\n return {\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n element: serialize(element),\n url: location.href,\n };\n };\n const ensureTabKit = () => {\n const kit = window.__elizaTabKit;\n if (!kit) {\n throw new Error(\n \"browser tab kit not installed (BROWSER_TAB_PRELOAD_SCRIPT missing)\",\n );\n }\n return kit;\n };\n const runRealisticSubaction = (subaction) => {\n const kit = ensureTabKit();\n const cursorDuration = Number(command.cursorDurationMs) || 220;\n if (subaction === \"cursor-hide\") {\n kit.cursor.hide();\n return { hidden: true };\n }\n if (subaction === \"cursor-move\") {\n const x = Number(command.x);\n const y = Number(command.y);\n if (!Number.isFinite(x) || !Number.isFinite(y)) {\n throw new Error(\"cursor-move requires x and y\");\n }\n kit.cursor.show();\n return Promise.resolve(\n kit.cursor.moveTo({ x: x, y: y }, { durationMs: cursorDuration }),\n ).then(() => ({ x: x, y: y }));\n }\n if (subaction === \"realistic-press\") {\n const target = findTarget() || document.activeElement || document.body;\n const key = command.key || \"Enter\";\n target.dispatchEvent(\n new KeyboardEvent(\"keydown\", {\n key: key,\n bubbles: true,\n cancelable: true,\n composed: true,\n }),\n );\n target.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n key: key,\n bubbles: true,\n cancelable: true,\n composed: true,\n }),\n );\n return { key: key, selector: selectorFor(target), url: location.href };\n }\n const element = findTarget();\n if (!element) {\n throw new Error(\"Target element was not found.\");\n }\n kit.cursor.show();\n if (subaction === \"realistic-click\") {\n kit.cursor.highlight(element);\n return Promise.resolve(\n kit.dispatchPointerSequence(element, { button: 0 }),\n ).then(() => ({\n element: serialize(element),\n url: location.href,\n }));\n }\n if (subaction === \"realistic-fill\" || subaction === \"realistic-type\") {\n const value = command.value ?? command.text ?? \"\";\n const replace = subaction === \"realistic-fill\" || command.replace === true;\n const perCharDelayMs = Number(command.perCharDelayMs);\n kit.cursor.highlight(element);\n return Promise.resolve(\n kit\n .dispatchPointerSequence(element, { button: 0 })\n .then(() =>\n kit.typeRealistic(element, value, {\n replace: replace,\n perCharDelayMs: Number.isFinite(perCharDelayMs)\n ? perCharDelayMs\n : undefined,\n }),\n ),\n ).then(() => ({\n element: serialize(element),\n value: element.value,\n }));\n }\n if (subaction === \"realistic-upload\") {\n const url = (command.files && command.files[0]) || command.url || command.value;\n if (!url) {\n throw new Error(\"realistic-upload requires files[0] or url\");\n }\n if (element.tagName !== \"INPUT\" || element.type !== \"file\") {\n throw new Error(\"realistic-upload target must be input[type=file]\");\n }\n kit.cursor.highlight(element);\n return Promise.resolve(kit.setFileInput(element, url, {})).then((info) => ({\n element: serialize(element),\n upload: info,\n }));\n }\n throw new Error(\"Unsupported realistic subaction: \" + subaction);\n };\n const keyboardTarget = () => findTarget() || document.activeElement || document.body;\n const keyboardWrite = (appendMode) => {\n const target = keyboardTarget();\n if (\n !(\n target instanceof HTMLInputElement ||\n target instanceof HTMLTextAreaElement ||\n target instanceof HTMLSelectElement\n )\n ) {\n throw new Error(\"Keyboard text input requires an input, textarea, or select target.\");\n }\n return setInputValue(appendMode, target);\n };\n const keyPhase = (phase) => {\n const target = keyboardTarget();\n const key = command.key || \"Enter\";\n target.dispatchEvent(new KeyboardEvent(phase, { key, bubbles: true }));\n return { key, phase, selector: selectorFor(target) };\n };\n const scrollTarget = () => findTarget();\n const scroll = () => {\n const target = scrollTarget();\n const direction = command.direction || \"down\";\n const pixels = Math.max(1, Math.abs(Number(command.pixels) || 240));\n const axis = direction === \"left\" || direction === \"right\" ? \"x\" : \"y\";\n const delta = direction === \"up\" || direction === \"left\" ? -pixels : pixels;\n if (target instanceof HTMLElement) {\n if (axis === \"y\") {\n target.scrollTop = (target.scrollTop || 0) + delta;\n return { axis, selector: selectorFor(target), value: target.scrollTop };\n }\n target.scrollLeft = (target.scrollLeft || 0) + delta;\n return { axis, selector: selectorFor(target), value: target.scrollLeft };\n }\n if (axis === \"y\") {\n window.scrollBy(0, delta);\n return { axis, selector: null, value: window.scrollY };\n }\n window.scrollBy(delta, 0);\n return { axis, selector: null, value: window.scrollX };\n };\n const getResult = () => {\n if (command.getMode === \"title\") return document.title;\n if (command.getMode === \"url\") return location.href;\n if (command.getMode === \"count\") {\n if (!command.selector) throw new Error(\"count requires selector\");\n const semantic = mergeSelectorCommand(command.selector);\n return semantic ? Number(Boolean(findSemantic(semantic))) : queryAll(command.selector).length;\n }\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n switch (command.getMode) {\n case \"attr\":\n if (!command.attribute) throw new Error(\"attr lookups require attribute\");\n return element.getAttribute(command.attribute);\n case \"box\":\n return element.getBoundingClientRect();\n case \"checked\":\n return element instanceof HTMLInputElement\n ? Boolean(element.checked)\n : element instanceof HTMLOptionElement\n ? Boolean(element.selected)\n : false;\n case \"enabled\":\n return \"disabled\" in element ? !Boolean(element.disabled) : true;\n case \"html\":\n return element.innerHTML;\n case \"styles\": {\n const computed = getComputedStyle(element);\n return {\n display: computed.display || null,\n visibility: computed.visibility || null,\n opacity: computed.opacity || null,\n };\n }\n case \"text\":\n return normalize(element.textContent);\n case \"value\":\n return element.value ?? element.getAttribute?.(\"value\");\n case \"visible\":\n return isVisible(element);\n default:\n return normalize(element.textContent);\n }\n };\n const waitForCondition = () =>\n new Promise((resolve, reject) => {\n if (\n !command.selector &&\n !command.findBy &&\n !command.text &&\n !command.url &&\n !command.script &&\n Number.isFinite(Number(command.timeoutMs))\n ) {\n const waitedMs = Math.max(0, Number(command.timeoutMs) || 0);\n setTimeout(() => resolve({ ok: true, waitedMs }), waitedMs);\n return;\n }\n const deadline = Date.now() + (Number(command.timeoutMs) || 4000);\n const check = () => {\n try {\n if (command.selector) {\n const found = findTarget();\n const visible =\n command.state === \"hidden\"\n ? !found || !isVisible(found)\n : found && isVisible(found);\n if (visible) {\n resolve({ ok: true, selector: command.selector, state: command.state || \"visible\" });\n return;\n }\n }\n if (command.findBy) {\n const found = findSemantic();\n if (command.state === \"hidden\" ? !found : found) {\n resolve({ findBy: command.findBy, ok: true });\n return;\n }\n }\n if (command.text && normalize(document.body?.textContent).includes(command.text)) {\n resolve({ ok: true, text: command.text });\n return;\n }\n if (command.url && location.href.includes(command.url)) {\n resolve({ ok: true, url: location.href });\n return;\n }\n ${waitScriptBranch}\n if (Date.now() >= deadline) {\n reject(new Error(\"Timed out waiting for browser workspace condition.\"));\n return;\n }\n setTimeout(check, 100);\n } catch (error) {\n reject(error);\n }\n };\n check();\n });\n\n switch (command.subaction) {\n case \"inspect\":\n return { title: document.title, url: location.href, elements: inspect() };\n case \"snapshot\":\n return snapshot();\n case \"get\":\n return { value: getResult() };\n case \"find\": {\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n switch (command.action) {\n case \"check\":\n return setChecked(true);\n case \"click\":\n return activateElement(\"click\", element);\n case \"fill\":\n return setInputValue(false, element);\n case \"focus\":\n return focusElement(element);\n case \"hover\":\n return hoverElement(element);\n case \"text\":\n case undefined:\n return { element: serialize(element), value: normalize(element.textContent) };\n case \"type\":\n return setInputValue(true, element);\n case \"uncheck\":\n return setChecked(false);\n default:\n throw new Error(\"Unsupported find action.\");\n }\n }\n case \"click\": {\n const element = findTarget();\n return activateElement(\"click\", element);\n }\n case \"dblclick\": {\n const element = findTarget();\n return activateElement(\"dblclick\", element);\n }\n case \"check\":\n return setChecked(true);\n case \"fill\":\n return setInputValue(false);\n case \"focus\": {\n const element = findTarget();\n return focusElement(element);\n }\n case \"hover\": {\n const element = findTarget();\n return hoverElement(element);\n }\n case \"keyboardinserttext\":\n return keyboardWrite(false);\n case \"keyboardtype\":\n return keyboardWrite(true);\n case \"keydown\":\n return keyPhase(\"keydown\");\n case \"keyup\":\n return keyPhase(\"keyup\");\n case \"type\":\n return setInputValue(true);\n case \"press\": {\n const target = findTarget() ?? document.activeElement ?? document.body;\n const key = command.key || \"Enter\";\n target.dispatchEvent(new KeyboardEvent(\"keydown\", { key, bubbles: true }));\n target.dispatchEvent(new KeyboardEvent(\"keyup\", { key, bubbles: true }));\n return { key, url: location.href };\n }\n case \"realistic-click\":\n case \"realistic-fill\":\n case \"realistic-type\":\n case \"realistic-press\":\n case \"realistic-upload\":\n case \"cursor-move\":\n case \"cursor-hide\":\n return runRealisticSubaction(command.subaction);\n case \"scroll\":\n return scroll();\n case \"scrollinto\": {\n const element = findTarget();\n if (!element) throw new Error(\"Target element was not found.\");\n if (typeof element.scrollIntoView === \"function\") {\n element.scrollIntoView();\n }\n return { scrolled: true, selector: selectorFor(element) };\n }\n case \"select\":\n return setSelectValue();\n case \"uncheck\":\n return setChecked(false);\n case \"wait\":\n return waitForCondition();\n case \"back\":\n history.back();\n return { url: location.href, title: document.title };\n case \"forward\":\n history.forward();\n return { url: location.href, title: document.title };\n case \"reload\":\n location.reload();\n return { url: location.href, title: document.title };\n default:\n throw new Error(\\`Unsupported desktop browser subaction: \\${command.subaction}\\`);\n }\n})()\n`.trim();\n}\n\nexport function createDesktopBrowserWorkspaceUtilityScript(\n command: BrowserWorkspaceCommand,\n): string {\n return `\n(() => {\n const command = ${JSON.stringify(command)};\n const normalize = (value) => String(value ?? \"\").replace(/\\\\s+/g, \" \").trim();\n const state =\n window.__elizaBrowserWorkspaceState ||\n (window.__elizaBrowserWorkspaceState = {\n clipboardText: \"\",\n consoleEntries: [],\n currentFrame: null,\n dialog: null,\n errors: [],\n highlightedSelector: null,\n mouse: { buttons: [], x: 0, y: 0 },\n networkHar: { active: false, entries: [], startedAt: null },\n networkNextRequestId: 1,\n networkRequests: [],\n networkRoutes: [],\n settings: {\n credentials: null,\n device: null,\n geo: null,\n headers: {},\n media: null,\n offline: false,\n viewport: null\n }\n });\n const patternMatches = (pattern, value) => {\n const trimmed = String(pattern ?? \"\").trim();\n if (!trimmed) return false;\n if (!trimmed.includes(\"*\")) return String(value ?? \"\").includes(trimmed);\n let wildcard = \"\";\n for (let i = 0; i < trimmed.length; i += 1) {\n const char = trimmed[i];\n if (char === \"*\") {\n if (trimmed[i + 1] === \"*\") {\n wildcard += \".*\";\n i += 1;\n } else {\n wildcard += \".*\";\n }\n } else {\n wildcard += char.replace(/[|\\\\\\\\{}()[\\\\]^$+?.]/g, \"\\\\\\\\$&\");\n }\n }\n return new RegExp(\"^\" + wildcard + \"$\", \"i\").test(String(value ?? \"\"));\n };\n const buildSelector = (element) => {\n if (!element || !element.tagName) return null;\n const testId = element.getAttribute && element.getAttribute(\"data-testid\");\n if (testId) return '[data-testid=\"' + testId + '\"]';\n const name = element.getAttribute && element.getAttribute(\"name\");\n if (name) return element.tagName.toLowerCase() + '[name=\"' + name + '\"]';\n const title = element.getAttribute && element.getAttribute(\"title\");\n if (title) return element.tagName.toLowerCase() + '[title=\"' + title + '\"]';\n return element.tagName.toLowerCase();\n };\n const activeDocument = (() => {\n if (!state.currentFrame) return document;\n try {\n const frame = document.querySelector(state.currentFrame);\n return frame && frame.contentDocument ? frame.contentDocument : document;\n } catch {\n return document;\n }\n })();\n const queryOne = (selector, root = activeDocument) => {\n try {\n return root.querySelector(selector);\n } catch {\n throw new Error(\"Invalid selector \" + selector);\n }\n };\n const findByText = (needle) => {\n const wanted = normalize(needle).toLowerCase();\n if (!wanted) return null;\n const candidates = Array.from(\n activeDocument.querySelectorAll(\n \"a, button, input, textarea, select, option, label, h1, h2, h3, [role='button'], [data-testid]\"\n )\n );\n return (\n candidates.find((element) => {\n const haystacks = [\n element.textContent,\n element.getAttribute(\"aria-label\"),\n element.getAttribute(\"placeholder\"),\n element.getAttribute(\"title\"),\n element.getAttribute(\"name\"),\n element.value\n ]\n .map((value) => normalize(value).toLowerCase())\n .filter(Boolean);\n return haystacks.some((value) => value.includes(wanted));\n }) || null\n );\n };\n const resolveTarget = () => {\n if (command.selector) return queryOne(command.selector);\n if (command.text) return findByText(command.text);\n return activeDocument.activeElement || activeDocument.body;\n };\n const recordRequest = (request) => {\n const entry = {\n ...request,\n id: \"req_\" + state.networkNextRequestId++,\n timestamp: new Date().toISOString()\n };\n state.networkRequests.push(entry);\n if (state.networkHar.active) state.networkHar.entries.push(entry);\n return entry;\n };\n if (!state.consoleWrapped) {\n for (const level of [\"log\", \"info\", \"warn\", \"error\"]) {\n console[level] = (...args) => {\n state.consoleEntries.push({\n level,\n message: args.map((value) => normalize(value)).join(\" \"),\n timestamp: new Date().toISOString()\n });\n };\n }\n state.consoleWrapped = true;\n }\n if (!state.dialogWrapped) {\n window.alert = (message) => {\n state.dialog = { defaultValue: null, message: String(message ?? \"\"), open: true, type: \"alert\" };\n };\n window.confirm = (message) => {\n state.dialog = { defaultValue: null, message: String(message ?? \"\"), open: true, type: \"confirm\" };\n return false;\n };\n window.prompt = (message, defaultValue) => {\n state.dialog = {\n defaultValue: defaultValue ?? null,\n message: String(message ?? \"\"),\n open: true,\n type: \"prompt\"\n };\n return null;\n };\n state.dialogWrapped = true;\n }\n if (!state.fetchWrapped) {\n state.originalFetch = window.fetch ? window.fetch.bind(window) : null;\n window.fetch = async (input, init = {}) => {\n const inputUrl =\n typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.toString()\n : typeof input?.url === \"string\"\n ? input.url\n : String(input);\n const url = new URL(inputUrl, location.href).toString();\n if (state.settings.offline) {\n recordRequest({\n matchedRoute: null,\n method: String(init.method || \"GET\").toUpperCase(),\n resourceType: \"fetch\",\n responseBody: null,\n responseHeaders: {},\n status: 0,\n url\n });\n throw new Error(\"Browser workspace is offline.\");\n }\n const route = [...state.networkRoutes].reverse().find((entry) => patternMatches(entry.pattern, url)) || null;\n if (route && route.abort) {\n recordRequest({\n matchedRoute: route.pattern,\n method: String(init.method || \"GET\").toUpperCase(),\n resourceType: \"fetch\",\n responseBody: null,\n responseHeaders: route.headers || {},\n status: 0,\n url\n });\n throw new Error(\"Browser workspace network route aborted request: \" + url);\n }\n if (route && (route.body !== null || route.status !== null || Object.keys(route.headers || {}).length > 0)) {\n const response = new Response(route.body || \"\", {\n headers: route.headers || {},\n status: route.status || 200\n });\n recordRequest({\n matchedRoute: route.pattern,\n method: String(init.method || \"GET\").toUpperCase(),\n resourceType: \"fetch\",\n responseBody: route.body || \"\",\n responseHeaders: route.headers || {},\n status: route.status || 200,\n url\n });\n return response;\n }\n const headers = new Headers(init.headers || {});\n for (const [key, value] of Object.entries(state.settings.headers || {})) {\n if (!headers.has(key)) headers.set(key, value);\n }\n if (state.settings.credentials && state.settings.credentials.username && !headers.has(\"Authorization\")) {\n headers.set(\n \"Authorization\",\n \"Basic \" + btoa(state.settings.credentials.username + \":\" + state.settings.credentials.password)\n );\n }\n const response = await state.originalFetch(url, { ...init, headers });\n recordRequest({\n matchedRoute: null,\n method: String(init.method || \"GET\").toUpperCase(),\n resourceType: \"fetch\",\n responseBody: null,\n responseHeaders: Object.fromEntries(response.headers.entries()),\n status: response.status,\n url: response.url || url\n });\n return response;\n };\n state.fetchWrapped = true;\n }\n Object.defineProperty(navigator, \"onLine\", {\n configurable: true,\n get: () => !state.settings.offline\n });\n switch (command.subaction) {\n case \"clipboard\": {\n const action = command.clipboardAction || \"read\";\n if (action === \"read\") return state.clipboardText;\n if (action === \"write\") {\n state.clipboardText = command.value || command.text || \"\";\n return state.clipboardText;\n }\n if (action === \"copy\") {\n const target = resolveTarget();\n state.clipboardText =\n target && typeof target.value === \"string\"\n ? String(target.value || \"\")\n : normalize(target?.textContent || activeDocument.body?.textContent);\n return state.clipboardText;\n }\n const target = resolveTarget();\n if (target && typeof target.value === \"string\") {\n target.value = String(target.value || \"\") + state.clipboardText;\n target.setAttribute(\"value\", target.value);\n return { selector: buildSelector(target), value: target.value };\n }\n return state.clipboardText;\n }\n case \"mouse\": {\n const action = command.mouseAction || \"move\";\n if (action === \"move\") {\n state.mouse.x = typeof command.x === \"number\" ? command.x : state.mouse.x;\n state.mouse.y = typeof command.y === \"number\" ? command.y : state.mouse.y;\n return state.mouse;\n }\n if (action === \"down\") {\n const button = command.button || \"left\";\n state.mouse.buttons = Array.from(new Set([...(state.mouse.buttons || []), button]));\n return state.mouse;\n }\n if (action === \"up\") {\n const button = command.button || \"left\";\n state.mouse.buttons = (state.mouse.buttons || []).filter((entry) => entry !== button);\n return state.mouse;\n }\n window.scrollBy(command.deltaX || 0, command.deltaY || command.pixels || 240);\n return { axis: Math.abs(command.deltaY || 0) >= Math.abs(command.deltaX || 0) ? \"y\" : \"x\", value: window.scrollY };\n }\n case \"drag\": {\n const source = resolveTarget();\n const target = command.value ? queryOne(command.value) : null;\n if (!source || !target) throw new Error(\"Eliza browser workspace drag requires source selector and target selector in value.\");\n source.setAttribute(\"data-eliza-dragging\", \"true\");\n target.setAttribute(\"data-eliza-drop-target\", \"true\");\n return { source: buildSelector(source), target: buildSelector(target) };\n }\n case \"upload\": {\n const target = resolveTarget();\n if (!target || target.tagName !== \"INPUT\") throw new Error(\"Eliza browser workspace upload requires a file input target.\");\n const files = Array.isArray(command.files) ? command.files.map((entry) => String(entry).split(/[\\\\\\\\/]/).pop()) : [];\n target.setAttribute(\"data-eliza-uploaded-files\", files.join(\",\"));\n return { files, selector: buildSelector(target) };\n }\n case \"set\": {\n const action = command.setAction || \"viewport\";\n if (action === \"viewport\") {\n state.settings.viewport = { width: command.width || 1280, height: command.height || 720, scale: command.scale || 1 };\n } else if (action === \"device\") {\n state.settings.device = command.device || null;\n } else if (action === \"geo\") {\n state.settings.geo =\n typeof command.latitude === \"number\" && typeof command.longitude === \"number\"\n ? { latitude: command.latitude, longitude: command.longitude }\n : null;\n } else if (action === \"offline\") {\n state.settings.offline = Boolean(command.offline);\n } else if (action === \"headers\") {\n state.settings.headers = command.headers || {};\n } else if (action === \"credentials\") {\n state.settings.credentials =\n command.username || command.password\n ? { username: command.username || \"\", password: command.password || \"\" }\n : null;\n } else if (action === \"media\") {\n state.settings.media = command.media || null;\n }\n return state.settings;\n }\n case \"cookies\": {\n const action = command.cookieAction || \"get\";\n if (action === \"clear\") {\n const current = document.cookie || \"\";\n current.split(/;\\\\s*/).forEach((entry) => {\n const name = entry.split(\"=\")[0];\n if (name) document.cookie = name + \"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/\";\n });\n return { cleared: true };\n }\n if (action === \"set\") {\n const name = command.name || command.entryKey;\n if (!name) throw new Error(\"Eliza browser workspace cookies set requires name.\");\n document.cookie = name + \"=\" + (command.value || \"\") + \"; path=/\";\n }\n const cookieString = document.cookie || \"\";\n return Object.fromEntries(\n cookieString\n .split(/;\\\\s*/)\n .filter(Boolean)\n .map((entry) => {\n const [name, ...rest] = entry.split(\"=\");\n return [name, rest.join(\"=\")];\n })\n );\n }\n case \"storage\": {\n const storage = command.storageArea === \"session\" ? sessionStorage : localStorage;\n const action = command.storageAction || \"get\";\n if (action === \"clear\") {\n storage.clear();\n return { cleared: true };\n }\n if (action === \"set\") {\n const key = command.entryKey || command.name;\n if (!key) throw new Error(\"Eliza browser workspace storage set requires entryKey.\");\n storage.setItem(key, command.value || \"\");\n }\n if (command.entryKey || command.name) {\n return storage.getItem(command.entryKey || command.name);\n }\n const out = {};\n for (let i = 0; i < storage.length; i += 1) {\n const key = storage.key(i);\n if (key) out[key] = storage.getItem(key) || \"\";\n }\n return out;\n }\n case \"network\": {\n const action = command.networkAction || \"requests\";\n if (action === \"route\") {\n if (!command.url) throw new Error(\"Eliza browser workspace network route requires url pattern.\");\n state.networkRoutes.push({\n abort: Boolean(command.offline),\n body: command.responseBody ?? null,\n headers: command.responseHeaders || {},\n pattern: command.url,\n status: typeof command.responseStatus === \"number\" ? command.responseStatus : null\n });\n return state.networkRoutes;\n }\n if (action === \"unroute\") {\n state.networkRoutes = command.url\n ? state.networkRoutes.filter((entry) => entry.pattern !== command.url)\n : [];\n return state.networkRoutes;\n }\n if (action === \"request\") {\n return state.networkRequests.find((entry) => entry.id === command.requestId) || null;\n }\n if (action === \"harstart\") {\n state.networkHar = { active: true, entries: [], startedAt: new Date().toISOString() };\n return state.networkHar;\n }\n if (action === \"harstop\") {\n state.networkHar.active = false;\n return { log: { entries: state.networkHar.entries, startedAt: state.networkHar.startedAt } };\n }\n let requests = [...state.networkRequests];\n if (command.filter) requests = requests.filter((entry) => entry.url.includes(command.filter));\n if (command.method) requests = requests.filter((entry) => entry.method === String(command.method).toUpperCase());\n if (command.status) requests = requests.filter((entry) => String(entry.status || \"\") === String(command.status));\n return requests;\n }\n case \"dialog\": {\n const action = command.dialogAction || \"status\";\n if (action === \"status\") return state.dialog;\n if (state.dialog) state.dialog.open = false;\n const result =\n action === \"accept\"\n ? { accepted: true, dialog: state.dialog, promptText: command.promptText || command.value || null }\n : { accepted: false, dialog: state.dialog };\n state.dialog = null;\n return result;\n }\n case \"console\":\n if (command.consoleAction === \"clear\") state.consoleEntries = [];\n return state.consoleEntries;\n case \"errors\":\n if (command.consoleAction === \"clear\") state.errors = [];\n return state.errors;\n case \"highlight\": {\n const target = resolveTarget();\n if (!target) throw new Error(\"Target element was not found.\");\n target.setAttribute(\"data-eliza-highlight\", \"true\");\n state.highlightedSelector = buildSelector(target);\n return { selector: state.highlightedSelector };\n }\n case \"frame\": {\n if ((command.frameAction || \"select\") === \"main\") {\n state.currentFrame = null;\n return { frame: null };\n }\n const frame = command.selector ? document.querySelector(command.selector) : null;\n if (!frame || frame.tagName !== \"IFRAME\") throw new Error(\"Eliza browser workspace frame select requires an iframe selector.\");\n state.currentFrame = buildSelector(frame);\n return { frame: state.currentFrame };\n }\n default:\n throw new Error(\"Unsupported desktop browser workspace utility subaction: \" + command.subaction);\n }\n})()\n`.trim();\n}\n\nexport async function executeDesktopBrowserWorkspaceUtilityCommand(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<BrowserWorkspaceCommandResult> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n if (\n command.subaction === \"cookies\" ||\n command.subaction === \"storage\" ||\n (command.subaction === \"set\" &&\n (command.setAction === \"credentials\" || command.setAction === \"headers\"))\n ) {\n await assertDesktopBrowserWorkspaceCanAccessProfileSecrets(\n id,\n env,\n command.subaction,\n );\n }\n const startedAt = Date.now();\n const result = await evaluateBrowserWorkspaceTab(\n {\n id,\n script: createDesktopBrowserWorkspaceUtilityScript({\n ...command,\n id,\n }),\n },\n env,\n );\n const runtime = getBrowserWorkspaceRuntimeState(\"desktop\", id);\n appendBrowserWorkspaceTraceEntry(runtime, {\n subaction: command.subaction,\n type: \"utility\",\n });\n appendBrowserWorkspaceProfilerEntry(runtime, {\n durationMs: Date.now() - startedAt,\n subaction: command.subaction,\n type: \"utility\",\n });\n return {\n mode: \"desktop\",\n subaction: command.subaction,\n value: result,\n };\n}\n\nexport async function getDesktopBrowserWorkspaceSnapshotRecord(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<BrowserWorkspaceSnapshotRecord> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n const result = await evaluateBrowserWorkspaceTab(\n {\n id,\n script: `\n(() => {\n const activeDocument = (() => {\n const state = window.__elizaBrowserWorkspaceState || {};\n if (!state.currentFrame) return document;\n try {\n const frame = document.querySelector(state.currentFrame);\n return frame && frame.contentDocument ? frame.contentDocument : document;\n } catch {\n return document;\n }\n })();\n const normalize = (value) => String(value ?? \"\").replace(/\\\\s+/g, \" \").trim();\n const controlText = Array.from(activeDocument.querySelectorAll(\"input, textarea, select, option:checked\"))\n .map((element) => {\n const name = element.getAttribute(\"name\") || element.getAttribute(\"id\") || element.tagName.toLowerCase();\n const value =\n element.tagName === \"SELECT\"\n ? element.value\n : typeof element.value === \"string\"\n ? element.value\n : element.textContent || \"\";\n return name + \":\" + normalize(value);\n })\n .filter(Boolean)\n .join(\" \");\n return {\n bodyText: normalize((activeDocument.body?.textContent || \"\") + \" \" + controlText),\n title: normalize(document.title),\n url: location.href\n };\n})()\n `.trim(),\n },\n env,\n );\n return result as BrowserWorkspaceSnapshotRecord;\n}\n\nexport async function getDesktopBrowserWorkspaceSessionState(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<Record<string, unknown>> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n await assertDesktopBrowserWorkspaceCanAccessProfileSecrets(id, env, \"state\");\n const result = await evaluateBrowserWorkspaceTab(\n {\n id,\n script: `\n(() => {\n const state = window.__elizaBrowserWorkspaceState || {};\n const readStorage = (storage) => {\n const out = {};\n for (let i = 0; i < storage.length; i += 1) {\n const key = storage.key(i);\n if (key) out[key] = storage.getItem(key) || \"\";\n }\n return out;\n };\n const cookies = Object.fromEntries(\n String(document.cookie || \"\")\n .split(/;\\\\s*/)\n .filter(Boolean)\n .map((entry) => {\n const [name, ...rest] = entry.split(\"=\");\n return [name, rest.join(\"=\")];\n })\n );\n return {\n clipboard: state.clipboardText || \"\",\n cookies,\n localStorage: readStorage(localStorage),\n sessionStorage: readStorage(sessionStorage),\n settings: state.settings || {},\n url: location.href\n };\n})()\n `.trim(),\n },\n env,\n );\n return result as Record<string, unknown>;\n}\n\nexport async function loadDesktopBrowserWorkspaceSessionState(\n command: BrowserWorkspaceCommand,\n payload: Record<string, unknown>,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n await assertDesktopBrowserWorkspaceCanAccessProfileSecrets(id, env, \"state\");\n await evaluateBrowserWorkspaceTab(\n {\n id,\n script: `\n(() => {\n const payload = ${JSON.stringify(payload)};\n const state =\n window.__elizaBrowserWorkspaceState ||\n (window.__elizaBrowserWorkspaceState = { settings: {} });\n localStorage.clear();\n for (const [key, value] of Object.entries(payload.localStorage || {})) {\n localStorage.setItem(key, String(value ?? \"\"));\n }\n sessionStorage.clear();\n for (const [key, value] of Object.entries(payload.sessionStorage || {})) {\n sessionStorage.setItem(key, String(value ?? \"\"));\n }\n for (const [key, value] of Object.entries(payload.cookies || {})) {\n document.cookie = key + \"=\" + String(value ?? \"\") + \"; path=/\";\n }\n state.clipboardText = typeof payload.clipboard === \"string\" ? payload.clipboard : \"\";\n state.settings = typeof payload.settings === \"object\" && payload.settings ? payload.settings : state.settings;\n return { loaded: true };\n})()\n `.trim(),\n },\n env,\n );\n}\n\nexport async function executeDesktopBrowserWorkspaceDomCommand(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<BrowserWorkspaceCommandResult> {\n assertBrowserWorkspaceUserScriptAllowed(\n command.script,\n \"wait\",\n \"desktop\",\n env,\n );\n const id = await resolveDesktopBrowserWorkspaceTargetTabId(command, env);\n const startedAt = Date.now();\n command = resolveBrowserWorkspaceCommandElementRefs(command, \"desktop\", id);\n const result = await evaluateBrowserWorkspaceTab(\n {\n id,\n script: createDesktopBrowserWorkspaceCommandScript(\n {\n ...command,\n id,\n },\n env,\n ),\n },\n env,\n );\n\n if (command.subaction === \"inspect\" || command.subaction === \"snapshot\") {\n const value =\n result && typeof result === \"object\" && !Array.isArray(result)\n ? (result as {\n bodyText?: string;\n elements?: BrowserWorkspaceDomElementSummary[];\n })\n : null;\n const elements = registerBrowserWorkspaceElementRefs(\n \"desktop\",\n id,\n Array.isArray(value?.elements) ? value.elements : [],\n );\n return {\n mode: \"desktop\",\n subaction: command.subaction,\n elements,\n value: result,\n };\n }\n\n const runtime = getBrowserWorkspaceRuntimeState(\"desktop\", id);\n appendBrowserWorkspaceTraceEntry(runtime, {\n subaction: command.subaction,\n type: \"dom\",\n });\n appendBrowserWorkspaceProfilerEntry(runtime, {\n durationMs: Date.now() - startedAt,\n subaction: command.subaction,\n type: \"dom\",\n });\n return {\n mode: \"desktop\",\n subaction: command.subaction,\n value:\n result && typeof result === \"object\" && !Array.isArray(result)\n ? ((result as { value?: unknown }).value ?? result)\n : result,\n };\n}\n\n// --- Desktop tab resolution ---\n\nexport function resolveBrowserWorkspaceCurrentTab(\n tabs: BrowserWorkspaceTab[],\n): BrowserWorkspaceTab | null {\n if (tabs.length === 0) {\n return null;\n }\n\n return (\n tabs.find((tab) => tab.visible) ??\n [...tabs].sort((left, right) => {\n const leftTime = left.lastFocusedAt ?? left.updatedAt ?? \"\";\n const rightTime = right.lastFocusedAt ?? right.updatedAt ?? \"\";\n return (\n rightTime.localeCompare(leftTime) || left.id.localeCompare(right.id)\n );\n })[0] ??\n null\n );\n}\n\nexport async function resolveDesktopBrowserWorkspaceTargetTabId(\n command: BrowserWorkspaceCommand,\n env: NodeJS.ProcessEnv,\n): Promise<string> {\n if (command.id?.trim()) {\n return command.id.trim();\n }\n\n // This function is only called on desktop-bridge paths where the\n // Electrobun HTTP bridge is configured. Calling `listBrowserWorkspaceTabs`\n // from `browser-workspace.ts` here would create a compile-time circular\n // dependency (browser-workspace → desktop → browser-workspace); the web\n // fallback in that function is also not reachable from these call sites.\n // Query the bridge directly and avoid the circular detour.\n const payload = await requestBrowserWorkspace<{\n tabs?: BrowserWorkspaceTab[];\n }>(\"/tabs\", undefined, env);\n const tabs = Array.isArray(payload.tabs) ? payload.tabs : [];\n const current = resolveBrowserWorkspaceCurrentTab(tabs);\n if (!current) {\n throw createBrowserWorkspaceCommandTargetError(command.subaction);\n }\n return current.id;\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YA8oB/B,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuH1B,KAAK;AACP;AAEO,SAAS,2CACd,SACQ;AACR,SAAO;AAAA;AAAA,oBAEW,KAAK,UAAU,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6azC,KAAK;AACP;AAEA,eAAsB,6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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-workspace-forms.d.ts","sourceRoot":"","sources":["../../src/workspace/browser-workspace-forms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"browser-workspace-forms.d.ts","sourceRoot":"","sources":["../../src/workspace/browser-workspace-forms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAsBnC,OAAO,KAAK,EACV,6BAA6B,EAC7B,+BAA+B,EAC/B,2BAA2B,EAC5B,MAAM,8BAA8B,CAAC;AAEtC,wBAAgB,wCAAwC,CACtD,OAAO,EAAE,OAAO,EAChB,SAAS,EACL,WAAW,GACX,MAAM,GACN,oBAAoB,GACpB,cAAc,GACd,QAAQ,GACR,MAAM,GACT,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,CAe5D;AAED,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,OAAO,GAAG,SAAS,GAC7B,gBAAgB,CAYlB;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,EACnE,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED,wBAAsB,kCAAkC,CACtD,GAAG,EAAE,2BAA2B,EAChC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,OAAO,GAAG,UAAU,GAC9B,OAAO,CAAC,6BAA6B,CAAC,CAwExC;AAED,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,KAAK,EACV,OAAO,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,EAAE,+BAA+B,EAC1C,MAAM,EAAE,MAAM,GACb;IACD,IAAI,EAAE,GAAG,GAAG,GAAG,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf,CAoCA;AAED,wBAAsB,6BAA6B,CACjD,GAAG,EAAE,2BAA2B,EAChC,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,IAAI,CAAC,CA8Df;AAKD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAExE,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE1E;AAED,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,2BAA2B,GAC/B,mBAAmB,CAYrB;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,2BAA2B,EAChC,OAAO,EAAE,MAAM,GACd,IAAI,CAKN;AAED,wBAAsB,kCAAkC,CACtD,GAAG,EAAE,2BAA2B,GAC/B,OAAO,CAAC,IAAI,CAAC,CAwCf;AAED,wBAAsB,0CAA0C,CAC9D,GAAG,EAAE,2BAA2B,GAC/B,OAAO,CAAC,KAAK,CAAC,CAKhB"}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
normalizeBrowserWorkspaceText
|
|
9
9
|
} from "./browser-workspace-helpers.js";
|
|
10
10
|
import {
|
|
11
|
+
createEmptyWebBrowserWorkspaceDom,
|
|
11
12
|
ensureBrowserWorkspaceDom,
|
|
12
13
|
getJSDOMClass,
|
|
13
14
|
installBrowserWorkspaceWebRuntime
|
|
@@ -220,7 +221,6 @@ function pushWebBrowserWorkspaceHistory(tab, nextUrl) {
|
|
|
220
221
|
}
|
|
221
222
|
async function loadWebBrowserWorkspaceTabDocument(tab) {
|
|
222
223
|
const state = getBrowserWorkspaceRuntimeState("web", tab.id);
|
|
223
|
-
const { createEmptyWebBrowserWorkspaceDom } = await import("./browser-workspace-jsdom.js");
|
|
224
224
|
if (tab.url === "about:blank") {
|
|
225
225
|
tab.dom = createEmptyWebBrowserWorkspaceDom(tab.url);
|
|
226
226
|
installBrowserWorkspaceWebRuntime(tab, tab.dom);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/workspace/browser-workspace-forms.ts"],"sourcesContent":["import type { JSDOM } from \"jsdom\";\nimport {\n buildBrowserWorkspaceElementSelector,\n findClosestBrowserWorkspaceForm,\n} from \"./browser-workspace-elements.js\";\nimport {\n assertBrowserWorkspaceUrl,\n inferBrowserWorkspaceTitle,\n normalizeBrowserWorkspaceText,\n} from \"./browser-workspace-helpers.js\";\nimport {\n ensureBrowserWorkspaceDom,\n getJSDOMClass,\n installBrowserWorkspaceWebRuntime,\n} from \"./browser-workspace-jsdom.js\";\nimport { fetchBrowserWorkspaceTrackedResponse } from \"./browser-workspace-network.js\";\nimport {\n getBrowserWorkspaceRuntimeState,\n getBrowserWorkspaceTimestamp,\n resetBrowserWorkspaceRuntimeNavigationState,\n} from \"./browser-workspace-state.js\";\nimport type {\n BrowserWorkspaceCommandResult,\n BrowserWorkspaceScrollDirection,\n WebBrowserWorkspaceTabState,\n} from \"./browser-workspace-types.js\";\n\nexport function ensureBrowserWorkspaceFormControlElement(\n element: Element,\n subaction:\n | \"clipboard\"\n | \"fill\"\n | \"keyboardinserttext\"\n | \"keyboardtype\"\n | \"select\"\n | \"type\",\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement {\n if (\n element.tagName === \"INPUT\" ||\n element.tagName === \"TEXTAREA\" ||\n element.tagName === \"SELECT\"\n ) {\n return element as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement;\n }\n\n throw new Error(\n `Eliza browser workspace ${subaction} requires an input, textarea, or select target.`,\n );\n}\n\nexport function ensureBrowserWorkspaceCheckboxElement(\n element: Element,\n subaction: \"check\" | \"uncheck\",\n): HTMLInputElement {\n if (element.tagName === \"INPUT\") {\n const input = element as HTMLInputElement;\n const type = input.type.trim().toLowerCase();\n if (type === \"checkbox\" || type === \"radio\") {\n return input;\n }\n }\n\n throw new Error(\n `Eliza browser workspace ${subaction} requires a checkbox or radio input target.`,\n );\n}\n\nexport function setBrowserWorkspaceControlValue(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n nextValue: string,\n): void {\n control.value = nextValue;\n if (control.tagName === \"TEXTAREA\") {\n control.textContent = nextValue;\n }\n control.setAttribute(\"value\", nextValue);\n}\n\nexport async function activateWebBrowserWorkspaceElement(\n tab: WebBrowserWorkspaceTabState,\n element: Element,\n subaction: \"click\" | \"dblclick\",\n): Promise<BrowserWorkspaceCommandResult> {\n const tag = element.tagName.toLowerCase();\n if (tag === \"a\") {\n const href = element.getAttribute(\"href\")?.trim();\n if (!href) {\n throw new Error(\"Target link does not have an href.\");\n }\n const nextUrl = new URL(href, tab.url).toString();\n clearWebBrowserWorkspaceTabElementRefs(tab.id);\n tab.url = assertBrowserWorkspaceUrl(nextUrl);\n tab.title = inferBrowserWorkspaceTitle(tab.url);\n tab.dom = null;\n tab.loadedUrl = null;\n pushWebBrowserWorkspaceHistory(tab, tab.url);\n await loadWebBrowserWorkspaceTabDocument(tab);\n return {\n mode: \"web\",\n subaction,\n tab: cloneWebBrowserWorkspaceTabState(tab),\n value: {\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n selector: buildBrowserWorkspaceElementSelector(element),\n url: tab.url,\n },\n };\n }\n\n const inputElement = tag === \"input\" ? (element as HTMLInputElement) : null;\n const inputType = inputElement?.type?.toLowerCase() ?? \"\";\n if (inputElement && (inputType === \"checkbox\" || inputType === \"radio\")) {\n inputElement.checked = inputType === \"radio\" ? true : !inputElement.checked;\n return {\n mode: \"web\",\n subaction,\n value: {\n checked: inputElement.checked,\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n selector: buildBrowserWorkspaceElementSelector(element),\n },\n };\n }\n\n const submitForm = findClosestBrowserWorkspaceForm(element);\n if (\n submitForm &&\n (tag === \"form\" ||\n tag === \"button\" ||\n (tag === \"input\" &&\n [\"button\", \"image\", \"submit\"].includes(inputType || \"submit\")))\n ) {\n await submitWebBrowserWorkspaceForm(tab, submitForm);\n return {\n mode: \"web\",\n subaction,\n tab: cloneWebBrowserWorkspaceTabState(tab),\n value: {\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n selector: buildBrowserWorkspaceElementSelector(element),\n url: tab.url,\n },\n };\n }\n\n return {\n mode: \"web\",\n subaction,\n value: {\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n selector: buildBrowserWorkspaceElementSelector(element),\n text: normalizeBrowserWorkspaceText(element.textContent),\n },\n };\n}\n\nexport function scrollWebBrowserWorkspaceTarget(\n dom: JSDOM,\n element: Element | null,\n direction: BrowserWorkspaceScrollDirection,\n pixels: number,\n): {\n axis: \"x\" | \"y\";\n selector: string | null;\n value: number;\n} {\n const resolvedPixels = Number.isFinite(pixels)\n ? Math.max(1, Math.abs(pixels))\n : 240;\n const axis = direction === \"left\" || direction === \"right\" ? \"x\" : \"y\";\n const delta =\n direction === \"up\" || direction === \"left\"\n ? -resolvedPixels\n : resolvedPixels;\n\n if (element && element instanceof dom.window.HTMLElement) {\n if (axis === \"y\") {\n element.scrollTop = (element.scrollTop || 0) + delta;\n return {\n axis,\n selector: buildBrowserWorkspaceElementSelector(element),\n value: element.scrollTop,\n };\n }\n element.scrollLeft = (element.scrollLeft || 0) + delta;\n return {\n axis,\n selector: buildBrowserWorkspaceElementSelector(element),\n value: element.scrollLeft,\n };\n }\n\n const key = axis === \"y\" ? \"__elizaScrollY\" : \"__elizaScrollX\";\n const current = Number(Reflect.get(dom.window, key) ?? 0);\n const next = current + delta;\n Reflect.set(dom.window, key, next);\n return {\n axis,\n selector: null,\n value: next,\n };\n}\n\nexport async function submitWebBrowserWorkspaceForm(\n tab: WebBrowserWorkspaceTabState,\n form: HTMLFormElement,\n): Promise<void> {\n const state = getBrowserWorkspaceRuntimeState(\"web\", tab.id);\n const dom = ensureBrowserWorkspaceDom(tab);\n const action = form.getAttribute(\"action\")?.trim() || tab.url;\n const method = (form.getAttribute(\"method\")?.trim() || \"get\").toLowerCase();\n const submitUrl = new URL(action, tab.url).toString();\n const formData = new dom.window.FormData(form);\n const searchParams = new URLSearchParams();\n\n for (const [key, value] of formData.entries()) {\n searchParams.append(key, String(value));\n }\n\n if (method === \"get\") {\n const nextUrl = new URL(submitUrl);\n nextUrl.search = searchParams.toString();\n clearWebBrowserWorkspaceTabElementRefs(tab.id);\n tab.url = nextUrl.toString();\n tab.title = inferBrowserWorkspaceTitle(tab.url);\n tab.dom = null;\n tab.loadedUrl = null;\n pushWebBrowserWorkspaceHistory(tab, tab.url);\n await loadWebBrowserWorkspaceTabDocument(tab);\n return;\n }\n\n const response = await fetchBrowserWorkspaceTrackedResponse(\n state,\n submitUrl,\n {\n body: searchParams.toString(),\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded; charset=utf-8\",\n },\n method: method.toUpperCase(),\n },\n \"document\",\n );\n\n if (!response.ok) {\n throw new Error(\n `Browser workspace form submit failed (${response.status}): ${submitUrl}`,\n );\n }\n\n const html = await response.text();\n const finalUrl = assertBrowserWorkspaceUrl(response.url?.trim() || submitUrl);\n const nextDom = new (getJSDOMClass())(html, {\n pretendToBeVisual: true,\n url: finalUrl,\n });\n installBrowserWorkspaceWebRuntime(tab, nextDom);\n resetBrowserWorkspaceRuntimeNavigationState(state);\n clearWebBrowserWorkspaceTabElementRefs(tab.id);\n tab.url = finalUrl;\n tab.dom = nextDom;\n tab.loadedUrl = finalUrl;\n tab.title =\n normalizeBrowserWorkspaceText(nextDom.window.document.title) ||\n inferBrowserWorkspaceTitle(finalUrl);\n tab.updatedAt = getBrowserWorkspaceTimestamp();\n pushWebBrowserWorkspaceHistory(tab, finalUrl);\n}\n\n// --- Web tab helpers that forms/activation need ---\n\nimport { clearBrowserWorkspaceElementRefs } from \"./browser-workspace-state.js\";\nimport type { BrowserWorkspaceTab } from \"./browser-workspace-types.js\";\n\nexport function clearWebBrowserWorkspaceTabElementRefs(tabId: string): void {\n clearBrowserWorkspaceElementRefs(\"web\", tabId);\n}\n\nexport function cloneWebBrowserWorkspaceTabState(\n tab: WebBrowserWorkspaceTabState,\n): BrowserWorkspaceTab {\n return {\n id: tab.id,\n title: tab.title,\n url: tab.url,\n partition: tab.partition,\n kind: tab.kind,\n visible: tab.visible,\n createdAt: tab.createdAt,\n updatedAt: tab.updatedAt,\n lastFocusedAt: tab.lastFocusedAt,\n };\n}\n\nexport function pushWebBrowserWorkspaceHistory(\n tab: WebBrowserWorkspaceTabState,\n nextUrl: string,\n): void {\n const nextHistory = tab.history.slice(0, tab.historyIndex + 1);\n nextHistory.push(nextUrl);\n tab.history = nextHistory;\n tab.historyIndex = nextHistory.length - 1;\n}\n\nexport async function loadWebBrowserWorkspaceTabDocument(\n tab: WebBrowserWorkspaceTabState,\n): Promise<void> {\n const state = getBrowserWorkspaceRuntimeState(\"web\", tab.id);\n const { createEmptyWebBrowserWorkspaceDom } = await import(\n \"./browser-workspace-jsdom.js\"\n );\n if (tab.url === \"about:blank\") {\n tab.dom = createEmptyWebBrowserWorkspaceDom(tab.url);\n installBrowserWorkspaceWebRuntime(tab, tab.dom);\n tab.loadedUrl = tab.url;\n tab.title = \"New Tab\";\n tab.updatedAt = getBrowserWorkspaceTimestamp();\n return;\n }\n\n const response = await fetchBrowserWorkspaceTrackedResponse(\n state,\n tab.url,\n {},\n \"document\",\n );\n if (!response.ok) {\n throw new Error(\n `Browser workspace web load failed (${response.status}): ${tab.url}`,\n );\n }\n\n const html = await response.text();\n const finalUrl = assertBrowserWorkspaceUrl(response.url?.trim() || tab.url);\n const dom = new (getJSDOMClass())(html, {\n pretendToBeVisual: true,\n url: finalUrl,\n });\n installBrowserWorkspaceWebRuntime(tab, dom);\n resetBrowserWorkspaceRuntimeNavigationState(state);\n\n tab.dom = dom;\n tab.loadedUrl = finalUrl;\n tab.url = finalUrl;\n tab.title =\n normalizeBrowserWorkspaceText(dom.window.document.title) ||\n inferBrowserWorkspaceTitle(finalUrl);\n tab.updatedAt = getBrowserWorkspaceTimestamp();\n tab.history[tab.historyIndex] = finalUrl;\n}\n\nexport async function ensureLoadedWebBrowserWorkspaceTabDocument(\n tab: WebBrowserWorkspaceTabState,\n): Promise<JSDOM> {\n if (!tab.dom || tab.loadedUrl !== tab.url) {\n await loadWebBrowserWorkspaceTabDocument(tab);\n }\n return ensureBrowserWorkspaceDom(tab);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/workspace/browser-workspace-forms.ts"],"sourcesContent":["import type { JSDOM } from \"jsdom\";\nimport {\n buildBrowserWorkspaceElementSelector,\n findClosestBrowserWorkspaceForm,\n} from \"./browser-workspace-elements.js\";\nimport {\n assertBrowserWorkspaceUrl,\n inferBrowserWorkspaceTitle,\n normalizeBrowserWorkspaceText,\n} from \"./browser-workspace-helpers.js\";\nimport {\n createEmptyWebBrowserWorkspaceDom,\n ensureBrowserWorkspaceDom,\n getJSDOMClass,\n installBrowserWorkspaceWebRuntime,\n} from \"./browser-workspace-jsdom.js\";\nimport { fetchBrowserWorkspaceTrackedResponse } from \"./browser-workspace-network.js\";\nimport {\n getBrowserWorkspaceRuntimeState,\n getBrowserWorkspaceTimestamp,\n resetBrowserWorkspaceRuntimeNavigationState,\n} from \"./browser-workspace-state.js\";\nimport type {\n BrowserWorkspaceCommandResult,\n BrowserWorkspaceScrollDirection,\n WebBrowserWorkspaceTabState,\n} from \"./browser-workspace-types.js\";\n\nexport function ensureBrowserWorkspaceFormControlElement(\n element: Element,\n subaction:\n | \"clipboard\"\n | \"fill\"\n | \"keyboardinserttext\"\n | \"keyboardtype\"\n | \"select\"\n | \"type\",\n): HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement {\n if (\n element.tagName === \"INPUT\" ||\n element.tagName === \"TEXTAREA\" ||\n element.tagName === \"SELECT\"\n ) {\n return element as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement;\n }\n\n throw new Error(\n `Eliza browser workspace ${subaction} requires an input, textarea, or select target.`,\n );\n}\n\nexport function ensureBrowserWorkspaceCheckboxElement(\n element: Element,\n subaction: \"check\" | \"uncheck\",\n): HTMLInputElement {\n if (element.tagName === \"INPUT\") {\n const input = element as HTMLInputElement;\n const type = input.type.trim().toLowerCase();\n if (type === \"checkbox\" || type === \"radio\") {\n return input;\n }\n }\n\n throw new Error(\n `Eliza browser workspace ${subaction} requires a checkbox or radio input target.`,\n );\n}\n\nexport function setBrowserWorkspaceControlValue(\n control: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,\n nextValue: string,\n): void {\n control.value = nextValue;\n if (control.tagName === \"TEXTAREA\") {\n control.textContent = nextValue;\n }\n control.setAttribute(\"value\", nextValue);\n}\n\nexport async function activateWebBrowserWorkspaceElement(\n tab: WebBrowserWorkspaceTabState,\n element: Element,\n subaction: \"click\" | \"dblclick\",\n): Promise<BrowserWorkspaceCommandResult> {\n const tag = element.tagName.toLowerCase();\n if (tag === \"a\") {\n const href = element.getAttribute(\"href\")?.trim();\n if (!href) {\n throw new Error(\"Target link does not have an href.\");\n }\n const nextUrl = new URL(href, tab.url).toString();\n clearWebBrowserWorkspaceTabElementRefs(tab.id);\n tab.url = assertBrowserWorkspaceUrl(nextUrl);\n tab.title = inferBrowserWorkspaceTitle(tab.url);\n tab.dom = null;\n tab.loadedUrl = null;\n pushWebBrowserWorkspaceHistory(tab, tab.url);\n await loadWebBrowserWorkspaceTabDocument(tab);\n return {\n mode: \"web\",\n subaction,\n tab: cloneWebBrowserWorkspaceTabState(tab),\n value: {\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n selector: buildBrowserWorkspaceElementSelector(element),\n url: tab.url,\n },\n };\n }\n\n const inputElement = tag === \"input\" ? (element as HTMLInputElement) : null;\n const inputType = inputElement?.type?.toLowerCase() ?? \"\";\n if (inputElement && (inputType === \"checkbox\" || inputType === \"radio\")) {\n inputElement.checked = inputType === \"radio\" ? true : !inputElement.checked;\n return {\n mode: \"web\",\n subaction,\n value: {\n checked: inputElement.checked,\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n selector: buildBrowserWorkspaceElementSelector(element),\n },\n };\n }\n\n const submitForm = findClosestBrowserWorkspaceForm(element);\n if (\n submitForm &&\n (tag === \"form\" ||\n tag === \"button\" ||\n (tag === \"input\" &&\n [\"button\", \"image\", \"submit\"].includes(inputType || \"submit\")))\n ) {\n await submitWebBrowserWorkspaceForm(tab, submitForm);\n return {\n mode: \"web\",\n subaction,\n tab: cloneWebBrowserWorkspaceTabState(tab),\n value: {\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n selector: buildBrowserWorkspaceElementSelector(element),\n url: tab.url,\n },\n };\n }\n\n return {\n mode: \"web\",\n subaction,\n value: {\n clickCount: subaction === \"dblclick\" ? 2 : 1,\n selector: buildBrowserWorkspaceElementSelector(element),\n text: normalizeBrowserWorkspaceText(element.textContent),\n },\n };\n}\n\nexport function scrollWebBrowserWorkspaceTarget(\n dom: JSDOM,\n element: Element | null,\n direction: BrowserWorkspaceScrollDirection,\n pixels: number,\n): {\n axis: \"x\" | \"y\";\n selector: string | null;\n value: number;\n} {\n const resolvedPixels = Number.isFinite(pixels)\n ? Math.max(1, Math.abs(pixels))\n : 240;\n const axis = direction === \"left\" || direction === \"right\" ? \"x\" : \"y\";\n const delta =\n direction === \"up\" || direction === \"left\"\n ? -resolvedPixels\n : resolvedPixels;\n\n if (element && element instanceof dom.window.HTMLElement) {\n if (axis === \"y\") {\n element.scrollTop = (element.scrollTop || 0) + delta;\n return {\n axis,\n selector: buildBrowserWorkspaceElementSelector(element),\n value: element.scrollTop,\n };\n }\n element.scrollLeft = (element.scrollLeft || 0) + delta;\n return {\n axis,\n selector: buildBrowserWorkspaceElementSelector(element),\n value: element.scrollLeft,\n };\n }\n\n const key = axis === \"y\" ? \"__elizaScrollY\" : \"__elizaScrollX\";\n const current = Number(Reflect.get(dom.window, key) ?? 0);\n const next = current + delta;\n Reflect.set(dom.window, key, next);\n return {\n axis,\n selector: null,\n value: next,\n };\n}\n\nexport async function submitWebBrowserWorkspaceForm(\n tab: WebBrowserWorkspaceTabState,\n form: HTMLFormElement,\n): Promise<void> {\n const state = getBrowserWorkspaceRuntimeState(\"web\", tab.id);\n const dom = ensureBrowserWorkspaceDom(tab);\n const action = form.getAttribute(\"action\")?.trim() || tab.url;\n const method = (form.getAttribute(\"method\")?.trim() || \"get\").toLowerCase();\n const submitUrl = new URL(action, tab.url).toString();\n const formData = new dom.window.FormData(form);\n const searchParams = new URLSearchParams();\n\n for (const [key, value] of formData.entries()) {\n searchParams.append(key, String(value));\n }\n\n if (method === \"get\") {\n const nextUrl = new URL(submitUrl);\n nextUrl.search = searchParams.toString();\n clearWebBrowserWorkspaceTabElementRefs(tab.id);\n tab.url = nextUrl.toString();\n tab.title = inferBrowserWorkspaceTitle(tab.url);\n tab.dom = null;\n tab.loadedUrl = null;\n pushWebBrowserWorkspaceHistory(tab, tab.url);\n await loadWebBrowserWorkspaceTabDocument(tab);\n return;\n }\n\n const response = await fetchBrowserWorkspaceTrackedResponse(\n state,\n submitUrl,\n {\n body: searchParams.toString(),\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded; charset=utf-8\",\n },\n method: method.toUpperCase(),\n },\n \"document\",\n );\n\n if (!response.ok) {\n throw new Error(\n `Browser workspace form submit failed (${response.status}): ${submitUrl}`,\n );\n }\n\n const html = await response.text();\n const finalUrl = assertBrowserWorkspaceUrl(response.url?.trim() || submitUrl);\n const nextDom = new (getJSDOMClass())(html, {\n pretendToBeVisual: true,\n url: finalUrl,\n });\n installBrowserWorkspaceWebRuntime(tab, nextDom);\n resetBrowserWorkspaceRuntimeNavigationState(state);\n clearWebBrowserWorkspaceTabElementRefs(tab.id);\n tab.url = finalUrl;\n tab.dom = nextDom;\n tab.loadedUrl = finalUrl;\n tab.title =\n normalizeBrowserWorkspaceText(nextDom.window.document.title) ||\n inferBrowserWorkspaceTitle(finalUrl);\n tab.updatedAt = getBrowserWorkspaceTimestamp();\n pushWebBrowserWorkspaceHistory(tab, finalUrl);\n}\n\n// --- Web tab helpers that forms/activation need ---\n\nimport { clearBrowserWorkspaceElementRefs } from \"./browser-workspace-state.js\";\nimport type { BrowserWorkspaceTab } from \"./browser-workspace-types.js\";\n\nexport function clearWebBrowserWorkspaceTabElementRefs(tabId: string): void {\n clearBrowserWorkspaceElementRefs(\"web\", tabId);\n}\n\nexport function cloneWebBrowserWorkspaceTabState(\n tab: WebBrowserWorkspaceTabState,\n): BrowserWorkspaceTab {\n return {\n id: tab.id,\n title: tab.title,\n url: tab.url,\n partition: tab.partition,\n kind: tab.kind,\n visible: tab.visible,\n createdAt: tab.createdAt,\n updatedAt: tab.updatedAt,\n lastFocusedAt: tab.lastFocusedAt,\n };\n}\n\nexport function pushWebBrowserWorkspaceHistory(\n tab: WebBrowserWorkspaceTabState,\n nextUrl: string,\n): void {\n const nextHistory = tab.history.slice(0, tab.historyIndex + 1);\n nextHistory.push(nextUrl);\n tab.history = nextHistory;\n tab.historyIndex = nextHistory.length - 1;\n}\n\nexport async function loadWebBrowserWorkspaceTabDocument(\n tab: WebBrowserWorkspaceTabState,\n): Promise<void> {\n const state = getBrowserWorkspaceRuntimeState(\"web\", tab.id);\n if (tab.url === \"about:blank\") {\n tab.dom = createEmptyWebBrowserWorkspaceDom(tab.url);\n installBrowserWorkspaceWebRuntime(tab, tab.dom);\n tab.loadedUrl = tab.url;\n tab.title = \"New Tab\";\n tab.updatedAt = getBrowserWorkspaceTimestamp();\n return;\n }\n\n const response = await fetchBrowserWorkspaceTrackedResponse(\n state,\n tab.url,\n {},\n \"document\",\n );\n if (!response.ok) {\n throw new Error(\n `Browser workspace web load failed (${response.status}): ${tab.url}`,\n );\n }\n\n const html = await response.text();\n const finalUrl = assertBrowserWorkspaceUrl(response.url?.trim() || tab.url);\n const dom = new (getJSDOMClass())(html, {\n pretendToBeVisual: true,\n url: finalUrl,\n });\n installBrowserWorkspaceWebRuntime(tab, dom);\n resetBrowserWorkspaceRuntimeNavigationState(state);\n\n tab.dom = dom;\n tab.loadedUrl = finalUrl;\n tab.url = finalUrl;\n tab.title =\n normalizeBrowserWorkspaceText(dom.window.document.title) ||\n inferBrowserWorkspaceTitle(finalUrl);\n tab.updatedAt = getBrowserWorkspaceTimestamp();\n tab.history[tab.historyIndex] = finalUrl;\n}\n\nexport async function ensureLoadedWebBrowserWorkspaceTabDocument(\n tab: WebBrowserWorkspaceTabState,\n): Promise<JSDOM> {\n if (!tab.dom || tab.loadedUrl !== tab.url) {\n await loadWebBrowserWorkspaceTabDocument(tab);\n }\n return ensureBrowserWorkspaceDom(tab);\n}\n"],"mappings":"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","names":[]}
|