waygraph 0.16.2 → 0.16.4
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/README.md +12 -0
- package/dist/analysis/practices-check.d.ts +1 -1
- package/dist/analysis/practices-check.js +36 -0
- package/dist/beta.d.ts +6 -0
- package/dist/beta.js +5 -0
- package/dist/cli/commands/codemod-flows.js +2 -2
- package/dist/cli/commands/codemod-stubs.js +43 -1
- package/dist/cli/commands/pilot.js +3 -16
- package/dist/cli/commands/session-ops.js +26 -1
- package/dist/cli/flags.d.ts +1 -1
- package/dist/cli/flags.js +1 -1
- package/dist/cli/usage.js +6 -0
- package/dist/demo/narration/stub-ctx.js +126 -124
- package/dist/demo/overlay/cursor.js +1 -1
- package/dist/demo/overlay/inpage/banner.js +5 -0
- package/dist/demo/overlay/inpage/core.js +1 -0
- package/dist/demo/overlay/inpage/network.js +1 -1
- package/dist/demo/overlay/inpage/todos.js +0 -15
- package/dist/demo/overlay/network-capture.js +2 -2
- package/dist/demo/overlay/overlay-css.js +18 -21
- package/dist/demo/overlay/overlay-stage.js +1 -1
- package/dist/demo/overlay/phase-fixtures.js +5 -5
- package/dist/demo/overlay/pilot/shell.js +7 -5
- package/dist/demo/overlay/ui/components/ring.js +2 -2
- package/dist/demo/overlay/ui/overlay.css +98 -35
- package/dist/demo/overlay/ui/shadow.js +69 -2
- package/dist/demo/stepper/demo-log.js +8 -8
- package/dist/demo/stepper/slides.d.ts +1 -1
- package/dist/demo/stepper/slides.js +1 -1
- package/dist/demo/stepper/stepper.js +1 -1
- package/dist/engine/block/kind.d.ts +3 -0
- package/dist/engine/block/kind.js +11 -0
- package/dist/engine/errors.d.ts +12 -0
- package/dist/engine/errors.js +12 -0
- package/dist/engine/read/read-block.d.ts +61 -0
- package/dist/engine/read/read-block.js +94 -0
- package/dist/engine/read/read-key.d.ts +32 -0
- package/dist/engine/read/read-key.js +34 -0
- package/dist/engine/read/read-page.d.ts +19 -0
- package/dist/engine/read/read-page.js +50 -0
- package/dist/engine/read/readers.d.ts +82 -0
- package/dist/engine/read/readers.js +43 -0
- package/dist/engine/read/run-read.d.ts +17 -0
- package/dist/engine/read/run-read.js +156 -0
- package/dist/engine/run/connect.js +3 -2
- package/dist/engine/run/events.d.ts +28 -10
- package/dist/engine/run/events.js +52 -38
- package/dist/engine/run/run.js +25 -13
- package/dist/engine/stub/stub.d.ts +26 -21
- package/dist/index.d.ts +7 -4
- package/dist/index.js +9 -4
- package/dist/live/auto/auto.js +2 -3
- package/dist/live/auto/menu.js +3 -2
- package/dist/live/auto-session/auto-session.d.ts +9 -2
- package/dist/live/auto-session/auto-session.js +23 -20
- package/dist/live/auto-session/feed.d.ts +16 -0
- package/dist/live/auto-session/feed.js +71 -0
- package/dist/live/auto-session/helpers.d.ts +7 -0
- package/dist/live/auto-session/helpers.js +9 -0
- package/dist/live/auto-session/ipc.d.ts +15 -47
- package/dist/live/auto-session/ipc.js +34 -4
- package/dist/live/auto-session/protocol.d.ts +127 -0
- package/dist/live/auto-session/protocol.js +6 -0
- package/dist/live/auto-session/read.d.ts +25 -0
- package/dist/live/auto-session/read.js +50 -0
- package/dist/live/traverse/worker.d.ts +1 -0
- package/dist/live/traverse/worker.js +6 -1
- package/dist/manifest/manifest.d.ts +22 -2
- package/dist/manifest/manifest.js +27 -1
- package/dist/manifest/project.d.ts +10 -1
- package/dist/manifest/project.js +9 -12
- package/dist/manifest/read-blocks.d.ts +2 -0
- package/dist/manifest/read-blocks.js +23 -0
- package/dist/manifest/scan.d.ts +3 -2
- package/dist/manifest/scan.js +8 -4
- package/dist/runner/runner.d.ts +0 -1
- package/dist/runner/runner.js +6 -6
- package/dist/runtime/full-launch.d.ts +2 -1
- package/dist/runtime/full-launch.js +29 -27
- package/dist/runtime/run-log.d.ts +2 -0
- package/dist/runtime/run-log.js +13 -6
- package/examples/saucedemo/README.md +2 -2
- package/examples/saucedemo/src/blocks/SITE-MAP.md +3 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.read.block.ts +26 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/header.read.block.ts +25 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/inventory.read.block.ts +28 -0
- package/examples/saucedemo/src/flows/api-demo.flow.ts +1 -1
- package/examples/saucedemo/src/flows/cart-bulk.flow.ts +1 -1
- package/examples/saucedemo/src/flows/cart-continue.flow.ts +1 -1
- package/examples/saucedemo/src/flows/catalog-logout.flow.ts +1 -1
- package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +1 -1
- package/examples/saucedemo/src/flows/checkout.flow.ts +11 -11
- package/examples/saucedemo/src/flows/login.flow.ts +1 -1
- package/examples/saucedemo/src/flows/shop.flow.ts +1 -1
- package/examples/saucedemo/src/flows/stub-on-error.flow.ts +1 -1
- package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +1 -1
- package/examples/saucedemo/src/states/checkout.reads.ts +9 -0
- package/examples/saucedemo/src/stubs/device-showcase.ts +37 -37
- package/examples/saucedemo/src/stubs/finish-order.ts +2 -2
- package/examples/saucedemo/src/stubs/login.ts +5 -5
- package/package.json +5 -1
- package/templates/quickstart/src/flows/api-demo.flow.ts +1 -1
- package/templates/quickstart/src/flows/cart-bulk.flow.ts +1 -1
- package/templates/quickstart/src/flows/cart-continue.flow.ts +1 -1
- package/templates/quickstart/src/flows/catalog-logout.flow.ts +1 -1
- package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +1 -1
- package/templates/quickstart/src/flows/checkout.flow.ts +11 -11
- package/templates/quickstart/src/flows/login.flow.ts +1 -1
- package/templates/quickstart/src/flows/shop.flow.ts +1 -1
- package/templates/quickstart/src/flows/stub-on-error.flow.ts +1 -1
- package/templates/quickstart/src/flows/viewer-blocked.flow.ts +1 -1
- package/templates/quickstart/src/stubs/device-showcase.ts +37 -37
- package/templates/quickstart/src/stubs/finish-order.ts +2 -2
- package/templates/quickstart/src/stubs/login.ts +5 -5
- package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +1 -1
- package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +1 -1
- package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +1 -1
- package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +4 -4
- package/templates/scaffold/src/flows/README.md +1 -1
- package/templates/scaffold/src/flows/home/cart.flow.ts +7 -7
- package/templates/scaffold/src/flows/home/hello.flow.ts +8 -8
- package/templates/skills/waygraph-convention.skill.md +10 -2
- package/templates/skills/waygraph-pilot.skill.md +7 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import "./runtime/
|
|
3
|
-
//
|
|
4
|
-
|
|
1
|
+
import { installFullLaunch } from "./runtime/full-launch.js";
|
|
2
|
+
import { installRunLog } from "./runtime/run-log.js";
|
|
3
|
+
// The package entry wires the runtime in, explicitly: how an Engine launches its own browser (config, Chromium path,
|
|
4
|
+
// extensions), and the run log (.waygraph/runs) the CLI turns on. No module switches itself on by being imported.
|
|
5
|
+
installFullLaunch();
|
|
6
|
+
installRunLog();
|
|
5
7
|
export { connect } from "./engine/run/connect.js";
|
|
6
8
|
export { checkpoint } from "./engine/checkpoint/checkpoint.js";
|
|
7
9
|
export { MemKey, MemPage, key, keyGroup } from "./engine/mem/mem.js";
|
|
@@ -39,6 +41,9 @@ export { runTraverse } from "./live/traverse/traverse.js";
|
|
|
39
41
|
export { buildCoverageReport, collectGraphEdgeKeys, defaultCoverageOutPath, formatCoverageLine, normalizeHitKey, parseMinEdgeCoverage, writeCoverageReport } from "./live/traverse/coverage.js";
|
|
40
42
|
export { WG_UI_SEL, WG_READY_PANEL_SEL, WG_READY_AUTO_SEL, WG_READY_BANNER_SEL, readWgOverlayBeacon, readWgOverlayBeaconInPage, assertWgOverlayReady } from "./demo/overlay/beacon.js";
|
|
41
43
|
export { AutoSession } from "./live/auto-session/auto-session.js";
|
|
44
|
+
export { SESSION_OPS, SESSION_PROTOCOL_VERSION } from "./live/auto-session/protocol.js";
|
|
45
|
+
export { subscribeSession } from "./live/auto-session/feed.js";
|
|
46
|
+
export { requestSession } from "./live/auto-session/ipc.js";
|
|
42
47
|
export { stubFilePath } from "./live/auto-session/helpers.js";
|
|
43
48
|
export { pilotStart } from "./live/pilot/pilot.js";
|
|
44
49
|
export { defineConfig, resolveBaseUrl } from "./runtime/waygraph-config.js";
|
package/dist/live/auto/auto.js
CHANGED
|
@@ -61,8 +61,6 @@ export async function runOneBlock(engine, entry, context, page, mem, headful) {
|
|
|
61
61
|
await page.evaluate("delete window.__wgPendingNavClickLabel").catch(ignorePageGone);
|
|
62
62
|
return result;
|
|
63
63
|
}
|
|
64
|
-
/** Shown in the headful panel after each Block run. */
|
|
65
|
-
let lastRunNote = null;
|
|
66
64
|
/**
|
|
67
65
|
* Interactive waygraph auto: locate current Checkpoint, list runnable Blocks, pick one, repeat.
|
|
68
66
|
*/
|
|
@@ -70,7 +68,8 @@ export async function runAutoExplore(projectDir, options = {}) {
|
|
|
70
68
|
await withWebServer(projectDir, () => runAutoExploreCore(projectDir, options));
|
|
71
69
|
}
|
|
72
70
|
async function runAutoExploreCore(projectDir, options = {}) {
|
|
73
|
-
|
|
71
|
+
/** Shown in the headful panel after each Block run. */
|
|
72
|
+
let lastRunNote = null;
|
|
74
73
|
const cli = options.cli === true;
|
|
75
74
|
const baseURL = options.baseURL ?? (await resolveBaseUrl(projectDir)) ?? envString("WAYGRAPH_BASE_URL");
|
|
76
75
|
const startUrl = options.startUrl ?? baseURL;
|
package/dist/live/auto/menu.js
CHANGED
|
@@ -27,8 +27,9 @@ export async function setAutoPanelRunning(page, label) {
|
|
|
27
27
|
}, label)
|
|
28
28
|
.catch(ignorePageGone);
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// In the bottom-center slot of the overlay layout (demo/overlay/ui/css/layout.css), like the demo's step panel.
|
|
31
|
+
export const PANEL_CSS = "#wg-auto-panel{z-index:var(--wg-z-top);" +
|
|
32
|
+
"width:min(92vw,720px);overflow-y:auto;box-sizing:border-box;" +
|
|
32
33
|
"background:rgba(20,10,40,.94);color:#fff;border-radius:14px;padding:16px 20px;" +
|
|
33
34
|
"font:14px/1.4 system-ui,sans-serif;box-shadow:0 12px 30px rgba(0,0,0,.35);}" +
|
|
34
35
|
"#wg-auto-panel h3{margin:0 0 10px;font-size:16px;}" +
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PilotHighlightFixtures } from "../../demo/overlay/pilot/fixtures.js";
|
|
2
2
|
import type { ApplyPathResult, ApplyPickResult, AutoSessionInit, ConsoleLogEntry, InspectDomOptions, InspectDomResult, SessionSnapshot, StorageSnapshot, StubFileKind, TraceStep } from "./helpers.js";
|
|
3
|
+
import { type SessionRead } from "./read.js";
|
|
3
4
|
/**
|
|
4
5
|
* A live session driven over IPC (`browser start`, `pilot start`): the same graph, library, mem and browser as
|
|
5
6
|
* `waygraph auto`, moved one call at a time (`applyPick`, `applyPath`, the raw actions) instead of a menu loop.
|
|
@@ -17,12 +18,12 @@ export declare class AutoSession {
|
|
|
17
18
|
private page;
|
|
18
19
|
private readonly startUrl;
|
|
19
20
|
private readonly sessionId;
|
|
21
|
+
private readonly knownInteractionSelectors;
|
|
22
|
+
private readonly headless;
|
|
20
23
|
private here;
|
|
21
24
|
private lastRunNote;
|
|
22
25
|
private readonly trace;
|
|
23
26
|
private readonly consoleLog;
|
|
24
|
-
private readonly knownInteractionSelectors;
|
|
25
|
-
private readonly headless;
|
|
26
27
|
private lastOverlayInfo;
|
|
27
28
|
private constructor();
|
|
28
29
|
static start(init: AutoSessionInit): Promise<AutoSession>;
|
|
@@ -43,8 +44,14 @@ export declare class AutoSession {
|
|
|
43
44
|
* Deduped per page load so repeated status/send calls stay quiet.
|
|
44
45
|
*/
|
|
45
46
|
private warnUnmappedInteractions;
|
|
47
|
+
/** The Checkpoint the session is at, if known. */
|
|
48
|
+
get where(): string | null;
|
|
46
49
|
/** Pure getter - no Block runs, no mem/page mutation. */
|
|
47
50
|
currentSnapshot(): Promise<SessionSnapshot>;
|
|
51
|
+
/** The menu as a snapshot, with what the page shows (Read Blocks) when any covers it. */
|
|
52
|
+
private snapshotOf;
|
|
53
|
+
/** Pure getter - what the page shows, from the project's Read Blocks (beta). */
|
|
54
|
+
read(path?: string): Promise<SessionRead>;
|
|
48
55
|
/**
|
|
49
56
|
* Pure getter - reads the live page's structure at the requested fidelity.
|
|
50
57
|
* No Block runs, no mem/page mutation. `selector` scopes either mode to one
|
|
@@ -3,7 +3,7 @@ import { findBlockPathDetailed } from "../../analysis/graph.js";
|
|
|
3
3
|
import { runStubPhase } from "../../demo/narration/narration.js";
|
|
4
4
|
import { describeOverlayFixtures, phaseToOverlayFixtures } from "../../demo/overlay/phase-fixtures.js";
|
|
5
5
|
import { showPilotFixtures } from "../../demo/overlay/pilot/fixtures.js";
|
|
6
|
-
import { showPilotActivity
|
|
6
|
+
import { showPilotActivity } from "../../demo/overlay/pilot/shell.js";
|
|
7
7
|
import { keepOverlayOnNavigation } from "../../demo/overlay/reattach.js";
|
|
8
8
|
import { verboseLog } from "../../demo/verbose-log.js";
|
|
9
9
|
import { Engine } from "../../engine/engine.js";
|
|
@@ -15,17 +15,10 @@ import { buildExploreContext, buildExploreMenu } from "../auto/explore.js";
|
|
|
15
15
|
import { ensureMem, seedDefaultMem } from "../auto/mem.js";
|
|
16
16
|
import { updatePilotOverlay } from "../pilot/panel.js";
|
|
17
17
|
import { recordPageConsole } from "./console.js";
|
|
18
|
-
import { CONSOLE_LOG_MAX_ENTRIES, TRACE_MAX_STEPS, buildSessionSnapshot, describeHighlight, parsePick, stubFilePath, stubPhaseHasContent } from "./helpers.js";
|
|
18
|
+
import { CONSOLE_LOG_MAX_ENTRIES, TRACE_MAX_STEPS, buildSessionSnapshot, describeHighlight, parsePick, rawTarget, stubFilePath, stubPhaseHasContent } from "./helpers.js";
|
|
19
19
|
import { inspectDom, inspectStorage } from "./inspect.js";
|
|
20
20
|
import { openSessionBrowser } from "./open-browser.js";
|
|
21
|
-
|
|
22
|
-
async function rawTarget(page, selector, label) {
|
|
23
|
-
const locator = page.locator(selector).first();
|
|
24
|
-
if ((await locator.count()) === 0)
|
|
25
|
-
return `no element matches selector "${selector}"`;
|
|
26
|
-
await showPilotVision(page, selector, label);
|
|
27
|
-
return locator;
|
|
28
|
-
}
|
|
21
|
+
import { readSession, snapshotRead } from "./read.js";
|
|
29
22
|
/**
|
|
30
23
|
* A live session driven over IPC (`browser start`, `pilot start`): the same graph, library, mem and browser as
|
|
31
24
|
* `waygraph auto`, moved one call at a time (`applyPick`, `applyPath`, the raw actions) instead of a menu loop.
|
|
@@ -43,12 +36,12 @@ export class AutoSession {
|
|
|
43
36
|
page;
|
|
44
37
|
startUrl;
|
|
45
38
|
sessionId;
|
|
39
|
+
knownInteractionSelectors;
|
|
40
|
+
headless;
|
|
46
41
|
here = null;
|
|
47
42
|
lastRunNote = null;
|
|
48
43
|
trace = [];
|
|
49
44
|
consoleLog = [];
|
|
50
|
-
knownInteractionSelectors;
|
|
51
|
-
headless;
|
|
52
45
|
lastOverlayInfo = null;
|
|
53
46
|
constructor(projectDir, inject, blocksSelect, graph, library, mem, engine, closeBrowser, context, page, startUrl, sessionId, knownInteractionSelectors, headless) {
|
|
54
47
|
this.projectDir = projectDir;
|
|
@@ -118,7 +111,7 @@ export class AutoSession {
|
|
|
118
111
|
// watching sees the state an API caller just got back, not a frame behind.
|
|
119
112
|
this.lastOverlayInfo = {
|
|
120
113
|
sessionId: this.sessionId,
|
|
121
|
-
snapshot:
|
|
114
|
+
snapshot: await this.snapshotOf(menu),
|
|
122
115
|
graph: this.graph,
|
|
123
116
|
};
|
|
124
117
|
await updatePilotOverlay(this.page, this.lastOverlayInfo);
|
|
@@ -163,10 +156,20 @@ export class AutoSession {
|
|
|
163
156
|
};
|
|
164
157
|
await this.page.evaluate(unmappedInteractionsPageScript, payload).catch(ignorePageGone);
|
|
165
158
|
}
|
|
159
|
+
/** The Checkpoint the session is at, if known. */
|
|
160
|
+
get where() { return this.here; }
|
|
166
161
|
/** Pure getter - no Block runs, no mem/page mutation. */
|
|
167
162
|
async currentSnapshot() {
|
|
168
|
-
|
|
169
|
-
|
|
163
|
+
return this.snapshotOf(await this.currentMenu());
|
|
164
|
+
}
|
|
165
|
+
/** The menu as a snapshot, with what the page shows (Read Blocks) when any covers it. */
|
|
166
|
+
async snapshotOf(menu) {
|
|
167
|
+
return { ...buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote), ...(await snapshotRead(this.page, this.mem, this.here)) };
|
|
168
|
+
}
|
|
169
|
+
/** Pure getter - what the page shows, from the project's Read Blocks (beta). */
|
|
170
|
+
async read(path) {
|
|
171
|
+
this.page = await ensureLivePage(this.context, this.page, this.startUrl);
|
|
172
|
+
return readSession(this.page, this.mem, this.here, path);
|
|
170
173
|
}
|
|
171
174
|
/**
|
|
172
175
|
* Pure getter - reads the live page's structure at the requested fidelity.
|
|
@@ -238,7 +241,7 @@ export class AutoSession {
|
|
|
238
241
|
const nextMenu = await this.currentMenu();
|
|
239
242
|
return {
|
|
240
243
|
ok: true,
|
|
241
|
-
snapshot:
|
|
244
|
+
snapshot: await this.snapshotOf(nextMenu),
|
|
242
245
|
quit: false,
|
|
243
246
|
};
|
|
244
247
|
}
|
|
@@ -251,7 +254,7 @@ export class AutoSession {
|
|
|
251
254
|
if (pick.type === "quit") {
|
|
252
255
|
return {
|
|
253
256
|
ok: true,
|
|
254
|
-
snapshot:
|
|
257
|
+
snapshot: await this.snapshotOf(menu),
|
|
255
258
|
quit: true,
|
|
256
259
|
};
|
|
257
260
|
}
|
|
@@ -292,7 +295,7 @@ export class AutoSession {
|
|
|
292
295
|
this.lastRunNote = `resync -> ${this.here ?? "Unknown"}`;
|
|
293
296
|
return {
|
|
294
297
|
ok: true,
|
|
295
|
-
snapshot:
|
|
298
|
+
snapshot: await this.snapshotOf(menu),
|
|
296
299
|
quit: false,
|
|
297
300
|
};
|
|
298
301
|
}
|
|
@@ -363,7 +366,7 @@ export class AutoSession {
|
|
|
363
366
|
this.lastRunNote = `${what} -> ${this.here ?? "Unknown"}`;
|
|
364
367
|
return {
|
|
365
368
|
ok: true,
|
|
366
|
-
snapshot:
|
|
369
|
+
snapshot: await this.snapshotOf(menu),
|
|
367
370
|
quit: false,
|
|
368
371
|
};
|
|
369
372
|
}
|
|
@@ -390,7 +393,7 @@ export class AutoSession {
|
|
|
390
393
|
ok: true,
|
|
391
394
|
painted,
|
|
392
395
|
missing,
|
|
393
|
-
snapshot:
|
|
396
|
+
snapshot: await this.snapshotOf(menu),
|
|
394
397
|
};
|
|
395
398
|
}
|
|
396
399
|
catch (err) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Socket } from "node:net";
|
|
2
|
+
import type { SessionStreamMessage } from "./protocol.js";
|
|
3
|
+
/** The server side: subscribed sockets, fed every run event in this process until `close`. */
|
|
4
|
+
export declare function createFeed(): {
|
|
5
|
+
add(sock: Socket): void;
|
|
6
|
+
send(message: SessionStreamMessage): void;
|
|
7
|
+
close(): void;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The client side: subscribes to a running session and calls `onMessage` for each line until `close()` (or the session
|
|
11
|
+
* ends). Rejects when the session is unknown or does not answer the subscribe.
|
|
12
|
+
*/
|
|
13
|
+
export declare function subscribeSession(projectDir: string, sessionId: string, onMessage: (message: SessionStreamMessage) => void, timeoutMs?: number): Promise<{
|
|
14
|
+
close(): void;
|
|
15
|
+
closed: Promise<void>;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// A session's live feed (`subscribe`): every run event in the session (steps, reads) and the snapshot after each change,
|
|
2
|
+
// as JSON lines to every subscribed socket. Waygraph UI watches a session through it; `pilot watch <id>` prints it.
|
|
3
|
+
import { connect } from "node:net";
|
|
4
|
+
import { onRunEvent } from "../../engine/run/events.js";
|
|
5
|
+
import { resolveSessionMeta } from "./files.js";
|
|
6
|
+
import { SESSION_PROTOCOL_VERSION } from "./protocol.js";
|
|
7
|
+
/** The server side: subscribed sockets, fed every run event in this process until `close`. */
|
|
8
|
+
export function createFeed() {
|
|
9
|
+
const subscribers = new Set();
|
|
10
|
+
const send = (message) => {
|
|
11
|
+
const line = JSON.stringify(message) + "\n";
|
|
12
|
+
for (const sock of subscribers)
|
|
13
|
+
if (!sock.destroyed)
|
|
14
|
+
sock.write(line);
|
|
15
|
+
};
|
|
16
|
+
const off = onRunEvent((event) => send({ type: "event", event }));
|
|
17
|
+
return {
|
|
18
|
+
add(sock) {
|
|
19
|
+
subscribers.add(sock);
|
|
20
|
+
sock.on("close", () => subscribers.delete(sock));
|
|
21
|
+
sock.write(JSON.stringify({ ok: true, protocol: SESSION_PROTOCOL_VERSION }) + "\n");
|
|
22
|
+
},
|
|
23
|
+
send,
|
|
24
|
+
close() {
|
|
25
|
+
off();
|
|
26
|
+
for (const sock of subscribers)
|
|
27
|
+
sock.end();
|
|
28
|
+
subscribers.clear();
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The client side: subscribes to a running session and calls `onMessage` for each line until `close()` (or the session
|
|
34
|
+
* ends). Rejects when the session is unknown or does not answer the subscribe.
|
|
35
|
+
*/
|
|
36
|
+
export function subscribeSession(projectDir, sessionId, onMessage, timeoutMs = 15_000) {
|
|
37
|
+
const meta = resolveSessionMeta(sessionId, projectDir);
|
|
38
|
+
if (!meta)
|
|
39
|
+
return Promise.reject(new Error(`no such session "${sessionId}" - \`waygraph pilot sessions\` lists live ids`));
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const sock = connect(meta.socketPath);
|
|
42
|
+
const timer = setTimeout(() => {
|
|
43
|
+
sock.destroy();
|
|
44
|
+
reject(new Error(`session "${sessionId}" did not answer subscribe within ${timeoutMs}ms`));
|
|
45
|
+
}, timeoutMs);
|
|
46
|
+
const closed = new Promise((done) => sock.on("close", () => done()));
|
|
47
|
+
let buf = "";
|
|
48
|
+
let acked = false;
|
|
49
|
+
sock.on("connect", () => sock.write(JSON.stringify({ op: "subscribe" }) + "\n"));
|
|
50
|
+
sock.on("error", (err) => {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
if (!acked)
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
sock.on("data", (chunk) => {
|
|
56
|
+
buf += chunk.toString("utf-8");
|
|
57
|
+
let nl;
|
|
58
|
+
while ((nl = buf.indexOf("\n")) !== -1) {
|
|
59
|
+
const line = buf.slice(0, nl);
|
|
60
|
+
buf = buf.slice(nl + 1);
|
|
61
|
+
if (!acked) {
|
|
62
|
+
acked = true;
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
resolve({ close: () => sock.end(), closed });
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
onMessage(JSON.parse(line));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Locator, Page } from "@playwright/test";
|
|
1
2
|
import type { PilotHighlightFixtures } from "../../demo/overlay/pilot/fixtures.js";
|
|
2
3
|
import type { StubPhaseResult } from "../../engine/stub/stub.js";
|
|
3
4
|
import type { BlockEntry } from "../../manifest/project.js";
|
|
@@ -27,6 +28,10 @@ export interface SessionSnapshot {
|
|
|
27
28
|
/** True when there are no runnable moves from `here` right now. */
|
|
28
29
|
done: boolean;
|
|
29
30
|
lastRunNote: string | null;
|
|
31
|
+
/** What the page shows, from the project's Read Blocks (beta); absent when none covers this page. */
|
|
32
|
+
read?: Record<string, unknown>;
|
|
33
|
+
missing?: string[];
|
|
34
|
+
readNotes?: Record<string, string>;
|
|
30
35
|
}
|
|
31
36
|
/** Plain, JSON-serializable view of a menu - no Block/MemKey/class references. */
|
|
32
37
|
export declare function buildSessionSnapshot(menu: ExploreMenu, library: Map<string, BlockEntry>, here: string | null, lastRunNote: string | null): SessionSnapshot;
|
|
@@ -214,4 +219,6 @@ export interface StorageSnapshot {
|
|
|
214
219
|
}
|
|
215
220
|
/** What a highlight call paints, in a few words, for the session's activity line. */
|
|
216
221
|
export declare function describeHighlight(fixtures: PilotHighlightFixtures): string;
|
|
222
|
+
/** The first element `selector` matches, ringed with the pilot's vision ring; or why there is none. */
|
|
223
|
+
export declare function rawTarget(page: Page, selector: string, label: string): Promise<Locator | string>;
|
|
217
224
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// The session's shared shapes and pure helpers: snapshots, picks, trace / console entries, DOM walking, stub files.
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { showPilotVision } from "../../demo/overlay/pilot/shell.js";
|
|
4
5
|
/** True when a stub-phase result actually carries authored content worth keeping. */
|
|
5
6
|
export function stubPhaseHasContent(r) {
|
|
6
7
|
return (Object.keys(r.highlights).length > 0 ||
|
|
@@ -163,3 +164,11 @@ export function describeHighlight(fixtures) {
|
|
|
163
164
|
.filter(Boolean)
|
|
164
165
|
.join(", ") || "fixtures");
|
|
165
166
|
}
|
|
167
|
+
/** The first element `selector` matches, ringed with the pilot's vision ring; or why there is none. */
|
|
168
|
+
export async function rawTarget(page, selector, label) {
|
|
169
|
+
const locator = page.locator(selector).first();
|
|
170
|
+
if ((await locator.count()) === 0)
|
|
171
|
+
return `no element matches selector "${selector}"`;
|
|
172
|
+
await showPilotVision(page, selector, label);
|
|
173
|
+
return locator;
|
|
174
|
+
}
|
|
@@ -1,52 +1,8 @@
|
|
|
1
1
|
import type { PilotHighlightFixtures } from "../../demo/overlay/pilot/fixtures.js";
|
|
2
2
|
import { AutoSession } from "./auto-session.js";
|
|
3
3
|
import { SessionMeta } from "./files.js";
|
|
4
|
-
import { type
|
|
5
|
-
|
|
6
|
-
ok: true;
|
|
7
|
-
snapshot: SessionSnapshot;
|
|
8
|
-
quit: boolean;
|
|
9
|
-
} | {
|
|
10
|
-
ok: false;
|
|
11
|
-
error: string;
|
|
12
|
-
};
|
|
13
|
-
export type DomResponse = {
|
|
14
|
-
ok: true;
|
|
15
|
-
snapshot: DomSnapshot;
|
|
16
|
-
} | {
|
|
17
|
-
ok: false;
|
|
18
|
-
error: string;
|
|
19
|
-
};
|
|
20
|
-
export type TraceResponse = {
|
|
21
|
-
ok: true;
|
|
22
|
-
trace: TraceStep[];
|
|
23
|
-
} | {
|
|
24
|
-
ok: false;
|
|
25
|
-
error: string;
|
|
26
|
-
};
|
|
27
|
-
export type ConsoleResponse = {
|
|
28
|
-
ok: true;
|
|
29
|
-
consoleLog: ConsoleLogEntry[];
|
|
30
|
-
} | {
|
|
31
|
-
ok: false;
|
|
32
|
-
error: string;
|
|
33
|
-
};
|
|
34
|
-
export type StorageResponse = ({
|
|
35
|
-
ok: true;
|
|
36
|
-
} & StorageSnapshot) | {
|
|
37
|
-
ok: false;
|
|
38
|
-
error: string;
|
|
39
|
-
};
|
|
40
|
-
export type ReachResponse = ApplyPathResult;
|
|
41
|
-
export type HighlightResponse = {
|
|
42
|
-
ok: true;
|
|
43
|
-
painted: number;
|
|
44
|
-
missing: string[];
|
|
45
|
-
snapshot: SessionSnapshot;
|
|
46
|
-
} | {
|
|
47
|
-
ok: false;
|
|
48
|
-
error: string;
|
|
49
|
-
};
|
|
4
|
+
import { type AutoSessionInit, type InspectDomOptions, type StubFileKind } from "./helpers.js";
|
|
5
|
+
import type { HelloResponse, ConsoleResponse, DomResponse, HighlightResponse, ReachResponse, ReadResponse, StatusOrSendResponse, StorageResponse, TraceResponse } from "./protocol.js";
|
|
50
6
|
/**
|
|
51
7
|
* Client-side: sends one request to a running session and returns its
|
|
52
8
|
* response. Reports a stale/unreachable session as an error within
|
|
@@ -94,6 +50,16 @@ export declare function requestSession(projectDir: string, sessionId: string, re
|
|
|
94
50
|
export declare function requestSession(projectDir: string, sessionId: string, request: {
|
|
95
51
|
op: "storage";
|
|
96
52
|
}, timeoutMs?: number): Promise<StorageResponse>;
|
|
53
|
+
export declare function requestSession(projectDir: string, sessionId: string, request: {
|
|
54
|
+
op: "hello";
|
|
55
|
+
}, timeoutMs?: number): Promise<HelloResponse | {
|
|
56
|
+
ok: false;
|
|
57
|
+
error: string;
|
|
58
|
+
}>;
|
|
59
|
+
export declare function requestSession(projectDir: string, sessionId: string, request: {
|
|
60
|
+
op: "read";
|
|
61
|
+
path?: string;
|
|
62
|
+
}, timeoutMs?: number): Promise<ReadResponse>;
|
|
97
63
|
export declare function requestSession(projectDir: string, sessionId: string, request: {
|
|
98
64
|
op: "reach";
|
|
99
65
|
checkpoint: string;
|
|
@@ -110,7 +76,9 @@ export declare function requestSession(projectDir: string, sessionId: string, re
|
|
|
110
76
|
* already prevents true parallelism, but two overlapping `await`-heavy
|
|
111
77
|
* handlers could still interleave against the same page/mem.
|
|
112
78
|
*/
|
|
113
|
-
export declare function serveSession(session: AutoSession, projectDir: string, sessionId: string, headless?: boolean
|
|
79
|
+
export declare function serveSession(session: AutoSession, projectDir: string, sessionId: string, headless?: boolean,
|
|
80
|
+
/** After a quit closed the session and removed its files: the session process exits here. */
|
|
81
|
+
onShutdown?: () => void): Promise<void>;
|
|
114
82
|
/** Entry point for the hidden `__auto-serve` CLI command (runs in the detached child). */
|
|
115
83
|
export declare function runAutoServeCommand(init: AutoSessionInit, sessionId: string): Promise<void>;
|
|
116
84
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* See openspec/changes/waygraph-auto-cli-session-control.
|
|
6
6
|
*/
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
|
-
import { mkdirSync, openSync, rmSync, writeFileSync, } from "node:fs";
|
|
8
|
+
import { mkdirSync, openSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
9
9
|
import { createRequire } from "node:module";
|
|
10
10
|
import { connect, createServer } from "node:net";
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
@@ -15,6 +15,8 @@ import { fileURLToPath } from "node:url";
|
|
|
15
15
|
import { ignorePageGone } from "../../engine/page-gone.js";
|
|
16
16
|
import { AutoSession } from "./auto-session.js";
|
|
17
17
|
import { generateSessionId, isPidAlive, logPathFor, metaPath, readSessionMeta, removeSessionFiles, resolveSessionMeta, sessionDir, socketDir, socketPathFor, writeGlobalSessionMeta } from "./files.js";
|
|
18
|
+
import { createFeed } from "./feed.js";
|
|
19
|
+
import { SESSION_PROTOCOL_VERSION } from "./protocol.js";
|
|
18
20
|
/** Reads newline-delimited JSON off a socket, one message at a time. */
|
|
19
21
|
function readOneMessage(sock, timeoutMs) {
|
|
20
22
|
return new Promise((resolvePromise, reject) => {
|
|
@@ -109,7 +111,9 @@ export async function requestSession(projectDir, sessionId, request, timeoutMs =
|
|
|
109
111
|
* already prevents true parallelism, but two overlapping `await`-heavy
|
|
110
112
|
* handlers could still interleave against the same page/mem.
|
|
111
113
|
*/
|
|
112
|
-
export async function serveSession(session, projectDir, sessionId, headless = true
|
|
114
|
+
export async function serveSession(session, projectDir, sessionId, headless = true,
|
|
115
|
+
/** After a quit closed the session and removed its files: the session process exits here. */
|
|
116
|
+
onShutdown = () => { }) {
|
|
113
117
|
mkdirSync(sessionDir(projectDir), { recursive: true });
|
|
114
118
|
mkdirSync(socketDir(), { recursive: true });
|
|
115
119
|
const socketPath = socketPathFor(sessionId);
|
|
@@ -120,6 +124,7 @@ export async function serveSession(session, projectDir, sessionId, headless = tr
|
|
|
120
124
|
queue = next.catch(() => { });
|
|
121
125
|
return next;
|
|
122
126
|
};
|
|
127
|
+
const feed = createFeed();
|
|
123
128
|
const server = createServer((sock) => {
|
|
124
129
|
let buf = "";
|
|
125
130
|
sock.on("data", (chunk) => {
|
|
@@ -141,6 +146,15 @@ export async function serveSession(session, projectDir, sessionId, headless = tr
|
|
|
141
146
|
sock.write(JSON.stringify({ ok: false, error: "malformed request (invalid JSON)" }) + "\n");
|
|
142
147
|
return;
|
|
143
148
|
}
|
|
149
|
+
// Neither waits behind a running request: hello names the protocol, subscribe turns this socket into a live feed.
|
|
150
|
+
if (request.op === "hello") {
|
|
151
|
+
sock.write(JSON.stringify({ ok: true, protocol: SESSION_PROTOCOL_VERSION, waygraph: waygraphVersion(), sessionId, here: session.where }) + "\n");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (request.op === "subscribe") {
|
|
155
|
+
feed.add(sock);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
144
158
|
await enqueue(async () => {
|
|
145
159
|
let response;
|
|
146
160
|
let shouldQuit = false;
|
|
@@ -165,6 +179,9 @@ export async function serveSession(session, projectDir, sessionId, headless = tr
|
|
|
165
179
|
else if (request.op === "console") {
|
|
166
180
|
response = { ok: true, consoleLog: session.getConsoleLog() };
|
|
167
181
|
}
|
|
182
|
+
else if (request.op === "read") {
|
|
183
|
+
response = { ok: true, ...(await session.read(request.path)) };
|
|
184
|
+
}
|
|
168
185
|
else if (request.op === "storage") {
|
|
169
186
|
response = { ok: true, ...(await session.inspectStorage()) };
|
|
170
187
|
}
|
|
@@ -213,6 +230,9 @@ export async function serveSession(session, projectDir, sessionId, headless = tr
|
|
|
213
230
|
response = { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
214
231
|
}
|
|
215
232
|
sock.write(JSON.stringify(response) + "\n");
|
|
233
|
+
// Whatever changed (a step ran, a reload, a raw action): subscribers get the new snapshot.
|
|
234
|
+
if (response.ok && "snapshot" in response && "sections" in response.snapshot)
|
|
235
|
+
feed.send({ type: "snapshot", snapshot: response.snapshot });
|
|
216
236
|
if (shouldQuit) {
|
|
217
237
|
setImmediate(() => void shutdown());
|
|
218
238
|
}
|
|
@@ -224,9 +244,10 @@ export async function serveSession(session, projectDir, sessionId, headless = tr
|
|
|
224
244
|
return;
|
|
225
245
|
shuttingDown = true;
|
|
226
246
|
server.close();
|
|
247
|
+
feed.close();
|
|
227
248
|
await session.close().catch(ignorePageGone);
|
|
228
249
|
removeSessionFiles(projectDir, sessionId);
|
|
229
|
-
|
|
250
|
+
onShutdown();
|
|
230
251
|
}
|
|
231
252
|
await new Promise((resolvePromise, reject) => {
|
|
232
253
|
server.on("error", reject);
|
|
@@ -248,7 +269,7 @@ export async function serveSession(session, projectDir, sessionId, headless = tr
|
|
|
248
269
|
/** Entry point for the hidden `__auto-serve` CLI command (runs in the detached child). */
|
|
249
270
|
export async function runAutoServeCommand(init, sessionId) {
|
|
250
271
|
const session = await AutoSession.start({ ...init, sessionId });
|
|
251
|
-
await serveSession(session, init.projectDir, sessionId, init.headless ?? true);
|
|
272
|
+
await serveSession(session, init.projectDir, sessionId, init.headless ?? true, () => process.exit(0));
|
|
252
273
|
// Tell the parent that spawned us (spawnDetachedSession) we are serving, then let it go.
|
|
253
274
|
process.send?.({ ready: true });
|
|
254
275
|
process.disconnect?.();
|
|
@@ -372,3 +393,12 @@ export async function runAttachLoop(projectDir, sessionId) {
|
|
|
372
393
|
rl.close();
|
|
373
394
|
}
|
|
374
395
|
}
|
|
396
|
+
/** This package's version, for `hello` (the file sits three levels below the package root in src/ and dist/). */
|
|
397
|
+
function waygraphVersion() {
|
|
398
|
+
try {
|
|
399
|
+
return JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "package.json"), "utf8")).version;
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
return "unknown";
|
|
403
|
+
}
|
|
404
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { PilotHighlightFixtures } from "../../demo/overlay/pilot/fixtures.js";
|
|
2
|
+
import type { RunEvent } from "../../engine/run/events.js";
|
|
3
|
+
import type { ApplyPathResult, ConsoleLogEntry, DomSnapshot, InspectDomOptions, SessionSnapshot, StorageSnapshot, StubFileKind, TraceStep } from "./helpers.js";
|
|
4
|
+
import type { SessionRead } from "./read.js";
|
|
5
|
+
/** Bumped when a request or response changes shape (adding an op or an optional field does not). */
|
|
6
|
+
export declare const SESSION_PROTOCOL_VERSION = 1;
|
|
7
|
+
/** Every request a session answers. */
|
|
8
|
+
export type SessionRequest = {
|
|
9
|
+
op: "hello";
|
|
10
|
+
} | {
|
|
11
|
+
op: "subscribe";
|
|
12
|
+
} | {
|
|
13
|
+
op: "status";
|
|
14
|
+
} | {
|
|
15
|
+
op: "send";
|
|
16
|
+
pick: string;
|
|
17
|
+
} | ({
|
|
18
|
+
op: "dom";
|
|
19
|
+
} & InspectDomOptions) | {
|
|
20
|
+
op: "trace";
|
|
21
|
+
} | {
|
|
22
|
+
op: "click";
|
|
23
|
+
selector: string;
|
|
24
|
+
} | {
|
|
25
|
+
op: "type";
|
|
26
|
+
selector: string;
|
|
27
|
+
text: string;
|
|
28
|
+
} | {
|
|
29
|
+
op: "press";
|
|
30
|
+
selector: string;
|
|
31
|
+
key: string;
|
|
32
|
+
} | {
|
|
33
|
+
op: "goto";
|
|
34
|
+
url: string;
|
|
35
|
+
} | {
|
|
36
|
+
op: "reload";
|
|
37
|
+
} | {
|
|
38
|
+
op: "reach";
|
|
39
|
+
checkpoint: string;
|
|
40
|
+
} | {
|
|
41
|
+
op: "resync";
|
|
42
|
+
} | {
|
|
43
|
+
op: "console";
|
|
44
|
+
} | {
|
|
45
|
+
op: "storage";
|
|
46
|
+
} | {
|
|
47
|
+
op: "read";
|
|
48
|
+
path?: string;
|
|
49
|
+
} | {
|
|
50
|
+
op: "upload";
|
|
51
|
+
selector: string;
|
|
52
|
+
stub: StubFileKind | {
|
|
53
|
+
filePath: string;
|
|
54
|
+
};
|
|
55
|
+
} | ({
|
|
56
|
+
op: "highlight";
|
|
57
|
+
} & PilotHighlightFixtures);
|
|
58
|
+
export type StatusOrSendResponse = {
|
|
59
|
+
ok: true;
|
|
60
|
+
snapshot: SessionSnapshot;
|
|
61
|
+
quit: boolean;
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
error: string;
|
|
65
|
+
};
|
|
66
|
+
export type DomResponse = {
|
|
67
|
+
ok: true;
|
|
68
|
+
snapshot: DomSnapshot;
|
|
69
|
+
} | {
|
|
70
|
+
ok: false;
|
|
71
|
+
error: string;
|
|
72
|
+
};
|
|
73
|
+
export type TraceResponse = {
|
|
74
|
+
ok: true;
|
|
75
|
+
trace: TraceStep[];
|
|
76
|
+
} | {
|
|
77
|
+
ok: false;
|
|
78
|
+
error: string;
|
|
79
|
+
};
|
|
80
|
+
export type ConsoleResponse = {
|
|
81
|
+
ok: true;
|
|
82
|
+
consoleLog: ConsoleLogEntry[];
|
|
83
|
+
} | {
|
|
84
|
+
ok: false;
|
|
85
|
+
error: string;
|
|
86
|
+
};
|
|
87
|
+
export type StorageResponse = ({
|
|
88
|
+
ok: true;
|
|
89
|
+
} & StorageSnapshot) | {
|
|
90
|
+
ok: false;
|
|
91
|
+
error: string;
|
|
92
|
+
};
|
|
93
|
+
export type ReachResponse = ApplyPathResult;
|
|
94
|
+
export type ReadResponse = ({
|
|
95
|
+
ok: true;
|
|
96
|
+
} & SessionRead) | {
|
|
97
|
+
ok: false;
|
|
98
|
+
error: string;
|
|
99
|
+
};
|
|
100
|
+
export type HighlightResponse = {
|
|
101
|
+
ok: true;
|
|
102
|
+
painted: number;
|
|
103
|
+
missing: string[];
|
|
104
|
+
snapshot: SessionSnapshot;
|
|
105
|
+
} | {
|
|
106
|
+
ok: false;
|
|
107
|
+
error: string;
|
|
108
|
+
};
|
|
109
|
+
/** What `hello` answers: the protocol a client is talking to, and where the session is (`null` until a status or step has located it). */
|
|
110
|
+
export type HelloResponse = {
|
|
111
|
+
ok: true;
|
|
112
|
+
protocol: number;
|
|
113
|
+
waygraph: string;
|
|
114
|
+
sessionId: string;
|
|
115
|
+
here: string | null;
|
|
116
|
+
};
|
|
117
|
+
/** One line of a `subscribe` feed: every run event in the session (steps, reads), and the snapshot after each change. */
|
|
118
|
+
export type SessionStreamMessage = {
|
|
119
|
+
type: "event";
|
|
120
|
+
event: RunEvent;
|
|
121
|
+
} | {
|
|
122
|
+
type: "snapshot";
|
|
123
|
+
snapshot: SessionSnapshot;
|
|
124
|
+
};
|
|
125
|
+
export type SessionResponse = HelloResponse | StatusOrSendResponse | DomResponse | TraceResponse | ConsoleResponse | StorageResponse | ReachResponse | ReadResponse | HighlightResponse;
|
|
126
|
+
/** Every op, once: the server, the CLI and `pilot` all check against this list. */
|
|
127
|
+
export declare const SESSION_OPS: readonly ["hello", "subscribe", "status", "send", "dom", "trace", "console", "storage", "read", "upload", "click", "type", "press", "goto", "reload", "reach", "resync", "highlight"];
|