waygraph 0.15.51 → 0.15.52
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/analysis/graph.d.ts +1 -1
- package/dist/analysis/graph.js +10 -15
- package/dist/analysis/practices-check.d.ts +0 -1
- package/dist/analysis/practices-check.js +0 -13
- package/dist/auto-session/session.d.ts +1 -1
- package/dist/auto-session/session.js +13 -8
- package/dist/blocks-select.d.ts +7 -4
- package/dist/blocks-select.js +0 -14
- package/dist/browser.d.ts +27 -0
- package/dist/browser.js +62 -0
- package/dist/cli/run-chain.js +4 -0
- package/dist/cli/usage.js +14 -1
- package/dist/cli.js +10 -1
- package/dist/commands/agent-dive-scaffold.d.ts +5 -0
- package/dist/commands/agent-dive-scaffold.js +7 -0
- package/dist/commands/extensions.d.ts +1 -0
- package/dist/commands/extensions.js +22 -0
- package/dist/commands/init.js +42 -4
- package/dist/commands/test.js +16 -2
- package/dist/commands/try.js +2 -1
- package/dist/commands/upgrade.d.ts +6 -0
- package/dist/commands/upgrade.js +107 -0
- package/dist/config.d.ts +47 -3
- package/dist/config.js +45 -12
- package/dist/core/block.d.ts +12 -0
- package/dist/core/block.js +36 -0
- package/dist/core/blocks/nav.d.ts +11 -0
- package/dist/core/blocks/nav.js +8 -0
- package/dist/core/config.d.ts +5 -0
- package/dist/core/engine.d.ts +17 -16
- package/dist/core/engine.js +3 -3
- package/dist/core/flow.d.ts +17 -0
- package/dist/core/flow.js +44 -21
- package/dist/core/map-builder.d.ts +3 -3
- package/dist/core/map-builder.js +3 -3
- package/dist/env-file.d.ts +7 -7
- package/dist/env-file.js +29 -16
- package/dist/extensions.d.ts +21 -0
- package/dist/extensions.js +178 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +3 -2
- package/dist/pilot-overlay/overlay-fixtures.d.ts +0 -7
- package/dist/pilot-overlay/overlay-fixtures.js +0 -45
- package/dist/playwright-config.d.ts +4 -0
- package/dist/playwright-config.js +15 -0
- package/dist/playwright.config.d.ts +2 -0
- package/dist/playwright.config.js +6 -0
- package/dist/runner/demo-log.js +1 -7
- package/dist/runner/inpage/core.d.ts +1 -1
- package/dist/runner/inpage/core.js +8 -7
- package/dist/runner/main.js +18 -10
- package/dist/runner/rings.js +0 -49
- package/dist/session/auto-explore-run.js +9 -5
- package/dist/session/auto-explore.js +6 -4
- package/dist/session/step-overlay.js +3 -1
- package/dist/traverse/run.js +3 -1
- package/dist/ui/components/ring.js +1 -0
- package/dist/ui/overlay.css +1 -0
- package/dist/ui/shadow.d.ts +1 -3
- package/dist/ui/shadow.js +0 -4
- package/dist/ui/tokens.d.ts +0 -11
- package/dist/ui/tokens.js +0 -5
- package/dist/video-dir.d.ts +8 -0
- package/dist/video-dir.js +60 -0
- package/dist/webserver.js +5 -1
- package/package.json +1 -1
- package/templates/scaffold/README.md +4 -2
- package/templates/scaffold/STRUCTURE.md +5 -2
- package/templates/scaffold/config/waygraph.config.ts +26 -0
- package/templates/scaffold/package.json +1 -1
- package/templates/scaffold/src/flows/README.md +5 -5
- package/templates/scaffold/src/flows/example.flow.ts +31 -17
- package/templates/scaffold/src/flows/hoppscotch-auth.flow.ts +35 -0
- package/templates/scaffold/src/flows/hoppscotch-get.flow.ts +23 -0
- package/templates/scaffold/src/flows/hoppscotch-post.flow.ts +33 -0
- package/templates/scaffold/src/flows/mail-verify.flow.ts +36 -75
- package/templates/scaffold/src/flows/shop.flow.ts +41 -65
- package/templates/scaffold/src/map/(app_base)/docs/_nav.block.ts +0 -9
- package/templates/scaffold/src/map/(app_base)/docs/_page.block.ts +0 -5
- package/templates/scaffold/src/map/(app_base)/docs/_sel.ts +1 -1
- package/templates/scaffold/src/map/(app_base)/home/_methods/add-item.effect.block.ts +3 -19
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +2 -25
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-item-added.method.block.ts +2 -19
- package/templates/scaffold/src/map/(app_base)/home/_methods/clear-cart.method.block.ts +1 -17
- package/templates/scaffold/src/map/(app_base)/home/_methods/remove-item.effect.block.ts +1 -10
- package/templates/scaffold/src/map/(app_base)/home/_nav.block.ts +1 -12
- package/templates/scaffold/src/map/(app_base)/home/_page.block.ts +0 -9
- package/templates/scaffold/src/map/(app_base)/home/_sel.ts +3 -1
- package/templates/scaffold/src/map/(app_base)/home/nav-verification-link.block.ts +1 -23
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-response-body.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-response-header.method.block.ts +19 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-status.method.block.ts +20 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/pick-extension-interceptor.method.block.ts +18 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/read-response-body.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/read-response-headers.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/send-request.method.block.ts +15 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-bearer-token.method.block.ts +20 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-headers.method.block.ts +23 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-json-body.method.block.ts +36 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-method.method.block.ts +17 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-query-params.method.block.ts +22 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-url.method.block.ts +18 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_nav.block.ts +11 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_page.block.ts +36 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_sel.ts +27 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/settings/_nav.block.ts +10 -0
- package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-content.method.block.ts +2 -27
- package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-received.method.block.ts +2 -21
- package/templates/scaffold/src/map/(external)/mailpit/_methods/extract-email-link.method.block.ts +1 -19
- package/templates/scaffold/src/map/(external)/mailpit/_methods/open-message.method.block.ts +1 -11
- package/templates/scaffold/src/map/(external)/mailpit/_nav.block.ts +1 -9
- package/templates/scaffold/src/map/(external)/mailpit/_page.block.ts +0 -11
- package/templates/scaffold/src/states/demo.mem-keys.ts +20 -19
- package/templates/scaffold/src/states/demo.mem-stubs.ts +4 -0
- package/templates/scaffold/src/states/demo.states.ts +5 -1
- package/templates/scaffold/tests/example.spec.ts +2 -3
- package/templates/scaffold/tests/hoppscotch.spec.ts +97 -0
- package/templates/scaffold/tests/mail-verify.spec.ts +2 -11
- package/templates/scaffold/tsconfig.json +1 -1
- package/templates/skills/waygraph-convention.skill.md +32 -3
- package/templates/skills/waygraph-pilot.skill.md +4 -5
- package/dist/browser-path.d.ts +0 -5
- package/dist/browser-path.js +0 -18
- package/dist/runner/overlay-present.d.ts +0 -8
- package/dist/runner/overlay-present.js +0 -57
- package/dist/ui/components/banner.d.ts +0 -2
- package/dist/ui/components/banner.js +0 -10
- package/dist/ui/components/cursor.d.ts +0 -2
- package/dist/ui/components/cursor.js +0 -19
- package/templates/scaffold/playwright.config.ts +0 -12
package/dist/analysis/graph.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface WaygraphEdge {
|
|
|
13
13
|
block: string;
|
|
14
14
|
/** Path the Block was discovered in, relative to the project root. */
|
|
15
15
|
file: string;
|
|
16
|
-
/** `"*"` for a NavBlock - reachable from anywhere,
|
|
16
|
+
/** `"*"` for a NavBlock - reachable from anywhere, unless it declares `from` (then one edge per tag). */
|
|
17
17
|
from: string;
|
|
18
18
|
to: string;
|
|
19
19
|
kind: "nav" | "action";
|
package/dist/analysis/graph.js
CHANGED
|
@@ -53,21 +53,10 @@ function discoverBlocks(projectDir) {
|
|
|
53
53
|
function discoverFlows(projectDir) {
|
|
54
54
|
return walkDir(projectDir, /\.flow\.ts$/);
|
|
55
55
|
}
|
|
56
|
-
/** Block export ids
|
|
56
|
+
/** Block export ids a flow file uses: every identifier outside its import lines (a Block can sit in the array or in a local like `const AddShown = AddBlock.stubAfter(...)`). */
|
|
57
57
|
function extractFlowBlockRefs(flowSrc) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
let m;
|
|
61
|
-
while ((m = re.exec(flowSrc)) !== null) {
|
|
62
|
-
for (const part of m[1].split(",")) {
|
|
63
|
-
const trimmed = part.trim().replace(/\/\/.*$/, "").trim();
|
|
64
|
-
const id = trimmed.split(/\s+/)[0];
|
|
65
|
-
if (!id || id === "start" || id === "end")
|
|
66
|
-
continue;
|
|
67
|
-
refs.push(id);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return refs;
|
|
58
|
+
const body = flowSrc.replace(/^\s*import[\s\S]*?;\s*$/gm, "").replace(/\/\/.*$/gm, "");
|
|
59
|
+
return body.match(/[A-Za-z_$][\w$]*/g) ?? [];
|
|
71
60
|
}
|
|
72
61
|
function exportNameToBlockName(exportName) {
|
|
73
62
|
const base = exportName.replace(/Block$/, "");
|
|
@@ -420,7 +409,13 @@ export async function discoverGraph(projectDir, opts) {
|
|
|
420
409
|
const deepLink = !isPage ||
|
|
421
410
|
exported.__waygraphPageDeepLink === true;
|
|
422
411
|
if (deepLink) {
|
|
423
|
-
|
|
412
|
+
// `from: [...]` on the Nav = only those Checkpoints; otherwise reachable from anywhere.
|
|
413
|
+
const navFrom = exported.__waygraphNavFrom;
|
|
414
|
+
for (const from of navFrom ?? ["*"]) {
|
|
415
|
+
if (from !== "*")
|
|
416
|
+
nodeTags.add(from);
|
|
417
|
+
edges.push({ block: blockName, file: relFile, from, to, kind: "nav" });
|
|
418
|
+
}
|
|
424
419
|
}
|
|
425
420
|
}
|
|
426
421
|
catch {
|
|
@@ -11,4 +11,3 @@ export interface PracticeCheckOptions {
|
|
|
11
11
|
}
|
|
12
12
|
export declare function collectPracticeWarnings(projectDir: string, walkDir: (dir: string, re: RegExp) => string[], opts?: PracticeCheckOptions): PracticeWarning[];
|
|
13
13
|
export declare function practiceKindLabel(kind: PracticeKind): string;
|
|
14
|
-
export declare function formatPracticeWarnings(projectDir: string, warnings: PracticeWarning[]): string[];
|
|
@@ -193,16 +193,3 @@ export function practiceKindLabel(kind) {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
-
export function formatPracticeWarnings(projectDir, warnings) {
|
|
197
|
-
const lines = [];
|
|
198
|
-
for (const w of warnings) {
|
|
199
|
-
lines.push(`waygraph practices: ${w.file} (${practiceKindLabel(w.kind)}) — ${w.detail}`);
|
|
200
|
-
}
|
|
201
|
-
if (warnings.length === 0) {
|
|
202
|
-
lines.push(`waygraph practices: no bad-practice patterns under ${projectDir}`);
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
lines.push(`waygraph practices: ${warnings.length} bad-practice warning${warnings.length === 1 ? "" : "s"}`);
|
|
206
|
-
}
|
|
207
|
-
return lines;
|
|
208
|
-
}
|
|
@@ -9,7 +9,8 @@ import { collectKnownInteractionSelectors, unmappedInteractionsPageScript } from
|
|
|
9
9
|
import { runStubPhase } from "../highlights.js";
|
|
10
10
|
import { verboseLog } from "./verbose-log.js";
|
|
11
11
|
import { findBlockPathDetailed } from "../analysis/graph.js";
|
|
12
|
-
import {
|
|
12
|
+
import { configuredExtensions } from "../config.js";
|
|
13
|
+
import { chromiumExecutablePath, launchContext } from "../browser.js";
|
|
13
14
|
/**
|
|
14
15
|
* Non-interactive explore session: same graph/library/mem/browser setup
|
|
15
16
|
* `runAutoExplore` runs in `--cli` mode, driven by `applyPick` instead of a
|
|
@@ -26,7 +27,7 @@ export class AutoSession {
|
|
|
26
27
|
library;
|
|
27
28
|
mem;
|
|
28
29
|
engine;
|
|
29
|
-
|
|
30
|
+
closeBrowser;
|
|
30
31
|
context;
|
|
31
32
|
page;
|
|
32
33
|
startUrl;
|
|
@@ -38,7 +39,7 @@ export class AutoSession {
|
|
|
38
39
|
knownInteractionSelectors;
|
|
39
40
|
headless;
|
|
40
41
|
lastOverlayInfo = null;
|
|
41
|
-
constructor(projectDir, inject, blocksSelect, graph, library, mem, engine,
|
|
42
|
+
constructor(projectDir, inject, blocksSelect, graph, library, mem, engine, closeBrowser, context, page, startUrl, sessionId, knownInteractionSelectors, headless) {
|
|
42
43
|
this.projectDir = projectDir;
|
|
43
44
|
this.inject = inject;
|
|
44
45
|
this.blocksSelect = blocksSelect;
|
|
@@ -46,7 +47,7 @@ export class AutoSession {
|
|
|
46
47
|
this.library = library;
|
|
47
48
|
this.mem = mem;
|
|
48
49
|
this.engine = engine;
|
|
49
|
-
this.
|
|
50
|
+
this.closeBrowser = closeBrowser;
|
|
50
51
|
this.context = context;
|
|
51
52
|
this.page = page;
|
|
52
53
|
this.startUrl = startUrl;
|
|
@@ -85,13 +86,17 @@ export class AutoSession {
|
|
|
85
86
|
};
|
|
86
87
|
if (executablePath)
|
|
87
88
|
launchOpts.executablePath = executablePath;
|
|
88
|
-
const browser = await chromium.launch(launchOpts);
|
|
89
89
|
const contextOpts = {
|
|
90
90
|
viewport: headless ? { width: 1280, height: 720 } : null,
|
|
91
91
|
};
|
|
92
92
|
if (baseURL)
|
|
93
93
|
contextOpts.baseURL = baseURL;
|
|
94
|
-
const context = await
|
|
94
|
+
const { close: closeBrowser, context } = await launchContext({
|
|
95
|
+
launcher: chromium,
|
|
96
|
+
launch: launchOpts,
|
|
97
|
+
context: contextOpts,
|
|
98
|
+
extensions: await configuredExtensions(init.projectDir),
|
|
99
|
+
});
|
|
95
100
|
await installPersistentPilotOverlay(context);
|
|
96
101
|
const page = await context.newPage();
|
|
97
102
|
instrumentPilotActions(page, headless ? 0 : 450);
|
|
@@ -103,7 +108,7 @@ export class AutoSession {
|
|
|
103
108
|
await page.goto("about:blank").catch(() => { });
|
|
104
109
|
}
|
|
105
110
|
const knownInteractionSelectors = collectKnownInteractionSelectors(init.projectDir, library, init.inject);
|
|
106
|
-
const session = new AutoSession(init.projectDir, init.inject, init.blocksSelect, graph, library, mem, engine,
|
|
111
|
+
const session = new AutoSession(init.projectDir, init.inject, init.blocksSelect, graph, library, mem, engine, closeBrowser, context, page, startUrl, init.sessionId, knownInteractionSelectors, headless);
|
|
107
112
|
page.on("load", () => {
|
|
108
113
|
void session.repaintOverlayAfterNavigation();
|
|
109
114
|
});
|
|
@@ -709,6 +714,6 @@ export class AutoSession {
|
|
|
709
714
|
}
|
|
710
715
|
async close() {
|
|
711
716
|
await this.context.close();
|
|
712
|
-
await this.
|
|
717
|
+
await this.closeBrowser();
|
|
713
718
|
}
|
|
714
719
|
}
|
package/dist/blocks-select.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `--blocks` select grammar (RFC Phase C).
|
|
3
|
+
*
|
|
4
|
+
* - `/pattern/` -> regex on relative file path OR block name
|
|
5
|
+
* - has `*`/`**` -> glob on relative file path (POSIX-ish)
|
|
6
|
+
* - otherwise -> bare token (flow export / checkpoint / block name) — caller decides
|
|
7
|
+
*/
|
|
1
8
|
export type BlocksSelectKind = "regex" | "glob" | "bare";
|
|
2
9
|
export interface BlocksSelect {
|
|
3
10
|
kind: BlocksSelectKind;
|
|
@@ -16,7 +23,3 @@ export declare function matchBlocksSelect(select: BlocksSelect, opts: {
|
|
|
16
23
|
relFile: string;
|
|
17
24
|
blockName?: string;
|
|
18
25
|
}): boolean;
|
|
19
|
-
export declare function filterByBlocksSelect<T extends {
|
|
20
|
-
file: string;
|
|
21
|
-
block?: string;
|
|
22
|
-
}>(items: T[], select: BlocksSelect | undefined, projectDir: string): T[];
|
package/dist/blocks-select.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* - has `*`/`**` -> glob on relative file path (POSIX-ish)
|
|
6
6
|
* - otherwise -> bare token (flow export / checkpoint / block name) — caller decides
|
|
7
7
|
*/
|
|
8
|
-
import { relative } from "node:path";
|
|
9
8
|
/** True when the token is file-select (glob/regex), not a bare flow/checkpoint name. */
|
|
10
9
|
export function isFileSelectToken(token) {
|
|
11
10
|
if (!token)
|
|
@@ -70,16 +69,3 @@ export function matchBlocksSelect(select, opts) {
|
|
|
70
69
|
return true;
|
|
71
70
|
return false;
|
|
72
71
|
}
|
|
73
|
-
export function filterByBlocksSelect(items, select, projectDir) {
|
|
74
|
-
if (!select)
|
|
75
|
-
return items;
|
|
76
|
-
return items.filter((it) => {
|
|
77
|
-
const relFile = it.file.startsWith(projectDir)
|
|
78
|
-
? relative(projectDir, it.file)
|
|
79
|
-
: it.file;
|
|
80
|
-
return matchBlocksSelect(select, {
|
|
81
|
-
relFile: relFile.replace(/\\/g, "/"),
|
|
82
|
-
...(it.block !== undefined ? { blockName: it.block } : {}),
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Browser, BrowserContext, BrowserContextOptions, BrowserType, LaunchOptions } from "@playwright/test";
|
|
2
|
+
/**
|
|
3
|
+
* CHROME_PATH / CHROMIUM_PATH, if set and the file exists. A stale path (browser uninstalled)
|
|
4
|
+
* falls back to Playwright's bundled Chromium with a one-time warning instead of failing the run.
|
|
5
|
+
*/
|
|
6
|
+
export declare function chromiumExecutablePath(): string | undefined;
|
|
7
|
+
/** Unpacked extension directories, as resolved by `resolveExtensions` - also what `WAYGRAPH_EXTENSIONS` carries to child processes. */
|
|
8
|
+
export declare function extensionPathsFromEnv(): string[];
|
|
9
|
+
export interface LaunchedContext {
|
|
10
|
+
/** Null for a persistent (extension) context. */
|
|
11
|
+
browser: Browser | null;
|
|
12
|
+
context: BrowserContext;
|
|
13
|
+
/** Closes the context and the browser (and the throwaway profile when extensions are loaded). */
|
|
14
|
+
close(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Launches a browser and its first context. With `extensions` it uses a persistent context, the only
|
|
18
|
+
* way Chromium loads extensions: new headless (`channel: "chromium"`; the default headless shell
|
|
19
|
+
* ignores them) and Playwright's own Chromium (branded Chrome 137+ ignores `--load-extension`).
|
|
20
|
+
* Extensions are not active in extra contexts made with `browser.newContext()`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function launchContext(opts: {
|
|
23
|
+
launcher: BrowserType;
|
|
24
|
+
launch: LaunchOptions;
|
|
25
|
+
context?: BrowserContextOptions;
|
|
26
|
+
extensions?: readonly string[];
|
|
27
|
+
}): Promise<LaunchedContext>;
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
let warned = false;
|
|
5
|
+
/**
|
|
6
|
+
* CHROME_PATH / CHROMIUM_PATH, if set and the file exists. A stale path (browser uninstalled)
|
|
7
|
+
* falls back to Playwright's bundled Chromium with a one-time warning instead of failing the run.
|
|
8
|
+
*/
|
|
9
|
+
export function chromiumExecutablePath() {
|
|
10
|
+
const path = process.env.CHROME_PATH || process.env.CHROMIUM_PATH;
|
|
11
|
+
if (!path)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (existsSync(path))
|
|
14
|
+
return path;
|
|
15
|
+
if (!warned) {
|
|
16
|
+
warned = true;
|
|
17
|
+
console.error(`waygraph: CHROME_PATH/CHROMIUM_PATH "${path}" does not exist - using Playwright's bundled Chromium`);
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
/** Unpacked extension directories, as resolved by `resolveExtensions` - also what `WAYGRAPH_EXTENSIONS` carries to child processes. */
|
|
22
|
+
export function extensionPathsFromEnv() {
|
|
23
|
+
return (process.env.WAYGRAPH_EXTENSIONS ?? "").split(",").filter(Boolean);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Launches a browser and its first context. With `extensions` it uses a persistent context, the only
|
|
27
|
+
* way Chromium loads extensions: new headless (`channel: "chromium"`; the default headless shell
|
|
28
|
+
* ignores them) and Playwright's own Chromium (branded Chrome 137+ ignores `--load-extension`).
|
|
29
|
+
* Extensions are not active in extra contexts made with `browser.newContext()`.
|
|
30
|
+
*/
|
|
31
|
+
export async function launchContext(opts) {
|
|
32
|
+
const extensions = opts.extensions ?? [];
|
|
33
|
+
if (extensions.length === 0) {
|
|
34
|
+
const browser = await opts.launcher.launch(opts.launch);
|
|
35
|
+
const context = await browser.newContext(opts.context);
|
|
36
|
+
return { browser, context, close: () => browser.close() };
|
|
37
|
+
}
|
|
38
|
+
if (opts.launcher.name() !== "chromium")
|
|
39
|
+
throw new Error("waygraph: extensions only work in Chromium");
|
|
40
|
+
const list = extensions.join(",");
|
|
41
|
+
const { executablePath: _ignored, ...launch } = opts.launch;
|
|
42
|
+
const profile = mkdtempSync(join(tmpdir(), "waygraph-profile-"));
|
|
43
|
+
const context = await opts.launcher.launchPersistentContext(profile, {
|
|
44
|
+
...launch,
|
|
45
|
+
...opts.context,
|
|
46
|
+
...(launch.headless === false ? {} : { channel: "chromium" }),
|
|
47
|
+
args: [...(launch.args ?? []), `--disable-extensions-except=${list}`, `--load-extension=${list}`],
|
|
48
|
+
});
|
|
49
|
+
// An extension's background worker registers its page hooks, so wait for it before the first navigation.
|
|
50
|
+
const hasWorker = extensions.some((dir) => JSON.parse(readFileSync(join(dir, "manifest.json"), "utf-8")).background?.service_worker !== undefined);
|
|
51
|
+
if (hasWorker && context.serviceWorkers().length === 0) {
|
|
52
|
+
await context.waitForEvent("serviceworker", { timeout: 10_000 }).catch(() => undefined);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
browser: context.browser(),
|
|
56
|
+
context,
|
|
57
|
+
close: async () => {
|
|
58
|
+
await context.close();
|
|
59
|
+
rmSync(profile, { recursive: true, force: true });
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
package/dist/cli/run-chain.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { configuredExtensions } from "../config.js";
|
|
2
3
|
import { expandSpecFlowFiles, resolveBaseUrl } from "./flags.js";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { rmSync, writeFileSync } from "node:fs";
|
|
@@ -45,6 +46,9 @@ export async function runChain(projectDir, spec) {
|
|
|
45
46
|
if (resolved)
|
|
46
47
|
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
47
48
|
}
|
|
49
|
+
const extensions = await configuredExtensions(projectDir);
|
|
50
|
+
if (extensions.length > 0)
|
|
51
|
+
process.env.WAYGRAPH_EXTENSIONS = extensions.join(",");
|
|
48
52
|
const tsxEsm = import.meta.resolve("tsx/esm");
|
|
49
53
|
const scriptPath = join(projectDir, `.waygraph-chain-${process.pid}.mjs`);
|
|
50
54
|
writeFileSync(scriptPath, CHAIN_RUNNER_BOOTSTRAP);
|
package/dist/cli/usage.js
CHANGED
|
@@ -156,7 +156,9 @@ Primary (less is more):
|
|
|
156
156
|
--ff-disabled Dispute: expand FF (alias --no-ff); blitz kept on those inners
|
|
157
157
|
--auto-play-video Unattended + recorded: --auto-next + --video (+ step); headless
|
|
158
158
|
--auto-play-video-head Same, but keep the browser visible (--non-headless)
|
|
159
|
-
--video [dir] Record .webm (demo: headless unless --non-headless)
|
|
159
|
+
--video [dir] Record .webm (demo: headless unless --non-headless);
|
|
160
|
+
default folder ~/Downloads/waygraph-videos/<project>,
|
|
161
|
+
or video.dir in the config / WAYGRAPH_VIDEO_DIR
|
|
160
162
|
--video-viewport WxH Recording size (default demo: 1920x1080)
|
|
161
163
|
--title / --base-url
|
|
162
164
|
waygraph run [--blocks <flow|file|spec>] Execute (no overlay unless --step)
|
|
@@ -184,6 +186,17 @@ Primary (less is more):
|
|
|
184
186
|
has a trace - test report opens it.
|
|
185
187
|
|
|
186
188
|
Also:
|
|
189
|
+
--env <mode> (any command) load .env.<mode> / .env.<mode>.local on top of .env, e.g.
|
|
190
|
+
waygraph test --env staging (default: NODE_ENV, else development)
|
|
191
|
+
waygraph upgrade [project] [paths...] [--apply] [--overwrite] [--diff]
|
|
192
|
+
Compare a project with the scaffold in this waygraph:
|
|
193
|
+
lists new and differing files and a stale waygraph
|
|
194
|
+
dependency. Dry run unless --apply; only the paths you
|
|
195
|
+
name are copied, differing files only with --overwrite.
|
|
196
|
+
waygraph extensions install [project] [--update]
|
|
197
|
+
Download the browser extensions listed in
|
|
198
|
+
waygraph.config.* (extensions: ["hoppscotch"]) into
|
|
199
|
+
the user cache; runs also fetch them on first use.
|
|
187
200
|
waygraph list | nav | validate | check | typecheck | graph | init <name>
|
|
188
201
|
check/typecheck --no-practices Skip bad-practice warnings
|
|
189
202
|
waygraph map [project] Waygraph Map convention enforcement: every static
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* "project" defaults to cwd. Flags beat WAYGRAPH_* env.
|
|
16
16
|
*/
|
|
17
17
|
import { SKILL_FLAG_MAP, printSkillIndex, readSkillMarkdown } from "./commands/agent-dive-scaffold.js";
|
|
18
|
+
import { takeEnvFlag } from "./env-file.js";
|
|
18
19
|
import { usage } from "./cli/usage.js";
|
|
19
20
|
import { listCase, navCase } from "./commands/list.js";
|
|
20
21
|
import { checkCase, typecheckCase, validateCase } from "./commands/check.js";
|
|
@@ -25,8 +26,10 @@ import { traverseCase } from "./commands/traverse.js";
|
|
|
25
26
|
import { agentDiveCase } from "./commands/agent-dive.js";
|
|
26
27
|
import { autoServeCase, sessionCase } from "./commands/session.js";
|
|
27
28
|
import { tryCase } from "./commands/try.js";
|
|
29
|
+
import { upgradeCase } from "./commands/upgrade.js";
|
|
30
|
+
import { extensionsCase } from "./commands/extensions.js";
|
|
28
31
|
import { mapCase } from "./commands/map.js";
|
|
29
|
-
const args = process.argv.slice(2);
|
|
32
|
+
const args = takeEnvFlag(process.argv.slice(2));
|
|
30
33
|
const command = args[0];
|
|
31
34
|
async function main() {
|
|
32
35
|
if (!command || command === "--help" || command === "-h") {
|
|
@@ -97,6 +100,12 @@ async function main() {
|
|
|
97
100
|
case "map":
|
|
98
101
|
await mapCase(args);
|
|
99
102
|
break;
|
|
103
|
+
case "upgrade":
|
|
104
|
+
await upgradeCase(args);
|
|
105
|
+
break;
|
|
106
|
+
case "extensions":
|
|
107
|
+
await extensionsCase(args);
|
|
108
|
+
break;
|
|
100
109
|
default:
|
|
101
110
|
console.error(`waygraph: unknown command "${command}"`);
|
|
102
111
|
process.exit(1);
|
|
@@ -28,6 +28,11 @@ export declare const SKILL_FLAG_MAP: {
|
|
|
28
28
|
export type SkillFlag = keyof typeof SKILL_FLAG_MAP;
|
|
29
29
|
/** Raw skill markdown (frontmatter + body), as shipped in the package. */
|
|
30
30
|
export declare function readSkillMarkdown(stem: string): string;
|
|
31
|
+
/** Every packaged skill as `.agents/skills/<name>/SKILL.md` (relative path + markdown), the cross-agent skills folder. */
|
|
32
|
+
export declare function agentSkillFiles(): {
|
|
33
|
+
rel: string;
|
|
34
|
+
text: string;
|
|
35
|
+
}[];
|
|
31
36
|
/** Print `--skill` index (available flags + one-line descriptions). */
|
|
32
37
|
export declare function printSkillIndex(): void;
|
|
33
38
|
export interface AgentDiveOptions {
|
|
@@ -100,6 +100,13 @@ export function readSkillMarkdown(stem) {
|
|
|
100
100
|
const raw = readFileSync(path, "utf-8");
|
|
101
101
|
return raw.endsWith("\n") ? raw : raw + "\n";
|
|
102
102
|
}
|
|
103
|
+
/** Every packaged skill as `.agents/skills/<name>/SKILL.md` (relative path + markdown), the cross-agent skills folder. */
|
|
104
|
+
export function agentSkillFiles() {
|
|
105
|
+
return Object.values(SKILL_FLAG_MAP).map((stem) => ({
|
|
106
|
+
rel: join(".agents", "skills", stem, "SKILL.md"),
|
|
107
|
+
text: readSkillMarkdown(stem),
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
103
110
|
/** Print `--skill` index (available flags + one-line descriptions). */
|
|
104
111
|
export function printSkillIndex() {
|
|
105
112
|
const skills = loadSkillSpecs();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function extensionsCase(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { loadWaygraphConfig } from "../config.js";
|
|
3
|
+
import { extensionInfo, resolveExtension } from "../extensions.js";
|
|
4
|
+
export async function extensionsCase(args) {
|
|
5
|
+
const update = args.includes("--update");
|
|
6
|
+
const rest = args.slice(2).filter((a) => a !== "--update");
|
|
7
|
+
if (args[1] !== "install") {
|
|
8
|
+
console.error("usage: waygraph extensions install [project] [--update]");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const proj = resolve(rest[0] ?? process.cwd());
|
|
12
|
+
const specs = (await loadWaygraphConfig(proj))?.extensions ?? [];
|
|
13
|
+
if (specs.length === 0) {
|
|
14
|
+
console.log("waygraph extensions: no `extensions` in waygraph.config.* - nothing to install");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
for (const spec of specs) {
|
|
18
|
+
const dir = await resolveExtension(spec, proj, { update });
|
|
19
|
+
const { name, version } = extensionInfo(dir);
|
|
20
|
+
console.log(`waygraph extensions: ${name} ${version} -> ${dir}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
2
|
import { packageRoot } from "../cli/util.js";
|
|
3
|
-
import {
|
|
3
|
+
import { agentSkillFiles } from "./agent-dive-scaffold.js";
|
|
4
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
4
6
|
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
7
|
import { pathToFileURL } from "node:url";
|
|
8
|
+
/**
|
|
9
|
+
* Running from a checkout (not an installed package), npm doesn't have this build's code, so a plain `^version` would install an
|
|
10
|
+
* older waygraph and break the scaffold's types. Pack this checkout into the project and depend on that file instead.
|
|
11
|
+
*/
|
|
12
|
+
function packLocalBuild(targetDir) {
|
|
13
|
+
const root = packageRoot();
|
|
14
|
+
if (root.split(sep).includes("node_modules"))
|
|
15
|
+
return undefined;
|
|
16
|
+
const dir = join(targetDir, ".waygraph-local");
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
const packed = spawnSync("npm", ["pack", "--pack-destination", dir, "--silent"], {
|
|
19
|
+
cwd: root,
|
|
20
|
+
encoding: "utf-8",
|
|
21
|
+
shell: process.platform === "win32",
|
|
22
|
+
});
|
|
23
|
+
const file = packed.stdout?.trim().split("\n").pop();
|
|
24
|
+
if (packed.status !== 0 || !file) {
|
|
25
|
+
console.error("waygraph init: could not pack this checkout; the project will depend on the published waygraph");
|
|
26
|
+
rmSync(dir, { recursive: true, force: true });
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
writeFileSync(join(targetDir, ".gitignore"), readFileSync(join(targetDir, ".gitignore"), "utf-8") + ".waygraph-local/\n");
|
|
30
|
+
return `file:.waygraph-local/${file}`;
|
|
31
|
+
}
|
|
6
32
|
function initCommand(projectName) {
|
|
7
33
|
if (!projectName) {
|
|
8
34
|
console.error("waygraph init: missing <project-name>, e.g. waygraph init my-app");
|
|
@@ -34,9 +60,20 @@ function initCommand(projectName) {
|
|
|
34
60
|
// Demo pages open via file://; the absolute path is only known once copied, so it's filled in here.
|
|
35
61
|
const appBaseUrl = pathToFileURL(join(targetDir, "src", "map", "(app_base)", "index.html")).href;
|
|
36
62
|
const packageJsonPath = join(targetDir, "package.json");
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8").replaceAll("__PROJECT_NAME__", pkgName));
|
|
64
|
+
// Pin the project to the waygraph that created it - a stale range here installs an old engine.
|
|
65
|
+
const ownVersion = JSON.parse(readFileSync(join(packageRoot(), "package.json"), "utf-8")).version;
|
|
66
|
+
const local = packLocalBuild(targetDir);
|
|
67
|
+
pkg.dependencies.waygraph = local ?? `^${ownVersion}`;
|
|
68
|
+
if (local)
|
|
69
|
+
console.log(`(running from a waygraph checkout: depending on its local build, ${local})`);
|
|
70
|
+
writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
71
|
+
const configPath = join(targetDir, "config", "waygraph.config.ts");
|
|
72
|
+
writeFileSync(configPath, readFileSync(configPath, "utf-8").replaceAll("__APP_BASE_URL__", appBaseUrl));
|
|
73
|
+
for (const { rel, text } of agentSkillFiles()) {
|
|
74
|
+
mkdirSync(dirname(join(targetDir, rel)), { recursive: true });
|
|
75
|
+
writeFileSync(join(targetDir, rel), text);
|
|
76
|
+
}
|
|
40
77
|
console.log(`Scaffolded ${pkgName}/`);
|
|
41
78
|
console.log("");
|
|
42
79
|
console.log(` cd ${relative(process.cwd(), targetDir) || pkgName}`);
|
|
@@ -50,6 +87,7 @@ function initCommand(projectName) {
|
|
|
50
87
|
console.log(" waygraph auto --cli # same menus in the terminal");
|
|
51
88
|
console.log(" waygraph demo --blocks exampleFlow");
|
|
52
89
|
console.log(" waygraph graph # static state graph JSON");
|
|
90
|
+
console.log(" .agents/skills/ # waygraph skills for coding agents (waygraph agent-dive adds agent personas)");
|
|
53
91
|
console.log("");
|
|
54
92
|
console.log(" Layout: see STRUCTURE.md (or https://deviate-dv8.github.io/waygraph/scaffold.html)");
|
|
55
93
|
}
|
package/dist/commands/test.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { configuredExtensions } from "../config.js";
|
|
2
3
|
import { runInherited } from "../cli/util.js";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { existsSync } from "node:fs";
|
|
6
|
+
import { packageRoot } from "../cli/util.js";
|
|
5
7
|
export async function testCase(args) {
|
|
6
8
|
const rest = args.slice(1);
|
|
7
9
|
const proj = process.cwd();
|
|
@@ -23,11 +25,23 @@ export async function testCase(args) {
|
|
|
23
25
|
process.exitCode = await runPw(["show-trace", ...rest.slice(1)]);
|
|
24
26
|
return;
|
|
25
27
|
}
|
|
28
|
+
const extensions = await configuredExtensions(proj);
|
|
29
|
+
if (extensions.length > 0)
|
|
30
|
+
process.env.WAYGRAPH_EXTENSIONS = extensions.join(",");
|
|
31
|
+
// No playwright.config in the project: waygraph builds one from config/waygraph.config.ts.
|
|
32
|
+
const hasOwnConfig = ["ts", "mts", "js", "mjs", "cjs"].some((e) => existsSync(join(proj, `playwright.config.${e}`)));
|
|
33
|
+
if (hasOwnConfig) {
|
|
34
|
+
console.error("waygraph: playwright.config is deprecated - move its settings into config/waygraph.config.ts");
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
process.env.WAYGRAPH_PROJECT_DIR = proj;
|
|
38
|
+
}
|
|
39
|
+
const configArgs = hasOwnConfig ? [] : ["-c", join(packageRoot(), "dist", "playwright.config.js")];
|
|
26
40
|
const wantsUi = rest[0] === "ui" || rest.includes("--ui");
|
|
27
41
|
const forwarded = rest[0] === "ui" ? rest.slice(1) : rest;
|
|
28
42
|
const pwArgs = wantsUi && !forwarded.includes("--ui")
|
|
29
|
-
? ["test", "--ui", ...forwarded]
|
|
30
|
-
: ["test", ...forwarded];
|
|
43
|
+
? ["test", ...configArgs, "--ui", ...forwarded]
|
|
44
|
+
: ["test", ...configArgs, ...forwarded];
|
|
31
45
|
process.exitCode = await runPw(pwArgs);
|
|
32
46
|
return;
|
|
33
47
|
}
|
package/dist/commands/try.js
CHANGED
|
@@ -6,6 +6,7 @@ import { packageRoot, runInherited } from "../cli/util.js";
|
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { runChain } from "../cli/run-chain.js";
|
|
8
8
|
import { applyRunFlags, parseRunFlags } from "../cli/flags.js";
|
|
9
|
+
import { resolveVideoDir } from "../video-dir.js";
|
|
9
10
|
/**
|
|
10
11
|
* Runs a throwaway probe script rooted at `projectDir` - the exact same
|
|
11
12
|
* directory the real demo files get copied into, so it resolves "waygraph"
|
|
@@ -196,7 +197,7 @@ async function runTryDemo() {
|
|
|
196
197
|
const videoTo = process.env.WAYGRAPH_VIDEO;
|
|
197
198
|
console.log(videoTo
|
|
198
199
|
? "waygraph try demo: step-through + record (Sign In -> Shop & Checkout -> blocked Viewer login) - click Next; video -> " +
|
|
199
|
-
(videoTo === "1" ?
|
|
200
|
+
(videoTo === "1" ? await resolveVideoDir(destDir) : videoTo)
|
|
200
201
|
: "waygraph try demo: step-through (Sign In -> Shop & Checkout -> blocked Viewer login) - click Next for each step ...");
|
|
201
202
|
await runChain(destDir, TRY_DEMO_CHAIN);
|
|
202
203
|
if (process.exitCode)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `waygraph upgrade [project] [paths...] [--apply] [--overwrite] [--diff]`
|
|
3
|
+
* Compares the project with the scaffold shipped in this waygraph. Dry run by default.
|
|
4
|
+
* Only the paths you name are copied, because a real project deliberately drops parts of the demo.
|
|
5
|
+
*/
|
|
6
|
+
export declare function upgradeCase(args: string[]): Promise<void>;
|