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.
Files changed (234) hide show
  1. package/CHANGELOG.md +25 -12
  2. package/README.md +16 -6
  3. package/dist/cli/agentSession.d.ts +62 -0
  4. package/dist/cli/agentSession.d.ts.map +1 -0
  5. package/dist/cli/agentSession.js +100 -0
  6. package/dist/cli/agentSession.js.map +1 -0
  7. package/dist/cli/artifactPaths.d.ts +42 -0
  8. package/dist/cli/artifactPaths.d.ts.map +1 -0
  9. package/dist/cli/artifactPaths.js +144 -0
  10. package/dist/cli/artifactPaths.js.map +1 -0
  11. package/dist/cli/bounds.d.ts +88 -0
  12. package/dist/cli/bounds.d.ts.map +1 -0
  13. package/dist/cli/bounds.js +128 -0
  14. package/dist/cli/bounds.js.map +1 -0
  15. package/dist/cli/client.d.ts +3 -0
  16. package/dist/cli/client.d.ts.map +1 -1
  17. package/dist/cli/client.js +24 -8
  18. package/dist/cli/client.js.map +1 -1
  19. package/dist/cli/daemon.d.ts +3 -0
  20. package/dist/cli/daemon.d.ts.map +1 -1
  21. package/dist/cli/daemon.js +152 -39
  22. package/dist/cli/daemon.js.map +1 -1
  23. package/dist/cli/defaults.d.ts +23 -3
  24. package/dist/cli/defaults.d.ts.map +1 -1
  25. package/dist/cli/defaults.js +50 -5
  26. package/dist/cli/defaults.js.map +1 -1
  27. package/dist/cli/dispatcher.d.ts +54 -11
  28. package/dist/cli/dispatcher.d.ts.map +1 -1
  29. package/dist/cli/dispatcher.js +851 -54
  30. package/dist/cli/dispatcher.js.map +1 -1
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +305 -56
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/init.d.ts +14 -14
  35. package/dist/cli/init.d.ts.map +1 -1
  36. package/dist/cli/init.js +328 -180
  37. package/dist/cli/init.js.map +1 -1
  38. package/dist/cli/journal.d.ts +153 -0
  39. package/dist/cli/journal.d.ts.map +1 -0
  40. package/dist/cli/journal.js +252 -0
  41. package/dist/cli/journal.js.map +1 -0
  42. package/dist/cli/lineReader.d.ts +33 -0
  43. package/dist/cli/lineReader.d.ts.map +1 -0
  44. package/dist/cli/lineReader.js +66 -0
  45. package/dist/cli/lineReader.js.map +1 -0
  46. package/dist/cli/locatorCandidates.d.ts +44 -0
  47. package/dist/cli/locatorCandidates.d.ts.map +1 -0
  48. package/dist/cli/locatorCandidates.js +190 -0
  49. package/dist/cli/locatorCandidates.js.map +1 -0
  50. package/dist/cli/mcp/artifacts.d.ts +39 -1
  51. package/dist/cli/mcp/artifacts.d.ts.map +1 -1
  52. package/dist/cli/mcp/artifacts.js +104 -9
  53. package/dist/cli/mcp/artifacts.js.map +1 -1
  54. package/dist/cli/mcp/bounds.d.ts +65 -0
  55. package/dist/cli/mcp/bounds.d.ts.map +1 -0
  56. package/dist/cli/mcp/bounds.js +124 -0
  57. package/dist/cli/mcp/bounds.js.map +1 -0
  58. package/dist/cli/mcp/params.d.ts +38 -0
  59. package/dist/cli/mcp/params.d.ts.map +1 -0
  60. package/dist/cli/mcp/params.js +174 -0
  61. package/dist/cli/mcp/params.js.map +1 -0
  62. package/dist/cli/mcp/server.d.ts +94 -16
  63. package/dist/cli/mcp/server.d.ts.map +1 -1
  64. package/dist/cli/mcp/server.js +304 -150
  65. package/dist/cli/mcp/server.js.map +1 -1
  66. package/dist/cli/mcp/tools.d.ts +62 -17
  67. package/dist/cli/mcp/tools.d.ts.map +1 -1
  68. package/dist/cli/mcp/tools.js +373 -158
  69. package/dist/cli/mcp/tools.js.map +1 -1
  70. package/dist/cli/pageSemantics.d.ts +6 -0
  71. package/dist/cli/pageSemantics.d.ts.map +1 -0
  72. package/dist/cli/pageSemantics.js +6 -0
  73. package/dist/cli/pageSemantics.js.map +1 -0
  74. package/dist/cli/parseArgs.d.ts +6 -18
  75. package/dist/cli/parseArgs.d.ts.map +1 -1
  76. package/dist/cli/parseArgs.js +689 -45
  77. package/dist/cli/parseArgs.js.map +1 -1
  78. package/dist/cli/protocol.d.ts +8 -0
  79. package/dist/cli/protocol.d.ts.map +1 -1
  80. package/dist/cli/selector.d.ts +3 -3
  81. package/dist/cli/selector.d.ts.map +1 -1
  82. package/dist/cli/selector.js +6 -9
  83. package/dist/cli/selector.js.map +1 -1
  84. package/dist/cli/sessionRegistry.d.ts +42 -0
  85. package/dist/cli/sessionRegistry.d.ts.map +1 -0
  86. package/dist/cli/sessionRegistry.js +99 -0
  87. package/dist/cli/sessionRegistry.js.map +1 -0
  88. package/dist/cli/snapshot.d.ts +100 -19
  89. package/dist/cli/snapshot.d.ts.map +1 -1
  90. package/dist/cli/snapshot.js +295 -108
  91. package/dist/cli/snapshot.js.map +1 -1
  92. package/dist/cli/stateStore.d.ts +50 -0
  93. package/dist/cli/stateStore.d.ts.map +1 -0
  94. package/dist/cli/stateStore.js +122 -0
  95. package/dist/cli/stateStore.js.map +1 -0
  96. package/dist/index.d.ts +3 -2
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +1 -0
  99. package/dist/index.js.map +1 -1
  100. package/dist/lib/a11y.d.ts +6 -2
  101. package/dist/lib/a11y.d.ts.map +1 -1
  102. package/dist/lib/a11y.js +3 -1
  103. package/dist/lib/a11y.js.map +1 -1
  104. package/dist/lib/bidi/evaluate.d.ts +11 -0
  105. package/dist/lib/bidi/evaluate.d.ts.map +1 -0
  106. package/dist/lib/bidi/evaluate.js +25 -0
  107. package/dist/lib/bidi/evaluate.js.map +1 -0
  108. package/dist/lib/bidi/index.d.ts +1 -1
  109. package/dist/lib/bidi/index.d.ts.map +1 -1
  110. package/dist/lib/bidi/index.js +2 -2
  111. package/dist/lib/bidi/index.js.map +1 -1
  112. package/dist/lib/bidi/logs.d.ts +3 -0
  113. package/dist/lib/bidi/logs.d.ts.map +1 -1
  114. package/dist/lib/bidi/logs.js +7 -0
  115. package/dist/lib/bidi/logs.js.map +1 -1
  116. package/dist/lib/bidi/network.d.ts +14 -1
  117. package/dist/lib/bidi/network.d.ts.map +1 -1
  118. package/dist/lib/bidi/network.js +45 -7
  119. package/dist/lib/bidi/network.js.map +1 -1
  120. package/dist/lib/bidi/storage.d.ts +30 -6
  121. package/dist/lib/bidi/storage.d.ts.map +1 -1
  122. package/dist/lib/bidi/storage.js +190 -45
  123. package/dist/lib/bidi/storage.js.map +1 -1
  124. package/dist/lib/bidi/types.d.ts +9 -2
  125. package/dist/lib/bidi/types.d.ts.map +1 -1
  126. package/dist/lib/browser.d.ts +7 -4
  127. package/dist/lib/browser.d.ts.map +1 -1
  128. package/dist/lib/browser.js +186 -65
  129. package/dist/lib/browser.js.map +1 -1
  130. package/dist/lib/browserContext.d.ts +58 -24
  131. package/dist/lib/browserContext.d.ts.map +1 -1
  132. package/dist/lib/browserContext.js +300 -99
  133. package/dist/lib/browserContext.js.map +1 -1
  134. package/dist/lib/by.d.ts +68 -30
  135. package/dist/lib/by.d.ts.map +1 -1
  136. package/dist/lib/by.js +54 -23
  137. package/dist/lib/by.js.map +1 -1
  138. package/dist/lib/clearFastPath.d.ts.map +1 -1
  139. package/dist/lib/clearFastPath.js +2 -0
  140. package/dist/lib/clearFastPath.js.map +1 -1
  141. package/dist/lib/clickFastPath.d.ts.map +1 -1
  142. package/dist/lib/clickFastPath.js +2 -0
  143. package/dist/lib/clickFastPath.js.map +1 -1
  144. package/dist/lib/driver.d.ts +26 -0
  145. package/dist/lib/driver.d.ts.map +1 -1
  146. package/dist/lib/driver.js +123 -4
  147. package/dist/lib/driver.js.map +1 -1
  148. package/dist/lib/elementHandle.d.ts +13 -10
  149. package/dist/lib/elementHandle.d.ts.map +1 -1
  150. package/dist/lib/elementHandle.js +119 -50
  151. package/dist/lib/elementHandle.js.map +1 -1
  152. package/dist/lib/errors.d.ts +12 -0
  153. package/dist/lib/errors.d.ts.map +1 -1
  154. package/dist/lib/errors.js +12 -0
  155. package/dist/lib/errors.js.map +1 -1
  156. package/dist/lib/expect.d.ts +10 -0
  157. package/dist/lib/expect.d.ts.map +1 -1
  158. package/dist/lib/expect.js +76 -49
  159. package/dist/lib/expect.js.map +1 -1
  160. package/dist/lib/fillFastPath.d.ts.map +1 -1
  161. package/dist/lib/fillFastPath.js +2 -0
  162. package/dist/lib/fillFastPath.js.map +1 -1
  163. package/dist/lib/frame.d.ts.map +1 -1
  164. package/dist/lib/frame.js +29 -17
  165. package/dist/lib/frame.js.map +1 -1
  166. package/dist/lib/initScript.d.ts +9 -0
  167. package/dist/lib/initScript.d.ts.map +1 -0
  168. package/dist/lib/initScript.js +14 -0
  169. package/dist/lib/initScript.js.map +1 -0
  170. package/dist/lib/locator.d.ts +21 -54
  171. package/dist/lib/locator.d.ts.map +1 -1
  172. package/dist/lib/locator.js +165 -208
  173. package/dist/lib/locator.js.map +1 -1
  174. package/dist/lib/page.d.ts +22 -0
  175. package/dist/lib/page.d.ts.map +1 -1
  176. package/dist/lib/page.js +71 -13
  177. package/dist/lib/page.js.map +1 -1
  178. package/dist/lib/pageSemantics.d.ts +42 -0
  179. package/dist/lib/pageSemantics.d.ts.map +1 -0
  180. package/dist/lib/pageSemantics.js +216 -0
  181. package/dist/lib/pageSemantics.js.map +1 -0
  182. package/dist/lib/query.d.ts +68 -0
  183. package/dist/lib/query.d.ts.map +1 -0
  184. package/dist/lib/query.js +590 -0
  185. package/dist/lib/query.js.map +1 -0
  186. package/dist/lib/secureFile.d.ts +8 -0
  187. package/dist/lib/secureFile.d.ts.map +1 -0
  188. package/dist/lib/secureFile.js +67 -0
  189. package/dist/lib/secureFile.js.map +1 -0
  190. package/dist/lib/sessionStateValidation.d.ts +35 -0
  191. package/dist/lib/sessionStateValidation.d.ts.map +1 -0
  192. package/dist/lib/sessionStateValidation.js +223 -0
  193. package/dist/lib/sessionStateValidation.js.map +1 -0
  194. package/dist/lib/shadowRoot.d.ts +21 -0
  195. package/dist/lib/shadowRoot.d.ts.map +1 -0
  196. package/dist/lib/shadowRoot.js +29 -0
  197. package/dist/lib/shadowRoot.js.map +1 -0
  198. package/dist/lib/shadowRootLocator.d.ts +31 -0
  199. package/dist/lib/shadowRootLocator.d.ts.map +1 -0
  200. package/dist/lib/shadowRootLocator.js +55 -0
  201. package/dist/lib/shadowRootLocator.js.map +1 -0
  202. package/dist/lib/timing.d.ts +2 -2
  203. package/dist/lib/timing.js +2 -2
  204. package/dist/lib/tracing.d.ts +2 -1
  205. package/dist/lib/tracing.d.ts.map +1 -1
  206. package/dist/lib/tracing.js +12 -1
  207. package/dist/lib/tracing.js.map +1 -1
  208. package/docs/accessibility.md +9 -1
  209. package/docs/agents.md +56 -61
  210. package/docs/api-reference.md +5 -1
  211. package/docs/browser-api.md +27 -7
  212. package/docs/browser-context.md +21 -15
  213. package/docs/cli.md +353 -30
  214. package/docs/error-codes.md +7 -4
  215. package/docs/getting-started.md +6 -1
  216. package/docs/index.md +1 -1
  217. package/docs/mcp.md +136 -65
  218. package/docs/public/examples/agent-actions.html +48 -0
  219. package/docs/public/examples/agent-debug.html +86 -0
  220. package/docs/public/examples/agent-late-mutation.html +18 -0
  221. package/docs/public/examples/agent-ref-shift.html +33 -0
  222. package/docs/public/examples/iframe-child.html +12 -1
  223. package/docs/public/examples/shadow-dom.html +137 -0
  224. package/docs/recipes/login-once-reuse-session.md +33 -5
  225. package/docs/selectors.md +61 -0
  226. package/docs/session-management.md +80 -12
  227. package/docs/why-craftdriver.md +1 -1
  228. package/package.json +5 -3
  229. package/skills/craftdriver/SKILL.md +59 -75
  230. package/skills/craftdriver/agents/openai.yaml +4 -0
  231. package/skills/craftdriver/cheatsheet.md +18 -4
  232. package/skills/craftdriver/cli.md +194 -68
  233. package/skills/craftdriver/patterns.md +17 -8
  234. package/skills/craftdriver/workflow.md +149 -0
