site-agent-pro 1.0.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 +689 -0
- package/dist/auth/credentialStore.js +62 -0
- package/dist/auth/inbox.js +193 -0
- package/dist/auth/profile.js +379 -0
- package/dist/auth/runner.js +1124 -0
- package/dist/backend/dashboardData.js +194 -0
- package/dist/backend/runArtifacts.js +48 -0
- package/dist/backend/runRepository.js +93 -0
- package/dist/bin.js +2 -0
- package/dist/cli/backfillSiteChecks.js +143 -0
- package/dist/cli/run.js +309 -0
- package/dist/cli/trade.js +69 -0
- package/dist/config.js +199 -0
- package/dist/core/agentProfiles.js +55 -0
- package/dist/core/aggregateReport.js +382 -0
- package/dist/core/audit.js +30 -0
- package/dist/core/customTaskSuite.js +148 -0
- package/dist/core/evaluator.js +217 -0
- package/dist/core/executor.js +788 -0
- package/dist/core/fallbackReport.js +335 -0
- package/dist/core/formHeuristics.js +411 -0
- package/dist/core/gameplaySummary.js +164 -0
- package/dist/core/interaction.js +202 -0
- package/dist/core/pageState.js +201 -0
- package/dist/core/planner.js +1669 -0
- package/dist/core/processSubmissionBatch.js +204 -0
- package/dist/core/runAuditJob.js +170 -0
- package/dist/core/runner.js +2352 -0
- package/dist/core/siteBrief.js +107 -0
- package/dist/core/siteChecks.js +1526 -0
- package/dist/core/taskDirectives.js +279 -0
- package/dist/core/taskHeuristics.js +263 -0
- package/dist/dashboard/client.js +1256 -0
- package/dist/dashboard/contracts.js +95 -0
- package/dist/dashboard/narrative.js +277 -0
- package/dist/dashboard/server.js +458 -0
- package/dist/dashboard/theme.js +888 -0
- package/dist/index.js +84 -0
- package/dist/llm/client.js +188 -0
- package/dist/paystack/account.js +123 -0
- package/dist/paystack/client.js +100 -0
- package/dist/paystack/index.js +13 -0
- package/dist/paystack/test-paystack.js +83 -0
- package/dist/paystack/transfer.js +138 -0
- package/dist/paystack/types.js +74 -0
- package/dist/paystack/webhook.js +121 -0
- package/dist/prompts/browserAgent.js +124 -0
- package/dist/prompts/reviewer.js +71 -0
- package/dist/reporting/clickReplay.js +290 -0
- package/dist/reporting/html.js +930 -0
- package/dist/reporting/markdown.js +238 -0
- package/dist/reporting/template.js +1141 -0
- package/dist/schemas/types.js +361 -0
- package/dist/submissions/customTasks.js +196 -0
- package/dist/submissions/html.js +770 -0
- package/dist/submissions/model.js +56 -0
- package/dist/submissions/publicUrl.js +76 -0
- package/dist/submissions/service.js +74 -0
- package/dist/submissions/store.js +37 -0
- package/dist/submissions/types.js +65 -0
- package/dist/trade/engine.js +241 -0
- package/dist/trade/evm/erc20.js +44 -0
- package/dist/trade/extractor.js +148 -0
- package/dist/trade/policy.js +35 -0
- package/dist/trade/session.js +31 -0
- package/dist/trade/types.js +107 -0
- package/dist/trade/validator.js +148 -0
- package/dist/utils/files.js +59 -0
- package/dist/utils/log.js +24 -0
- package/dist/utils/playwrightCompat.js +14 -0
- package/dist/utils/time.js +3 -0
- package/dist/wallet/provider.js +345 -0
- package/dist/wallet/relay.js +129 -0
- package/dist/wallet/wallet.js +178 -0
- package/docs/01-installation.md +134 -0
- package/docs/02-running-your-first-audit.md +136 -0
- package/docs/03-configuration.md +233 -0
- package/docs/04-how-the-agent-thinks.md +41 -0
- package/docs/05-extending-personas-and-tasks.md +42 -0
- package/docs/06-hardening-for-production.md +92 -0
- package/package.json +60 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { buildAgentVariants } from "./agentProfiles.js";
|
|
3
|
+
import { createAggregateRun } from "./aggregateReport.js";
|
|
4
|
+
import { buildCustomTaskSuite } from "./customTaskSuite.js";
|
|
5
|
+
import { runAuditJob } from "./runAuditJob.js";
|
|
6
|
+
import { SUBMISSION_TASKS_REQUIRED_MESSAGE } from "../submissions/customTasks.js";
|
|
7
|
+
import { SubmissionSchema } from "../submissions/types.js";
|
|
8
|
+
export async function processSubmissionBatch(args) {
|
|
9
|
+
if (args.submission.customTasks.length === 0) {
|
|
10
|
+
const failedSubmission = SubmissionSchema.parse({
|
|
11
|
+
...args.submission,
|
|
12
|
+
status: "failed",
|
|
13
|
+
startedAt: args.submission.startedAt ?? new Date().toISOString(),
|
|
14
|
+
completedAt: new Date().toISOString(),
|
|
15
|
+
error: SUBMISSION_TASKS_REQUIRED_MESSAGE,
|
|
16
|
+
runId: null,
|
|
17
|
+
runDir: null,
|
|
18
|
+
reportSummary: null,
|
|
19
|
+
overallScore: null
|
|
20
|
+
});
|
|
21
|
+
await args.writeSubmission(failedSubmission);
|
|
22
|
+
return failedSubmission;
|
|
23
|
+
}
|
|
24
|
+
const baseSuite = buildCustomTaskSuite(args.submission.customTasks);
|
|
25
|
+
const variants = buildAgentVariants(args.submission.agentCount, baseSuite);
|
|
26
|
+
let currentSubmission = SubmissionSchema.parse({
|
|
27
|
+
...args.submission,
|
|
28
|
+
agentCount: args.submission.agentCount || variants.length,
|
|
29
|
+
agentRuns: args.submission.agentRuns.length > 0 ? args.submission.agentRuns : variants.map((variant) => ({
|
|
30
|
+
id: variant.id,
|
|
31
|
+
index: variant.index,
|
|
32
|
+
label: variant.label,
|
|
33
|
+
profileLabel: variant.profileLabel,
|
|
34
|
+
personaName: variant.personaName,
|
|
35
|
+
personaVariantKey: variant.personaVariantKey,
|
|
36
|
+
status: "queued",
|
|
37
|
+
startedAt: null,
|
|
38
|
+
completedAt: null,
|
|
39
|
+
runId: null,
|
|
40
|
+
runDir: null,
|
|
41
|
+
error: null,
|
|
42
|
+
reportSummary: null,
|
|
43
|
+
overallScore: null
|
|
44
|
+
}))
|
|
45
|
+
});
|
|
46
|
+
let mutationChain = Promise.resolve();
|
|
47
|
+
const syncCounts = (submission) => SubmissionSchema.parse({
|
|
48
|
+
...submission,
|
|
49
|
+
completedAgentCount: submission.agentRuns.filter((agentRun) => agentRun.status === "completed").length,
|
|
50
|
+
failedAgentCount: submission.agentRuns.filter((agentRun) => agentRun.status === "failed").length
|
|
51
|
+
});
|
|
52
|
+
const commit = async (mutator) => {
|
|
53
|
+
mutationChain = mutationChain.then(async () => {
|
|
54
|
+
const nextSubmission = structuredClone(currentSubmission);
|
|
55
|
+
mutator(nextSubmission);
|
|
56
|
+
currentSubmission = syncCounts(nextSubmission);
|
|
57
|
+
await args.writeSubmission(currentSubmission);
|
|
58
|
+
});
|
|
59
|
+
await mutationChain;
|
|
60
|
+
return currentSubmission;
|
|
61
|
+
};
|
|
62
|
+
const batchStartedAt = new Date().toISOString();
|
|
63
|
+
await commit((draft) => {
|
|
64
|
+
draft.status = "running";
|
|
65
|
+
draft.startedAt = batchStartedAt;
|
|
66
|
+
draft.completedAt = null;
|
|
67
|
+
draft.error = null;
|
|
68
|
+
draft.runId = null;
|
|
69
|
+
draft.runDir = null;
|
|
70
|
+
draft.reportSummary = null;
|
|
71
|
+
draft.overallScore = null;
|
|
72
|
+
draft.agentRuns = draft.agentRuns.map((agentRun) => ({
|
|
73
|
+
...agentRun,
|
|
74
|
+
status: "queued",
|
|
75
|
+
startedAt: null,
|
|
76
|
+
completedAt: null,
|
|
77
|
+
runId: null,
|
|
78
|
+
runDir: null,
|
|
79
|
+
error: null,
|
|
80
|
+
reportSummary: null,
|
|
81
|
+
overallScore: null
|
|
82
|
+
}));
|
|
83
|
+
});
|
|
84
|
+
const agentOutcomes = await Promise.all(variants.map(async (variant) => {
|
|
85
|
+
const agentStartedAt = new Date().toISOString();
|
|
86
|
+
await commit((draft) => {
|
|
87
|
+
draft.agentRuns = draft.agentRuns.map((agentRun) => agentRun.id === variant.id
|
|
88
|
+
? {
|
|
89
|
+
...agentRun,
|
|
90
|
+
status: "running",
|
|
91
|
+
startedAt: agentStartedAt,
|
|
92
|
+
completedAt: null,
|
|
93
|
+
error: null
|
|
94
|
+
}
|
|
95
|
+
: agentRun);
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
const result = await runAuditJob({
|
|
99
|
+
baseUrl: currentSubmission.url,
|
|
100
|
+
suiteOverride: variant.taskSuite,
|
|
101
|
+
headed: currentSubmission.headed,
|
|
102
|
+
mobile: currentSubmission.mobile,
|
|
103
|
+
ignoreHttpsErrors: currentSubmission.ignoreHttpsErrors,
|
|
104
|
+
tradeOptions: currentSubmission.tradeOptions,
|
|
105
|
+
extraInputs: {
|
|
106
|
+
source: args.source,
|
|
107
|
+
submissionId: currentSubmission.id,
|
|
108
|
+
reportToken: currentSubmission.reportToken,
|
|
109
|
+
expiresAt: currentSubmission.expiresAt,
|
|
110
|
+
batchRole: "child",
|
|
111
|
+
parentSubmissionId: currentSubmission.id,
|
|
112
|
+
agentCount: currentSubmission.agentCount,
|
|
113
|
+
instructionText: currentSubmission.instructionText,
|
|
114
|
+
instructionFileName: currentSubmission.instructionFileName,
|
|
115
|
+
agentIndex: variant.index,
|
|
116
|
+
agentLabel: variant.label,
|
|
117
|
+
agentProfileLabel: variant.profileLabel,
|
|
118
|
+
personaVariantKey: variant.personaVariantKey,
|
|
119
|
+
customTasks: currentSubmission.customTasks,
|
|
120
|
+
tradeOptions: currentSubmission.tradeOptions
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
const runId = path.basename(result.runDir);
|
|
124
|
+
await args.uploadRunArtifacts(runId, result.runDir);
|
|
125
|
+
const completedAt = new Date().toISOString();
|
|
126
|
+
await commit((draft) => {
|
|
127
|
+
draft.agentRuns = draft.agentRuns.map((agentRun) => agentRun.id === variant.id
|
|
128
|
+
? {
|
|
129
|
+
...agentRun,
|
|
130
|
+
status: "completed",
|
|
131
|
+
startedAt: agentStartedAt,
|
|
132
|
+
completedAt,
|
|
133
|
+
runId,
|
|
134
|
+
runDir: null,
|
|
135
|
+
error: null,
|
|
136
|
+
reportSummary: result.report.summary,
|
|
137
|
+
overallScore: result.report.overall_score
|
|
138
|
+
}
|
|
139
|
+
: agentRun);
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
agentRun: {
|
|
143
|
+
id: variant.id,
|
|
144
|
+
index: variant.index,
|
|
145
|
+
label: variant.label,
|
|
146
|
+
profileLabel: variant.profileLabel,
|
|
147
|
+
personaName: variant.personaName,
|
|
148
|
+
personaVariantKey: variant.personaVariantKey,
|
|
149
|
+
status: "completed",
|
|
150
|
+
startedAt: agentStartedAt,
|
|
151
|
+
completedAt,
|
|
152
|
+
runId,
|
|
153
|
+
runDir: null,
|
|
154
|
+
error: null,
|
|
155
|
+
reportSummary: result.report.summary,
|
|
156
|
+
overallScore: result.report.overall_score
|
|
157
|
+
},
|
|
158
|
+
report: result.report,
|
|
159
|
+
taskResults: result.execution.taskResults,
|
|
160
|
+
accessibility: result.execution.accessibility,
|
|
161
|
+
siteChecks: result.execution.siteChecks,
|
|
162
|
+
runId
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
const completedAt = new Date().toISOString();
|
|
167
|
+
const message = error instanceof Error ? error.message : "Unknown submission failure";
|
|
168
|
+
await commit((draft) => {
|
|
169
|
+
draft.agentRuns = draft.agentRuns.map((agentRun) => agentRun.id === variant.id
|
|
170
|
+
? {
|
|
171
|
+
...agentRun,
|
|
172
|
+
status: "failed",
|
|
173
|
+
startedAt: agentStartedAt,
|
|
174
|
+
completedAt,
|
|
175
|
+
error: message
|
|
176
|
+
}
|
|
177
|
+
: agentRun);
|
|
178
|
+
});
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}));
|
|
182
|
+
const completedOutcomes = agentOutcomes.filter((outcome) => outcome !== null);
|
|
183
|
+
if (completedOutcomes.length === 0) {
|
|
184
|
+
await commit((draft) => {
|
|
185
|
+
draft.status = "failed";
|
|
186
|
+
draft.completedAt = new Date().toISOString();
|
|
187
|
+
draft.error = `All ${draft.agentCount} agent runs failed before an aggregate report could be produced.`;
|
|
188
|
+
});
|
|
189
|
+
return currentSubmission;
|
|
190
|
+
}
|
|
191
|
+
const aggregateSubmissionSnapshot = structuredClone(currentSubmission);
|
|
192
|
+
const aggregateRun = createAggregateRun(aggregateSubmissionSnapshot, completedOutcomes);
|
|
193
|
+
await args.uploadRunArtifacts(aggregateRun.runId, aggregateRun.runDir);
|
|
194
|
+
await commit((draft) => {
|
|
195
|
+
draft.status = "completed";
|
|
196
|
+
draft.completedAt = new Date().toISOString();
|
|
197
|
+
draft.runId = aggregateRun.runId;
|
|
198
|
+
draft.runDir = null;
|
|
199
|
+
draft.error = null;
|
|
200
|
+
draft.reportSummary = aggregateRun.report.summary;
|
|
201
|
+
draft.overallScore = aggregateRun.report.overall_score;
|
|
202
|
+
});
|
|
203
|
+
return currentSubmission;
|
|
204
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { getAccessIdentityLabel, runWithAccessIdentityContext } from "../auth/profile.js";
|
|
3
|
+
import { clampRunDurationMs, config, deriveBrowserExecutionBudgetMs, deriveReportingReserveMs, resolveLlmRuntime } from "../config.js";
|
|
4
|
+
import { evaluateRun } from "./evaluator.js";
|
|
5
|
+
import { runTaskSuite } from "./runner.js";
|
|
6
|
+
import { generateClickReplay } from "../reporting/clickReplay.js";
|
|
7
|
+
import { renderHtmlReport } from "../reporting/html.js";
|
|
8
|
+
import { renderMarkdownReport } from "../reporting/markdown.js";
|
|
9
|
+
import { ensureDir, resolveRunDir, writeJson, writeText } from "../utils/files.js";
|
|
10
|
+
function summarizeSessionPath(filePath) {
|
|
11
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
|
12
|
+
const relativePath = path.relative(process.cwd(), resolvedPath);
|
|
13
|
+
return relativePath && relativePath !== "" && !relativePath.startsWith("..")
|
|
14
|
+
? relativePath
|
|
15
|
+
: path.basename(resolvedPath);
|
|
16
|
+
}
|
|
17
|
+
export async function runAuditJob(options) {
|
|
18
|
+
const accessIdentityContext = {
|
|
19
|
+
...(typeof options.extraInputs?.agentIndex === "number" ? { agentIndex: options.extraInputs.agentIndex } : {}),
|
|
20
|
+
...(typeof options.extraInputs?.agentLabel === "string" ? { agentLabel: options.extraInputs.agentLabel } : {}),
|
|
21
|
+
...(typeof options.extraInputs?.agentProfileLabel === "string"
|
|
22
|
+
? { agentProfileLabel: options.extraInputs.agentProfileLabel }
|
|
23
|
+
: {})
|
|
24
|
+
};
|
|
25
|
+
return await runWithAccessIdentityContext(accessIdentityContext, async () => {
|
|
26
|
+
const suite = options.suiteOverride;
|
|
27
|
+
const runDir = options.runDir ?? resolveRunDir(options.baseUrl);
|
|
28
|
+
ensureDir(runDir);
|
|
29
|
+
const inputsPath = path.join(runDir, "inputs.json");
|
|
30
|
+
const startedAt = new Date().toISOString();
|
|
31
|
+
const startedAtMs = Date.now();
|
|
32
|
+
const requestedMaxSessionDurationMs = options.maxSessionDurationMs ?? config.maxSessionDurationMs;
|
|
33
|
+
const maxRunDurationMs = clampRunDurationMs(requestedMaxSessionDurationMs);
|
|
34
|
+
const browserExecutionBudgetMs = deriveBrowserExecutionBudgetMs(maxRunDurationMs);
|
|
35
|
+
const reportingReserveMs = deriveReportingReserveMs(maxRunDurationMs);
|
|
36
|
+
const llmRuntime = resolveLlmRuntime({
|
|
37
|
+
...(options.llmProvider ? { provider: options.llmProvider } : {}),
|
|
38
|
+
...(options.model ? { model: options.model } : {}),
|
|
39
|
+
...(options.ollamaBaseUrl ? { ollamaBaseUrl: options.ollamaBaseUrl } : {})
|
|
40
|
+
});
|
|
41
|
+
const storageStatePath = options.storageStatePath ?? config.playwrightStorageStatePath;
|
|
42
|
+
const saveStorageStatePath = options.saveStorageStatePath;
|
|
43
|
+
const instructionText = typeof options.extraInputs?.instructionText === "string"
|
|
44
|
+
? options.extraInputs.instructionText
|
|
45
|
+
: suite.tasks.map((task) => task.goal).join("\n");
|
|
46
|
+
const accessIdentityName = getAccessIdentityLabel(accessIdentityContext);
|
|
47
|
+
const baseInputs = {
|
|
48
|
+
baseUrl: options.baseUrl,
|
|
49
|
+
persona: suite.persona.name,
|
|
50
|
+
headed: Boolean(options.headed),
|
|
51
|
+
mobile: Boolean(options.mobile),
|
|
52
|
+
ignoreHttpsErrors: Boolean(options.ignoreHttpsErrors),
|
|
53
|
+
llmProvider: llmRuntime.provider,
|
|
54
|
+
storageStateLoaded: Boolean(storageStatePath),
|
|
55
|
+
storageStateSource: storageStatePath ? summarizeSessionPath(storageStatePath) : null,
|
|
56
|
+
saveStorageStateRequested: Boolean(saveStorageStatePath),
|
|
57
|
+
saveStorageStateTarget: saveStorageStatePath ? summarizeSessionPath(saveStorageStatePath) : null,
|
|
58
|
+
model: llmRuntime.model,
|
|
59
|
+
startedAt,
|
|
60
|
+
maxRunDurationMs,
|
|
61
|
+
maxRunDurationSeconds: Math.round(maxRunDurationMs / 1000),
|
|
62
|
+
browserExecutionBudgetMs,
|
|
63
|
+
reportingReserveMs,
|
|
64
|
+
maxRunDurationClamped: maxRunDurationMs !== requestedMaxSessionDurationMs,
|
|
65
|
+
deviceTimezone: config.deviceTimezone,
|
|
66
|
+
synchronizedTimezone: config.deviceTimezone,
|
|
67
|
+
customTasks: suite.tasks.map((task) => task.goal),
|
|
68
|
+
accessIdentityName,
|
|
69
|
+
recordVideo: config.recordVideo,
|
|
70
|
+
...(options.tradeOptions ? { tradeOptions: options.tradeOptions } : {}),
|
|
71
|
+
...(options.extraInputs ?? {})
|
|
72
|
+
};
|
|
73
|
+
writeJson(inputsPath, baseInputs);
|
|
74
|
+
const execution = await runTaskSuite({
|
|
75
|
+
baseUrl: options.baseUrl,
|
|
76
|
+
suite,
|
|
77
|
+
runDir,
|
|
78
|
+
headed: Boolean(options.headed),
|
|
79
|
+
mobile: Boolean(options.mobile),
|
|
80
|
+
ignoreHttpsErrors: Boolean(options.ignoreHttpsErrors),
|
|
81
|
+
storageStatePath,
|
|
82
|
+
saveStorageStatePath,
|
|
83
|
+
maxSessionDurationMs: browserExecutionBudgetMs,
|
|
84
|
+
...(options.tradeOptions ? { tradeOptions: options.tradeOptions } : {}),
|
|
85
|
+
provider: llmRuntime.provider,
|
|
86
|
+
model: llmRuntime.model,
|
|
87
|
+
ollamaBaseUrl: llmRuntime.ollamaBaseUrl
|
|
88
|
+
});
|
|
89
|
+
let clickReplayArtifact = null;
|
|
90
|
+
let clickReplayFrameCount = null;
|
|
91
|
+
let clickReplayDurationMs = null;
|
|
92
|
+
try {
|
|
93
|
+
const clickReplay = await generateClickReplay({
|
|
94
|
+
runDir,
|
|
95
|
+
taskResults: execution.taskResults
|
|
96
|
+
});
|
|
97
|
+
clickReplayArtifact = clickReplay?.artifactName ?? null;
|
|
98
|
+
clickReplayFrameCount = clickReplay?.frameCount ?? null;
|
|
99
|
+
clickReplayDurationMs = clickReplay?.durationMs ?? null;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Replay generation is optional and should never block the main report.
|
|
103
|
+
}
|
|
104
|
+
writeJson(inputsPath, {
|
|
105
|
+
...baseInputs,
|
|
106
|
+
...(execution.siteBrief ? { siteBrief: execution.siteBrief } : {}),
|
|
107
|
+
browserTimezone: execution.browserTimezone,
|
|
108
|
+
synchronizedTimezone: execution.browserTimezone || execution.deviceTimezone,
|
|
109
|
+
...(clickReplayArtifact ? { clickReplayArtifact } : {}),
|
|
110
|
+
...(clickReplayFrameCount !== null ? { clickReplayFrameCount } : {}),
|
|
111
|
+
...(clickReplayDurationMs !== null ? { clickReplayDurationMs } : {})
|
|
112
|
+
});
|
|
113
|
+
const remainingEvaluationBudgetMs = Math.max(0, maxRunDurationMs - (Date.now() - startedAtMs));
|
|
114
|
+
const report = await evaluateRun({
|
|
115
|
+
baseUrl: options.baseUrl,
|
|
116
|
+
suite,
|
|
117
|
+
siteBrief: execution.siteBrief,
|
|
118
|
+
taskResults: execution.taskResults,
|
|
119
|
+
rawEvents: execution.rawEvents,
|
|
120
|
+
accessibility: execution.accessibility,
|
|
121
|
+
mobile: Boolean(options.mobile),
|
|
122
|
+
timeoutMs: remainingEvaluationBudgetMs,
|
|
123
|
+
totalRunDurationMs: maxRunDurationMs,
|
|
124
|
+
llm: {
|
|
125
|
+
provider: llmRuntime.provider,
|
|
126
|
+
model: llmRuntime.model,
|
|
127
|
+
ollamaBaseUrl: llmRuntime.ollamaBaseUrl
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
writeJson(path.join(runDir, "report.json"), report);
|
|
131
|
+
writeText(path.join(runDir, "report.html"), renderHtmlReport({
|
|
132
|
+
website: options.baseUrl,
|
|
133
|
+
persona: suite.persona.name,
|
|
134
|
+
acceptedTasks: suite.tasks.map((task) => task.goal),
|
|
135
|
+
instructionText,
|
|
136
|
+
report,
|
|
137
|
+
taskResults: execution.taskResults,
|
|
138
|
+
accessibility: execution.accessibility,
|
|
139
|
+
siteChecks: execution.siteChecks,
|
|
140
|
+
siteBrief: execution.siteBrief,
|
|
141
|
+
rawEvents: execution.rawEvents,
|
|
142
|
+
runId: path.basename(runDir),
|
|
143
|
+
startedAt,
|
|
144
|
+
mobile: Boolean(options.mobile),
|
|
145
|
+
timeZone: execution.browserTimezone || execution.deviceTimezone,
|
|
146
|
+
clickReplayArtifact
|
|
147
|
+
}));
|
|
148
|
+
writeText(path.join(runDir, "report.md"), renderMarkdownReport({
|
|
149
|
+
website: options.baseUrl,
|
|
150
|
+
persona: suite.persona.name,
|
|
151
|
+
acceptedTasks: suite.tasks.map((task) => task.goal),
|
|
152
|
+
instructionText,
|
|
153
|
+
report,
|
|
154
|
+
taskResults: execution.taskResults,
|
|
155
|
+
accessibility: execution.accessibility,
|
|
156
|
+
siteChecks: execution.siteChecks,
|
|
157
|
+
siteBrief: execution.siteBrief,
|
|
158
|
+
rawEvents: execution.rawEvents,
|
|
159
|
+
startedAt,
|
|
160
|
+
mobile: Boolean(options.mobile),
|
|
161
|
+
timeZone: execution.browserTimezone || execution.deviceTimezone
|
|
162
|
+
}));
|
|
163
|
+
return {
|
|
164
|
+
startedAt,
|
|
165
|
+
runDir,
|
|
166
|
+
report,
|
|
167
|
+
execution
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
}
|