waygraph 0.16.0 → 0.16.2

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 (143) hide show
  1. package/dist/cli/commands/browser.d.ts +2 -0
  2. package/dist/cli/commands/browser.js +109 -0
  3. package/dist/cli/commands/codemod-stubs.js +90 -2
  4. package/dist/cli/commands/pilot.d.ts +2 -0
  5. package/dist/cli/commands/pilot.js +102 -0
  6. package/dist/cli/commands/session-ops.d.ts +3 -0
  7. package/dist/cli/commands/session-ops.js +257 -0
  8. package/dist/cli/commands/session.js +20 -461
  9. package/dist/cli/run-chain.js +2 -2
  10. package/dist/cli/usage.d.ts +1 -1
  11. package/dist/cli/util.js +1 -1
  12. package/dist/demo/narration/demo-pace.d.ts +2 -2
  13. package/dist/demo/narration/demo-pace.js +1 -1
  14. package/dist/demo/narration/device.d.ts +2 -2
  15. package/dist/demo/narration/merge.d.ts +5 -3
  16. package/dist/demo/narration/merge.js +3 -7
  17. package/dist/demo/narration/narration.d.ts +9 -13
  18. package/dist/demo/narration/narration.js +5 -17
  19. package/dist/demo/narration/stub-ctx.js +3 -3
  20. package/dist/demo/narration/todo-dock.d.ts +5 -5
  21. package/dist/demo/overlay/cursor.d.ts +9 -2
  22. package/dist/demo/overlay/cursor.js +1 -1
  23. package/dist/demo/overlay/device-stage.d.ts +15 -2
  24. package/dist/demo/overlay/inpage/banner.d.ts +2 -1
  25. package/dist/demo/overlay/inpage/banner.js +3 -3
  26. package/dist/demo/overlay/inpage/caption.d.ts +1 -1
  27. package/dist/demo/overlay/inpage/core.d.ts +2 -1
  28. package/dist/demo/overlay/inpage/core.js +1 -7
  29. package/dist/demo/overlay/inpage/device-chip.d.ts +1 -1
  30. package/dist/demo/overlay/inpage/device-chip.js +6 -6
  31. package/dist/demo/overlay/inpage/network.d.ts +3 -2
  32. package/dist/demo/overlay/inpage/network.js +1 -12
  33. package/dist/demo/overlay/inpage/panel-chrome.d.ts +1 -1
  34. package/dist/demo/overlay/inpage/panel-chrome.js +1 -1
  35. package/dist/demo/overlay/inpage/stamp.d.ts +1 -1
  36. package/dist/demo/overlay/inpage/stamp.js +1 -1
  37. package/dist/demo/overlay/inpage/swipe-zoom.d.ts +1 -1
  38. package/dist/demo/overlay/inpage/swipe-zoom.js +1 -1
  39. package/dist/demo/overlay/inpage/todos.d.ts +2 -1
  40. package/dist/demo/overlay/inpage/todos.js +1 -2
  41. package/dist/demo/overlay/network-capture.d.ts +2 -2
  42. package/dist/demo/overlay/network-capture.js +1 -1
  43. package/dist/demo/overlay/overlay-css.js +1 -55
  44. package/dist/demo/overlay/overlay-stage.d.ts +19 -11
  45. package/dist/demo/overlay/overlay.d.ts +2 -2
  46. package/dist/demo/overlay/overlay.js +3 -2
  47. package/dist/demo/overlay/rings.d.ts +38 -18
  48. package/dist/demo/overlay/rings.js +21 -62
  49. package/dist/demo/overlay/ui/overlay.css +40 -0
  50. package/dist/demo/stepper/after.js +11 -34
  51. package/dist/demo/stepper/banner-log.d.ts +4 -6
  52. package/dist/demo/stepper/banner-log.js +3 -1
  53. package/dist/demo/stepper/before.js +6 -4
  54. package/dist/demo/stepper/demo-log.d.ts +26 -9
  55. package/dist/demo/stepper/demo-log.js +10 -27
  56. package/dist/demo/stepper/interaction-patch.d.ts +3 -2
  57. package/dist/demo/stepper/interaction-patch.js +22 -34
  58. package/dist/demo/stepper/panel-after.d.ts +17 -0
  59. package/dist/demo/stepper/panel-after.js +166 -0
  60. package/dist/demo/stepper/panel-before.d.ts +12 -0
  61. package/dist/demo/stepper/panel-before.js +241 -0
  62. package/dist/demo/stepper/slides.d.ts +16 -1
  63. package/dist/demo/stepper/slides.js +21 -5
  64. package/dist/demo/stepper/step-panels.d.ts +57 -9
  65. package/dist/demo/stepper/step-panels.js +10 -427
  66. package/dist/demo/stepper/stepper.js +1 -1
  67. package/dist/demo/stepper/teardown.d.ts +4 -5
  68. package/dist/demo/stepper/teardown.js +4 -6
  69. package/dist/demo/stepper/todo-advance.d.ts +3 -2
  70. package/dist/demo/stepper/todo-advance.js +2 -2
  71. package/dist/demo/stepper/view.d.ts +4 -14
  72. package/dist/demo/types.d.ts +69 -1
  73. package/dist/demo/verbose-log.js +1 -1
  74. package/dist/engine/block/action-page.d.ts +53 -0
  75. package/dist/engine/block/action-page.js +1 -0
  76. package/dist/engine/block/block.d.ts +24 -90
  77. package/dist/engine/block/block.js +2 -77
  78. package/dist/engine/block/branch.d.ts +17 -0
  79. package/dist/engine/block/branch.js +40 -0
  80. package/dist/engine/block/dialog.d.ts +13 -0
  81. package/dist/engine/block/dialog.js +37 -0
  82. package/dist/engine/block/kinds/method.d.ts +0 -2
  83. package/dist/engine/block/kinds/method.js +0 -1
  84. package/dist/engine/block/kinds/nav.js +1 -1
  85. package/dist/engine/flow/map-branch.d.ts +9 -0
  86. package/dist/engine/flow/map-branch.js +57 -0
  87. package/dist/engine/flow/map-builder.d.ts +7 -22
  88. package/dist/engine/flow/map-builder.js +6 -145
  89. package/dist/engine/flow/map-steps.d.ts +30 -0
  90. package/dist/engine/flow/map-steps.js +87 -0
  91. package/dist/engine/run/run.d.ts +1 -19
  92. package/dist/engine/run/run.js +0 -88
  93. package/dist/engine/stub/ring.d.ts +2 -4
  94. package/dist/engine/stub/ring.js +1 -0
  95. package/dist/engine/stub/stub.d.ts +16 -8
  96. package/dist/engine/trait/trait.js +1 -1
  97. package/dist/index.d.ts +15 -12
  98. package/dist/index.js +9 -7
  99. package/dist/live/auto/auto.d.ts +3 -15
  100. package/dist/live/auto/auto.js +10 -420
  101. package/dist/live/auto/explore.d.ts +2 -2
  102. package/dist/live/auto/mem.d.ts +11 -0
  103. package/dist/live/auto/mem.js +85 -0
  104. package/dist/live/auto/menu.d.ts +30 -0
  105. package/dist/live/auto/menu.js +335 -0
  106. package/dist/live/auto-session/auto-session.d.ts +23 -103
  107. package/dist/live/auto-session/auto-session.js +100 -370
  108. package/dist/live/auto-session/console.d.ts +4 -0
  109. package/dist/live/auto-session/console.js +41 -0
  110. package/dist/live/auto-session/files.d.ts +44 -0
  111. package/dist/live/auto-session/files.js +145 -0
  112. package/dist/live/auto-session/helpers.d.ts +5 -2
  113. package/dist/live/auto-session/helpers.js +17 -0
  114. package/dist/live/auto-session/inspect.d.ts +4 -0
  115. package/dist/live/auto-session/inspect.js +75 -0
  116. package/dist/live/auto-session/ipc.d.ts +3 -22
  117. package/dist/live/auto-session/ipc.js +7 -147
  118. package/dist/live/auto-session/open-browser.d.ts +6 -0
  119. package/dist/live/auto-session/open-browser.js +47 -0
  120. package/dist/live/pilot/pilot.d.ts +1 -1
  121. package/dist/live/pilot/pilot.js +2 -1
  122. package/dist/live/traverse/traverse.js +6 -200
  123. package/dist/live/traverse/worker.d.ts +37 -0
  124. package/dist/live/traverse/worker.js +200 -0
  125. package/dist/runner/plan/find.js +2 -2
  126. package/dist/runner/plan/plan.js +1 -1
  127. package/dist/runner/plan/seed-mem.js +5 -5
  128. package/dist/runner/plan/spec.js +1 -1
  129. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
  130. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +2 -1
  131. package/examples/saucedemo/src/flows/checkout.flow.ts +2 -1
  132. package/examples/saucedemo/src/flows/shop.flow.ts +2 -1
  133. package/examples/saucedemo/src/stubs/device-showcase.ts +37 -43
  134. package/examples/saucedemo/src/stubs/finish-order.ts +38 -0
  135. package/package.json +5 -1
  136. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
  137. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +2 -1
  138. package/templates/quickstart/src/flows/checkout.flow.ts +2 -1
  139. package/templates/quickstart/src/flows/shop.flow.ts +2 -1
  140. package/templates/quickstart/src/stubs/device-showcase.ts +37 -43
  141. package/templates/quickstart/src/stubs/finish-order.ts +38 -0
  142. package/templates/scaffold/src/flows/home/hello.flow.ts +3 -0
  143. package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +1 -9