@@ -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 lines = await page.evaluate(JS_SNAPSHOT);
15
- return { url, title, lines: Array.isArray(lines) ? lines : [] };
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
- * Ref numbers (`e1`, `e2`, …) re-allocate on every snapshot so they
30
- * can't be used to drive the diff directly we strip them before
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
- if (prev.url !== next.url) {
45
- // Page changed full snapshot.
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
- const prevBodies = new Set(prev.lines.map(strip));
50
- const nextBodies = new Set(next.lines.map(strip));
51
- const removed = prev.lines.filter((l) => !nextBodies.has(strip(l))).map((l) => `- ${l}`);
52
- const added = next.lines.filter((l) => !prevBodies.has(strip(l))).map((l) => `+ ${l}`);
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. Walk a curated set of selectors (interactive + structural).
77
- * 2. Skip nodes outside the viewport-ish bounds and zero-sized nodes.
78
- * 3. For each, compute role + accessible name + a stable selector hint
79
- * (prefer #id, then [data-testid], then [name], then tag+text).
80
- * 4. Cap output at MAX_NODES so the snapshot stays bounded regardless
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
- const JS_SNAPSHOT = `
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
- function firstRoleToken(el) {
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 nodes = document.querySelectorAll(sel);
186
- for (let i = 0; i < nodes.length && out.length < MAX_NODES; i++) {
187
- const el = nodes[i];
188
- if (!visible(el)) continue;
189
- const role = impliedRole(el);
190
- if (!role) continue;
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 = 'e' + (out.length + 1);
195
- el.setAttribute('data-craftdriver-ref', ref);
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
- return out;
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
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/cli/snapshot.ts"],"names":[],"mappings":"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"}
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"}