craftdriver 0.1.0 → 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.
- package/CHANGELOG.md +104 -0
- package/README.md +157 -45
- package/bin/craftdriver.mjs +26 -0
- package/dist/cli/client.d.ts +17 -0
- package/dist/cli/client.d.ts.map +1 -0
- package/dist/cli/client.js +88 -0
- package/dist/cli/client.js.map +1 -0
- package/dist/cli/daemon.d.ts +15 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +145 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/cli/defaults.d.ts +10 -0
- package/dist/cli/defaults.d.ts.map +1 -0
- package/dist/cli/defaults.js +42 -0
- package/dist/cli/defaults.js.map +1 -0
- package/dist/cli/dispatcher.d.ts +28 -0
- package/dist/cli/dispatcher.d.ts.map +1 -0
- package/dist/cli/dispatcher.js +319 -0
- package/dist/cli/dispatcher.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +404 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +24 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +192 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/mcp/artifacts.d.ts +43 -0
- package/dist/cli/mcp/artifacts.d.ts.map +1 -0
- package/dist/cli/mcp/artifacts.js +104 -0
- package/dist/cli/mcp/artifacts.js.map +1 -0
- package/dist/cli/mcp/server.d.ts +34 -0
- package/dist/cli/mcp/server.d.ts.map +1 -0
- package/dist/cli/mcp/server.js +214 -0
- package/dist/cli/mcp/server.js.map +1 -0
- package/dist/cli/mcp/tools.d.ts +34 -0
- package/dist/cli/mcp/tools.d.ts.map +1 -0
- package/dist/cli/mcp/tools.js +236 -0
- package/dist/cli/mcp/tools.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +37 -0
- package/dist/cli/parseArgs.d.ts.map +1 -0
- package/dist/cli/parseArgs.js +215 -0
- package/dist/cli/parseArgs.js.map +1 -0
- package/dist/cli/protocol.d.ts +34 -0
- package/dist/cli/protocol.d.ts.map +1 -0
- package/dist/cli/protocol.js +2 -0
- package/dist/cli/protocol.js.map +1 -0
- package/dist/cli/selector.d.ts +29 -0
- package/dist/cli/selector.d.ts.map +1 -0
- package/dist/cli/selector.js +100 -0
- package/dist/cli/selector.js.map +1 -0
- package/dist/cli/snapshot.d.ts +48 -0
- package/dist/cli/snapshot.d.ts.map +1 -0
- package/dist/cli/snapshot.js +179 -0
- package/dist/cli/snapshot.js.map +1 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/a11y.d.ts +81 -0
- package/dist/lib/a11y.d.ts.map +1 -0
- package/dist/lib/a11y.js +171 -0
- package/dist/lib/a11y.js.map +1 -0
- package/dist/lib/bidi/index.d.ts +26 -1
- package/dist/lib/bidi/index.d.ts.map +1 -1
- package/dist/lib/bidi/index.js +49 -0
- package/dist/lib/bidi/index.js.map +1 -1
- package/dist/lib/bidi/logs.d.ts +2 -2
- package/dist/lib/bidi/logs.d.ts.map +1 -1
- package/dist/lib/bidi/logs.js +2 -2
- package/dist/lib/bidi/logs.js.map +1 -1
- package/dist/lib/bidi/network.d.ts +74 -1
- package/dist/lib/bidi/network.d.ts.map +1 -1
- package/dist/lib/bidi/network.js +222 -4
- package/dist/lib/bidi/network.js.map +1 -1
- package/dist/lib/bidi/storage.d.ts +5 -3
- package/dist/lib/bidi/storage.d.ts.map +1 -1
- package/dist/lib/bidi/storage.js +14 -7
- package/dist/lib/bidi/storage.js.map +1 -1
- package/dist/lib/bidi/types.d.ts +27 -6
- package/dist/lib/bidi/types.d.ts.map +1 -1
- package/dist/lib/browser.d.ts +637 -16
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +1428 -102
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +486 -0
- package/dist/lib/browserContext.d.ts.map +1 -0
- package/dist/lib/browserContext.js +937 -0
- package/dist/lib/browserContext.js.map +1 -0
- package/dist/lib/builder.d.ts +3 -0
- package/dist/lib/builder.d.ts.map +1 -1
- package/dist/lib/builder.js +31 -4
- package/dist/lib/builder.js.map +1 -1
- package/dist/lib/by.d.ts +18 -2
- package/dist/lib/by.d.ts.map +1 -1
- package/dist/lib/by.js +20 -2
- package/dist/lib/by.js.map +1 -1
- package/dist/lib/chrome.d.ts +13 -1
- package/dist/lib/chrome.d.ts.map +1 -1
- package/dist/lib/chrome.js +13 -17
- package/dist/lib/chrome.js.map +1 -1
- package/dist/lib/clock.d.ts +115 -0
- package/dist/lib/clock.d.ts.map +1 -0
- package/dist/lib/clock.js +407 -0
- package/dist/lib/clock.js.map +1 -0
- package/dist/lib/driver.d.ts +43 -0
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +120 -0
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/driverManager.d.ts +25 -0
- package/dist/lib/driverManager.d.ts.map +1 -0
- package/dist/lib/driverManager.js +399 -0
- package/dist/lib/driverManager.js.map +1 -0
- package/dist/lib/elementHandle.d.ts +67 -3
- package/dist/lib/elementHandle.d.ts.map +1 -1
- package/dist/lib/elementHandle.js +209 -67
- package/dist/lib/elementHandle.js.map +1 -1
- package/dist/lib/errors.d.ts +71 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +73 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/expect.d.ts +6 -1
- package/dist/lib/expect.d.ts.map +1 -1
- package/dist/lib/expect.js +55 -37
- package/dist/lib/expect.js.map +1 -1
- package/dist/lib/firefox.d.ts +18 -0
- package/dist/lib/firefox.d.ts.map +1 -0
- package/dist/lib/firefox.js +33 -0
- package/dist/lib/firefox.js.map +1 -0
- package/dist/lib/frame.d.ts +57 -0
- package/dist/lib/frame.d.ts.map +1 -0
- package/dist/lib/frame.js +277 -0
- package/dist/lib/frame.js.map +1 -0
- package/dist/lib/keyboard.d.ts +1 -1
- package/dist/lib/keyboard.d.ts.map +1 -1
- package/dist/lib/keyboard.js +1 -1
- package/dist/lib/keyboard.js.map +1 -1
- package/dist/lib/locator.d.ts +71 -2
- package/dist/lib/locator.d.ts.map +1 -1
- package/dist/lib/locator.js +289 -31
- package/dist/lib/locator.js.map +1 -1
- package/dist/lib/mouse.d.ts +1 -1
- package/dist/lib/mouse.d.ts.map +1 -1
- package/dist/lib/mouse.js +1 -1
- package/dist/lib/mouse.js.map +1 -1
- package/dist/lib/page.d.ts +107 -0
- package/dist/lib/page.d.ts.map +1 -0
- package/dist/lib/page.js +388 -0
- package/dist/lib/page.js.map +1 -0
- package/dist/lib/tracing.d.ts +128 -0
- package/dist/lib/tracing.d.ts.map +1 -0
- package/dist/lib/tracing.js +272 -0
- package/dist/lib/tracing.js.map +1 -0
- package/dist/lib/wait.d.ts.map +1 -1
- package/dist/lib/wait.js +14 -1
- package/dist/lib/wait.js.map +1 -1
- package/dist/lib/webelement.d.ts +2 -0
- package/dist/lib/webelement.d.ts.map +1 -1
- package/dist/lib/webelement.js +18 -0
- package/dist/lib/webelement.js.map +1 -1
- package/docs/accessibility.md +177 -0
- package/docs/api-reference.md +71 -0
- package/docs/assertions.md +182 -0
- package/docs/bidi-features.md +462 -0
- package/docs/browser-api.md +673 -0
- package/docs/browser-context.md +522 -0
- package/docs/cli.md +233 -0
- package/docs/clock.md +245 -0
- package/docs/dialogs.md +143 -0
- package/docs/driver-configuration.md +64 -0
- package/docs/element-api.md +219 -0
- package/docs/emulation.md +155 -0
- package/docs/error-codes.md +67 -0
- package/docs/getting-started.md +168 -0
- package/docs/keyboard-mouse.md +250 -0
- package/docs/mcp.md +250 -0
- package/docs/mobile-emulation.md +194 -0
- package/docs/screenshots.md +146 -0
- package/docs/selectors.md +382 -0
- package/docs/session-management.md +229 -0
- package/docs/tracing.md +282 -0
- package/package.json +23 -6
- package/skills/craftdriver/SKILL.md +84 -0
- package/skills/craftdriver/cheatsheet.md +194 -0
- package/skills/craftdriver/cli.md +104 -0
- package/skills/craftdriver/patterns.md +139 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI entry point — `bin/craftdriver` invokes this after compile.
|
|
3
|
+
*
|
|
4
|
+
* Three modes:
|
|
5
|
+
*
|
|
6
|
+
* 1. `daemon start` — fork into a long-running daemon process.
|
|
7
|
+
* 2. `--ephemeral` — short-lived browser, read commands from stdin
|
|
8
|
+
* (one per line). Required for sandboxed cloud
|
|
9
|
+
* agents that can't keep a daemon between calls.
|
|
10
|
+
* 3. default — talk to the running daemon over the socket;
|
|
11
|
+
* auto-start one if none is up.
|
|
12
|
+
*/
|
|
13
|
+
import { spawn } from 'child_process';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { Browser } from '../lib/browser.js';
|
|
16
|
+
import { ErrorCode } from '../lib/errors.js';
|
|
17
|
+
import { createBrowserHandle, dispatch } from './dispatcher.js';
|
|
18
|
+
import { parseArgv, HELP_TEXT } from './parseArgs.js';
|
|
19
|
+
import { DaemonClient } from './client.js';
|
|
20
|
+
import { runDaemon, toWireError } from './daemon.js';
|
|
21
|
+
import { DAEMON_SOCKET_PATH } from './defaults.js';
|
|
22
|
+
import { runInit, SUPPORTED_FLAVORS } from './init.js';
|
|
23
|
+
import { runMcpServer } from './mcp/server.js';
|
|
24
|
+
const VERSION = '0.1.0';
|
|
25
|
+
export async function main(argv) {
|
|
26
|
+
const parsed = parseArgv(argv);
|
|
27
|
+
if (parsed === null) {
|
|
28
|
+
process.stdout.write(HELP_TEXT);
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
if (parsed.cmd === '__version__') {
|
|
32
|
+
process.stdout.write(VERSION + '\n');
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
if (parsed.flags.version) {
|
|
36
|
+
process.stdout.write(VERSION + '\n');
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
if (parsed.cmd === '__help__' || parsed.flags.help) {
|
|
40
|
+
process.stdout.write(HELP_TEXT);
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
if (parsed.cmd === '__unknown__') {
|
|
44
|
+
process.stderr.write(`error: unknown command "${parsed.args.cmd}"\nrun: craftdriver --help\n`);
|
|
45
|
+
return 2;
|
|
46
|
+
}
|
|
47
|
+
applyHeadless(parsed.flags);
|
|
48
|
+
// ------------------------------------------------------------------
|
|
49
|
+
// `init <flavor>` — no browser, no daemon. Writes agent-guide files
|
|
50
|
+
// into the current project.
|
|
51
|
+
// ------------------------------------------------------------------
|
|
52
|
+
if (parsed.cmd === 'init')
|
|
53
|
+
return runInitCommand(parsed);
|
|
54
|
+
// ------------------------------------------------------------------
|
|
55
|
+
// `mcp` — long-lived JSON-RPC server on stdio. Browser is launched
|
|
56
|
+
// lazily on first tool call. No daemon involved (the MCP process is
|
|
57
|
+
// itself the long-lived session).
|
|
58
|
+
// ------------------------------------------------------------------
|
|
59
|
+
if (parsed.cmd === 'mcp') {
|
|
60
|
+
await runMcpServer({ launch: parsed.flags.launch });
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
// ------------------------------------------------------------------
|
|
64
|
+
// Daemon control commands — handled in the foreground process.
|
|
65
|
+
// ------------------------------------------------------------------
|
|
66
|
+
if (parsed.cmd === 'daemon:start')
|
|
67
|
+
return daemonStart(parsed.flags);
|
|
68
|
+
if (parsed.cmd === 'daemon:stop')
|
|
69
|
+
return daemonStop(parsed);
|
|
70
|
+
if (parsed.cmd === 'daemon:status')
|
|
71
|
+
return daemonStatus(parsed);
|
|
72
|
+
if (parsed.cmd === 'daemon:run')
|
|
73
|
+
return daemonRun(parsed.flags); // internal: actual daemon process
|
|
74
|
+
// ------------------------------------------------------------------
|
|
75
|
+
// Ephemeral mode — no daemon involved.
|
|
76
|
+
// ------------------------------------------------------------------
|
|
77
|
+
if (parsed.flags.ephemeral)
|
|
78
|
+
return runEphemeral(parsed);
|
|
79
|
+
// ------------------------------------------------------------------
|
|
80
|
+
// Single command via daemon. Auto-start if needed.
|
|
81
|
+
// ------------------------------------------------------------------
|
|
82
|
+
if (!(await DaemonClient.isRunning())) {
|
|
83
|
+
const ok = await autoStartDaemon(parsed.flags);
|
|
84
|
+
if (!ok) {
|
|
85
|
+
process.stderr.write('error: could not start daemon\ncode: DRIVER_ERROR\nhint: try `craftdriver daemon start` to see launch errors\n');
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const client = new DaemonClient();
|
|
90
|
+
try {
|
|
91
|
+
const resp = await client.send(parsed.cmd, parsed.args);
|
|
92
|
+
return emitResponse(parsed, resp);
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
process.stderr.write('error: ' + (e.message ?? String(e)) + '\ncode: DRIVER_ERROR\n');
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function applyHeadless(flags) {
|
|
100
|
+
if (flags.headless === true)
|
|
101
|
+
process.env.HEADLESS = 'true';
|
|
102
|
+
else if (flags.headless === false)
|
|
103
|
+
process.env.HEADLESS = 'false';
|
|
104
|
+
// Default to headless on the agent surface — interactive agents rarely
|
|
105
|
+
// want a window popping up, and tests expect HEADLESS=true.
|
|
106
|
+
else if (process.env.HEADLESS === undefined)
|
|
107
|
+
process.env.HEADLESS = 'true';
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Ephemeral mode
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
async function runEphemeral(parsed) {
|
|
113
|
+
const handle = createBrowserHandle(() => Browser.launch(parsed.flags.launch));
|
|
114
|
+
let rc = 0;
|
|
115
|
+
try {
|
|
116
|
+
if (parsed.cmd === '__stdin__') {
|
|
117
|
+
const lines = await readAllStdin();
|
|
118
|
+
for (const raw of lines.split('\n')) {
|
|
119
|
+
const line = raw.trim();
|
|
120
|
+
if (!line || line.startsWith('#'))
|
|
121
|
+
continue;
|
|
122
|
+
const tokens = tokenize(line);
|
|
123
|
+
const sub = parseArgv(tokens);
|
|
124
|
+
if (!sub || sub.cmd.startsWith('__'))
|
|
125
|
+
continue;
|
|
126
|
+
const result = await safeDispatch(handle, sub);
|
|
127
|
+
rc = emitInProc(sub, result) || rc;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const result = await safeDispatch(handle, parsed);
|
|
132
|
+
rc = emitInProc(parsed, result);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
await handle.close();
|
|
137
|
+
}
|
|
138
|
+
return rc;
|
|
139
|
+
}
|
|
140
|
+
async function safeDispatch(handle, parsed) {
|
|
141
|
+
try {
|
|
142
|
+
const value = await dispatch({ handle, launchOptions: parsed.flags.launch }, parsed.cmd, parsed.args);
|
|
143
|
+
return { ok: true, value };
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
return { ok: false, error: toWireError(e) };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function emitInProc(parsed, outcome) {
|
|
150
|
+
if (outcome.ok) {
|
|
151
|
+
writeOk(parsed, outcome.value);
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
writeErr(outcome.error);
|
|
155
|
+
return exitCodeFor(outcome.error.code);
|
|
156
|
+
}
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// init
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
function runInitCommand(parsed) {
|
|
161
|
+
const flavor = String(parsed.args.flavor ?? '').toLowerCase();
|
|
162
|
+
const allowed = new Set([...SUPPORTED_FLAVORS, 'all']);
|
|
163
|
+
if (!allowed.has(flavor)) {
|
|
164
|
+
const list = [...SUPPORTED_FLAVORS, 'all'].join(' | ');
|
|
165
|
+
process.stderr.write(`error: init requires a flavor (${list})\n` +
|
|
166
|
+
`code: INVALID_ARGUMENT\n` +
|
|
167
|
+
`hint: example: craftdriver init copilot\n`);
|
|
168
|
+
return 2;
|
|
169
|
+
}
|
|
170
|
+
const force = parsed.args.force === true;
|
|
171
|
+
const dryRun = parsed.args['dry-run'] === true || parsed.args.dryRun === true;
|
|
172
|
+
const result = runInit({ flavor: flavor, cwd: process.cwd(), force, dryRun });
|
|
173
|
+
const prefix = dryRun ? '[dry-run] ' : '';
|
|
174
|
+
for (const p of result.written)
|
|
175
|
+
process.stdout.write(`${prefix}wrote ${p}\n`);
|
|
176
|
+
for (const p of result.skipped) {
|
|
177
|
+
process.stdout.write(`skipped ${p} (already exists; use --force to overwrite)\n`);
|
|
178
|
+
}
|
|
179
|
+
if (result.written.length === 0 && result.skipped.length > 0)
|
|
180
|
+
return 1;
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// Daemon control
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
async function daemonStart(flags) {
|
|
187
|
+
if (await DaemonClient.isRunning()) {
|
|
188
|
+
const pid = DaemonClient.getPid();
|
|
189
|
+
process.stdout.write(`daemon already running (pid ${pid ?? '?'})\n`);
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
const ok = await autoStartDaemon(flags);
|
|
193
|
+
if (!ok) {
|
|
194
|
+
process.stderr.write('error: failed to start daemon\n');
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
197
|
+
const pid = DaemonClient.getPid();
|
|
198
|
+
process.stdout.write(`daemon started (pid ${pid ?? '?'}) on ${DAEMON_SOCKET_PATH}\n`);
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
async function daemonStop(parsed) {
|
|
202
|
+
if (!(await DaemonClient.isRunning())) {
|
|
203
|
+
process.stdout.write('daemon not running\n');
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
const client = new DaemonClient();
|
|
207
|
+
try {
|
|
208
|
+
await client.send('daemon:stop');
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// expected — the daemon may close before flushing.
|
|
212
|
+
}
|
|
213
|
+
// Wait briefly for socket/PID cleanup.
|
|
214
|
+
for (let i = 0; i < 20; i++) {
|
|
215
|
+
if (!(await DaemonClient.isRunning()))
|
|
216
|
+
break;
|
|
217
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
218
|
+
}
|
|
219
|
+
process.stdout.write('daemon stopped\n');
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
async function daemonStatus(parsed) {
|
|
223
|
+
const running = await DaemonClient.isRunning();
|
|
224
|
+
const pid = DaemonClient.getPid();
|
|
225
|
+
if (!running) {
|
|
226
|
+
const result = { running: false, pid: null, socket: DAEMON_SOCKET_PATH };
|
|
227
|
+
writeOk(parsed, result);
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
let info = { running: true, pid, socket: DAEMON_SOCKET_PATH };
|
|
231
|
+
try {
|
|
232
|
+
const client = new DaemonClient();
|
|
233
|
+
const resp = await client.send('status');
|
|
234
|
+
if (resp.ok)
|
|
235
|
+
info = { ...info, ...resp.result };
|
|
236
|
+
}
|
|
237
|
+
catch { /* ignore */ }
|
|
238
|
+
writeOk(parsed, info);
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
241
|
+
async function daemonRun(flags) {
|
|
242
|
+
// Block here forever; runDaemon never resolves on success (it exits on signal).
|
|
243
|
+
await runDaemon({ launch: flags.launch });
|
|
244
|
+
return 0;
|
|
245
|
+
}
|
|
246
|
+
async function autoStartDaemon(flags) {
|
|
247
|
+
const self = process.argv[1] ?? fileURLToPath(import.meta.url);
|
|
248
|
+
const args = ['daemon', '__run__'];
|
|
249
|
+
if (flags.launch.browserName) {
|
|
250
|
+
args.push('--browser', flags.launch.browserName);
|
|
251
|
+
}
|
|
252
|
+
if (flags.headless === true)
|
|
253
|
+
args.push('--headless');
|
|
254
|
+
if (flags.headless === false)
|
|
255
|
+
args.push('--headed');
|
|
256
|
+
const child = spawn(process.execPath, [self, ...args], {
|
|
257
|
+
detached: true,
|
|
258
|
+
stdio: 'ignore',
|
|
259
|
+
env: { ...process.env, HEADLESS: process.env.HEADLESS ?? 'true' },
|
|
260
|
+
});
|
|
261
|
+
child.unref();
|
|
262
|
+
// Wait up to 10s for the socket to appear.
|
|
263
|
+
const deadline = Date.now() + 10_000;
|
|
264
|
+
while (Date.now() < deadline) {
|
|
265
|
+
if (await DaemonClient.isRunning())
|
|
266
|
+
return true;
|
|
267
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
268
|
+
}
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
// Output formatting
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
function shouldJson(parsed) {
|
|
275
|
+
if (parsed.flags.json)
|
|
276
|
+
return true;
|
|
277
|
+
if (parsed.flags.pretty)
|
|
278
|
+
return false;
|
|
279
|
+
return !process.stdout.isTTY;
|
|
280
|
+
}
|
|
281
|
+
function writeOk(parsed, result) {
|
|
282
|
+
if (shouldJson(parsed)) {
|
|
283
|
+
process.stdout.write(JSON.stringify({ ok: true, result }) + '\n');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
// Pretty mode: small dispatcher-aware formatting for the most common cases.
|
|
287
|
+
process.stdout.write(prettyResult(parsed.cmd, result) + '\n');
|
|
288
|
+
}
|
|
289
|
+
function writeErr(err) {
|
|
290
|
+
// Strip the chromedriver/geckodriver stack-trace dump that comes through
|
|
291
|
+
// on transport errors. Agents read `code` and the first line of `message`;
|
|
292
|
+
// the rest is noise.
|
|
293
|
+
const cleanMessage = err.message.split('\n')[0].split('stacktrace')[0].trim();
|
|
294
|
+
if (process.stdout.isTTY) {
|
|
295
|
+
process.stderr.write(`error: ${cleanMessage}\ncode: ${err.code}\n`);
|
|
296
|
+
if (err.hint)
|
|
297
|
+
process.stderr.write(`hint: ${err.hint}\n`);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
process.stdout.write(JSON.stringify({ ok: false, error: { ...err, message: cleanMessage } }) + '\n');
|
|
301
|
+
}
|
|
302
|
+
function emitResponse(parsed, resp) {
|
|
303
|
+
if (resp.ok) {
|
|
304
|
+
writeOk(parsed, resp.result);
|
|
305
|
+
// `exists` is a probe: exit 1 when nothing matched, even though
|
|
306
|
+
// the call itself succeeded. Agents script around the exit code.
|
|
307
|
+
if (parsed.cmd === 'exists') {
|
|
308
|
+
const r = resp.result;
|
|
309
|
+
if (r && r.exists === false)
|
|
310
|
+
return 1;
|
|
311
|
+
}
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
writeErr(resp.error);
|
|
315
|
+
return exitCodeFor(resp.error.code);
|
|
316
|
+
}
|
|
317
|
+
function exitCodeFor(code) {
|
|
318
|
+
if (code === ErrorCode.INVALID_ARGUMENT)
|
|
319
|
+
return 2;
|
|
320
|
+
return 1;
|
|
321
|
+
}
|
|
322
|
+
function prettyResult(cmd, result) {
|
|
323
|
+
if (result === null || result === undefined)
|
|
324
|
+
return '';
|
|
325
|
+
if (typeof result === 'string')
|
|
326
|
+
return result;
|
|
327
|
+
if (typeof result === 'number' || typeof result === 'boolean')
|
|
328
|
+
return String(result);
|
|
329
|
+
const r = result;
|
|
330
|
+
if (cmd === 'find' && Array.isArray(r.matches)) {
|
|
331
|
+
const lines = r.matches.map((m, i) => {
|
|
332
|
+
const idx = m.index ?? i;
|
|
333
|
+
const tag = m.tag ?? '';
|
|
334
|
+
const text = m.text ?? '';
|
|
335
|
+
const vis = m.visible ? '' : ' (hidden)';
|
|
336
|
+
return `[${idx}] <${tag}>${vis} "${text}"`;
|
|
337
|
+
});
|
|
338
|
+
const header = `count=${r.count}${r.truncated ? ' (truncated)' : ''}`;
|
|
339
|
+
const trailer = r.truncated && r.total !== undefined && r.next_offset !== undefined && r.next_offset !== null
|
|
340
|
+
? `\n... ${r.total} total; resume with --offset ${r.next_offset}`
|
|
341
|
+
: '';
|
|
342
|
+
return [header, ...lines].join('\n') + trailer;
|
|
343
|
+
}
|
|
344
|
+
if (cmd === 'pages' && Array.isArray(r.pages)) {
|
|
345
|
+
return r.pages
|
|
346
|
+
.map((p, i) => `[${i}] ${p.url ?? ''} — ${p.title ?? ''}`)
|
|
347
|
+
.join('\n');
|
|
348
|
+
}
|
|
349
|
+
if (cmd === 'snapshot' && Array.isArray(r.lines)) {
|
|
350
|
+
const header = `page: ${r.title || '(untitled)'} — ${r.url || '(no url)'}`;
|
|
351
|
+
const lines = r.lines;
|
|
352
|
+
if (lines.length === 0)
|
|
353
|
+
return `${header}\n(no interactive elements detected)`;
|
|
354
|
+
return `${header}\n${lines.join('\n')}`;
|
|
355
|
+
}
|
|
356
|
+
return JSON.stringify(result, null, 2);
|
|
357
|
+
}
|
|
358
|
+
// ---------------------------------------------------------------------------
|
|
359
|
+
// Helpers
|
|
360
|
+
// ---------------------------------------------------------------------------
|
|
361
|
+
function tokenize(line) {
|
|
362
|
+
// Minimal shell-like tokeniser: supports single/double quotes.
|
|
363
|
+
const out = [];
|
|
364
|
+
let cur = '';
|
|
365
|
+
let quote = null;
|
|
366
|
+
for (const ch of line) {
|
|
367
|
+
if (quote) {
|
|
368
|
+
if (ch === quote) {
|
|
369
|
+
quote = null;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
cur += ch;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (ch === '"' || ch === "'") {
|
|
376
|
+
quote = ch;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (ch === ' ' || ch === '\t') {
|
|
380
|
+
if (cur) {
|
|
381
|
+
out.push(cur);
|
|
382
|
+
cur = '';
|
|
383
|
+
}
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
cur += ch;
|
|
387
|
+
}
|
|
388
|
+
if (cur)
|
|
389
|
+
out.push(cur);
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
async function readAllStdin() {
|
|
393
|
+
return new Promise((resolve) => {
|
|
394
|
+
let buf = '';
|
|
395
|
+
process.stdin.setEncoding('utf8');
|
|
396
|
+
process.stdin.on('data', (c) => { buf += c; });
|
|
397
|
+
process.stdin.on('end', () => resolve(buf));
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
// Handle the internal "daemon __run__" invocation used by autoStartDaemon.
|
|
401
|
+
// Map sub-token "__run__" → "run" so parseArgv produces `daemon:run`.
|
|
402
|
+
if (process.argv[3] === '__run__')
|
|
403
|
+
process.argv[3] = 'run';
|
|
404
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type Flavor = 'agents' | 'copilot' | 'claude' | 'cursor' | 'gemini' | 'all';
|
|
2
|
+
export declare const SUPPORTED_FLAVORS: ReadonlyArray<Exclude<Flavor, 'all'>>;
|
|
3
|
+
/** Single source of truth for the body each adapter writes. */
|
|
4
|
+
export declare function buildBody(): string;
|
|
5
|
+
export interface InitResult {
|
|
6
|
+
/** Files actually written (or that would be written, in dry-run mode). */
|
|
7
|
+
written: string[];
|
|
8
|
+
/** Files that already existed and were skipped (no --force). */
|
|
9
|
+
skipped: string[];
|
|
10
|
+
/** When true, no files were touched on disk. */
|
|
11
|
+
dryRun: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface InitOptions {
|
|
14
|
+
flavor: Flavor;
|
|
15
|
+
cwd: string;
|
|
16
|
+
force?: boolean;
|
|
17
|
+
dryRun?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the flavor list, render the body once, and write each target.
|
|
21
|
+
* Returns the list of paths so the caller can print a summary.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runInit(opts: InitOptions): InitResult;
|
|
24
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEnF,eAAO,MAAM,iBAAiB,EAAE,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAMnE,CAAC;AAwCF,+DAA+D;AAC/D,wBAAgB,SAAS,IAAI,MAAM,CAkHlC;AAED,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gEAAgE;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gDAAgD;IAChD,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAyBrD"}
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `craftdriver init <flavor>` — write an agent-guide file into the
|
|
3
|
+
* current project. Each flavor maps to one or more well-known paths
|
|
4
|
+
* that a specific AI assistant reads on every turn.
|
|
5
|
+
*
|
|
6
|
+
* One body of text, N filenames. Adapters can prepend a flavor-specific
|
|
7
|
+
* header (e.g. Cursor's `.mdc` frontmatter) but the rules below the
|
|
8
|
+
* header are identical across flavors so every agent on a team gets the
|
|
9
|
+
* same instructions.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
12
|
+
import { dirname, join, relative } from 'path';
|
|
13
|
+
export const SUPPORTED_FLAVORS = [
|
|
14
|
+
'agents',
|
|
15
|
+
'copilot',
|
|
16
|
+
'claude',
|
|
17
|
+
'cursor',
|
|
18
|
+
'gemini',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Map a flavor to the paths it writes, relative to the project root.
|
|
22
|
+
* Keep this small and conventional — anything more specific belongs in
|
|
23
|
+
* each tool's own docs.
|
|
24
|
+
*/
|
|
25
|
+
function targetsFor(flavor) {
|
|
26
|
+
switch (flavor) {
|
|
27
|
+
case 'agents':
|
|
28
|
+
// AGENTS.md is the cross-tool convention picked up by Codex,
|
|
29
|
+
// OpenCode, Aider, Amp, and also Cursor.
|
|
30
|
+
return [{ path: 'AGENTS.md' }];
|
|
31
|
+
case 'copilot':
|
|
32
|
+
return [{ path: '.github/copilot-instructions.md' }];
|
|
33
|
+
case 'claude':
|
|
34
|
+
return [{ path: 'CLAUDE.md' }];
|
|
35
|
+
case 'cursor':
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
path: '.cursor/rules/craftdriver.mdc',
|
|
39
|
+
header: '---\n' +
|
|
40
|
+
'description: craftdriver — agent rules for browser automation tests\n' +
|
|
41
|
+
'globs: ["**/*.{ts,tsx,js,jsx}"]\n' +
|
|
42
|
+
'alwaysApply: true\n' +
|
|
43
|
+
'---\n\n',
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
case 'gemini':
|
|
47
|
+
return [{ path: 'GEMINI.md' }];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Single source of truth for the body each adapter writes. */
|
|
51
|
+
export function buildBody() {
|
|
52
|
+
return `# Working with craftdriver
|
|
53
|
+
|
|
54
|
+
This project uses [craftdriver](https://www.npmjs.com/package/craftdriver),
|
|
55
|
+
a BiDi-first WebDriver library for Node.js. Follow these rules whenever
|
|
56
|
+
you write or modify browser-automation code in this repo.
|
|
57
|
+
|
|
58
|
+
## Use craftdriver, not raw Selenium or Playwright
|
|
59
|
+
|
|
60
|
+
\`\`\`ts
|
|
61
|
+
import { Browser, By, expect } from 'craftdriver';
|
|
62
|
+
|
|
63
|
+
const browser = await Browser.launch({ browserName: 'chrome' });
|
|
64
|
+
await browser.navigateTo('https://example.com/login');
|
|
65
|
+
await browser.fill('#username', 'alice');
|
|
66
|
+
await browser.click('button[type=submit]');
|
|
67
|
+
await expect(browser.locator('#welcome')).toHaveText(/welcome/i);
|
|
68
|
+
await browser.quit();
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
The public surface is whatever \`src/index.ts\` of \`craftdriver\` re-exports.
|
|
72
|
+
Anything under \`craftdriver/src/lib/\` is internal — do not import it.
|
|
73
|
+
|
|
74
|
+
## Selector preference order
|
|
75
|
+
|
|
76
|
+
Prefer stable, semantic locators. They survive DOM refactors; CSS and
|
|
77
|
+
XPath break the moment markup shifts.
|
|
78
|
+
|
|
79
|
+
\`\`\`
|
|
80
|
+
By.testId > By.role({ name }) > By.labelText >
|
|
81
|
+
By.text({ exact: true }) > By.css > By.xpath
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
String selectors passed directly to \`browser.click('…')\` etc. are
|
|
85
|
+
treated as CSS. Use \`By.*\` helpers or \`browser.getBy*()\` when you
|
|
86
|
+
want a semantic locator.
|
|
87
|
+
|
|
88
|
+
## Auto-waiting — do not add sleeps
|
|
89
|
+
|
|
90
|
+
Every action and every \`expect(locator).to…()\` already waits up to the
|
|
91
|
+
default timeout (30 s in-library, 5 s on the CLI). **Never** add
|
|
92
|
+
\`setTimeout\`, \`sleep\`, or hand-rolled \`while (…) { … }\` retry loops.
|
|
93
|
+
They are footguns: actions wait for the right condition; sleeps wait
|
|
94
|
+
for the wrong amount of time.
|
|
95
|
+
|
|
96
|
+
\`\`\`ts
|
|
97
|
+
// Good
|
|
98
|
+
await expect(browser.locator('#status')).toHaveText('Ready');
|
|
99
|
+
|
|
100
|
+
// Bad
|
|
101
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
102
|
+
expect(await browser.text('#status')).toBe('Ready');
|
|
103
|
+
\`\`\`
|
|
104
|
+
|
|
105
|
+
## Errors carry a stable \`code\`
|
|
106
|
+
|
|
107
|
+
Every error thrown by the public API is a \`CraftdriverError\` with a
|
|
108
|
+
stable \`code\` field. Match on the code, not the prose:
|
|
109
|
+
|
|
110
|
+
\`\`\`ts
|
|
111
|
+
import { CraftdriverError, ErrorCode } from 'craftdriver';
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
await browser.click('#submit');
|
|
115
|
+
} catch (e) {
|
|
116
|
+
if (e instanceof CraftdriverError && e.code === ErrorCode.NO_MATCH) {
|
|
117
|
+
// selector matched zero elements — fix the selector
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
\`\`\`
|
|
121
|
+
|
|
122
|
+
Common codes: \`NO_MATCH\`, \`TIMEOUT_WAITING_VISIBLE\`,
|
|
123
|
+
\`TIMEOUT_WAITING_STATE\`, \`TIMEOUT_WAITING_LOAD\`, \`EXPECT_MISMATCH\`,
|
|
124
|
+
\`INVALID_ARGUMENT\`, \`UNSUPPORTED\`. Full list in
|
|
125
|
+
\`node_modules/craftdriver/docs/error-codes.md\`.
|
|
126
|
+
|
|
127
|
+
## CLI for exploration and agent loops
|
|
128
|
+
|
|
129
|
+
For ad-hoc probing from a shell — including agent-driven loops —
|
|
130
|
+
craftdriver ships a \`craftdriver\` binary:
|
|
131
|
+
|
|
132
|
+
\`\`\`bash
|
|
133
|
+
npx craftdriver daemon start
|
|
134
|
+
npx craftdriver go http://127.0.0.1:8080/login.html
|
|
135
|
+
npx craftdriver exists '#submit' # 0-wait probe; exit 0 = match
|
|
136
|
+
npx craftdriver fill '#username' alice
|
|
137
|
+
npx craftdriver click 'button[type=submit]'
|
|
138
|
+
npx craftdriver text '#welcome'
|
|
139
|
+
npx craftdriver daemon stop
|
|
140
|
+
\`\`\`
|
|
141
|
+
|
|
142
|
+
Selectors are CSS by default; switch with \`prefix=value\`
|
|
143
|
+
(\`role=button[name=Submit]\`, \`text=Sign In\`, \`label=Email\`,
|
|
144
|
+
\`testid=login-btn\`, \`xpath=…\`). Exit codes: \`0\` success,
|
|
145
|
+
\`1\` assertion/timeout/no-match, \`2\` usage error.
|
|
146
|
+
|
|
147
|
+
Probe before acting on a guess: \`craftdriver exists …\` is a 0-wait
|
|
148
|
+
check that returns immediately. Don't \`click\` a hallucinated selector
|
|
149
|
+
and burn the full timeout.
|
|
150
|
+
|
|
151
|
+
Full reference: \`node_modules/craftdriver/docs/cli.md\`.
|
|
152
|
+
|
|
153
|
+
## Pointers
|
|
154
|
+
|
|
155
|
+
- \`node_modules/craftdriver/docs/api-reference.md\` — every public
|
|
156
|
+
symbol, one row each. Read this before inventing a method.
|
|
157
|
+
- \`node_modules/craftdriver/docs/error-codes.md\` — every error code,
|
|
158
|
+
when it fires, recommended recovery.
|
|
159
|
+
- \`node_modules/craftdriver/docs/cli.md\` — full CLI reference.
|
|
160
|
+
- \`node_modules/craftdriver/skills/craftdriver/\` — agent-facing SKILL
|
|
161
|
+
pack: \`SKILL.md\` (always-on rules), \`cheatsheet.md\` (writing tests),
|
|
162
|
+
\`patterns.md\` (login/upload/network-wait recipes), \`cli.md\` (CLI from
|
|
163
|
+
an agent's POV).
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Resolve the flavor list, render the body once, and write each target.
|
|
168
|
+
* Returns the list of paths so the caller can print a summary.
|
|
169
|
+
*/
|
|
170
|
+
export function runInit(opts) {
|
|
171
|
+
const flavors = opts.flavor === 'all' ? [...SUPPORTED_FLAVORS] : [opts.flavor];
|
|
172
|
+
const body = buildBody();
|
|
173
|
+
const written = [];
|
|
174
|
+
const skipped = [];
|
|
175
|
+
for (const flavor of flavors) {
|
|
176
|
+
for (const target of targetsFor(flavor)) {
|
|
177
|
+
const abs = join(opts.cwd, target.path);
|
|
178
|
+
if (existsSync(abs) && !opts.force) {
|
|
179
|
+
skipped.push(relative(opts.cwd, abs));
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const content = (target.header ?? '') + body;
|
|
183
|
+
if (!opts.dryRun) {
|
|
184
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
185
|
+
writeFileSync(abs, content, 'utf8');
|
|
186
|
+
}
|
|
187
|
+
written.push(relative(opts.cwd, abs));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { written, skipped, dryRun: opts.dryRun === true };
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAI/C,MAAM,CAAC,MAAM,iBAAiB,GAA0C;IACtE,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,QAAQ;CACT,CAAC;AAQF;;;;GAIG;AACH,SAAS,UAAU,CAAC,MAA8B;IAChD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,6DAA6D;YAC7D,yCAAyC;YACzC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QACjC,KAAK,SAAS;YACZ,OAAO,CAAC,EAAE,IAAI,EAAE,iCAAiC,EAAE,CAAC,CAAC;QACvD,KAAK,QAAQ;YACX,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QACjC,KAAK,QAAQ;YACX,OAAO;gBACL;oBACE,IAAI,EAAE,+BAA+B;oBACrC,MAAM,EACJ,OAAO;wBACP,uEAAuE;wBACvE,mCAAmC;wBACnC,qBAAqB;wBACrB,SAAS;iBACZ;aACF,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,SAAS;IACvB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgHR,CAAC;AACF,CAAC;AAkBD;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,IAAiB;IACvC,MAAM,OAAO,GACX,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjE,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;gBACtC,SAAS;YACX,CAAC;YACD,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC7C,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;AAC5D,CAAC"}
|