multiclone 0.0.0-stage → 0.2.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 (240) hide show
  1. package/README.md +78 -2
  2. package/agents/section-builder.md +37 -0
  3. package/commands/multiclone.md +15 -0
  4. package/dist/adapters/cli.js +129 -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 +340 -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 +121 -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 +61 -0
  47. package/dist/capture/collectors/index.js.map +1 -0
  48. package/dist/capture/collectors/interactions.js +207 -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 +91 -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 +34 -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 +58 -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 +40 -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 +139 -0
  99. package/dist/compare/session.js.map +1 -0
  100. package/dist/compare/shoot.js +62 -0
  101. package/dist/compare/shoot.js.map +1 -0
  102. package/dist/contracts/catalog.js +29 -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 +87 -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 +60 -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/tools/session.js +195 -0
  125. package/dist/contracts/tools/session.js.map +1 -0
  126. package/dist/contracts/types.js +20 -0
  127. package/dist/contracts/types.js.map +1 -0
  128. package/dist/core/cdp.js +146 -0
  129. package/dist/core/cdp.js.map +1 -0
  130. package/dist/core/chrome.js +185 -0
  131. package/dist/core/chrome.js.map +1 -0
  132. package/dist/core/files.js +31 -0
  133. package/dist/core/files.js.map +1 -0
  134. package/dist/core/inpage.js +24 -0
  135. package/dist/core/inpage.js.map +1 -0
  136. package/dist/core/log.js +10 -0
  137. package/dist/core/log.js.map +1 -0
  138. package/dist/core/page.js +269 -0
  139. package/dist/core/page.js.map +1 -0
  140. package/dist/doctor.js +37 -0
  141. package/dist/doctor.js.map +1 -0
  142. package/dist/domain/breakpoints.js +47 -0
  143. package/dist/domain/breakpoints.js.map +1 -0
  144. package/dist/domain/budget.js +38 -0
  145. package/dist/domain/budget.js.map +1 -0
  146. package/dist/domain/compare.js +138 -0
  147. package/dist/domain/compare.js.map +1 -0
  148. package/dist/domain/coverage.js +96 -0
  149. package/dist/domain/coverage.js.map +1 -0
  150. package/dist/domain/motion.js +142 -0
  151. package/dist/domain/motion.js.map +1 -0
  152. package/dist/domain/paths.js +55 -0
  153. package/dist/domain/paths.js.map +1 -0
  154. package/dist/domain/result.js +38 -0
  155. package/dist/domain/result.js.map +1 -0
  156. package/dist/domain/routes.js +160 -0
  157. package/dist/domain/routes.js.map +1 -0
  158. package/dist/domain/sections.js +94 -0
  159. package/dist/domain/sections.js.map +1 -0
  160. package/dist/domain/snapshot.js +268 -0
  161. package/dist/domain/snapshot.js.map +1 -0
  162. package/dist/domain/sourcemap.js +95 -0
  163. package/dist/domain/sourcemap.js.map +1 -0
  164. package/dist/domain/stack.js +342 -0
  165. package/dist/domain/stack.js.map +1 -0
  166. package/dist/domain/tokens.js +183 -0
  167. package/dist/domain/tokens.js.map +1 -0
  168. package/dist/domain/types.js +2 -0
  169. package/dist/domain/types.js.map +1 -0
  170. package/dist/domain/viewports.js +45 -0
  171. package/dist/domain/viewports.js.map +1 -0
  172. package/dist/mirror/rewrite.js +56 -0
  173. package/dist/mirror/rewrite.js.map +1 -0
  174. package/dist/mirror/server.js +191 -0
  175. package/dist/mirror/server.js.map +1 -0
  176. package/dist/mirror/store.js +86 -0
  177. package/dist/mirror/store.js.map +1 -0
  178. package/dist/project/assets.js +69 -0
  179. package/dist/project/assets.js.map +1 -0
  180. package/dist/project/draft.js +860 -0
  181. package/dist/project/draft.js.map +1 -0
  182. package/dist/project/method.js +154 -0
  183. package/dist/project/method.js.map +1 -0
  184. package/dist/project/scaffold.js +142 -0
  185. package/dist/project/scaffold.js.map +1 -0
  186. package/dist/session/discover.js +53 -0
  187. package/dist/session/discover.js.map +1 -0
  188. package/dist/session/proc.js +113 -0
  189. package/dist/session/proc.js.map +1 -0
  190. package/dist/session/progress.js +83 -0
  191. package/dist/session/progress.js.map +1 -0
  192. package/dist/session/session.js +450 -0
  193. package/dist/session/session.js.map +1 -0
  194. package/dist/session/state.js +47 -0
  195. package/dist/session/state.js.map +1 -0
  196. package/dist/store/inspect.js +212 -0
  197. package/dist/store/inspect.js.map +1 -0
  198. package/dist/store/run.js +112 -0
  199. package/dist/store/run.js.map +1 -0
  200. package/dist/store/summary.js +69 -0
  201. package/dist/store/summary.js.map +1 -0
  202. package/dist/version.js +2 -0
  203. package/dist/version.js.map +1 -0
  204. package/install.sh +29 -0
  205. package/package.json +67 -4
  206. package/skills/multiclone/SKILL.md +123 -0
  207. package/skills/multiclone/references/capture-format.md +73 -0
  208. package/skills/multiclone/references/fidelity.md +54 -0
  209. package/skills/multiclone/references/quality.md +47 -0
  210. package/skills/multiclone/references/stacks.md +156 -0
  211. package/templates/react-vite/.prettierignore +5 -0
  212. package/templates/react-vite/.prettierrc.json +6 -0
  213. package/templates/react-vite/eslint.config.js +38 -0
  214. package/templates/react-vite/gitignore +4 -0
  215. package/templates/react-vite/index.html +12 -0
  216. package/templates/react-vite/package.json +36 -0
  217. package/templates/react-vite/src/app/App.tsx +15 -0
  218. package/templates/react-vite/src/app/Page.tsx +11 -0
  219. package/templates/react-vite/src/app/routes.ts +7 -0
  220. package/templates/react-vite/src/components/README.md +7 -0
  221. package/templates/react-vite/src/content/README.md +2 -0
  222. package/templates/react-vite/src/hooks/useActiveSection.ts +37 -0
  223. package/templates/react-vite/src/hooks/useInView.ts +45 -0
  224. package/templates/react-vite/src/hooks/useMediaQuery.ts +15 -0
  225. package/templates/react-vite/src/lib/cn.ts +4 -0
  226. package/templates/react-vite/src/lib/math.ts +21 -0
  227. package/templates/react-vite/src/lib/mc.ts +13 -0
  228. package/templates/react-vite/src/lib/media.ts +4 -0
  229. package/templates/react-vite/src/main.tsx +13 -0
  230. package/templates/react-vite/src/providers/SmoothScroll.tsx +24 -0
  231. package/templates/react-vite/src/styles/base.css +42 -0
  232. package/templates/react-vite/src/styles/draft.css +1 -0
  233. package/templates/react-vite/src/styles/fonts.css +1 -0
  234. package/templates/react-vite/src/styles/index.css +4 -0
  235. package/templates/react-vite/src/styles/tokens.css +3 -0
  236. package/templates/react-vite/src/vite-env.d.ts +1 -0
  237. package/templates/react-vite/tsconfig.app.json +29 -0
  238. package/templates/react-vite/tsconfig.json +4 -0
  239. package/templates/react-vite/tsconfig.node.json +19 -0
  240. package/templates/react-vite/vite.config.ts +37 -0
