libretto 0.2.6 → 0.3.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/LICENSE +21 -0
- package/package.json +12 -12
- package/skill/SKILL.md +20 -18
- package/skill/code-generation-rules.md +3 -3
- package/skill/integration-approach-selection.md +3 -3
- package/dist/cli/cli.js +0 -209
- package/dist/cli/commands/ai.js +0 -21
- package/dist/cli/commands/browser.js +0 -82
- package/dist/cli/commands/execution.js +0 -461
- package/dist/cli/commands/init.js +0 -95
- package/dist/cli/commands/logs.js +0 -93
- package/dist/cli/commands/snapshot.js +0 -106
- package/dist/cli/core/ai-config.js +0 -149
- package/dist/cli/core/browser.js +0 -648
- package/dist/cli/core/context.js +0 -118
- package/dist/cli/core/pause-signals.js +0 -29
- package/dist/cli/core/session-telemetry.js +0 -491
- package/dist/cli/core/session.js +0 -183
- package/dist/cli/core/snapshot-analyzer.js +0 -492
- package/dist/cli/core/telemetry.js +0 -362
- package/dist/cli/index.js +0 -13
- package/dist/cli/workers/run-integration-runtime.js +0 -227
- package/dist/cli/workers/run-integration-worker-protocol.js +0 -12
- package/dist/cli/workers/run-integration-worker.js +0 -66
- package/dist/index.cjs +0 -116
- package/dist/index.d.cts +0 -21
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -97
- package/dist/runtime/download/download.cjs +0 -70
- package/dist/runtime/download/download.d.cts +0 -35
- package/dist/runtime/download/download.d.ts +0 -35
- package/dist/runtime/download/download.js +0 -45
- package/dist/runtime/download/index.cjs +0 -30
- package/dist/runtime/download/index.d.cts +0 -3
- package/dist/runtime/download/index.d.ts +0 -3
- package/dist/runtime/download/index.js +0 -8
- package/dist/runtime/extract/extract.cjs +0 -88
- package/dist/runtime/extract/extract.d.cts +0 -23
- package/dist/runtime/extract/extract.d.ts +0 -23
- package/dist/runtime/extract/extract.js +0 -64
- package/dist/runtime/extract/index.cjs +0 -28
- package/dist/runtime/extract/index.d.cts +0 -5
- package/dist/runtime/extract/index.d.ts +0 -5
- package/dist/runtime/extract/index.js +0 -4
- package/dist/runtime/network/index.cjs +0 -28
- package/dist/runtime/network/index.d.cts +0 -4
- package/dist/runtime/network/index.d.ts +0 -4
- package/dist/runtime/network/index.js +0 -6
- package/dist/runtime/network/network.cjs +0 -91
- package/dist/runtime/network/network.d.cts +0 -28
- package/dist/runtime/network/network.d.ts +0 -28
- package/dist/runtime/network/network.js +0 -67
- package/dist/runtime/recovery/agent.cjs +0 -223
- package/dist/runtime/recovery/agent.d.cts +0 -13
- package/dist/runtime/recovery/agent.d.ts +0 -13
- package/dist/runtime/recovery/agent.js +0 -199
- package/dist/runtime/recovery/errors.cjs +0 -124
- package/dist/runtime/recovery/errors.d.cts +0 -31
- package/dist/runtime/recovery/errors.d.ts +0 -31
- package/dist/runtime/recovery/errors.js +0 -100
- package/dist/runtime/recovery/index.cjs +0 -34
- package/dist/runtime/recovery/index.d.cts +0 -7
- package/dist/runtime/recovery/index.d.ts +0 -7
- package/dist/runtime/recovery/index.js +0 -10
- package/dist/runtime/recovery/recovery.cjs +0 -55
- package/dist/runtime/recovery/recovery.d.cts +0 -12
- package/dist/runtime/recovery/recovery.d.ts +0 -12
- package/dist/runtime/recovery/recovery.js +0 -31
- package/dist/shared/config/config.cjs +0 -44
- package/dist/shared/config/config.d.cts +0 -10
- package/dist/shared/config/config.d.ts +0 -10
- package/dist/shared/config/config.js +0 -18
- package/dist/shared/config/index.cjs +0 -32
- package/dist/shared/config/index.d.cts +0 -1
- package/dist/shared/config/index.d.ts +0 -1
- package/dist/shared/config/index.js +0 -10
- package/dist/shared/debug/index.cjs +0 -30
- package/dist/shared/debug/index.d.cts +0 -1
- package/dist/shared/debug/index.d.ts +0 -1
- package/dist/shared/debug/index.js +0 -5
- package/dist/shared/debug/pause.cjs +0 -90
- package/dist/shared/debug/pause.d.cts +0 -16
- package/dist/shared/debug/pause.d.ts +0 -16
- package/dist/shared/debug/pause.js +0 -55
- package/dist/shared/instrumentation/errors.cjs +0 -81
- package/dist/shared/instrumentation/errors.d.cts +0 -12
- package/dist/shared/instrumentation/errors.d.ts +0 -12
- package/dist/shared/instrumentation/errors.js +0 -57
- package/dist/shared/instrumentation/index.cjs +0 -35
- package/dist/shared/instrumentation/index.d.cts +0 -6
- package/dist/shared/instrumentation/index.d.ts +0 -6
- package/dist/shared/instrumentation/index.js +0 -12
- package/dist/shared/instrumentation/instrument.cjs +0 -206
- package/dist/shared/instrumentation/instrument.d.cts +0 -32
- package/dist/shared/instrumentation/instrument.d.ts +0 -32
- package/dist/shared/instrumentation/instrument.js +0 -190
- package/dist/shared/llm/ai-sdk-adapter.cjs +0 -67
- package/dist/shared/llm/ai-sdk-adapter.d.cts +0 -22
- package/dist/shared/llm/ai-sdk-adapter.d.ts +0 -22
- package/dist/shared/llm/ai-sdk-adapter.js +0 -43
- package/dist/shared/llm/client.cjs +0 -139
- package/dist/shared/llm/client.d.cts +0 -6
- package/dist/shared/llm/client.d.ts +0 -6
- package/dist/shared/llm/client.js +0 -115
- package/dist/shared/llm/index.cjs +0 -31
- package/dist/shared/llm/index.d.cts +0 -5
- package/dist/shared/llm/index.d.ts +0 -5
- package/dist/shared/llm/index.js +0 -6
- package/dist/shared/llm/types.cjs +0 -16
- package/dist/shared/llm/types.d.cts +0 -66
- package/dist/shared/llm/types.d.ts +0 -66
- package/dist/shared/llm/types.js +0 -0
- package/dist/shared/logger/index.cjs +0 -37
- package/dist/shared/logger/index.d.cts +0 -2
- package/dist/shared/logger/index.d.ts +0 -2
- package/dist/shared/logger/index.js +0 -13
- package/dist/shared/logger/logger.cjs +0 -213
- package/dist/shared/logger/logger.d.cts +0 -82
- package/dist/shared/logger/logger.d.ts +0 -82
- package/dist/shared/logger/logger.js +0 -188
- package/dist/shared/logger/sinks.cjs +0 -160
- package/dist/shared/logger/sinks.d.cts +0 -9
- package/dist/shared/logger/sinks.d.ts +0 -9
- package/dist/shared/logger/sinks.js +0 -124
- package/dist/shared/paths/paths.cjs +0 -104
- package/dist/shared/paths/paths.d.cts +0 -10
- package/dist/shared/paths/paths.d.ts +0 -10
- package/dist/shared/paths/paths.js +0 -73
- package/dist/shared/run/api.cjs +0 -28
- package/dist/shared/run/api.d.cts +0 -2
- package/dist/shared/run/api.d.ts +0 -2
- package/dist/shared/run/api.js +0 -4
- package/dist/shared/run/browser.cjs +0 -98
- package/dist/shared/run/browser.d.cts +0 -22
- package/dist/shared/run/browser.d.ts +0 -22
- package/dist/shared/run/browser.js +0 -74
- package/dist/shared/state/index.cjs +0 -38
- package/dist/shared/state/index.d.cts +0 -2
- package/dist/shared/state/index.d.ts +0 -2
- package/dist/shared/state/index.js +0 -16
- package/dist/shared/state/session-state.cjs +0 -85
- package/dist/shared/state/session-state.d.cts +0 -34
- package/dist/shared/state/session-state.d.ts +0 -34
- package/dist/shared/state/session-state.js +0 -56
- package/dist/shared/visualization/ghost-cursor.cjs +0 -174
- package/dist/shared/visualization/ghost-cursor.d.cts +0 -37
- package/dist/shared/visualization/ghost-cursor.d.ts +0 -37
- package/dist/shared/visualization/ghost-cursor.js +0 -145
- package/dist/shared/visualization/highlight.cjs +0 -134
- package/dist/shared/visualization/highlight.d.cts +0 -22
- package/dist/shared/visualization/highlight.d.ts +0 -22
- package/dist/shared/visualization/highlight.js +0 -108
- package/dist/shared/visualization/index.cjs +0 -45
- package/dist/shared/visualization/index.d.cts +0 -3
- package/dist/shared/visualization/index.d.ts +0 -3
- package/dist/shared/visualization/index.js +0 -24
- package/dist/shared/workflow/workflow.cjs +0 -47
- package/dist/shared/workflow/workflow.d.cts +0 -21
- package/dist/shared/workflow/workflow.d.ts +0 -21
- package/dist/shared/workflow/workflow.js +0 -21
package/dist/cli/core/context.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { Logger, createFileLogSink } from "../../shared/logger/index.js";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { cwd } from "node:process";
|
|
4
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { validateSessionName } from "./session.js";
|
|
7
|
-
function getRepoRoot() {
|
|
8
|
-
const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
9
|
-
encoding: "utf-8"
|
|
10
|
-
});
|
|
11
|
-
if (result.status === 0 && result.stdout) {
|
|
12
|
-
return result.stdout.trim();
|
|
13
|
-
}
|
|
14
|
-
return cwd();
|
|
15
|
-
}
|
|
16
|
-
const REPO_ROOT = getRepoRoot();
|
|
17
|
-
const LIBRETTO_CONFIG_DIR = join(REPO_ROOT, ".libretto");
|
|
18
|
-
const LIBRETTO_CONFIG_PATH = join(LIBRETTO_CONFIG_DIR, "config.json");
|
|
19
|
-
const PROFILES_DIR = join(LIBRETTO_CONFIG_DIR, "profiles");
|
|
20
|
-
const LIBRETTO_SESSIONS_DIR = join(LIBRETTO_CONFIG_DIR, "sessions");
|
|
21
|
-
const LIBRETTO_GITIGNORE_PATH = join(LIBRETTO_CONFIG_DIR, ".gitignore");
|
|
22
|
-
const LIBRETTO_GITIGNORE_CONTENT = [
|
|
23
|
-
"# Local libretto runtime state",
|
|
24
|
-
"sessions/",
|
|
25
|
-
"profiles/",
|
|
26
|
-
""
|
|
27
|
-
].join("\n");
|
|
28
|
-
function getSessionDir(session) {
|
|
29
|
-
return join(LIBRETTO_SESSIONS_DIR, session);
|
|
30
|
-
}
|
|
31
|
-
function getSessionStatePath(session) {
|
|
32
|
-
return join(getSessionDir(session), "state.json");
|
|
33
|
-
}
|
|
34
|
-
function getSessionLogsPath(session) {
|
|
35
|
-
return join(getSessionDir(session), "logs.jsonl");
|
|
36
|
-
}
|
|
37
|
-
function getSessionNetworkLogPath(session) {
|
|
38
|
-
return join(getSessionDir(session), "network.jsonl");
|
|
39
|
-
}
|
|
40
|
-
function getSessionActionsLogPath(session) {
|
|
41
|
-
return join(getSessionDir(session), "actions.jsonl");
|
|
42
|
-
}
|
|
43
|
-
function getSessionSnapshotsDir(session) {
|
|
44
|
-
return join(getSessionDir(session), "snapshots");
|
|
45
|
-
}
|
|
46
|
-
function getSessionSnapshotRunDir(session, snapshotRunId) {
|
|
47
|
-
return join(getSessionSnapshotsDir(session), snapshotRunId);
|
|
48
|
-
}
|
|
49
|
-
function ensureLibrettoSetup() {
|
|
50
|
-
mkdirSync(LIBRETTO_CONFIG_DIR, { recursive: true });
|
|
51
|
-
mkdirSync(LIBRETTO_SESSIONS_DIR, { recursive: true });
|
|
52
|
-
mkdirSync(PROFILES_DIR, { recursive: true });
|
|
53
|
-
if (!existsSync(LIBRETTO_GITIGNORE_PATH)) {
|
|
54
|
-
writeFileSync(LIBRETTO_GITIGNORE_PATH, LIBRETTO_GITIGNORE_CONTENT, "utf-8");
|
|
55
|
-
}
|
|
56
|
-
const agentsSkillsDir = join(REPO_ROOT, ".agents", "skills", "libretto");
|
|
57
|
-
const claudeSkillsDir = join(REPO_ROOT, ".claude", "skills", "libretto");
|
|
58
|
-
if (!existsSync(agentsSkillsDir) && !existsSync(claudeSkillsDir)) {
|
|
59
|
-
console.log("[libretto] Skills not installed. Run 'npx libretto init' to complete setup.");
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function createLoggerForSession(session) {
|
|
63
|
-
validateSessionName(session);
|
|
64
|
-
const sessionDir = getSessionDir(session);
|
|
65
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
66
|
-
const logFilePath = getSessionLogsPath(session);
|
|
67
|
-
return new Logger(["libretto-cli"], [createFileLogSink({ filePath: logFilePath })]);
|
|
68
|
-
}
|
|
69
|
-
async function closeLogger(logger) {
|
|
70
|
-
if (!logger) return;
|
|
71
|
-
await logger.close();
|
|
72
|
-
}
|
|
73
|
-
async function withSessionLogger(session, run) {
|
|
74
|
-
const logger = createLoggerForSession(session);
|
|
75
|
-
try {
|
|
76
|
-
return await run(logger);
|
|
77
|
-
} finally {
|
|
78
|
-
await closeLogger(logger);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
let llmClientFactory = null;
|
|
82
|
-
function setLLMClientFactory(factory) {
|
|
83
|
-
llmClientFactory = factory;
|
|
84
|
-
}
|
|
85
|
-
function getLLMClientFactory() {
|
|
86
|
-
return llmClientFactory;
|
|
87
|
-
}
|
|
88
|
-
function maybeConfigureLLMClientFactoryFromEnv() {
|
|
89
|
-
if (llmClientFactory) return;
|
|
90
|
-
const hasAnyCreds = process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
91
|
-
if (!hasAnyCreds) return;
|
|
92
|
-
setLLMClientFactory(async (_logger, model) => {
|
|
93
|
-
const { createLLMClient } = await import("../../shared/llm/index.js");
|
|
94
|
-
return createLLMClient(model);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
export {
|
|
98
|
-
LIBRETTO_CONFIG_DIR,
|
|
99
|
-
LIBRETTO_CONFIG_PATH,
|
|
100
|
-
LIBRETTO_GITIGNORE_PATH,
|
|
101
|
-
LIBRETTO_SESSIONS_DIR,
|
|
102
|
-
PROFILES_DIR,
|
|
103
|
-
REPO_ROOT,
|
|
104
|
-
closeLogger,
|
|
105
|
-
createLoggerForSession,
|
|
106
|
-
ensureLibrettoSetup,
|
|
107
|
-
getLLMClientFactory,
|
|
108
|
-
getSessionActionsLogPath,
|
|
109
|
-
getSessionDir,
|
|
110
|
-
getSessionLogsPath,
|
|
111
|
-
getSessionNetworkLogPath,
|
|
112
|
-
getSessionSnapshotRunDir,
|
|
113
|
-
getSessionSnapshotsDir,
|
|
114
|
-
getSessionStatePath,
|
|
115
|
-
maybeConfigureLLMClientFactoryFromEnv,
|
|
116
|
-
setLLMClientFactory,
|
|
117
|
-
withSessionLogger
|
|
118
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { unlink } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { getSessionDir } from "./context.js";
|
|
5
|
-
function getPauseSignalPaths(session) {
|
|
6
|
-
const sessionDir = getSessionDir(session);
|
|
7
|
-
return {
|
|
8
|
-
pausedSignalPath: join(sessionDir, `${session}.paused`),
|
|
9
|
-
resumeSignalPath: join(sessionDir, `${session}.resume`),
|
|
10
|
-
completedSignalPath: join(sessionDir, `${session}.completed`),
|
|
11
|
-
failedSignalPath: join(sessionDir, `${session}.failed`),
|
|
12
|
-
outputSignalPath: join(sessionDir, `${session}.output`)
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
async function removeSignalIfExists(path) {
|
|
16
|
-
if (!existsSync(path)) return;
|
|
17
|
-
try {
|
|
18
|
-
await unlink(path);
|
|
19
|
-
} catch (error) {
|
|
20
|
-
const code = error.code;
|
|
21
|
-
if (code !== "ENOENT") {
|
|
22
|
-
throw error;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
export {
|
|
27
|
-
getPauseSignalPaths,
|
|
28
|
-
removeSignalIfExists
|
|
29
|
-
};
|
|
@@ -1,491 +0,0 @@
|
|
|
1
|
-
async function installSessionTelemetry(options) {
|
|
2
|
-
const STATIC_EXT_RE = /\.(css|js|png|jpg|jpeg|gif|woff|woff2|ttf|ico|svg)(\?|$)/i;
|
|
3
|
-
const { context, initialPage, logAction, logNetwork } = options;
|
|
4
|
-
const includeUserDomActions = options.includeUserDomActions ?? false;
|
|
5
|
-
const pageIdCache = /* @__PURE__ */ new WeakMap();
|
|
6
|
-
const wrappedPages = /* @__PURE__ */ new WeakSet();
|
|
7
|
-
const exposedPages = /* @__PURE__ */ new WeakSet();
|
|
8
|
-
const resolvePageId = async (page) => {
|
|
9
|
-
if (pageIdCache.has(page)) return pageIdCache.get(page);
|
|
10
|
-
const cdpSession = await context.newCDPSession(page);
|
|
11
|
-
try {
|
|
12
|
-
const targetInfo = await cdpSession.send("Target.getTargetInfo");
|
|
13
|
-
const targetId = targetInfo?.targetInfo?.targetId;
|
|
14
|
-
if (typeof targetId !== "string" || targetId.length === 0) {
|
|
15
|
-
throw new Error(`Could not resolve target id for page at URL "${page.url()}".`);
|
|
16
|
-
}
|
|
17
|
-
pageIdCache.set(page, targetId);
|
|
18
|
-
return targetId;
|
|
19
|
-
} finally {
|
|
20
|
-
await cdpSession.detach();
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
const emitAction = (entry) => {
|
|
24
|
-
logAction({
|
|
25
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26
|
-
...entry
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
const emitNetwork = (entry) => {
|
|
30
|
-
logNetwork({
|
|
31
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32
|
-
...entry
|
|
33
|
-
});
|
|
34
|
-
};
|
|
35
|
-
const markApiActionInProgress = async (page, inProgress) => {
|
|
36
|
-
await page.evaluate((flag) => {
|
|
37
|
-
window.__btApiActionInProgress = flag;
|
|
38
|
-
}, inProgress);
|
|
39
|
-
};
|
|
40
|
-
const wrapLocator = (locator, page, pageId) => {
|
|
41
|
-
if (locator.__librettoActionLogged) return locator;
|
|
42
|
-
locator.__librettoActionLogged = true;
|
|
43
|
-
const locatorActionMethods = [
|
|
44
|
-
"click",
|
|
45
|
-
"dblclick",
|
|
46
|
-
"fill",
|
|
47
|
-
"type",
|
|
48
|
-
"press",
|
|
49
|
-
"check",
|
|
50
|
-
"uncheck",
|
|
51
|
-
"selectOption",
|
|
52
|
-
"hover",
|
|
53
|
-
"focus",
|
|
54
|
-
"scrollIntoViewIfNeeded",
|
|
55
|
-
"waitFor",
|
|
56
|
-
"innerHTML",
|
|
57
|
-
"innerText",
|
|
58
|
-
"textContent",
|
|
59
|
-
"inputValue",
|
|
60
|
-
"isChecked",
|
|
61
|
-
"isDisabled",
|
|
62
|
-
"isEditable",
|
|
63
|
-
"isEnabled",
|
|
64
|
-
"isHidden",
|
|
65
|
-
"isVisible",
|
|
66
|
-
"count",
|
|
67
|
-
"boundingBox",
|
|
68
|
-
"screenshot",
|
|
69
|
-
"evaluate",
|
|
70
|
-
"evaluateAll",
|
|
71
|
-
"evaluateHandle",
|
|
72
|
-
"getAttribute",
|
|
73
|
-
"dispatchEvent",
|
|
74
|
-
"setInputFiles",
|
|
75
|
-
"selectText",
|
|
76
|
-
"dragTo",
|
|
77
|
-
"highlight",
|
|
78
|
-
"tap"
|
|
79
|
-
];
|
|
80
|
-
const locatorReturningMethods = [
|
|
81
|
-
"first",
|
|
82
|
-
"last",
|
|
83
|
-
"locator",
|
|
84
|
-
"getByRole",
|
|
85
|
-
"getByText",
|
|
86
|
-
"getByLabel",
|
|
87
|
-
"getByPlaceholder",
|
|
88
|
-
"getByAltText",
|
|
89
|
-
"getByTitle",
|
|
90
|
-
"getByTestId",
|
|
91
|
-
"filter",
|
|
92
|
-
"and",
|
|
93
|
-
"or"
|
|
94
|
-
];
|
|
95
|
-
for (const actMethod of locatorActionMethods) {
|
|
96
|
-
if (typeof locator[actMethod] !== "function") continue;
|
|
97
|
-
const originalAction = locator[actMethod].bind(locator);
|
|
98
|
-
locator[actMethod] = async (...actionArgs) => {
|
|
99
|
-
const start = Date.now();
|
|
100
|
-
await markApiActionInProgress(page, true);
|
|
101
|
-
try {
|
|
102
|
-
const result = await originalAction(...actionArgs);
|
|
103
|
-
emitAction({
|
|
104
|
-
pageId,
|
|
105
|
-
action: actMethod,
|
|
106
|
-
source: "agent",
|
|
107
|
-
selector: "locator",
|
|
108
|
-
value: actionArgs[0] !== void 0 ? String(actionArgs[0]).slice(0, 100) : void 0,
|
|
109
|
-
duration: Date.now() - start,
|
|
110
|
-
success: true
|
|
111
|
-
});
|
|
112
|
-
return result;
|
|
113
|
-
} catch (error) {
|
|
114
|
-
emitAction({
|
|
115
|
-
pageId,
|
|
116
|
-
action: actMethod,
|
|
117
|
-
source: "agent",
|
|
118
|
-
selector: "locator",
|
|
119
|
-
duration: Date.now() - start,
|
|
120
|
-
success: false,
|
|
121
|
-
error: error?.message ?? String(error)
|
|
122
|
-
});
|
|
123
|
-
throw error;
|
|
124
|
-
} finally {
|
|
125
|
-
await markApiActionInProgress(page, false);
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
for (const method of locatorReturningMethods) {
|
|
130
|
-
if (typeof locator[method] !== "function") continue;
|
|
131
|
-
const originalMethod = locator[method].bind(locator);
|
|
132
|
-
locator[method] = (...args) => {
|
|
133
|
-
const child = originalMethod(...args);
|
|
134
|
-
return wrapLocator(child, page, pageId);
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
if (typeof locator.nth === "function") {
|
|
138
|
-
const originalNth = locator.nth.bind(locator);
|
|
139
|
-
locator.nth = (index) => {
|
|
140
|
-
const child = originalNth(index);
|
|
141
|
-
return wrapLocator(child, page, pageId);
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
if (typeof locator.all === "function") {
|
|
145
|
-
const originalAll = locator.all.bind(locator);
|
|
146
|
-
locator.all = async () => {
|
|
147
|
-
const items = await originalAll();
|
|
148
|
-
return items.map((item) => wrapLocator(item, page, pageId));
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
return locator;
|
|
152
|
-
};
|
|
153
|
-
const installUserDomTracking = async (page, pageId) => {
|
|
154
|
-
if (exposedPages.has(page)) return;
|
|
155
|
-
exposedPages.add(page);
|
|
156
|
-
await page.exposeFunction("__btActionLog", (jsonStr) => {
|
|
157
|
-
const parsed = JSON.parse(jsonStr);
|
|
158
|
-
emitAction({
|
|
159
|
-
pageId,
|
|
160
|
-
source: "user",
|
|
161
|
-
...parsed
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
await page.addInitScript(() => {
|
|
165
|
-
if (window.__btDomListenersInstalled) return;
|
|
166
|
-
window.__btDomListenersInstalled = true;
|
|
167
|
-
const identify = (el) => {
|
|
168
|
-
if (!el || !el.tagName) return "";
|
|
169
|
-
const testId = el.getAttribute("data-testid");
|
|
170
|
-
if (testId) return `[data-testid="${testId}"]`;
|
|
171
|
-
const role = el.getAttribute("role") || "";
|
|
172
|
-
const id = el.id;
|
|
173
|
-
if (role && id) return `${role}#${id}`;
|
|
174
|
-
const label = el.getAttribute("aria-label") || (el.textContent || "").trim().slice(0, 30) || "";
|
|
175
|
-
if (role && label) return `${role} "${label}"`;
|
|
176
|
-
const tag = el.tagName.toLowerCase();
|
|
177
|
-
const cls = el.className && typeof el.className === "string" ? "." + el.className.trim().split(/\s+/).slice(0, 2).join(".") : "";
|
|
178
|
-
return `${tag}${cls}`;
|
|
179
|
-
};
|
|
180
|
-
let clickTimer = null;
|
|
181
|
-
let pendingClick = null;
|
|
182
|
-
document.addEventListener(
|
|
183
|
-
"click",
|
|
184
|
-
(event) => {
|
|
185
|
-
if (window.__btApiActionInProgress) return;
|
|
186
|
-
const target = event.target;
|
|
187
|
-
const selector = identify(target);
|
|
188
|
-
if (target?.type === "checkbox") {
|
|
189
|
-
window.__btActionLog(
|
|
190
|
-
JSON.stringify({
|
|
191
|
-
action: target.checked ? "check" : "uncheck",
|
|
192
|
-
selector,
|
|
193
|
-
success: true
|
|
194
|
-
})
|
|
195
|
-
);
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
pendingClick = { selector };
|
|
199
|
-
if (clickTimer) clearTimeout(clickTimer);
|
|
200
|
-
clickTimer = setTimeout(() => {
|
|
201
|
-
if (pendingClick) {
|
|
202
|
-
window.__btActionLog(
|
|
203
|
-
JSON.stringify({
|
|
204
|
-
action: "click",
|
|
205
|
-
selector: pendingClick.selector,
|
|
206
|
-
success: true
|
|
207
|
-
})
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
pendingClick = null;
|
|
211
|
-
clickTimer = null;
|
|
212
|
-
}, 200);
|
|
213
|
-
},
|
|
214
|
-
true
|
|
215
|
-
);
|
|
216
|
-
document.addEventListener(
|
|
217
|
-
"dblclick",
|
|
218
|
-
(event) => {
|
|
219
|
-
if (window.__btApiActionInProgress) return;
|
|
220
|
-
if (clickTimer) {
|
|
221
|
-
clearTimeout(clickTimer);
|
|
222
|
-
clickTimer = null;
|
|
223
|
-
pendingClick = null;
|
|
224
|
-
}
|
|
225
|
-
const selector = identify(event.target);
|
|
226
|
-
window.__btActionLog(
|
|
227
|
-
JSON.stringify({ action: "dblclick", selector, success: true })
|
|
228
|
-
);
|
|
229
|
-
},
|
|
230
|
-
true
|
|
231
|
-
);
|
|
232
|
-
const inputTimers = /* @__PURE__ */ new WeakMap();
|
|
233
|
-
document.addEventListener(
|
|
234
|
-
"input",
|
|
235
|
-
(event) => {
|
|
236
|
-
if (window.__btApiActionInProgress) return;
|
|
237
|
-
const target = event.target;
|
|
238
|
-
const selector = identify(target);
|
|
239
|
-
if (target.tagName === "SELECT") {
|
|
240
|
-
window.__btActionLog(
|
|
241
|
-
JSON.stringify({
|
|
242
|
-
action: "selectOption",
|
|
243
|
-
selector,
|
|
244
|
-
value: target.value,
|
|
245
|
-
success: true
|
|
246
|
-
})
|
|
247
|
-
);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
const existing = inputTimers.get(target);
|
|
251
|
-
if (existing) clearTimeout(existing);
|
|
252
|
-
inputTimers.set(
|
|
253
|
-
target,
|
|
254
|
-
setTimeout(() => {
|
|
255
|
-
inputTimers.delete(target);
|
|
256
|
-
window.__btActionLog(
|
|
257
|
-
JSON.stringify({
|
|
258
|
-
action: "fill",
|
|
259
|
-
selector,
|
|
260
|
-
value: (target.value || "").slice(0, 100),
|
|
261
|
-
success: true
|
|
262
|
-
})
|
|
263
|
-
);
|
|
264
|
-
}, 500)
|
|
265
|
-
);
|
|
266
|
-
},
|
|
267
|
-
true
|
|
268
|
-
);
|
|
269
|
-
const specialKeys = /* @__PURE__ */ new Set([
|
|
270
|
-
"Enter",
|
|
271
|
-
"Escape",
|
|
272
|
-
"Tab",
|
|
273
|
-
"Backspace",
|
|
274
|
-
"Delete",
|
|
275
|
-
"ArrowUp",
|
|
276
|
-
"ArrowDown",
|
|
277
|
-
"ArrowLeft",
|
|
278
|
-
"ArrowRight",
|
|
279
|
-
"Home",
|
|
280
|
-
"End",
|
|
281
|
-
"PageUp",
|
|
282
|
-
"PageDown",
|
|
283
|
-
"F1",
|
|
284
|
-
"F2",
|
|
285
|
-
"F3",
|
|
286
|
-
"F4",
|
|
287
|
-
"F5",
|
|
288
|
-
"F6",
|
|
289
|
-
"F7",
|
|
290
|
-
"F8",
|
|
291
|
-
"F9",
|
|
292
|
-
"F10",
|
|
293
|
-
"F11",
|
|
294
|
-
"F12"
|
|
295
|
-
]);
|
|
296
|
-
document.addEventListener(
|
|
297
|
-
"keydown",
|
|
298
|
-
(event) => {
|
|
299
|
-
if (window.__btApiActionInProgress) return;
|
|
300
|
-
const isShortcut = event.ctrlKey || event.metaKey || event.altKey;
|
|
301
|
-
if (!isShortcut && !specialKeys.has(event.key)) return;
|
|
302
|
-
const selector = identify(event.target);
|
|
303
|
-
const keyDesc = (event.ctrlKey ? "Ctrl+" : "") + (event.metaKey ? "Meta+" : "") + (event.altKey ? "Alt+" : "") + (event.shiftKey ? "Shift+" : "") + event.key;
|
|
304
|
-
window.__btActionLog(
|
|
305
|
-
JSON.stringify({
|
|
306
|
-
action: "press",
|
|
307
|
-
selector,
|
|
308
|
-
value: keyDesc,
|
|
309
|
-
success: true
|
|
310
|
-
})
|
|
311
|
-
);
|
|
312
|
-
},
|
|
313
|
-
true
|
|
314
|
-
);
|
|
315
|
-
let scrollTimer = null;
|
|
316
|
-
document.addEventListener(
|
|
317
|
-
"scroll",
|
|
318
|
-
() => {
|
|
319
|
-
if (window.__btApiActionInProgress) return;
|
|
320
|
-
if (scrollTimer) clearTimeout(scrollTimer);
|
|
321
|
-
scrollTimer = setTimeout(() => {
|
|
322
|
-
scrollTimer = null;
|
|
323
|
-
window.__btActionLog(
|
|
324
|
-
JSON.stringify({
|
|
325
|
-
action: "scroll",
|
|
326
|
-
selector: "document",
|
|
327
|
-
value: `y=${window.scrollY}`,
|
|
328
|
-
success: true
|
|
329
|
-
})
|
|
330
|
-
);
|
|
331
|
-
}, 300);
|
|
332
|
-
},
|
|
333
|
-
true
|
|
334
|
-
);
|
|
335
|
-
});
|
|
336
|
-
};
|
|
337
|
-
const wrapPageActions = (page, pageId) => {
|
|
338
|
-
if (wrappedPages.has(page)) return;
|
|
339
|
-
wrappedPages.add(page);
|
|
340
|
-
const pageActions = [
|
|
341
|
-
"click",
|
|
342
|
-
"dblclick",
|
|
343
|
-
"fill",
|
|
344
|
-
"type",
|
|
345
|
-
"press",
|
|
346
|
-
"check",
|
|
347
|
-
"uncheck",
|
|
348
|
-
"selectOption",
|
|
349
|
-
"hover",
|
|
350
|
-
"focus"
|
|
351
|
-
];
|
|
352
|
-
const navActions = ["goto", "reload", "goBack", "goForward"];
|
|
353
|
-
const locatorFactories = [
|
|
354
|
-
"locator",
|
|
355
|
-
"getByRole",
|
|
356
|
-
"getByText",
|
|
357
|
-
"getByLabel",
|
|
358
|
-
"getByPlaceholder",
|
|
359
|
-
"getByAltText",
|
|
360
|
-
"getByTitle",
|
|
361
|
-
"getByTestId"
|
|
362
|
-
];
|
|
363
|
-
for (const method of pageActions) {
|
|
364
|
-
const originalMethod = page[method].bind(page);
|
|
365
|
-
page[method] = async (...args) => {
|
|
366
|
-
const start = Date.now();
|
|
367
|
-
await markApiActionInProgress(page, true);
|
|
368
|
-
try {
|
|
369
|
-
const result = await originalMethod(...args);
|
|
370
|
-
emitAction({
|
|
371
|
-
pageId,
|
|
372
|
-
action: method,
|
|
373
|
-
source: "agent",
|
|
374
|
-
selector: typeof args[0] === "string" ? args[0] : void 0,
|
|
375
|
-
value: args[1] !== void 0 ? String(args[1]).slice(0, 100) : void 0,
|
|
376
|
-
duration: Date.now() - start,
|
|
377
|
-
success: true
|
|
378
|
-
});
|
|
379
|
-
return result;
|
|
380
|
-
} catch (error) {
|
|
381
|
-
emitAction({
|
|
382
|
-
pageId,
|
|
383
|
-
action: method,
|
|
384
|
-
source: "agent",
|
|
385
|
-
selector: typeof args[0] === "string" ? args[0] : void 0,
|
|
386
|
-
duration: Date.now() - start,
|
|
387
|
-
success: false,
|
|
388
|
-
error: error?.message ?? String(error)
|
|
389
|
-
});
|
|
390
|
-
throw error;
|
|
391
|
-
} finally {
|
|
392
|
-
await markApiActionInProgress(page, false);
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
for (const method of navActions) {
|
|
397
|
-
const originalMethod = page[method].bind(page);
|
|
398
|
-
page[method] = async (...args) => {
|
|
399
|
-
const start = Date.now();
|
|
400
|
-
try {
|
|
401
|
-
const result = await originalMethod(...args);
|
|
402
|
-
emitAction({
|
|
403
|
-
pageId,
|
|
404
|
-
action: method,
|
|
405
|
-
source: "agent",
|
|
406
|
-
url: typeof args[0] === "string" ? args[0] : page.url(),
|
|
407
|
-
duration: Date.now() - start,
|
|
408
|
-
success: true
|
|
409
|
-
});
|
|
410
|
-
return result;
|
|
411
|
-
} catch (error) {
|
|
412
|
-
emitAction({
|
|
413
|
-
pageId,
|
|
414
|
-
action: method,
|
|
415
|
-
source: "agent",
|
|
416
|
-
url: typeof args[0] === "string" ? args[0] : void 0,
|
|
417
|
-
duration: Date.now() - start,
|
|
418
|
-
success: false,
|
|
419
|
-
error: error?.message ?? String(error)
|
|
420
|
-
});
|
|
421
|
-
throw error;
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
for (const factory of locatorFactories) {
|
|
426
|
-
const originalFactory = page[factory].bind(page);
|
|
427
|
-
page[factory] = (...factoryArgs) => {
|
|
428
|
-
const locator = originalFactory(...factoryArgs);
|
|
429
|
-
return wrapLocator(locator, page, pageId);
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
const installForPage = async (page) => {
|
|
434
|
-
const pageId = await resolvePageId(page);
|
|
435
|
-
wrapPageActions(page, pageId);
|
|
436
|
-
if (includeUserDomActions) {
|
|
437
|
-
await installUserDomTracking(page, pageId);
|
|
438
|
-
}
|
|
439
|
-
page.on("response", async (response) => {
|
|
440
|
-
const request = response.request();
|
|
441
|
-
const url = request.url();
|
|
442
|
-
if (STATIC_EXT_RE.test(url) || url.startsWith("chrome-extension://")) return;
|
|
443
|
-
emitNetwork({
|
|
444
|
-
pageId,
|
|
445
|
-
method: request.method(),
|
|
446
|
-
url,
|
|
447
|
-
status: response.status(),
|
|
448
|
-
contentType: response.headers()["content-type"] ?? null,
|
|
449
|
-
postData: request.method() === "POST" || request.method() === "PUT" || request.method() === "PATCH" ? (request.postData() ?? "").substring(0, 2e3) : void 0,
|
|
450
|
-
responseBody: null,
|
|
451
|
-
size: null,
|
|
452
|
-
durationMs: null
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
page.on("framenavigated", (frame) => {
|
|
456
|
-
if (frame !== page.mainFrame()) return;
|
|
457
|
-
emitAction({
|
|
458
|
-
pageId,
|
|
459
|
-
action: "navigate",
|
|
460
|
-
source: "agent",
|
|
461
|
-
url: frame.url(),
|
|
462
|
-
success: true
|
|
463
|
-
});
|
|
464
|
-
});
|
|
465
|
-
page.on("popup", (popup) => {
|
|
466
|
-
emitAction({
|
|
467
|
-
pageId,
|
|
468
|
-
action: "popup",
|
|
469
|
-
source: "agent",
|
|
470
|
-
url: popup.url(),
|
|
471
|
-
success: true
|
|
472
|
-
});
|
|
473
|
-
});
|
|
474
|
-
page.on("dialog", (dialog) => {
|
|
475
|
-
emitAction({
|
|
476
|
-
pageId,
|
|
477
|
-
action: "dialog",
|
|
478
|
-
source: "agent",
|
|
479
|
-
value: `${dialog.type()}: ${dialog.message().slice(0, 500)}`,
|
|
480
|
-
success: true
|
|
481
|
-
});
|
|
482
|
-
});
|
|
483
|
-
};
|
|
484
|
-
await installForPage(initialPage);
|
|
485
|
-
context.on("page", (newPage) => {
|
|
486
|
-
void installForPage(newPage);
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
export {
|
|
490
|
-
installSessionTelemetry
|
|
491
|
-
};
|