@dreamdata_io/verify-tracking 0.1.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/README.md +756 -0
- package/dist/ai/checker.js +222 -0
- package/dist/ai/checker.js.map +1 -0
- package/dist/ai/checklist.js +190 -0
- package/dist/ai/checklist.js.map +1 -0
- package/dist/ai/cli-presets.js +27 -0
- package/dist/ai/cli-presets.js.map +1 -0
- package/dist/ai/evidence-view.js +143 -0
- package/dist/ai/evidence-view.js.map +1 -0
- package/dist/ai/local-cli.js +58 -0
- package/dist/ai/local-cli.js.map +1 -0
- package/dist/ai/openai-compatible.js +78 -0
- package/dist/ai/openai-compatible.js.map +1 -0
- package/dist/ai/response-parser.js +105 -0
- package/dist/ai/response-parser.js.map +1 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/analysis/consent-mode.js +225 -0
- package/dist/analysis/consent-mode.js.map +1 -0
- package/dist/analysis/deployment.js +2658 -0
- package/dist/analysis/deployment.js.map +1 -0
- package/dist/analysis/diagnosis/copy.en.js +303 -0
- package/dist/analysis/diagnosis/copy.en.js.map +1 -0
- package/dist/analysis/diagnosis/diagnose.js +172 -0
- package/dist/analysis/diagnosis/diagnose.js.map +1 -0
- package/dist/analysis/diagnosis/docs.js +118 -0
- package/dist/analysis/diagnosis/docs.js.map +1 -0
- package/dist/analysis/diagnosis/index.js +5 -0
- package/dist/analysis/diagnosis/index.js.map +1 -0
- package/dist/analysis/diagnosis/rules.js +656 -0
- package/dist/analysis/diagnosis/rules.js.map +1 -0
- package/dist/analysis/diagnosis/types.js +2 -0
- package/dist/analysis/diagnosis/types.js.map +1 -0
- package/dist/cli.js +344 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/env.js +100 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/preflight.js +167 -0
- package/dist/config/preflight.js.map +1 -0
- package/dist/config/user-config.js +79 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/evidence/collect.js +527 -0
- package/dist/evidence/collect.js.map +1 -0
- package/dist/evidence/consent-accept.js +377 -0
- package/dist/evidence/consent-accept.js.map +1 -0
- package/dist/evidence/consent.js +340 -0
- package/dist/evidence/consent.js.map +1 -0
- package/dist/evidence/constants.js +615 -0
- package/dist/evidence/constants.js.map +1 -0
- package/dist/evidence/globals.js +132 -0
- package/dist/evidence/globals.js.map +1 -0
- package/dist/evidence/gtm-container.js +432 -0
- package/dist/evidence/gtm-container.js.map +1 -0
- package/dist/evidence/heuristic-banner.js +107 -0
- package/dist/evidence/heuristic-banner.js.map +1 -0
- package/dist/evidence/http-probe.js +139 -0
- package/dist/evidence/http-probe.js.map +1 -0
- package/dist/evidence/init-script.js +112 -0
- package/dist/evidence/init-script.js.map +1 -0
- package/dist/evidence/network.js +365 -0
- package/dist/evidence/network.js.map +1 -0
- package/dist/evidence/preference-center.js +368 -0
- package/dist/evidence/preference-center.js.map +1 -0
- package/dist/evidence/script.js +425 -0
- package/dist/evidence/script.js.map +1 -0
- package/dist/evidence/second-page.js +332 -0
- package/dist/evidence/second-page.js.map +1 -0
- package/dist/evidence/selector-probe.js +53 -0
- package/dist/evidence/selector-probe.js.map +1 -0
- package/dist/evidence/stealth.js +21 -0
- package/dist/evidence/stealth.js.map +1 -0
- package/dist/evidence/types.js +14 -0
- package/dist/evidence/types.js.map +1 -0
- package/dist/interactive/prompt.js +31 -0
- package/dist/interactive/prompt.js.map +1 -0
- package/dist/mcp/server.js +308 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/summary.js +41 -0
- package/dist/mcp/summary.js.map +1 -0
- package/dist/report/context.js +184 -0
- package/dist/report/context.js.map +1 -0
- package/dist/report/projection.js +125 -0
- package/dist/report/projection.js.map +1 -0
- package/dist/report/quote-check.js +90 -0
- package/dist/report/quote-check.js.map +1 -0
- package/dist/report/render.js +178 -0
- package/dist/report/render.js.map +1 -0
- package/dist/report/serializers.js +124 -0
- package/dist/report/serializers.js.map +1 -0
- package/dist/report/store.js +139 -0
- package/dist/report/store.js.map +1 -0
- package/dist/run/verify.js +77 -0
- package/dist/run/verify.js.map +1 -0
- package/dist/utils/duration.js +10 -0
- package/dist/utils/duration.js.map +1 -0
- package/dist/utils/errors.js +17 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/runTimingsLog.js +27 -0
- package/dist/utils/runTimingsLog.js.map +1 -0
- package/dist/utils/slug.js +27 -0
- package/dist/utils/slug.js.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -0
- package/scripts/install.sh +314 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { delimiter, join } from "node:path";
|
|
4
|
+
import { chromium } from "playwright";
|
|
5
|
+
/**
|
|
6
|
+
* The one command that installs everything a run needs on a bare machine.
|
|
7
|
+
* Named in every preflight failure, because each of them is a thing the
|
|
8
|
+
* installer would have handled — a teammate who hit one of these has a
|
|
9
|
+
* half-provisioned machine, and re-running this is a shorter path than
|
|
10
|
+
* following instructions per missing piece.
|
|
11
|
+
*/
|
|
12
|
+
export const INSTALLER_ONE_LINER = "curl -fsSL https://unpkg.com/@dreamdata_io/verify-tracking/scripts/install.sh | sh";
|
|
13
|
+
/**
|
|
14
|
+
* Where Playwright's `channel: "chrome"` looks for a real Chrome. Playwright
|
|
15
|
+
* resolves the channel itself at launch and offers no way to ask beforehand,
|
|
16
|
+
* so the well-known install locations are checked directly. A false negative
|
|
17
|
+
* only costs a warning — the launch still tries the channel — so this stays a
|
|
18
|
+
* short list of the default paths rather than an exhaustive search.
|
|
19
|
+
*/
|
|
20
|
+
const CHROME_PATHS = [
|
|
21
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
22
|
+
"/opt/google/chrome/chrome",
|
|
23
|
+
"/usr/bin/google-chrome",
|
|
24
|
+
"/usr/bin/google-chrome-stable",
|
|
25
|
+
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
|
26
|
+
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
|
27
|
+
];
|
|
28
|
+
function hasBundledChromium() {
|
|
29
|
+
try {
|
|
30
|
+
// Returns the path Playwright would launch whether or not it was ever
|
|
31
|
+
// downloaded, so the path's existence is the actual question.
|
|
32
|
+
const path = chromium.executablePath();
|
|
33
|
+
return Boolean(path) && existsSync(path);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function detectBrowsers() {
|
|
40
|
+
return {
|
|
41
|
+
chrome: CHROME_PATHS.some((path) => existsSync(path)),
|
|
42
|
+
bundledChromium: hasBundledChromium(),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The message to show when no browser was found, or null when the run can
|
|
47
|
+
* proceed. Names both ways out, because installing Chrome is the one a
|
|
48
|
+
* non-technical teammate can already do.
|
|
49
|
+
*/
|
|
50
|
+
export function browserPreflightError(availability = detectBrowsers()) {
|
|
51
|
+
if (availability.chrome || availability.bundledChromium)
|
|
52
|
+
return null;
|
|
53
|
+
return [
|
|
54
|
+
"No browser found to drive — this check needs one and does not ship with it.",
|
|
55
|
+
"",
|
|
56
|
+
"Re-running the installer downloads one for you:",
|
|
57
|
+
"",
|
|
58
|
+
` ${INSTALLER_ONE_LINER}`,
|
|
59
|
+
"",
|
|
60
|
+
"Or do it by hand — install Google Chrome (https://www.google.com/chrome/),",
|
|
61
|
+
"or fetch Playwright's own Chromium:",
|
|
62
|
+
"",
|
|
63
|
+
" npx playwright install chromium",
|
|
64
|
+
"",
|
|
65
|
+
].join("\n");
|
|
66
|
+
}
|
|
67
|
+
/** Whether an executable of this name sits on the PATH. */
|
|
68
|
+
function isOnPath(command) {
|
|
69
|
+
return (process.env.PATH ?? "")
|
|
70
|
+
.split(delimiter)
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
.some((dir) => {
|
|
73
|
+
try {
|
|
74
|
+
return existsSync(join(dir, command));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The message to show when the local-CLI backend names a command this machine
|
|
83
|
+
* does not have, or null when it does.
|
|
84
|
+
*
|
|
85
|
+
* The zero-key backend shells out to an AI CLI, and a missing one is only
|
|
86
|
+
* discovered after the browser work is finished — minutes in, on a spawn error
|
|
87
|
+
* naming a binary the operator was never told to install. Checked up front
|
|
88
|
+
* instead, since with no API key set this is now the default backend rather
|
|
89
|
+
* than an opt-in.
|
|
90
|
+
*/
|
|
91
|
+
export function cliBackendPreflightError(command) {
|
|
92
|
+
if (isOnPath(command))
|
|
93
|
+
return null;
|
|
94
|
+
return [
|
|
95
|
+
`The model backend is set to run "${command}", which is not installed on this machine.`,
|
|
96
|
+
"",
|
|
97
|
+
"Re-running the installer installs it and walks you through the sign-in:",
|
|
98
|
+
"",
|
|
99
|
+
` ${INSTALLER_ONE_LINER}`,
|
|
100
|
+
"",
|
|
101
|
+
`Or install it by hand (the "claude" preset expects the Claude Code CLI:`,
|
|
102
|
+
"https://claude.com/claude-code), or use a hosted endpoint instead by putting",
|
|
103
|
+
"an API key in ~/.config/verify-tracking/config.env:",
|
|
104
|
+
"",
|
|
105
|
+
" AI_API_KEY=...",
|
|
106
|
+
"",
|
|
107
|
+
].join("\n");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Whether the Claude Code CLI on this machine is signed in.
|
|
111
|
+
*
|
|
112
|
+
* Installing the CLI and authenticating it are two separate things, and only
|
|
113
|
+
* the first is a file on disk. A logged-out CLI is on the PATH, answers
|
|
114
|
+
* `--version`, and then fails the actual call — minutes into the run, with an
|
|
115
|
+
* exit code and a stderr blob that read like a bug in this tool rather than an
|
|
116
|
+
* expired session. `auth status --json` is the CLI's own answer to the
|
|
117
|
+
* question, so it is asked up front.
|
|
118
|
+
*
|
|
119
|
+
* Only meaningful for the claude preset: no other backend authenticates this
|
|
120
|
+
* way, and a custom `--cli-cmd` may not have a notion of sign-in at all.
|
|
121
|
+
*/
|
|
122
|
+
export function isClaudeSignedIn(command) {
|
|
123
|
+
try {
|
|
124
|
+
const probe = spawnSync(command, ["auth", "status", "--json"], {
|
|
125
|
+
encoding: "utf8",
|
|
126
|
+
// A hung probe must not become a hung run: the check is a convenience,
|
|
127
|
+
// and its failure mode should be "skip the check", never "never start".
|
|
128
|
+
timeout: 10_000,
|
|
129
|
+
});
|
|
130
|
+
if (probe.error || probe.status !== 0)
|
|
131
|
+
return false;
|
|
132
|
+
return /"loggedIn"\s*:\s*true/.test(probe.stdout ?? "");
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// An unreadable answer is not evidence of being logged out, and refusing
|
|
136
|
+
// to run on it would be worse than letting the AI step report the truth.
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The message to show when the Claude CLI is installed but not signed in, or
|
|
142
|
+
* null when it is (or when the question does not apply to this backend).
|
|
143
|
+
*
|
|
144
|
+
* The sign-in is an OAuth flow through a browser — the one step the installer
|
|
145
|
+
* cannot complete unattended — so this names the single command that finishes
|
|
146
|
+
* it rather than sending anyone back to the installer.
|
|
147
|
+
*/
|
|
148
|
+
export function claudeAuthPreflightError(command, preset) {
|
|
149
|
+
if (preset !== "claude")
|
|
150
|
+
return null;
|
|
151
|
+
if (isClaudeSignedIn(command))
|
|
152
|
+
return null;
|
|
153
|
+
return [
|
|
154
|
+
`"${command}" is installed but not signed in, so it cannot answer this run.`,
|
|
155
|
+
"",
|
|
156
|
+
"Sign in once on this machine — it opens a browser and takes a moment:",
|
|
157
|
+
"",
|
|
158
|
+
` ${command} auth login`,
|
|
159
|
+
"",
|
|
160
|
+
"Then run this again. To use a hosted endpoint instead, put a key in",
|
|
161
|
+
"~/.config/verify-tracking/config.env:",
|
|
162
|
+
"",
|
|
163
|
+
" AI_API_KEY=...",
|
|
164
|
+
"",
|
|
165
|
+
].join("\n");
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/config/preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Where a distributed binary looks for its settings.
|
|
6
|
+
*
|
|
7
|
+
* `bun run src/cli.ts` gets the project's `.env` for free — Bun loads it from
|
|
8
|
+
* the working directory before the process starts. A compiled binary keeps
|
|
9
|
+
* that behaviour, which is the problem: a teammate who runs the binary from
|
|
10
|
+
* their home directory or Downloads folder has no `.env` there, and the run
|
|
11
|
+
* fails at the AI step with an authentication error that says nothing about
|
|
12
|
+
* the missing file. So the binary reads a fixed path in the user's config
|
|
13
|
+
* directory as well, set up once and independent of where they run from.
|
|
14
|
+
*/
|
|
15
|
+
export const USER_CONFIG_PATH = join(homedir(), ".config", "verify-tracking", "config.env");
|
|
16
|
+
/**
|
|
17
|
+
* The directory the default report and evidence writes are rooted at, relative
|
|
18
|
+
* to the working directory unless given as an absolute path.
|
|
19
|
+
*
|
|
20
|
+
* Installed globally, this CLI is run from wherever the operator happens to be,
|
|
21
|
+
* and the default grows a `verification/` tree there. That is right for a run
|
|
22
|
+
* inside this repo — the review dashboard reads exactly those paths — and
|
|
23
|
+
* unwelcome in someone's home directory, so it stays the default and becomes
|
|
24
|
+
* overridable rather than moving.
|
|
25
|
+
*
|
|
26
|
+
* Resolved on call, not at import: `loadUserConfig()` runs inside main(), long
|
|
27
|
+
* after this module's imports have been evaluated, so a value read at module
|
|
28
|
+
* scope would freeze the default before the config file was ever seen.
|
|
29
|
+
*/
|
|
30
|
+
export const DEFAULT_OUTPUT_ROOT = "verification";
|
|
31
|
+
export function outputRoot() {
|
|
32
|
+
const fromEnv = process.env.VERIFY_TRACKING_OUTPUT_DIR?.trim();
|
|
33
|
+
return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_OUTPUT_ROOT;
|
|
34
|
+
}
|
|
35
|
+
/** A `KEY=value` line, minus `export`, comments, and surrounding quotes. */
|
|
36
|
+
function parseLine(line) {
|
|
37
|
+
const trimmed = line.trim().replace(/^export\s+/, "");
|
|
38
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
39
|
+
return null;
|
|
40
|
+
const eq = trimmed.indexOf("=");
|
|
41
|
+
if (eq <= 0)
|
|
42
|
+
return null;
|
|
43
|
+
const key = trimmed.slice(0, eq).trim();
|
|
44
|
+
const raw = trimmed.slice(eq + 1).trim();
|
|
45
|
+
const value = /^(".*"|'.*')$/s.test(raw) ? raw.slice(1, -1) : raw;
|
|
46
|
+
return [key, value];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Loads the user config file into `process.env`, without overwriting anything
|
|
50
|
+
* already set. That order makes the precedence match the flags: an explicit
|
|
51
|
+
* `AI_MODEL=… verify-tracking` or a project `.env` still wins over the file a
|
|
52
|
+
* teammate wrote once and forgot, so the file is a default and never a
|
|
53
|
+
* surprise override.
|
|
54
|
+
*
|
|
55
|
+
* Returns the path when one was read, so the CLI can name it in diagnostics.
|
|
56
|
+
*/
|
|
57
|
+
export function loadUserConfig(path = USER_CONFIG_PATH) {
|
|
58
|
+
if (!existsSync(path))
|
|
59
|
+
return null;
|
|
60
|
+
let contents;
|
|
61
|
+
try {
|
|
62
|
+
contents = readFileSync(path, "utf8");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// An unreadable config is not worth failing the run over: the flags and
|
|
66
|
+
// the environment may well carry everything the run needs.
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
for (const line of contents.split(/\r?\n/)) {
|
|
70
|
+
const entry = parseLine(line);
|
|
71
|
+
if (!entry)
|
|
72
|
+
continue;
|
|
73
|
+
const [key, value] = entry;
|
|
74
|
+
if (process.env[key] === undefined)
|
|
75
|
+
process.env[key] = value;
|
|
76
|
+
}
|
|
77
|
+
return path;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=user-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-config.js","sourceRoot":"","sources":["../../src/config/user-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,iBAAiB,EAAE,YAAY,CAAC,CAAC;AAE5F;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD,MAAM,UAAU,UAAU;IACxB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,EAAE,IAAI,EAAE,CAAC;IAC/D,OAAO,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC;AACvE,CAAC;AAED,4EAA4E;AAC5E,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,EAAE,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,gBAAgB;IAC5D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;QACxE,2DAA2D;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC;QAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|