multiclone 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +31 -41
  2. package/commands/multiclone.md +5 -10
  3. package/dist/adapters/cli.js +24 -1
  4. package/dist/adapters/cli.js.map +1 -1
  5. package/dist/capture/capture.js +189 -80
  6. package/dist/capture/capture.js.map +1 -1
  7. package/dist/capture/collectors/coverage.js +20 -18
  8. package/dist/capture/collectors/coverage.js.map +1 -1
  9. package/dist/capture/collectors/index.js +11 -0
  10. package/dist/capture/collectors/index.js.map +1 -1
  11. package/dist/capture/collectors/interactions.js +129 -130
  12. package/dist/capture/collectors/interactions.js.map +1 -1
  13. package/dist/capture/collectors/motion.js +14 -9
  14. package/dist/capture/collectors/motion.js.map +1 -1
  15. package/dist/capture/collectors/probes.js +3 -2
  16. package/dist/capture/collectors/probes.js.map +1 -1
  17. package/dist/capture/collectors/shots.js +38 -18
  18. package/dist/capture/collectors/shots.js.map +1 -1
  19. package/dist/capture/context.js +5 -0
  20. package/dist/capture/context.js.map +1 -1
  21. package/dist/compare/session.js +22 -16
  22. package/dist/compare/session.js.map +1 -1
  23. package/dist/compare/shoot.js +27 -9
  24. package/dist/compare/shoot.js.map +1 -1
  25. package/dist/contracts/catalog.js +5 -0
  26. package/dist/contracts/catalog.js.map +1 -1
  27. package/dist/contracts/tools/capture.js +22 -6
  28. package/dist/contracts/tools/capture.js.map +1 -1
  29. package/dist/contracts/tools/method.js +22 -7
  30. package/dist/contracts/tools/method.js.map +1 -1
  31. package/dist/contracts/tools/session.js +195 -0
  32. package/dist/contracts/tools/session.js.map +1 -0
  33. package/dist/core/page.js +39 -18
  34. package/dist/core/page.js.map +1 -1
  35. package/dist/doctor.js +9 -0
  36. package/dist/doctor.js.map +1 -1
  37. package/dist/domain/snapshot.js +1 -1
  38. package/dist/domain/snapshot.js.map +1 -1
  39. package/dist/project/assets.js +25 -4
  40. package/dist/project/assets.js.map +1 -1
  41. package/dist/project/draft.js +860 -0
  42. package/dist/project/draft.js.map +1 -0
  43. package/dist/project/method.js +74 -6
  44. package/dist/project/method.js.map +1 -1
  45. package/dist/project/scaffold.js +23 -21
  46. package/dist/project/scaffold.js.map +1 -1
  47. package/dist/session/discover.js +53 -0
  48. package/dist/session/discover.js.map +1 -0
  49. package/dist/session/proc.js +113 -0
  50. package/dist/session/proc.js.map +1 -0
  51. package/dist/session/progress.js +83 -0
  52. package/dist/session/progress.js.map +1 -0
  53. package/dist/session/session.js +450 -0
  54. package/dist/session/session.js.map +1 -0
  55. package/dist/session/state.js +47 -0
  56. package/dist/session/state.js.map +1 -0
  57. package/dist/version.js +1 -1
  58. package/install.sh +30 -0
  59. package/package.json +5 -2
  60. package/scripts/postinstall.mjs +21 -0
  61. package/skills/multiclone/SKILL.md +40 -17
  62. package/templates/react-vite/src/app/App.tsx +6 -1
  63. package/templates/react-vite/src/app/Page.tsx +9 -2
  64. package/templates/react-vite/src/app/routes.ts +7 -0
  65. package/templates/react-vite/src/styles/draft.css +1 -0
  66. package/templates/react-vite/src/styles/index.css +1 -0