@@ -0,0 +1,30 @@
1
+ import type { Page } from "@playwright/test";
2
+ import { createInterface } from "node:readline/promises";
3
+ import type { MemKey, MemPage } from "../../engine/mem/mem.js";
4
+ import type { BlockEntry } from "../../manifest/project.js";
5
+ import type { ExploreMenu } from "./explore.js";
6
+ export declare function setAutoPanelRunning(page: Page, label: string): Promise<void>;
7
+ export declare const PANEL_CSS: string;
8
+ export type PickResult = {
9
+ type: "pick";
10
+ index: number;
11
+ } | {
12
+ type: "quit";
13
+ };
14
+ /**
15
+ * Mem keys any Block on the current menu might read this turn - minus keys a
16
+ * dynamic `instanceOptions` row already answers by picking it (e.g. "which
17
+ * item to add to cart" comes from the menu row itself, not a hand-typed JSON
18
+ * textarea).
19
+ */
20
+ export declare function memKeysForMenu(menu: ExploreMenu, library: Map<string, BlockEntry>): readonly MemKey<unknown>[];
21
+ export declare function syncPanelMem(page: Page, mem: MemPage, keys: readonly MemKey<unknown>[]): Promise<void>;
22
+ export declare function armPickWait(): {
23
+ promise: Promise<PickResult>;
24
+ resolve: (v: PickResult) => void;
25
+ };
26
+ export declare function headfulPick(page: Page, menu: ExploreMenu, library: Map<string, BlockEntry>, mem: MemPage, waitPick: () => Promise<PickResult>,
27
+ /** What the last Block run did, shown at the top of the menu. */
28
+ note: string | null): Promise<PickResult>;
29
+ export declare function printCliMenu(menu: ExploreMenu, library: Map<string, BlockEntry>): void;
30
+ export declare function cliPick(menu: ExploreMenu, rl: ReturnType<typeof createInterface>): Promise<PickResult>;
@@ -0,0 +1,335 @@
1
+ import { ensureShadowRoot } from "../../demo/overlay/ui/shadow.js";
2
+ import { ignorePageGone } from "../../engine/page-gone.js";
3
+ import { memStubValue } from "./mem.js";
4
+ export async function setAutoPanelRunning(page, label) {
5
+ await page
6
+ .evaluate((label) => {
7
+ const panel = __wgById("wg-auto-panel");
8
+ if (!panel)
9
+ return;
10
+ __wgSet(panel, __wgEl("h3", null, "waygraph auto"), __wgEl("div", { style: "padding:12px 0;color:#c9a6ff" }, "Running: ", __wgEl("strong", null, label)));
11
+ if (typeof window.__wgWirePanelChrome === "function") {
12
+ window.__wgWirePanelChrome(panel, "wg-auto-panel-hidden", "waygraph auto");
13
+ }
14
+ if (typeof window.__wgStampModal === "function") {
15
+ window.__wgStampModal(panel, "auto-panel", {
16
+ phase: "running",
17
+ block: label,
18
+ ready: true,
19
+ });
20
+ }
21
+ else {
22
+ panel.setAttribute("data-wg-ui", "1");
23
+ panel.setAttribute("data-wg-modal", "auto-panel");
24
+ panel.setAttribute("data-wg-phase", "running");
25
+ panel.setAttribute("data-wg-ready", "1");
26
+ }
27
+ }, label)
28
+ .catch(ignorePageGone);
29
+ }
30
+ export const PANEL_CSS = "#wg-auto-panel{position:fixed;z-index:2147483647;left:50%;bottom:12px;transform:translateX(-50%);" +
31
+ "max-width:min(92vw,720px);max-height:calc(100vh - 24px);overflow-y:auto;box-sizing:border-box;" +
32
+ "background:rgba(20,10,40,.94);color:#fff;border-radius:14px;padding:16px 20px;" +
33
+ "font:14px/1.4 system-ui,sans-serif;box-shadow:0 12px 30px rgba(0,0,0,.35);}" +
34
+ "#wg-auto-panel h3{margin:0 0 10px;font-size:16px;}" +
35
+ "#wg-auto-panel .wg-here{color:#c9a6ff;margin-bottom:12px;}" +
36
+ "#wg-auto-panel .wg-actions{display:flex;flex-direction:column;gap:8px;}" +
37
+ "#wg-auto-panel button.wg-act{text-align:left;padding:10px 12px;border-radius:10px;border:1px solid #5b3aa8;" +
38
+ "background:#2a1450;color:#fff;font:13px/1.3 system-ui,sans-serif;cursor:pointer;}" +
39
+ "#wg-auto-panel button.wg-act:hover{background:#3a1d70;}" +
40
+ "#wg-auto-panel button.wg-act .wg-kind{color:#a78bfa;font-size:11px;text-transform:uppercase;}" +
41
+ "#wg-auto-panel button.wg-quit{margin-top:12px;background:#3a1a1a;border-color:#7f1d1d;}" +
42
+ "#wg-auto-panel textarea{width:100%;box-sizing:border-box;margin:6px 0 10px;background:#1a1030;color:#fff;" +
43
+ "border:1px solid #5b3aa8;border-radius:8px;padding:8px;font:12px/1.3 monospace;}" +
44
+ "#wg-auto-panel label.wg-mem{display:block;margin-top:8px;font-size:12px;color:#ddd;}" +
45
+ "#wg-auto-panel .wg-section{margin:12px 0 6px;font-size:11px;font-weight:700;color:#a78bfa;text-transform:uppercase;letter-spacing:.04em;}" +
46
+ "#wg-auto-panel .wg-map{margin:8px 0 12px;padding:8px 10px;border-radius:8px;background:rgba(0,0,0,.25);font-size:11px;line-height:1.5;}" +
47
+ "#wg-auto-panel .wg-map .wg-cp{opacity:.55;}#wg-auto-panel .wg-map .wg-cp-here{color:#fff;opacity:1;font-weight:700;}" +
48
+ "#wg-auto-panel .wg-chrome{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 0 8px;}" +
49
+ "#wg-auto-panel .wg-chrome-title{font:700 11px/1.2 system-ui,sans-serif;color:#c9a6ff;" +
50
+ "letter-spacing:.04em;text-transform:uppercase;}" +
51
+ "#wg-auto-panel button.wg-hide-btn{margin:0;padding:4px 10px;font:600 11px system-ui,sans-serif;" +
52
+ "background:#3a2a60;color:#e8dcff;border:1px solid #5b3aa8;border-radius:6px;cursor:pointer;}" +
53
+ "#wg-auto-panel button.wg-hide-btn:hover{background:#4b2a80;}" +
54
+ "#wg-auto-panel.wg-collapsed{width:auto;max-width:90vw;padding:8px 12px;max-height:none;overflow:hidden;}" +
55
+ "#wg-auto-panel.wg-collapsed .wg-body{display:none;}" +
56
+ "#wg-auto-panel.wg-collapsed .wg-chrome{margin:0;}" +
57
+ "@media (max-width:640px){" +
58
+ "#wg-auto-panel{left:8px;right:8px;bottom:8px;transform:none;max-width:none;width:auto;" +
59
+ "max-height:min(55vh,calc(100vh - 16px));padding:12px 14px;border-radius:12px;}" +
60
+ "#wg-auto-panel.wg-collapsed{left:50%;right:auto;transform:translateX(-50%);width:auto;}" +
61
+ "#wg-auto-panel button.wg-act{padding:12px;font-size:14px;}" +
62
+ "}";
63
+ /**
64
+ * Mem keys any Block on the current menu might read this turn - minus keys a
65
+ * dynamic `instanceOptions` row already answers by picking it (e.g. "which
66
+ * item to add to cart" comes from the menu row itself, not a hand-typed JSON
67
+ * textarea).
68
+ */
69
+ export function memKeysForMenu(menu, library) {
70
+ const instanceKeys = new Set();
71
+ for (const edge of menu.flat) {
72
+ if (edge.instanceOption)
73
+ instanceKeys.add(edge.instanceOption.key);
74
+ }
75
+ const keys = new Map();
76
+ for (const edge of menu.flat) {
77
+ const entry = library.get(edge.block);
78
+ for (const k of entry?.block.requires ?? []) {
79
+ if (instanceKeys.has(k))
80
+ continue;
81
+ keys.set(k.name, k);
82
+ }
83
+ }
84
+ return [...keys.values()];
85
+ }
86
+ export async function syncPanelMem(page, mem, keys) {
87
+ if (keys.length === 0)
88
+ return;
89
+ await ensureShadowRoot(page);
90
+ const edits = (await page.evaluate(() => {
91
+ const out = {};
92
+ __wgQA("textarea.wg-mem-input").forEach((ta) => {
93
+ const key = ta.getAttribute("data-mem-key");
94
+ if (key)
95
+ out[key] = ta.value;
96
+ });
97
+ return out;
98
+ }));
99
+ for (const k of keys) {
100
+ const raw = edits[k.name];
101
+ if (raw === undefined)
102
+ continue;
103
+ try {
104
+ mem.set(k, JSON.parse(raw));
105
+ }
106
+ catch (err) {
107
+ throw new Error(`Invalid JSON for mem key "${k.name}": ${err instanceof Error ? err.message : String(err)}`);
108
+ }
109
+ }
110
+ }
111
+ export function armPickWait() {
112
+ let resolve;
113
+ const promise = new Promise((res) => {
114
+ resolve = res;
115
+ });
116
+ return { promise, resolve };
117
+ }
118
+ export async function headfulPick(page, menu, library, mem, waitPick,
119
+ /** What the last Block run did, shown at the top of the menu. */
120
+ note) {
121
+ const here = menu.here;
122
+ const hereLabel = here ?? (menu.flat.length > 0 ? "Unknown - pick Start here" : "Unknown - no moves available");
123
+ const menuMemKeys = memKeysForMenu(menu, library);
124
+ const memFields = menuMemKeys.map((k) => {
125
+ let value = "{}";
126
+ try {
127
+ if (mem.has(k))
128
+ value = JSON.stringify(mem.get(k), null, 2);
129
+ else {
130
+ const def = memStubValue(k);
131
+ if (def !== undefined)
132
+ value = JSON.stringify(def, null, 2);
133
+ }
134
+ }
135
+ catch {
136
+ /* a value JSON can't encode shows as {} for the person to fill in */
137
+ }
138
+ return { name: k.name, value };
139
+ });
140
+ // Pre-resolve every candidate's hover-preview box THROUGH Playwright's own locator (>>
141
+ // piercing / :has-text() / :visible support), once, here on the Node side - not a raw selector
142
+ // string handed to the page for a per-mouseenter document.querySelector to fail silently on
143
+ // (same class of bug as rings.js's showRing; see its own comment for the full story). A
144
+ // mouseenter handler can't itself await a Node-side locator without visible hover lag, so the
145
+ // box is embedded as a data attribute instead and the in-page handler just reads it.
146
+ const hlSelectors = new Set();
147
+ for (const section of menu.sections) {
148
+ for (const c of section.edges) {
149
+ const entry = library.get(c.block);
150
+ const navClick = entry?.block?.__waygraphNavClick;
151
+ const hl = c.instanceOption?.highlight ?? (typeof navClick === "string" ? navClick : undefined);
152
+ if (hl)
153
+ hlSelectors.add(hl);
154
+ }
155
+ }
156
+ const hlBoxes = new Map();
157
+ await Promise.all([...hlSelectors].map(async (sel) => {
158
+ const loc = page.locator(sel).first();
159
+ const box = (await loc.count().catch(() => 0)) > 0
160
+ ? await loc
161
+ .evaluate((el) => {
162
+ const r = el.getBoundingClientRect();
163
+ if (r.width < 1 && r.height < 1)
164
+ return null;
165
+ return { x: r.x, y: r.y, width: r.width, height: r.height };
166
+ })
167
+ .catch(() => null)
168
+ : null;
169
+ hlBoxes.set(sel, box);
170
+ }));
171
+ let idx = 0;
172
+ const sections = menu.sections.map((section) => ({
173
+ title: section.title,
174
+ buttons: section.edges.map((c) => {
175
+ const entry = library.get(c.block);
176
+ const navClick = entry?.block?.__waygraphNavClick;
177
+ const hl = c.instanceOption?.highlight ?? (typeof navClick === "string" ? navClick : undefined);
178
+ const hlBox = hl ? hlBoxes.get(hl) : null;
179
+ const base = { idx: idx++, hlBox: hlBox ? JSON.stringify(hlBox) : undefined };
180
+ if (c.label)
181
+ return { ...base, label: c.label };
182
+ return { ...base, kind: c.kind, block: c.block, to: c.to, ...(entry?.description ? { description: entry.description } : {}) };
183
+ }),
184
+ }));
185
+ await ensureShadowRoot(page);
186
+ await page.evaluate(({ css, note, hereLabel, memFields, sections }) => {
187
+ __wgCss(css, "wg-auto-style");
188
+ __wgById("wg-auto-panel")?.remove();
189
+ const root = __wgEl("div", { id: "wg-auto-panel" }, __wgEl("h3", null, "waygraph auto"), note ? __wgEl("div", { style: "margin-bottom:8px;font-size:12px;color:#a7f3d0" }, note) : null, __wgEl("div", { class: "wg-here" }, "You are here: ", __wgEl("strong", null, hereLabel)), memFields.length ? __wgEl("div", { class: "wg-section" }, "Mem - edit credentials, then click a block below") : null, memFields.map((f, i) => {
190
+ const field = __wgEl("textarea", { class: "wg-mem-input", id: "wg-mem-" + i, data: { memKey: f.name }, attrs: { rows: 4 } });
191
+ field.value = f.value;
192
+ return [__wgEl("label", { class: "wg-mem" }, f.name), field];
193
+ }), __wgEl("div", { class: "wg-actions" }, sections.length === 0 ? __wgEl("div", { style: "opacity:.85;padding:8px 0" }, "No moves from this screen right now.") : null, sections.map((section) => [
194
+ __wgEl("div", { class: "wg-section" }, section.title),
195
+ section.buttons.map((b) => __wgEl("button", { class: "wg-act", data: { idx: b.idx, hlBox: b.hlBox } }, b.label !== undefined
196
+ ? b.label
197
+ : [
198
+ __wgEl("span", { class: "wg-kind" }, b.kind ?? ""),
199
+ " ",
200
+ __wgEl("strong", null, b.block ?? ""),
201
+ " -> " + (b.to ?? ""),
202
+ b.description ? [__wgEl("br"), __wgEl("span", { style: "opacity:.85" }, b.description)] : null,
203
+ ])),
204
+ ])), __wgEl("button", { class: "wg-act wg-quit", data: { quit: "1" } }, "Quit"));
205
+ __wgAdd(root);
206
+ // Chrome + Hide/Show, shared with setAutoPanelRunning.
207
+ window.__wgWirePanelChrome = (el, storageKey, chromeTitle) => {
208
+ if (!el)
209
+ return;
210
+ if (!el.querySelector(":scope > .wg-chrome")) {
211
+ const body = __wgEl("div", { class: "wg-body" }, Array.from(el.childNodes));
212
+ const chrome = __wgEl("div", { class: "wg-chrome" }, __wgEl("span", { class: "wg-chrome-title" }, chromeTitle || "waygraph auto"), __wgEl("button", { class: "wg-hide-btn", data: { wgToggle: "1" }, attrs: { type: "button" } }, "Hide"));
213
+ el.append(chrome, body);
214
+ }
215
+ const apply = (hidden) => {
216
+ el.classList.toggle("wg-collapsed", hidden);
217
+ el.setAttribute("data-wg-collapsed", hidden ? "1" : "0");
218
+ const t = el.querySelector("[data-wg-toggle]");
219
+ if (t)
220
+ t.textContent = hidden ? "Show" : "Hide";
221
+ try {
222
+ localStorage.setItem(storageKey, hidden ? "1" : "0");
223
+ }
224
+ catch {
225
+ /* blocked storage: Hide still works for this page */
226
+ }
227
+ };
228
+ let hidden = false;
229
+ try {
230
+ hidden = localStorage.getItem(storageKey) === "1";
231
+ }
232
+ catch {
233
+ /* blocked storage: starts shown */
234
+ }
235
+ apply(hidden);
236
+ const toggle = el.querySelector("[data-wg-toggle]");
237
+ if (toggle && !toggle.dataset.wgWired) {
238
+ toggle.dataset.wgWired = "1";
239
+ toggle.addEventListener("click", (e) => {
240
+ e.stopPropagation();
241
+ apply(!el.classList.contains("wg-collapsed"));
242
+ });
243
+ }
244
+ };
245
+ window.__wgWirePanelChrome(root, "wg-auto-panel-hidden", "waygraph auto");
246
+ if (typeof window.__wgStampModal === "function") {
247
+ window.__wgStampModal(root, "auto-panel", { phase: "pick", ready: true });
248
+ }
249
+ else {
250
+ root.setAttribute("data-wg-ui", "1");
251
+ root.setAttribute("data-wg-modal", "auto-panel");
252
+ root.setAttribute("data-wg-phase", "pick");
253
+ root.setAttribute("data-wg-collapsed", root.classList.contains("wg-collapsed") ? "1" : "0");
254
+ root.setAttribute("data-wg-ready", "1");
255
+ window.__wgOverlayBeacon =
256
+ window.__wgOverlayBeacon ||
257
+ (() => Array.from(__wgQA('[data-wg-ui="1"]')).map((el) => {
258
+ const r = el.getBoundingClientRect();
259
+ const st = getComputedStyle(el);
260
+ return {
261
+ id: el.id || null,
262
+ modal: el.getAttribute("data-wg-modal"),
263
+ ready: el.getAttribute("data-wg-ready") === "1",
264
+ phase: el.getAttribute("data-wg-phase"),
265
+ step: el.getAttribute("data-wg-step"),
266
+ block: el.getAttribute("data-wg-block"),
267
+ collapsed: el.getAttribute("data-wg-collapsed") === "1" || el.classList.contains("wg-collapsed"),
268
+ opacity: st.opacity,
269
+ textLen: (el.innerText || "").trim().length,
270
+ w: Math.round(r.width),
271
+ h: Math.round(r.height),
272
+ visible: r.width > 0 && r.height > 0 && Number(st.opacity) > 0.05,
273
+ };
274
+ }));
275
+ }
276
+ root.querySelectorAll("button[data-idx]").forEach((btn) => {
277
+ btn.addEventListener("click", () => window.__wgAutoPick?.(Number(btn.dataset.idx)));
278
+ btn.addEventListener("mouseenter", () => {
279
+ // Pre-resolved on the Node side via Playwright's own locator (>> piercing / :has-text()
280
+ // support) when this menu was built - see headfulPick's own comment for why a
281
+ // mouseenter handler can't itself round-trip to a Node-side locator per hover.
282
+ const raw = btn.dataset.hlBox;
283
+ if (!raw || typeof window.__wgPositionRing !== "function")
284
+ return;
285
+ window.__wgPositionRing(JSON.parse(raw), btn.textContent?.trim()?.slice(0, 80) || "target", "auto");
286
+ });
287
+ btn.addEventListener("mouseleave", () => window.__wgHideRing?.());
288
+ });
289
+ root.querySelector("[data-quit]")?.addEventListener("click", () => window.__wgAutoQuit?.());
290
+ }, { css: PANEL_CSS, note, hereLabel, memFields, sections });
291
+ return waitPick();
292
+ }
293
+ export function printCliMenu(menu, library) {
294
+ console.log("");
295
+ console.log(`You are here: ${menu.here ?? "Unknown (pick Start here)"}`);
296
+ console.log("");
297
+ if (menu.flat.length === 0) {
298
+ console.log(" (no moves available on this page)");
299
+ }
300
+ let n = 0;
301
+ for (const section of menu.sections) {
302
+ console.log(` -- ${section.title} --`);
303
+ for (const c of section.edges) {
304
+ const entry = library.get(c.block);
305
+ n++;
306
+ if (c.label) {
307
+ console.log(` [${n}] ${c.label}`);
308
+ }
309
+ else {
310
+ const desc = entry?.description ? ` - ${entry.description}` : "";
311
+ console.log(` [${n}] ${c.block} (${c.kind}) -> ${c.to}${desc}`);
312
+ }
313
+ }
314
+ }
315
+ console.log(" [q] quit");
316
+ console.log("");
317
+ }
318
+ export async function cliPick(menu, rl) {
319
+ while (true) {
320
+ let ans;
321
+ try {
322
+ ans = (await rl.question("Choose: ")).trim().toLowerCase();
323
+ }
324
+ catch {
325
+ return { type: "quit" }; // stdin ended (piped / Ctrl-D) - same as q
326
+ }
327
+ if (ans === "q" || ans === "quit")
328
+ return { type: "quit" };
329
+ const n = Number(ans);
330
+ if (Number.isInteger(n) && n >= 1 && n <= menu.flat.length) {
331
+ return { type: "pick", index: n - 1 };
332
+ }
333
+ console.log("Enter a number from the list, or q to quit.");
334
+ }
335
+ }
@@ -1,12 +1,8 @@
1
- import type { ApplyPathResult, ApplyPickResult, AutoSessionInit, ConsoleLogEntry, InspectDomOptions, InspectDomResult, SessionSnapshot, StorageSnapshot, StubFileKind, TraceStep } from "./helpers.js";
2
1
  import type { PilotHighlightFixtures } from "../../demo/overlay/pilot/fixtures.js";
