waygraph 0.12.7 → 0.12.8
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/dist/cli.d.ts +1 -1
- package/dist/cli.js +100 -1
- package/dist/traverse-run.d.ts +24 -0
- package/dist/traverse-run.js +227 -0
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* demo Watch (step overlay); `--blocks` `--data` `--auto-next` `--fast` `--full` `--ff-expand`
|
|
8
8
|
* run Execute; `--blocks` `--data` `--non-headless` `--video`
|
|
9
9
|
*
|
|
10
|
-
* Also: list / nav / validate / check / graph / init / agent-dive / try
|
|
10
|
+
* Also: list / nav / validate / check / graph / init / agent-dive / traverse / try
|
|
11
11
|
* Aliases (one release): `chain` -> run/demo --blocks; `--autoplay` -> `--auto-next`
|
|
12
12
|
*
|
|
13
13
|
* "project" defaults to cwd. Flags beat WAYGRAPH_* env.
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* demo Watch (step overlay); `--blocks` `--data` `--auto-next` `--fast` `--full` `--ff-expand`
|
|
8
8
|
* run Execute; `--blocks` `--data` `--non-headless` `--video`
|
|
9
9
|
*
|
|
10
|
-
* Also: list / nav / validate / check / graph / init / agent-dive / try
|
|
10
|
+
* Also: list / nav / validate / check / graph / init / agent-dive / traverse / try
|
|
11
11
|
* Aliases (one release): `chain` -> run/demo --blocks; `--autoplay` -> `--auto-next`
|
|
12
12
|
*
|
|
13
13
|
* "project" defaults to cwd. Flags beat WAYGRAPH_* env.
|
|
@@ -20,6 +20,7 @@ import { spawn } from "node:child_process";
|
|
|
20
20
|
import { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
|
21
21
|
import { runAutoExplore } from "./auto-explore-run.js";
|
|
22
22
|
import { runAgentDive } from "./agent-dive.js";
|
|
23
|
+
import { runTraverse } from "./traverse-run.js";
|
|
23
24
|
// ---------------------------------------------------------------------------
|
|
24
25
|
// Filesystem
|
|
25
26
|
// ---------------------------------------------------------------------------
|
|
@@ -3661,6 +3662,12 @@ Primary (less is more):
|
|
|
3661
3662
|
|
|
3662
3663
|
Also:
|
|
3663
3664
|
waygraph list | nav | validate | check | graph | init <name>
|
|
3665
|
+
waygraph traverse [project] Serial graph crawl (Phase B)
|
|
3666
|
+
--from <Checkpoint> Seed / start checkpoint
|
|
3667
|
+
--data '{...}' Mem seed
|
|
3668
|
+
--max-steps N Cap Block runs (default 50)
|
|
3669
|
+
--max-visits N Cap visits per node (default 2)
|
|
3670
|
+
--non-headless Show browser
|
|
3664
3671
|
waygraph agent-dive [--loop claude|opencode|cursor|vscode] [--prompts]
|
|
3665
3672
|
Initialize coding-agent defs (Playwright init-agents analogue)
|
|
3666
3673
|
waygraph try [demo|auto|auto:cli] One-shot saucedemo in a temp dir
|
|
@@ -3759,6 +3766,98 @@ async function main() {
|
|
|
3759
3766
|
initCommand(args[1] ?? "");
|
|
3760
3767
|
break;
|
|
3761
3768
|
}
|
|
3769
|
+
case "traverse": {
|
|
3770
|
+
if (args.includes("--step") || args.includes("--auto-next") || args.includes("--autoplay")) {
|
|
3771
|
+
console.error("waygraph traverse: --step / --auto-next not supported (not a demo)");
|
|
3772
|
+
process.exit(1);
|
|
3773
|
+
}
|
|
3774
|
+
const rest = args.slice(1);
|
|
3775
|
+
let from;
|
|
3776
|
+
let data;
|
|
3777
|
+
let maxSteps;
|
|
3778
|
+
let maxVisits;
|
|
3779
|
+
let maxEdge;
|
|
3780
|
+
let headed = false;
|
|
3781
|
+
let baseUrl;
|
|
3782
|
+
let projectDir = process.cwd();
|
|
3783
|
+
for (let i = 0; i < rest.length; i++) {
|
|
3784
|
+
const a = rest[i];
|
|
3785
|
+
if (a === "--from" || a.startsWith("--from=")) {
|
|
3786
|
+
from = a.startsWith("--from=") ? a.slice(7) : rest[++i];
|
|
3787
|
+
continue;
|
|
3788
|
+
}
|
|
3789
|
+
if (a === "--data" || a.startsWith("--data=")) {
|
|
3790
|
+
data = a.startsWith("--data=") ? a.slice(7) : rest[++i];
|
|
3791
|
+
continue;
|
|
3792
|
+
}
|
|
3793
|
+
if (a === "--max-steps" || a.startsWith("--max-steps=")) {
|
|
3794
|
+
const v = a.startsWith("--max-steps=") ? a.slice(12) : rest[++i];
|
|
3795
|
+
maxSteps = Number(v);
|
|
3796
|
+
continue;
|
|
3797
|
+
}
|
|
3798
|
+
if (a === "--max-visits" || a.startsWith("--max-visits=")) {
|
|
3799
|
+
const v = a.startsWith("--max-visits=") ? a.slice(13) : rest[++i];
|
|
3800
|
+
maxVisits = Number(v);
|
|
3801
|
+
continue;
|
|
3802
|
+
}
|
|
3803
|
+
if (a === "--max-visits-per-edge" || a.startsWith("--max-visits-per-edge=")) {
|
|
3804
|
+
const v = a.startsWith("--max-visits-per-edge=")
|
|
3805
|
+
? a.slice("--max-visits-per-edge=".length)
|
|
3806
|
+
: rest[++i];
|
|
3807
|
+
maxEdge = Number(v);
|
|
3808
|
+
continue;
|
|
3809
|
+
}
|
|
3810
|
+
if (a === "--non-headless" || a === "--headed") {
|
|
3811
|
+
headed = true;
|
|
3812
|
+
continue;
|
|
3813
|
+
}
|
|
3814
|
+
if (a === "--base-url" || a.startsWith("--base-url=")) {
|
|
3815
|
+
baseUrl = a.startsWith("--base-url=") ? a.slice(11) : rest[++i];
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
if (a === "--help" || a === "-h") {
|
|
3819
|
+
console.log(`waygraph traverse [project] [flags]
|
|
3820
|
+
|
|
3821
|
+
Serial graph crawl (RFC Phase B). Walks unused legal edges until a leaf,
|
|
3822
|
+
budget kill, or first broken edge.
|
|
3823
|
+
|
|
3824
|
+
--from <Checkpoint> start checkpoint (optional)
|
|
3825
|
+
--data '{...}' Mem seed JSON
|
|
3826
|
+
--max-steps N default 50
|
|
3827
|
+
--max-visits N per-node visit cap (default 2)
|
|
3828
|
+
--max-visits-per-edge N default 1
|
|
3829
|
+
--non-headless show browser
|
|
3830
|
+
--base-url URL
|
|
3831
|
+
|
|
3832
|
+
PASS: [Reached Leaf Node[traverse-1] at=... steps=N]
|
|
3833
|
+
FAIL: [Broke at edge[traverse-1] block=... from=... to=...]
|
|
3834
|
+
`);
|
|
3835
|
+
process.exit(0);
|
|
3836
|
+
}
|
|
3837
|
+
if (!a.startsWith("-")) {
|
|
3838
|
+
projectDir = resolve(a);
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
if (!existsSync(projectDir) || !statSync(projectDir).isDirectory()) {
|
|
3842
|
+
console.error(`waygraph traverse: no such directory: ${projectDir}`);
|
|
3843
|
+
process.exit(1);
|
|
3844
|
+
}
|
|
3845
|
+
const code = await runTraverse(projectDir, {
|
|
3846
|
+
...(from ? { from } : {}),
|
|
3847
|
+
...(data ? { data } : {}),
|
|
3848
|
+
...(maxSteps !== undefined && Number.isFinite(maxSteps) ? { maxSteps } : {}),
|
|
3849
|
+
...(maxVisits !== undefined && Number.isFinite(maxVisits)
|
|
3850
|
+
? { maxVisitsPerNode: maxVisits }
|
|
3851
|
+
: {}),
|
|
3852
|
+
...(maxEdge !== undefined && Number.isFinite(maxEdge)
|
|
3853
|
+
? { maxVisitsPerEdge: maxEdge }
|
|
3854
|
+
: {}),
|
|
3855
|
+
headed,
|
|
3856
|
+
...(baseUrl ? { baseURL: baseUrl } : {}),
|
|
3857
|
+
});
|
|
3858
|
+
process.exitCode = code;
|
|
3859
|
+
break;
|
|
3860
|
+
}
|
|
3762
3861
|
case "agent-dive":
|
|
3763
3862
|
case "init-agents": {
|
|
3764
3863
|
// Playwright analogue: npx playwright init-agents --loop <provider>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface TraverseOptions {
|
|
2
|
+
baseURL?: string;
|
|
3
|
+
startUrl?: string;
|
|
4
|
+
/** Seed checkpoint label (optional - otherwise detect from page / nav). */
|
|
5
|
+
from?: string;
|
|
6
|
+
/** Global Mem seed JSON (or use WAYGRAPH_DATA / WAYGRAPH_AUTO_MEM). */
|
|
7
|
+
data?: string;
|
|
8
|
+
/** Max Block runs this traverse (default 50). */
|
|
9
|
+
maxSteps?: number;
|
|
10
|
+
/** Max times a checkpoint may be visited (default 2). */
|
|
11
|
+
maxVisitsPerNode?: number;
|
|
12
|
+
/** Max times an edge key (block::instanceId) may be taken (default 1). */
|
|
13
|
+
maxVisitsPerEdge?: number;
|
|
14
|
+
/** Wall-clock budget ms (default 15m). */
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
/** Show browser (default headless). */
|
|
17
|
+
headed?: boolean;
|
|
18
|
+
/** Traverse id for greppable lines (default traverse-1). */
|
|
19
|
+
traverseId?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Serial graph crawl. Prints greppable PASS/FAIL lines; non-zero exit on break.
|
|
23
|
+
*/
|
|
24
|
+
export declare function runTraverse(projectDir: string, options?: TraverseOptions): Promise<number>;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase B: serial graph traverse (RFC traverse-ffcompose).
|
|
3
|
+
* Walk unused legal edges until leaf / budget / break. No parallel yet.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { Engine, start, end, locate } from "./engine.js";
|
|
8
|
+
import { MemPage } from "./mem-page.js";
|
|
9
|
+
import { buildExploreContext, buildExploreMenu, } from "./auto-explore.js";
|
|
10
|
+
import { defaultMemValueForKey } from "./auto-explore-run.js";
|
|
11
|
+
function resolveBaseUrl(projectDir) {
|
|
12
|
+
if (process.env.WAYGRAPH_BASE_URL)
|
|
13
|
+
return process.env.WAYGRAPH_BASE_URL;
|
|
14
|
+
const pkgPath = join(projectDir, "package.json");
|
|
15
|
+
if (!existsSync(pkgPath))
|
|
16
|
+
return undefined;
|
|
17
|
+
try {
|
|
18
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
19
|
+
return pkg.waygraph?.baseUrl;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function seedMem(library, mem, dataJson) {
|
|
26
|
+
const raw = dataJson ?? process.env.WAYGRAPH_DATA ?? process.env.WAYGRAPH_AUTO_MEM;
|
|
27
|
+
let envParsed = {};
|
|
28
|
+
if (raw) {
|
|
29
|
+
try {
|
|
30
|
+
envParsed = JSON.parse(raw);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new Error(`waygraph traverse: --data / WAYGRAPH_DATA is not valid JSON`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const entry of library.values()) {
|
|
37
|
+
for (const k of entry.block.requires ?? []) {
|
|
38
|
+
if (mem.has(k))
|
|
39
|
+
continue;
|
|
40
|
+
if (k.name in envParsed) {
|
|
41
|
+
mem.set(k, envParsed[k.name]);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const def = defaultMemValueForKey(k.name);
|
|
45
|
+
if (def !== undefined)
|
|
46
|
+
mem.set(k, def);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function missingMemKeys(entry, mem) {
|
|
51
|
+
return (entry.block.requires ?? []).filter((k) => !mem.has(k));
|
|
52
|
+
}
|
|
53
|
+
function ensureMem(entry, mem) {
|
|
54
|
+
const missing = missingMemKeys(entry, mem);
|
|
55
|
+
if (missing.length === 0)
|
|
56
|
+
return;
|
|
57
|
+
throw new Error(`Block "${entry.block.name}" needs Mem keys: ${missing.map((k) => k.name).join(", ")} ` +
|
|
58
|
+
`(pass --data '{...}' or WAYGRAPH_DATA)`);
|
|
59
|
+
}
|
|
60
|
+
function edgeKey(edge) {
|
|
61
|
+
const id = edge.instanceOption?.id;
|
|
62
|
+
return id ? `${edge.block}::${id}` : edge.block;
|
|
63
|
+
}
|
|
64
|
+
async function detectHere(page, navBlocks) {
|
|
65
|
+
try {
|
|
66
|
+
const url = page.url();
|
|
67
|
+
if (url.includes("/login"))
|
|
68
|
+
return "PiaLogin";
|
|
69
|
+
if (url.includes("mailpit") || url.includes(":54324") || url.includes("/mailhog")) {
|
|
70
|
+
return "PiaMailpitGui";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
/* ignore */
|
|
75
|
+
}
|
|
76
|
+
const tags = navBlocks.map((n) => n.block);
|
|
77
|
+
return locate(page, tags, { timeoutMs: 1500 });
|
|
78
|
+
}
|
|
79
|
+
async function runOneBlock(engine, entry, context, page, mem) {
|
|
80
|
+
const flow = engine.defineFlow([start, entry.block, end]);
|
|
81
|
+
const BLOCK_TIMEOUT_MS = 45_000;
|
|
82
|
+
let timer;
|
|
83
|
+
try {
|
|
84
|
+
const ran = await Promise.race([
|
|
85
|
+
flow.run(context, mem, { page, closeOnFinish: false }),
|
|
86
|
+
new Promise((_, reject) => {
|
|
87
|
+
timer = setTimeout(() => reject(new Error(`Block "${entry.block.name}" timed out after ${BLOCK_TIMEOUT_MS / 1000}s`)), BLOCK_TIMEOUT_MS);
|
|
88
|
+
}),
|
|
89
|
+
]);
|
|
90
|
+
return ran.result;
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
if (timer)
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Serial graph crawl. Prints greppable PASS/FAIL lines; non-zero exit on break.
|
|
99
|
+
*/
|
|
100
|
+
export async function runTraverse(projectDir, options = {}) {
|
|
101
|
+
const traverseId = options.traverseId ?? "traverse-1";
|
|
102
|
+
const maxSteps = options.maxSteps ?? 50;
|
|
103
|
+
const maxVisitsPerNode = options.maxVisitsPerNode ?? 2;
|
|
104
|
+
const maxVisitsPerEdge = options.maxVisitsPerEdge ?? 1;
|
|
105
|
+
const timeoutMs = options.timeoutMs ?? 15 * 60_000;
|
|
106
|
+
const headed = options.headed === true || process.env.WAYGRAPH_HEADED === "1";
|
|
107
|
+
const baseURL = options.baseURL ?? resolveBaseUrl(projectDir) ?? process.env.WAYGRAPH_BASE_URL;
|
|
108
|
+
const startUrl = options.startUrl ?? baseURL;
|
|
109
|
+
const { graph, library } = await buildExploreContext(projectDir);
|
|
110
|
+
const mem = new MemPage();
|
|
111
|
+
seedMem(library.byName, mem, options.data);
|
|
112
|
+
const engine = new Engine({ headless: !headed, slowMo: headed ? 100 : 0 });
|
|
113
|
+
const { chromium } = await import("@playwright/test");
|
|
114
|
+
const executablePath = process.env.CHROME_PATH || process.env.CHROMIUM_PATH;
|
|
115
|
+
const launchOpts = {
|
|
116
|
+
headless: !headed,
|
|
117
|
+
};
|
|
118
|
+
if (executablePath)
|
|
119
|
+
launchOpts.executablePath = executablePath;
|
|
120
|
+
const browser = await chromium.launch(launchOpts);
|
|
121
|
+
const contextOpts = {
|
|
122
|
+
viewport: { width: 1280, height: 720 },
|
|
123
|
+
};
|
|
124
|
+
if (baseURL)
|
|
125
|
+
contextOpts.baseURL = baseURL;
|
|
126
|
+
const context = await browser.newContext(contextOpts);
|
|
127
|
+
let page = await context.newPage();
|
|
128
|
+
const nodeVisits = new Map();
|
|
129
|
+
const edgeVisits = new Map();
|
|
130
|
+
const edgesHit = new Set();
|
|
131
|
+
let steps = 0;
|
|
132
|
+
const startedAt = Date.now();
|
|
133
|
+
let lastHere = options.from ?? null;
|
|
134
|
+
let exitCode = 0;
|
|
135
|
+
console.error(`waygraph traverse[${traverseId}]: ${graph.nodes.length} node(s), ${graph.edges.length} edge(s)` +
|
|
136
|
+
(baseURL ? ` base=${baseURL}` : "") +
|
|
137
|
+
` maxSteps=${maxSteps} maxVisits/node=${maxVisitsPerNode} maxVisits/edge=${maxVisitsPerEdge}`);
|
|
138
|
+
try {
|
|
139
|
+
if (startUrl) {
|
|
140
|
+
await page.goto(startUrl, { waitUntil: "domcontentloaded" }).catch(() => { });
|
|
141
|
+
await page.waitForLoadState("load").catch(() => { });
|
|
142
|
+
}
|
|
143
|
+
for (;;) {
|
|
144
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
145
|
+
console.log(`[Killed timeout[${traverseId}] node=${lastHere ?? "?"} wallMs=${timeoutMs}]`);
|
|
146
|
+
exitCode = 1;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
if (steps >= maxSteps) {
|
|
150
|
+
console.log(`[Killed max-steps[${traverseId}] node=${lastHere ?? "?"} steps=${steps} max=${maxSteps}]`);
|
|
151
|
+
exitCode = 1;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
if (page.isClosed()) {
|
|
155
|
+
page = await context.newPage();
|
|
156
|
+
if (startUrl) {
|
|
157
|
+
await page.goto(startUrl, { waitUntil: "domcontentloaded" }).catch(() => { });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const detected = await detectHere(page, library.navBlocks);
|
|
161
|
+
const here = lastHere ?? detected ?? options.from ?? null;
|
|
162
|
+
if (here && (nodeVisits.get(here) ?? 0) >= maxVisitsPerNode) {
|
|
163
|
+
console.log(`[Killed loop[${traverseId}] node=${here} visits=${nodeVisits.get(here)} max=${maxVisitsPerNode}]`);
|
|
164
|
+
exitCode = 1;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
const menu = await buildExploreMenu(page, graph, library, here);
|
|
168
|
+
const candidates = menu.flat.filter((edge) => {
|
|
169
|
+
const key = edgeKey(edge);
|
|
170
|
+
return (edgeVisits.get(key) ?? 0) < maxVisitsPerEdge;
|
|
171
|
+
});
|
|
172
|
+
if (candidates.length === 0) {
|
|
173
|
+
const label = here ?? detected ?? "unknown";
|
|
174
|
+
console.log(`[Reached Leaf Node[${traverseId}] at=${label} steps=${steps}]`);
|
|
175
|
+
console.error(`waygraph traverse[${traverseId}]: PASS leaf edgesHit=${edgesHit.size} steps=${steps}`);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
const pick = [...candidates].sort((a, b) => {
|
|
179
|
+
const aWild = a.from === "*" ? 1 : 0;
|
|
180
|
+
const bWild = b.from === "*" ? 1 : 0;
|
|
181
|
+
if (aWild !== bWild)
|
|
182
|
+
return aWild - bWild;
|
|
183
|
+
return edgeKey(a).localeCompare(edgeKey(b));
|
|
184
|
+
})[0];
|
|
185
|
+
const entry = library.byName.get(pick.block);
|
|
186
|
+
if (!entry) {
|
|
187
|
+
console.log(`[Broke at edge[${traverseId}] block=${pick.block} from=${here ?? "?"} to=${pick.to} reason=not-loaded]`);
|
|
188
|
+
exitCode = 1;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
const fromLabel = here ?? pick.from;
|
|
192
|
+
try {
|
|
193
|
+
if (pick.instanceOption) {
|
|
194
|
+
mem.set(pick.instanceOption.key, pick.instanceOption.value);
|
|
195
|
+
}
|
|
196
|
+
ensureMem(entry, mem);
|
|
197
|
+
console.error(`waygraph traverse[${traverseId}]: step ${steps + 1} ${fromLabel} -[${pick.block}]-> ${pick.to}` +
|
|
198
|
+
(pick.label ? ` (${pick.label})` : ""));
|
|
199
|
+
const out = await runOneBlock(engine, entry, context, page, mem);
|
|
200
|
+
const observed = typeof out === "object" && out && "__state" in out
|
|
201
|
+
? String(out.__state)
|
|
202
|
+
: pick.to;
|
|
203
|
+
const key = edgeKey(pick);
|
|
204
|
+
edgeVisits.set(key, (edgeVisits.get(key) ?? 0) + 1);
|
|
205
|
+
edgesHit.add(key);
|
|
206
|
+
if (here)
|
|
207
|
+
nodeVisits.set(here, (nodeVisits.get(here) ?? 0) + 1);
|
|
208
|
+
lastHere = observed;
|
|
209
|
+
steps += 1;
|
|
210
|
+
if (observed !== pick.to && pick.to !== "*") {
|
|
211
|
+
console.error(`waygraph traverse[${traverseId}]: WARN static to=${pick.to} observed=${observed}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
216
|
+
console.log(`[Broke at edge[${traverseId}] block=${pick.block} from=${fromLabel} to=${pick.to}] ${msg}`);
|
|
217
|
+
exitCode = 1;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
await browser.close().catch(() => { });
|
|
224
|
+
}
|
|
225
|
+
console.error(`waygraph traverse[${traverseId}]: done exit=${exitCode} steps=${steps} edgesHit=${edgesHit.size}`);
|
|
226
|
+
return exitCode;
|
|
227
|
+
}
|