craftdriver 1.7.0 → 1.9.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/CHANGELOG.md +25 -12
- package/README.md +16 -6
- package/dist/cli/agentSession.d.ts +62 -0
- package/dist/cli/agentSession.d.ts.map +1 -0
- package/dist/cli/agentSession.js +100 -0
- package/dist/cli/agentSession.js.map +1 -0
- package/dist/cli/artifactPaths.d.ts +42 -0
- package/dist/cli/artifactPaths.d.ts.map +1 -0
- package/dist/cli/artifactPaths.js +144 -0
- package/dist/cli/artifactPaths.js.map +1 -0
- package/dist/cli/bounds.d.ts +88 -0
- package/dist/cli/bounds.d.ts.map +1 -0
- package/dist/cli/bounds.js +128 -0
- package/dist/cli/bounds.js.map +1 -0
- package/dist/cli/client.d.ts +3 -0
- package/dist/cli/client.d.ts.map +1 -1
- package/dist/cli/client.js +24 -8
- package/dist/cli/client.js.map +1 -1
- package/dist/cli/daemon.d.ts +3 -0
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +152 -39
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/defaults.d.ts +23 -3
- package/dist/cli/defaults.d.ts.map +1 -1
- package/dist/cli/defaults.js +50 -5
- package/dist/cli/defaults.js.map +1 -1
- package/dist/cli/dispatcher.d.ts +54 -11
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +851 -54
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +305 -56
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +14 -14
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +328 -180
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/journal.d.ts +153 -0
- package/dist/cli/journal.d.ts.map +1 -0
- package/dist/cli/journal.js +252 -0
- package/dist/cli/journal.js.map +1 -0
- package/dist/cli/lineReader.d.ts +33 -0
- package/dist/cli/lineReader.d.ts.map +1 -0
- package/dist/cli/lineReader.js +66 -0
- package/dist/cli/lineReader.js.map +1 -0
- package/dist/cli/locatorCandidates.d.ts +44 -0
- package/dist/cli/locatorCandidates.d.ts.map +1 -0
- package/dist/cli/locatorCandidates.js +190 -0
- package/dist/cli/locatorCandidates.js.map +1 -0
- package/dist/cli/mcp/artifacts.d.ts +39 -1
- package/dist/cli/mcp/artifacts.d.ts.map +1 -1
- package/dist/cli/mcp/artifacts.js +104 -9
- package/dist/cli/mcp/artifacts.js.map +1 -1
- package/dist/cli/mcp/bounds.d.ts +65 -0
- package/dist/cli/mcp/bounds.d.ts.map +1 -0
- package/dist/cli/mcp/bounds.js +124 -0
- package/dist/cli/mcp/bounds.js.map +1 -0
- package/dist/cli/mcp/params.d.ts +38 -0
- package/dist/cli/mcp/params.d.ts.map +1 -0
- package/dist/cli/mcp/params.js +174 -0
- package/dist/cli/mcp/params.js.map +1 -0
- package/dist/cli/mcp/server.d.ts +94 -16
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/server.js +304 -150
- package/dist/cli/mcp/server.js.map +1 -1
- package/dist/cli/mcp/tools.d.ts +62 -17
- package/dist/cli/mcp/tools.d.ts.map +1 -1
- package/dist/cli/mcp/tools.js +373 -158
- package/dist/cli/mcp/tools.js.map +1 -1
- package/dist/cli/pageSemantics.d.ts +6 -0
- package/dist/cli/pageSemantics.d.ts.map +1 -0
- package/dist/cli/pageSemantics.js +6 -0
- package/dist/cli/pageSemantics.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +6 -18
- package/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +689 -45
- package/dist/cli/parseArgs.js.map +1 -1
- package/dist/cli/protocol.d.ts +8 -0
- package/dist/cli/protocol.d.ts.map +1 -1
- package/dist/cli/selector.d.ts +3 -3
- package/dist/cli/selector.d.ts.map +1 -1
- package/dist/cli/selector.js +6 -9
- package/dist/cli/selector.js.map +1 -1
- package/dist/cli/sessionRegistry.d.ts +42 -0
- package/dist/cli/sessionRegistry.d.ts.map +1 -0
- package/dist/cli/sessionRegistry.js +99 -0
- package/dist/cli/sessionRegistry.js.map +1 -0
- package/dist/cli/snapshot.d.ts +100 -19
- package/dist/cli/snapshot.d.ts.map +1 -1
- package/dist/cli/snapshot.js +295 -108
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/stateStore.d.ts +50 -0
- package/dist/cli/stateStore.d.ts.map +1 -0
- package/dist/cli/stateStore.js +122 -0
- package/dist/cli/stateStore.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/a11y.d.ts +6 -2
- package/dist/lib/a11y.d.ts.map +1 -1
- package/dist/lib/a11y.js +3 -1
- package/dist/lib/a11y.js.map +1 -1
- package/dist/lib/bidi/evaluate.d.ts +11 -0
- package/dist/lib/bidi/evaluate.d.ts.map +1 -0
- package/dist/lib/bidi/evaluate.js +25 -0
- package/dist/lib/bidi/evaluate.js.map +1 -0
- package/dist/lib/bidi/index.d.ts +1 -1
- package/dist/lib/bidi/index.d.ts.map +1 -1
- package/dist/lib/bidi/index.js +2 -2
- package/dist/lib/bidi/index.js.map +1 -1
- package/dist/lib/bidi/logs.d.ts +3 -0
- package/dist/lib/bidi/logs.d.ts.map +1 -1
- package/dist/lib/bidi/logs.js +7 -0
- package/dist/lib/bidi/logs.js.map +1 -1
- package/dist/lib/bidi/network.d.ts +14 -1
- package/dist/lib/bidi/network.d.ts.map +1 -1
- package/dist/lib/bidi/network.js +45 -7
- package/dist/lib/bidi/network.js.map +1 -1
- package/dist/lib/bidi/storage.d.ts +30 -6
- package/dist/lib/bidi/storage.d.ts.map +1 -1
- package/dist/lib/bidi/storage.js +190 -45
- package/dist/lib/bidi/storage.js.map +1 -1
- package/dist/lib/bidi/types.d.ts +9 -2
- package/dist/lib/bidi/types.d.ts.map +1 -1
- package/dist/lib/browser.d.ts +7 -4
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +186 -65
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +58 -24
- package/dist/lib/browserContext.d.ts.map +1 -1
- package/dist/lib/browserContext.js +300 -99
- package/dist/lib/browserContext.js.map +1 -1
- package/dist/lib/by.d.ts +68 -30
- package/dist/lib/by.d.ts.map +1 -1
- package/dist/lib/by.js +54 -23
- package/dist/lib/by.js.map +1 -1
- package/dist/lib/clearFastPath.d.ts.map +1 -1
- package/dist/lib/clearFastPath.js +2 -0
- package/dist/lib/clearFastPath.js.map +1 -1
- package/dist/lib/clickFastPath.d.ts.map +1 -1
- package/dist/lib/clickFastPath.js +2 -0
- package/dist/lib/clickFastPath.js.map +1 -1
- package/dist/lib/driver.d.ts +26 -0
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +123 -4
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/elementHandle.d.ts +13 -10
- package/dist/lib/elementHandle.d.ts.map +1 -1
- package/dist/lib/elementHandle.js +119 -50
- package/dist/lib/elementHandle.js.map +1 -1
- package/dist/lib/errors.d.ts +12 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +12 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/expect.d.ts +10 -0
- package/dist/lib/expect.d.ts.map +1 -1
- package/dist/lib/expect.js +76 -49
- package/dist/lib/expect.js.map +1 -1
- package/dist/lib/fillFastPath.d.ts.map +1 -1
- package/dist/lib/fillFastPath.js +2 -0
- package/dist/lib/fillFastPath.js.map +1 -1
- package/dist/lib/frame.d.ts.map +1 -1
- package/dist/lib/frame.js +29 -17
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/initScript.d.ts +9 -0
- package/dist/lib/initScript.d.ts.map +1 -0
- package/dist/lib/initScript.js +14 -0
- package/dist/lib/initScript.js.map +1 -0
- package/dist/lib/locator.d.ts +21 -54
- package/dist/lib/locator.d.ts.map +1 -1
- package/dist/lib/locator.js +165 -208
- package/dist/lib/locator.js.map +1 -1
- package/dist/lib/page.d.ts +22 -0
- package/dist/lib/page.d.ts.map +1 -1
- package/dist/lib/page.js +71 -13
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/pageSemantics.d.ts +42 -0
- package/dist/lib/pageSemantics.d.ts.map +1 -0
- package/dist/lib/pageSemantics.js +216 -0
- package/dist/lib/pageSemantics.js.map +1 -0
- package/dist/lib/query.d.ts +68 -0
- package/dist/lib/query.d.ts.map +1 -0
- package/dist/lib/query.js +590 -0
- package/dist/lib/query.js.map +1 -0
- package/dist/lib/secureFile.d.ts +8 -0
- package/dist/lib/secureFile.d.ts.map +1 -0
- package/dist/lib/secureFile.js +67 -0
- package/dist/lib/secureFile.js.map +1 -0
- package/dist/lib/sessionStateValidation.d.ts +35 -0
- package/dist/lib/sessionStateValidation.d.ts.map +1 -0
- package/dist/lib/sessionStateValidation.js +223 -0
- package/dist/lib/sessionStateValidation.js.map +1 -0
- package/dist/lib/shadowRoot.d.ts +21 -0
- package/dist/lib/shadowRoot.d.ts.map +1 -0
- package/dist/lib/shadowRoot.js +29 -0
- package/dist/lib/shadowRoot.js.map +1 -0
- package/dist/lib/shadowRootLocator.d.ts +31 -0
- package/dist/lib/shadowRootLocator.d.ts.map +1 -0
- package/dist/lib/shadowRootLocator.js +55 -0
- package/dist/lib/shadowRootLocator.js.map +1 -0
- package/dist/lib/timing.d.ts +2 -2
- package/dist/lib/timing.js +2 -2
- package/dist/lib/tracing.d.ts +2 -1
- package/dist/lib/tracing.d.ts.map +1 -1
- package/dist/lib/tracing.js +12 -1
- package/dist/lib/tracing.js.map +1 -1
- package/docs/accessibility.md +9 -1
- package/docs/agents.md +56 -61
- package/docs/api-reference.md +5 -1
- package/docs/browser-api.md +27 -7
- package/docs/browser-context.md +21 -15
- package/docs/cli.md +353 -30
- package/docs/error-codes.md +7 -4
- package/docs/getting-started.md +6 -1
- package/docs/index.md +1 -1
- package/docs/mcp.md +136 -65
- package/docs/public/examples/agent-actions.html +48 -0
- package/docs/public/examples/agent-debug.html +86 -0
- package/docs/public/examples/agent-late-mutation.html +18 -0
- package/docs/public/examples/agent-ref-shift.html +33 -0
- package/docs/public/examples/iframe-child.html +12 -1
- package/docs/public/examples/shadow-dom.html +137 -0
- package/docs/recipes/login-once-reuse-session.md +33 -5
- package/docs/selectors.md +61 -0
- package/docs/session-management.md +80 -12
- package/docs/why-craftdriver.md +1 -1
- package/package.json +5 -3
- package/skills/craftdriver/SKILL.md +59 -75
- package/skills/craftdriver/agents/openai.yaml +4 -0
- package/skills/craftdriver/cheatsheet.md +18 -4
- package/skills/craftdriver/cli.md +194 -68
- package/skills/craftdriver/patterns.md +17 -8
- package/skills/craftdriver/workflow.md +149 -0
package/dist/cli/snapshot.js
CHANGED
|
@@ -1,23 +1,96 @@
|
|
|
1
|
+
import { PAGE_SEMANTICS_JS } from './pageSemantics.js';
|
|
1
2
|
const MAX_NODES = 80;
|
|
2
3
|
const MAX_NAME = 80;
|
|
4
|
+
/**
|
|
5
|
+
* Upper bound on refs issued per document. Reaching it resets the registry
|
|
6
|
+
* under a fresh `documentId`, which invalidates every outstanding ref
|
|
7
|
+
* rather than letting numbers wrap onto live elements.
|
|
8
|
+
*/
|
|
9
|
+
const MAX_REFS = 2000;
|
|
3
10
|
/**
|
|
4
11
|
* Build a fresh snapshot of the active page. Errors swallowed so a
|
|
5
12
|
* partial post-action payload never breaks the protocol.
|
|
6
13
|
*/
|
|
7
|
-
export async function takeSnapshot(browser) {
|
|
14
|
+
export async function takeSnapshot(browser, minRef = 0) {
|
|
8
15
|
try {
|
|
9
16
|
const page = await browser.activePage();
|
|
10
17
|
const [url, title] = await Promise.all([
|
|
11
18
|
page.url().catch(() => ''),
|
|
12
19
|
page.title().catch(() => ''),
|
|
13
20
|
]);
|
|
14
|
-
const
|
|
15
|
-
|
|
21
|
+
const raw = await page.evaluate(jsSnapshot(minRef));
|
|
22
|
+
if (!raw || typeof raw !== 'object')
|
|
23
|
+
return null;
|
|
24
|
+
const shaped = raw;
|
|
25
|
+
return {
|
|
26
|
+
url,
|
|
27
|
+
title,
|
|
28
|
+
lines: Array.isArray(shaped.lines) ? shaped.lines : [],
|
|
29
|
+
documentId: typeof shaped.documentId === 'string' ? shaped.documentId : '',
|
|
30
|
+
revision: 0,
|
|
31
|
+
refs: Array.isArray(shaped.refs) ? shaped.refs : [],
|
|
32
|
+
nextRef: typeof shaped.nextRef === 'number' ? shaped.nextRef : minRef + 1,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Return the text of an open JavaScript dialog, or null if none is open.
|
|
41
|
+
*
|
|
42
|
+
* Cheap (~2ms) and, unlike almost every other command, safe to call while
|
|
43
|
+
* a modal dialog is blocking the page. That matters: a modal blocks script
|
|
44
|
+
* execution, so an unguarded post-action snapshot sits there until the
|
|
45
|
+
* WebDriver script timeout — a measured 60 seconds — before failing.
|
|
46
|
+
*/
|
|
47
|
+
export async function peekDialog(browser) {
|
|
48
|
+
try {
|
|
49
|
+
return await browser.getDialogMessage();
|
|
16
50
|
}
|
|
17
51
|
catch {
|
|
52
|
+
// The driver reports "no such alert" by throwing; that is the common case.
|
|
18
53
|
return null;
|
|
19
54
|
}
|
|
20
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Ask the page whether `ref` still identifies exactly one live element.
|
|
58
|
+
*
|
|
59
|
+
* `expectedDocumentId` is the document the ref was issued against; a
|
|
60
|
+
* mismatch means the page navigated and every outstanding ref is stale.
|
|
61
|
+
*/
|
|
62
|
+
export async function probeRef(browser, ref, expectedDocumentId) {
|
|
63
|
+
if (!/^e\d+$/.test(ref))
|
|
64
|
+
return { status: 'unknown-ref', documentId: null };
|
|
65
|
+
try {
|
|
66
|
+
const page = await browser.activePage();
|
|
67
|
+
const js = jsProbeRef(ref, expectedDocumentId);
|
|
68
|
+
const raw = await page.evaluate(js);
|
|
69
|
+
if (!raw || typeof raw !== 'object')
|
|
70
|
+
return { status: 'no-registry', documentId: null };
|
|
71
|
+
const shaped = raw;
|
|
72
|
+
return {
|
|
73
|
+
status: (typeof shaped.status === 'string' ? shaped.status : 'no-registry'),
|
|
74
|
+
documentId: typeof shaped.documentId === 'string' ? shaped.documentId : null,
|
|
75
|
+
...(typeof shaped.count === 'number' ? { count: shaped.count } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { status: 'no-registry', documentId: null };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Human-readable reason attached to a STALE_REF failure. */
|
|
83
|
+
export function describeRefStatus(status) {
|
|
84
|
+
switch (status) {
|
|
85
|
+
case 'no-snapshot': return 'no snapshot has been taken in this session yet';
|
|
86
|
+
case 'no-registry': return 'the page has no ref registry (it navigated or was replaced)';
|
|
87
|
+
case 'document-changed': return 'the page navigated or reloaded after the ref was issued';
|
|
88
|
+
case 'unknown-ref': return 'the ref was never issued for the current document';
|
|
89
|
+
case 'detached': return 'the element was removed from the document';
|
|
90
|
+
case 'ambiguous': return 'the ref no longer identifies exactly one element';
|
|
91
|
+
default: return 'the ref is no longer valid';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
21
94
|
/**
|
|
22
95
|
* Render a snapshot as a compact, agent-friendly string. Either the
|
|
23
96
|
* full snapshot (first call in a session) or a `+/-` diff vs. the
|
|
@@ -26,10 +99,8 @@ export async function takeSnapshot(browser) {
|
|
|
26
99
|
* Diff format intentionally mirrors `diff -u` lines so agents
|
|
27
100
|
* already familiar with unified diffs read it without instruction.
|
|
28
101
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* the set-difference and re-attach them when rendering, so the diff
|
|
32
|
-
* stays semantically stable across renumberings.
|
|
102
|
+
* Refs are stripped before the set-difference and re-attached when
|
|
103
|
+
* rendering, so a diff reports semantic change rather than renumbering.
|
|
33
104
|
*/
|
|
34
105
|
export function renderDelta(prev, next) {
|
|
35
106
|
if (!next)
|
|
@@ -41,15 +112,41 @@ export function renderDelta(prev, next) {
|
|
|
41
112
|
return `${header}\n(no interactive elements detected)`;
|
|
42
113
|
return `${header}\n${next.lines.join('\n')}`;
|
|
43
114
|
}
|
|
44
|
-
|
|
45
|
-
|
|
115
|
+
// A new document means the old lines describe a page that no longer
|
|
116
|
+
// exists; a diff against them would be noise. Same-URL reloads count.
|
|
117
|
+
if (prev.documentId !== next.documentId || prev.url !== next.url) {
|
|
46
118
|
return `${header}\n${next.lines.join('\n')}`;
|
|
47
119
|
}
|
|
48
|
-
const strip = (l) => l.replace(/^e\d+:\s*/, '');
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
120
|
+
const strip = (l) => l.replace(/^(\s*)e\d+:\s*/, '$1');
|
|
121
|
+
// Count occurrences rather than testing membership: a page with three
|
|
122
|
+
// identical "Remove" buttons must still report a change when one of them
|
|
123
|
+
// goes, and a set-difference silently calls that "no changes".
|
|
124
|
+
const tally = (lines) => {
|
|
125
|
+
const counts = new Map();
|
|
126
|
+
for (const line of lines) {
|
|
127
|
+
const body = strip(line);
|
|
128
|
+
counts.set(body, (counts.get(body) ?? 0) + 1);
|
|
129
|
+
}
|
|
130
|
+
return counts;
|
|
131
|
+
};
|
|
132
|
+
const prevCounts = tally(prev.lines);
|
|
133
|
+
const nextCounts = tally(next.lines);
|
|
134
|
+
const take = (lines, budget) => {
|
|
135
|
+
const out = [];
|
|
136
|
+
const seen = new Map();
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
const body = strip(line);
|
|
139
|
+
const used = seen.get(body) ?? 0;
|
|
140
|
+
// Report the surplus copies only — the first N matching lines are
|
|
141
|
+
// accounted for by the other side.
|
|
142
|
+
if (used >= (budget.get(body) ?? 0))
|
|
143
|
+
out.push(line);
|
|
144
|
+
seen.set(body, used + 1);
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
};
|
|
148
|
+
const removed = take(prev.lines, nextCounts).map((l) => `- ${l}`);
|
|
149
|
+
const added = take(next.lines, prevCounts).map((l) => `+ ${l}`);
|
|
53
150
|
if (removed.length === 0 && added.length === 0) {
|
|
54
151
|
return `${header}\n(no a11y changes)`;
|
|
55
152
|
}
|
|
@@ -69,20 +166,153 @@ export function renderFull(next) {
|
|
|
69
166
|
}
|
|
70
167
|
return `${header}\n${next.lines.join('\n')}`;
|
|
71
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Owns the one snapshot baseline and ref-issuing document for a session.
|
|
171
|
+
*
|
|
172
|
+
* Both explicit `snapshot` and the automatic post-action snapshot record
|
|
173
|
+
* through here, so an explicit snapshot the agent has already been shown
|
|
174
|
+
* is never re-reported as a change caused by the next action.
|
|
175
|
+
*/
|
|
176
|
+
export class SnapshotTracker {
|
|
177
|
+
baseline = null;
|
|
178
|
+
revision = 0;
|
|
179
|
+
highWater = 0;
|
|
180
|
+
/** The document outstanding refs were issued against, if any. */
|
|
181
|
+
get documentId() {
|
|
182
|
+
return this.baseline ? this.baseline.documentId : null;
|
|
183
|
+
}
|
|
184
|
+
get current() {
|
|
185
|
+
return this.baseline;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Highest ref number issued anywhere in this session.
|
|
189
|
+
*
|
|
190
|
+
* Seeding each new document above this is what stops a reload from
|
|
191
|
+
* reissuing `e2` to a different element: a ref the agent still holds
|
|
192
|
+
* would otherwise silently resolve against the new page.
|
|
193
|
+
*/
|
|
194
|
+
get minRef() {
|
|
195
|
+
return this.highWater;
|
|
196
|
+
}
|
|
197
|
+
/** Stamp a session-monotonic revision and make this the new baseline. */
|
|
198
|
+
record(snap) {
|
|
199
|
+
if (!snap)
|
|
200
|
+
return null;
|
|
201
|
+
const stamped = { ...snap, revision: ++this.revision };
|
|
202
|
+
this.baseline = stamped;
|
|
203
|
+
this.highWater = Math.max(this.highWater, stamped.nextRef - 1);
|
|
204
|
+
return stamped;
|
|
205
|
+
}
|
|
206
|
+
/** Diff against the baseline and advance it in one step. */
|
|
207
|
+
advance(snap) {
|
|
208
|
+
if (!snap)
|
|
209
|
+
return '';
|
|
210
|
+
const delta = renderDelta(this.baseline, { ...snap, revision: this.revision + 1 });
|
|
211
|
+
this.record(snap);
|
|
212
|
+
return delta;
|
|
213
|
+
}
|
|
214
|
+
reset() {
|
|
215
|
+
this.baseline = null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Ref-validation probe. `ref` is `^e\d+$`-validated before it gets here and
|
|
220
|
+
* both values are JSON-encoded into the source, so neither can break out.
|
|
221
|
+
*/
|
|
222
|
+
function jsProbeRef(ref, expectedDocumentId) {
|
|
223
|
+
return `
|
|
224
|
+
const reg = window.__craftdriverRefs;
|
|
225
|
+
const ref = ${JSON.stringify(ref)};
|
|
226
|
+
const expected = ${JSON.stringify(expectedDocumentId)};
|
|
227
|
+
if (!reg || !reg.map) return { status: 'no-registry', documentId: null };
|
|
228
|
+
if (expected !== null && reg.doc !== expected) {
|
|
229
|
+
return { status: 'document-changed', documentId: reg.doc };
|
|
230
|
+
}
|
|
231
|
+
const el = reg.map.get(ref);
|
|
232
|
+
if (!el) return { status: 'unknown-ref', documentId: reg.doc };
|
|
233
|
+
if (!el.isConnected || el.ownerDocument !== document) {
|
|
234
|
+
return { status: 'detached', documentId: reg.doc };
|
|
235
|
+
}
|
|
236
|
+
// The reverse map is the exact identity index. A mismatch is only possible for a
|
|
237
|
+
// corrupt/legacy registry; marker attributes deliberately do not participate.
|
|
238
|
+
if (reg.reverse && reg.reverse.get(el) !== ref) {
|
|
239
|
+
return { status: 'ambiguous', documentId: reg.doc };
|
|
240
|
+
}
|
|
241
|
+
return { status: 'ok', documentId: reg.doc };
|
|
242
|
+
`;
|
|
243
|
+
}
|
|
72
244
|
/**
|
|
73
245
|
* JS executed in-page. Keep it self-contained — no closure capture.
|
|
74
246
|
*
|
|
75
247
|
* Strategy:
|
|
76
|
-
* 1.
|
|
77
|
-
* 2.
|
|
78
|
-
* 3.
|
|
79
|
-
*
|
|
80
|
-
*
|
|
248
|
+
* 1. Get or create the per-document ref registry.
|
|
249
|
+
* 2. Walk the composed tree, entering open shadow roots and flattened slots.
|
|
250
|
+
* 3. Skip nodes outside the viewport-ish bounds and zero-sized nodes.
|
|
251
|
+
* 4. Reuse each node's existing ref, or issue the next unused one.
|
|
252
|
+
* 5. Cap output at MAX_NODES so the snapshot stays bounded regardless
|
|
81
253
|
* of page complexity.
|
|
82
254
|
*/
|
|
83
|
-
|
|
255
|
+
function jsSnapshot(minRef) {
|
|
256
|
+
const floor = Number.isSafeInteger(minRef) && minRef >= 0 ? minRef : 0;
|
|
257
|
+
return `
|
|
84
258
|
const MAX_NODES = ${MAX_NODES};
|
|
85
259
|
const MAX_NAME = ${MAX_NAME};
|
|
260
|
+
const MAX_REFS = ${MAX_REFS};
|
|
261
|
+
const MIN_REF = ${floor};
|
|
262
|
+
function newDocId(epoch) {
|
|
263
|
+
return 'd' + epoch + '-' + Math.random().toString(36).slice(2, 10);
|
|
264
|
+
}
|
|
265
|
+
function registry() {
|
|
266
|
+
let reg = window.__craftdriverRefs;
|
|
267
|
+
if (!reg || !reg.map) {
|
|
268
|
+
reg = {
|
|
269
|
+
doc: newDocId(1), epoch: 1, next: 1, issued: 0,
|
|
270
|
+
map: new Map(), reverse: new WeakMap()
|
|
271
|
+
};
|
|
272
|
+
window.__craftdriverRefs = reg;
|
|
273
|
+
}
|
|
274
|
+
if (!reg.reverse) {
|
|
275
|
+
reg.reverse = new WeakMap();
|
|
276
|
+
reg.map.forEach(function (el, ref) {
|
|
277
|
+
if (el) reg.reverse.set(el, ref);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
// Never reissue a number the session has already handed out. A fresh
|
|
281
|
+
// document starts its counter above the session high-water mark, so a
|
|
282
|
+
// ref captured before a navigation can never match a new element.
|
|
283
|
+
if (reg.next <= MIN_REF) reg.next = MIN_REF + 1;
|
|
284
|
+
// Bounded: at the cap, drop every marker and re-key the document so all
|
|
285
|
+
// outstanding refs fail as stale instead of wrapping onto live elements.
|
|
286
|
+
//
|
|
287
|
+
// The cap counts refs issued *by this registry*, not the counter value.
|
|
288
|
+
// Counting the counter would compare against the session-wide high-water
|
|
289
|
+
// mark, which passes the cap after enough navigations and would then
|
|
290
|
+
// re-key on every single snapshot — invalidating refs the instant they
|
|
291
|
+
// were handed out.
|
|
292
|
+
if (reg.issued >= MAX_REFS) {
|
|
293
|
+
reg.map.forEach(function (el) {
|
|
294
|
+
if (el && el.removeAttribute) el.removeAttribute('data-craftdriver-ref');
|
|
295
|
+
});
|
|
296
|
+
reg.epoch += 1;
|
|
297
|
+
reg.doc = newDocId(reg.epoch);
|
|
298
|
+
reg.issued = 0;
|
|
299
|
+
reg.map = new Map();
|
|
300
|
+
reg.reverse = new WeakMap();
|
|
301
|
+
}
|
|
302
|
+
return reg;
|
|
303
|
+
}
|
|
304
|
+
function refFor(reg, el) {
|
|
305
|
+
const existing = reg.reverse.get(el);
|
|
306
|
+
if (existing && reg.map.get(existing) === el) return existing;
|
|
307
|
+
const ref = 'e' + reg.next;
|
|
308
|
+
reg.next += 1;
|
|
309
|
+
reg.issued += 1;
|
|
310
|
+
reg.map.set(ref, el);
|
|
311
|
+
reg.reverse.set(el, ref);
|
|
312
|
+
// Diagnostic only. Exact ref resolution never queries this attribute.
|
|
313
|
+
el.setAttribute('data-craftdriver-ref', ref);
|
|
314
|
+
return ref;
|
|
315
|
+
}
|
|
86
316
|
function visible(el) {
|
|
87
317
|
if (!el || !el.getClientRects) return false;
|
|
88
318
|
if (el.hidden) return false;
|
|
@@ -94,81 +324,7 @@ function visible(el) {
|
|
|
94
324
|
if (cs.visibility === 'hidden' || cs.display === 'none' || cs.opacity === '0') return false;
|
|
95
325
|
return true;
|
|
96
326
|
}
|
|
97
|
-
|
|
98
|
-
const role = (el.getAttribute('role') || '').trim();
|
|
99
|
-
return role ? role.split(/\\s+/)[0] : null;
|
|
100
|
-
}
|
|
101
|
-
function hasSectioningAncestor(el) {
|
|
102
|
-
for (let p = el.parentElement; p; p = p.parentElement) {
|
|
103
|
-
const t = p.tagName.toLowerCase();
|
|
104
|
-
if (t === 'article' || t === 'aside' || t === 'main' || t === 'nav' || t === 'section') {
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
const role = firstRoleToken(p);
|
|
108
|
-
if (
|
|
109
|
-
role === 'article' ||
|
|
110
|
-
role === 'complementary' ||
|
|
111
|
-
role === 'main' ||
|
|
112
|
-
role === 'navigation' ||
|
|
113
|
-
role === 'region'
|
|
114
|
-
) {
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
function impliedRole(el) {
|
|
121
|
-
const t = el.tagName.toLowerCase();
|
|
122
|
-
const explicit = firstRoleToken(el);
|
|
123
|
-
if (explicit) return explicit;
|
|
124
|
-
if (t === 'a' && el.hasAttribute('href')) return 'link';
|
|
125
|
-
if (t === 'button') return 'button';
|
|
126
|
-
if (t === 'input') {
|
|
127
|
-
const it = (el.getAttribute('type') || 'text').toLowerCase();
|
|
128
|
-
if (it === 'submit' || it === 'button' || it === 'reset') return 'button';
|
|
129
|
-
if (it === 'checkbox') return 'checkbox';
|
|
130
|
-
if (it === 'radio') return 'radio';
|
|
131
|
-
return 'textbox';
|
|
132
|
-
}
|
|
133
|
-
if (t === 'textarea') return 'textbox';
|
|
134
|
-
if (t === 'select') {
|
|
135
|
-
const size = Number(el.getAttribute('size') || '0');
|
|
136
|
-
return el.multiple || size > 1 ? 'listbox' : 'combobox';
|
|
137
|
-
}
|
|
138
|
-
if (/^h[1-6]$/.test(t)) return 'heading';
|
|
139
|
-
if (t === 'nav') return 'navigation';
|
|
140
|
-
if (t === 'main') return 'main';
|
|
141
|
-
if (t === 'header') return hasSectioningAncestor(el) ? null : 'banner';
|
|
142
|
-
if (t === 'footer') return hasSectioningAncestor(el) ? null : 'contentinfo';
|
|
143
|
-
if (t === 'form') return 'form';
|
|
144
|
-
if (t === 'img') return 'img';
|
|
145
|
-
return t;
|
|
146
|
-
}
|
|
147
|
-
function accName(el) {
|
|
148
|
-
const aria = el.getAttribute('aria-label');
|
|
149
|
-
if (aria) return aria.trim();
|
|
150
|
-
const labelledby = el.getAttribute('aria-labelledby');
|
|
151
|
-
if (labelledby) {
|
|
152
|
-
const ref = document.getElementById(labelledby);
|
|
153
|
-
if (ref) return (ref.textContent || '').trim();
|
|
154
|
-
}
|
|
155
|
-
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') {
|
|
156
|
-
if (el.id) {
|
|
157
|
-
const lbl = document.querySelector('label[for=' + JSON.stringify(el.id) + ']');
|
|
158
|
-
if (lbl) return (lbl.textContent || '').trim();
|
|
159
|
-
}
|
|
160
|
-
const parentLabel = el.closest('label');
|
|
161
|
-
if (parentLabel) return (parentLabel.textContent || '').trim();
|
|
162
|
-
const placeholder = el.getAttribute('placeholder');
|
|
163
|
-
if (placeholder) return placeholder.trim();
|
|
164
|
-
}
|
|
165
|
-
if (el.tagName === 'IMG') {
|
|
166
|
-
const alt = el.getAttribute('alt');
|
|
167
|
-
if (alt) return alt.trim();
|
|
168
|
-
}
|
|
169
|
-
const text = (el.textContent || '').trim().replace(/\\s+/g, ' ');
|
|
170
|
-
return text;
|
|
171
|
-
}
|
|
327
|
+
${PAGE_SEMANTICS_JS}
|
|
172
328
|
function locatorHint(el) {
|
|
173
329
|
if (el.id) return '#' + el.id;
|
|
174
330
|
const testid = el.getAttribute('data-testid') || el.getAttribute('data-test-id');
|
|
@@ -177,23 +333,25 @@ function locatorHint(el) {
|
|
|
177
333
|
if (name) return el.tagName.toLowerCase() + '[name=' + JSON.stringify(name) + ']';
|
|
178
334
|
return null;
|
|
179
335
|
}
|
|
336
|
+
const reg = registry();
|
|
180
337
|
const sel = 'a,button,input,select,textarea,h1,h2,h3,h4,h5,h6,[role],nav,main,header,footer,form,img,label';
|
|
181
|
-
// Wipe stale refs from a previous snapshot so numbering is deterministic.
|
|
182
|
-
const stale = document.querySelectorAll('[data-craftdriver-ref]');
|
|
183
|
-
for (let i = 0; i < stale.length; i++) stale[i].removeAttribute('data-craftdriver-ref');
|
|
184
338
|
const out = [];
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (!visible(el))
|
|
189
|
-
|
|
190
|
-
|
|
339
|
+
const refs = [];
|
|
340
|
+
const seen = new WeakSet();
|
|
341
|
+
function emit(el, depth) {
|
|
342
|
+
if (out.length >= MAX_NODES || !visible(el)) return;
|
|
343
|
+
// A <header>/<footer> inside sectioning content is not a landmark, and
|
|
344
|
+
// listing it as a bare tag would be pure noise. Everything else keeps a
|
|
345
|
+
// line even without an ARIA role, so controls stay addressable by ref.
|
|
346
|
+
const tag = el.tagName.toLowerCase();
|
|
347
|
+
if (!ariaRole(el) && (tag === 'header' || tag === 'footer')) return;
|
|
348
|
+
const role = displayRole(el);
|
|
191
349
|
let name = accName(el);
|
|
192
350
|
if (name.length > MAX_NAME) name = name.slice(0, MAX_NAME - 1) + '…';
|
|
193
351
|
const hint = locatorHint(el);
|
|
194
|
-
const ref =
|
|
195
|
-
|
|
196
|
-
let line = ref + ': ' + role;
|
|
352
|
+
const ref = refFor(reg, el);
|
|
353
|
+
refs.push(ref);
|
|
354
|
+
let line = ' '.repeat(depth) + ref + ': ' + role;
|
|
197
355
|
if (name) line += ' "' + name + '"';
|
|
198
356
|
if (hint) line += ' ' + hint;
|
|
199
357
|
// Annotate disabled/checked state, agents need it.
|
|
@@ -201,6 +359,35 @@ for (let i = 0; i < nodes.length && out.length < MAX_NODES; i++) {
|
|
|
201
359
|
if (el.checked) line += ' (checked)';
|
|
202
360
|
out.push(line);
|
|
203
361
|
}
|
|
204
|
-
|
|
362
|
+
function visitContainer(container, depth) {
|
|
363
|
+
const children = container && container.children ? Array.from(container.children) : [];
|
|
364
|
+
for (let i = 0; i < children.length && out.length < MAX_NODES; i++) {
|
|
365
|
+
visitElement(children[i], depth);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function visitElement(el, depth) {
|
|
369
|
+
if (!el || seen.has(el) || out.length >= MAX_NODES) return;
|
|
370
|
+
seen.add(el);
|
|
371
|
+
const openRoot = el.shadowRoot || null;
|
|
372
|
+
if (el.matches(sel) || openRoot) emit(el, depth);
|
|
373
|
+
if (openRoot) {
|
|
374
|
+
if (out.length < MAX_NODES) out.push(' '.repeat(depth + 1) + '#shadow-root (open)');
|
|
375
|
+
visitContainer(openRoot, depth + 2);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (el.tagName === 'SLOT') {
|
|
379
|
+
const assigned = el.assignedElements ? el.assignedElements({ flatten: true }) : [];
|
|
380
|
+
if (assigned.length) {
|
|
381
|
+
for (let i = 0; i < assigned.length && out.length < MAX_NODES; i++) {
|
|
382
|
+
visitElement(assigned[i], depth);
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
visitContainer(el, depth);
|
|
388
|
+
}
|
|
389
|
+
visitContainer(document, 0);
|
|
390
|
+
return { lines: out, refs: refs, documentId: reg.doc, nextRef: reg.next };
|
|
205
391
|
`;
|
|
392
|
+
}
|
|
206
393
|
//# sourceMappingURL=snapshot.js.map
|
package/dist/cli/snapshot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/cli/snapshot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/cli/snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { SessionState } from '../lib/bidi/types.js';
|
|
2
|
+
import { prepareTempPath, commitFile, discardTempFile } from './artifactPaths.js';
|
|
3
|
+
/** Owned root for state files, overridable for tests and CI sandboxes. */
|
|
4
|
+
export declare function stateRoot(env?: NodeJS.ProcessEnv): string;
|
|
5
|
+
/** Longest accepted state name. Kept exported for the error-message tests. */
|
|
6
|
+
export declare const MAX_STATE_NAME = 64;
|
|
7
|
+
/**
|
|
8
|
+
* Normalize and validate an untrusted state name.
|
|
9
|
+
*
|
|
10
|
+
* The rule is shared with every other CLI-owned artifact — see
|
|
11
|
+
* `artifactPaths.ts` — so a name that is safe as a path component is safe for
|
|
12
|
+
* all of them, and there is only one place to get it wrong.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateStateName(raw: unknown): string;
|
|
15
|
+
/**
|
|
16
|
+
* Full path for a state name, guaranteed to sit inside the owned root.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveStatePath(name: string, env?: NodeJS.ProcessEnv): Promise<string>;
|
|
19
|
+
/** @see prepareTempPath */
|
|
20
|
+
export declare const prepareTempStatePath: typeof prepareTempPath;
|
|
21
|
+
/** @see commitFile */
|
|
22
|
+
export declare const commitStateFile: typeof commitFile;
|
|
23
|
+
/** @see discardTempFile */
|
|
24
|
+
export declare const discardTempStateFile: typeof discardTempFile;
|
|
25
|
+
/**
|
|
26
|
+
* Read and structurally validate a state file.
|
|
27
|
+
*
|
|
28
|
+
* The file is JSON that will be fed back into a browser, so a malformed or
|
|
29
|
+
* hand-edited one should fail here with a clear message rather than deep
|
|
30
|
+
* inside cookie restoration.
|
|
31
|
+
*/
|
|
32
|
+
export declare function readStateFile(target: string, name: string): Promise<SessionState>;
|
|
33
|
+
/** Origins whose storage a state file carries. */
|
|
34
|
+
export declare function stateOrigins(state: SessionState): string[];
|
|
35
|
+
export interface StateSummary {
|
|
36
|
+
cookies: number;
|
|
37
|
+
origins: string[];
|
|
38
|
+
storageKeys: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reportable shape of a state file: counts and origins, never values.
|
|
42
|
+
*
|
|
43
|
+
* Cookie names and storage keys are omitted too. They are not secrets in the
|
|
44
|
+
* way values are, but they are unnecessary to the caller and land in agent
|
|
45
|
+
* transcripts and CI logs, so the useful-to-leaky trade is not worth it.
|
|
46
|
+
*/
|
|
47
|
+
export declare function summarizeState(state: SessionState): StateSummary;
|
|
48
|
+
/** Saved state names under the root, without the `.json` suffix. */
|
|
49
|
+
export declare function listStateNames(env?: NodeJS.ProcessEnv): Promise<string[]>;
|
|
50
|
+
//# sourceMappingURL=stateStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateStore.d.ts","sourceRoot":"","sources":["../../src/cli/stateStore.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,EAIL,eAAe,EACf,UAAU,EACV,eAAe,EAEhB,MAAM,oBAAoB,CAAC;AAE5B,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAEtE;AAED,8EAA8E;AAC9E,eAAO,MAAM,cAAc,KAAoB,CAAC;AAEhD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAO7F;AAED,2BAA2B;AAC3B,eAAO,MAAM,oBAAoB,wBAAkB,CAAC;AACpD,sBAAsB;AACtB,eAAO,MAAM,eAAe,mBAAa,CAAC;AAC1C,2BAA2B;AAC3B,eAAO,MAAM,oBAAoB,wBAAkB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAevF;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,CAK1D;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAYhE;AAED,oEAAoE;AACpE,wBAAsB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW/E"}
|