@pcamarajr/scout 0.6.0 → 0.7.1
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 +73 -237
- package/dist/cli.js +121 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/credentials.d.ts +73 -0
- package/dist/credentials.js +196 -0
- package/dist/credentials.js.map +1 -0
- package/dist/engine.js +5 -8
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/runner/agent-tools.d.ts +45 -0
- package/dist/runner/agent-tools.js +151 -0
- package/dist/runner/agent-tools.js.map +1 -0
- package/dist/runner/ai-runner.d.ts +11 -35
- package/dist/runner/ai-runner.js +36 -229
- package/dist/runner/ai-runner.js.map +1 -1
- package/dist/runner/engines/ai-sdk.d.ts +58 -0
- package/dist/runner/engines/ai-sdk.js +144 -0
- package/dist/runner/engines/ai-sdk.js.map +1 -0
- package/dist/runner/engines/claude-agent-sdk.d.ts +11 -0
- package/dist/runner/engines/claude-agent-sdk.js +117 -0
- package/dist/runner/engines/claude-agent-sdk.js.map +1 -0
- package/dist/runner/engines/index.d.ts +20 -0
- package/dist/runner/engines/index.js +28 -0
- package/dist/runner/engines/index.js.map +1 -0
- package/dist/runner/engines/types.d.ts +68 -0
- package/dist/runner/engines/types.js +37 -0
- package/dist/runner/engines/types.js.map +1 -0
- package/package.json +7 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createSdkMcpServer, query, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* The original, trusted engine: runs the verification through the
|
|
4
|
+
* `@anthropic-ai/claude-agent-sdk` subprocess. Behavior here is intentionally
|
|
5
|
+
* IDENTICAL to the pre-refactor ai-runner: bypassPermissions, empty
|
|
6
|
+
* settingSources, allowedTools scoped to the in-process MCP browser server, the
|
|
7
|
+
* query()+drain() loop, and an abort-in-finally so no subprocess outlives a run.
|
|
8
|
+
*/
|
|
9
|
+
export class ClaudeAgentSdkEngine {
|
|
10
|
+
async run(spec) {
|
|
11
|
+
const browserServer = createSdkMcpServer({
|
|
12
|
+
name: "browser",
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
tools: spec.tools.map(toAgentSdkTool),
|
|
15
|
+
});
|
|
16
|
+
const baseOptions = {
|
|
17
|
+
model: spec.model,
|
|
18
|
+
systemPrompt: spec.systemPrompt,
|
|
19
|
+
mcpServers: { browser: browserServer },
|
|
20
|
+
allowedTools: ["mcp__browser__*"],
|
|
21
|
+
tools: [],
|
|
22
|
+
settingSources: [],
|
|
23
|
+
permissionMode: "bypassPermissions",
|
|
24
|
+
};
|
|
25
|
+
const transcript = [];
|
|
26
|
+
let sessionId;
|
|
27
|
+
let endInfo;
|
|
28
|
+
// Drains a query, collecting transcript/session/end info. Always aborts at
|
|
29
|
+
// the end so no claude-agent-sdk subprocess outlives the run.
|
|
30
|
+
const drain = async (q, controller) => {
|
|
31
|
+
try {
|
|
32
|
+
for await (const message of q) {
|
|
33
|
+
if ("session_id" in message && message.session_id)
|
|
34
|
+
sessionId = message.session_id;
|
|
35
|
+
if (message.type === "assistant") {
|
|
36
|
+
for (const block of message.message.content) {
|
|
37
|
+
if (block.type === "text" && block.text.trim())
|
|
38
|
+
transcript.push(block.text.trim());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (message.type === "result") {
|
|
42
|
+
endInfo = {
|
|
43
|
+
subtype: message.subtype,
|
|
44
|
+
numTurns: message.num_turns,
|
|
45
|
+
errors: "errors" in message ? message.errors : undefined,
|
|
46
|
+
};
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
controller.abort();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const mainController = new AbortController();
|
|
56
|
+
try {
|
|
57
|
+
await drain(query({
|
|
58
|
+
prompt: spec.userPrompt,
|
|
59
|
+
options: { ...baseOptions, maxTurns: spec.maxTurns, abortController: mainController },
|
|
60
|
+
}), mainController);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
endInfo = {
|
|
64
|
+
subtype: "sdk_error",
|
|
65
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const resume = async (prompt, maxTurns) => {
|
|
69
|
+
const resumeTranscript = [];
|
|
70
|
+
let resumeEnd;
|
|
71
|
+
if (!sessionId) {
|
|
72
|
+
return { end: { subtype: "no_session" }, transcript: resumeTranscript };
|
|
73
|
+
}
|
|
74
|
+
const rescueController = new AbortController();
|
|
75
|
+
// Reuse drain() so transcript/session/end semantics stay identical, then
|
|
76
|
+
// split off only what the rescue produced for the resume result.
|
|
77
|
+
const beforeLen = transcript.length;
|
|
78
|
+
try {
|
|
79
|
+
await drain(query({
|
|
80
|
+
prompt,
|
|
81
|
+
options: { ...baseOptions, maxTurns, resume: sessionId, abortController: rescueController },
|
|
82
|
+
}), rescueController);
|
|
83
|
+
resumeEnd = endInfo;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
resumeEnd = {
|
|
87
|
+
subtype: "sdk_error",
|
|
88
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
resumeTranscript.push(...transcript.slice(beforeLen));
|
|
92
|
+
return { end: resumeEnd ?? { subtype: "no_session" }, transcript: resumeTranscript };
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
get end() {
|
|
96
|
+
return endInfo ?? { subtype: "sdk_error" };
|
|
97
|
+
},
|
|
98
|
+
transcript,
|
|
99
|
+
resume,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Adapts an engine-neutral ScoutTool into the Agent SDK `tool()` shape. The
|
|
105
|
+
* Agent SDK's tool() wants a raw Zod shape, so we hand it `schema.shape`, and we
|
|
106
|
+
* map our normalized {text, isError} result back into a CallToolResult.
|
|
107
|
+
*/
|
|
108
|
+
function toAgentSdkTool(scoutTool) {
|
|
109
|
+
return tool(scoutTool.name, scoutTool.description, scoutTool.schema.shape, async (args) => {
|
|
110
|
+
const result = await scoutTool.handler(args);
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text", text: result.text }],
|
|
113
|
+
...(result.isError ? { isError: true } : {}),
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=claude-agent-sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-agent-sdk.js","sourceRoot":"","sources":["../../../src/runner/engines/claude-agent-sdk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AiProvider } from "../../credentials.js";
|
|
2
|
+
import { AiSdkEngine } from "./ai-sdk.js";
|
|
3
|
+
import { ClaudeAgentSdkEngine } from "./claude-agent-sdk.js";
|
|
4
|
+
import type { AgentEngine } from "./types.js";
|
|
5
|
+
/** The two engines Scout can run an AI verification through. */
|
|
6
|
+
export type EngineKind = "agent-sdk" | "ai-sdk";
|
|
7
|
+
/**
|
|
8
|
+
* Resolves which engine to use. Explicit preference (SCOUT_ENGINE / config
|
|
9
|
+
* `engine`) wins. Otherwise the default is forward-looking: the trusted Agent
|
|
10
|
+
* SDK for Anthropic, the AI SDK for non-Anthropic providers (which is the only
|
|
11
|
+
* engine that can reach them — still gated by credential detection until those
|
|
12
|
+
* providers are wired up).
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveEngineKind(provider: AiProvider, enginePref: EngineKind | undefined): EngineKind;
|
|
15
|
+
/** Instantiates the engine for a provider + explicit preference. */
|
|
16
|
+
export declare function selectEngine(provider: AiProvider, enginePref: EngineKind | undefined): AgentEngine;
|
|
17
|
+
/** Parses a raw SCOUT_ENGINE / config value, ignoring unknown strings. */
|
|
18
|
+
export declare function parseEngineKind(value: string | undefined): EngineKind | undefined;
|
|
19
|
+
export { AiSdkEngine, ClaudeAgentSdkEngine };
|
|
20
|
+
export type { AgentEngine };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AiSdkEngine } from "./ai-sdk.js";
|
|
2
|
+
import { ClaudeAgentSdkEngine } from "./claude-agent-sdk.js";
|
|
3
|
+
/**
|
|
4
|
+
* Resolves which engine to use. Explicit preference (SCOUT_ENGINE / config
|
|
5
|
+
* `engine`) wins. Otherwise the default is forward-looking: the trusted Agent
|
|
6
|
+
* SDK for Anthropic, the AI SDK for non-Anthropic providers (which is the only
|
|
7
|
+
* engine that can reach them — still gated by credential detection until those
|
|
8
|
+
* providers are wired up).
|
|
9
|
+
*/
|
|
10
|
+
export function resolveEngineKind(provider, enginePref) {
|
|
11
|
+
if (enginePref)
|
|
12
|
+
return enginePref;
|
|
13
|
+
return provider === "anthropic" ? "agent-sdk" : "ai-sdk";
|
|
14
|
+
}
|
|
15
|
+
/** Instantiates the engine for a provider + explicit preference. */
|
|
16
|
+
export function selectEngine(provider, enginePref) {
|
|
17
|
+
return resolveEngineKind(provider, enginePref) === "ai-sdk"
|
|
18
|
+
? new AiSdkEngine()
|
|
19
|
+
: new ClaudeAgentSdkEngine();
|
|
20
|
+
}
|
|
21
|
+
/** Parses a raw SCOUT_ENGINE / config value, ignoring unknown strings. */
|
|
22
|
+
export function parseEngineKind(value) {
|
|
23
|
+
if (value === "agent-sdk" || value === "ai-sdk")
|
|
24
|
+
return value;
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
export { AiSdkEngine, ClaudeAgentSdkEngine };
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runner/engines/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAM7D;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAoB,EACpB,UAAkC;IAElC,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC3D,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAC1B,QAAoB,EACpB,UAAkC;IAElC,OAAO,iBAAiB,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,QAAQ;QACzD,CAAC,CAAC,IAAI,WAAW,EAAE;QACnB,CAAC,CAAC,IAAI,oBAAoB,EAAE,CAAC;AACjC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,KAAyB;IACvD,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AiProvider } from "../../credentials.js";
|
|
2
|
+
import type { Step, Verdict } from "../../types.js";
|
|
3
|
+
import type { ScoutTool } from "../agent-tools.js";
|
|
4
|
+
export interface AiRunOutcome {
|
|
5
|
+
verdict: Verdict;
|
|
6
|
+
reason: string;
|
|
7
|
+
steps: Step[];
|
|
8
|
+
transcript: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Set when the runner itself failed to produce a verdict (agent never called
|
|
11
|
+
* scout_verdict) — NOT a UI judgment. Callers may retry and must report it
|
|
12
|
+
* as an infrastructure failure, never as "the scenario is blocked by the UI".
|
|
13
|
+
*/
|
|
14
|
+
runnerFailure?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* How an engine run ended, for no-verdict diagnostics. The Agent SDK fills this
|
|
18
|
+
* from the SDK `result` message; the AI SDK engine maps its `finishReason` into
|
|
19
|
+
* the same `subtype` vocabulary so {@link describeNoVerdict} works unchanged.
|
|
20
|
+
*/
|
|
21
|
+
export interface QueryEndInfo {
|
|
22
|
+
subtype: string;
|
|
23
|
+
numTurns?: number;
|
|
24
|
+
errors?: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Human-readable cause for a run that ended without scout_verdict.
|
|
28
|
+
* Distinguishes runner-infrastructure causes (turn budget, SDK errors) from
|
|
29
|
+
* an agent that simply stopped talking.
|
|
30
|
+
*/
|
|
31
|
+
export declare function describeNoVerdict(end: QueryEndInfo | undefined, maxTurns: number): string;
|
|
32
|
+
/**
|
|
33
|
+
* Records navigation relative to the configured baseUrl so cached scripts
|
|
34
|
+
* survive --base-url / SCOUT_BASE_URL pointing at another server. URLs that
|
|
35
|
+
* merely share the prefix (http://localhost:3000x) or live on other hosts
|
|
36
|
+
* stay absolute.
|
|
37
|
+
*/
|
|
38
|
+
export declare function relativizeUrl(url: string, baseUrl: string): string;
|
|
39
|
+
/** Everything an engine needs to drive one agent run. */
|
|
40
|
+
export interface EngineRunSpec {
|
|
41
|
+
/** Inferred from the model id; selects the AI SDK provider in the AI SDK engine. */
|
|
42
|
+
provider: AiProvider;
|
|
43
|
+
model: string;
|
|
44
|
+
systemPrompt: string;
|
|
45
|
+
userPrompt: string;
|
|
46
|
+
tools: ScoutTool[];
|
|
47
|
+
maxTurns: number;
|
|
48
|
+
}
|
|
49
|
+
/** The result of resuming a session (the forced-verdict rescue). */
|
|
50
|
+
export interface EngineResumeResult {
|
|
51
|
+
end: QueryEndInfo;
|
|
52
|
+
transcript: string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A live (or just-finished) engine session. The shared orchestrator reads
|
|
56
|
+
* `end`/`transcript`, and — if no verdict was captured — calls `resume()` to
|
|
57
|
+
* run the forced-verdict rescue with a small turn budget.
|
|
58
|
+
*/
|
|
59
|
+
export interface EngineSession {
|
|
60
|
+
end: QueryEndInfo;
|
|
61
|
+
/** Assistant text collected during the run, in order. */
|
|
62
|
+
transcript: string[];
|
|
63
|
+
resume(prompt: string, maxTurns: number): Promise<EngineResumeResult>;
|
|
64
|
+
}
|
|
65
|
+
/** A pluggable agent engine. `run` performs the initial verification pass. */
|
|
66
|
+
export interface AgentEngine {
|
|
67
|
+
run(spec: EngineRunSpec): Promise<EngineSession>;
|
|
68
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-readable cause for a run that ended without scout_verdict.
|
|
3
|
+
* Distinguishes runner-infrastructure causes (turn budget, SDK errors) from
|
|
4
|
+
* an agent that simply stopped talking.
|
|
5
|
+
*/
|
|
6
|
+
export function describeNoVerdict(end, maxTurns) {
|
|
7
|
+
if (!end)
|
|
8
|
+
return "a sessão do agente terminou sem emitir resultado (subprocesso morreu?)";
|
|
9
|
+
switch (end.subtype) {
|
|
10
|
+
case "error_max_turns":
|
|
11
|
+
return `o agente estourou o limite de ${maxTurns} turns sem chamar scout_verdict`;
|
|
12
|
+
case "error_during_execution":
|
|
13
|
+
return `erro durante a execução do agente${end.errors?.length ? `: ${end.errors.join("; ")}` : ""}`;
|
|
14
|
+
case "success":
|
|
15
|
+
return "o agente encerrou normalmente sem chamar scout_verdict";
|
|
16
|
+
default:
|
|
17
|
+
return `a sessão do agente terminou com "${end.subtype}"${end.errors?.length ? `: ${end.errors.join("; ")}` : ""}`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Records navigation relative to the configured baseUrl so cached scripts
|
|
22
|
+
* survive --base-url / SCOUT_BASE_URL pointing at another server. URLs that
|
|
23
|
+
* merely share the prefix (http://localhost:3000x) or live on other hosts
|
|
24
|
+
* stay absolute.
|
|
25
|
+
*/
|
|
26
|
+
export function relativizeUrl(url, baseUrl) {
|
|
27
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
28
|
+
if (url === base || url === `${base}/`)
|
|
29
|
+
return "/";
|
|
30
|
+
if (url.startsWith(base)) {
|
|
31
|
+
const rest = url.slice(base.length);
|
|
32
|
+
if (rest.startsWith("/") || rest.startsWith("?") || rest.startsWith("#"))
|
|
33
|
+
return rest;
|
|
34
|
+
}
|
|
35
|
+
return url;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/runner/engines/types.ts"],"names":[],"mappings":"AA4BA;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAA6B,EAAE,QAAgB;IAC/E,IAAI,CAAC,GAAG;QAAE,OAAO,wEAAwE,CAAC;IAC1F,QAAQ,GAAG,CAAC,OAAO,EAAE,CAAC;QACpB,KAAK,iBAAiB;YACpB,OAAO,iCAAiC,QAAQ,iCAAiC,CAAC;QACpF,KAAK,wBAAwB;YAC3B,OAAO,oCAAoC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACtG,KAAK,SAAS;YACZ,OAAO,wDAAwD,CAAC;QAClE;YACE,OAAO,oCAAoC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACvH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,OAAe;IACxD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACnD,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IACxF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pcamarajr/scout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Self-healing browser QA: natural-language scenarios verified by an AI agent, replayed deterministically in CI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,12 +43,18 @@
|
|
|
43
43
|
"dev": "tsx src/cli.ts",
|
|
44
44
|
"typecheck": "tsc --noEmit",
|
|
45
45
|
"test": "node --import tsx --test tests/*.test.ts",
|
|
46
|
+
"smoke": "node scripts/smoke.mjs",
|
|
46
47
|
"prepublishOnly": "npm run build",
|
|
47
48
|
"prepare": "husky"
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
51
|
+
"@ai-sdk/anthropic": "^3.0.85",
|
|
52
|
+
"@ai-sdk/google": "^3.0.83",
|
|
53
|
+
"@ai-sdk/google-vertex": "^4.0.147",
|
|
54
|
+
"@ai-sdk/openai": "^3.0.73",
|
|
50
55
|
"@anthropic-ai/claude-agent-sdk": "^0.3.0",
|
|
51
56
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
57
|
+
"ai": "^6.0.208",
|
|
52
58
|
"commander": "^13.1.0",
|
|
53
59
|
"gray-matter": "^4.0.3",
|
|
54
60
|
"playwright": "^1.52.0",
|