multiclone 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/README.md +89 -2
  2. package/agents/section-builder.md +37 -0
  3. package/commands/multiclone.md +20 -0
  4. package/dist/adapters/cli.js +120 -0
  5. package/dist/adapters/cli.js.map +1 -0
  6. package/dist/adapters/cliArgs.js +63 -0
  7. package/dist/adapters/cliArgs.js.map +1 -0
  8. package/dist/adapters/mcp.js +83 -0
  9. package/dist/adapters/mcp.js.map +1 -0
  10. package/dist/analyze/analyze.js +85 -0
  11. package/dist/analyze/analyze.js.map +1 -0
  12. package/dist/analyze/animations.js +340 -0
  13. package/dist/analyze/animations.js.map +1 -0
  14. package/dist/analyze/ast.js +182 -0
  15. package/dist/analyze/ast.js.map +1 -0
  16. package/dist/analyze/chunks.js +160 -0
  17. package/dist/analyze/chunks.js.map +1 -0
  18. package/dist/analyze/jobs/code.js +117 -0
  19. package/dist/analyze/jobs/code.js.map +1 -0
  20. package/dist/analyze/jobs/sections.js +356 -0
  21. package/dist/analyze/jobs/sections.js.map +1 -0
  22. package/dist/analyze/pretty.js +20 -0
  23. package/dist/analyze/pretty.js.map +1 -0
  24. package/dist/analyze/report.js +101 -0
  25. package/dist/analyze/report.js.map +1 -0
  26. package/dist/analyze/worker.js +71 -0
  27. package/dist/analyze/worker.js.map +1 -0
  28. package/dist/capture/capture.js +231 -0
  29. package/dist/capture/capture.js.map +1 -0
  30. package/dist/capture/collectors/a11y.js +92 -0
  31. package/dist/capture/collectors/a11y.js.map +1 -0
  32. package/dist/capture/collectors/animations.js +113 -0
  33. package/dist/capture/collectors/animations.js.map +1 -0
  34. package/dist/capture/collectors/breakpoints.js +31 -0
  35. package/dist/capture/collectors/breakpoints.js.map +1 -0
  36. package/dist/capture/collectors/consent.js +39 -0
  37. package/dist/capture/collectors/consent.js.map +1 -0
  38. package/dist/capture/collectors/coverage.js +119 -0
  39. package/dist/capture/collectors/coverage.js.map +1 -0
  40. package/dist/capture/collectors/dom.js +16 -0
  41. package/dist/capture/collectors/dom.js.map +1 -0
  42. package/dist/capture/collectors/fonts.js +154 -0
  43. package/dist/capture/collectors/fonts.js.map +1 -0
  44. package/dist/capture/collectors/frames.js +93 -0
  45. package/dist/capture/collectors/frames.js.map +1 -0
  46. package/dist/capture/collectors/index.js +50 -0
  47. package/dist/capture/collectors/index.js.map +1 -0
  48. package/dist/capture/collectors/interactions.js +208 -0
  49. package/dist/capture/collectors/interactions.js.map +1 -0
  50. package/dist/capture/collectors/intro.js +84 -0
  51. package/dist/capture/collectors/intro.js.map +1 -0
  52. package/dist/capture/collectors/listeners.js +84 -0
  53. package/dist/capture/collectors/listeners.js.map +1 -0
  54. package/dist/capture/collectors/motion.js +86 -0
  55. package/dist/capture/collectors/motion.js.map +1 -0
  56. package/dist/capture/collectors/network.js +288 -0
  57. package/dist/capture/collectors/network.js.map +1 -0
  58. package/dist/capture/collectors/probes.js +33 -0
  59. package/dist/capture/collectors/probes.js.map +1 -0
  60. package/dist/capture/collectors/runtime.js +38 -0
  61. package/dist/capture/collectors/runtime.js.map +1 -0
  62. package/dist/capture/collectors/scripts.js +98 -0
  63. package/dist/capture/collectors/scripts.js.map +1 -0
  64. package/dist/capture/collectors/sections.js +17 -0
  65. package/dist/capture/collectors/sections.js.map +1 -0
  66. package/dist/capture/collectors/shots.js +38 -0
  67. package/dist/capture/collectors/shots.js.map +1 -0
  68. package/dist/capture/collectors/snapshot.js +47 -0
  69. package/dist/capture/collectors/snapshot.js.map +1 -0
  70. package/dist/capture/collectors/sourcemaps.js +153 -0
  71. package/dist/capture/collectors/sourcemaps.js.map +1 -0
  72. package/dist/capture/collectors/storage.js +72 -0
  73. package/dist/capture/collectors/storage.js.map +1 -0
  74. package/dist/capture/context.js +35 -0
  75. package/dist/capture/context.js.map +1 -0
  76. package/dist/capture/hooks/candidates.js +104 -0
  77. package/dist/capture/hooks/candidates.js.map +1 -0
  78. package/dist/capture/hooks/consent.js +82 -0
  79. package/dist/capture/hooks/consent.js.map +1 -0
  80. package/dist/capture/hooks/recorders.js +507 -0
  81. package/dist/capture/hooks/recorders.js.map +1 -0
  82. package/dist/capture/hooks/sections.js +62 -0
  83. package/dist/capture/hooks/sections.js.map +1 -0
  84. package/dist/capture/hooks/styles.js +86 -0
  85. package/dist/capture/hooks/styles.js.map +1 -0
  86. package/dist/capture/probes.js +491 -0
  87. package/dist/capture/probes.js.map +1 -0
  88. package/dist/capture/report.js +115 -0
  89. package/dist/capture/report.js.map +1 -0
  90. package/dist/compare/compare.js +391 -0
  91. package/dist/compare/compare.js.map +1 -0
  92. package/dist/compare/image.js +105 -0
  93. package/dist/compare/image.js.map +1 -0
  94. package/dist/compare/probes.js +129 -0
  95. package/dist/compare/probes.js.map +1 -0
  96. package/dist/compare/report.js +99 -0
  97. package/dist/compare/report.js.map +1 -0
  98. package/dist/compare/session.js +133 -0
  99. package/dist/compare/session.js.map +1 -0
  100. package/dist/compare/shoot.js +44 -0
  101. package/dist/compare/shoot.js.map +1 -0
  102. package/dist/contracts/catalog.js +24 -0
  103. package/dist/contracts/catalog.js.map +1 -0
  104. package/dist/contracts/context.js +19 -0
  105. package/dist/contracts/context.js.map +1 -0
  106. package/dist/contracts/fields.js +16 -0
  107. package/dist/contracts/fields.js.map +1 -0
  108. package/dist/contracts/tools/analyze.js +38 -0
  109. package/dist/contracts/tools/analyze.js.map +1 -0
  110. package/dist/contracts/tools/capture.js +71 -0
  111. package/dist/contracts/tools/capture.js.map +1 -0
  112. package/dist/contracts/tools/compare.js +142 -0
  113. package/dist/contracts/tools/compare.js.map +1 -0
  114. package/dist/contracts/tools/doctor.js +23 -0
  115. package/dist/contracts/tools/doctor.js.map +1 -0
  116. package/dist/contracts/tools/inspect.js +41 -0
  117. package/dist/contracts/tools/inspect.js.map +1 -0
  118. package/dist/contracts/tools/method.js +56 -0
  119. package/dist/contracts/tools/method.js.map +1 -0
  120. package/dist/contracts/tools/mirror.js +53 -0
  121. package/dist/contracts/tools/mirror.js.map +1 -0
  122. package/dist/contracts/tools/project.js +61 -0
  123. package/dist/contracts/tools/project.js.map +1 -0
  124. package/dist/contracts/types.js +20 -0
  125. package/dist/contracts/types.js.map +1 -0
  126. package/dist/core/cdp.js +146 -0
  127. package/dist/core/cdp.js.map +1 -0
  128. package/dist/core/chrome.js +185 -0
  129. package/dist/core/chrome.js.map +1 -0
  130. package/dist/core/files.js +31 -0
  131. package/dist/core/files.js.map +1 -0
  132. package/dist/core/inpage.js +24 -0
  133. package/dist/core/inpage.js.map +1 -0
  134. package/dist/core/log.js +10 -0
  135. package/dist/core/log.js.map +1 -0
  136. package/dist/core/page.js +248 -0
  137. package/dist/core/page.js.map +1 -0
  138. package/dist/doctor.js +37 -0
  139. package/dist/doctor.js.map +1 -0
  140. package/dist/domain/breakpoints.js +47 -0
  141. package/dist/domain/breakpoints.js.map +1 -0
  142. package/dist/domain/budget.js +38 -0
  143. package/dist/domain/budget.js.map +1 -0
  144. package/dist/domain/compare.js +138 -0
  145. package/dist/domain/compare.js.map +1 -0
  146. package/dist/domain/coverage.js +96 -0
  147. package/dist/domain/coverage.js.map +1 -0
  148. package/dist/domain/motion.js +142 -0
  149. package/dist/domain/motion.js.map +1 -0
  150. package/dist/domain/paths.js +55 -0
  151. package/dist/domain/paths.js.map +1 -0
  152. package/dist/domain/result.js +38 -0
  153. package/dist/domain/result.js.map +1 -0
  154. package/dist/domain/routes.js +160 -0
  155. package/dist/domain/routes.js.map +1 -0
  156. package/dist/domain/sections.js +94 -0
  157. package/dist/domain/sections.js.map +1 -0
  158. package/dist/domain/snapshot.js +268 -0
  159. package/dist/domain/snapshot.js.map +1 -0
  160. package/dist/domain/sourcemap.js +95 -0
  161. package/dist/domain/sourcemap.js.map +1 -0
  162. package/dist/domain/stack.js +342 -0
  163. package/dist/domain/stack.js.map +1 -0
  164. package/dist/domain/tokens.js +183 -0
  165. package/dist/domain/tokens.js.map +1 -0
  166. package/dist/domain/types.js +2 -0
  167. package/dist/domain/types.js.map +1 -0
  168. package/dist/domain/viewports.js +45 -0
  169. package/dist/domain/viewports.js.map +1 -0
  170. package/dist/mirror/rewrite.js +56 -0
  171. package/dist/mirror/rewrite.js.map +1 -0
  172. package/dist/mirror/server.js +191 -0
  173. package/dist/mirror/server.js.map +1 -0
  174. package/dist/mirror/store.js +86 -0
  175. package/dist/mirror/store.js.map +1 -0
  176. package/dist/project/assets.js +48 -0
  177. package/dist/project/assets.js.map +1 -0
  178. package/dist/project/method.js +152 -0
  179. package/dist/project/method.js.map +1 -0
  180. package/dist/project/scaffold.js +140 -0
  181. package/dist/project/scaffold.js.map +1 -0
  182. package/dist/store/inspect.js +212 -0
  183. package/dist/store/inspect.js.map +1 -0
  184. package/dist/store/run.js +112 -0
  185. package/dist/store/run.js.map +1 -0
  186. package/dist/store/summary.js +69 -0
  187. package/dist/store/summary.js.map +1 -0
  188. package/dist/version.js +2 -0
  189. package/dist/version.js.map +1 -0
  190. package/package.json +66 -4
  191. package/skills/multiclone/SKILL.md +102 -0
  192. package/skills/multiclone/references/capture-format.md +73 -0
  193. package/skills/multiclone/references/fidelity.md +54 -0
  194. package/skills/multiclone/references/quality.md +47 -0
  195. package/skills/multiclone/references/stacks.md +156 -0
  196. package/templates/react-vite/.prettierignore +5 -0
  197. package/templates/react-vite/.prettierrc.json +6 -0
  198. package/templates/react-vite/eslint.config.js +38 -0
  199. package/templates/react-vite/gitignore +4 -0
  200. package/templates/react-vite/index.html +12 -0
  201. package/templates/react-vite/package.json +36 -0
  202. package/templates/react-vite/src/app/App.tsx +10 -0
  203. package/templates/react-vite/src/app/Page.tsx +4 -0
  204. package/templates/react-vite/src/components/README.md +7 -0
  205. package/templates/react-vite/src/content/README.md +2 -0
  206. package/templates/react-vite/src/hooks/useActiveSection.ts +37 -0
  207. package/templates/react-vite/src/hooks/useInView.ts +45 -0
  208. package/templates/react-vite/src/hooks/useMediaQuery.ts +15 -0
  209. package/templates/react-vite/src/lib/cn.ts +4 -0
  210. package/templates/react-vite/src/lib/math.ts +21 -0
  211. package/templates/react-vite/src/lib/mc.ts +13 -0
  212. package/templates/react-vite/src/lib/media.ts +4 -0
  213. package/templates/react-vite/src/main.tsx +13 -0
  214. package/templates/react-vite/src/providers/SmoothScroll.tsx +24 -0
  215. package/templates/react-vite/src/styles/base.css +42 -0
  216. package/templates/react-vite/src/styles/fonts.css +1 -0
  217. package/templates/react-vite/src/styles/index.css +3 -0
  218. package/templates/react-vite/src/styles/tokens.css +3 -0
  219. package/templates/react-vite/src/vite-env.d.ts +1 -0
  220. package/templates/react-vite/tsconfig.app.json +29 -0
  221. package/templates/react-vite/tsconfig.json +4 -0
  222. package/templates/react-vite/tsconfig.node.json +19 -0
  223. package/templates/react-vite/vite.config.ts +37 -0
