@monoes/monomindcli 2.13.0 → 2.14.1
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/.claude/skills/monodesign/scripts/context.mjs +1 -1
- package/.claude/skills/monodesign/scripts/detector/detect-antipatterns-browser.js +2 -2
- package/.claude/skills/monodesign/scripts/detector/rules/checks.mjs +2 -2
- package/.claude/skills/monodesign/scripts/live-browser.js +1 -1
- package/bin/cli.js +21 -1
- package/bin/mcp-server.js +21 -1
- package/dist/src/capabilities/cap-documents.d.ts.map +1 -1
- package/dist/src/capabilities/cap-documents.js +23 -0
- package/dist/src/capabilities/cap-documents.js.map +1 -1
- package/dist/src/commands/doc-filters.d.ts +19 -0
- package/dist/src/commands/doc-filters.d.ts.map +1 -0
- package/dist/src/commands/doc-filters.js +58 -0
- package/dist/src/commands/doc-filters.js.map +1 -0
- package/dist/src/commands/doc-library.d.ts +21 -0
- package/dist/src/commands/doc-library.d.ts.map +1 -0
- package/dist/src/commands/doc-library.js +390 -0
- package/dist/src/commands/doc-library.js.map +1 -0
- package/dist/src/commands/doc-list.d.ts +9 -0
- package/dist/src/commands/doc-list.d.ts.map +1 -0
- package/dist/src/commands/doc-list.js +99 -0
- package/dist/src/commands/doc-list.js.map +1 -0
- package/dist/src/commands/doc.d.ts.map +1 -1
- package/dist/src/commands/doc.js +49 -37
- package/dist/src/commands/doc.js.map +1 -1
- package/dist/src/commands/doctor-mcp-probe.d.ts +48 -0
- package/dist/src/commands/doctor-mcp-probe.d.ts.map +1 -0
- package/dist/src/commands/doctor-mcp-probe.js +205 -0
- package/dist/src/commands/doctor-mcp-probe.js.map +1 -0
- package/dist/src/commands/doctor-project-checks.d.ts +12 -1
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +55 -4
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +10 -1
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +17 -0
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/mcp.d.ts.map +1 -1
- package/dist/src/commands/mcp.js +4 -3
- package/dist/src/commands/mcp.js.map +1 -1
- package/dist/src/commands/org-observe.d.ts.map +1 -1
- package/dist/src/commands/org-observe.js +14 -0
- package/dist/src/commands/org-observe.js.map +1 -1
- package/dist/src/commands/org.d.ts.map +1 -1
- package/dist/src/commands/org.js +12 -2
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/init/claudemd-generator.d.ts.map +1 -1
- package/dist/src/init/claudemd-generator.js +2 -1
- package/dist/src/init/claudemd-generator.js.map +1 -1
- package/dist/src/init/mcp-generator.d.ts.map +1 -1
- package/dist/src/init/mcp-generator.js +3 -3
- package/dist/src/init/mcp-generator.js.map +1 -1
- package/dist/src/init/types.d.ts +6 -0
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/init/types.js.map +1 -1
- package/dist/src/init/write-runtime-config.d.ts.map +1 -1
- package/dist/src/init/write-runtime-config.js +12 -2
- package/dist/src/init/write-runtime-config.js.map +1 -1
- package/dist/src/knowledge/capture-envelope.d.ts +80 -0
- package/dist/src/knowledge/capture-envelope.d.ts.map +1 -0
- package/dist/src/knowledge/capture-envelope.js +165 -0
- package/dist/src/knowledge/capture-envelope.js.map +1 -0
- package/dist/src/knowledge/capture-text.d.ts +23 -0
- package/dist/src/knowledge/capture-text.d.ts.map +1 -0
- package/dist/src/knowledge/capture-text.js +49 -0
- package/dist/src/knowledge/capture-text.js.map +1 -0
- package/dist/src/knowledge/citation.d.ts +121 -0
- package/dist/src/knowledge/citation.d.ts.map +1 -0
- package/dist/src/knowledge/citation.js +252 -0
- package/dist/src/knowledge/citation.js.map +1 -0
- package/dist/src/knowledge/document-pipeline.d.ts +60 -0
- package/dist/src/knowledge/document-pipeline.d.ts.map +1 -1
- package/dist/src/knowledge/document-pipeline.js +188 -4
- package/dist/src/knowledge/document-pipeline.js.map +1 -1
- package/dist/src/knowledge/html-extract.d.ts +43 -0
- package/dist/src/knowledge/html-extract.d.ts.map +1 -0
- package/dist/src/knowledge/html-extract.js +286 -0
- package/dist/src/knowledge/html-extract.js.map +1 -0
- package/dist/src/knowledge/html-tags.d.ts +30 -0
- package/dist/src/knowledge/html-tags.d.ts.map +1 -0
- package/dist/src/knowledge/html-tags.js +229 -0
- package/dist/src/knowledge/html-tags.js.map +1 -0
- package/dist/src/knowledge/library.d.ts +106 -0
- package/dist/src/knowledge/library.d.ts.map +1 -0
- package/dist/src/knowledge/library.js +195 -0
- package/dist/src/knowledge/library.js.map +1 -0
- package/dist/src/knowledge/mhtml.d.ts +60 -0
- package/dist/src/knowledge/mhtml.d.ts.map +1 -0
- package/dist/src/knowledge/mhtml.js +154 -0
- package/dist/src/knowledge/mhtml.js.map +1 -0
- package/dist/src/knowledge/related.d.ts +58 -0
- package/dist/src/knowledge/related.d.ts.map +1 -0
- package/dist/src/knowledge/related.js +222 -0
- package/dist/src/knowledge/related.js.map +1 -0
- package/dist/src/knowledge/section-diff.d.ts +48 -0
- package/dist/src/knowledge/section-diff.d.ts.map +1 -0
- package/dist/src/knowledge/section-diff.js +151 -0
- package/dist/src/knowledge/section-diff.js.map +1 -0
- package/dist/src/knowledge/watch.d.ts +92 -0
- package/dist/src/knowledge/watch.d.ts.map +1 -0
- package/dist/src/knowledge/watch.js +221 -0
- package/dist/src/knowledge/watch.js.map +1 -0
- package/dist/src/mcp-server.d.ts.map +1 -1
- package/dist/src/mcp-server.js +44 -87
- package/dist/src/mcp-server.js.map +1 -1
- package/dist/src/mcp-tools/browser-instrument-tools.d.ts +35 -0
- package/dist/src/mcp-tools/browser-instrument-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/browser-instrument-tools.js +359 -0
- package/dist/src/mcp-tools/browser-instrument-tools.js.map +1 -0
- package/dist/src/mcp-tools/browser-metrics.d.ts +42 -0
- package/dist/src/mcp-tools/browser-metrics.d.ts.map +1 -0
- package/dist/src/mcp-tools/browser-metrics.js +91 -0
- package/dist/src/mcp-tools/browser-metrics.js.map +1 -0
- package/dist/src/mcp-tools/browser-profile-tools.d.ts +16 -0
- package/dist/src/mcp-tools/browser-profile-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/browser-profile-tools.js +324 -0
- package/dist/src/mcp-tools/browser-profile-tools.js.map +1 -0
- package/dist/src/mcp-tools/browser-session.d.ts +68 -0
- package/dist/src/mcp-tools/browser-session.d.ts.map +1 -0
- package/dist/src/mcp-tools/browser-session.js +224 -0
- package/dist/src/mcp-tools/browser-session.js.map +1 -0
- package/dist/src/mcp-tools/browser-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/browser-tools.js +10 -176
- package/dist/src/mcp-tools/browser-tools.js.map +1 -1
- package/dist/src/mcp-tools/capture-resource-read.d.ts +115 -0
- package/dist/src/mcp-tools/capture-resource-read.d.ts.map +1 -0
- package/dist/src/mcp-tools/capture-resource-read.js +296 -0
- package/dist/src/mcp-tools/capture-resource-read.js.map +1 -0
- package/dist/src/mcp-tools/capture-resource-tools.d.ts +22 -0
- package/dist/src/mcp-tools/capture-resource-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/capture-resource-tools.js +182 -0
- package/dist/src/mcp-tools/capture-resource-tools.js.map +1 -0
- package/dist/src/mcp-tools/capture-resources.d.ts +142 -0
- package/dist/src/mcp-tools/capture-resources.d.ts.map +1 -0
- package/dist/src/mcp-tools/capture-resources.js +289 -0
- package/dist/src/mcp-tools/capture-resources.js.map +1 -0
- package/dist/src/mcp-tools/index.d.ts +3 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +6 -0
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/knowledge-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/knowledge-tools.js +9 -1
- package/dist/src/mcp-tools/knowledge-tools.js.map +1 -1
- package/dist/src/mcp-tools/resource-router.d.ts +86 -0
- package/dist/src/mcp-tools/resource-router.d.ts.map +1 -0
- package/dist/src/mcp-tools/resource-router.js +181 -0
- package/dist/src/mcp-tools/resource-router.js.map +1 -0
- package/dist/src/memory/memory-bridge.js +1 -1
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/orgrt/agent-runner.d.ts +38 -0
- package/dist/src/orgrt/agent-runner.d.ts.map +1 -1
- package/dist/src/orgrt/agent-runner.js +47 -0
- package/dist/src/orgrt/agent-runner.js.map +1 -1
- package/dist/src/orgrt/checkpoint.d.ts +12 -1
- package/dist/src/orgrt/checkpoint.d.ts.map +1 -1
- package/dist/src/orgrt/checkpoint.js +15 -6
- package/dist/src/orgrt/checkpoint.js.map +1 -1
- package/dist/src/orgrt/completion-gate.d.ts +74 -0
- package/dist/src/orgrt/completion-gate.d.ts.map +1 -1
- package/dist/src/orgrt/completion-gate.js +45 -0
- package/dist/src/orgrt/completion-gate.js.map +1 -1
- package/dist/src/orgrt/cost-tier.d.ts +144 -0
- package/dist/src/orgrt/cost-tier.d.ts.map +1 -0
- package/dist/src/orgrt/cost-tier.js +183 -0
- package/dist/src/orgrt/cost-tier.js.map +1 -0
- package/dist/src/orgrt/cross-org.d.ts.map +1 -1
- package/dist/src/orgrt/cross-org.js +14 -0
- package/dist/src/orgrt/cross-org.js.map +1 -1
- package/dist/src/orgrt/daemon.d.ts +12 -1
- package/dist/src/orgrt/daemon.d.ts.map +1 -1
- package/dist/src/orgrt/daemon.js +216 -49
- package/dist/src/orgrt/daemon.js.map +1 -1
- package/dist/src/orgrt/decisions.d.ts +14 -2
- package/dist/src/orgrt/decisions.d.ts.map +1 -1
- package/dist/src/orgrt/decisions.js +216 -10
- package/dist/src/orgrt/decisions.js.map +1 -1
- package/dist/src/orgrt/file-roots.d.ts +7 -0
- package/dist/src/orgrt/file-roots.d.ts.map +1 -1
- package/dist/src/orgrt/file-roots.js +38 -1
- package/dist/src/orgrt/file-roots.js.map +1 -1
- package/dist/src/orgrt/forwarder.d.ts.map +1 -1
- package/dist/src/orgrt/forwarder.js +16 -2
- package/dist/src/orgrt/forwarder.js.map +1 -1
- package/dist/src/orgrt/idle-deadline.d.ts +74 -1
- package/dist/src/orgrt/idle-deadline.d.ts.map +1 -1
- package/dist/src/orgrt/idle-deadline.js +52 -2
- package/dist/src/orgrt/idle-deadline.js.map +1 -1
- package/dist/src/orgrt/loadouts.d.ts +38 -0
- package/dist/src/orgrt/loadouts.d.ts.map +1 -0
- package/dist/src/orgrt/loadouts.js +129 -0
- package/dist/src/orgrt/loadouts.js.map +1 -0
- package/dist/src/orgrt/mailbox.d.ts +17 -1
- package/dist/src/orgrt/mailbox.d.ts.map +1 -1
- package/dist/src/orgrt/mailbox.js +50 -3
- package/dist/src/orgrt/mailbox.js.map +1 -1
- package/dist/src/orgrt/policy.d.ts +42 -2
- package/dist/src/orgrt/policy.d.ts.map +1 -1
- package/dist/src/orgrt/policy.js +53 -8
- package/dist/src/orgrt/policy.js.map +1 -1
- package/dist/src/orgrt/prompt-vars.d.ts +11 -0
- package/dist/src/orgrt/prompt-vars.d.ts.map +1 -0
- package/dist/src/orgrt/prompt-vars.js +49 -0
- package/dist/src/orgrt/prompt-vars.js.map +1 -0
- package/dist/src/orgrt/questions.d.ts +22 -1
- package/dist/src/orgrt/questions.d.ts.map +1 -1
- package/dist/src/orgrt/questions.js +24 -3
- package/dist/src/orgrt/questions.js.map +1 -1
- package/dist/src/orgrt/review-packet.d.ts +29 -0
- package/dist/src/orgrt/review-packet.d.ts.map +1 -0
- package/dist/src/orgrt/review-packet.js +67 -0
- package/dist/src/orgrt/review-packet.js.map +1 -0
- package/dist/src/orgrt/role-sandbox.d.ts.map +1 -1
- package/dist/src/orgrt/role-sandbox.js +16 -3
- package/dist/src/orgrt/role-sandbox.js.map +1 -1
- package/dist/src/orgrt/session-ledger.d.ts +68 -0
- package/dist/src/orgrt/session-ledger.d.ts.map +1 -0
- package/dist/src/orgrt/session-ledger.js +128 -0
- package/dist/src/orgrt/session-ledger.js.map +1 -0
- package/dist/src/orgrt/session.d.ts +45 -5
- package/dist/src/orgrt/session.d.ts.map +1 -1
- package/dist/src/orgrt/session.js +397 -27
- package/dist/src/orgrt/session.js.map +1 -1
- package/dist/src/orgrt/task-dag.d.ts +34 -1
- package/dist/src/orgrt/task-dag.d.ts.map +1 -1
- package/dist/src/orgrt/task-dag.js +58 -7
- package/dist/src/orgrt/task-dag.js.map +1 -1
- package/dist/src/orgrt/tool-spill.d.ts +94 -0
- package/dist/src/orgrt/tool-spill.d.ts.map +1 -0
- package/dist/src/orgrt/tool-spill.js +180 -0
- package/dist/src/orgrt/tool-spill.js.map +1 -0
- package/dist/src/orgrt/types.d.ts +138 -0
- package/dist/src/orgrt/types.d.ts.map +1 -1
- package/dist/src/orgrt/types.js +148 -0
- package/dist/src/orgrt/types.js.map +1 -1
- package/dist/src/platform-adapters/renderers/mcp.d.ts +20 -2
- package/dist/src/platform-adapters/renderers/mcp.d.ts.map +1 -1
- package/dist/src/platform-adapters/renderers/mcp.js +25 -5
- package/dist/src/platform-adapters/renderers/mcp.js.map +1 -1
- package/dist/src/protocol-capabilities.d.ts +2 -1
- package/dist/src/protocol-capabilities.d.ts.map +1 -1
- package/dist/src/protocol-capabilities.js +2 -1
- package/dist/src/protocol-capabilities.js.map +1 -1
- package/dist/src/ui/dashboard.html +450 -164
- package/dist/src/ui/org-hil.mjs +276 -0
- package/dist/src/ui/org-runtime.mjs +350 -0
- package/dist/src/ui/orgs.html +15 -4
- package/dist/src/ui/routes-org.mjs +290 -541
- package/dist/src/ui/server.mjs +25 -5
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -8
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser performance tools: web vitals, CPU/heap profiling, device emulation.
|
|
3
|
+
*
|
|
4
|
+
* Profiles and heap snapshots are megabytes of JSON, so these tools write the
|
|
5
|
+
* artifact to a file under `.monomind/` and return the path plus a summary an
|
|
6
|
+
* agent can actually act on — never the raw dump.
|
|
7
|
+
*
|
|
8
|
+
* Registered from `browser-tools.ts`; shared plumbing lives in
|
|
9
|
+
* `browser-session.ts`, console/network capture in
|
|
10
|
+
* `browser-instrument-tools.ts`, and the grading/summarising in
|
|
11
|
+
* `browser-metrics.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import { collectRequests } from './browser-instrument-tools.js';
|
|
14
|
+
import { gradeVitals, summarizeCpuProfile } from './browser-metrics.js';
|
|
15
|
+
import { fail, getConnection, ok, rejectFlagLike, resolveArtifactPath, safeFileSlug, touchSession, validateSessionId, } from './browser-session.js';
|
|
16
|
+
/** Most devices allowed in one emulation matrix run — each is a full screenshot. */
|
|
17
|
+
const MAX_MATRIX_DEVICES = 8;
|
|
18
|
+
/** Refuse to parse a .cpuprofile larger than this; the file is still written. */
|
|
19
|
+
const MAX_PROFILE_PARSE_BYTES = 64 * 1024 * 1024;
|
|
20
|
+
/** Settle time after applying a device profile before screenshotting. */
|
|
21
|
+
const EMULATION_SETTLE_MS = 300;
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Tools
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
export const browserProfileTools = [
|
|
26
|
+
{
|
|
27
|
+
name: 'browser_vitals',
|
|
28
|
+
description: 'Measure Core Web Vitals for the loaded page — LCP, CLS, INP, TTFB, FCP — graded against the standard budgets, with the metrics that miss them listed worst-first. Returns numbers to act on plus the human-readable report. Reach for this when asked whether a page is fast, or after a change that could affect load performance. Run browser_network with action:"start" and reload beforehand to also get the heaviest resources.',
|
|
29
|
+
category: 'browser',
|
|
30
|
+
tags: ['performance', 'vitals'],
|
|
31
|
+
inputSchema: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
waitMs: {
|
|
35
|
+
type: 'number',
|
|
36
|
+
description: 'How long to observe before reporting (default 2000, max 10000)',
|
|
37
|
+
},
|
|
38
|
+
session: { type: 'string', description: 'Session ID' },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
handler: async (input) => {
|
|
42
|
+
const raw = input;
|
|
43
|
+
let sessionId;
|
|
44
|
+
try {
|
|
45
|
+
sessionId = validateSessionId(raw.session);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
return fail(e.message);
|
|
49
|
+
}
|
|
50
|
+
const waitMs = Math.min(Math.max(Number(raw.waitMs ?? 2000), 0), 10_000);
|
|
51
|
+
try {
|
|
52
|
+
const { collectVitals, formatVitals } = await import('@monoes/monobrowse');
|
|
53
|
+
const conn = await getConnection(sessionId);
|
|
54
|
+
const vitals = await collectVitals(conn.client, conn.cdpSessionId, waitMs);
|
|
55
|
+
const { assessment, worst } = gradeVitals(vitals);
|
|
56
|
+
const heaviest = (await collectRequests(conn.cdpSessionId))
|
|
57
|
+
.filter((r) => r.sizeBytes !== undefined)
|
|
58
|
+
.sort((a, b) => (b.sizeBytes ?? 0) - (a.sizeBytes ?? 0))
|
|
59
|
+
.slice(0, 5)
|
|
60
|
+
.map((r) => ({
|
|
61
|
+
url: r.url,
|
|
62
|
+
type: r.type,
|
|
63
|
+
sizeBytes: r.sizeBytes,
|
|
64
|
+
durationMs: r.durationMs,
|
|
65
|
+
}));
|
|
66
|
+
touchSession(sessionId);
|
|
67
|
+
return ok({
|
|
68
|
+
session: sessionId,
|
|
69
|
+
vitals,
|
|
70
|
+
formatted: formatVitals(vitals),
|
|
71
|
+
assessment,
|
|
72
|
+
worst,
|
|
73
|
+
heaviestResources: heaviest.length > 0 ? heaviest : undefined,
|
|
74
|
+
note: heaviest.length > 0
|
|
75
|
+
? undefined
|
|
76
|
+
: 'No resource sizes available — run browser_network with action:"start" and reload the page to attribute page weight to specific requests.',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
return fail(e.message);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'browser_profile',
|
|
86
|
+
description: 'CPU profile or heap snapshot of the page. action:"start" then interact then action:"stop" writes a .cpuprofile and returns the functions burning the most self time — enough to answer "what is slow" without reading the file. action:"heap" writes a .heapsnapshot for leak hunting. Reach for this when a page is janky or memory-hungry and browser_vitals only told you that it is.',
|
|
87
|
+
category: 'browser',
|
|
88
|
+
tags: ['performance', 'profiling', 'memory'],
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
action: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
enum: ['start', 'stop', 'heap'],
|
|
95
|
+
description: 'start/stop a CPU profile, or heap for a one-shot heap snapshot',
|
|
96
|
+
},
|
|
97
|
+
topFunctions: {
|
|
98
|
+
type: 'number',
|
|
99
|
+
description: 'How many hot functions to summarise on stop (default 10, max 50)',
|
|
100
|
+
},
|
|
101
|
+
samplingInterval: {
|
|
102
|
+
type: 'number',
|
|
103
|
+
description: 'CPU sampling interval in microseconds (default: Chrome’s 1000)',
|
|
104
|
+
},
|
|
105
|
+
path: {
|
|
106
|
+
type: 'string',
|
|
107
|
+
description: 'Output file, relative to .monomind/profiles/ (default: timestamped)',
|
|
108
|
+
},
|
|
109
|
+
session: { type: 'string', description: 'Session ID' },
|
|
110
|
+
},
|
|
111
|
+
required: ['action'],
|
|
112
|
+
},
|
|
113
|
+
handler: async (input) => {
|
|
114
|
+
const raw = input;
|
|
115
|
+
let sessionId;
|
|
116
|
+
try {
|
|
117
|
+
sessionId = validateSessionId(raw.session);
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
return fail(e.message);
|
|
121
|
+
}
|
|
122
|
+
const action = raw.action;
|
|
123
|
+
if (action !== 'start' && action !== 'stop' && action !== 'heap')
|
|
124
|
+
return fail('action: must be one of start|stop|heap');
|
|
125
|
+
const top = Math.min(Math.max(Number(raw.topFunctions ?? 10), 1), 50);
|
|
126
|
+
try {
|
|
127
|
+
const { startCpuProfile, stopCpuProfile, startHeapSnapshot } = await import('@monoes/monobrowse');
|
|
128
|
+
const conn = await getConnection(sessionId);
|
|
129
|
+
touchSession(sessionId);
|
|
130
|
+
if (action === 'start') {
|
|
131
|
+
const interval = raw.samplingInterval !== undefined ? Number(raw.samplingInterval) : undefined;
|
|
132
|
+
if (interval !== undefined && (!Number.isFinite(interval) || interval <= 0))
|
|
133
|
+
return fail('samplingInterval: must be a positive number of microseconds');
|
|
134
|
+
await startCpuProfile(conn.client, conn.cdpSessionId, { samplingInterval: interval });
|
|
135
|
+
return ok({
|
|
136
|
+
session: sessionId,
|
|
137
|
+
profiling: true,
|
|
138
|
+
note: 'CPU profiler running. Drive the page, then call browser_profile with action:"stop".',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const fs = await import('node:fs/promises');
|
|
142
|
+
if (action === 'heap') {
|
|
143
|
+
const outPath = await resolveArtifactPath('profiles', raw.path, `heap-${Date.now()}.heapsnapshot`);
|
|
144
|
+
// A snapshot that captured no chunks throws inside startHeapSnapshot
|
|
145
|
+
// rather than returning a path, so a path here always has data and
|
|
146
|
+
// an empty one surfaces as fail() through the catch below.
|
|
147
|
+
const written = await startHeapSnapshot(conn.client, conn.cdpSessionId, outPath);
|
|
148
|
+
const { size } = await fs.stat(written);
|
|
149
|
+
return ok({
|
|
150
|
+
session: sessionId,
|
|
151
|
+
path: written,
|
|
152
|
+
sizeBytes: size,
|
|
153
|
+
note: 'Heap snapshot written. Open it in Chrome DevTools → Memory → Load to compare retainers.',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// action === 'stop'
|
|
157
|
+
const outPath = await resolveArtifactPath('profiles', raw.path, `cpu-${Date.now()}.cpuprofile`);
|
|
158
|
+
const written = await stopCpuProfile(conn.client, conn.cdpSessionId, outPath);
|
|
159
|
+
const { size } = await fs.stat(written);
|
|
160
|
+
if (size > MAX_PROFILE_PARSE_BYTES) {
|
|
161
|
+
return ok({
|
|
162
|
+
session: sessionId,
|
|
163
|
+
path: written,
|
|
164
|
+
sizeBytes: size,
|
|
165
|
+
note: `Profile is ${Math.round(size / 1024 / 1024)}MB — too large to summarise here. Open it in Chrome DevTools → Performance.`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
let summary;
|
|
169
|
+
try {
|
|
170
|
+
summary = summarizeCpuProfile(JSON.parse(await fs.readFile(written, 'utf8')), top);
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
return ok({
|
|
174
|
+
session: sessionId,
|
|
175
|
+
path: written,
|
|
176
|
+
sizeBytes: size,
|
|
177
|
+
note: `Profile written but could not be summarised (${e.message}). Open it in Chrome DevTools → Performance.`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return ok({
|
|
181
|
+
session: sessionId,
|
|
182
|
+
path: written,
|
|
183
|
+
sizeBytes: size,
|
|
184
|
+
durationMs: summary.durationMs,
|
|
185
|
+
totalSamples: summary.totalSamples,
|
|
186
|
+
topFunctions: summary.topFunctions,
|
|
187
|
+
note: summary.totalSamples === 0
|
|
188
|
+
? 'No samples recorded — the page was idle for the whole profile.'
|
|
189
|
+
: 'selfMs is time sampled inside the function itself, not its callees.',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
return fail(e.message);
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: 'browser_emulate',
|
|
199
|
+
description: 'Apply a device profile (viewport, DPR, touch, user agent) to the session, or pass devices:[…] to screenshot the current URL across a matrix of them and get the file paths back. Call with no arguments to list the available devices. Reach for this to check responsive layout or to reproduce a mobile-only bug. Emulation persists until reset:true, so clear it before other work.',
|
|
200
|
+
category: 'browser',
|
|
201
|
+
tags: ['emulation', 'responsive', 'screenshot'],
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: 'object',
|
|
204
|
+
properties: {
|
|
205
|
+
device: {
|
|
206
|
+
type: 'string',
|
|
207
|
+
description: 'Device profile name to apply (see the no-arg listing)',
|
|
208
|
+
},
|
|
209
|
+
devices: {
|
|
210
|
+
type: 'array',
|
|
211
|
+
items: { type: 'string' },
|
|
212
|
+
description: `Matrix mode: screenshot the current URL on each device (max ${MAX_MATRIX_DEVICES})`,
|
|
213
|
+
},
|
|
214
|
+
fullPage: { type: 'boolean', description: 'Matrix mode: capture the full scrollable page' },
|
|
215
|
+
reload: {
|
|
216
|
+
type: 'boolean',
|
|
217
|
+
description: 'Matrix mode: reload on each device instead of only reflowing',
|
|
218
|
+
},
|
|
219
|
+
reset: {
|
|
220
|
+
type: 'boolean',
|
|
221
|
+
description: 'Clear emulation overrides and restore the defaults',
|
|
222
|
+
},
|
|
223
|
+
session: { type: 'string', description: 'Session ID' },
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
handler: async (input) => {
|
|
227
|
+
const raw = input;
|
|
228
|
+
let sessionId;
|
|
229
|
+
try {
|
|
230
|
+
sessionId = validateSessionId(raw.session);
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
return fail(e.message);
|
|
234
|
+
}
|
|
235
|
+
const matrix = raw.devices;
|
|
236
|
+
if (matrix !== undefined) {
|
|
237
|
+
if (!Array.isArray(matrix) || matrix.some((d) => typeof d !== 'string'))
|
|
238
|
+
return fail('devices: must be an array of device names');
|
|
239
|
+
if (matrix.length === 0)
|
|
240
|
+
return fail('devices: must not be empty');
|
|
241
|
+
if (matrix.length > MAX_MATRIX_DEVICES)
|
|
242
|
+
return fail(`devices: too many (max ${MAX_MATRIX_DEVICES} per run)`);
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
const { emulateDevice, listDevices, captureScreenshot, getCurrentUrl, waitForLoad } = await import('@monoes/monobrowse');
|
|
246
|
+
if (raw.device === undefined && matrix === undefined && raw.reset !== true) {
|
|
247
|
+
return ok({
|
|
248
|
+
devices: listDevices(),
|
|
249
|
+
note: 'Pass device:"<name>" to emulate one, or devices:["…","…"] to screenshot a matrix.',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
const conn = await getConnection(sessionId);
|
|
253
|
+
touchSession(sessionId);
|
|
254
|
+
if (raw.reset === true && matrix === undefined && raw.device === undefined) {
|
|
255
|
+
await clearEmulation(conn);
|
|
256
|
+
return ok({ session: sessionId, reset: true });
|
|
257
|
+
}
|
|
258
|
+
if (matrix === undefined) {
|
|
259
|
+
const device = rejectFlagLike(raw.device, 'device');
|
|
260
|
+
await emulateDevice(conn.client, conn.cdpSessionId, device);
|
|
261
|
+
return ok({
|
|
262
|
+
session: sessionId,
|
|
263
|
+
device,
|
|
264
|
+
note: 'Emulation stays active for this session — call browser_emulate with reset:true when done.',
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
// Matrix mode: one screenshot per device, then put the session back.
|
|
268
|
+
const url = await getCurrentUrl(conn.client, conn.cdpSessionId);
|
|
269
|
+
const stamp = Date.now();
|
|
270
|
+
const screenshots = [];
|
|
271
|
+
for (const device of matrix) {
|
|
272
|
+
try {
|
|
273
|
+
await emulateDevice(conn.client, conn.cdpSessionId, device);
|
|
274
|
+
if (raw.reload === true) {
|
|
275
|
+
await conn.client.send('Page.reload', {}, conn.cdpSessionId);
|
|
276
|
+
await waitForLoad(conn.client, conn.cdpSessionId, 'load', 30_000);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
await new Promise((r) => setTimeout(r, EMULATION_SETTLE_MS));
|
|
280
|
+
}
|
|
281
|
+
const outPath = await resolveArtifactPath('screenshots', undefined, `emulate-${stamp}-${safeFileSlug(device)}.png`);
|
|
282
|
+
const shot = await captureScreenshot(conn.client, conn.cdpSessionId, {
|
|
283
|
+
path: outPath,
|
|
284
|
+
fullPage: raw.fullPage === true,
|
|
285
|
+
format: 'png',
|
|
286
|
+
});
|
|
287
|
+
screenshots.push({ device, path: shot.path ?? outPath });
|
|
288
|
+
}
|
|
289
|
+
catch (e) {
|
|
290
|
+
screenshots.push({ device, error: e.message });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
await clearEmulation(conn);
|
|
294
|
+
const failed = screenshots.filter((s) => s.error !== undefined).length;
|
|
295
|
+
return ok({
|
|
296
|
+
session: sessionId,
|
|
297
|
+
url,
|
|
298
|
+
screenshots,
|
|
299
|
+
failed,
|
|
300
|
+
note: 'Emulation overrides were cleared after the matrix run.',
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
catch (e) {
|
|
304
|
+
return fail(e.message);
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
];
|
|
309
|
+
/** Drop every emulation override so later tool calls see a normal browser. */
|
|
310
|
+
async function clearEmulation(conn) {
|
|
311
|
+
const send = async (method, params = {}) => {
|
|
312
|
+
try {
|
|
313
|
+
await conn.client.send(method, params, conn.cdpSessionId);
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
/* best-effort: a cleared override that was never set is not an error */
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
await send('Emulation.clearDeviceMetricsOverride');
|
|
320
|
+
await send('Emulation.setTouchEmulationEnabled', { enabled: false });
|
|
321
|
+
await send('Emulation.setUserAgentOverride', { userAgent: '' });
|
|
322
|
+
}
|
|
323
|
+
export default browserProfileTools;
|
|
324
|
+
//# sourceMappingURL=browser-profile-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-profile-tools.js","sourceRoot":"","sources":["../../../src/mcp-tools/browser-profile-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared plumbing for the browser MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* Session registry, CDP connection cache, input validation and result
|
|
5
|
+
* formatting — used by both `browser-tools.ts` (navigation / interaction)
|
|
6
|
+
* and the instrument tools (`browser-instrument-tools.ts`,
|
|
7
|
+
* `browser-profile-tools.ts`). Lives in its own module so the tool files
|
|
8
|
+
* stay readable and neither has to import the other.
|
|
9
|
+
*/
|
|
10
|
+
import type { MCPToolResult } from './types.js';
|
|
11
|
+
export declare const MAX_BROWSER_SESSIONS = 5;
|
|
12
|
+
export declare const SESSION_TTL_MS: number;
|
|
13
|
+
/** Tracking metadata for a single browser session. */
|
|
14
|
+
export interface BrowserSessionInfo {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
lastActivity: string;
|
|
18
|
+
}
|
|
19
|
+
export interface BrowserConnection {
|
|
20
|
+
client: import('@monoes/monobrowse').CdpClient;
|
|
21
|
+
cdpSessionId: string;
|
|
22
|
+
refs: Map<string, import('@monoes/monobrowse').ElementRef>;
|
|
23
|
+
/**
|
|
24
|
+
* When this CDP connection was established. Console/network capture buffers
|
|
25
|
+
* are armed at connect time and keyed by `cdpSessionId`, so this is the
|
|
26
|
+
* start of the window those buffers cover — the instrument tools report it
|
|
27
|
+
* so an agent knows what it is and isn't seeing.
|
|
28
|
+
*/
|
|
29
|
+
openedAt: string;
|
|
30
|
+
}
|
|
31
|
+
export declare const browserSessions: Map<string, BrowserSessionInfo>;
|
|
32
|
+
export declare const connectionCache: Map<string, BrowserConnection>;
|
|
33
|
+
export declare function pruneExpiredSessions(): Promise<void>;
|
|
34
|
+
export declare function touchSession(sessionId: string): void;
|
|
35
|
+
export declare function getConnection(sessionId: string): Promise<BrowserConnection>;
|
|
36
|
+
export declare function releaseConnection(sessionId: string): Promise<void>;
|
|
37
|
+
export declare function closeTarget(conn: BrowserConnection, port: number): Promise<void>;
|
|
38
|
+
export declare function ok(data?: Record<string, unknown>): MCPToolResult;
|
|
39
|
+
export declare function fail(message: string): MCPToolResult;
|
|
40
|
+
/** Validate a session ID against a strict allowlist. */
|
|
41
|
+
export declare function validateSessionId(value: unknown): string;
|
|
42
|
+
/** Validate a URL against a scheme allowlist. */
|
|
43
|
+
export declare const ALLOWED_URL_SCHEMES: Set<string>;
|
|
44
|
+
export declare function validateUrl(value: unknown): string;
|
|
45
|
+
/**
|
|
46
|
+
* Validate a screenshot path. Resolved path must be within
|
|
47
|
+
* `<projectRoot>/.monomind/screenshots` and must not already exist.
|
|
48
|
+
*/
|
|
49
|
+
export declare function validateScreenshotPath(value: unknown): Promise<string>;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the output path for a generated artifact (HAR, CPU profile, heap
|
|
52
|
+
* snapshot, device screenshot) under `<projectRoot>/.monomind/<subdir>/`.
|
|
53
|
+
* A caller-supplied path is taken relative to that directory, must stay
|
|
54
|
+
* inside it, and must not already exist. With no path, `fallbackName` is
|
|
55
|
+
* used. Creates the directory when missing.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveArtifactPath(subdir: string, value: unknown, fallbackName: string): Promise<string>;
|
|
58
|
+
/** Reduce an arbitrary label to something safe to use as a file name. */
|
|
59
|
+
export declare function safeFileSlug(value: string): string;
|
|
60
|
+
/** Reject strings starting with '-' (flag-injection defense). */
|
|
61
|
+
export declare function rejectFlagLike(value: unknown, field: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* Resolve an element target using monobrowse finders.
|
|
64
|
+
* target: CSS selector string (e.g. "#id", ".class", "button")
|
|
65
|
+
* locator: "selector" (default) | "role" | "text" | "label" | "placeholder"
|
|
66
|
+
*/
|
|
67
|
+
export declare function findElement(conn: BrowserConnection, target: string, locator?: string): Promise<import('@monoes/monobrowse').ElementRef>;
|
|
68
|
+
//# sourceMappingURL=browser-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-session.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/browser-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,cAAc,QAAiB,CAAC;AAE7C,sDAAsD;AACtD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,OAAO,oBAAoB,EAAE,SAAS,CAAC;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,oBAAoB,EAAE,UAAU,CAAC,CAAC;IAC3D;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,eAAe,iCAAwC,CAAC;AACrE,eAAO,MAAM,eAAe,gCAAuC,CAAC;AAEpE,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAW1D;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGpD;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4BjF;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxE;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBtF;AAED,wBAAgB,EAAE,CAAC,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,aAAa,CAEpE;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAKnD;AAED,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAMxD;AAED,iDAAiD;AACjD,eAAO,MAAM,mBAAmB,aAAyC,CAAC;AAE1E,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAalD;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAe5E;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CAajB;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQlD;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKpE;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,SAAa,GACnB,OAAO,CAAC,OAAO,oBAAoB,EAAE,UAAU,CAAC,CA2BlD"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared plumbing for the browser MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* Session registry, CDP connection cache, input validation and result
|
|
5
|
+
* formatting — used by both `browser-tools.ts` (navigation / interaction)
|
|
6
|
+
* and the instrument tools (`browser-instrument-tools.ts`,
|
|
7
|
+
* `browser-profile-tools.ts`). Lives in its own module so the tool files
|
|
8
|
+
* stay readable and neither has to import the other.
|
|
9
|
+
*/
|
|
10
|
+
export const MAX_BROWSER_SESSIONS = 5;
|
|
11
|
+
export const SESSION_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
12
|
+
// Session registry for multi-session support (sessionId → connection)
|
|
13
|
+
export const browserSessions = new Map();
|
|
14
|
+
export const connectionCache = new Map();
|
|
15
|
+
export async function pruneExpiredSessions() {
|
|
16
|
+
const cutoff = Date.now() - SESSION_TTL_MS;
|
|
17
|
+
const port = Number(process.env.MONOBROWSE_CDP_PORT ?? 9222);
|
|
18
|
+
for (const [id, info] of browserSessions) {
|
|
19
|
+
if (new Date(info.lastActivity).getTime() < cutoff) {
|
|
20
|
+
browserSessions.delete(id);
|
|
21
|
+
const conn = connectionCache.get(id);
|
|
22
|
+
connectionCache.delete(id);
|
|
23
|
+
if (conn)
|
|
24
|
+
await closeTarget(conn, port);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function touchSession(sessionId) {
|
|
29
|
+
const info = browserSessions.get(sessionId);
|
|
30
|
+
if (info)
|
|
31
|
+
info.lastActivity = new Date().toISOString();
|
|
32
|
+
}
|
|
33
|
+
export async function getConnection(sessionId) {
|
|
34
|
+
if (connectionCache.has(sessionId)) {
|
|
35
|
+
const conn = connectionCache.get(sessionId);
|
|
36
|
+
try {
|
|
37
|
+
// Liveness check — evict stale CDP connections
|
|
38
|
+
await conn.client.send('Runtime.evaluate', { expression: '1', returnByValue: true }, conn.cdpSessionId);
|
|
39
|
+
return conn;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
const port = Number(process.env.MONOBROWSE_CDP_PORT ?? 9222);
|
|
43
|
+
connectionCache.delete(sessionId);
|
|
44
|
+
await closeTarget(conn, port);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const port = Number(process.env.MONOBROWSE_CDP_PORT ?? 9222);
|
|
48
|
+
const { connectToTarget } = await import('@monoes/monobrowse');
|
|
49
|
+
const { client, sessionId: cdpSessionId } = await connectToTarget(port);
|
|
50
|
+
const conn = {
|
|
51
|
+
client,
|
|
52
|
+
cdpSessionId,
|
|
53
|
+
refs: new Map(),
|
|
54
|
+
openedAt: new Date().toISOString(),
|
|
55
|
+
};
|
|
56
|
+
connectionCache.set(sessionId, conn);
|
|
57
|
+
return conn;
|
|
58
|
+
}
|
|
59
|
+
export async function releaseConnection(sessionId) {
|
|
60
|
+
const conn = connectionCache.get(sessionId);
|
|
61
|
+
connectionCache.delete(sessionId);
|
|
62
|
+
browserSessions.delete(sessionId);
|
|
63
|
+
if (conn) {
|
|
64
|
+
const port = Number(process.env.MONOBROWSE_CDP_PORT ?? 9222);
|
|
65
|
+
// Close the actual Chrome tab — just closing the WebSocket leaves the
|
|
66
|
+
// renderer process alive and consuming ~250MB+ RAM each. Awaited (not
|
|
67
|
+
// fire-and-forget) so a burst of session churn can't pile up concurrent
|
|
68
|
+
// in-flight closes and leak renderers when one fails silently.
|
|
69
|
+
await closeTarget(conn, port);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export async function closeTarget(conn, port) {
|
|
73
|
+
try {
|
|
74
|
+
// Get the target ID for this session so we can close the tab
|
|
75
|
+
const result = await conn.client.send('Target.getTargetInfo', {}, conn.cdpSessionId);
|
|
76
|
+
const targetId = result?.targetInfo?.targetId;
|
|
77
|
+
if (targetId) {
|
|
78
|
+
// Close via HTTP endpoint — works even if the CDP session is stale
|
|
79
|
+
await fetch(`http://127.0.0.1:${port}/json/close/${encodeURIComponent(targetId)}`, {
|
|
80
|
+
method: 'GET',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
/* best-effort */
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
conn.client.close();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* ignore */
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function ok(data = {}) {
|
|
95
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }] };
|
|
96
|
+
}
|
|
97
|
+
export function fail(message) {
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: 'text', text: JSON.stringify({ success: false, error: message }) }],
|
|
100
|
+
isError: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Validate a session ID against a strict allowlist. */
|
|
104
|
+
export function validateSessionId(value) {
|
|
105
|
+
if (value === undefined || value === null || value === '')
|
|
106
|
+
return 'default';
|
|
107
|
+
if (typeof value !== 'string' || !/^[A-Za-z0-9_-]{1,64}$/.test(value)) {
|
|
108
|
+
throw new Error('session: must match ^[A-Za-z0-9_-]{1,64}$');
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
/** Validate a URL against a scheme allowlist. */
|
|
113
|
+
export const ALLOWED_URL_SCHEMES = new Set(['http:', 'https:', 'about:']);
|
|
114
|
+
export function validateUrl(value) {
|
|
115
|
+
if (typeof value !== 'string')
|
|
116
|
+
throw new Error('url: must be a string');
|
|
117
|
+
if (value.length > 4096)
|
|
118
|
+
throw new Error('url: too long (max 4096)');
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = new URL(value);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
throw new Error(`url: not a valid URL: ${value}`);
|
|
125
|
+
}
|
|
126
|
+
if (!ALLOWED_URL_SCHEMES.has(parsed.protocol)) {
|
|
127
|
+
throw new Error(`url: scheme "${parsed.protocol}" not allowed (only http/https/about)`);
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validate a screenshot path. Resolved path must be within
|
|
133
|
+
* `<projectRoot>/.monomind/screenshots` and must not already exist.
|
|
134
|
+
*/
|
|
135
|
+
export async function validateScreenshotPath(value) {
|
|
136
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
137
|
+
throw new Error('path: must be a non-empty string');
|
|
138
|
+
if (value.startsWith('-'))
|
|
139
|
+
throw new Error('path: must not start with "-"');
|
|
140
|
+
const path = await import('node:path');
|
|
141
|
+
const fs = await import('node:fs');
|
|
142
|
+
const root = path.resolve(process.cwd(), '.monomind', 'screenshots');
|
|
143
|
+
await fs.promises.mkdir(root, { recursive: true });
|
|
144
|
+
const resolved = path.resolve(value);
|
|
145
|
+
if (!resolved.startsWith(root + path.sep) && resolved !== root) {
|
|
146
|
+
throw new Error(`path: must be within ${root}`);
|
|
147
|
+
}
|
|
148
|
+
if (fs.existsSync(resolved))
|
|
149
|
+
throw new Error(`path: refuses to overwrite existing file at ${resolved}`);
|
|
150
|
+
return resolved;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Resolve the output path for a generated artifact (HAR, CPU profile, heap
|
|
154
|
+
* snapshot, device screenshot) under `<projectRoot>/.monomind/<subdir>/`.
|
|
155
|
+
* A caller-supplied path is taken relative to that directory, must stay
|
|
156
|
+
* inside it, and must not already exist. With no path, `fallbackName` is
|
|
157
|
+
* used. Creates the directory when missing.
|
|
158
|
+
*/
|
|
159
|
+
export async function resolveArtifactPath(subdir, value, fallbackName) {
|
|
160
|
+
const path = await import('node:path');
|
|
161
|
+
const fs = await import('node:fs');
|
|
162
|
+
const root = path.resolve(process.cwd(), '.monomind', subdir);
|
|
163
|
+
await fs.promises.mkdir(root, { recursive: true });
|
|
164
|
+
if (value === undefined || value === null || value === '')
|
|
165
|
+
return path.join(root, fallbackName);
|
|
166
|
+
if (typeof value !== 'string')
|
|
167
|
+
throw new Error('path: must be a string');
|
|
168
|
+
if (value.startsWith('-'))
|
|
169
|
+
throw new Error('path: must not start with "-"');
|
|
170
|
+
const resolved = path.resolve(root, value);
|
|
171
|
+
if (!resolved.startsWith(root + path.sep))
|
|
172
|
+
throw new Error(`path: must be within ${root}`);
|
|
173
|
+
if (fs.existsSync(resolved))
|
|
174
|
+
throw new Error(`path: refuses to overwrite existing file at ${resolved}`);
|
|
175
|
+
return resolved;
|
|
176
|
+
}
|
|
177
|
+
/** Reduce an arbitrary label to something safe to use as a file name. */
|
|
178
|
+
export function safeFileSlug(value) {
|
|
179
|
+
return (value
|
|
180
|
+
.toLowerCase()
|
|
181
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
182
|
+
.replace(/^-+|-+$/g, '')
|
|
183
|
+
.slice(0, 40) || 'item');
|
|
184
|
+
}
|
|
185
|
+
/** Reject strings starting with '-' (flag-injection defense). */
|
|
186
|
+
export function rejectFlagLike(value, field) {
|
|
187
|
+
if (typeof value !== 'string')
|
|
188
|
+
throw new Error(`${field}: must be a string`);
|
|
189
|
+
if (value.startsWith('-'))
|
|
190
|
+
throw new Error(`${field}: must not start with '-' (flag-injection defense)`);
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Resolve an element target using monobrowse finders.
|
|
195
|
+
* target: CSS selector string (e.g. "#id", ".class", "button")
|
|
196
|
+
* locator: "selector" (default) | "role" | "text" | "label" | "placeholder"
|
|
197
|
+
*/
|
|
198
|
+
export async function findElement(conn, target, locator = 'selector') {
|
|
199
|
+
const { findBySelector, findByRole, findByText, findByLabel, findByPlaceholder } = await import('@monoes/monobrowse');
|
|
200
|
+
let ref = null;
|
|
201
|
+
switch (locator) {
|
|
202
|
+
case 'selector':
|
|
203
|
+
ref = await findBySelector(conn.client, conn.cdpSessionId, conn.refs, target);
|
|
204
|
+
break;
|
|
205
|
+
case 'role':
|
|
206
|
+
ref = await findByRole(conn.client, conn.cdpSessionId, conn.refs, target);
|
|
207
|
+
break;
|
|
208
|
+
case 'text':
|
|
209
|
+
ref = await findByText(conn.client, conn.cdpSessionId, conn.refs, target);
|
|
210
|
+
break;
|
|
211
|
+
case 'label':
|
|
212
|
+
ref = await findByLabel(conn.client, conn.cdpSessionId, conn.refs, target);
|
|
213
|
+
break;
|
|
214
|
+
case 'placeholder':
|
|
215
|
+
ref = await findByPlaceholder(conn.client, conn.cdpSessionId, conn.refs, target);
|
|
216
|
+
break;
|
|
217
|
+
default:
|
|
218
|
+
throw new Error(`Unknown locator "${locator}". Use: selector|role|text|label|placeholder`);
|
|
219
|
+
}
|
|
220
|
+
if (!ref)
|
|
221
|
+
throw new Error(`Element not found: ${locator}="${target}"`);
|
|
222
|
+
return ref;
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=browser-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-session.js","sourceRoot":"","sources":["../../../src/mcp-tools/browser-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AAsB3D,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,EAA8B,CAAC;AACrE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,CAAC;IAC7D,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,eAAe,EAAE,CAAC;QACzC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC;YACnD,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC3B,IAAI,IAAI;gBAAE,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC5C,IAAI,IAAI;QAAE,IAAI,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB;IACnD,IAAI,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;QAC7C,IAAI,CAAC;YACH,+CAA+C;YAC/C,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CACpB,kBAAkB,EAClB,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,EACxC,IAAI,CAAC,YAAY,CAClB,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,CAAC;YAC7D,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,CAAC;IAC7D,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAC/D,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,IAAI,GAAsB;QAC9B,MAAM;QACN,YAAY;QACZ,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACnC,CAAC;IACF,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IACvD,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC5C,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,CAAC;QAC7D,sEAAsE;QACtE,sEAAsE;QACtE,wEAAwE;QACxE,+DAA+D;QAC/D,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAuB,EAAE,IAAY;IACrE,IAAI,CAAC;QACH,6DAA6D;QAC7D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CACnC,sBAAsB,EACtB,EAAE,EACF,IAAI,CAAC,YAAY,CAClB,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC;QAC9C,IAAI,QAAQ,EAAE,CAAC;YACb,mEAAmE;YACnE,MAAM,KAAK,CAAC,oBAAoB,IAAI,eAAe,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAAE;gBACjF,MAAM,EAAE,KAAK;aACd,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iBAAiB;IACnB,CAAC;IACD,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,EAAE,CAAC,IAAI,GAA4B,EAAE;IACnD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACrF,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC5E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iDAAiD;AACjD,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE1E,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACxE,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACrE,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,gBAAgB,MAAM,CAAC,QAAQ,uCAAuC,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,KAAc;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;IACrE,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,+CAA+C,QAAQ,EAAE,CAAC,CAAC;IAC7E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAc,EACd,KAAc,EACd,YAAoB;IAEpB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAC9D,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAChG,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACzE,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAC3F,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,+CAA+C,QAAQ,EAAE,CAAC,CAAC;IAC7E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,CACL,KAAK;SACF,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,MAAM,CAC1B,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,KAAa;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;IAC7E,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,oDAAoD,CAAC,CAAC;IAChF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAuB,EACvB,MAAc,EACd,OAAO,GAAG,UAAU;IAEpB,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAC7F,oBAAoB,CACrB,CAAC;IAEF,IAAI,GAAG,GAAmD,IAAI,CAAC;IAC/D,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,UAAU;YACb,GAAG,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC9E,MAAM;QACR,KAAK,MAAM;YACT,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC1E,MAAM;QACR,KAAK,MAAM;YACT,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC1E,MAAM;QACR,KAAK,OAAO;YACV,GAAG,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC3E,MAAM;QACR,KAAK,aAAa;YAChB,GAAG,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACjF,MAAM;QACR;YACE,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,8CAA8C,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC;IACvE,OAAO,GAAG,CAAC;AACb,CAAC"}
|