@vijaypjavvadi/bdd2pw 1.1.7 → 2.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/CHANGELOG.md +148 -0
- package/dist/cli.js +22 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +34 -7
- package/dist/index.js.map +1 -1
- package/dist/llm/anthropicClient.d.ts +49 -0
- package/dist/llm/anthropicClient.js +279 -0
- package/dist/llm/anthropicClient.js.map +1 -0
- package/dist/llm/cache.d.ts +51 -0
- package/dist/llm/cache.js +150 -0
- package/dist/llm/cache.js.map +1 -0
- package/dist/llm/candidateRules.d.ts +30 -0
- package/dist/llm/candidateRules.js +89 -0
- package/dist/llm/candidateRules.js.map +1 -0
- package/dist/llm/governanceClient.d.ts +36 -16
- package/dist/llm/governanceClient.js +73 -7
- package/dist/llm/governanceClient.js.map +1 -1
- package/dist/llm/index.d.ts +28 -0
- package/dist/llm/index.js +83 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/llmStepMatcher.d.ts +35 -0
- package/dist/llm/llmStepMatcher.js +93 -0
- package/dist/llm/llmStepMatcher.js.map +1 -0
- package/dist/llm/prompt.d.ts +39 -0
- package/dist/llm/prompt.js +113 -0
- package/dist/llm/prompt.js.map +1 -0
- package/dist/llm/types.d.ts +143 -0
- package/dist/llm/types.js +17 -0
- package/dist/llm/types.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/package.json +1 -1
|
@@ -1,24 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* No direct calls to provider SDKs — providers are plugins inside the sidecar.
|
|
2
|
+
* Wrapper for the ai-governance sidecar's `/sanitize` endpoint.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
4
|
+
* Per `E:\EB1A_Research\ai-governance\service\app.py`:
|
|
5
|
+
*
|
|
6
|
+
* POST /sanitize
|
|
7
|
+
* body: { kind: "code", content: string }
|
|
8
|
+
* 200: { kind: "code", sanitised: string, findings: [] }
|
|
9
|
+
*
|
|
10
|
+
* v2.0 (this file): we send the LLM PROMPT (the user-message content
|
|
11
|
+
* built in `prompt.ts`) as kind="code" — `code` re-uses the log-rule
|
|
12
|
+
* scrubber to redact secrets that might leak via test data in the
|
|
13
|
+
* .feature file.
|
|
14
|
+
*
|
|
15
|
+
* v1.x note: the original Phase 4 stub here imagined a unified
|
|
16
|
+
* "callViaGovernance" RPC that the sidecar would proxy to providers.
|
|
17
|
+
* That's NOT the actual sidecar API — the sidecar is a sanitiser, not
|
|
18
|
+
* an LLM proxy. bdd2pw owns the provider call; the sidecar just scrubs
|
|
19
|
+
* the payload first.
|
|
20
|
+
*
|
|
21
|
+
* Fail-closed: if the sidecar is unreachable or returns a non-2xx, we
|
|
22
|
+
* REFUSE to call the LLM. Better to degrade to TODO than leak unsanitised
|
|
23
|
+
* payloads. The caller can pass `skipGovernance: true` for unit tests
|
|
24
|
+
* (NOT for production).
|
|
6
25
|
*/
|
|
26
|
+
export interface SanitiseResult {
|
|
27
|
+
sanitised: string;
|
|
28
|
+
findings: Array<Record<string, unknown>>;
|
|
29
|
+
}
|
|
30
|
+
export declare class GovernanceUnreachableError extends Error {
|
|
31
|
+
constructor(url: string, cause?: unknown);
|
|
32
|
+
}
|
|
33
|
+
/** Legacy alias kept for backwards-compat with any external imports. */
|
|
7
34
|
export declare class GovernanceError extends Error {
|
|
8
35
|
readonly cause?: unknown | undefined;
|
|
9
36
|
constructor(message: string, cause?: unknown | undefined);
|
|
10
37
|
}
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
export interface GovernanceCallResult {
|
|
19
|
-
text: string;
|
|
20
|
-
redactions: number;
|
|
21
|
-
provider: string;
|
|
22
|
-
model: string;
|
|
38
|
+
export declare class GovernanceClient {
|
|
39
|
+
private baseUrl;
|
|
40
|
+
constructor(baseUrl: string);
|
|
41
|
+
sanitiseCode(content: string): Promise<SanitiseResult>;
|
|
42
|
+
/** Probe `/health` — used at startup to fail-fast before the first scaffold. */
|
|
43
|
+
health(): Promise<boolean>;
|
|
23
44
|
}
|
|
24
|
-
export declare function callViaGovernance(_input: GovernanceCallInput): Promise<GovernanceCallResult>;
|
|
@@ -1,13 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* No direct calls to provider SDKs — providers are plugins inside the sidecar.
|
|
3
|
+
* Wrapper for the ai-governance sidecar's `/sanitize` endpoint.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
5
|
+
* Per `E:\EB1A_Research\ai-governance\service\app.py`:
|
|
6
|
+
*
|
|
7
|
+
* POST /sanitize
|
|
8
|
+
* body: { kind: "code", content: string }
|
|
9
|
+
* 200: { kind: "code", sanitised: string, findings: [] }
|
|
10
|
+
*
|
|
11
|
+
* v2.0 (this file): we send the LLM PROMPT (the user-message content
|
|
12
|
+
* built in `prompt.ts`) as kind="code" — `code` re-uses the log-rule
|
|
13
|
+
* scrubber to redact secrets that might leak via test data in the
|
|
14
|
+
* .feature file.
|
|
15
|
+
*
|
|
16
|
+
* v1.x note: the original Phase 4 stub here imagined a unified
|
|
17
|
+
* "callViaGovernance" RPC that the sidecar would proxy to providers.
|
|
18
|
+
* That's NOT the actual sidecar API — the sidecar is a sanitiser, not
|
|
19
|
+
* an LLM proxy. bdd2pw owns the provider call; the sidecar just scrubs
|
|
20
|
+
* the payload first.
|
|
21
|
+
*
|
|
22
|
+
* Fail-closed: if the sidecar is unreachable or returns a non-2xx, we
|
|
23
|
+
* REFUSE to call the LLM. Better to degrade to TODO than leak unsanitised
|
|
24
|
+
* payloads. The caller can pass `skipGovernance: true` for unit tests
|
|
25
|
+
* (NOT for production).
|
|
7
26
|
*/
|
|
8
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.GovernanceError = void 0;
|
|
10
|
-
|
|
28
|
+
exports.GovernanceClient = exports.GovernanceError = exports.GovernanceUnreachableError = void 0;
|
|
29
|
+
const undici_1 = require("undici");
|
|
30
|
+
class GovernanceUnreachableError extends Error {
|
|
31
|
+
constructor(url, cause) {
|
|
32
|
+
super(`ai-governance sidecar unreachable at ${url}. Start it with: ` +
|
|
33
|
+
`'uvicorn service.app:app --port 4900' from the ai-governance repo, ` +
|
|
34
|
+
`or pass --skip-governance for non-production runs (NOT recommended). ` +
|
|
35
|
+
(cause instanceof Error ? `Underlying error: ${cause.message}` : ""));
|
|
36
|
+
this.name = "GovernanceUnreachableError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.GovernanceUnreachableError = GovernanceUnreachableError;
|
|
40
|
+
/** Legacy alias kept for backwards-compat with any external imports. */
|
|
11
41
|
class GovernanceError extends Error {
|
|
12
42
|
constructor(message, cause) {
|
|
13
43
|
super(message);
|
|
@@ -16,7 +46,43 @@ class GovernanceError extends Error {
|
|
|
16
46
|
}
|
|
17
47
|
}
|
|
18
48
|
exports.GovernanceError = GovernanceError;
|
|
19
|
-
|
|
20
|
-
|
|
49
|
+
class GovernanceClient {
|
|
50
|
+
constructor(baseUrl) {
|
|
51
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
52
|
+
}
|
|
53
|
+
async sanitiseCode(content) {
|
|
54
|
+
const url = `${this.baseUrl}/sanitize`;
|
|
55
|
+
let res;
|
|
56
|
+
try {
|
|
57
|
+
res = await (0, undici_1.request)(url, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { "content-type": "application/json" },
|
|
60
|
+
body: JSON.stringify({ kind: "code", content }),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
throw new GovernanceUnreachableError(url, err);
|
|
65
|
+
}
|
|
66
|
+
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
67
|
+
const body = await res.body.text().catch(() => "(no body)");
|
|
68
|
+
throw new Error(`ai-governance /sanitize returned ${res.statusCode}: ${body.slice(0, 200)}`);
|
|
69
|
+
}
|
|
70
|
+
const json = (await res.body.json());
|
|
71
|
+
return {
|
|
72
|
+
sanitised: json.sanitised ?? content,
|
|
73
|
+
findings: json.findings ?? [],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Probe `/health` — used at startup to fail-fast before the first scaffold. */
|
|
77
|
+
async health() {
|
|
78
|
+
try {
|
|
79
|
+
const res = await (0, undici_1.request)(`${this.baseUrl}/health`);
|
|
80
|
+
return res.statusCode === 200;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
21
86
|
}
|
|
87
|
+
exports.GovernanceClient = GovernanceClient;
|
|
22
88
|
//# sourceMappingURL=governanceClient.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"governanceClient.js","sourceRoot":"","sources":["../../src/llm/governanceClient.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"governanceClient.js","sourceRoot":"","sources":["../../src/llm/governanceClient.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AAEH,mCAAiC;AAOjC,MAAa,0BAA2B,SAAQ,KAAK;IACnD,YAAY,GAAW,EAAE,KAAe;QACtC,KAAK,CACH,wCAAwC,GAAG,mBAAmB;YAC5D,qEAAqE;YACrE,uEAAuE;YACvE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,qBAAqB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACvE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AAVD,gEAUC;AAED,wEAAwE;AACxE,MAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe,EAAkB,KAAe;QAC1D,KAAK,CAAC,OAAO,CAAC,CAAC;QAD4B,UAAK,GAAL,KAAK,CAAU;QAE1D,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AALD,0CAKC;AAED,MAAa,gBAAgB;IAE3B,YAAY,OAAe;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAAe;QAChC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,WAAW,CAAC;QACvC,IAAI,GAAG,CAAC;QACR,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,IAAA,gBAAO,EAAC,GAAG,EAAE;gBACvB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;aAChD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,0BAA0B,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YAClD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC;YAC5D,MAAM,IAAI,KAAK,CACb,oCAAoC,GAAG,CAAC,UAAU,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC5E,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAGlC,CAAC;QACF,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,OAAO;YACpC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;SAC9B,CAAC;IACJ,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAA,gBAAO,EAAC,GAAG,IAAI,CAAC,OAAO,SAAS,CAAC,CAAC;YACpD,OAAO,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF;AA3CD,4CA2CC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API of the LLM module — v2.0.
|
|
3
|
+
*
|
|
4
|
+
* import { createLLMClient, matchStepWithLLM, CandidateRulesWriter }
|
|
5
|
+
* from "./llm";
|
|
6
|
+
*
|
|
7
|
+
* The bdd2pw orchestrator (src/index.ts) only needs to import these
|
|
8
|
+
* three names. Everything else is internal.
|
|
9
|
+
*/
|
|
10
|
+
import type { LLMClient, LLMClientOptions } from "./types";
|
|
11
|
+
export { AnthropicLLMClient } from "./anthropicClient";
|
|
12
|
+
export { CandidateRulesWriter } from "./candidateRules";
|
|
13
|
+
export { GovernanceClient, GovernanceUnreachableError } from "./governanceClient";
|
|
14
|
+
export { matchStepWithLLM, matchStepsWithLLM, type MatchStepWithLLMContext, } from "./llmStepMatcher";
|
|
15
|
+
export type { CandidateRuleEntry, GenerateBindingInput, GenerateBindingResult, LLMClient, LLMClientOptions, LLMLogEvent, } from "./types";
|
|
16
|
+
/**
|
|
17
|
+
* Construct the right LLMClient for the given options. Today the only
|
|
18
|
+
* provider is "anthropic"; the factory exists so v2.1 (OpenAI/Gemini)
|
|
19
|
+
* can plug in without touching callers.
|
|
20
|
+
*
|
|
21
|
+
* `repoRoot` is used to derive the default cache path:
|
|
22
|
+
* `<repoRoot>/.bdd2pw/llm-cache.sqlite`.
|
|
23
|
+
*
|
|
24
|
+
* Returns undefined when LLM is disabled (no provider configured) — so
|
|
25
|
+
* callers can pass the result straight into matchStepWithLLM's `llm`
|
|
26
|
+
* field without an explicit null-check.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createLLMClient(opts: LLMClientOptions | undefined, repoRoot: string): LLMClient | undefined;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Public API of the LLM module — v2.0.
|
|
4
|
+
*
|
|
5
|
+
* import { createLLMClient, matchStepWithLLM, CandidateRulesWriter }
|
|
6
|
+
* from "./llm";
|
|
7
|
+
*
|
|
8
|
+
* The bdd2pw orchestrator (src/index.ts) only needs to import these
|
|
9
|
+
* three names. Everything else is internal.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.matchStepsWithLLM = exports.matchStepWithLLM = exports.GovernanceUnreachableError = exports.GovernanceClient = exports.CandidateRulesWriter = exports.AnthropicLLMClient = void 0;
|
|
46
|
+
exports.createLLMClient = createLLMClient;
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const anthropicClient_1 = require("./anthropicClient");
|
|
49
|
+
var anthropicClient_2 = require("./anthropicClient");
|
|
50
|
+
Object.defineProperty(exports, "AnthropicLLMClient", { enumerable: true, get: function () { return anthropicClient_2.AnthropicLLMClient; } });
|
|
51
|
+
var candidateRules_1 = require("./candidateRules");
|
|
52
|
+
Object.defineProperty(exports, "CandidateRulesWriter", { enumerable: true, get: function () { return candidateRules_1.CandidateRulesWriter; } });
|
|
53
|
+
var governanceClient_1 = require("./governanceClient");
|
|
54
|
+
Object.defineProperty(exports, "GovernanceClient", { enumerable: true, get: function () { return governanceClient_1.GovernanceClient; } });
|
|
55
|
+
Object.defineProperty(exports, "GovernanceUnreachableError", { enumerable: true, get: function () { return governanceClient_1.GovernanceUnreachableError; } });
|
|
56
|
+
var llmStepMatcher_1 = require("./llmStepMatcher");
|
|
57
|
+
Object.defineProperty(exports, "matchStepWithLLM", { enumerable: true, get: function () { return llmStepMatcher_1.matchStepWithLLM; } });
|
|
58
|
+
Object.defineProperty(exports, "matchStepsWithLLM", { enumerable: true, get: function () { return llmStepMatcher_1.matchStepsWithLLM; } });
|
|
59
|
+
/**
|
|
60
|
+
* Construct the right LLMClient for the given options. Today the only
|
|
61
|
+
* provider is "anthropic"; the factory exists so v2.1 (OpenAI/Gemini)
|
|
62
|
+
* can plug in without touching callers.
|
|
63
|
+
*
|
|
64
|
+
* `repoRoot` is used to derive the default cache path:
|
|
65
|
+
* `<repoRoot>/.bdd2pw/llm-cache.sqlite`.
|
|
66
|
+
*
|
|
67
|
+
* Returns undefined when LLM is disabled (no provider configured) — so
|
|
68
|
+
* callers can pass the result straight into matchStepWithLLM's `llm`
|
|
69
|
+
* field without an explicit null-check.
|
|
70
|
+
*/
|
|
71
|
+
function createLLMClient(opts, repoRoot) {
|
|
72
|
+
if (!opts || !opts.provider)
|
|
73
|
+
return undefined;
|
|
74
|
+
const cachePathDefault = path.join(repoRoot, ".bdd2pw", "llm-cache.sqlite");
|
|
75
|
+
switch (opts.provider) {
|
|
76
|
+
case "anthropic":
|
|
77
|
+
return new anthropicClient_1.AnthropicLLMClient(opts, cachePathDefault);
|
|
78
|
+
default:
|
|
79
|
+
// v2.1 will add openai / gemini.
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCH,0CAiBC;AAlDD,2CAA6B;AAC7B,uDAAuD;AAGvD,qDAAuD;AAA9C,qHAAA,kBAAkB,OAAA;AAC3B,mDAAwD;AAA/C,sHAAA,oBAAoB,OAAA;AAC7B,uDAAkF;AAAzE,oHAAA,gBAAgB,OAAA;AAAE,8HAAA,0BAA0B,OAAA;AACrD,mDAI0B;AAHxB,kHAAA,gBAAgB,OAAA;AAChB,mHAAA,iBAAiB,OAAA;AAYnB;;;;;;;;;;;GAWG;AACH,SAAgB,eAAe,CAC7B,IAAkC,EAClC,QAAgB;IAEhB,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAChC,QAAQ,EACR,SAAS,EACT,kBAAkB,CACnB,CAAC;IACF,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,KAAK,WAAW;YACd,OAAO,IAAI,oCAAkB,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;QACxD;YACE,iCAAiC;YACjC,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async overlay over the deterministic stepMatcher — when the rule table
|
|
3
|
+
* fails to match a step AND an LLM client is provided, fall back to the
|
|
4
|
+
* LLM. Every successful LLM-generated binding is appended to
|
|
5
|
+
* `<repo>/artefacts/candidate-rules.jsonl` for offline review.
|
|
6
|
+
*
|
|
7
|
+
* Why a wrapper, not modifying matchStep:
|
|
8
|
+
* - Keeps the rule-based matcher pure-sync, so unit tests stay fast.
|
|
9
|
+
* - LLM fallback is opt-in and orthogonal — easier to reason about
|
|
10
|
+
* and to disable in CI where determinism matters.
|
|
11
|
+
* - The wrapper is the natural integration point for the candidate-
|
|
12
|
+
* rules JSONL writer; the deterministic matcher doesn't need to
|
|
13
|
+
* know that pipeline exists.
|
|
14
|
+
*/
|
|
15
|
+
import type { PageObjectIR, StepBinding, StepIR } from "../types";
|
|
16
|
+
import { CandidateRulesWriter } from "./candidateRules";
|
|
17
|
+
import type { LLMClient } from "./types";
|
|
18
|
+
export interface MatchStepWithLLMContext {
|
|
19
|
+
/** When provided AND the rule matcher returns a warning, ask the LLM. */
|
|
20
|
+
llm?: LLMClient;
|
|
21
|
+
/** Where to log every successful LLM binding for offline review. */
|
|
22
|
+
candidates?: CandidateRulesWriter;
|
|
23
|
+
/** Stable scaffold-run identifier — written into each candidate row. */
|
|
24
|
+
scaffoldId: string;
|
|
25
|
+
/** Target URL — passed to LLM for additional context. */
|
|
26
|
+
url?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function matchStepWithLLM(step: StepIR, pom: PageObjectIR, pageVar: string, ctx: MatchStepWithLLMContext): Promise<StepBinding>;
|
|
29
|
+
/**
|
|
30
|
+
* Convenience: match a list of steps with LLM overlay, sequentially. We
|
|
31
|
+
* deliberately don't parallelise — the cache means repeated step text
|
|
32
|
+
* within a feature returns instantly, but parallel calls would all miss
|
|
33
|
+
* the cache and pay for the same step twice.
|
|
34
|
+
*/
|
|
35
|
+
export declare function matchStepsWithLLM(steps: StepIR[], pom: PageObjectIR, pageVar: string, ctx: MatchStepWithLLMContext): Promise<StepBinding[]>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Async overlay over the deterministic stepMatcher — when the rule table
|
|
4
|
+
* fails to match a step AND an LLM client is provided, fall back to the
|
|
5
|
+
* LLM. Every successful LLM-generated binding is appended to
|
|
6
|
+
* `<repo>/artefacts/candidate-rules.jsonl` for offline review.
|
|
7
|
+
*
|
|
8
|
+
* Why a wrapper, not modifying matchStep:
|
|
9
|
+
* - Keeps the rule-based matcher pure-sync, so unit tests stay fast.
|
|
10
|
+
* - LLM fallback is opt-in and orthogonal — easier to reason about
|
|
11
|
+
* and to disable in CI where determinism matters.
|
|
12
|
+
* - The wrapper is the natural integration point for the candidate-
|
|
13
|
+
* rules JSONL writer; the deterministic matcher doesn't need to
|
|
14
|
+
* know that pipeline exists.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.matchStepWithLLM = matchStepWithLLM;
|
|
18
|
+
exports.matchStepsWithLLM = matchStepsWithLLM;
|
|
19
|
+
const stepMatcher_1 = require("../transformers/stepMatcher");
|
|
20
|
+
async function matchStepWithLLM(step, pom, pageVar, ctx) {
|
|
21
|
+
// 1) Always try rule-based first.
|
|
22
|
+
const ruleBased = (0, stepMatcher_1.matchStep)(step, pom, pageVar);
|
|
23
|
+
// 2) If the rule matcher produced a real binding (no warning), use it.
|
|
24
|
+
if (!ruleBased.warning)
|
|
25
|
+
return ruleBased;
|
|
26
|
+
// 3) No LLM client → return the warning binding (TODO behaviour).
|
|
27
|
+
if (!ctx.llm)
|
|
28
|
+
return ruleBased;
|
|
29
|
+
// 4) Budget exhausted → return the warning binding (caller already
|
|
30
|
+
// notified at startup; this just keeps scaffolding moving).
|
|
31
|
+
if (ctx.llm.budgetExhausted())
|
|
32
|
+
return ruleBased;
|
|
33
|
+
// 5) Try the LLM. Soft-fail back to rule-based on any error.
|
|
34
|
+
try {
|
|
35
|
+
const result = await ctx.llm.generateBinding({
|
|
36
|
+
step,
|
|
37
|
+
pom,
|
|
38
|
+
pageVar,
|
|
39
|
+
url: ctx.url,
|
|
40
|
+
scaffoldId: ctx.scaffoldId,
|
|
41
|
+
});
|
|
42
|
+
if (!result.binding) {
|
|
43
|
+
// Annotate the warning with the LLM error so BDD_REVIEW shows why
|
|
44
|
+
// the LLM didn't help. The step still falls through to TODO.
|
|
45
|
+
return {
|
|
46
|
+
...ruleBased,
|
|
47
|
+
warning: result.error
|
|
48
|
+
? `${ruleBased.warning} (LLM fallback also failed: ${result.error})`
|
|
49
|
+
: ruleBased.warning,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Append to candidate-rules.jsonl for offline review. Best-effort.
|
|
53
|
+
if (ctx.candidates) {
|
|
54
|
+
await ctx.candidates.append({
|
|
55
|
+
ts: new Date().toISOString(),
|
|
56
|
+
scaffoldId: ctx.scaffoldId,
|
|
57
|
+
stepText: step.text,
|
|
58
|
+
stepKeyword: step.keyword,
|
|
59
|
+
binding: result.binding,
|
|
60
|
+
pomSignature: {
|
|
61
|
+
className: pom.className,
|
|
62
|
+
fieldNames: pom.fields.map((f) => f.fieldName),
|
|
63
|
+
methodNames: pom.methods.map((m) => m.name),
|
|
64
|
+
},
|
|
65
|
+
provider: "anthropic",
|
|
66
|
+
model: result.model ?? "(unknown)",
|
|
67
|
+
fromCache: result.fromCache,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return result.binding;
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
74
|
+
return {
|
|
75
|
+
...ruleBased,
|
|
76
|
+
warning: `${ruleBased.warning} (LLM fallback threw: ${message})`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Convenience: match a list of steps with LLM overlay, sequentially. We
|
|
82
|
+
* deliberately don't parallelise — the cache means repeated step text
|
|
83
|
+
* within a feature returns instantly, but parallel calls would all miss
|
|
84
|
+
* the cache and pay for the same step twice.
|
|
85
|
+
*/
|
|
86
|
+
async function matchStepsWithLLM(steps, pom, pageVar, ctx) {
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const step of steps) {
|
|
89
|
+
out.push(await matchStepWithLLM(step, pom, pageVar, ctx));
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=llmStepMatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llmStepMatcher.js","sourceRoot":"","sources":["../../src/llm/llmStepMatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the Anthropic prompt used to generate a `StepBinding` from an
|
|
3
|
+
* unmatched Gherkin step.
|
|
4
|
+
*
|
|
5
|
+
* Design principles:
|
|
6
|
+
*
|
|
7
|
+
* 1. **JSON-only output.** The model is instructed to return ONE JSON
|
|
8
|
+
* object matching the StepBinding type — no prose, no Markdown
|
|
9
|
+
* fences. We then JSON.parse it; malformed output triggers a soft-fail.
|
|
10
|
+
*
|
|
11
|
+
* 2. **Specific not creative.** Show the model the existing rule
|
|
12
|
+
* taxonomy (briefly) so it tries to match the same shapes our
|
|
13
|
+
* deterministic rules produce. Otherwise the LLM invents novel
|
|
14
|
+
* locator chains that drift from the rest of the codebase.
|
|
15
|
+
*
|
|
16
|
+
* 3. **POM-aware.** Every prompt includes the current POM's field and
|
|
17
|
+
* method names. The LLM should prefer existing fields over
|
|
18
|
+
* synthesising raw locators.
|
|
19
|
+
*
|
|
20
|
+
* 4. **Temperature 0** (set at call site) — same input → same output,
|
|
21
|
+
* paired with caching for true determinism.
|
|
22
|
+
*/
|
|
23
|
+
import type { GenerateBindingInput } from "./types";
|
|
24
|
+
/** System prompt — shared across all generations. */
|
|
25
|
+
export declare const SYSTEM_PROMPT = "You are a deterministic step-binding generator for bdd2pw, a Gherkin \u2192 Playwright TypeScript scaffolder.\n\nbdd2pw normally maps each Gherkin step to one of 30+ deterministic regex rules. When no rule matches, you produce the binding instead.\n\nYour output MUST be a single JSON object matching this TypeScript type:\n\ninterface StepBinding {\n step: { keyword: \"Given\"|\"When\"|\"Then\"|\"And\"|\"But\"; text: string };\n pomCall?: { page: string; method: string; args: string[] }; // for actions\n assertion?: { locator: string; matcher: string; expected?: string }; // for assertions\n customBody?: string; // pre-rendered TS for compound steps\n warning?: string; // populated only if you genuinely cannot map the step\n}\n\nRules:\n- Output exactly ONE JSON object. No prose, no Markdown, no backticks.\n- Echo the step in the \"step\" field unchanged.\n- Choose ONE of: pomCall, assertion, customBody. Never two.\n- Prefer existing POM fields over synthesizing locators. Only use page.locator(...) / page.getByText(...) / page.getByRole(...) when no POM field matches.\n- For assertions, set \"matcher\" to one of: toBeVisible, toBeHidden, toContainText, toHaveText, toHaveURL, toHaveAttribute, not.toBeVisible, not.toHaveURL.\n- For toContainText/toHaveText, \"expected\" must be a JS-quoted string like \"\\\"X\\\"\".\n- For toHaveURL, \"expected\" should be: new RegExp(\"...\") (a JS expression as a STRING).\n- For toHaveAttribute, \"expected\" should be: \"name\", \"value\" (two args, as a single string).\n- Always end synthesised locators with .first() to avoid Playwright strict-mode violations when text appears in multiple elements.\n- If the step has no quoted value AND no resolvable POM field, set \"warning\" to a clear single-sentence explanation of what's ambiguous and emit no pomCall/assertion. The user will hand-edit the spec.";
|
|
26
|
+
/**
|
|
27
|
+
* Build the user-side message for one step. Includes:
|
|
28
|
+
* - The step keyword + text.
|
|
29
|
+
* - The POM IR (className, field names with their locator API+args, methods).
|
|
30
|
+
* - The target URL (gives the model context about what page).
|
|
31
|
+
* - The pageVar variable name to use in pomCall.page.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildUserPrompt(input: GenerateBindingInput): string;
|
|
34
|
+
/**
|
|
35
|
+
* Cache key — stable across runs given identical inputs. Determinism plus
|
|
36
|
+
* cost control: the same step text + POM signature returns the same binding
|
|
37
|
+
* forever (until the user explicitly clears the cache or upgrades bdd2pw).
|
|
38
|
+
*/
|
|
39
|
+
export declare function cacheKey(input: GenerateBindingInput, model: string): string;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Build the Anthropic prompt used to generate a `StepBinding` from an
|
|
4
|
+
* unmatched Gherkin step.
|
|
5
|
+
*
|
|
6
|
+
* Design principles:
|
|
7
|
+
*
|
|
8
|
+
* 1. **JSON-only output.** The model is instructed to return ONE JSON
|
|
9
|
+
* object matching the StepBinding type — no prose, no Markdown
|
|
10
|
+
* fences. We then JSON.parse it; malformed output triggers a soft-fail.
|
|
11
|
+
*
|
|
12
|
+
* 2. **Specific not creative.** Show the model the existing rule
|
|
13
|
+
* taxonomy (briefly) so it tries to match the same shapes our
|
|
14
|
+
* deterministic rules produce. Otherwise the LLM invents novel
|
|
15
|
+
* locator chains that drift from the rest of the codebase.
|
|
16
|
+
*
|
|
17
|
+
* 3. **POM-aware.** Every prompt includes the current POM's field and
|
|
18
|
+
* method names. The LLM should prefer existing fields over
|
|
19
|
+
* synthesising raw locators.
|
|
20
|
+
*
|
|
21
|
+
* 4. **Temperature 0** (set at call site) — same input → same output,
|
|
22
|
+
* paired with caching for true determinism.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.SYSTEM_PROMPT = void 0;
|
|
26
|
+
exports.buildUserPrompt = buildUserPrompt;
|
|
27
|
+
exports.cacheKey = cacheKey;
|
|
28
|
+
/** System prompt — shared across all generations. */
|
|
29
|
+
exports.SYSTEM_PROMPT = `You are a deterministic step-binding generator for bdd2pw, a Gherkin → Playwright TypeScript scaffolder.
|
|
30
|
+
|
|
31
|
+
bdd2pw normally maps each Gherkin step to one of 30+ deterministic regex rules. When no rule matches, you produce the binding instead.
|
|
32
|
+
|
|
33
|
+
Your output MUST be a single JSON object matching this TypeScript type:
|
|
34
|
+
|
|
35
|
+
interface StepBinding {
|
|
36
|
+
step: { keyword: "Given"|"When"|"Then"|"And"|"But"; text: string };
|
|
37
|
+
pomCall?: { page: string; method: string; args: string[] }; // for actions
|
|
38
|
+
assertion?: { locator: string; matcher: string; expected?: string }; // for assertions
|
|
39
|
+
customBody?: string; // pre-rendered TS for compound steps
|
|
40
|
+
warning?: string; // populated only if you genuinely cannot map the step
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Rules:
|
|
44
|
+
- Output exactly ONE JSON object. No prose, no Markdown, no backticks.
|
|
45
|
+
- Echo the step in the "step" field unchanged.
|
|
46
|
+
- Choose ONE of: pomCall, assertion, customBody. Never two.
|
|
47
|
+
- Prefer existing POM fields over synthesizing locators. Only use page.locator(...) / page.getByText(...) / page.getByRole(...) when no POM field matches.
|
|
48
|
+
- For assertions, set "matcher" to one of: toBeVisible, toBeHidden, toContainText, toHaveText, toHaveURL, toHaveAttribute, not.toBeVisible, not.toHaveURL.
|
|
49
|
+
- For toContainText/toHaveText, "expected" must be a JS-quoted string like "\\"X\\"".
|
|
50
|
+
- For toHaveURL, "expected" should be: new RegExp("...") (a JS expression as a STRING).
|
|
51
|
+
- For toHaveAttribute, "expected" should be: "name", "value" (two args, as a single string).
|
|
52
|
+
- Always end synthesised locators with .first() to avoid Playwright strict-mode violations when text appears in multiple elements.
|
|
53
|
+
- If the step has no quoted value AND no resolvable POM field, set "warning" to a clear single-sentence explanation of what's ambiguous and emit no pomCall/assertion. The user will hand-edit the spec.`;
|
|
54
|
+
/**
|
|
55
|
+
* Build the user-side message for one step. Includes:
|
|
56
|
+
* - The step keyword + text.
|
|
57
|
+
* - The POM IR (className, field names with their locator API+args, methods).
|
|
58
|
+
* - The target URL (gives the model context about what page).
|
|
59
|
+
* - The pageVar variable name to use in pomCall.page.
|
|
60
|
+
*/
|
|
61
|
+
function buildUserPrompt(input) {
|
|
62
|
+
const { step, pom, pageVar, url } = input;
|
|
63
|
+
const fieldsBlock = pom.fields.length
|
|
64
|
+
? pom.fields
|
|
65
|
+
.map((f) => ` - ${f.fieldName}: ${pageVar}.${f.fieldName} (page.${f.api}(${f.args}))`)
|
|
66
|
+
.join("\n")
|
|
67
|
+
: " (no fields discovered yet)";
|
|
68
|
+
const methodsBlock = pom.methods.length
|
|
69
|
+
? pom.methods
|
|
70
|
+
.map((m) => ` - ${m.name}(${m.params.map((p) => `${p.name}: ${p.type}`).join(", ")})`)
|
|
71
|
+
.join("\n")
|
|
72
|
+
: " (no methods)";
|
|
73
|
+
const stepArg = step.argument
|
|
74
|
+
? typeof step.argument === "string"
|
|
75
|
+
? `\nDoc string:\n${step.argument}`
|
|
76
|
+
: `\nData table:\n${JSON.stringify(step.argument)}`
|
|
77
|
+
: "";
|
|
78
|
+
return `Step that didn't match any rule:
|
|
79
|
+
${step.keyword} ${step.text}${stepArg}
|
|
80
|
+
|
|
81
|
+
POM context:
|
|
82
|
+
className: ${pom.className}
|
|
83
|
+
pageVar: ${pageVar}
|
|
84
|
+
url: ${url ?? "(none)"}
|
|
85
|
+
|
|
86
|
+
POM fields:
|
|
87
|
+
${fieldsBlock}
|
|
88
|
+
|
|
89
|
+
POM methods:
|
|
90
|
+
${methodsBlock}
|
|
91
|
+
|
|
92
|
+
Return the JSON binding now.`;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Cache key — stable across runs given identical inputs. Determinism plus
|
|
96
|
+
* cost control: the same step text + POM signature returns the same binding
|
|
97
|
+
* forever (until the user explicitly clears the cache or upgrades bdd2pw).
|
|
98
|
+
*/
|
|
99
|
+
function cacheKey(input, model) {
|
|
100
|
+
const fieldNames = input.pom.fields.map((f) => f.fieldName).sort().join(",");
|
|
101
|
+
const methodNames = input.pom.methods.map((m) => m.name).sort().join(",");
|
|
102
|
+
// Include the model so a model upgrade invalidates old cache entries.
|
|
103
|
+
return [
|
|
104
|
+
"v2",
|
|
105
|
+
model,
|
|
106
|
+
input.step.keyword,
|
|
107
|
+
input.step.text,
|
|
108
|
+
input.pom.className,
|
|
109
|
+
fieldNames,
|
|
110
|
+
methodNames,
|
|
111
|
+
].join("|");
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/llm/prompt.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AAsCH,0CA0CC;AAOD,4BAaC;AAhGD,qDAAqD;AACxC,QAAA,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;yMAwB4K,CAAC;AAE1M;;;;;;GAMG;AACH,SAAgB,eAAe,CAAC,KAA2B;IACzD,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IAE1C,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM;QACnC,CAAC,CAAC,GAAG,CAAC,MAAM;aACP,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,OAAO,CAAC,CAAC,SAAS,KAAK,OAAO,IAAI,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAC7E;aACA,IAAI,CAAC,IAAI,CAAC;QACf,CAAC,CAAC,8BAA8B,CAAC;IAEnC,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM;QACrC,CAAC,CAAC,GAAG,CAAC,OAAO;aACR,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC7E;aACA,IAAI,CAAC,IAAI,CAAC;QACf,CAAC,CAAC,gBAAgB,CAAC;IAErB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ;QAC3B,CAAC,CAAC,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ;YACjC,CAAC,CAAC,kBAAkB,IAAI,CAAC,QAAQ,EAAE;YACnC,CAAC,CAAC,kBAAkB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACrD,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;IACL,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,GAAG,OAAO;;;eAGxB,GAAG,CAAC,SAAS;aACf,OAAO;SACX,GAAG,IAAI,QAAQ;;;EAGtB,WAAW;;;EAGX,YAAY;;6BAEe,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,SAAgB,QAAQ,CAAC,KAA2B,EAAE,KAAa;IACjE,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7E,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1E,sEAAsE;IACtE,OAAO;QACL,IAAI;QACJ,KAAK;QACL,KAAK,CAAC,IAAI,CAAC,OAAO;QAClB,KAAK,CAAC,IAAI,CAAC,IAAI;QACf,KAAK,CAAC,GAAG,CAAC,SAAS;QACnB,UAAU;QACV,WAAW;KACZ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC"}
|