demo-dev 0.0.1-alpha.0
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 +174 -0
- package/bin/demo-cli.js +26 -0
- package/bin/demo-dev.js +26 -0
- package/demo.dev.config.example.json +20 -0
- package/dist/index.d.ts +392 -0
- package/dist/index.js +2116 -0
- package/package.json +76 -0
- package/skills/demo-dev/SKILL.md +153 -0
- package/skills/demo-dev/references/configuration.md +102 -0
- package/skills/demo-dev/references/recipes.md +83 -0
- package/src/ai/provider.ts +254 -0
- package/src/auth/bootstrap.ts +72 -0
- package/src/browser/session.ts +43 -0
- package/src/capture/continuous-capture.ts +739 -0
- package/src/cli.ts +337 -0
- package/src/config/project.ts +183 -0
- package/src/github/comment.ts +134 -0
- package/src/index.ts +10 -0
- package/src/lib/data-uri.ts +21 -0
- package/src/lib/fs.ts +7 -0
- package/src/lib/git.ts +59 -0
- package/src/lib/media.ts +23 -0
- package/src/orchestrate.ts +166 -0
- package/src/planner/heuristic.ts +180 -0
- package/src/planner/index.ts +26 -0
- package/src/planner/llm.ts +85 -0
- package/src/planner/openai.ts +77 -0
- package/src/planner/prompt.ts +331 -0
- package/src/planner/refine.ts +155 -0
- package/src/planner/schema.ts +62 -0
- package/src/presentation/polish.ts +84 -0
- package/src/probe/page-probe.ts +225 -0
- package/src/render/browser-frame.ts +176 -0
- package/src/render/ffmpeg-compose.ts +779 -0
- package/src/render/visual-plan.ts +422 -0
- package/src/setup/doctor.ts +158 -0
- package/src/setup/init.ts +90 -0
- package/src/types.ts +105 -0
- package/src/voice/script.ts +42 -0
- package/src/voice/tts.ts +286 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { chromium, type Locator, type Page } from "playwright";
|
|
4
|
+
import type { ActionTarget } from "../types.js";
|
|
5
|
+
import type { ProjectAuthConfig } from "../config/project.js";
|
|
6
|
+
|
|
7
|
+
export interface BootstrapAuthOptions {
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
email: string;
|
|
10
|
+
password: string;
|
|
11
|
+
outputPath: string;
|
|
12
|
+
auth?: ProjectAuthConfig;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const resolveLocator = (page: Page, target: ActionTarget): Locator => {
|
|
16
|
+
switch (target.strategy) {
|
|
17
|
+
case "label":
|
|
18
|
+
return page.getByLabel(target.value, { exact: target.exact });
|
|
19
|
+
case "text":
|
|
20
|
+
return page.getByText(target.value, { exact: target.exact });
|
|
21
|
+
case "placeholder":
|
|
22
|
+
return page.getByPlaceholder(target.value, { exact: target.exact });
|
|
23
|
+
case "testId":
|
|
24
|
+
return page.getByTestId(target.value);
|
|
25
|
+
case "css":
|
|
26
|
+
return page.locator(target.value);
|
|
27
|
+
case "role":
|
|
28
|
+
return page.getByRole(target.role as never, {
|
|
29
|
+
name: target.name,
|
|
30
|
+
exact: target.exact,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const bootstrapAuth = async (options: BootstrapAuthOptions) => {
|
|
36
|
+
const browser = await chromium.launch({ headless: true });
|
|
37
|
+
const auth = options.auth;
|
|
38
|
+
const loginPath = auth?.loginPath ?? "/login";
|
|
39
|
+
const emailTarget: ActionTarget = auth?.emailTarget ?? { strategy: "css", value: "#email" };
|
|
40
|
+
const passwordTarget: ActionTarget = auth?.passwordTarget ?? { strategy: "css", value: "#password" };
|
|
41
|
+
const submitTarget: ActionTarget = auth?.submitTarget ?? { strategy: "role", role: "button", name: "Login" };
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const context = await browser.newContext();
|
|
45
|
+
const page = await context.newPage();
|
|
46
|
+
await page.goto(new URL(loginPath, options.baseUrl).toString(), { waitUntil: "networkidle" });
|
|
47
|
+
|
|
48
|
+
await resolveLocator(page, emailTarget).first().fill(options.email);
|
|
49
|
+
await resolveLocator(page, passwordTarget).first().fill(options.password);
|
|
50
|
+
await resolveLocator(page, submitTarget).first().click();
|
|
51
|
+
|
|
52
|
+
await page.waitForLoadState("networkidle").catch(() => undefined);
|
|
53
|
+
await page.waitForTimeout(auth?.postSubmitWaitMs ?? 1200);
|
|
54
|
+
|
|
55
|
+
if (auth?.successUrlPattern) {
|
|
56
|
+
await page.waitForURL(auth.successUrlPattern, { timeout: 15000 });
|
|
57
|
+
} else {
|
|
58
|
+
await page.waitForURL((url) => !url.toString().includes(loginPath), { timeout: 15000 });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await mkdir(dirname(options.outputPath), { recursive: true });
|
|
62
|
+
await context.storageState({ path: options.outputPath });
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
storageStatePath: options.outputPath,
|
|
66
|
+
url: page.url(),
|
|
67
|
+
title: await page.title(),
|
|
68
|
+
};
|
|
69
|
+
} finally {
|
|
70
|
+
await browser.close();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { access, mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import type { BrowserContextOptions, Page } from "playwright";
|
|
4
|
+
|
|
5
|
+
const fileExists = async (path: string) => {
|
|
6
|
+
try {
|
|
7
|
+
await access(path);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface SessionConfig {
|
|
15
|
+
storageStatePath?: string;
|
|
16
|
+
saveStorageStatePath?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const resolveSessionConfig = (outputDir: string): SessionConfig => {
|
|
20
|
+
const storageStatePath = process.env.DEMO_STORAGE_STATE;
|
|
21
|
+
const saveStorageStatePath = process.env.DEMO_SAVE_STORAGE_STATE ?? `${outputDir}/storage-state.json`;
|
|
22
|
+
return { storageStatePath, saveStorageStatePath };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const getContextOptionsWithSession = async (
|
|
26
|
+
contextOptions: BrowserContextOptions,
|
|
27
|
+
session: SessionConfig,
|
|
28
|
+
): Promise<BrowserContextOptions> => {
|
|
29
|
+
if (session.storageStatePath && (await fileExists(session.storageStatePath))) {
|
|
30
|
+
return {
|
|
31
|
+
...contextOptions,
|
|
32
|
+
storageState: session.storageStatePath,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return contextOptions;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const persistSessionState = async (page: Page, session: SessionConfig) => {
|
|
40
|
+
if (!session.saveStorageStatePath) return;
|
|
41
|
+
await mkdir(dirname(session.saveStorageStatePath), { recursive: true }).catch(() => undefined);
|
|
42
|
+
await page.context().storageState({ path: session.saveStorageStatePath }).catch(() => undefined);
|
|
43
|
+
};
|