blastproof 0.21.0 → 0.22.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/dist/cli.js +41 -5
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -333,6 +333,9 @@ function referencedEnvVars(text) {
|
|
|
333
333
|
}
|
|
334
334
|
return [...names];
|
|
335
335
|
}
|
|
336
|
+
function placeholdersAsLabels(text) {
|
|
337
|
+
return text.replace(ENV_PLACEHOLDER, (_match, name) => redactionLabel(name));
|
|
338
|
+
}
|
|
336
339
|
function escapeRegExp(value) {
|
|
337
340
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
338
341
|
}
|
|
@@ -343,6 +346,12 @@ var REDACTION_PREFIX = "[redacted";
|
|
|
343
346
|
function redactionLabel(name) {
|
|
344
347
|
return name === void 0 ? `${REDACTION_PREFIX}]` : `${REDACTION_PREFIX} ${name}]`;
|
|
345
348
|
}
|
|
349
|
+
var LABEL = /\[redacted ([A-Za-z_][A-Za-z0-9_]*)\]/g;
|
|
350
|
+
function labelledVariables(text) {
|
|
351
|
+
const names = /* @__PURE__ */ new Set();
|
|
352
|
+
for (const match of text.matchAll(LABEL)) if (match[1]) names.add(match[1]);
|
|
353
|
+
return [...names];
|
|
354
|
+
}
|
|
346
355
|
function compile(secrets, names) {
|
|
347
356
|
return [...secrets].filter(Boolean).sort((a, b) => b.length - a.length).map((value) => ({ value, pattern: secretPattern(value), label: redactionLabel(names?.get(value)) }));
|
|
348
357
|
}
|
|
@@ -1607,8 +1616,10 @@ function parseAgentAction(value) {
|
|
|
1607
1616
|
return parsedAgentActionSchema.safeParse(value);
|
|
1608
1617
|
}
|
|
1609
1618
|
var assertJudgmentSchema = z2.object({
|
|
1610
|
-
|
|
1611
|
-
|
|
1619
|
+
reason: z2.string().describe("One sentence: what the STEP requires to be true now, and whether the snapshot shows it."),
|
|
1620
|
+
pass: z2.boolean().describe(
|
|
1621
|
+
"Whether the snapshot shows the STEP's own outcome. The expectation is only a claim offered in support; it never replaces the step. False if any part of the outcome the step asks for is not shown, or cannot be assessed from this snapshot."
|
|
1622
|
+
)
|
|
1612
1623
|
});
|
|
1613
1624
|
var generatedTestSchema = z2.object({
|
|
1614
1625
|
summary: z2.string().min(1).describe('One line naming the user journey this test covers, e.g. "Applying a discount updates the cart total".'),
|
|
@@ -1665,11 +1676,20 @@ function createBrain(model, generate = generateObject, budget) {
|
|
|
1665
1676
|
return parsed.data;
|
|
1666
1677
|
},
|
|
1667
1678
|
async judge(step, expectation, snapshot, stepHistory) {
|
|
1679
|
+
const asJudged = stepHistory?.map((entry) => ({
|
|
1680
|
+
action: placeholdersAsLabels(entry.action),
|
|
1681
|
+
result: placeholdersAsLabels(entry.result)
|
|
1682
|
+
}));
|
|
1668
1683
|
const result = await countedGenerate(generate, budget, {
|
|
1669
1684
|
model,
|
|
1670
1685
|
schema: assertJudgmentSchema,
|
|
1671
1686
|
system: assertSystemPrompt(),
|
|
1672
|
-
prompt: assertUserPrompt(
|
|
1687
|
+
prompt: assertUserPrompt(
|
|
1688
|
+
placeholdersAsLabels(step),
|
|
1689
|
+
placeholdersAsLabels(expectation),
|
|
1690
|
+
snapshot,
|
|
1691
|
+
asJudged
|
|
1692
|
+
),
|
|
1673
1693
|
temperature: 0
|
|
1674
1694
|
});
|
|
1675
1695
|
const parsed = assertJudgmentSchema.safeParse(result.object);
|
|
@@ -1678,10 +1698,26 @@ function createBrain(model, generate = generateObject, budget) {
|
|
|
1678
1698
|
`Model returned an invalid assert judgment: ${parsed.error.issues[0]?.message ?? "unknown"}`
|
|
1679
1699
|
);
|
|
1680
1700
|
}
|
|
1681
|
-
return parsed.data;
|
|
1701
|
+
return secretMismatch(placeholdersAsLabels(step), snapshot, asJudged, parsed.data);
|
|
1682
1702
|
}
|
|
1683
1703
|
};
|
|
1684
1704
|
}
|
|
1705
|
+
function secretMismatch(judgedStep, snapshot, judgedRecord, judgment) {
|
|
1706
|
+
if (!judgment.pass) return judgment;
|
|
1707
|
+
const onPage = labelledVariables(snapshot);
|
|
1708
|
+
const inRecord = labelledVariables((judgedRecord ?? []).map((e) => `${e.action}
|
|
1709
|
+
${e.result}`).join("\n"));
|
|
1710
|
+
for (const name of labelledVariables(judgedStep)) {
|
|
1711
|
+
const others = onPage.filter((other) => other !== name);
|
|
1712
|
+
if (onPage.includes(name) || inRecord.includes(name) || others.length === 0) continue;
|
|
1713
|
+
const shown = others.map((other) => redactionLabel(other)).join(", ");
|
|
1714
|
+
return {
|
|
1715
|
+
pass: false,
|
|
1716
|
+
reason: `The step names ${redactionLabel(name)}, which appears neither on the page nor in this step's actions, while the page shows ${shown}: a different secret cannot satisfy it. (The judge had said: ${judgment.reason})`
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1719
|
+
return judgment;
|
|
1720
|
+
}
|
|
1685
1721
|
function createPlanner(model, generate = generateObject, budget) {
|
|
1686
1722
|
return {
|
|
1687
1723
|
async planTest(input) {
|
|
@@ -3256,7 +3292,7 @@ function parsePositiveNumber(flag) {
|
|
|
3256
3292
|
};
|
|
3257
3293
|
}
|
|
3258
3294
|
var program = new Command();
|
|
3259
|
-
program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.
|
|
3295
|
+
program.name("blastproof").description("Open-source AI testing agent: plain-English YAML tests executed agentically on a real browser.").version("0.22.0");
|
|
3260
3296
|
program.command("init").description("Scaffold .blastproof/ (config, tests, sample tests) in the current directory").action(async () => {
|
|
3261
3297
|
try {
|
|
3262
3298
|
const result = await initProject(process.cwd());
|