@@ -0,0 +1,146 @@
1
+ export class CdpError extends Error {
2
+ method;
3
+ constructor(method, message) {
4
+ super(`${method}: ${message}`);
5
+ this.method = method;
6
+ this.name = 'CdpError';
7
+ }
8
+ }
9
+ /** Minimal Chrome DevTools Protocol client over the global WebSocket, with flat sessions. */
10
+ export class CdpClient {
11
+ socket;
12
+ sessionId = undefined;
13
+ nextId = 0;
14
+ pending = new Map();
15
+ listeners = new Map();
16
+ closed = false;
17
+ /** Bytes received over the socket, for protocol budgets. */
18
+ receivedBytes = 0;
19
+ constructor(socket) {
20
+ this.socket = socket;
21
+ socket.addEventListener('message', (event) => {
22
+ this.receivedBytes += event.data.length;
23
+ this.dispatch(JSON.parse(event.data));
24
+ });
25
+ socket.addEventListener('close', () => {
26
+ this.closed = true;
27
+ for (const [id, entry] of this.pending) {
28
+ clearTimeout(entry.timer);
29
+ entry.reject(new CdpError(entry.method, 'connection closed'));
30
+ this.pending.delete(id);
31
+ }
32
+ });
33
+ }
34
+ static async connect(url) {
35
+ const socket = new WebSocket(url);
36
+ await new Promise((resolve, reject) => {
37
+ socket.addEventListener('open', () => {
38
+ resolve();
39
+ });
40
+ socket.addEventListener('error', () => {
41
+ reject(new Error(`Could not connect to Chrome at ${url}`));
42
+ });
43
+ });
44
+ return new CdpClient(socket);
45
+ }
46
+ get isClosed() {
47
+ return this.closed;
48
+ }
49
+ send(method, params = {}, timeoutMs = 60_000) {
50
+ return this.sendTo(method, params, { timeoutMs });
51
+ }
52
+ sendTo(method, params, options) {
53
+ if (this.closed)
54
+ return Promise.reject(new CdpError(method, 'connection closed'));
55
+ const id = ++this.nextId;
56
+ const timeoutMs = options.timeoutMs ?? 60_000;
57
+ return new Promise((resolve, reject) => {
58
+ const timer = setTimeout(() => {
59
+ this.pending.delete(id);
60
+ reject(new CdpError(method, `timed out after ${String(timeoutMs)}ms`));
61
+ }, timeoutMs);
62
+ this.pending.set(id, {
63
+ method,
64
+ resolve: (value) => {
65
+ resolve(value);
66
+ },
67
+ reject,
68
+ timer,
69
+ });
70
+ const message = { id, method, params };
71
+ if (options.sessionId !== undefined)
72
+ message.sessionId = options.sessionId;
73
+ this.socket.send(JSON.stringify(message));
74
+ });
75
+ }
76
+ /** Listens on the root session only. */
77
+ on(method, listener) {
78
+ return this.onAny(method, (params, sessionId) => {
79
+ if (sessionId === undefined)
80
+ listener(params);
81
+ });
82
+ }
83
+ /** Listens across the root and every attached child session. */
84
+ onAny(method, listener) {
85
+ let set = this.listeners.get(method);
86
+ if (!set) {
87
+ set = new Set();
88
+ this.listeners.set(method, set);
89
+ }
90
+ set.add(listener);
91
+ return () => set.delete(listener);
92
+ }
93
+ /** A session view that routes commands and events to one attached child target. */
94
+ session(sessionId) {
95
+ return {
96
+ sessionId,
97
+ send: (method, params = {}, timeoutMs) => this.sendTo(method, params, {
98
+ sessionId,
99
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
100
+ }),
101
+ on: (method, listener) => this.onAny(method, (params, from) => {
102
+ if (from === sessionId)
103
+ listener(params);
104
+ }),
105
+ };
106
+ }
107
+ waitFor(method, timeoutMs) {
108
+ return new Promise((resolve) => {
109
+ const timer = setTimeout(() => {
110
+ off();
111
+ resolve(false);
112
+ }, timeoutMs);
113
+ const off = this.on(method, () => {
114
+ clearTimeout(timer);
115
+ off();
116
+ resolve(true);
117
+ });
118
+ });
119
+ }
120
+ close() {
121
+ this.closed = true;
122
+ this.socket.close();
123
+ }
124
+ dispatch(message) {
125
+ if (message.id !== undefined) {
126
+ const entry = this.pending.get(message.id);
127
+ if (!entry)
128
+ return;
129
+ this.pending.delete(message.id);
130
+ clearTimeout(entry.timer);
131
+ if (message.error)
132
+ entry.reject(new CdpError(entry.method, message.error.message));
133
+ else
134
+ entry.resolve(message.result);
135
+ return;
136
+ }
137
+ if (!message.method)
138
+ return;
139
+ const set = this.listeners.get(message.method);
140
+ if (!set)
141
+ return;
142
+ for (const listener of set)
143
+ listener(message.params ?? {}, message.sessionId);
144
+ }
145
+ }
146
+ //# sourceMappingURL=cdp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp.js","sourceRoot":"","sources":["../../src/core/cdp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,185 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import { join, resolve } from 'node:path';
6
+ import { mcError, TaggedError } from '../domain/result.js';
7
+ import { CdpClient } from './cdp.js';
8
+ import { sleep } from './files.js';
9
+ const CANDIDATES = {
10
+ darwin: [
11
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
12
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
13
+ '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
14
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
15
+ '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
16
+ ],
17
+ linux: [
18
+ '/usr/bin/google-chrome',
19
+ '/usr/bin/google-chrome-stable',
20
+ '/usr/bin/chromium',
21
+ '/usr/bin/chromium-browser',
22
+ '/snap/bin/chromium',
23
+ '/usr/bin/microsoft-edge',
24
+ ],
25
+ win32: [
26
+ 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
27
+ 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
28
+ 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
29
+ ],
30
+ };
31
+ export function findChrome() {
32
+ const override = process.env.MULTICLONE_CHROME ?? process.env.CHROME_PATH;
33
+ if (override)
34
+ return existsSync(override) ? override : null;
35
+ return (CANDIDATES[process.platform] ?? []).find((path) => existsSync(path)) ?? null;
36
+ }
37
+ /** Chrome flags that keep rendering faithful and hide the obvious automation tells. */
38
+ export function chromeArgs(profile, options) {
39
+ const size = options.windowSize ?? { width: 1440, height: 900 };
40
+ const args = [
41
+ `--user-data-dir=${profile}`,
42
+ '--remote-debugging-port=0',
43
+ '--no-first-run',
44
+ '--no-default-browser-check',
45
+ '--disable-background-timer-throttling',
46
+ '--disable-renderer-backgrounding',
47
+ '--disable-backgrounding-occluded-windows',
48
+ '--disable-blink-features=AutomationControlled',
49
+ '--disable-features=Translate,MediaRouter,OptimizationHints',
50
+ '--hide-scrollbars',
51
+ '--mute-audio',
52
+ '--autoplay-policy=no-user-gesture-required',
53
+ // Software GL keeps WebGL/canvas sites rendering in headless mode.
54
+ '--use-angle=swiftshader',
55
+ '--enable-unsafe-swiftshader',
56
+ `--window-size=${String(size.width)},${String(size.height)}`,
57
+ ];
58
+ if (options.headless !== false)
59
+ args.unshift('--headless=new');
60
+ args.push('about:blank');
61
+ return args;
62
+ }
63
+ /** Every Chrome this process started, so exit and signal handlers can reap them. */
64
+ const live = new Set();
65
+ let hooksInstalled = false;
66
+ function killTree(child) {
67
+ if (child.pid === undefined || child.exitCode !== null)
68
+ return;
69
+ try {
70
+ // Negative pid kills the whole process group: GPU, renderer and utility helpers too.
71
+ if (process.platform === 'win32')
72
+ child.kill('SIGKILL');
73
+ else
74
+ process.kill(-child.pid, 'SIGKILL');
75
+ }
76
+ catch {
77
+ child.kill('SIGKILL');
78
+ }
79
+ }
80
+ function installExitHooks() {
81
+ if (hooksInstalled)
82
+ return;
83
+ hooksInstalled = true;
84
+ process.on('exit', () => {
85
+ for (const child of live)
86
+ killTree(child);
87
+ });
88
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
89
+ process.once(signal, () => {
90
+ for (const child of live)
91
+ killTree(child);
92
+ if (process.listenerCount(signal) === 0)
93
+ process.exit(130);
94
+ });
95
+ }
96
+ }
97
+ /** Launches a supervised Chrome (own process group, deadline, abort) and attaches to its page. */
98
+ export async function launchBrowser(options = {}) {
99
+ const executable = findChrome();
100
+ if (!executable) {
101
+ throw new TaggedError(mcError('chrome-missing', 'Chrome was not found.', 'Install Google Chrome or set MULTICLONE_CHROME to a Chromium binary.'));
102
+ }
103
+ options.signal?.throwIfAborted();
104
+ installExitHooks();
105
+ const keepProfile = options.profile !== undefined;
106
+ const profile = keepProfile
107
+ ? resolve(options.profile ?? '')
108
+ : await mkdtemp(join(tmpdir(), 'multiclone-'));
109
+ if (keepProfile)
110
+ await mkdir(profile, { recursive: true });
111
+ const child = spawn(executable, chromeArgs(profile, options), {
112
+ stdio: 'ignore',
113
+ detached: process.platform !== 'win32',
114
+ });
115
+ live.add(child);
116
+ child.once('exit', () => live.delete(child));
117
+ let closed = false;
118
+ const teardown = async () => {
119
+ if (closed)
120
+ return;
121
+ closed = true;
122
+ clearTimeout(deadline);
123
+ options.signal?.removeEventListener('abort', onAbort);
124
+ killTree(child);
125
+ live.delete(child);
126
+ await sleep(150);
127
+ // DevTools rewrites this file on every start; stale copies confuse a reused profile.
128
+ if (keepProfile)
129
+ await rm(join(profile, 'DevToolsActivePort'), { force: true });
130
+ else
131
+ await rm(profile, { recursive: true, force: true }).catch(() => undefined);
132
+ };
133
+ const onAbort = () => void teardown();
134
+ options.signal?.addEventListener('abort', onAbort, { once: true });
135
+ const deadline = setTimeout(() => void teardown(), options.deadlineMs ?? 30 * 60_000);
136
+ deadline.unref();
137
+ try {
138
+ if (keepProfile)
139
+ await rm(join(profile, 'DevToolsActivePort'), { force: true });
140
+ const port = await readDevtoolsPort(profile, child);
141
+ let page;
142
+ for (let attempt = 0; attempt < 50 && !page; attempt++) {
143
+ const targets = (await fetch(`http://127.0.0.1:${String(port)}/json/list`).then((r) => r.json()));
144
+ page = targets.find((target) => target.type === 'page');
145
+ if (!page)
146
+ await sleep(100);
147
+ }
148
+ if (!page)
149
+ throw new TaggedError(mcError('chrome-launch', 'Chrome exposed no page target.'));
150
+ const versionInfo = (await fetch(`http://127.0.0.1:${String(port)}/json/version`).then((r) => r.json()));
151
+ const cdp = await CdpClient.connect(page.webSocketDebuggerUrl);
152
+ return {
153
+ cdp,
154
+ version: versionInfo.Browser,
155
+ headless: options.headless !== false,
156
+ close: async () => {
157
+ cdp.close();
158
+ await teardown();
159
+ },
160
+ };
161
+ }
162
+ catch (error) {
163
+ await teardown();
164
+ throw error;
165
+ }
166
+ }
167
+ async function readDevtoolsPort(profile, child) {
168
+ const file = join(profile, 'DevToolsActivePort');
169
+ for (let attempt = 0; attempt < 150; attempt++) {
170
+ if (child.exitCode !== null) {
171
+ throw new TaggedError(mcError('chrome-launch', `Chrome exited with code ${String(child.exitCode)}.`, 'A persistent --profile may already be open in another Chrome window.'));
172
+ }
173
+ try {
174
+ const [port] = (await readFile(file, 'utf8')).split('\n');
175
+ if (port)
176
+ return Number(port);
177
+ }
178
+ catch {
179
+ // Not written yet.
180
+ }
181
+ await sleep(100);
182
+ }
183
+ throw new TaggedError(mcError('timeout', 'Chrome did not open a DevTools port within 15s.'));
184
+ }
185
+ //# sourceMappingURL=chrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/core/chrome.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { dirname } from 'node:path';
4
+ export async function ensureDir(path) {
5
+ await mkdir(path, { recursive: true });
6
+ }
7
+ export async function writeJson(path, value) {
8
+ await ensureDir(dirname(path));
9
+ await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
10
+ }
11
+ export async function readJson(path) {
12
+ return JSON.parse(await readFile(path, 'utf8'));
13
+ }
14
+ export async function writeBytes(path, data) {
15
+ await ensureDir(dirname(path));
16
+ await writeFile(path, data);
17
+ }
18
+ export function sha1(input) {
19
+ return createHash('sha1').update(input).digest('hex');
20
+ }
21
+ export function sha256(input) {
22
+ return createHash('sha256').update(input).digest('hex');
23
+ }
24
+ /** Filesystem-safe folder name for a URL, e.g. `www.shopify.com-editions-fall2026`. */
25
+ export function slugForUrl(url) {
26
+ const parsed = new URL(url);
27
+ const path = parsed.pathname.replace(/\/+$/, '').replace(/[^a-zA-Z0-9]+/g, '-');
28
+ return `${parsed.hostname}${path}`.replace(/-+$/, '').slice(0, 80);
29
+ }
30
+ export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
31
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/core/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,KAAc;IAC1D,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,IAAY;IAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,IAAyB;IACtE,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,KAA0B;IAC7C,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAA0B;IAC/C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;IAChF,OAAO,GAAG,MAAM,CAAC,QAAQ,GAAG,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAU,EAAiB,EAAE,CACjD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { stablePathOf } from '../domain/paths.js';
2
+ /**
3
+ * Source of the helper object. `stablePathOf` is self-contained, so its text runs in the page with
4
+ * DOM accessors plugged in.
5
+ */
6
+ export const IN_PAGE_HELPERS = `(() => {
7
+ const stablePathOf = ${stablePathOf.toString()};
8
+ const access = {
9
+ parent: (el) => el.parentElement,
10
+ shadowHost: (el) => {
11
+ const root = el.parentNode;
12
+ return root && root.nodeType === 11 && root.host ? root.host : null;
13
+ },
14
+ tag: (el) => el.tagName,
15
+ id: (el) => el.getAttribute('id'),
16
+ siblings: (el) => el.parentNode ? Array.from(el.parentNode.children) : [el],
17
+ uniqueId: (el, id) => {
18
+ const root = el.getRootNode();
19
+ try { return root.querySelectorAll('#' + CSS.escape(id)).length === 1; } catch { return false; }
20
+ },
21
+ };
22
+ return { path: (el) => stablePathOf(el, access) };
23
+ })()`;
24
+ //# sourceMappingURL=inpage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inpage.js","sourceRoot":"","sources":["../../src/core/inpage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;yBACN,YAAY,CAAC,QAAQ,EAAE;;;;;;;;;;;;;;;;KAgB3C,CAAC"}
@@ -0,0 +1,10 @@
1
+ /** Progress goes to stderr so stdout stays clean for JSON output and the MCP transport. */
2
+ let quiet = false;
3
+ export function setQuiet(value) {
4
+ quiet = value;
5
+ }
6
+ export function log(message) {
7
+ if (!quiet)
8
+ process.stderr.write(`${message}\n`);
9
+ }
10
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/core/log.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,IAAI,KAAK,GAAG,KAAK,CAAC;AAElB,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,KAAK,GAAG,KAAK,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;AACnD,CAAC"}