waygraph 0.15.22 → 0.15.24

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 (58) hide show
  1. package/dist/cli/discover.d.ts +40 -0
  2. package/dist/cli/discover.js +148 -0
  3. package/dist/cli/flags.d.ts +91 -0
  4. package/dist/cli/flags.js +442 -0
  5. package/dist/cli/run-chain.d.ts +8 -0
  6. package/dist/cli/run-chain.js +67 -0
  7. package/dist/cli/usage.d.ts +4 -0
  8. package/dist/cli/usage.js +236 -0
  9. package/dist/cli/util.d.ts +4 -0
  10. package/dist/cli/util.js +24 -0
  11. package/dist/cli.js +45 -8494
  12. package/dist/commands/agent-dive.d.ts +1 -0
  13. package/dist/commands/agent-dive.js +52 -0
  14. package/dist/commands/check.d.ts +4 -0
  15. package/dist/commands/check.js +192 -0
  16. package/dist/commands/init.d.ts +1 -0
  17. package/dist/commands/init.js +54 -0
  18. package/dist/commands/list.d.ts +2 -0
  19. package/dist/commands/list.js +67 -0
  20. package/dist/commands/map.d.ts +1 -0
  21. package/dist/commands/map.js +29 -0
  22. package/dist/commands/run.d.ts +3 -0
  23. package/dist/commands/run.js +104 -0
  24. package/dist/commands/session.d.ts +2 -0
  25. package/dist/commands/session.js +600 -0
  26. package/dist/commands/test.d.ts +1 -0
  27. package/dist/commands/test.js +33 -0
  28. package/dist/commands/traverse.d.ts +1 -0
  29. package/dist/commands/traverse.js +177 -0
  30. package/dist/commands/try.d.ts +1 -0
  31. package/dist/commands/try.js +298 -0
  32. package/dist/runner/chain-run.js +102 -0
  33. package/dist/runner/cursor.js +140 -0
  34. package/dist/runner/demo-log.js +314 -0
  35. package/dist/runner/device-stage.js +400 -0
  36. package/dist/runner/discovery.js +105 -0
  37. package/dist/runner/index.js +2 -0
  38. package/dist/runner/interaction-patch.js +353 -0
  39. package/dist/runner/main.js +397 -0
  40. package/dist/runner/overlay-css.js +342 -0
  41. package/dist/runner/overlay-install.js +1301 -0
  42. package/dist/runner/rings.js +490 -0
  43. package/dist/runner/seed-mem.js +110 -0
  44. package/dist/runner/slides.js +272 -0
  45. package/dist/runner/step-mode.js +776 -0
  46. package/dist/runner/step-panels.js +723 -0
  47. package/dist/runner/teardown.js +195 -0
  48. package/dist/runner/todo-dock.js +142 -0
  49. package/examples/saucedemo/.waygraph-auto/0ac58118.log +0 -0
  50. package/examples/saucedemo/.waygraph-auto/309c107f.log +0 -0
  51. package/examples/saucedemo/.waygraph-auto/3ab7c7bd.log +44 -0
  52. package/examples/saucedemo/.waygraph-auto/45864828.log +0 -0
  53. package/examples/saucedemo/.waygraph-auto/95bd8371.log +0 -0
  54. package/examples/saucedemo/.waygraph-auto/bcb9e31a.log +0 -0
  55. package/examples/saucedemo/.waygraph-auto/cd3e528c.log +0 -0
  56. package/examples/saucedemo/.waygraph-auto/dce1535d.log +0 -0
  57. package/examples/saucedemo/.waygraph-auto/e280bf20.log +0 -0
  58. package/package.json +5 -2