@@ -0,0 +1,195 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir } from 'node:fs/promises';
3
+ import { join, resolve } from 'node:path';
4
+ import { z } from 'zod';
5
+ import { ok, err, mcError } from '../../domain/result.js';
6
+ import { generateDraft } from '../../project/draft.js';
7
+ import { sessionStatus, startSession, stopSession } from '../../session/session.js';
8
+ import { defineTool } from '../types.js';
9
+ /** Short, capped summary for agents; everything else is in .multiclone/session.json. */
10
+ export function summarize(state) {
11
+ const lines = [];
12
+ lines.push(`multiclone ${state.phase} — ${state.url} (${state.scope})`);
13
+ if (state.links.progress)
14
+ lines.push(` progress: ${state.links.progress}`);
15
+ if (state.links.clone)
16
+ lines.push(` clone: ${state.links.clone}`);
17
+ if (state.links.mirror)
18
+ lines.push(` original: ${state.links.mirror} (offline mirror)`);
19
+ lines.push(` project: ${state.project}`);
20
+ const pages = state.pages
21
+ .slice(0, 12)
22
+ .map((page) => ` ${page.route} ${page.status}${page.sections === undefined ? '' : ` (${String(page.sections)} sections)`}${page.error ? ` — ${page.error.slice(0, 120)}` : ''}`);
23
+ lines.push(` pages:`, ...pages);
24
+ if (state.pages.length > 12)
25
+ lines.push(` … ${String(state.pages.length - 12)} more`);
26
+ const running = state.steps.filter((step) => step.status === 'running').map((step) => step.name);
27
+ if (running.length)
28
+ lines.push(` now: ${running.join(', ')}`);
29
+ lines.push(` deep pass: ${state.deep.status}${state.deep.note ? ` (${state.deep.note})` : ''}`);
30
+ if (state.warnings.length) {
31
+ lines.push(` warnings (${String(state.warnings.length)}):`);
32
+ for (const warning of state.warnings.slice(-5))
33
+ lines.push(` - ${warning.slice(0, 160)}`);
34
+ }
35
+ if (state.error)
36
+ lines.push(` error: ${state.error.slice(0, 300)}`);
37
+ lines.push(nextHint(state));
38
+ return lines.join('\n');
39
+ }
40
+ function nextHint(state) {
41
+ if (state.phase === 'failed' || state.phase === 'stopped')
42
+ return 'Next: fix the error above, then `multiclone start` again (it resumes).';
43
+ if (state.phase !== 'ready')
44
+ return 'Next: `multiclone status --wait draft` to block until the first draft is live.';
45
+ return `Next: polish ${state.project}/src/sections (one folder per page), compare with \`multiclone compare\`. The deep pass adds motion and hover data to the capture folders when done.`;
46
+ }
47
+ const scopeField = z
48
+ .enum(['landing', 'pages', 'site'])
49
+ .optional()
50
+ .describe('What to clone. Ask the user first: landing (the URL only), pages (the URL plus `pages`) or site (the URL plus pages linked from its navigation).');
51
+ export const startTool = defineTool({
52
+ name: 'start',
53
+ title: 'Start cloning a website',
54
+ description: 'The fast path. Ask the user the scope first (landing page, specific pages, or the whole site), then call this. Starts a background session and returns within seconds with a progress link to show the user. The session runs a quick capture (1–2 min), creates the React project, writes a first draft (one component per section, real text, media and fonts), serves the clone and an offline copy of the original, then captures the other pages and a deep pass (motion, hovers) in the background. Re-running resumes; finished captures are reused. Follow with `status`.',
55
+ input: z.object({
56
+ url: z.url().describe('Page to clone (the landing page).'),
57
+ scope: scopeField,
58
+ pages: z
59
+ .array(z.string())
60
+ .optional()
61
+ .describe('Paths or URLs to clone besides the URL (scope pages).'),
62
+ maxPages: z
63
+ .number()
64
+ .int()
65
+ .min(1)
66
+ .max(30)
67
+ .optional()
68
+ .describe('Page limit for scope site, landing page included (default 8).'),
69
+ dir: z.string().optional().describe('Workspace folder (default ./multiclone-<host>).'),
70
+ deep: z
71
+ .boolean()
72
+ .default(true)
73
+ .describe('Run the deep pass (motion, hovers, coverage) after the draft.'),
74
+ fresh: z.boolean().default(false).describe('Capture again instead of reusing captures.'),
75
+ clonePort: z.number().int().min(1024).max(65535).optional(),
76
+ mirrorPort: z.number().int().min(1024).max(65535).optional(),
77
+ progressPort: z.number().int().min(1024).max(65535).optional(),
78
+ headed: z.boolean().default(false).describe('Show the Chrome window.'),
79
+ }),
80
+ cli: { command: 'start', positionals: ['url'] },
81
+ readOnly: false,
82
+ openWorld: true,
83
+ run: async (input) => {
84
+ const state = await startSession({
85
+ url: input.url,
86
+ deep: input.deep,
87
+ fresh: input.fresh,
88
+ headless: !input.headed,
89
+ ...(input.scope === undefined ? {} : { scope: input.scope }),
90
+ ...(input.pages === undefined ? {} : { pages: input.pages }),
91
+ ...(input.maxPages === undefined ? {} : { maxPages: input.maxPages }),
92
+ ...(input.dir === undefined ? {} : { dir: input.dir }),
93
+ ...(input.clonePort === undefined ? {} : { clonePort: input.clonePort }),
94
+ ...(input.mirrorPort === undefined ? {} : { mirrorPort: input.mirrorPort }),
95
+ ...(input.progressPort === undefined ? {} : { progressPort: input.progressPort }),
96
+ });
97
+ return ok({ data: state, text: `${summarize(state)}\nShow the user the progress link now.` });
98
+ },
99
+ });
100
+ export const statusTool = defineTool({
101
+ name: 'status',
102
+ title: 'Session status',
103
+ description: 'Where a `start` session is: phase, links (progress, clone, offline original), pages and their sections, the deep pass, warnings. wait=draft blocks until the first draft is live (or timeout); wait=pages until every page is drafted; wait=deep until the deep pass ends.',
104
+ input: z.object({
105
+ dir: z
106
+ .string()
107
+ .optional()
108
+ .describe('Workspace folder (default: the one in the current folder).'),
109
+ wait: z.enum(['draft', 'pages', 'deep']).optional(),
110
+ timeout: z.number().int().min(1).max(1800).default(240).describe('Seconds to wait at most.'),
111
+ }),
112
+ cli: { command: 'status', positionals: ['dir'] },
113
+ readOnly: true,
114
+ openWorld: false,
115
+ run: async (input) => {
116
+ const workspace = await findWorkspace(input.dir);
117
+ if (!workspace)
118
+ return err(noSession());
119
+ const wait = input.wait ?? null;
120
+ const state = await sessionStatus(workspace, wait, input.timeout * 1000);
121
+ if (!state)
122
+ return err(noSession());
123
+ return ok({ data: state, text: summarize(state) });
124
+ },
125
+ });
126
+ export const stopTool = defineTool({
127
+ name: 'stop',
128
+ title: 'Stop a session',
129
+ description: 'Stop a `start` session and its servers (clone, mirror, progress). Files stay.',
130
+ input: z.object({ dir: z.string().optional() }),
131
+ cli: { command: 'stop', positionals: ['dir'] },
132
+ readOnly: false,
133
+ openWorld: false,
134
+ run: async (input) => {
135
+ const workspace = await findWorkspace(input.dir);
136
+ if (!workspace)
137
+ return err(noSession());
138
+ const state = await stopSession(workspace);
139
+ if (!state)
140
+ return err(noSession());
141
+ return ok({ data: state, text: `Stopped. Project kept at ${state.project}.` });
142
+ },
143
+ });
144
+ export const draftTool = defineTool({
145
+ name: 'draft',
146
+ title: 'Write a first draft from a capture',
147
+ description: 'Turn a capture into React components in a scaffolded project: one component and CSS module per section with the real text, links, media and fonts, a page that composes them, and a route. Layout is approximated from the captured computed styles (flex and grid kept, sizes relative); motion and interactions are left to the agent. `start` runs this for you.',
148
+ input: z.object({
149
+ capture: z.string().describe('Capture folder.'),
150
+ project: z.string().describe('Scaffolded project folder.'),
151
+ route: z.string().default('/').describe('Path this page answers on in the clone.'),
152
+ overwrite: z
153
+ .boolean()
154
+ .default(false)
155
+ .describe('Replace an earlier draft of this route, discarding edits.'),
156
+ }),
157
+ cli: { command: 'draft', positionals: ['capture', 'project'] },
158
+ readOnly: false,
159
+ openWorld: false,
160
+ run: async (input) => {
161
+ const result = await generateDraft({
162
+ capture: input.capture,
163
+ project: input.project,
164
+ route: input.route,
165
+ overwrite: input.overwrite,
166
+ });
167
+ const list = result.sections
168
+ .slice(0, 30)
169
+ .map((section) => ` ${section.file}${section.truncated ? ' (truncated)' : ''}`);
170
+ return ok({
171
+ data: result,
172
+ text: [
173
+ `Drafted ${result.route} → ${result.page} (${String(result.sections.length)} sections)`,
174
+ ...list,
175
+ ...result.warnings.slice(0, 5).map((warning) => ` ! ${warning}`),
176
+ ].join('\n'),
177
+ });
178
+ },
179
+ });
180
+ function noSession() {
181
+ return mcError('not-found', 'No multiclone session here.', 'Run `multiclone start <url>` first, or pass the workspace folder.');
182
+ }
183
+ /** The given folder, or the single multiclone-* workspace (or one) in the current folder. */
184
+ async function findWorkspace(dir) {
185
+ if (dir !== undefined)
186
+ return resolve(dir);
187
+ if (existsSync(join('.multiclone', 'session.json')))
188
+ return resolve('.');
189
+ const candidates = (await readdir('.').catch(() => []))
190
+ .filter((name) => name.startsWith('multiclone-') && existsSync(join(name, '.multiclone', 'session.json')))
191
+ .sort();
192
+ const first = candidates[0];
193
+ return first === undefined ? null : resolve(first);
194
+ }
195
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/contracts/tools/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ import { err, mcError } from '../domain/result.js';
3
+ export function defineTool(contract) {
4
+ return {
5
+ name: contract.name,
6
+ title: contract.title,
7
+ description: contract.description,
8
+ input: contract.input,
9
+ cli: contract.cli,
10
+ readOnly: contract.readOnly,
11
+ openWorld: contract.openWorld,
12
+ run: async (raw, context) => {
13
+ const parsed = contract.input.safeParse(raw);
14
+ if (!parsed.success)
15
+ return err(mcError('invalid-input', z.prettifyError(parsed.error)));
16
+ return contract.run(parsed.data, context);
17
+ },
18
+ };
19
+ }
20
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/contracts/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,GAAG,EAAE,OAAO,EAAe,MAAM,qBAAqB,CAAC;AAkDhE,MAAM,UAAU,UAAU,CACxB,QAA6B;IAE7B,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;YAC1B,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC7C,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACzF,OAAO,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -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