@mcoda/integrations 0.1.9 → 0.1.10
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 +1 -1
- package/dist/docdex/DocdexClient.d.ts +10 -0
- package/dist/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/docdex/DocdexClient.js +112 -4
- package/dist/docdex/DocdexRuntime.d.ts +23 -6
- package/dist/docdex/DocdexRuntime.d.ts.map +1 -1
- package/dist/docdex/DocdexRuntime.js +124 -50
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/qa/ChromiumQaAdapter.d.ts +59 -1
- package/dist/qa/ChromiumQaAdapter.d.ts.map +1 -1
- package/dist/qa/ChromiumQaAdapter.js +1636 -55
- package/dist/qa/CliQaAdapter.d.ts +2 -0
- package/dist/qa/CliQaAdapter.d.ts.map +1 -1
- package/dist/qa/CliQaAdapter.js +88 -33
- package/dist/qa/QaTypes.d.ts +4 -0
- package/dist/qa/QaTypes.d.ts.map +1 -1
- package/dist/telemetry/TelemetryClient.d.ts +13 -0
- package/dist/telemetry/TelemetryClient.d.ts.map +1 -1
- package/dist/vcs/VcsClient.d.ts +18 -0
- package/dist/vcs/VcsClient.d.ts.map +1 -1
- package/dist/vcs/VcsClient.js +102 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ const docs = await client.search({ docType: "rfp", query: "payments" });
|
|
|
27
27
|
|
|
28
28
|
## Notes
|
|
29
29
|
- Docdex state lives under `~/.docdex` (managed by the `docdex` CLI); mcoda does not create repo-local `.docdex`.
|
|
30
|
-
- Chromium QA expects
|
|
30
|
+
- Chromium QA expects Docdex-installed Chromium (`docdex setup` or `MCODA_QA_CHROMIUM_PATH`).
|
|
31
31
|
- Some integrations call external services; configure base URLs and tokens as needed.
|
|
32
32
|
- Primarily used by the mcoda CLI; APIs may evolve.
|
|
33
33
|
|
|
@@ -28,14 +28,21 @@ export declare class DocdexClient {
|
|
|
28
28
|
private resolvedBaseUrl?;
|
|
29
29
|
private repoId?;
|
|
30
30
|
private initializing;
|
|
31
|
+
private disabledReason?;
|
|
31
32
|
constructor(options?: {
|
|
32
33
|
workspaceRoot?: string;
|
|
33
34
|
baseUrl?: string;
|
|
34
35
|
authToken?: string;
|
|
35
36
|
repoId?: string;
|
|
36
37
|
});
|
|
38
|
+
disable(reason?: string): void;
|
|
39
|
+
isAvailable(): boolean;
|
|
37
40
|
private normalizePath;
|
|
41
|
+
private resolveLocalDocPath;
|
|
42
|
+
private loadLocalDoc;
|
|
38
43
|
private resolveBaseUrl;
|
|
44
|
+
private buildMissingRepoMessage;
|
|
45
|
+
ensureRepoScope(): Promise<void>;
|
|
39
46
|
private ensureRepoInitialized;
|
|
40
47
|
private fetchRemote;
|
|
41
48
|
private buildLocalDoc;
|
|
@@ -51,5 +58,8 @@ export declare class DocdexClient {
|
|
|
51
58
|
reindex(): Promise<void>;
|
|
52
59
|
registerDocument(input: RegisterDocumentInput): Promise<DocdexDocument>;
|
|
53
60
|
ensureRegisteredFromFile(docPath: string, docType: string, metadata?: Record<string, unknown>): Promise<DocdexDocument>;
|
|
61
|
+
private callMcp;
|
|
62
|
+
memorySave(text: string): Promise<void>;
|
|
63
|
+
savePreference(agentId: string, category: string, content: string): Promise<void>;
|
|
54
64
|
}
|
|
55
65
|
//# sourceMappingURL=DocdexClient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocdexClient.d.ts","sourceRoot":"","sources":["../../src/docdex/DocdexClient.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAkDD,qBAAa,YAAY;
|
|
1
|
+
{"version":3,"file":"DocdexClient.d.ts","sourceRoot":"","sources":["../../src/docdex/DocdexClient.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAkDD,qBAAa,YAAY;IAOrB,OAAO,CAAC,OAAO;IANjB,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAC,CAAS;gBAGtB,OAAO,GAAE;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;KACZ;IAKR,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAI9B,WAAW,IAAI,OAAO;IAItB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,mBAAmB;YASb,YAAY;YAYZ,cAAc;IAY5B,OAAO,CAAC,uBAAuB;IAKzB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;YAWxB,qBAAqB;YA0BrB,WAAW;IAwCzB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,mBAAmB;IAmDrB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA4BtD,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAkB1F,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAkBtH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAcxB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC;IA0BvE,wBAAwB,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,cAAc,CAAC;YAqBZ,OAAO;IAyBf,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQvC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQxF"}
|
|
@@ -59,6 +59,12 @@ export class DocdexClient {
|
|
|
59
59
|
this.initializing = false;
|
|
60
60
|
this.repoId = options.repoId;
|
|
61
61
|
}
|
|
62
|
+
disable(reason) {
|
|
63
|
+
this.disabledReason = reason?.trim() || "docdex unavailable";
|
|
64
|
+
}
|
|
65
|
+
isAvailable() {
|
|
66
|
+
return !this.disabledReason;
|
|
67
|
+
}
|
|
62
68
|
normalizePath(inputPath) {
|
|
63
69
|
if (!inputPath)
|
|
64
70
|
return undefined;
|
|
@@ -71,7 +77,32 @@ export class DocdexClient {
|
|
|
71
77
|
}
|
|
72
78
|
return absolute;
|
|
73
79
|
}
|
|
80
|
+
resolveLocalDocPath(docPath) {
|
|
81
|
+
if (!this.options.workspaceRoot)
|
|
82
|
+
return undefined;
|
|
83
|
+
const root = path.resolve(this.options.workspaceRoot);
|
|
84
|
+
const absolute = path.resolve(path.isAbsolute(docPath) ? docPath : path.join(root, docPath));
|
|
85
|
+
if (!absolute.startsWith(root))
|
|
86
|
+
return undefined;
|
|
87
|
+
const relative = path.relative(root, absolute);
|
|
88
|
+
return { absolute, relative: relative || undefined };
|
|
89
|
+
}
|
|
90
|
+
async loadLocalDoc(docPath, docType) {
|
|
91
|
+
const resolved = this.resolveLocalDocPath(docPath);
|
|
92
|
+
if (!resolved)
|
|
93
|
+
return undefined;
|
|
94
|
+
try {
|
|
95
|
+
const content = await fs.readFile(resolved.absolute, "utf8");
|
|
96
|
+
const inferred = docType || inferDocType(resolved.relative ?? docPath);
|
|
97
|
+
return this.buildLocalDoc(inferred, resolved.relative ?? docPath, content);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
74
103
|
async resolveBaseUrl() {
|
|
104
|
+
if (this.disabledReason)
|
|
105
|
+
return undefined;
|
|
75
106
|
if (this.options.baseUrl !== undefined) {
|
|
76
107
|
const trimmed = this.options.baseUrl.trim();
|
|
77
108
|
return trimmed ? normalizeBaseUrl(trimmed) : undefined;
|
|
@@ -82,6 +113,21 @@ export class DocdexClient {
|
|
|
82
113
|
this.resolvedBaseUrl = resolved ? normalizeBaseUrl(resolved) : undefined;
|
|
83
114
|
return this.resolvedBaseUrl;
|
|
84
115
|
}
|
|
116
|
+
buildMissingRepoMessage() {
|
|
117
|
+
const root = this.options.workspaceRoot ? path.resolve(this.options.workspaceRoot) : "unknown workspace";
|
|
118
|
+
return `Docdex repo scope missing for ${root}. Ensure docdexd is running and initialized for this repo, or set MCODA_DOCDEX_URL and MCODA_DOCDEX_REPO_ID.`;
|
|
119
|
+
}
|
|
120
|
+
async ensureRepoScope() {
|
|
121
|
+
const baseUrl = await this.resolveBaseUrl();
|
|
122
|
+
if (!baseUrl)
|
|
123
|
+
return;
|
|
124
|
+
if (!this.repoId) {
|
|
125
|
+
await this.ensureRepoInitialized(baseUrl, true);
|
|
126
|
+
}
|
|
127
|
+
if (!this.repoId) {
|
|
128
|
+
throw new Error(this.buildMissingRepoMessage());
|
|
129
|
+
}
|
|
130
|
+
}
|
|
85
131
|
async ensureRepoInitialized(baseUrl, force = false) {
|
|
86
132
|
if ((this.repoId && !force) || this.initializing)
|
|
87
133
|
return;
|
|
@@ -113,18 +159,29 @@ export class DocdexClient {
|
|
|
113
159
|
}
|
|
114
160
|
}
|
|
115
161
|
async fetchRemote(pathname, init) {
|
|
162
|
+
if (this.disabledReason) {
|
|
163
|
+
throw new Error(`Docdex unavailable: ${this.disabledReason}`);
|
|
164
|
+
}
|
|
116
165
|
const baseUrl = await this.resolveBaseUrl();
|
|
117
166
|
if (!baseUrl) {
|
|
118
167
|
throw new Error("Docdex baseUrl not configured. Run docdex setup or set MCODA_DOCDEX_URL.");
|
|
119
168
|
}
|
|
120
|
-
await this.
|
|
169
|
+
await this.ensureRepoScope();
|
|
121
170
|
const url = new URL(pathname, baseUrl);
|
|
171
|
+
if (this.repoId && !url.searchParams.has("repo_id")) {
|
|
172
|
+
url.searchParams.set("repo_id", this.repoId);
|
|
173
|
+
}
|
|
174
|
+
if (this.options.workspaceRoot && !url.searchParams.has("repo_root")) {
|
|
175
|
+
url.searchParams.set("repo_root", path.resolve(this.options.workspaceRoot));
|
|
176
|
+
}
|
|
122
177
|
const buildHeaders = () => {
|
|
123
178
|
const headers = { "Content-Type": "application/json" };
|
|
124
179
|
if (this.options.authToken)
|
|
125
180
|
headers.authorization = `Bearer ${this.options.authToken}`;
|
|
126
181
|
if (this.repoId)
|
|
127
182
|
headers["x-docdex-repo-id"] = this.repoId;
|
|
183
|
+
if (this.options.workspaceRoot)
|
|
184
|
+
headers["x-docdex-repo-root"] = path.resolve(this.options.workspaceRoot);
|
|
128
185
|
return { ...headers, ...init?.headers };
|
|
129
186
|
};
|
|
130
187
|
const response = await fetch(url, { ...init, headers: buildHeaders() });
|
|
@@ -241,9 +298,19 @@ export class DocdexClient {
|
|
|
241
298
|
async findDocumentByPath(docPath, docType) {
|
|
242
299
|
const normalized = this.normalizePath(docPath);
|
|
243
300
|
const query = normalized ?? docPath;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
301
|
+
let docs = [];
|
|
302
|
+
try {
|
|
303
|
+
docs = await this.search({ query, docType });
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
const local = await this.loadLocalDoc(docPath, docType);
|
|
307
|
+
if (local)
|
|
308
|
+
return local;
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
if (!docs.length) {
|
|
312
|
+
return await this.loadLocalDoc(docPath, docType);
|
|
313
|
+
}
|
|
247
314
|
if (!normalized)
|
|
248
315
|
return docs[0];
|
|
249
316
|
return docs.find((doc) => doc.path === normalized) ?? docs[0];
|
|
@@ -331,4 +398,45 @@ export class DocdexClient {
|
|
|
331
398
|
return this.buildLocalDoc(inferredType, normalizedPath, content, metadata);
|
|
332
399
|
}
|
|
333
400
|
}
|
|
401
|
+
async callMcp(toolName, args) {
|
|
402
|
+
const payload = {
|
|
403
|
+
jsonrpc: "2.0",
|
|
404
|
+
id: randomUUID(),
|
|
405
|
+
method: "tools/call",
|
|
406
|
+
params: {
|
|
407
|
+
name: toolName,
|
|
408
|
+
arguments: args,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
const response = await this.fetchRemote("/v1/mcp", {
|
|
412
|
+
method: "POST",
|
|
413
|
+
body: JSON.stringify(payload),
|
|
414
|
+
});
|
|
415
|
+
const raw = (await response.json());
|
|
416
|
+
if (raw.error) {
|
|
417
|
+
throw new Error(raw.error.message ?? "Docdex MCP call failed");
|
|
418
|
+
}
|
|
419
|
+
const result = raw.result;
|
|
420
|
+
if (result && typeof result === "object" && result.structuredContent !== undefined) {
|
|
421
|
+
return result.structuredContent;
|
|
422
|
+
}
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
async memorySave(text) {
|
|
426
|
+
if (!text.trim())
|
|
427
|
+
return;
|
|
428
|
+
await this.callMcp("docdex_memory_save", {
|
|
429
|
+
project_root: this.options.workspaceRoot,
|
|
430
|
+
text,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
async savePreference(agentId, category, content) {
|
|
434
|
+
if (!agentId.trim() || !category.trim() || !content.trim())
|
|
435
|
+
return;
|
|
436
|
+
await this.callMcp("docdex_save_preference", {
|
|
437
|
+
agent_id: agentId,
|
|
438
|
+
category,
|
|
439
|
+
content,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
334
442
|
}
|
|
@@ -8,18 +8,32 @@ export interface DocdexCheckResult {
|
|
|
8
8
|
details?: Record<string, unknown>;
|
|
9
9
|
}>;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface DocdexHealthSummary {
|
|
12
12
|
ok: boolean;
|
|
13
13
|
message?: string;
|
|
14
|
-
|
|
15
|
-
browsers?: Array<{
|
|
14
|
+
failedChecks?: Array<{
|
|
16
15
|
name?: string;
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
status?: string;
|
|
17
|
+
message?: string;
|
|
19
18
|
}>;
|
|
20
19
|
}
|
|
20
|
+
export interface DocdexChromiumDetails {
|
|
21
|
+
path?: string;
|
|
22
|
+
manifestPath?: string;
|
|
23
|
+
installedAt?: string;
|
|
24
|
+
version?: string;
|
|
25
|
+
platform?: string;
|
|
26
|
+
downloadUrl?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface DocdexBrowserInfo {
|
|
29
|
+
ok: boolean;
|
|
30
|
+
message?: string;
|
|
31
|
+
installHint?: string;
|
|
32
|
+
autoInstallEnabled?: boolean;
|
|
33
|
+
configuredKind?: string;
|
|
34
|
+
chromium?: DocdexChromiumDetails;
|
|
35
|
+
}
|
|
21
36
|
export declare const resolveDocdexBinary: () => string | undefined;
|
|
22
|
-
export declare const resolvePlaywrightCli: () => string | undefined;
|
|
23
37
|
export declare const runDocdex: (args: string[], options?: {
|
|
24
38
|
cwd?: string;
|
|
25
39
|
env?: NodeJS.ProcessEnv;
|
|
@@ -27,14 +41,17 @@ export declare const runDocdex: (args: string[], options?: {
|
|
|
27
41
|
stdout: string;
|
|
28
42
|
stderr: string;
|
|
29
43
|
}>;
|
|
44
|
+
export declare const parseDocdexCheckOutput: (output: string) => DocdexCheckResult;
|
|
30
45
|
export declare const readDocdexCheck: (options?: {
|
|
31
46
|
cwd?: string;
|
|
32
47
|
env?: NodeJS.ProcessEnv;
|
|
33
48
|
}) => Promise<DocdexCheckResult>;
|
|
49
|
+
export declare const summarizeDocdexCheck: (check: DocdexCheckResult) => DocdexHealthSummary;
|
|
34
50
|
export declare const resolveDocdexBaseUrl: (options?: {
|
|
35
51
|
cwd?: string;
|
|
36
52
|
env?: NodeJS.ProcessEnv;
|
|
37
53
|
}) => Promise<string | undefined>;
|
|
54
|
+
export declare const parseDocdexBrowserCheck: (check: DocdexCheckResult) => DocdexBrowserInfo;
|
|
38
55
|
export declare const resolveDocdexBrowserInfo: (options?: {
|
|
39
56
|
cwd?: string;
|
|
40
57
|
env?: NodeJS.ProcessEnv;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocdexRuntime.d.ts","sourceRoot":"","sources":["../../src/docdex/DocdexRuntime.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"DocdexRuntime.d.ts","sourceRoot":"","sources":["../../src/docdex/DocdexRuntime.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC;AAuBD,eAAO,MAAM,mBAAmB,QAAO,MAAM,GAAG,SAI/C,CAAC;AAEF,eAAO,MAAM,SAAS,GACpB,MAAM,MAAM,EAAE,EACd,UAAS;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAO,KACtD,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAU5C,CAAC;AAUF,eAAO,MAAM,sBAAsB,GAAI,QAAQ,MAAM,KAAG,iBAiCvD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,UAAS;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAO,KAAG,OAAO,CAAC,iBAAiB,CAexH,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,iBAAiB,KAAG,mBAsB/D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,UAAS;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAO,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAsB9H,CAAC;AA0BF,eAAO,MAAM,uBAAuB,GAAI,OAAO,iBAAiB,KAAG,iBAoClE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,UAAS;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAO,KACtD,OAAO,CAAC,iBAAiB,CAW3B,CAAC"}
|
|
@@ -29,25 +29,6 @@ export const resolveDocdexBinary = () => {
|
|
|
29
29
|
return undefined;
|
|
30
30
|
return path.join(root, "bin", "docdex.js");
|
|
31
31
|
};
|
|
32
|
-
export const resolvePlaywrightCli = () => {
|
|
33
|
-
const require = createRequire(import.meta.url);
|
|
34
|
-
try {
|
|
35
|
-
return require.resolve("playwright/cli.js");
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
// fall through to docdex-local resolution
|
|
39
|
-
}
|
|
40
|
-
const root = resolveDocdexPackageRoot();
|
|
41
|
-
if (!root)
|
|
42
|
-
return undefined;
|
|
43
|
-
try {
|
|
44
|
-
const requireFromDocdex = createRequire(path.join(root, "package.json"));
|
|
45
|
-
return requireFromDocdex.resolve("playwright/cli.js");
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
32
|
export const runDocdex = async (args, options = {}) => {
|
|
52
33
|
const binary = resolveDocdexBinary();
|
|
53
34
|
if (!binary) {
|
|
@@ -59,6 +40,46 @@ export const runDocdex = async (args, options = {}) => {
|
|
|
59
40
|
});
|
|
60
41
|
return { stdout: stdout ?? "", stderr: stderr ?? "" };
|
|
61
42
|
};
|
|
43
|
+
const MAX_DOCDEX_SNIPPET = 240;
|
|
44
|
+
const formatDocdexSnippet = (value) => {
|
|
45
|
+
const trimmed = value.trim().replace(/\s+/g, " ");
|
|
46
|
+
if (trimmed.length <= MAX_DOCDEX_SNIPPET)
|
|
47
|
+
return trimmed;
|
|
48
|
+
return `${trimmed.slice(0, MAX_DOCDEX_SNIPPET)}...`;
|
|
49
|
+
};
|
|
50
|
+
export const parseDocdexCheckOutput = (output) => {
|
|
51
|
+
const trimmed = output.trim();
|
|
52
|
+
if (!trimmed) {
|
|
53
|
+
throw new Error("Docdex check returned empty output");
|
|
54
|
+
}
|
|
55
|
+
const attempts = [{ label: "full", candidate: trimmed }];
|
|
56
|
+
const braceStart = trimmed.indexOf("{");
|
|
57
|
+
const braceEnd = trimmed.lastIndexOf("}");
|
|
58
|
+
if (braceStart !== -1 && braceEnd > braceStart) {
|
|
59
|
+
attempts.push({ label: "brace-slice", candidate: trimmed.slice(braceStart, braceEnd + 1) });
|
|
60
|
+
}
|
|
61
|
+
const lines = trimmed.split(/\r?\n/);
|
|
62
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
63
|
+
const line = lines[i].trim();
|
|
64
|
+
if (!line.startsWith("{"))
|
|
65
|
+
continue;
|
|
66
|
+
const joined = lines.slice(i).join("\n").trim();
|
|
67
|
+
attempts.push({ label: "line-join", candidate: joined });
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
let lastError;
|
|
71
|
+
for (const attempt of attempts) {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(attempt.candidate);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
lastError = error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const snippet = formatDocdexSnippet(trimmed);
|
|
80
|
+
const message = lastError?.message ? `${lastError.message}. ` : "";
|
|
81
|
+
throw new Error(`Docdex check returned invalid JSON: ${message}Output: "${snippet}"`);
|
|
82
|
+
};
|
|
62
83
|
export const readDocdexCheck = async (options = {}) => {
|
|
63
84
|
let stdout = "";
|
|
64
85
|
let stderr = "";
|
|
@@ -74,15 +95,29 @@ export const readDocdexCheck = async (options = {}) => {
|
|
|
74
95
|
}
|
|
75
96
|
}
|
|
76
97
|
const trimmed = stdout.trim() || stderr.trim();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
return parseDocdexCheckOutput(trimmed);
|
|
99
|
+
};
|
|
100
|
+
export const summarizeDocdexCheck = (check) => {
|
|
101
|
+
const failures = check.checks?.filter((item) => item?.status && item.status !== "ok")?.map((item) => ({
|
|
102
|
+
name: item.name,
|
|
103
|
+
status: item.status,
|
|
104
|
+
message: item.message,
|
|
105
|
+
})) ?? [];
|
|
106
|
+
if (check.success === false || failures.length > 0) {
|
|
107
|
+
const details = failures
|
|
108
|
+
.map((item) => {
|
|
109
|
+
const head = item.name ? `${item.name}=${item.status ?? "error"}` : item.status ?? "error";
|
|
110
|
+
const tail = item.message ? ` (${item.message})` : "";
|
|
111
|
+
return `${head}${tail}`;
|
|
112
|
+
})
|
|
113
|
+
.join("; ");
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
message: details || "Docdex check failed.",
|
|
117
|
+
failedChecks: failures,
|
|
118
|
+
};
|
|
85
119
|
}
|
|
120
|
+
return { ok: true };
|
|
86
121
|
};
|
|
87
122
|
export const resolveDocdexBaseUrl = async (options = {}) => {
|
|
88
123
|
for (const key of DOCDEX_ENV_URLS) {
|
|
@@ -109,34 +144,73 @@ export const resolveDocdexBaseUrl = async (options = {}) => {
|
|
|
109
144
|
return undefined;
|
|
110
145
|
}
|
|
111
146
|
};
|
|
147
|
+
const coerceString = (value) => {
|
|
148
|
+
if (typeof value !== "string")
|
|
149
|
+
return undefined;
|
|
150
|
+
const trimmed = value.trim();
|
|
151
|
+
return trimmed ? trimmed : undefined;
|
|
152
|
+
};
|
|
153
|
+
const coerceChromiumDetails = (value) => {
|
|
154
|
+
if (!value || typeof value !== "object")
|
|
155
|
+
return undefined;
|
|
156
|
+
const details = value;
|
|
157
|
+
return {
|
|
158
|
+
path: coerceString(details.path),
|
|
159
|
+
manifestPath: coerceString(details.manifest_path ?? details.manifestPath),
|
|
160
|
+
installedAt: coerceString(details.installed_at ?? details.installedAt),
|
|
161
|
+
version: coerceString(details.version),
|
|
162
|
+
platform: coerceString(details.platform),
|
|
163
|
+
downloadUrl: coerceString(details.download_url ?? details.downloadUrl),
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
const buildBrowserSetupHint = (installHint) => {
|
|
167
|
+
const hint = installHint ?? "docdexd browser install";
|
|
168
|
+
return `Run \`${hint}\` to install the headless Chromium browser.`;
|
|
169
|
+
};
|
|
170
|
+
export const parseDocdexBrowserCheck = (check) => {
|
|
171
|
+
const browserCheck = check.checks?.find((c) => c.name === "browser");
|
|
172
|
+
const details = browserCheck?.details;
|
|
173
|
+
const chromium = coerceChromiumDetails(details?.chromium);
|
|
174
|
+
const installHint = coerceString(details?.install_hint);
|
|
175
|
+
const autoInstallEnabled = typeof details?.auto_install_enabled === "boolean" ? details.auto_install_enabled : undefined;
|
|
176
|
+
const configuredKind = coerceString(details?.configured_kind);
|
|
177
|
+
if (!browserCheck) {
|
|
178
|
+
const setupHint = buildBrowserSetupHint(installHint);
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
message: `Docdex browser check unavailable. ${setupHint}`,
|
|
182
|
+
chromium,
|
|
183
|
+
installHint,
|
|
184
|
+
autoInstallEnabled,
|
|
185
|
+
configuredKind,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (browserCheck.status !== "ok") {
|
|
189
|
+
const setupHint = buildBrowserSetupHint(installHint);
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
message: `${browserCheck.message ?? "Docdex browser check failed."} ${setupHint}`,
|
|
193
|
+
chromium,
|
|
194
|
+
installHint,
|
|
195
|
+
autoInstallEnabled,
|
|
196
|
+
configuredKind,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
ok: true,
|
|
201
|
+
chromium,
|
|
202
|
+
installHint,
|
|
203
|
+
autoInstallEnabled,
|
|
204
|
+
configuredKind,
|
|
205
|
+
};
|
|
206
|
+
};
|
|
112
207
|
export const resolveDocdexBrowserInfo = async (options = {}) => {
|
|
113
|
-
const setupHint = "Run `docdex setup` to install Playwright and at least one browser.";
|
|
114
208
|
try {
|
|
115
209
|
const check = await readDocdexCheck(options);
|
|
116
|
-
|
|
117
|
-
if (!browserCheck || browserCheck.status !== "ok") {
|
|
118
|
-
return {
|
|
119
|
-
ok: false,
|
|
120
|
-
message: `${browserCheck?.message ?? "Docdex browser check failed."} ${setupHint}`,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
const details = browserCheck.details ?? {};
|
|
124
|
-
const playwright = details.playwright ?? {};
|
|
125
|
-
const browsers = Array.isArray(playwright.browsers) ? playwright.browsers : [];
|
|
126
|
-
if (!browsers.length) {
|
|
127
|
-
return {
|
|
128
|
-
ok: false,
|
|
129
|
-
message: `Docdex has no Playwright browsers configured. ${setupHint}`,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
const browsersPath = typeof playwright.browsers_path === "string" ? playwright.browsers_path : undefined;
|
|
133
|
-
return {
|
|
134
|
-
ok: true,
|
|
135
|
-
browsersPath,
|
|
136
|
-
browsers,
|
|
137
|
-
};
|
|
210
|
+
return parseDocdexBrowserCheck(check);
|
|
138
211
|
}
|
|
139
212
|
catch (error) {
|
|
213
|
+
const setupHint = buildBrowserSetupHint();
|
|
140
214
|
return {
|
|
141
215
|
ok: false,
|
|
142
216
|
message: `Docdex check failed: ${error.message}. ${setupHint}`,
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,68 @@
|
|
|
1
1
|
import { QaProfile } from '@mcoda/shared/qa/QaProfile.js';
|
|
2
|
+
import type { QaBrowserAction } from '@mcoda/shared/qa/QaPlan.js';
|
|
2
3
|
import { QaAdapter } from './QaAdapter.js';
|
|
3
4
|
import { QaContext, QaEnsureResult, QaRunResult } from './QaTypes.js';
|
|
5
|
+
type BrowserFetchResult = {
|
|
6
|
+
html: string;
|
|
7
|
+
innerText?: string;
|
|
8
|
+
textContent?: string;
|
|
9
|
+
status?: number;
|
|
10
|
+
finalUrl?: string;
|
|
11
|
+
};
|
|
12
|
+
type BrowserActionResult = {
|
|
13
|
+
index: number;
|
|
14
|
+
type: QaBrowserAction['type'];
|
|
15
|
+
ok: boolean;
|
|
16
|
+
message?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
durationMs?: number;
|
|
19
|
+
};
|
|
20
|
+
type BrowserActionSnapshot = {
|
|
21
|
+
index: number;
|
|
22
|
+
name: string;
|
|
23
|
+
html: string;
|
|
24
|
+
innerText?: string;
|
|
25
|
+
textContent?: string;
|
|
26
|
+
url?: string;
|
|
27
|
+
};
|
|
28
|
+
export declare const resolveChromiumBinary: () => Promise<string | undefined>;
|
|
29
|
+
declare class NetworkIdleTracker {
|
|
30
|
+
inflight: number;
|
|
31
|
+
lastActivity: number;
|
|
32
|
+
sawLoad: boolean;
|
|
33
|
+
documentStatus?: number;
|
|
34
|
+
documentUrl?: string;
|
|
35
|
+
handle(method: string, params?: any): void;
|
|
36
|
+
}
|
|
37
|
+
declare class CdpClient {
|
|
38
|
+
private ws;
|
|
39
|
+
private queue;
|
|
40
|
+
private nextId;
|
|
41
|
+
private closed;
|
|
42
|
+
static connect(wsUrl: string): Promise<CdpClient>;
|
|
43
|
+
private constructor();
|
|
44
|
+
call(method: string, params: Record<string, unknown>, tracker?: NetworkIdleTracker, timeoutMs?: number): Promise<any>;
|
|
45
|
+
waitForNetworkIdle(tracker: NetworkIdleTracker, timeoutMs: number): Promise<boolean>;
|
|
46
|
+
close(): void;
|
|
47
|
+
}
|
|
4
48
|
export declare class ChromiumQaAdapter implements QaAdapter {
|
|
5
|
-
private resolveCwd;
|
|
6
49
|
ensureInstalled(profile: QaProfile, ctx: QaContext): Promise<QaEnsureResult>;
|
|
7
50
|
private persistLogs;
|
|
51
|
+
private persistBrowserArtifacts;
|
|
8
52
|
invoke(profile: QaProfile, ctx: QaContext): Promise<QaRunResult>;
|
|
53
|
+
private runBrowserActions;
|
|
54
|
+
private runBrowser;
|
|
9
55
|
}
|
|
56
|
+
export declare const __testing: {
|
|
57
|
+
resolveActionUrl: (actionUrl: string | undefined, baseUrl: string | undefined) => string | undefined;
|
|
58
|
+
resolveActionTimeoutMs: (action: QaBrowserAction, fallbackMs: number) => number;
|
|
59
|
+
runBrowserActionsWithClient: (client: CdpClient, actions: QaBrowserAction[], baseUrl: string | undefined, timeoutMs: number) => Promise<{
|
|
60
|
+
outcome: "pass" | "fail";
|
|
61
|
+
errorMessage?: string;
|
|
62
|
+
results: BrowserActionResult[];
|
|
63
|
+
snapshots: BrowserActionSnapshot[];
|
|
64
|
+
finalDom?: BrowserFetchResult;
|
|
65
|
+
}>;
|
|
66
|
+
};
|
|
67
|
+
export {};
|
|
10
68
|
//# sourceMappingURL=ChromiumQaAdapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChromiumQaAdapter.d.ts","sourceRoot":"","sources":["../../src/qa/ChromiumQaAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ChromiumQaAdapter.d.ts","sourceRoot":"","sources":["../../src/qa/ChromiumQaAdapter.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAmFtE,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAoOF,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,MAAM,GAAG,SAAS,CAC1C,CAAC;AAgdhC,cAAM,kBAAkB;IACtB,QAAQ,SAAK;IACb,YAAY,SAAc;IAC1B,OAAO,UAAS;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG;CA8BpC;AAED,cAAM,SAAS;IACb,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAS;WAEV,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAyBvD,OAAO;IA2BD,IAAI,CACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,CAAC,EAAE,kBAAkB,EAC5B,SAAS,GAAE,MAA4B,GACtC,OAAO,CAAC,GAAG,CAAC;IA6BT,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuB1F,KAAK;CAON;AAgkBD,qBAAa,iBAAkB,YAAW,SAAS;IAC3C,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC;YASpE,WAAW;YAYX,uBAAuB;IAkC/B,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;YAUxD,iBAAiB;YAwJjB,UAAU;CAyGzB;AAED,eAAO,MAAM,SAAS;kCAj0Be,MAAM,GAAG,SAAS,WAAW,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAAS;qCARjE,eAAe,cAAc,MAAM,KAAG,MAAM;0CA4N1E,SAAS,WACR,eAAe,EAAE,WACjB,MAAM,GAAG,SAAS,aAChB,MAAM,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,mBAAmB,EAAE,CAAC;QAC/B,SAAS,EAAE,qBAAqB,EAAE,CAAC;QACnC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;KAC/B,CAAC;CAumBD,CAAC"}
|