free-computer-use 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +31 -0
- package/CHANGELOG.md +19 -0
- package/CONTRIBUTING.md +66 -0
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/SECURITY.md +133 -0
- package/artifacts/benchmark-public.json +797 -0
- package/assets/readme/hero.svg +53 -0
- package/assets/readme/incident-demo.gif +0 -0
- package/assets/readme/incident-evidence.json +52 -0
- package/dist/actions/compiler.d.ts +5 -0
- package/dist/actions/compiler.js +16 -0
- package/dist/actions/compiler.js.map +1 -0
- package/dist/actions/executor.d.ts +38 -0
- package/dist/actions/executor.js +305 -0
- package/dist/actions/executor.js.map +1 -0
- package/dist/actions/policy.d.ts +4 -0
- package/dist/actions/policy.js +18 -0
- package/dist/actions/policy.js.map +1 -0
- package/dist/actions/schema.d.ts +4149 -0
- package/dist/actions/schema.js +59 -0
- package/dist/actions/schema.js.map +1 -0
- package/dist/adapters/generic.d.ts +14 -0
- package/dist/adapters/generic.js +33 -0
- package/dist/adapters/generic.js.map +1 -0
- package/dist/agent/Agent.d.ts +69 -0
- package/dist/agent/Agent.js +375 -0
- package/dist/agent/Agent.js.map +1 -0
- package/dist/agent/Control.d.ts +23 -0
- package/dist/agent/Control.js +51 -0
- package/dist/agent/Control.js.map +1 -0
- package/dist/agent/TokenBudget.d.ts +52 -0
- package/dist/agent/TokenBudget.js +47 -0
- package/dist/agent/TokenBudget.js.map +1 -0
- package/dist/agent/goalCriteria.d.ts +2 -0
- package/dist/agent/goalCriteria.js +13 -0
- package/dist/agent/goalCriteria.js.map +1 -0
- package/dist/browser/Browser.d.ts +72 -0
- package/dist/browser/Browser.js +378 -0
- package/dist/browser/Browser.js.map +1 -0
- package/dist/browser/DomExtractor.d.ts +6 -0
- package/dist/browser/DomExtractor.js +134 -0
- package/dist/browser/DomExtractor.js.map +1 -0
- package/dist/browser/Interaction.d.ts +43 -0
- package/dist/browser/Interaction.js +225 -0
- package/dist/browser/Interaction.js.map +1 -0
- package/dist/browser/NetworkGuardProxy.d.ts +31 -0
- package/dist/browser/NetworkGuardProxy.js +331 -0
- package/dist/browser/NetworkGuardProxy.js.map +1 -0
- package/dist/browser/Observer.d.ts +12 -0
- package/dist/browser/Observer.js +31 -0
- package/dist/browser/Observer.js.map +1 -0
- package/dist/browser/PageCompressor.d.ts +47 -0
- package/dist/browser/PageCompressor.js +62 -0
- package/dist/browser/PageCompressor.js.map +1 -0
- package/dist/browser/SecurityBoundaryError.d.ts +3 -0
- package/dist/browser/SecurityBoundaryError.js +4 -0
- package/dist/browser/SecurityBoundaryError.js.map +1 -0
- package/dist/browser/SelectorEngine.d.ts +18 -0
- package/dist/browser/SelectorEngine.js +72 -0
- package/dist/browser/SelectorEngine.js.map +1 -0
- package/dist/browser/types.d.ts +38 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +284 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +77 -0
- package/dist/config.js.map +1 -0
- package/dist/history/TraceStore.d.ts +29 -0
- package/dist/history/TraceStore.js +53 -0
- package/dist/history/TraceStore.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/ClaudeSubscriptionProvider.d.ts +26 -0
- package/dist/llm/ClaudeSubscriptionProvider.js +182 -0
- package/dist/llm/ClaudeSubscriptionProvider.js.map +1 -0
- package/dist/llm/CodexSubscriptionProvider.d.ts +26 -0
- package/dist/llm/CodexSubscriptionProvider.js +168 -0
- package/dist/llm/CodexSubscriptionProvider.js.map +1 -0
- package/dist/llm/FlashProvider.d.ts +27 -0
- package/dist/llm/FlashProvider.js +103 -0
- package/dist/llm/FlashProvider.js.map +1 -0
- package/dist/llm/LLMProvider.d.ts +37 -0
- package/dist/llm/LLMProvider.js +2 -0
- package/dist/llm/LLMProvider.js.map +1 -0
- package/dist/llm/cliEnvironment.d.ts +5 -0
- package/dist/llm/cliEnvironment.js +21 -0
- package/dist/llm/cliEnvironment.js.map +1 -0
- package/dist/llm/prompts.d.ts +4 -0
- package/dist/llm/prompts.js +12 -0
- package/dist/llm/prompts.js.map +1 -0
- package/dist/llm/structuredOutput.d.ts +3 -0
- package/dist/llm/structuredOutput.js +58 -0
- package/dist/llm/structuredOutput.js.map +1 -0
- package/dist/mcp/index.d.ts +13 -0
- package/dist/mcp/index.js +243 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/profile/ProfileStore.d.ts +7 -0
- package/dist/profile/ProfileStore.js +52 -0
- package/dist/profile/ProfileStore.js.map +1 -0
- package/dist/profile/VariableResolver.d.ts +15 -0
- package/dist/profile/VariableResolver.js +50 -0
- package/dist/profile/VariableResolver.js.map +1 -0
- package/dist/server/index.d.ts +14 -0
- package/dist/server/index.js +239 -0
- package/dist/server/index.js.map +1 -0
- package/dist/ui/app.js +132 -0
- package/dist/ui/index.html +29 -0
- package/dist/ui/logo.svg +5 -0
- package/dist/ui/results.js +59 -0
- package/dist/ui/style.css +33 -0
- package/dist/verification/Verifier.d.ts +72 -0
- package/dist/verification/Verifier.js +87 -0
- package/dist/verification/Verifier.js.map +1 -0
- package/dist/workflows/WorkflowEngine.d.ts +23 -0
- package/dist/workflows/WorkflowEngine.js +52 -0
- package/dist/workflows/WorkflowEngine.js.map +1 -0
- package/docs/ADOPTION.md +61 -0
- package/docs/ARCHITECTURE.md +65 -0
- package/docs/BENCHMARKS.md +64 -0
- package/docs/IMPLEMENTATION.md +93 -0
- package/docs/LOCAL_DATA.md +98 -0
- package/docs/LOCAL_VALIDATION.md +87 -0
- package/docs/MCP.md +81 -0
- package/docs/PROVIDERS.md +150 -0
- package/docs/RELEASE_CHECKLIST.md +37 -0
- package/docs/REQUIREMENTS.md +39 -0
- package/docs/SUPPORT_MATRIX.md +193 -0
- package/docs/THREAT_MODEL.md +108 -0
- package/docs/USEFUL_EXAMPLES.md +151 -0
- package/docs/brand-mark.svg +12 -0
- package/package.json +90 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="430" viewBox="0 0 1200 430" role="img" aria-labelledby="title description">
|
|
2
|
+
<title id="title">FreeComputerUse — a browser agent you can see and control</title>
|
|
3
|
+
<desc id="description">Choose a model to plan a browser task. Approve the website. Playwright acts locally, verifies the result and can replay a compatible workflow without model calls.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<pattern id="grid" width="38" height="38" patternUnits="userSpaceOnUse"><path d="M38 0H0V38" fill="none" stroke="#ffffff09"/></pattern>
|
|
6
|
+
<linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#18251f"/><stop offset="1" stop-color="#101a24"/></linearGradient>
|
|
7
|
+
<clipPath id="corners"><rect width="1200" height="430" rx="18"/></clipPath>
|
|
8
|
+
</defs>
|
|
9
|
+
<g clip-path="url(#corners)">
|
|
10
|
+
<rect width="1200" height="430" fill="url(#wash)"/>
|
|
11
|
+
<rect x="690" width="510" height="430" fill="url(#grid)"/>
|
|
12
|
+
<path d="M690 0V430" stroke="#ffffff16"/>
|
|
13
|
+
<g transform="translate(42 29) scale(.7)">
|
|
14
|
+
<rect x="1" y="1" width="62" height="62" rx="19" fill="#101712"/>
|
|
15
|
+
<rect x="9" y="10" width="46" height="44" rx="7" fill="#18231a" stroke="#eaf0e4" stroke-width="3"/>
|
|
16
|
+
<path d="M12 21h40" stroke="#c9ed72" stroke-width="7"/>
|
|
17
|
+
<circle cx="17" cy="16" r="1.6" fill="#101712"/><circle cx="22.5" cy="16" r="1.6" fill="#101712"/><circle cx="28" cy="16" r="1.6" fill="#101712"/>
|
|
18
|
+
<path d="M16 29h7M16 35h6" stroke="#9aaa91" stroke-linecap="round" stroke-width="2.5"/>
|
|
19
|
+
<path d="M25 25v25l7.4-7.1 6.2 12.2 5.4-2.8-6.2-12.2 11-.4z" fill="#c9ed72" stroke="#18231a" stroke-linejoin="round" stroke-width="2.4"/>
|
|
20
|
+
</g>
|
|
21
|
+
<text x="94" y="67" fill="#eff4eb" font-family="Manrope,Arial,sans-serif" font-size="23" font-weight="700" letter-spacing="-.6">FreeComputerUse</text>
|
|
22
|
+
<text x="48" y="132" fill="#aabbb0" font-family="monospace" font-size="11" letter-spacing="2.5">LOCAL-FIRST BROWSER AUTOMATION</text>
|
|
23
|
+
<text x="44" y="203" fill="#f5f5ec" font-family="Manrope,Arial,sans-serif" font-size="53" font-weight="700" letter-spacing="-2.3">Give the browser</text>
|
|
24
|
+
<text x="43" y="262" fill="#c8f169" font-family="Georgia,serif" font-size="54" font-style="italic" letter-spacing="-1.4">the whole task.</text>
|
|
25
|
+
<text x="48" y="314" fill="#c2cec4" font-family="Manrope,Arial,sans-serif" font-size="16">Choose your model. Approve the site. Watch each action.</text>
|
|
26
|
+
<g transform="translate(48 354)" font-family="monospace" font-size="10" letter-spacing=".7">
|
|
27
|
+
<rect width="142" height="32" rx="16" fill="#293d31" stroke="#718d52"/><text x="17" y="21" fill="#def1bc">PLAYWRIGHT · LOCAL</text>
|
|
28
|
+
<rect x="154" width="155" height="32" rx="16" fill="#202f3f" stroke="#476482"/><text x="171" y="21" fill="#c5dcf5">MODEL-AGNOSTIC</text>
|
|
29
|
+
<rect x="321" width="171" height="32" rx="16" fill="#293d31" stroke="#718d52"/><text x="338" y="21" fill="#def1bc">REPEAT · ZERO CALLS</text>
|
|
30
|
+
</g>
|
|
31
|
+
<g transform="translate(738 43)" font-family="Manrope,Arial,sans-serif">
|
|
32
|
+
<rect width="412" height="340" rx="11" fill="#f4f3e9" stroke="#ffffff33"/>
|
|
33
|
+
<path d="M0 43H412" stroke="#d9ded2"/>
|
|
34
|
+
<circle cx="20" cy="22" r="4" fill="#d76250"/><circle cx="35" cy="22" r="4" fill="#d9b857"/><circle cx="50" cy="22" r="4" fill="#75a66b"/>
|
|
35
|
+
<text x="70" y="26" fill="#5f7167" font-family="monospace" font-size="10">local session / invoices</text>
|
|
36
|
+
<rect x="269" y="12" width="126" height="21" rx="10" fill="#e2efcf"/><circle cx="281" cy="22" r="3" fill="#518751"/><text x="290" y="26" fill="#416b43" font-family="monospace" font-size="8">SITE APPROVED</text>
|
|
37
|
+
<text x="21" y="71" fill="#75857a" font-family="monospace" font-size="9" letter-spacing="1.4">TASK / OVERDUE INVOICE</text>
|
|
38
|
+
<text x="20" y="96" fill="#1c2b25" font-size="18" font-weight="700">Find and verify INV-2609-04</text>
|
|
39
|
+
<rect x="18" y="115" width="376" height="53" rx="6" fill="#fff" stroke="#dce1d7"/>
|
|
40
|
+
<circle cx="42" cy="141" r="12" fill="#253d33"/><text x="38.5" y="145" fill="#d8f28f" font-family="monospace" font-size="10">01</text>
|
|
41
|
+
<text x="65" y="137" fill="#213229" font-size="12" font-weight="700">Observe the page</text><text x="65" y="154" fill="#75857a" font-family="monospace" font-size="9">DOM + accessible controls</text>
|
|
42
|
+
<rect x="18" y="178" width="376" height="53" rx="6" fill="#fff" stroke="#dce1d7"/>
|
|
43
|
+
<circle cx="42" cy="204" r="12" fill="#253d33"/><text x="38.5" y="208" fill="#d8f28f" font-family="monospace" font-size="10">02</text>
|
|
44
|
+
<text x="65" y="200" fill="#213229" font-size="12" font-weight="700">Plan a safe batch</text><text x="65" y="217" fill="#75857a" font-family="monospace" font-size="9">OpenAI · Claude · Grok · more</text>
|
|
45
|
+
<rect x="18" y="241" width="376" height="53" rx="6" fill="#fff" stroke="#dce1d7"/>
|
|
46
|
+
<circle cx="42" cy="267" r="12" fill="#253d33"/><text x="38.5" y="271" fill="#d8f28f" font-family="monospace" font-size="10">03</text>
|
|
47
|
+
<text x="65" y="263" fill="#213229" font-size="12" font-weight="700">Execute and verify</text><text x="65" y="280" fill="#75857a" font-family="monospace" font-size="9">Visible actions · checked result</text>
|
|
48
|
+
<rect x="18" y="307" width="376" height="20" rx="4" fill="#dff0bb"/><text x="29" y="321" fill="#34553b" font-family="monospace" font-size="9">COMPATIBLE REPEAT</text><text x="366" y="323" text-anchor="end" fill="#34553b" font-size="17" font-weight="700">0 CALLS</text>
|
|
49
|
+
<path d="M401 267c22-3 22-41 1-44" fill="none" stroke="#c8f169" stroke-width="2"/><path d="M397 220l8 2-4 7" fill="none" stroke="#c8f169" stroke-width="2"/>
|
|
50
|
+
</g>
|
|
51
|
+
<path d="M1139 348c-2-.8-3.6.7-2.8 2.7l8.1 20.5c.9 2.2 4 2 4.7-.4l2.4-8.1 8.1-2.4c2.3-.7 2.5-3.8.3-4.7Z" fill="#3264f6" stroke="#fff" stroke-width="2.2" stroke-linejoin="round"/>
|
|
52
|
+
</g>
|
|
53
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task": "INC-204 synthetic incident investigation",
|
|
3
|
+
"recording": "one continuous, unsped dashboard run",
|
|
4
|
+
"start": "https://othmaneblial.github.io/FreeComputerUse/lab/workspace.html?view=incident",
|
|
5
|
+
"goal": "Investigate synthetic incident INC-204. Filter the alert queue to API Gateway, High severity, and Last 24 hours. Open the incident, inspect its timeline, request metrics, deployment comparison, and runbook. Use the evidence you find to complete the incident brief: affected endpoint, before/after 429 rates, relevant deployment, configuration change, and the appropriate review decision. Save the brief locally and download it. Do not roll back a service, contact customers, or claim the cause is proven.",
|
|
6
|
+
"status": "completed",
|
|
7
|
+
"provider": "deepseek-flash",
|
|
8
|
+
"metrics": {
|
|
9
|
+
"durationMs": 74591,
|
|
10
|
+
"browserActions": 21,
|
|
11
|
+
"failedActions": 1,
|
|
12
|
+
"llmCalls": 10,
|
|
13
|
+
"inputTokens": 30584,
|
|
14
|
+
"outputTokens": 1469,
|
|
15
|
+
"estimatedCostUSD": 0.006497424,
|
|
16
|
+
"actionsPerLLMCall": 2.1,
|
|
17
|
+
"tokensPerAction": 1526.3333333333333,
|
|
18
|
+
"repairs": 4,
|
|
19
|
+
"workflowCacheHits": 0,
|
|
20
|
+
"pageCacheEntries": 16,
|
|
21
|
+
"planBatches": 5,
|
|
22
|
+
"compressionReduction": 0.24349064279902355,
|
|
23
|
+
"selectorSuccessRate": 1,
|
|
24
|
+
"usageEstimated": false,
|
|
25
|
+
"provider": "deepseek-flash",
|
|
26
|
+
"mode": "normal",
|
|
27
|
+
"approvedSites": [
|
|
28
|
+
"https://othmaneblial.github.io"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"visibleCursorSamples": 241,
|
|
32
|
+
"approved": [
|
|
33
|
+
"Practice site access",
|
|
34
|
+
"Synthetic brief save"
|
|
35
|
+
],
|
|
36
|
+
"pages": [
|
|
37
|
+
"/FreeComputerUse/lab/workspace.html",
|
|
38
|
+
"/FreeComputerUse/lab/incident-detail.html",
|
|
39
|
+
"/FreeComputerUse/lab/incident-metrics.html",
|
|
40
|
+
"/FreeComputerUse/lab/incident-deployments.html",
|
|
41
|
+
"/FreeComputerUse/lab/incident-runbook.html",
|
|
42
|
+
"/FreeComputerUse/lab/incident-brief.html"
|
|
43
|
+
],
|
|
44
|
+
"downloadedBrief": "INC-204-incident-brief.txt",
|
|
45
|
+
"checks": [
|
|
46
|
+
"brief content",
|
|
47
|
+
"six distinct page visits",
|
|
48
|
+
"visible agent cursor",
|
|
49
|
+
"normal-mode site approval"
|
|
50
|
+
],
|
|
51
|
+
"disclaimer": "Synthetic practice workspace. No production rollback or customer message."
|
|
52
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActionSchema } from './schema.js';
|
|
2
|
+
export class ActionCompiler {
|
|
3
|
+
async normalize(action, locator) {
|
|
4
|
+
if (action.type === 'click' && locator) {
|
|
5
|
+
const download = await locator.evaluate(el => el.matches('a[download]') ? el.getAttribute('download') : null);
|
|
6
|
+
if (download !== null)
|
|
7
|
+
return ActionSchema.parse({ ...action, type: 'download', filename: download || undefined });
|
|
8
|
+
}
|
|
9
|
+
// The DOM, not a model's guessed control kind, determines the operation.
|
|
10
|
+
if (action.type === 'fill' && locator && await locator.evaluate(el => el.tagName === 'SELECT')) {
|
|
11
|
+
return ActionSchema.parse({ ...action, type: 'select' });
|
|
12
|
+
}
|
|
13
|
+
return action;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=compiler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../src/actions/compiler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AACvD,MAAM,OAAO,cAAc;IACzB,KAAK,CAAC,SAAS,CAAC,MAAa,EAAC,OAAgB;QAC5C,IAAG,MAAM,CAAC,IAAI,KAAG,OAAO,IAAE,OAAO,EAAC,CAAC;YACjC,MAAM,QAAQ,GAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAA,EAAE,CAAA,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA,CAAC,CAAA,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAA,CAAC,CAAA,IAAI,CAAC,CAAC;YACtG,IAAG,QAAQ,KAAG,IAAI;gBAAC,OAAO,YAAY,CAAC,KAAK,CAAC,EAAC,GAAG,MAAM,EAAC,IAAI,EAAC,UAAU,EAAC,QAAQ,EAAC,QAAQ,IAAE,SAAS,EAAC,CAAC,CAAC;QACzG,CAAC;QACD,yEAAyE;QACzE,IAAG,MAAM,CAAC,IAAI,KAAG,MAAM,IAAE,OAAO,IAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAA,EAAE,CAAA,EAAE,CAAC,OAAO,KAAG,QAAQ,CAAC,EAAC,CAAC;YACnF,OAAO,YAAY,CAAC,KAAK,CAAC,EAAC,GAAG,MAAM,EAAC,IAAI,EAAC,QAAQ,EAAC,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Action, type Condition } from './schema.js';
|
|
2
|
+
import { type ConfirmationPolicy } from './policy.js';
|
|
3
|
+
import type { Browser } from '../browser/Browser.js';
|
|
4
|
+
import type { Observer } from '../browser/Observer.js';
|
|
5
|
+
import type { VariableResolver } from '../profile/VariableResolver.js';
|
|
6
|
+
import type { Control } from '../agent/Control.js';
|
|
7
|
+
import { Verifier } from '../verification/Verifier.js';
|
|
8
|
+
import { ActionCompiler } from './compiler.js';
|
|
9
|
+
export interface ActionResult {
|
|
10
|
+
action: Action;
|
|
11
|
+
startedAt: number;
|
|
12
|
+
durationMs: number;
|
|
13
|
+
success: boolean;
|
|
14
|
+
strategy?: string;
|
|
15
|
+
data?: unknown;
|
|
16
|
+
error?: string;
|
|
17
|
+
uncertain?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare class Executor {
|
|
20
|
+
readonly browser: Browser;
|
|
21
|
+
readonly observer: Observer;
|
|
22
|
+
readonly variables: VariableResolver;
|
|
23
|
+
readonly control: Control;
|
|
24
|
+
readonly options: {
|
|
25
|
+
confirmation?: ConfirmationPolicy;
|
|
26
|
+
downloadDir?: string;
|
|
27
|
+
};
|
|
28
|
+
readonly compiler: ActionCompiler;
|
|
29
|
+
readonly verifier: Verifier;
|
|
30
|
+
constructor(browser: Browser, observer: Observer, variables: VariableResolver, control: Control, options?: {
|
|
31
|
+
confirmation?: ConfirmationPolicy;
|
|
32
|
+
downloadDir?: string;
|
|
33
|
+
});
|
|
34
|
+
semantic(action: Action): Action;
|
|
35
|
+
semanticCondition(condition: Condition): Condition;
|
|
36
|
+
private storeDownload;
|
|
37
|
+
run(input: Action): Promise<ActionResult>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { chmod, lstat, mkdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { basename, resolve } from 'node:path';
|
|
4
|
+
import { ActionSchema } from './schema.js';
|
|
5
|
+
import { sensitiveReason } from './policy.js';
|
|
6
|
+
import { Verifier } from '../verification/Verifier.js';
|
|
7
|
+
import { ActionCompiler } from './compiler.js';
|
|
8
|
+
export class Executor {
|
|
9
|
+
browser;
|
|
10
|
+
observer;
|
|
11
|
+
variables;
|
|
12
|
+
control;
|
|
13
|
+
options;
|
|
14
|
+
compiler = new ActionCompiler();
|
|
15
|
+
verifier;
|
|
16
|
+
constructor(browser, observer, variables, control, options = {}) {
|
|
17
|
+
this.browser = browser;
|
|
18
|
+
this.observer = observer;
|
|
19
|
+
this.variables = variables;
|
|
20
|
+
this.control = control;
|
|
21
|
+
this.options = options;
|
|
22
|
+
this.verifier = new Verifier(browser, observer, variables);
|
|
23
|
+
}
|
|
24
|
+
semantic(action) {
|
|
25
|
+
const result = { ...action };
|
|
26
|
+
if ('target' in result && typeof result.target === 'string')
|
|
27
|
+
result.target = this.observer.selectors.descriptor(result.target);
|
|
28
|
+
if (result.verify)
|
|
29
|
+
result.verify = result.verify.map(c => this.semanticCondition(c));
|
|
30
|
+
if (result.type === 'wait')
|
|
31
|
+
result.condition = this.semanticCondition(result.condition);
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
semanticCondition(condition) {
|
|
35
|
+
return 'target' in condition && typeof condition.target === 'string' ? { ...condition, target: this.observer.selectors.descriptor(condition.target) } : condition;
|
|
36
|
+
}
|
|
37
|
+
async storeDownload(download, requestedName) {
|
|
38
|
+
const folder = resolve(this.options.downloadDir ?? '.fcu/downloads');
|
|
39
|
+
await mkdir(folder, { recursive: true, mode: 0o700 });
|
|
40
|
+
const info = await lstat(folder);
|
|
41
|
+
if (!info.isDirectory() || info.isSymbolicLink())
|
|
42
|
+
throw new Error('Download folder must be a real local directory');
|
|
43
|
+
await chmod(folder, 0o700);
|
|
44
|
+
const filename = [...basename((requestedName ?? download.suggestedFilename()).replaceAll('\\', '/')).normalize('NFC')
|
|
45
|
+
.replace(/[\u0000-\u001f\u007f<>:"|?*]/g, '_').replace(/[. ]+$/g, '')].slice(0, 180).join('');
|
|
46
|
+
if (!filename || filename === '.' || filename === '..')
|
|
47
|
+
throw new Error('Invalid download filename');
|
|
48
|
+
const path = resolve(folder, `${randomUUID()}-${filename}`);
|
|
49
|
+
await download.saveAs(path);
|
|
50
|
+
if (await download.failure())
|
|
51
|
+
throw new Error('Browser download failed');
|
|
52
|
+
await chmod(path, 0o600);
|
|
53
|
+
this.browser.downloads.push({ path, filename });
|
|
54
|
+
return { path, filename };
|
|
55
|
+
}
|
|
56
|
+
async run(input) {
|
|
57
|
+
let action = ActionSchema.parse(input);
|
|
58
|
+
const startedAt = Date.now();
|
|
59
|
+
let strategy, executed = false, receiptAction = action;
|
|
60
|
+
let assertApproved, disposeApproved;
|
|
61
|
+
try {
|
|
62
|
+
await this.control.checkpoint();
|
|
63
|
+
let locator;
|
|
64
|
+
if ('target' in action && action.target) {
|
|
65
|
+
const selected = await this.observer.selectors.resolve(this.browser.page, action.target, action.timeoutMs, action.type === 'extract');
|
|
66
|
+
locator = selected.locator;
|
|
67
|
+
strategy = selected.strategy;
|
|
68
|
+
}
|
|
69
|
+
action = await this.compiler.normalize(action, locator);
|
|
70
|
+
receiptAction = this.semantic(action);
|
|
71
|
+
const reason = await sensitiveReason(action, locator);
|
|
72
|
+
const policy = this.options.confirmation ?? 'sensitive';
|
|
73
|
+
if (policy === 'always' || policy === 'sensitive' && reason) {
|
|
74
|
+
const approvedPage = this.browser.page, approvedURL = approvedPage.url();
|
|
75
|
+
const approvedElement = await locator?.elementHandle();
|
|
76
|
+
disposeApproved = async () => { await approvedElement?.dispose(); };
|
|
77
|
+
const fingerprint = locator ? await locator.evaluate(el => JSON.stringify({ tag: el.tagName, text: el.textContent, aria: el.getAttribute('aria-label'), type: el.getAttribute('type'), href: el.getAttribute('href'), form: el.form?.action, method: el.form?.method })) : undefined;
|
|
78
|
+
assertApproved = async () => {
|
|
79
|
+
if (this.browser.page !== approvedPage || approvedPage.url() !== approvedURL)
|
|
80
|
+
throw new Error('Browser page changed while awaiting approval; review a new plan');
|
|
81
|
+
if (locator && approvedElement) {
|
|
82
|
+
const same = await locator.evaluate((el, approved) => el === approved && el.isConnected, approvedElement);
|
|
83
|
+
const current = await locator.evaluate(el => JSON.stringify({ tag: el.tagName, text: el.textContent, aria: el.getAttribute('aria-label'), type: el.getAttribute('type'), href: el.getAttribute('href'), form: el.form?.action, method: el.form?.method }));
|
|
84
|
+
if (!same || current !== fingerprint)
|
|
85
|
+
throw new Error('Approved target changed while awaiting approval; review a new action');
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
await this.control.confirm(reason ?? 'All-actions confirmation policy', this.variables.redact(JSON.stringify(receiptAction)));
|
|
89
|
+
await assertApproved();
|
|
90
|
+
}
|
|
91
|
+
await this.control.checkpoint();
|
|
92
|
+
const page = this.browser.page;
|
|
93
|
+
const timeout = action.timeoutMs ?? 4000;
|
|
94
|
+
let data;
|
|
95
|
+
const interaction = this.browser.interaction, interactionOptions = { timeout, checkpoint: () => this.control.checkpoint(), beforeEffect: assertApproved };
|
|
96
|
+
const value = 'value' in action ? this.variables.resolve(action.value) : '';
|
|
97
|
+
if (locator && ['click', 'press', 'submit'].includes(action.type)) {
|
|
98
|
+
const info = await locator.evaluate((el, type) => {
|
|
99
|
+
const f = el instanceof HTMLFormElement ? el : el.form;
|
|
100
|
+
const submitting = type === 'submit' || type === 'press' || el.matches('button:not([type=button]):not([type=reset]),input[type=submit]');
|
|
101
|
+
return f && submitting && f.checkValidity() ? { actionURL: f.action, method: f.method.toUpperCase(), id: f.id, label: f.getAttribute('aria-label') ?? '', unique: document.forms.length === 1 } : null;
|
|
102
|
+
}, action.type);
|
|
103
|
+
if (info && ('target' in receiptAction) && receiptAction.target)
|
|
104
|
+
this.browser.formReceipts.push({ ...info, target: this.observer.selectors.descriptor(receiptAction.target), time: Date.now() });
|
|
105
|
+
}
|
|
106
|
+
executed = true;
|
|
107
|
+
switch (action.type) {
|
|
108
|
+
case 'navigate':
|
|
109
|
+
if (action.url.includes('{{profile.') || action.url.includes('{{files.'))
|
|
110
|
+
throw new Error('Local vault values cannot be embedded in navigation URLs');
|
|
111
|
+
await this.browser.navigate(action.url);
|
|
112
|
+
break;
|
|
113
|
+
case 'openTab':
|
|
114
|
+
if (action.url.includes('{{profile.') || action.url.includes('{{files.'))
|
|
115
|
+
throw new Error('Local vault values cannot be embedded in navigation URLs');
|
|
116
|
+
await this.browser.openTab(action.url);
|
|
117
|
+
break;
|
|
118
|
+
case 'closeTab':
|
|
119
|
+
await this.browser.closeTab();
|
|
120
|
+
break;
|
|
121
|
+
case 'switchTab':
|
|
122
|
+
await this.browser.switchTab(action.index, timeout);
|
|
123
|
+
break;
|
|
124
|
+
case 'back':
|
|
125
|
+
await page.goBack({ waitUntil: 'domcontentloaded' });
|
|
126
|
+
break;
|
|
127
|
+
case 'forward':
|
|
128
|
+
await page.goForward({ waitUntil: 'domcontentloaded' });
|
|
129
|
+
break;
|
|
130
|
+
case 'reload':
|
|
131
|
+
await page.reload({ waitUntil: 'domcontentloaded' });
|
|
132
|
+
break;
|
|
133
|
+
case 'click': {
|
|
134
|
+
const downloadIntent = await locator.evaluate(el => /^\s*(download|export)\b/i.test(el.getAttribute('aria-label') || el.textContent || el.getAttribute('value') || ''));
|
|
135
|
+
const downloadEvent = downloadIntent ? page.waitForEvent('download', { timeout }).catch(() => undefined) : Promise.resolve(undefined);
|
|
136
|
+
await interaction.perform(locator, 'click', async () => {
|
|
137
|
+
const newTab = await locator.evaluate(el => el.matches('a[target="_blank"]'));
|
|
138
|
+
if (newTab) {
|
|
139
|
+
const both = await Promise.allSettled([page.waitForEvent('popup', { timeout }), locator.click({ timeout })]);
|
|
140
|
+
if (both[0].status === 'rejected')
|
|
141
|
+
throw both[0].reason;
|
|
142
|
+
if (both[1].status === 'rejected')
|
|
143
|
+
throw both[1].reason;
|
|
144
|
+
await both[0].value.waitForLoadState('domcontentloaded', { timeout });
|
|
145
|
+
this.browser.page = both[0].value;
|
|
146
|
+
}
|
|
147
|
+
else
|
|
148
|
+
await locator.click({ timeout });
|
|
149
|
+
}, interactionOptions);
|
|
150
|
+
const observed = await downloadEvent;
|
|
151
|
+
if (observed) {
|
|
152
|
+
data = await this.storeDownload(observed);
|
|
153
|
+
receiptAction = this.semantic(ActionSchema.parse({ ...action, type: 'download' }));
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
case 'doubleClick':
|
|
158
|
+
await interaction.perform(locator, 'doubleClick', () => locator.dblclick({ timeout }), interactionOptions);
|
|
159
|
+
break;
|
|
160
|
+
case 'hover':
|
|
161
|
+
await interaction.perform(locator, 'hover', () => locator.hover({ timeout }), interactionOptions);
|
|
162
|
+
break;
|
|
163
|
+
case 'fill':
|
|
164
|
+
await interaction.enter(locator, value, true, interactionOptions);
|
|
165
|
+
if (await locator.inputValue() !== value)
|
|
166
|
+
throw new Error('Fill postcondition failed');
|
|
167
|
+
break;
|
|
168
|
+
case 'type':
|
|
169
|
+
await interaction.enter(locator, value, false, interactionOptions);
|
|
170
|
+
break;
|
|
171
|
+
case 'select': {
|
|
172
|
+
const options = await locator.evaluate(el => [...el.options].map(o => ({ label: o.label, value: o.value })));
|
|
173
|
+
const option = options.find(o => o.value === value) || options.find(o => o.label === value);
|
|
174
|
+
if (!option)
|
|
175
|
+
throw new Error('Select option not found');
|
|
176
|
+
await interaction.perform(locator, 'select', () => locator.selectOption(option.value, { timeout }), interactionOptions);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
case 'check':
|
|
180
|
+
await interaction.perform(locator, 'check', () => locator.check({ timeout }), interactionOptions);
|
|
181
|
+
if (!await locator.isChecked())
|
|
182
|
+
throw new Error('Check postcondition failed');
|
|
183
|
+
break;
|
|
184
|
+
case 'uncheck':
|
|
185
|
+
await interaction.perform(locator, 'uncheck', () => locator.uncheck({ timeout }), interactionOptions);
|
|
186
|
+
if (await locator.isChecked())
|
|
187
|
+
throw new Error('Uncheck postcondition failed');
|
|
188
|
+
break;
|
|
189
|
+
case 'press':
|
|
190
|
+
await interaction.perform(locator, 'press', () => locator.press(value, { timeout }), interactionOptions);
|
|
191
|
+
break;
|
|
192
|
+
case 'scroll':
|
|
193
|
+
if (locator)
|
|
194
|
+
await interaction.perform(locator, 'scroll', () => locator.scrollIntoViewIfNeeded({ timeout }), interactionOptions);
|
|
195
|
+
else
|
|
196
|
+
await interaction.scroll(action.pixels * (action.direction === 'up' ? -1 : 1), interactionOptions);
|
|
197
|
+
break;
|
|
198
|
+
case 'upload': {
|
|
199
|
+
const file = this.variables.file(action.file);
|
|
200
|
+
if (!(await stat(file)).isFile())
|
|
201
|
+
throw new Error('Upload alias is not a regular file');
|
|
202
|
+
// Hidden native file controls have no pointer target.
|
|
203
|
+
if (await locator.isVisible())
|
|
204
|
+
await interaction.perform(locator, 'upload', () => locator.setInputFiles(file, { timeout }), interactionOptions);
|
|
205
|
+
else {
|
|
206
|
+
await assertApproved?.();
|
|
207
|
+
await locator.setInputFiles(file, { timeout });
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case 'download': {
|
|
212
|
+
const both = await interaction.perform(locator, 'click', () => Promise.allSettled([page.waitForEvent('download', { timeout }), locator.click({ timeout })]), interactionOptions);
|
|
213
|
+
if (both[0].status === 'rejected')
|
|
214
|
+
throw both[0].reason;
|
|
215
|
+
if (both[1].status === 'rejected')
|
|
216
|
+
throw both[1].reason;
|
|
217
|
+
data = await this.storeDownload(both[0].value, action.filename);
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
case 'wait': {
|
|
221
|
+
const verification = await this.verifier.check([action.condition], timeout, startedAt);
|
|
222
|
+
if (!verification.success)
|
|
223
|
+
throw new Error('Wait condition failed');
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case 'submit': {
|
|
227
|
+
const form = locator;
|
|
228
|
+
await interaction.perform(form, 'submit', () => form.evaluate(el => {
|
|
229
|
+
const f = el instanceof HTMLFormElement ? el : el.form;
|
|
230
|
+
if (!f)
|
|
231
|
+
throw new Error('Target is not a form or form control');
|
|
232
|
+
if (!f.reportValidity())
|
|
233
|
+
throw new Error('Form validation failed');
|
|
234
|
+
f.addEventListener('submit', () => f.setAttribute('data-fcu-submitted', 'true'), { once: true });
|
|
235
|
+
f.requestSubmit();
|
|
236
|
+
}), interactionOptions);
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
case 'extract': {
|
|
240
|
+
interaction.cue('extract');
|
|
241
|
+
const root = locator ?? page.locator('body');
|
|
242
|
+
if (action.format === 'table')
|
|
243
|
+
data = await root.locator('tr').filter({ visible: true }).evaluateAll(rows => rows.map(row => [...row.querySelectorAll('th,td')].map(cell => cell.textContent?.trim() ?? '')));
|
|
244
|
+
else if (action.format === 'links')
|
|
245
|
+
data = await root.evaluateAll(els => els.flatMap(el => [...(el.matches('a[href]') ? [el] : el.querySelectorAll('a[href]'))].filter(a => a.getClientRects().length > 0).map(a => ({ text: a.textContent?.trim(), url: a.href }))));
|
|
246
|
+
else if (action.format === 'records') {
|
|
247
|
+
if (!action.fields || !Object.keys(action.fields).length || Object.keys(action.fields).length > 20)
|
|
248
|
+
throw new Error('Records extraction requires 1..20 controlled CSS fields');
|
|
249
|
+
data = await root.filter({ visible: true }).evaluateAll((els, fields) => els.flatMap(el => el.matches('table,tbody') ? [...el.querySelectorAll('tr')].filter(row => row.querySelector('td')) : [el]).map(el => Object.fromEntries(Object.entries(fields).map(([key, field]) => {
|
|
250
|
+
let node = el.querySelector(field.css);
|
|
251
|
+
let value = '';
|
|
252
|
+
// Resolve tabular fields from actual headers, rather than guessed cell indices.
|
|
253
|
+
if (el.tagName === 'TR' && field.attribute === 'text') {
|
|
254
|
+
const headers = [...(el.closest('table')?.querySelectorAll('thead th,tr:first-child th') ?? [])];
|
|
255
|
+
const [normalize] = [(s) => (s ?? '').toLowerCase().replace(/[^a-z]/g, '')];
|
|
256
|
+
const wanted = normalize(key), indices = headers.map((h, i) => normalize(h.textContent ?? '') === wanted ? i : -1).filter(i => i >= 0);
|
|
257
|
+
const cells = [...el.querySelectorAll(':scope > td')];
|
|
258
|
+
if (indices.length === 1 && cells.length >= headers.length)
|
|
259
|
+
node = cells[indices[0] + cells.length - headers.length] ?? node;
|
|
260
|
+
}
|
|
261
|
+
if (node) {
|
|
262
|
+
if (field.attribute === 'text')
|
|
263
|
+
value = node.innerText?.trim() ?? node.textContent?.trim() ?? '';
|
|
264
|
+
else if (field.attribute === 'href')
|
|
265
|
+
value = node.href ?? '';
|
|
266
|
+
else if (field.attribute === 'src')
|
|
267
|
+
value = node.src ?? '';
|
|
268
|
+
else if (field.attribute === 'value')
|
|
269
|
+
value = node.matches('input[type=password],input[autocomplete=cc-number],input[autocomplete=cc-csc]') ? '[sensitive value omitted]' : node.value ?? '';
|
|
270
|
+
else
|
|
271
|
+
value = node.getAttribute(field.attribute) ?? '';
|
|
272
|
+
}
|
|
273
|
+
return [key, value];
|
|
274
|
+
}))), action.fields);
|
|
275
|
+
}
|
|
276
|
+
else
|
|
277
|
+
data = (await root.filter({ visible: true }).allInnerTexts()).join('\n').slice(0, 100000);
|
|
278
|
+
if (Array.isArray(data)) {
|
|
279
|
+
const match = action.match;
|
|
280
|
+
if (match)
|
|
281
|
+
data = data.filter(item => JSON.stringify(item).toLowerCase().includes(match.toLowerCase()));
|
|
282
|
+
if (action.limit)
|
|
283
|
+
data = data.slice(0, action.limit);
|
|
284
|
+
}
|
|
285
|
+
this.browser.extractions.push({ key: action.key, value: data });
|
|
286
|
+
data = { [action.key]: data };
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (action.verify?.length) {
|
|
291
|
+
const checked = await this.verifier.check(action.verify, timeout, startedAt);
|
|
292
|
+
if (!checked.success)
|
|
293
|
+
throw new Error(`Action verification failed: ${JSON.stringify(checked.failed)}`);
|
|
294
|
+
}
|
|
295
|
+
return { action: receiptAction, startedAt, durationMs: Date.now() - startedAt, success: true, strategy, data };
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
return { action: receiptAction, startedAt, durationMs: Date.now() - startedAt, success: false, strategy, error: this.variables.redact(error instanceof Error ? error.message : 'Browser action failed'), uncertain: executed && ['click', 'doubleClick', 'press', 'submit', 'download'].includes(action.type) };
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
await disposeApproved?.();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/actions/executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export async function sensitiveReason(action, locator) {
|
|
2
|
+
if (action.sensitive || action.type === 'submit')
|
|
3
|
+
return 'Explicit submission or sensitive action';
|
|
4
|
+
if (!['click', 'doubleClick', 'press', 'check', 'uncheck'].includes(action.type) || !locator)
|
|
5
|
+
return;
|
|
6
|
+
const facts = await locator.evaluate(el => ({
|
|
7
|
+
text: (el.getAttribute('aria-label') || el.textContent || el.getAttribute('value') || '').trim(),
|
|
8
|
+
type: el.getAttribute('type'), tag: el.tagName.toLowerCase(),
|
|
9
|
+
submit: !!el.form && (el.tagName === 'BUTTON' ? el.getAttribute('type') !== 'button' : el.getAttribute('type') === 'submit'),
|
|
10
|
+
}));
|
|
11
|
+
if (/\b(submit|purchase|buy|pay|checkout|delete|remove|send|confirm|transfer|publish|unsubscribe|register|create account|accept terms)\b/i.test(facts.text))
|
|
12
|
+
return `Potentially irreversible control: ${facts.text.slice(0, 80)}`;
|
|
13
|
+
if (action.type === 'press' && action.value === 'Enter' && facts.type !== 'search')
|
|
14
|
+
return 'Enter may submit a form';
|
|
15
|
+
if (facts.submit && !/\b(search|find flights|find tickets|filter|continue|next|review|apply|sign in|log in)\b/i.test(facts.text))
|
|
16
|
+
return 'Form submission';
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/actions/policy.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAa,EAAC,OAAgB;IAClE,IAAG,MAAM,CAAC,SAAS,IAAE,MAAM,CAAC,IAAI,KAAG,QAAQ;QAAC,OAAO,yCAAyC,CAAC;IAC7F,IAAG,CAAC,CAAC,OAAO,EAAC,aAAa,EAAC,OAAO,EAAC,OAAO,EAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAE,CAAC,OAAO;QAAC,OAAO;IAC7F,MAAM,KAAK,GAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAA,EAAE,CAAA,CAAC;QACtC,IAAI,EAAC,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,IAAE,EAAE,CAAC,WAAW,IAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAE,EAAE,CAAC,CAAC,IAAI,EAAE;QACzF,IAAI,EAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,EAAC,GAAG,EAAC,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE;QACzD,MAAM,EAAC,CAAC,CAAE,EAAuB,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,KAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,KAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,KAAG,QAAQ,CAAC;KAC5I,CAAC,CAAC,CAAC;IACJ,IAAG,sIAAsI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAC,OAAO,qCAAqC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAC,EAAE,CAAC,EAAE,CAAC;IAChO,IAAG,MAAM,CAAC,IAAI,KAAG,OAAO,IAAE,MAAM,CAAC,KAAK,KAAG,OAAO,IAAE,KAAK,CAAC,IAAI,KAAG,QAAQ;QAAC,OAAO,yBAAyB,CAAC;IACzG,IAAG,KAAK,CAAC,MAAM,IAAE,CAAC,0FAA0F,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAC,OAAO,iBAAiB,CAAC;AACzJ,CAAC"}
|