@@ -0,0 +1,450 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, openSync } from 'node:fs';
3
+ import { readdir, readFile, writeFile } from 'node:fs/promises';
4
+ import { join, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { capture } from '../capture/capture.js';
7
+ import { ensureDir, sleep, slugForUrl } from '../core/files.js';
8
+ import { log } from '../core/log.js';
9
+ import { DESKTOP, MOBILE } from '../domain/viewports.js';
10
+ import { startMirror } from '../mirror/server.js';
11
+ import { generateDraft } from '../project/draft.js';
12
+ import { scaffold, seedProject } from '../project/scaffold.js';
13
+ import { RunFolder } from '../store/run.js';
14
+ import { discoverPages } from './discover.js';
15
+ import { freePort, installDependencies, killGroup, startDevServer } from './proc.js';
16
+ import { startProgressServer } from './progress.js';
17
+ import { isAlive, logFile, readState, stateDir, writeState, } from './state.js';
18
+ /** Sessions end on their own after this long so forgotten servers don't pile up. */
19
+ const MAX_LIFETIME_MS = 4 * 60 * 60 * 1000;
20
+ /** A failed session keeps its progress page this long, then exits. */
21
+ const FAILED_LINGER_MS = 10 * 60 * 1000;
22
+ /**
23
+ * Starts (or reattaches to) a background session: quick capture, project and first draft, live
24
+ * clone and offline original, then extra pages and the deep pass. Returns once the progress link
25
+ * is up, a few seconds in; follow it with `status`.
26
+ */
27
+ export async function startSession(options) {
28
+ const url = new URL(options.url).toString();
29
+ const workspace = resolve(options.dir ?? `multiclone-${new URL(url).hostname.replace(/^www\./, '')}`);
30
+ const existing = await readState(workspace);
31
+ if (existing && isAlive(existing.pid) && existing.phase !== 'stopped')
32
+ return existing;
33
+ const scope = options.scope ?? (options.pages?.length ? 'pages' : 'landing');
34
+ const capturesDir = join(workspace, 'captures');
35
+ const pageFor = (target, route) => ({
36
+ url: target,
37
+ route,
38
+ capture: join(capturesDir, slugForUrl(target)),
39
+ status: 'pending',
40
+ });
41
+ const pages = [pageFor(url, '/')];
42
+ if (scope === 'pages') {
43
+ for (const entry of options.pages ?? []) {
44
+ const target = new URL(entry, url);
45
+ target.hash = '';
46
+ const route = target.pathname.replace(/\/+$/, '') || '/';
47
+ if (target.toString() === url || pages.some((page) => page.route === route))
48
+ continue;
49
+ pages.push(pageFor(target.toString(), route));
50
+ }
51
+ }
52
+ const state = {
53
+ version: 1,
54
+ url,
55
+ scope,
56
+ options: {
57
+ deep: options.deep ?? true,
58
+ fresh: options.fresh ?? false,
59
+ maxPages: Math.max(1, Math.min(options.maxPages ?? 8, 30)),
60
+ clonePort: options.clonePort ?? 5180,
61
+ mirrorPort: options.mirrorPort ?? 4400,
62
+ progressPort: options.progressPort ?? 4300,
63
+ headless: options.headless ?? true,
64
+ },
65
+ workspace,
66
+ project: join(workspace, 'clone'),
67
+ startedAt: new Date().toISOString(),
68
+ updatedAt: '',
69
+ phase: 'starting',
70
+ pid: 0,
71
+ children: [],
72
+ pages,
73
+ steps: [],
74
+ links: {},
75
+ deep: { status: options.deep === false ? 'skipped' : 'pending' },
76
+ warnings: [],
77
+ };
78
+ await writeState(state);
79
+ await ensureDir(stateDir(workspace));
80
+ const out = openSync(logFile(workspace), 'a');
81
+ const child = spawn(process.execPath, [...process.execArgv, cliEntry(), '__session', workspace], {
82
+ detached: true,
83
+ stdio: ['ignore', out, out],
84
+ env: { ...process.env, MULTICLONE_SESSION: '1' },
85
+ });
86
+ child.unref();
87
+ // The daemon records its own pid; writing here could overwrite its first update.
88
+ const pid = child.pid ?? 0;
89
+ const deadline = Date.now() + 15_000;
90
+ while (Date.now() < deadline) {
91
+ const current = await readState(workspace);
92
+ if (current?.links.progress || current?.phase === 'failed')
93
+ return current;
94
+ if (!isAlive(pid))
95
+ break;
96
+ await sleep(250);
97
+ }
98
+ return (await readState(workspace)) ?? state;
99
+ }
100
+ /** Reads the session, optionally waiting until a milestone is reached (or the timeout). */
101
+ export async function sessionStatus(workspace, waitFor, timeoutMs) {
102
+ const deadline = Date.now() + timeoutMs;
103
+ for (;;) {
104
+ const state = await readState(workspace);
105
+ if (!state)
106
+ return null;
107
+ if (state.phase !== 'stopped' && state.phase !== 'failed' && !isAlive(state.pid)) {
108
+ state.phase = 'stopped';
109
+ state.error ??=
110
+ 'The session process is no longer running. Run `multiclone start` again to resume.';
111
+ }
112
+ if (!waitFor || reached(state, waitFor) || Date.now() > deadline)
113
+ return state;
114
+ await sleep(1_000);
115
+ }
116
+ }
117
+ function reached(state, milestone) {
118
+ if (state.phase === 'failed' || state.phase === 'stopped')
119
+ return true;
120
+ const settled = (page) => page.status === 'ready' || page.status === 'failed';
121
+ if (milestone === 'draft')
122
+ return state.phase === 'ready' || state.pages[0]?.status === 'failed';
123
+ if (milestone === 'pages')
124
+ return state.pages.every(settled) && state.phase === 'ready';
125
+ return state.deep.status !== 'pending' && state.deep.status !== 'running';
126
+ }
127
+ /** Stops the session process and the servers it started. */
128
+ export async function stopSession(workspace) {
129
+ const state = await readState(workspace);
130
+ if (!state)
131
+ return null;
132
+ if (isAlive(state.pid)) {
133
+ try {
134
+ process.kill(state.pid, 'SIGTERM');
135
+ }
136
+ catch {
137
+ // Gone already.
138
+ }
139
+ const deadline = Date.now() + 8_000;
140
+ while (isAlive(state.pid) && Date.now() < deadline)
141
+ await sleep(200);
142
+ if (isAlive(state.pid))
143
+ killGroup(state.pid);
144
+ }
145
+ for (const pid of state.children)
146
+ killGroup(pid);
147
+ const after = (await readState(workspace)) ?? state;
148
+ after.phase = 'stopped';
149
+ after.links = {};
150
+ await writeState(after);
151
+ return after;
152
+ }
153
+ function cliEntry() {
154
+ const self = fileURLToPath(import.meta.url);
155
+ const ext = self.endsWith('.ts') ? 'ts' : 'js';
156
+ return fileURLToPath(new URL(`../adapters/cli.${ext}`, import.meta.url));
157
+ }
158
+ /** The background process behind `start`. Never throws: failures land in the state file. */
159
+ export async function runSessionDaemon(workspace) {
160
+ const loaded = await readState(workspace);
161
+ if (!loaded)
162
+ throw new Error(`No session in ${workspace}`);
163
+ const state = loaded;
164
+ state.pid = process.pid;
165
+ const controller = new AbortController();
166
+ const mirrors = [];
167
+ let progress = null;
168
+ let dev = null;
169
+ let writing = Promise.resolve();
170
+ const save = () => {
171
+ writing = writing.then(() => writeState(state)).catch(() => undefined);
172
+ return writing;
173
+ };
174
+ const warn = (message) => {
175
+ state.warnings.push(message);
176
+ log(` ! ${message}`);
177
+ };
178
+ const step = async (name, fn, note) => {
179
+ const entry = { name, status: 'running', startedAt: new Date().toISOString() };
180
+ state.steps.push(note ? { ...entry, note } : entry);
181
+ const record = state.steps[state.steps.length - 1];
182
+ await save();
183
+ try {
184
+ const value = await fn();
185
+ if (record)
186
+ record.status = 'done';
187
+ return value;
188
+ }
189
+ catch (error) {
190
+ if (controller.signal.aborted)
191
+ throw error;
192
+ const message = error instanceof Error ? error.message : String(error);
193
+ if (record) {
194
+ record.status = 'failed';
195
+ record.note = message.slice(0, 300);
196
+ }
197
+ warn(`${name}: ${message}`);
198
+ return null;
199
+ }
200
+ finally {
201
+ if (record)
202
+ record.endedAt = new Date().toISOString();
203
+ await save();
204
+ }
205
+ };
206
+ let shuttingDown = false;
207
+ const shutdown = async (phase) => {
208
+ if (!shuttingDown) {
209
+ shuttingDown = true;
210
+ controller.abort();
211
+ if (dev?.pid)
212
+ killGroup(dev.pid);
213
+ await Promise.allSettled(mirrors.map((mirror) => mirror.close()));
214
+ progress?.close();
215
+ if (state.phase !== 'failed')
216
+ state.phase = phase;
217
+ state.links = {};
218
+ await save();
219
+ }
220
+ process.exit(0);
221
+ };
222
+ for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP'])
223
+ process.on(signal, () => void shutdown('stopped'));
224
+ setTimeout(() => void shutdown('stopped'), MAX_LIFETIME_MS).unref();
225
+ try {
226
+ const port = await freePort(state.options.progressPort);
227
+ if (port) {
228
+ const started = await startProgressServer(() => state, port);
229
+ progress = started.server;
230
+ state.links.progress = started.url;
231
+ }
232
+ state.phase = 'capturing';
233
+ await save();
234
+ const project = state.project;
235
+ const scaffolded = step('create the React project', () => createProject(project, state.url));
236
+ const installed = scaffolded.then((made) => made === null
237
+ ? null
238
+ : step('install dependencies', async () => {
239
+ const result = await installDependencies(project);
240
+ if (!result.ok)
241
+ throw new Error(`npm install failed: ${result.output.slice(-600)}`);
242
+ return true;
243
+ }));
244
+ // Avoid an unhandled rejection while the capture runs; the step records any failure.
245
+ installed.catch(() => undefined);
246
+ const main = state.pages[0];
247
+ if (!main)
248
+ throw new Error('No page to capture.');
249
+ const mainOk = await capturePage(main);
250
+ if (!mainOk)
251
+ await fail(main.error ?? 'The capture failed.');
252
+ const mirror = await step('serve the original offline', async () => {
253
+ const mirrorPort = await freePort(state.options.mirrorPort);
254
+ const started = await startMirror({ dir: main.capture, port: mirrorPort, mode: 'live' });
255
+ mirrors.push(started);
256
+ state.links.mirror = started.url;
257
+ return started;
258
+ });
259
+ if ((await scaffolded) === null)
260
+ await fail('Could not create the React project.');
261
+ await draftPage(main, mirror ? new URL(mirror.url).port : String(state.options.mirrorPort));
262
+ state.phase = 'drafting';
263
+ await save();
264
+ if ((await installed) !== null) {
265
+ await step('start the clone dev server', async () => {
266
+ const clonePort = await freePort(state.options.clonePort);
267
+ const started = await startDevServer(project, clonePort, (line) => {
268
+ log(` [vite] ${line}`);
269
+ });
270
+ if (!started)
271
+ throw new Error('The dev server did not start within 60s.');
272
+ dev = started.child;
273
+ if (dev.pid)
274
+ state.children.push(dev.pid);
275
+ state.links.clone = started.url;
276
+ });
277
+ }
278
+ state.phase = 'ready';
279
+ await save();
280
+ if (state.scope === 'site') {
281
+ await step('find the site pages', async () => {
282
+ const opened = await RunFolder.open(main.capture);
283
+ if (!opened.ok)
284
+ throw new Error(opened.error.message);
285
+ const found = await discoverPages(opened.value, state.options.maxPages - 1);
286
+ for (const target of found) {
287
+ const route = new URL(target).pathname.replace(/\/+$/, '') || '/';
288
+ if (state.pages.some((page) => page.route === route || page.url === target))
289
+ continue;
290
+ state.pages.push({
291
+ url: target,
292
+ route,
293
+ capture: join(state.workspace, 'captures', slugForUrl(target)),
294
+ status: 'pending',
295
+ });
296
+ }
297
+ }, `up to ${String(state.options.maxPages)} pages`);
298
+ }
299
+ for (const page of state.pages.slice(1)) {
300
+ if (await capturePage(page))
301
+ await draftPage(page, null);
302
+ }
303
+ if (state.options.deep) {
304
+ state.deep = { status: 'running', capture: `${main.capture}-full` };
305
+ await save();
306
+ const deepOk = await step('deep pass: motion, hovers, clicks, coverage', async () => {
307
+ await capture({
308
+ url: main.url,
309
+ out: `${main.capture}-full`,
310
+ depth: 'full',
311
+ viewports: [DESKTOP, MOBILE],
312
+ headless: state.options.headless,
313
+ signal: controller.signal,
314
+ });
315
+ return true;
316
+ });
317
+ state.deep.status = deepOk ? 'done' : 'failed';
318
+ await save();
319
+ }
320
+ await idle();
321
+ }
322
+ catch (error) {
323
+ if (!controller.signal.aborted)
324
+ await fail(error instanceof Error ? error.message : String(error));
325
+ await idle();
326
+ }
327
+ /** Keeps the progress page up a while so the user can read the error, then exits. */
328
+ async function fail(message) {
329
+ state.phase = 'failed';
330
+ state.error = message;
331
+ await save();
332
+ await sleep(FAILED_LINGER_MS);
333
+ return shutdown('failed');
334
+ }
335
+ async function capturePage(page) {
336
+ page.status = 'capturing';
337
+ const reused = !state.options.fresh && (await isComplete(page.capture));
338
+ const ok = await step(`capture ${page.route}`, async () => {
339
+ if (reused)
340
+ return true;
341
+ const result = await capture({
342
+ url: page.url,
343
+ out: page.capture,
344
+ depth: 'quick',
345
+ viewports: [DESKTOP, MOBILE],
346
+ parallel: true,
347
+ headless: state.options.headless,
348
+ signal: controller.signal,
349
+ });
350
+ for (const message of result.manifest.warnings.slice(0, 5))
351
+ warn(`${page.route}: ${message}`);
352
+ if (result.analysis && !result.analysis.ok) {
353
+ page.error = result.analysis.error.message;
354
+ throw new Error(page.error);
355
+ }
356
+ return true;
357
+ }, reused ? 'reused the earlier capture' : 'quick: desktop + mobile in parallel');
358
+ if (!ok) {
359
+ page.status = 'failed';
360
+ page.error ??= lastNote(`capture ${page.route}`) ?? 'capture failed';
361
+ }
362
+ await save();
363
+ return ok === true;
364
+ }
365
+ async function draftPage(page, mirrorPort) {
366
+ if (await hasDraft(state.project, page.route)) {
367
+ // A resumed session must never overwrite components the agent has already polished.
368
+ page.status = 'ready';
369
+ state.steps.push({
370
+ name: `draft ${page.route}`,
371
+ status: 'skipped',
372
+ note: 'kept the existing draft',
373
+ });
374
+ await save();
375
+ return;
376
+ }
377
+ page.status = 'drafting';
378
+ await save();
379
+ const result = await step(`draft ${page.route}`, async () => {
380
+ const opened = await RunFolder.open(page.capture);
381
+ if (!opened.ok)
382
+ throw new Error(opened.error.message);
383
+ await seedProject(opened.value, state.project, Number(mirrorPort ?? state.options.mirrorPort));
384
+ if (page.route === '/')
385
+ await setTitle(state.project, opened.value.manifest.title);
386
+ const aliases = page.route === '/' ? [new URL(opened.value.manifest.finalUrl || page.url).pathname] : [];
387
+ return generateDraft({
388
+ capture: page.capture,
389
+ project: state.project,
390
+ route: page.route,
391
+ aliases,
392
+ });
393
+ });
394
+ if (result) {
395
+ page.status = 'ready';
396
+ page.sections = result.sections.length;
397
+ for (const message of result.warnings.slice(0, 5))
398
+ warn(`${page.route}: ${message}`);
399
+ }
400
+ else {
401
+ page.status = 'failed';
402
+ page.error = lastNote(`draft ${page.route}`) ?? 'draft failed';
403
+ }
404
+ await save();
405
+ }
406
+ function lastNote(name) {
407
+ return state.steps.findLast((entry) => entry.name === name)?.note;
408
+ }
409
+ /** Keeps the servers up until `stop` or the lifetime cap. */
410
+ async function idle() {
411
+ for (;;)
412
+ await sleep(60_000);
413
+ }
414
+ }
415
+ /** Scaffolds the template (no capture yet) unless the project already exists. */
416
+ async function createProject(project, url) {
417
+ if (existsSync(join(project, 'package.json')))
418
+ return true;
419
+ if (existsSync(project) && (await readdir(project)).length) {
420
+ throw new Error(`${project} exists and is not a multiclone project.`);
421
+ }
422
+ const host = new URL(url).hostname.replace(/^www\./, '');
423
+ await scaffold({ dir: project, name: `${host}-clone`, title: host });
424
+ return true;
425
+ }
426
+ async function hasDraft(project, route) {
427
+ try {
428
+ const pages = JSON.parse(await readFile(join(project, '.multiclone', 'pages.json'), 'utf8'));
429
+ return (Array.isArray(pages) &&
430
+ pages.some((page) => typeof page === 'object' &&
431
+ page !== null &&
432
+ page.route === route));
433
+ }
434
+ catch {
435
+ return false;
436
+ }
437
+ }
438
+ async function isComplete(captureDir) {
439
+ const opened = await RunFolder.open(captureDir);
440
+ return opened.ok && (await opened.value.exists('sections/index.json'));
441
+ }
442
+ async function setTitle(project, title) {
443
+ const file = join(project, 'index.html');
444
+ const html = await readFile(file, 'utf8').catch(() => null);
445
+ if (html === null || !title)
446
+ return;
447
+ const safe = title.replace(/[<>&]/g, '');
448
+ await writeFile(file, html.replace(/<title>[^<]*<\/title>/, `<title>${safe}</title>`));
449
+ }
450
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/session/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,EACR,UAAU,GAKX,MAAM,YAAY,CAAC;AAqBpB,oFAAoF;AACpF,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC3C,sEAAsE;AACtE,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAqB;IACtD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5C,MAAM,SAAS,GAAG,OAAO,CACvB,OAAO,CAAC,GAAG,IAAI,cAAc,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAC3E,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,IAAI,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAEvF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC7E,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,KAAa,EAAe,EAAE,CAAC,CAAC;QAC/D,GAAG,EAAE,MAAM;QACX,KAAK;QACL,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,EAAE,SAAS;KAClB,CAAC,CAAC;IACH,MAAM,KAAK,GAAkB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACnC,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;YACzD,IAAI,MAAM,CAAC,QAAQ,EAAE,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC;gBAAE,SAAS;YACtF,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAiB;QAC1B,OAAO,EAAE,CAAC;QACV,GAAG;QACH,KAAK;QACL,OAAO,EAAE;YACP,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK;YAC7B,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1D,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;YACpC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;YACtC,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,IAAI;YAC1C,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI;SACnC;QACD,SAAS;QACT,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC;QACjC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,SAAS,EAAE,EAAE;QACb,KAAK,EAAE,UAAU;QACjB,GAAG,EAAE,CAAC;QACN,QAAQ,EAAE,EAAE;QACZ,KAAK;QACL,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE;QAChE,QAAQ,EAAE,EAAE;KACb,CAAC;IACF,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IACxB,MAAM,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE;QAC/F,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC;QAC3B,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE;KACjD,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,iFAAiF;IACjF,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC;IACrC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,OAAO,EAAE,KAAK,CAAC,QAAQ,IAAI,OAAO,EAAE,KAAK,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC;QAC3E,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,MAAM;QACzB,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,KAAK,CAAC;AAC/C,CAAC;AAID,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,SAAiB,EACjB,OAAyB,EACzB,SAAiB;IAEjB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,SAAS,CAAC;QACR,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjF,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;YACxB,KAAK,CAAC,KAAK;gBACT,mFAAmF,CAAC;QACxF,CAAC;QACD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC/E,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,KAAmB,EAAE,SAAoB;IACxD,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvE,MAAM,OAAO,GAAG,CAAC,IAAiB,EAAW,EAAE,CAC7C,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;IACtD,IAAI,SAAS,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC,KAAK,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC;IACjG,IAAI,SAAS,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC;IACxF,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;AAC5E,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,SAAiB;IACjD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,gBAAgB;QAClB,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QACpC,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;YAAE,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QACrE,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;YAAE,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,QAAQ;QAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,CAAC,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,KAAK,CAAC;IACpD,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;IACxB,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;IACjB,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,QAAQ;IACf,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,mBAAmB,GAAG,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB;IACtD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,SAAS,EAAE,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,MAAM,CAAC;IACrB,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACxB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,GAAG,GAAwB,IAAI,CAAC;IACpC,IAAI,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,GAAkB,EAAE;QAC/B,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACvE,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,CAAC,OAAe,EAAQ,EAAE;QACrC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,GAAG,CAAC,OAAO,OAAO,EAAE,CAAC,CAAC;IACxB,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,EAAK,IAAY,EAAE,EAAoB,EAAE,IAAa,EAAqB,EAAE;QAC7F,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAuB,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QAC7F,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACnD,MAAM,IAAI,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,EAAE,EAAE,CAAC;YACzB,IAAI,MAAM;gBAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;YACnC,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,KAAK,CAAC;YAC3C,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;gBACzB,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACtC,CAAC;YACD,IAAI,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,IAAI,MAAM;gBAAE,MAAM,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;IACH,CAAC,CAAC;IACF,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,EAAE,KAA2B,EAAkB,EAAE;QACrE,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,YAAY,GAAG,IAAI,CAAC;YACpB,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,IAAI,GAAG,EAAE,GAAG;gBAAE,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAClE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAClB,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ;gBAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;YAClD,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAU;QAC3D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACrD,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC,CAAC,KAAK,EAAE,CAAC;IAEpE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACxD,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC7D,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;YAC1B,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;QACrC,CAAC;QACD,KAAK,CAAC,KAAK,GAAG,WAAW,CAAC;QAC1B,MAAM,IAAI,EAAE,CAAC;QAEb,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7F,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CACzC,IAAI,KAAK,IAAI;YACX,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;gBACtC,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC;gBAClD,IAAI,CAAC,MAAM,CAAC,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACpF,OAAO,IAAI,CAAC;YACd,CAAC,CAAC,CACP,CAAC;QACF,qFAAqF;QACrF,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAEjC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,qBAAqB,CAAC,CAAC;QAE7D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC5D,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACzF,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACtB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;YACjC,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,UAAU,CAAC,KAAK,IAAI;YAAE,MAAM,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACnF,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;QAE5F,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC;QACzB,MAAM,IAAI,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;gBAClD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC1D,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;oBAChE,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;gBAC1B,CAAC,CAAC,CAAC;gBACH,IAAI,CAAC,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;gBAC1E,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC;gBACpB,IAAI,GAAG,CAAC,GAAG;oBAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC1C,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC;YAClC,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC;QACtB,MAAM,IAAI,EAAE,CAAC;QAEb,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC3B,MAAM,IAAI,CACR,qBAAqB,EACrB,KAAK,IAAI,EAAE;gBACT,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAClD,IAAI,CAAC,MAAM,CAAC,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACtD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;gBAC5E,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;oBAC3B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;oBAClE,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,MAAM,CAAC;wBAAE,SAAS;oBACtF,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;wBACf,GAAG,EAAE,MAAM;wBACX,KAAK;wBACL,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;wBAC9D,MAAM,EAAE,SAAS;qBAClB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,EACD,SAAS,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAChD,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACxC,IAAI,MAAM,WAAW,CAAC,IAAI,CAAC;gBAAE,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,OAAO,EAAE,CAAC;YACpE,MAAM,IAAI,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;gBAClF,MAAM,OAAO,CAAC;oBACZ,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,OAAO;oBAC3B,KAAK,EAAE,MAAM;oBACb,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;oBAC5B,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ;oBAChC,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CAAC,CAAC;gBACH,OAAO,IAAI,CAAC;YACd,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC/C,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO;YAC5B,MAAM,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACrE,MAAM,IAAI,EAAE,CAAC;IACf,CAAC;IAED,qFAAqF;IACrF,KAAK,UAAU,IAAI,CAAC,OAAe;QACjC,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC;QACvB,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC;QACtB,MAAM,IAAI,EAAE,CAAC;QACb,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAC9B,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,UAAU,WAAW,CAAC,IAAiB;QAC1C,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC;QAC1B,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QACxE,MAAM,EAAE,GAAG,MAAM,IAAI,CACnB,WAAW,IAAI,CAAC,KAAK,EAAE,EACvB,KAAK,IAAI,EAAE;YACT,IAAI,MAAM;gBAAE,OAAO,IAAI,CAAC;YACxB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;gBAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,GAAG,EAAE,IAAI,CAAC,OAAO;gBACjB,KAAK,EAAE,OAAO;gBACd,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;gBAC5B,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ;gBAChC,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YACH,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;gBACxD,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC;YACpC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAC3C,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;gBAC3C,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,EACD,MAAM,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,qCAAqC,CAC9E,CAAC;QACF,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;YACvB,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,WAAW,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,gBAAgB,CAAC;QACvE,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;QACb,OAAO,EAAE,KAAK,IAAI,CAAC;IACrB,CAAC;IAED,KAAK,UAAU,SAAS,CAAC,IAAiB,EAAE,UAAyB;QACnE,IAAI,MAAM,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9C,oFAAoF;YACpF,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;YACtB,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE;gBAC3B,MAAM,EAAE,SAAS;gBACjB,IAAI,EAAE,yBAAyB;aAChC,CAAC,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;QACzB,MAAM,IAAI,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACtD,MAAM,WAAW,CACf,MAAM,CAAC,KAAK,EACZ,KAAK,CAAC,OAAO,EACb,MAAM,CAAC,UAAU,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAC/C,CAAC;YACF,IAAI,IAAI,CAAC,KAAK,KAAK,GAAG;gBAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACnF,MAAM,OAAO,GACX,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3F,OAAO,aAAa,CAAC;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,OAAO;aACR,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;YACtB,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACvC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;gBAAE,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC;QACvF,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;YACvB,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,cAAc,CAAC;QACjE,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC;IAED,SAAS,QAAQ,CAAC,IAAY;QAC5B,OAAO,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,IAAI,CAAC;IACpE,CAAC;IAED,6DAA6D;IAC7D,KAAK,UAAU,IAAI;QACjB;YAAS,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,GAAW;IACvD,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,0CAA0C,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACzD,MAAM,QAAQ,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,OAAe,EAAE,KAAa;IACpD,IAAI,CAAC;QACH,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAC/B,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CACnE,CAAC;QACF,OAAO,CACL,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACpB,KAAK,CAAC,IAAI,CACR,CAAC,IAAa,EAAE,EAAE,CAChB,OAAO,IAAI,KAAK,QAAQ;gBACxB,IAAI,KAAK,IAAI;gBACZ,IAA4B,CAAC,KAAK,KAAK,KAAK,CAChD,CACF,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,UAAkB;IAC1C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,OAAe,EAAE,KAAa;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO;IACpC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACzC,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE,UAAU,IAAI,UAAU,CAAC,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { readFile, rename, writeFile } from 'node:fs/promises';
2
+ import { join, resolve } from 'node:path';
3
+ import { ensureDir } from '../core/files.js';
4
+ export const stateDir = (workspace) => join(resolve(workspace), '.multiclone');
5
+ export const stateFile = (workspace) => join(stateDir(workspace), 'session.json');
6
+ export const logFile = (workspace) => join(stateDir(workspace), 'session.log');
7
+ export async function readState(workspace) {
8
+ try {
9
+ const parsed = JSON.parse(await readFile(stateFile(workspace), 'utf8'));
10
+ return isState(parsed) ? parsed : null;
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ /** Atomic write: readers never see half a file. */
17
+ export async function writeState(state) {
18
+ await ensureDir(stateDir(state.workspace));
19
+ state.updatedAt = new Date().toISOString();
20
+ const target = stateFile(state.workspace);
21
+ const temp = `${target}.${String(process.pid)}.tmp`;
22
+ await writeFile(temp, `${JSON.stringify(state, null, 2)}\n`);
23
+ await rename(temp, target);
24
+ }
25
+ function isState(value) {
26
+ if (typeof value !== 'object' || value === null)
27
+ return false;
28
+ const v = value;
29
+ return (v.version === 1 &&
30
+ typeof v.url === 'string' &&
31
+ typeof v.workspace === 'string' &&
32
+ typeof v.pid === 'number' &&
33
+ Array.isArray(v.pages) &&
34
+ Array.isArray(v.steps));
35
+ }
36
+ export function isAlive(pid) {
37
+ if (pid <= 0)
38
+ return false;
39
+ try {
40
+ process.kill(pid, 0);
41
+ return true;
42
+ }
43
+ catch (error) {
44
+ return error.code === 'EPERM';
45
+ }
46
+ }
47
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/session/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AA8D7C,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAiB,EAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;AAC/F,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAAiB,EAAU,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAC,CAAC;AAClG,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,SAAiB,EAAU,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;AAE/F,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAiB;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACjF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAmB;IAClD,MAAM,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3C,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;IACpD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7D,MAAM,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,OAAO,CAAC,KAAc;IAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAqD,CAAC;IAChE,OAAO,CACL,CAAC,CAAC,OAAO,KAAK,CAAC;QACf,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;QACzB,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAC/B,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QACtB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CACvB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAQ,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAC;IAC3D,CAAC;AACH,CAAC"}
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.1.0';
1
+ export const VERSION = '0.2.1';
2
2
  //# sourceMappingURL=version.js.map
package/install.sh ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env sh
2
+ # multiclone installer: puts the `multiclone` CLI on your PATH and checks Chrome.
3
+ # Download this file, read it, then run: sh install.sh
4
+ # Optional: MULTICLONE_VERSION=0.2.0 to pin a version.
5
+ set -eu
6
+
7
+ version="${MULTICLONE_VERSION:-latest}"
8
+ say() { printf '%s\n' "$*"; }
9
+ fail() { printf 'multiclone install: %s\n' "$*" >&2; exit 1; }
10
+
11
+ command -v node >/dev/null 2>&1 || fail "Node.js 22.12 or newer is required: https://nodejs.org"
12
+ node -e 'const [a,b]=process.versions.node.split(".").map(Number);process.exit(a>22||(a===22&&b>=12)?0:1)' \
13
+ || fail "Node.js 22.12 or newer is required (found $(node -v))."
14
+ command -v npm >/dev/null 2>&1 || fail "npm is required (it ships with Node.js)."
15
+
16
+ say "Installing multiclone@${version}…"
17
+ # --min-release-age=0: some setups hold back packages published in the last days.
18
+ if ! npm install -g "multiclone@${version}" --no-audit --no-fund --loglevel=error --min-release-age=0 2>/dev/null; then
19
+ npm install -g "multiclone@${version}" --no-audit --no-fund --loglevel=error \
20
+ || fail "npm install -g failed. For a permissions error, set an npm prefix you own: https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally"
21
+ fi
22
+
23
+ command -v multiclone >/dev/null 2>&1 \
24
+ || fail "installed, but \`multiclone\` is not on your PATH. Add \"$(npm prefix -g)/bin\" to PATH."
25
+
26
+ multiclone setup --global >/dev/null 2>&1 || say "Could not install the agent method; run: multiclone setup --global"
27
+ multiclone doctor || say "doctor found a problem above; multiclone is still installed."
28
+ say ""
29
+ say "Done. Open your agent in any folder and ask it to clone a site, or run: multiclone start <url>"
30
+ say "(Cursor only: run multiclone setup once in the project folder.)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multiclone",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Clone any website from a link into a React + Vite + TypeScript project with the same design, animations and interactions. Works with Claude Code, Cursor and any MCP agent.",
5
5
  "keywords": [
6
6
  "clone",
@@ -26,7 +26,9 @@
26
26
  "skills",
27
27
  "commands",
28
28
  "agents",
29
- "README.md"
29
+ "README.md",
30
+ "install.sh",
31
+ "scripts/postinstall.mjs"
30
32
  ],
31
33
  "engines": {
32
34
  "node": ">=22.12"
@@ -34,6 +36,7 @@
34
36
  "scripts": {
35
37
  "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && node scripts/chmod-bin.mjs",
36
38
  "prepare": "npm run build",
39
+ "postinstall": "node scripts/postinstall.mjs",
37
40
  "dev": "tsx src/adapters/cli.ts",
38
41
  "typecheck": "tsc --noEmit",
39
42
  "lint": "eslint .",
@@ -0,0 +1,21 @@
1
+ // Installs the agent method (Claude Code skill, /multiclone command, guides) on a global install.
2
+ // Never fails the install: the CLI repeats this on first run if anything here goes wrong.
3
+ import { existsSync } from 'node:fs';
4
+
5
+ const isGlobal =
6
+ process.env.npm_config_global === 'true' || process.env.npm_config_location === 'global';
7
+ const entry = new URL('../dist/project/method.js', import.meta.url);
8
+
9
+ if (isGlobal && process.env.MULTICLONE_NO_GLOBAL_SETUP !== '1' && existsSync(entry)) {
10
+ try {
11
+ const { setupGlobal } = await import(entry.href);
12
+ const result = await setupGlobal();
13
+ if (result.ok) {
14
+ process.stdout.write(
15
+ `multiclone: installed the cloning method for ${result.value.agents.join(', ')}.\n`,
16
+ );
17
+ }
18
+ } catch {
19
+ // Ignored on purpose; see above.
20
+ }
21
+ }