2
+ import type { ApplyPathResult, ApplyPickResult, AutoSessionInit, ConsoleLogEntry, InspectDomOptions, InspectDomResult, SessionSnapshot, StorageSnapshot, StubFileKind, TraceStep } from "./helpers.js";
3
3
  /**
4
- * Non-interactive explore session: same graph/library/mem/browser setup
5
- * `runAutoExplore` runs in `--cli` mode, driven by `applyPick` instead of a
6
- * blocking `readline` loop. Headless by default (matching
7
- * `runAutoExplore`'s own `cli` branch), optionally visible via
8
- * `AutoSessionInit.headless: false` - this type only ever backs the `--cli`
9
- * picker, never the headful page-embedded panel, headless or not.
4
+ * A live session driven over IPC (`browser start`, `pilot start`): the same graph, library, mem and browser as
5
+ * `waygraph auto`, moved one call at a time (`applyPick`, `applyPath`, the raw actions) instead of a menu loop.
10
6
  */
11
7
  export declare class AutoSession {
12
8
  private readonly projectDir;
@@ -55,87 +51,42 @@ export declare class AutoSession {
55
51
  * element's subtree; it is a modifier, not a third mode.
56
52
  */
57
53
  inspectDom(opts: InspectDomOptions): Promise<InspectDomResult>;
58
- /**
59
- * Pure getter - reads the live page's client-side state: localStorage,
60
- * sessionStorage, and registered service workers (scope/active URL/state).
61
- * Real, direct follow-up to `auto console`'s own gap: a site doing web
62
- * push (this target has a real `NOTIFICATION_VAPID` config, confirmed via
63
- * `auto dom` earlier) keeps push-subscription/auth state in these places,
64
- * not in the rendered DOM `inspectDom` reads.
65
- */
54
+ /** Reads the live page's client state: storage, cookies, service workers (where web-push state lives). */
66
55
  inspectStorage(): Promise<StorageSnapshot>;
67
- /**
68
- * Runs one already-resolved Block entry for real - the shared core both
69
- * `applyPick` (resolved via a live-menu index) and `applyPath` (resolved
70
- * directly by name, bypassing live-menu-visibility entirely - see
71
- * `applyPath`'s own comment for why) call. `expectedTo` is only used as a
72
- * fallback when a Block's own `resolve()` doesn't set `__state` - same
73
- * behavior `applyPick` always had, just factored out, not changed.
74
- */
75
56
  /** Paint + log a Block's authored waygraph overlay fixtures (same as demo does), best-effort. */
76
57
  private paintStubPhase;
58
+ /**
59
+ * Runs one Block for real: the core of `applyPick` (picked from the live menu) and `applyPath` (named by the route).
60
+ * `expectedTo` is used when the Block's resolve doesn't set `__state`.
61
+ */
77
62
  private runNamedBlock;
78
63
  /** Applies exactly one pick: runs at most one Block, returns the resulting state. */
79
64
  applyPick(raw: string): Promise<ApplyPickResult>;
80
65
  /**
81
- * Re-discovers the project's Block library and graph from disk, using the
82
- * exact same call `start()` itself makes - not a duplicated read - and
83
- * replaces `this.library`/`this.graph` in place. Leaves the live page,
84
- * mem, browser/context, and current Checkpoint (`here`) untouched, so a
85
- * Block written to disk mid-session becomes pickable via a later
86
- * `applyPick` without restarting the session. See
87
- * openspec/changes/waygraph-blind-pilot/design.md.
66
+ * Re-reads the project's Blocks and graph from disk (the same call `start()` makes) and swaps them in, keeping the
67
+ * page, mem, browser and `here`: a Block written mid-session becomes pickable without a restart.
88
68
  */
89
69
  reloadLibrary(): Promise<void>;
90
70
  /**
91
- * Forces `here` to be re-detected from the real live page, discarding
92
- * whatever was cached - the fix for a real, confirmed gap: `here` is only
93
- * ever updated by an action this class itself ran (`applyPick`/`applyPath`
94
- * set it to a Block's own resolved Checkpoint; `rawClick`/`rawType`/
95
- * `rawGoto` reset it to `null` because *they* just changed the page). If a
96
- * human co-driving the same visible session clicks/types/navigates
97
- * directly - a real scenario for a headful Pilot session someone is
98
- * watching, not hypothetical - none of that goes through this class at
99
- * all, so `here` silently goes stale: `currentMenu()`'s own lazy check
100
- * (`if (this.here === null)`, just above) only re-detects when `here` is
101
- * already unknown, never to confirm a *known* value is still correct.
102
- * This does not crash the session (the process/socket/page all stay
103
- * alive) but subsequent `send`/`reach` calls would act on a menu built
104
- * from the wrong Checkpoint until something forces re-detection - this
105
- * method is that: unconditionally re-run the same `detectHere` logic the
106
- * lazy path already uses, regardless of what `here` currently holds.
71
+ * Re-detects `here` from the live page, whatever is cached. `here` only changes when this session acts; a person
72
+ * clicking in a visible session moves the page without it, and later `send` / `reach` calls would use a stale menu.
107
73
  */
108
74
  resync(): Promise<ApplyPickResult>;
109
75
  /**
110
- * Raw interaction primitives for a page no Block covers yet (Blind Pilot -
111
- * see openspec/changes/waygraph-blind-pilot). Unlike `applyPick`, the
112
- * resulting Checkpoint isn't known ahead of time from a Block's own
113
- * `resolve()`, so each of these invalidates the cached `here` (forcing
114
- * `currentMenu()`'s existing lazy `detectHere` call to run again) rather
115
- * than assuming the page didn't move anywhere a Block now recognizes.
116
- * A selector matching nothing is a reported failure, not a silent no-op.
76
+ * Raw actions for a page no Block covers yet (Blind Pilot). The Checkpoint they land on isn't known ahead, so each
77
+ * one forgets `here` and the next menu re-detects it. A selector that matches nothing is a reported failure.
117
78
  */
118
79
  rawClick(selector: string): Promise<ApplyPickResult>;
119
80
  rawType(selector: string, text: string): Promise<ApplyPickResult>;
120
- /**
121
- * Presses a real key on a real focused element - a real gap found live:
122
- * many real inline-edit inputs (no visible Save button at all) commit on
123
- * Enter, not on blur/click-elsewhere. `key` is a Playwright key name
124
- * (e.g. "Enter", "Escape", "Tab") - same vocabulary as
125
- * `page.keyboard.press`, which this wraps.
126
- */
81
+ /** Presses a key on an element (`Enter`, `Escape`, ...): inline-edit inputs often commit on Enter, with no Save button. */
127
82
  rawPress(selector: string, key: string): Promise<ApplyPickResult>;
128
83
  rawGoto(url: string): Promise<ApplyPickResult>;
129
- /**
130
- * Fills a real `<input type="file">` - a built-in stub kind
131
- * ("image"/"pdf"/"video", see `stubFilePath`) or a caller-supplied
132
- * `filePath` for anything else ("whatever", per the real request this
133
- * responds to). Same raw-primitive shape as click/type/goto: works with
134
- * zero Blocks, invalidates `here` since the app may react to the upload.
135
- */
84
+ /** Fills an `<input type="file">` with a built-in stub file ("image", "pdf", "video") or a `filePath` of your own. */
136
85
  rawUpload(selector: string, stub: StubFileKind | {
137
86
  filePath: string;
138
87
  }): Promise<ApplyPickResult>;
88
+ /** One raw primitive (no Block): announce it, run it, then forget `here` so the next menu re-detects it. */
89
+ private rawStep;
139
90
  /**
140
91
  * Agent-sent highlight fixtures (rings + optional todos) on the live page -
141
92
  * the Pilot equivalent of Block `stubBefore` narration in `waygraph demo`.
@@ -151,42 +102,11 @@ export declare class AutoSession {
151
102
  error: string;
152
103
  }>;
153
104
  /**
154
- * Runs a whole multi-step route to `targetCheckpoint` in one call, instead
155
- * of an agent hand-picking one index at a time across many separate
156
- * `applyPick` round trips - real, reported pain for any non-trivial task
157
- * on a rich graph ("the entire prompts of the day" driving one step per
158
- * call).
159
- *
160
- * Two other designs were tried and rejected by direct reproduction against
161
- * live saucedemo.com before this one, both undone by the same root cause:
162
- * a `from: "*"` edge (e.g. "Checkout", only clickable once you're actually
163
- * on the cart page with items) looks globally reachable to
164
- * `findBlockPath`'s static-graph BFS, which has no live-page access and
165
- * can't know the edge's real precondition isn't showing yet. Computing the
166
- * whole route once upfront names a premature wildcard step immediately;
167
- * scoring each live menu option by its static-graph distance to the
168
- * target (greedy best-first) doesn't fix it either - a harmless self-loop
169
- * Method can score just as well as real progress once wildcard "shortcuts"
170
- * are baked into every distance calculation, and the session gets stuck
171
- * repeating it.
172
- *
173
- * The fix that actually works: stop trying to be clever about live-menu
174
- * visibility per step, and reuse `auto --blocks <From> <To>`'s own already
175
- * -proven approach instead - compute the path ONCE (via
176
- * `findBlockPathDetailed`, the same BFS `findBlockPath`/`runChainAuto`
177
- * already use, extended to also return each hop's specific edge - see its
178
- * own doc comment for why that extra detail is load-bearing, not cosmetic),
179
- * then run each Block in it directly via `runNamedBlock`, trusting the
180
- * graph the same way `runChain`'s own flow execution already does, not
181
- * gated on live-menu visibility at all. The one real difference from
182
- * `runChainAuto`: this runs against the session's own already-live
183
- * page/context/mem, not a freshly spawned browser - the whole point of
184
- * staying in one session. A step whose real precondition genuinely isn't
185
- * met still fails loud, naming that step, exactly the way a mis-ordered
186
- * `runChain` spec would - and a step that runs without throwing but lands
187
- * somewhere other than what its own edge promised (a real, observed case,
188
- * not hypothetical - see this method's own test suite) fails loud too,
189
- * rather than reporting false success.
105
+ * Runs a whole route to `targetCheckpoint` in one call, instead of one `applyPick` per step. The path is computed
106
+ * once with `findBlockPathDetailed` (the BFS `auto --blocks <From> <To>` uses) and each hop's Block runs directly on
107
+ * the session's live page and mem, trusting the graph rather than live-menu visibility. Per-step choosing was tried
108
+ * and fails: a `from: "*"` edge looks reachable from anywhere to a static BFS. A step whose precondition isn't met
109
+ * fails loud, naming it; so does a step that runs but lands somewhere other than its edge promised.
190
110
  */
191
111
  applyPath(targetCheckpoint: string): Promise<ApplyPathResult>;
192
112
  close(): Promise<void>;