@@ -0,0 +1,600 @@
1
+ // Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
2
+ import { printBrowserSessionsList, printBrowserUsage } from "../cli/usage.js";
3
+ import { applyRunFlags, looksLikeFlowFileRef, parseRunFlags, resolveBaseUrl } from "../cli/flags.js";
4
+ import { runChain } from "../cli/run-chain.js";
5
+ import { runChainAuto } from "./check.js";
6
+ import { resolve } from "node:path";
7
+ import { browserStart, listBrowserSessions, stopAllBrowserSessions, stopBrowserSession } from "../browser.js";
8
+ import { existsSync, statSync } from "node:fs";
9
+ import { resolveInjectRoots } from "../block-inject.js";
10
+ import { requestSession, resolveSessionMeta, runAttachLoop, runAutoServeCommand, spawnDetachedSession } from "../auto-session-ipc.js";
11
+ import { pilotAttach, pilotStart } from "../pilot.js";
12
+ import { parseHighlightShorthand } from "../highlight-shorthand.js";
13
+ import { isFileSelectToken, parseBlocksSelect } from "../blocks-select.js";
14
+ import { runAutoExplore } from "../auto-explore-run.js";
15
+ import { discoverGraph, findOrphanBlocks, toMermaid } from "../graph.js";
16
+ export async function sessionCase(args, command) {
17
+ if (command === "browser") {
18
+ const sub = args[1];
19
+ if (sub === "sessions") {
20
+ const rest = args.slice(2);
21
+ const json = rest.includes("--json");
22
+ const positional = rest.filter((a) => a !== "--json");
23
+ const proj = resolve(positional[0] ?? process.cwd());
24
+ const sessions = listBrowserSessions(proj);
25
+ if (json) {
26
+ console.log(JSON.stringify(sessions, null, 2));
27
+ }
28
+ else {
29
+ printBrowserSessionsList(sessions, proj);
30
+ }
31
+ return;
32
+ }
33
+ if (sub === "stop") {
34
+ const proj = resolve(args[3] ?? process.cwd());
35
+ const target = args[2];
36
+ if (!target) {
37
+ console.error("waygraph browser stop: usage: waygraph browser stop <sessionId|--all> [project]");
38
+ process.exit(1);
39
+ }
40
+ if (target === "--all") {
41
+ const n = stopAllBrowserSessions(proj);
42
+ console.log(JSON.stringify({ stopped: n, projectDir: proj }));
43
+ return;
44
+ }
45
+ if (!stopBrowserSession(proj, target)) {
46
+ console.error(`waygraph browser stop: no such session "${target}"`);
47
+ process.exit(1);
48
+ }
49
+ console.log(JSON.stringify({ stopped: target }));
50
+ return;
51
+ }
52
+ if (!sub) {
53
+ printBrowserUsage();
54
+ return;
55
+ }
56
+ if (sub === "start" || sub.startsWith("-")) {
57
+ const flagArgs = sub === "start" ? args.slice(2) : args.slice(1);
58
+ const flags = parseRunFlags(flagArgs);
59
+ applyRunFlags(flags);
60
+ const proj = resolve(flags.positionals[0] ?? process.cwd());
61
+ if (!existsSync(proj)) {
62
+ console.error(`waygraph browser: no such directory: ${proj}`);
63
+ process.exit(1);
64
+ }
65
+ const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
66
+ try {
67
+ const inject = flags.inject?.length ? resolveInjectRoots(flags.inject, proj) : undefined;
68
+ const result = await browserStart({
69
+ projectDir: proj,
70
+ ...(inject?.length ? { inject } : {}),
71
+ ...(baseURL ? { baseURL } : {}),
72
+ ...(flags.goto
73
+ ? { startUrl: flags.goto, skipInitialNavigation: false }
74
+ : { skipInitialNavigation: flags.blank !== false }),
75
+ headless: flags.headlessBrowser === true,
76
+ });
77
+ console.log(JSON.stringify(result));
78
+ if (!result.headless) {
79
+ console.error(`Session ${result.sessionId} started. List all: waygraph browser sessions`);
80
+ }
81
+ if (flags.cli) {
82
+ await runAttachLoop(proj, result.sessionId);
83
+ }
84
+ }
85
+ catch (err) {
86
+ console.error(`waygraph browser start: ${err instanceof Error ? err.message : String(err)}`);
87
+ process.exit(1);
88
+ }
89
+ return;
90
+ }
91
+ if (sub !== "send" &&
92
+ sub !== "status" &&
93
+ sub !== "attach" &&
94
+ sub !== "dom" &&
95
+ sub !== "trace" &&
96
+ sub !== "console" &&
97
+ sub !== "storage" &&
98
+ sub !== "upload" &&
99
+ sub !== "click" &&
100
+ sub !== "type" &&
101
+ sub !== "press" &&
102
+ sub !== "goto" &&
103
+ sub !== "reload" &&
104
+ sub !== "reach" &&
105
+ sub !== "resync" &&
106
+ sub !== "highlight") {
107
+ console.error("waygraph browser: unknown subcommand — run `waygraph browser` for usage");
108
+ printBrowserUsage();
109
+ process.exit(1);
110
+ }
111
+ }
112
+ if (command === "pilot") {
113
+ if (args[1] === "sessions") {
114
+ const rest = args.slice(2);
115
+ const json = rest.includes("--json");
116
+ const positional = rest.filter((a) => a !== "--json");
117
+ const proj = resolve(positional[0] ?? process.cwd());
118
+ const sessions = listBrowserSessions(proj);
119
+ if (json) {
120
+ console.log(JSON.stringify(sessions, null, 2));
121
+ }
122
+ else {
123
+ printBrowserSessionsList(sessions, proj);
124
+ }
125
+ return;
126
+ }
127
+ if (args[1] === "attach") {
128
+ const sessionId = args[2];
129
+ if (!sessionId) {
130
+ console.error("waygraph pilot attach: usage: waygraph pilot attach <sessionId> [--inject …] [project]");
131
+ process.exit(1);
132
+ }
133
+ const flags = parseRunFlags(args.slice(3));
134
+ const proj = resolve(flags.positionals[0] ?? process.cwd());
135
+ try {
136
+ const inject = flags.inject?.length ? resolveInjectRoots(flags.inject, proj) : undefined;
137
+ const result = await pilotAttach(proj, sessionId, inject);
138
+ console.log(JSON.stringify(result));
139
+ }
140
+ catch (err) {
141
+ console.error(`waygraph pilot attach: ${err instanceof Error ? err.message : String(err)}`);
142
+ process.exit(1);
143
+ }
144
+ return;
145
+ }
146
+ if (args[1] === "start" || !args[1] || args[1].startsWith("-")) {
147
+ const flagArgs = args[1] === "start" ? args.slice(2) : args.slice(1);
148
+ const flags = parseRunFlags(flagArgs);
149
+ applyRunFlags(flags);
150
+ const proj = resolve(flags.positionals[0] ?? process.cwd());
151
+ const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
152
+ try {
153
+ const result = await pilotStart({
154
+ projectDir: proj,
155
+ ...(flags.inject?.length ? { injectTokens: flags.inject } : {}),
156
+ ...(baseURL ? { baseURL } : {}),
157
+ ...(flags.goto
158
+ ? { startUrl: flags.goto, skipInitialNavigation: false }
159
+ : flags.blank === true
160
+ ? { skipInitialNavigation: true }
161
+ : {}),
162
+ headless: flags.headlessBrowser === true,
163
+ });
164
+ console.log(JSON.stringify(result));
165
+ if (!result.headless) {
166
+ console.error(`Chromium window opened (session ${result.sessionId}). ` +
167
+ `Terminal menu: waygraph pilot attach ${result.sessionId}`);
168
+ }
169
+ if (flags.cli) {
170
+ await runAttachLoop(proj, result.sessionId);
171
+ }
172
+ }
173
+ catch (err) {
174
+ console.error(`waygraph pilot start: ${err instanceof Error ? err.message : String(err)}`);
175
+ process.exit(1);
176
+ }
177
+ return;
178
+ }
179
+ if (args[1] !== "send" &&
180
+ args[1] !== "status" &&
181
+ args[1] !== "dom" &&
182
+ args[1] !== "trace" &&
183
+ args[1] !== "console" &&
184
+ args[1] !== "storage" &&
185
+ args[1] !== "upload" &&
186
+ args[1] !== "click" &&
187
+ args[1] !== "type" &&
188
+ args[1] !== "press" &&
189
+ args[1] !== "goto" &&
190
+ args[1] !== "reload" &&
191
+ args[1] !== "reach" &&
192
+ args[1] !== "resync" &&
193
+ args[1] !== "highlight") {
194
+ console.error("waygraph pilot: usage:\n" +
195
+ " waygraph pilot start [--inject …] [--goto <url>] [--headless] [--cli] [--base-url <url>] [project]\n" +
196
+ " waygraph pilot sessions [project] # list live browser sessions\n" +
197
+ " waygraph pilot attach <sessionId> # graph + snapshot for an existing session\n" +
198
+ " waygraph pilot send|status|attach|highlight|… <sessionId> … (controls browser — same as browser/auto)");
199
+ process.exit(1);
200
+ }
201
+ }
202
+ // auto send|status|attach <sessionId> - session control against a
203
+ // --detach'd background session. Intercepted before the normal
204
+ // project-directory resolution below, same pattern `try demo|auto|
205
+ // auto:cli` already uses for a sub-verb positional.
206
+ if ((command === "auto" || command === "browser" || command === "pilot") &&
207
+ (args[1] === "send" ||
208
+ args[1] === "status" ||
209
+ (args[1] === "attach" && command !== "pilot") ||
210
+ args[1] === "dom" ||
211
+ args[1] === "trace" ||
212
+ args[1] === "console" ||
213
+ args[1] === "storage" ||
214
+ args[1] === "upload" ||
215
+ args[1] === "click" ||
216
+ args[1] === "type" ||
217
+ args[1] === "press" ||
218
+ args[1] === "goto" ||
219
+ args[1] === "reload" ||
220
+ args[1] === "reach" ||
221
+ args[1] === "resync" ||
222
+ args[1] === "highlight")) {
223
+ const sub = args[1];
224
+ const sessionId = args[2];
225
+ if (!sessionId) {
226
+ console.error(`waygraph ${command} ${sub}: missing <sessionId>`);
227
+ process.exit(1);
228
+ }
229
+ const projHint = resolve(process.cwd());
230
+ const meta = resolveSessionMeta(sessionId, projHint);
231
+ if (!meta) {
232
+ console.error(`waygraph ${command} ${sub}: no such session "${sessionId}" — ` +
233
+ "wrong directory? run `waygraph pilot sessions` from the project that started it");
234
+ process.exit(1);
235
+ }
236
+ const proj = meta.projectDir;
237
+ if (sub === "attach") {
238
+ await runAttachLoop(proj, sessionId);
239
+ return;
240
+ }
241
+ if (sub === "send") {
242
+ const pick = args[3];
243
+ if (pick === undefined) {
244
+ console.error('waygraph auto send: usage: waygraph auto send <sessionId> "<pick>" [--timeout <ms>]');
245
+ process.exit(1);
246
+ }
247
+ // Real, direct need found live: a single Block can legitimately
248
+ // run a genuinely slow real interaction (e.g. a multi-step mouse
249
+ // drag) that exceeds the 15s default - same reasoning `auto
250
+ // reach` already gets a 90s default for (multiple Blocks in one
251
+ // call). Unlike reach, send has no way to know ahead of time
252
+ // whether the ONE Block it's running is fast or slow, so this is
253
+ // opt-in via a flag rather than a raised default.
254
+ let timeoutMs;
255
+ for (let i = 4; i < args.length; i++) {
256
+ if (args[i] === "--timeout") {
257
+ const raw = args[++i];
258
+ const n = Number(raw);
259
+ if (!Number.isFinite(n) || n <= 0) {
260
+ console.error(`waygraph auto send: --timeout must be a positive number of ms, got "${raw}"`);
261
+ process.exit(1);
262
+ }
263
+ timeoutMs = n;
264
+ }
265
+ }
266
+ const res = await requestSession(proj, sessionId, { op: "send", pick }, timeoutMs);
267
+ console.log(JSON.stringify(res));
268
+ if (!res.ok)
269
+ process.exitCode = 1;
270
+ return;
271
+ }
272
+ if (sub === "dom") {
273
+ const domArgs = args.slice(3);
274
+ let mode;
275
+ let selector;
276
+ let depth;
277
+ for (let i = 0; i < domArgs.length; i++) {
278
+ const a = domArgs[i];
279
+ if (a === "--mode" || a.startsWith("--mode=")) {
280
+ const v = a.startsWith("--mode=") ? a.slice("--mode=".length) : domArgs[++i];
281
+ if (v !== "aria" && v !== "full") {
282
+ console.error(`waygraph auto dom: --mode must be "aria" or "full", got "${v}"`);
283
+ process.exit(1);
284
+ }
285
+ mode = v;
286
+ }
287
+ else if (a === "--selector" || a.startsWith("--selector=")) {
288
+ selector = a.startsWith("--selector=") ? a.slice("--selector=".length) : domArgs[++i];
289
+ }
290
+ else if (a === "--depth" || a.startsWith("--depth=")) {
291
+ const raw = a.startsWith("--depth=") ? a.slice("--depth=".length) : domArgs[++i];
292
+ const n = Number(raw);
293
+ if (!Number.isInteger(n) || n < 1) {
294
+ console.error(`waygraph auto dom: --depth must be a positive integer, got "${raw}"`);
295
+ process.exit(1);
296
+ }
297
+ depth = n;
298
+ }
299
+ else {
300
+ console.error(`waygraph auto dom: unrecognized argument "${a}"`);
301
+ process.exit(1);
302
+ }
303
+ }
304
+ const res = await requestSession(proj, sessionId, {
305
+ op: "dom",
306
+ ...(mode ? { mode } : {}),
307
+ ...(selector ? { selector } : {}),
308
+ ...(depth !== undefined ? { depth } : {}),
309
+ });
310
+ console.log(JSON.stringify(res));
311
+ if (!res.ok)
312
+ process.exitCode = 1;
313
+ return;
314
+ }
315
+ if (sub === "trace") {
316
+ const res = await requestSession(proj, sessionId, { op: "trace" });
317
+ console.log(JSON.stringify(res));
318
+ if (!res.ok)
319
+ process.exitCode = 1;
320
+ return;
321
+ }
322
+ if (sub === "console") {
323
+ const res = await requestSession(proj, sessionId, { op: "console" });
324
+ console.log(JSON.stringify(res));
325
+ if (!res.ok)
326
+ process.exitCode = 1;
327
+ return;
328
+ }
329
+ if (sub === "storage") {
330
+ const res = await requestSession(proj, sessionId, { op: "storage" });
331
+ console.log(JSON.stringify(res));
332
+ if (!res.ok)
333
+ process.exitCode = 1;
334
+ return;
335
+ }
336
+ if (sub === "click") {
337
+ const selector = args[3];
338
+ if (selector === undefined) {
339
+ console.error('waygraph auto click: usage: waygraph auto click <sessionId> "<selector>"');
340
+ process.exit(1);
341
+ }
342
+ const res = await requestSession(proj, sessionId, { op: "click", selector });
343
+ console.log(JSON.stringify(res));
344
+ if (!res.ok)
345
+ process.exitCode = 1;
346
+ return;
347
+ }
348
+ if (sub === "type") {
349
+ const selector = args[3];
350
+ const text = args[4];
351
+ if (selector === undefined || text === undefined) {
352
+ console.error('waygraph auto type: usage: waygraph auto type <sessionId> "<selector>" "<text>"');
353
+ process.exit(1);
354
+ }
355
+ const res = await requestSession(proj, sessionId, { op: "type", selector, text });
356
+ console.log(JSON.stringify(res));
357
+ if (!res.ok)
358
+ process.exitCode = 1;
359
+ return;
360
+ }
361
+ if (sub === "press") {
362
+ const selector = args[3];
363
+ const key = args[4];
364
+ if (selector === undefined || key === undefined) {
365
+ console.error('waygraph auto press: usage: waygraph auto press <sessionId> "<selector>" <key>');
366
+ process.exit(1);
367
+ }
368
+ const res = await requestSession(proj, sessionId, { op: "press", selector, key });
369
+ console.log(JSON.stringify(res));
370
+ if (!res.ok)
371
+ process.exitCode = 1;
372
+ return;
373
+ }
374
+ if (sub === "goto") {
375
+ const url = args[3];
376
+ if (url === undefined) {
377
+ console.error("waygraph auto goto: usage: waygraph auto goto <sessionId> <url>");
378
+ process.exit(1);
379
+ }
380
+ const res = await requestSession(proj, sessionId, { op: "goto", url });
381
+ console.log(JSON.stringify(res));
382
+ if (!res.ok)
383
+ process.exitCode = 1;
384
+ return;
385
+ }
386
+ if (sub === "upload") {
387
+ const selector = args[3];
388
+ const kindOrPath = args[4];
389
+ if (selector === undefined || kindOrPath === undefined) {
390
+ console.error('waygraph auto upload: usage: waygraph auto upload <sessionId> "<selector>" <image|pdf|video|path-to-file>');
391
+ process.exit(1);
392
+ }
393
+ const stub = kindOrPath === "image" || kindOrPath === "pdf" || kindOrPath === "video"
394
+ ? { selector, stub: kindOrPath }
395
+ : { selector, stub: { filePath: resolve(process.cwd(), kindOrPath) } };
396
+ const res = await requestSession(proj, sessionId, { op: "upload", ...stub });
397
+ console.log(JSON.stringify(res));
398
+ if (!res.ok)
399
+ process.exitCode = 1;
400
+ return;
401
+ }
402
+ if (sub === "reload") {
403
+ const res = await requestSession(proj, sessionId, { op: "reload" });
404
+ console.log(JSON.stringify(res));
405
+ if (!res.ok)
406
+ process.exitCode = 1;
407
+ return;
408
+ }
409
+ if (sub === "reach") {
410
+ const checkpoint = args[3];
411
+ if (checkpoint === undefined) {
412
+ console.error("waygraph auto reach: usage: waygraph auto reach <sessionId> <Checkpoint>");
413
+ process.exit(1);
414
+ }
415
+ // Longer than every other op's default (15s): a real multi-step
416
+ // route runs several real Blocks in sequence server-side before
417
+ // responding - a legitimately slow single request, not a hang.
418
+ const res = await requestSession(proj, sessionId, { op: "reach", checkpoint }, 90_000);
419
+ console.log(JSON.stringify(res));
420
+ if (!res.ok)
421
+ process.exitCode = 1;
422
+ return;
423
+ }
424
+ if (sub === "resync") {
425
+ const res = await requestSession(proj, sessionId, { op: "resync" });
426
+ console.log(JSON.stringify(res));
427
+ if (!res.ok)
428
+ process.exitCode = 1;
429
+ return;
430
+ }
431
+ if (sub === "highlight") {
432
+ const raw = args[3];
433
+ if (raw === undefined) {
434
+ console.error(`waygraph ${command} highlight: usage:\n` +
435
+ ` waygraph ${command} highlight <sessionId> '{"rings":[{"selector":"#x","label":"X"}]}'\n` +
436
+ ` waygraph ${command} highlight <sessionId> "#x|X" (shorthand: <selector>|<label>[|<tone>], rings separated by ";")`);
437
+ process.exit(1);
438
+ }
439
+ let fixtures;
440
+ try {
441
+ fixtures = JSON.parse(raw);
442
+ }
443
+ catch (jsonErr) {
444
+ const shorthand = parseHighlightShorthand(raw);
445
+ if (shorthand.type === "error") {
446
+ console.error(`waygraph ${command} highlight: body is neither valid JSON (${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}) nor valid shorthand (${shorthand.reason})`);
447
+ process.exit(1);
448
+ }
449
+ fixtures = shorthand.fixtures;
450
+ }
451
+ if (fixtures.op !== undefined && fixtures.op !== "highlight") {
452
+ console.error(`waygraph ${command} highlight: do not set "op" (or set it to "highlight")`);
453
+ process.exit(1);
454
+ }
455
+ const { op: _ignore, ...rest } = fixtures;
456
+ const res = await requestSession(proj, sessionId, {
457
+ op: "highlight",
458
+ ...rest,
459
+ });
460
+ console.log(JSON.stringify(res));
461
+ if (!res.ok)
462
+ process.exitCode = 1;
463
+ return;
464
+ }
465
+ const res = await requestSession(proj, sessionId, { op: "status" });
466
+ console.log(JSON.stringify(res));
467
+ if (!res.ok)
468
+ process.exitCode = 1;
469
+ return;
470
+ }
471
+ const flags = parseRunFlags(args.slice(1));
472
+ applyRunFlags(flags);
473
+ const mermaid = flags.mermaid === true;
474
+ const mapOnly = command === "graph" || flags.map === true;
475
+ const cliPicker = flags.cli === true;
476
+ const firstPos = flags.positionals[0];
477
+ // auto src/flows/shop.flow.ts → run that flow (same as `run`). Do not
478
+ // treat a .flow.ts path as a project directory (existsSync is true for files).
479
+ if (command === "auto" && firstPos && looksLikeFlowFileRef(firstPos) && !flags.blocksFromTo) {
480
+ const proj = resolve(process.cwd());
481
+ if (!existsSync(proj) || !statSync(proj).isDirectory()) {
482
+ console.error(`waygraph: no such directory: ${proj}`);
483
+ process.exit(1);
484
+ }
485
+ if (!process.env.WAYGRAPH_BASE_URL) {
486
+ const resolved = resolveBaseUrl(proj);
487
+ if (resolved)
488
+ process.env.WAYGRAPH_BASE_URL = resolved;
489
+ }
490
+ await runChain(proj, firstPos);
491
+ return;
492
+ }
493
+ const proj = resolve(firstPos ?? process.cwd());
494
+ if (!existsSync(proj)) {
495
+ console.error(`waygraph: no such directory: ${proj}`);
496
+ process.exit(1);
497
+ }
498
+ if (!statSync(proj).isDirectory()) {
499
+ console.error(`waygraph ${command}: "${firstPos}" is a file, not a project directory\n` +
500
+ " run a flow: waygraph auto src/flows/shop.flow.ts\n" +
501
+ " or explore: waygraph auto\n" +
502
+ " or path-find: waygraph auto --blocks LoginPage OrderComplete");
503
+ process.exit(1);
504
+ }
505
+ // auto --cli --detach: start the explore session as a background socket
506
+ // server instead of blocking in the interactive loop. Session control
507
+ // only applies to --cli (spec: "Headful mode is unaffected").
508
+ if (command === "auto" && flags.detach) {
509
+ if (!cliPicker) {
510
+ console.error("waygraph auto --detach requires --cli (headful has no session control)");
511
+ process.exit(1);
512
+ }
513
+ const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
514
+ try {
515
+ const meta = await spawnDetachedSession({
516
+ projectDir: proj,
517
+ ...(baseURL ? { baseURL } : {}),
518
+ ...(flags.nonHeadless ? { headless: false } : {}),
519
+ });
520
+ console.log(JSON.stringify({ sessionId: meta.sessionId, socketPath: meta.socketPath, headless: meta.headless }));
521
+ }
522
+ catch (err) {
523
+ console.error(`waygraph auto --detach: ${err instanceof Error ? err.message : String(err)}`);
524
+ process.exit(1);
525
+ }
526
+ return;
527
+ }
528
+ // auto --blocks From To (graph path-find + run)
529
+ // auto --blocks '/regex/' or '**/*.block.ts' (Phase C file select → explore)
530
+ if (command === "auto" && (flags.blocksFromTo || flags.blocks)) {
531
+ if (flags.blocksFromTo) {
532
+ const [fromTag, toTag] = flags.blocksFromTo;
533
+ await runChainAuto(proj, fromTag, toTag);
534
+ return;
535
+ }
536
+ if (flags.blocks && isFileSelectToken(flags.blocks)) {
537
+ const blocksSelect = parseBlocksSelect(flags.blocks);
538
+ const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
539
+ await runAutoExplore(proj, baseURL
540
+ ? { cli: cliPicker, baseURL, blocksSelect }
541
+ : { cli: cliPicker, blocksSelect });
542
+ return;
543
+ }
544
+ console.error("waygraph auto --blocks expects <fromCheckpoint> <toCheckpoint>\n" +
545
+ " or file select: --blocks '**/mailpit/**/*.block.ts' / --blocks '/mailpit/'\n" +
546
+ ' e.g. waygraph auto --blocks LoginPage OrderComplete\n' +
547
+ ' for a hand-named chain use: waygraph run --blocks "a then b"\n' +
548
+ " or: waygraph auto src/flows/shop.flow.ts");
549
+ process.exit(1);
550
+ }
551
+ if (mapOnly || mermaid) {
552
+ const orphans = await findOrphanBlocks(proj);
553
+ const graph = await discoverGraph(proj);
554
+ if (mermaid) {
555
+ console.log(toMermaid(graph));
556
+ }
557
+ else {
558
+ console.log(JSON.stringify({ ...graph, orphans }, null, 2));
559
+ }
560
+ console.error(`waygraph graph: ${graph.nodes.length} node(s), ${graph.edges.length} edge(s), ` +
561
+ `${graph.skipped.length} Block(s) skipped (Out not resolvable), ` +
562
+ `${orphans.length} orphan Block(s)`);
563
+ if (orphans.length > 0) {
564
+ console.error("waygraph graph: orphan Blocks block auto --blocks path-find - wire each into a .flow.ts (waygraph check)");
565
+ }
566
+ return;
567
+ }
568
+ const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
569
+ await runAutoExplore(proj, baseURL ? { cli: cliPicker, baseURL } : { cli: cliPicker });
570
+ return;
571
+ }
572
+ export async function autoServeCase(args) {
573
+ const proj = resolve(args[1] ?? process.cwd());
574
+ const flags = parseRunFlags(args.slice(2));
575
+ const sessionIdIdx = args.indexOf("--session-id");
576
+ const sessionId = sessionIdIdx >= 0 ? args[sessionIdIdx + 1] : undefined;
577
+ if (!sessionId) {
578
+ console.error("waygraph __auto-serve: missing --session-id");
579
+ process.exit(1);
580
+ }
581
+ const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
582
+ const inject = flags.inject?.length ? resolveInjectRoots(flags.inject, proj) : undefined;
583
+ let headless;
584
+ if (flags.headlessBrowser)
585
+ headless = true;
586
+ else if (flags.nonHeadless)
587
+ headless = false;
588
+ await runAutoServeCommand({
589
+ projectDir: proj,
590
+ ...(baseURL ? { baseURL } : {}),
591
+ ...(flags.goto
592
+ ? { startUrl: flags.goto, skipInitialNavigation: false }
593
+ : flags.blank === true
594
+ ? { skipInitialNavigation: true }
595
+ : {}),
596
+ ...(headless !== undefined ? { headless } : {}),
597
+ ...(inject?.length ? { inject } : {}),
598
+ }, sessionId);
599
+ return;
600
+ }
@@ -0,0 +1 @@
1
+ export declare function testCase(args: string[]): Promise<void>;
@@ -0,0 +1,33 @@
1
+ // Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
2
+ import { runInherited } from "../cli/util.js";
3
+ import { join } from "node:path";
4
+ import { existsSync } from "node:fs";
5
+ export async function testCase(args) {
6
+ const rest = args.slice(1);
7
+ const proj = process.cwd();
8
+ const localPlaywrightBin = join(proj, "node_modules", ".bin", "playwright");
9
+ const runPw = (pwArgs) => existsSync(localPlaywrightBin)
10
+ ? runInherited(localPlaywrightBin, pwArgs, proj)
11
+ : runInherited("npx", ["--yes", "playwright", ...pwArgs], proj);
12
+ // report/show-trace are separate top-level `playwright` commands, not
13
+ // `playwright test` subcommands - handled before the `test` forward below.
14
+ if (rest[0] === "report") {
15
+ process.exitCode = await runPw(["show-report", ...rest.slice(1)]);
16
+ return;
17
+ }
18
+ if (rest[0] === "show-trace") {
19
+ if (!rest[1]) {
20
+ console.error("waygraph test show-trace: missing <trace.zip> (or a test-results/ dir)");
21
+ process.exit(1);
22
+ }
23
+ process.exitCode = await runPw(["show-trace", ...rest.slice(1)]);
24
+ return;
25
+ }
26
+ const wantsUi = rest[0] === "ui" || rest.includes("--ui");
27
+ const forwarded = rest[0] === "ui" ? rest.slice(1) : rest;
28
+ const pwArgs = wantsUi && !forwarded.includes("--ui")
29
+ ? ["test", "--ui", ...forwarded]
30
+ : ["test", ...forwarded];
31
+ process.exitCode = await runPw(pwArgs);
32
+ return;
33
+ }
@@ -0,0 +1 @@
1
+ export declare function traverseCase(args: string[]): Promise<void>;