expxagents 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/src/commands/__tests__/outdated.test.d.ts +1 -0
- package/dist/cli/src/commands/__tests__/outdated.test.js +76 -0
- package/dist/cli/src/commands/info.js +1 -1
- package/dist/cli/src/commands/login.js +1 -1
- package/dist/cli/src/commands/outdated.d.ts +14 -0
- package/dist/cli/src/commands/outdated.js +87 -0
- package/dist/cli/src/commands/registry-install.js +1 -1
- package/dist/cli/src/commands/search.js +1 -1
- package/dist/cli/src/commands/update.d.ts +2 -0
- package/dist/cli/src/commands/update.js +81 -0
- package/dist/cli/src/index.js +4 -0
- package/dist/server/api/registry-routes.js +1 -1
- package/dist/server/api/registry-routes.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mkdirSync, writeFileSync, rmSync, mkdtempSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { scanInstalledSquads } from '../outdated.js';
|
|
6
|
+
describe('scanInstalledSquads', () => {
|
|
7
|
+
let tempDir;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
tempDir = mkdtempSync(join(tmpdir(), 'squads-test-'));
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
it('returns empty array when directory does not exist', () => {
|
|
15
|
+
const result = scanInstalledSquads('/nonexistent/path');
|
|
16
|
+
expect(result).toEqual([]);
|
|
17
|
+
});
|
|
18
|
+
it('returns empty array when no @scope directories exist', () => {
|
|
19
|
+
mkdirSync(join(tempDir, 'local-squad'), { recursive: true });
|
|
20
|
+
writeFileSync(join(tempDir, 'local-squad', 'squad.yaml'), 'squad:\n version: "1.0.0"');
|
|
21
|
+
const result = scanInstalledSquads(tempDir);
|
|
22
|
+
expect(result).toEqual([]);
|
|
23
|
+
});
|
|
24
|
+
it('finds squads under @scope directories', () => {
|
|
25
|
+
const squadDir = join(tempDir, '@thulio', 'brand-squad');
|
|
26
|
+
mkdirSync(squadDir, { recursive: true });
|
|
27
|
+
writeFileSync(join(squadDir, 'squad.yaml'), 'squad:\n code: brand-squad\n version: "1.2.0"');
|
|
28
|
+
const result = scanInstalledSquads(tempDir);
|
|
29
|
+
expect(result).toEqual([
|
|
30
|
+
{ fullName: '@thulio/brand-squad', currentVersion: '1.2.0' },
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
it('skips squads without squad.yaml', () => {
|
|
34
|
+
const squadDir = join(tempDir, '@thulio', 'no-yaml');
|
|
35
|
+
mkdirSync(squadDir, { recursive: true });
|
|
36
|
+
const result = scanInstalledSquads(tempDir);
|
|
37
|
+
expect(result).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
it('skips squads with invalid yaml', () => {
|
|
40
|
+
const squadDir = join(tempDir, '@thulio', 'bad-yaml');
|
|
41
|
+
mkdirSync(squadDir, { recursive: true });
|
|
42
|
+
writeFileSync(join(squadDir, 'squad.yaml'), '}{invalid');
|
|
43
|
+
const result = scanInstalledSquads(tempDir);
|
|
44
|
+
expect(result).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
it('skips squads without version field', () => {
|
|
47
|
+
const squadDir = join(tempDir, '@thulio', 'no-version');
|
|
48
|
+
mkdirSync(squadDir, { recursive: true });
|
|
49
|
+
writeFileSync(join(squadDir, 'squad.yaml'), 'squad:\n code: no-version');
|
|
50
|
+
const result = scanInstalledSquads(tempDir);
|
|
51
|
+
expect(result).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
it('finds squads with nested tarball structure (@scope/name/name/squad.yaml)', () => {
|
|
54
|
+
const nestedDir = join(tempDir, '@community', 'copy-squad', 'copy-squad');
|
|
55
|
+
mkdirSync(nestedDir, { recursive: true });
|
|
56
|
+
writeFileSync(join(nestedDir, 'squad.yaml'), 'squad:\n code: copy-squad\n version: "2.0.0"');
|
|
57
|
+
const result = scanInstalledSquads(tempDir);
|
|
58
|
+
expect(result).toEqual([
|
|
59
|
+
{ fullName: '@community/copy-squad', currentVersion: '2.0.0' },
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
it('finds multiple squads across scopes', () => {
|
|
63
|
+
const squad1 = join(tempDir, '@thulio', 'squad-a');
|
|
64
|
+
const squad2 = join(tempDir, '@community', 'squad-b');
|
|
65
|
+
mkdirSync(squad1, { recursive: true });
|
|
66
|
+
mkdirSync(squad2, { recursive: true });
|
|
67
|
+
writeFileSync(join(squad1, 'squad.yaml'), 'squad:\n version: "1.0.0"');
|
|
68
|
+
writeFileSync(join(squad2, 'squad.yaml'), 'squad:\n version: "2.0.0"');
|
|
69
|
+
const result = scanInstalledSquads(tempDir);
|
|
70
|
+
expect(result).toHaveLength(2);
|
|
71
|
+
expect(result).toEqual(expect.arrayContaining([
|
|
72
|
+
{ fullName: '@thulio/squad-a', currentVersion: '1.0.0' },
|
|
73
|
+
{ fullName: '@community/squad-b', currentVersion: '2.0.0' },
|
|
74
|
+
]));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -6,7 +6,7 @@ export const infoCommand = new Command('info')
|
|
|
6
6
|
.action(async (name) => {
|
|
7
7
|
const creds = readCredentials();
|
|
8
8
|
const client = new RegistryClient({
|
|
9
|
-
registryUrl: creds?.registryUrl || 'https://
|
|
9
|
+
registryUrl: creds?.registryUrl || 'https://expxagents-marketplace-production.up.railway.app',
|
|
10
10
|
});
|
|
11
11
|
try {
|
|
12
12
|
const squad = await client.getSquad(name);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { RegistryClient, writeCredentials } from '@expxagents/registry-client';
|
|
3
|
-
const DEFAULT_REGISTRY = 'https://
|
|
3
|
+
const DEFAULT_REGISTRY = 'https://expxagents-marketplace-production.up.railway.app';
|
|
4
4
|
export const loginCommand = new Command('login')
|
|
5
5
|
.description('Authenticate with the ExpxAgents registry')
|
|
6
6
|
.option('--registry <url>', 'Registry URL', DEFAULT_REGISTRY)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { RegistryClient } from '@expxagents/registry-client';
|
|
3
|
+
export interface InstalledSquad {
|
|
4
|
+
fullName: string;
|
|
5
|
+
currentVersion: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function scanInstalledSquads(squadsDir: string): InstalledSquad[];
|
|
8
|
+
export interface OutdatedResult {
|
|
9
|
+
fullName: string;
|
|
10
|
+
current: string;
|
|
11
|
+
latest: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function checkOutdated(client: RegistryClient, squads: InstalledSquad[]): Promise<OutdatedResult[]>;
|
|
14
|
+
export declare const outdatedCommand: Command;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolve, join } from 'node:path';
|
|
3
|
+
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import { RegistryClient, readCredentials } from '@expxagents/registry-client';
|
|
5
|
+
import { load as loadYaml } from 'js-yaml';
|
|
6
|
+
export function scanInstalledSquads(squadsDir) {
|
|
7
|
+
const installed = [];
|
|
8
|
+
if (!existsSync(squadsDir))
|
|
9
|
+
return installed;
|
|
10
|
+
for (const entry of readdirSync(squadsDir, { withFileTypes: true })) {
|
|
11
|
+
if (!entry.isDirectory() || !entry.name.startsWith('@'))
|
|
12
|
+
continue;
|
|
13
|
+
const scope = entry.name.slice(1); // remove @
|
|
14
|
+
const scopeDir = join(squadsDir, entry.name);
|
|
15
|
+
for (const squadEntry of readdirSync(scopeDir, { withFileTypes: true })) {
|
|
16
|
+
if (!squadEntry.isDirectory())
|
|
17
|
+
continue;
|
|
18
|
+
// Tarball may extract as @scope/name/squad.yaml or @scope/name/name/squad.yaml
|
|
19
|
+
const directPath = join(scopeDir, squadEntry.name, 'squad.yaml');
|
|
20
|
+
const nestedPath = join(scopeDir, squadEntry.name, squadEntry.name, 'squad.yaml');
|
|
21
|
+
const yamlPath = existsSync(directPath) ? directPath : existsSync(nestedPath) ? nestedPath : null;
|
|
22
|
+
if (!yamlPath)
|
|
23
|
+
continue;
|
|
24
|
+
try {
|
|
25
|
+
const raw = readFileSync(yamlPath, 'utf-8');
|
|
26
|
+
const parsed = loadYaml(raw);
|
|
27
|
+
const version = parsed?.squad?.version ?? parsed?.version;
|
|
28
|
+
if (version) {
|
|
29
|
+
installed.push({
|
|
30
|
+
fullName: `@${scope}/${squadEntry.name}`,
|
|
31
|
+
currentVersion: String(version),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Skip squads with invalid yaml
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return installed;
|
|
41
|
+
}
|
|
42
|
+
export async function checkOutdated(client, squads) {
|
|
43
|
+
const outdated = [];
|
|
44
|
+
for (const squad of squads) {
|
|
45
|
+
try {
|
|
46
|
+
const meta = await client.getSquad(squad.fullName);
|
|
47
|
+
if (meta.latestVersion && meta.latestVersion !== squad.currentVersion) {
|
|
48
|
+
outdated.push({
|
|
49
|
+
fullName: squad.fullName,
|
|
50
|
+
current: squad.currentVersion,
|
|
51
|
+
latest: meta.latestVersion,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Squad not found on registry — skip (local-only squad)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return outdated;
|
|
60
|
+
}
|
|
61
|
+
export const outdatedCommand = new Command('outdated')
|
|
62
|
+
.description('Check installed squads for available updates')
|
|
63
|
+
.option('--dir <dir>', 'Squads directory', './squads')
|
|
64
|
+
.action(async (options) => {
|
|
65
|
+
const squadsDir = resolve(options.dir);
|
|
66
|
+
const installed = scanInstalledSquads(squadsDir);
|
|
67
|
+
if (installed.length === 0) {
|
|
68
|
+
console.log('No registry squads installed.');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const creds = readCredentials();
|
|
72
|
+
const client = new RegistryClient({
|
|
73
|
+
registryUrl: creds?.registryUrl || 'https://expxagents-marketplace-production.up.railway.app',
|
|
74
|
+
apiKey: creds?.apiKey,
|
|
75
|
+
});
|
|
76
|
+
console.log(`Checking ${installed.length} squad(s)...\n`);
|
|
77
|
+
const outdated = await checkOutdated(client, installed);
|
|
78
|
+
if (outdated.length === 0) {
|
|
79
|
+
console.log('All squads are up to date.');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
console.log('Updates available:\n');
|
|
83
|
+
for (const o of outdated) {
|
|
84
|
+
console.log(` ${o.fullName} ${o.current} \u2192 ${o.latest}`);
|
|
85
|
+
}
|
|
86
|
+
console.log(`\nRun \`expxagents update\` to update all.`);
|
|
87
|
+
});
|
|
@@ -9,7 +9,7 @@ export const registryInstallCommand = new Command('add')
|
|
|
9
9
|
.action(async (name, options) => {
|
|
10
10
|
const creds = readCredentials();
|
|
11
11
|
const client = new RegistryClient({
|
|
12
|
-
registryUrl: creds?.registryUrl || 'https://
|
|
12
|
+
registryUrl: creds?.registryUrl || 'https://expxagents-marketplace-production.up.railway.app',
|
|
13
13
|
apiKey: creds?.apiKey,
|
|
14
14
|
});
|
|
15
15
|
const targetDir = resolve(options.dir);
|
|
@@ -8,7 +8,7 @@ export const searchCommand = new Command('search')
|
|
|
8
8
|
.action(async (query, options) => {
|
|
9
9
|
const creds = readCredentials();
|
|
10
10
|
const client = new RegistryClient({
|
|
11
|
-
registryUrl: creds?.registryUrl || 'https://
|
|
11
|
+
registryUrl: creds?.registryUrl || 'https://expxagents-marketplace-production.up.railway.app',
|
|
12
12
|
});
|
|
13
13
|
try {
|
|
14
14
|
const result = await client.search({
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { createInterface } from 'node:readline';
|
|
4
|
+
import { RegistryClient, readCredentials } from '@expxagents/registry-client';
|
|
5
|
+
import { scanInstalledSquads, checkOutdated } from './outdated.js';
|
|
6
|
+
function confirm(question) {
|
|
7
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
8
|
+
return new Promise((res) => {
|
|
9
|
+
rl.question(question, (answer) => {
|
|
10
|
+
rl.close();
|
|
11
|
+
res(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function updateSquads(client, squads, targetDir) {
|
|
16
|
+
for (const squad of squads) {
|
|
17
|
+
try {
|
|
18
|
+
console.log(` Updating ${squad.fullName} ${squad.current} \u2192 ${squad.latest}...`);
|
|
19
|
+
const result = await client.installWithDeps(squad.fullName, squad.latest, targetDir);
|
|
20
|
+
console.log(` \u2713 ${result.root}`);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
24
|
+
console.error(` \u2717 ${squad.fullName}: ${message}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const updateCommand = new Command('update')
|
|
29
|
+
.description('Update installed squads to their latest versions')
|
|
30
|
+
.argument('[squad]', 'Specific squad to update (@scope/name)')
|
|
31
|
+
.option('--dir <dir>', 'Squads directory', './squads')
|
|
32
|
+
.option('--yes', 'Skip confirmation prompt')
|
|
33
|
+
.action(async (squad, options) => {
|
|
34
|
+
const squadsDir = resolve(options.dir);
|
|
35
|
+
const creds = readCredentials();
|
|
36
|
+
const client = new RegistryClient({
|
|
37
|
+
registryUrl: creds?.registryUrl || 'https://expxagents-marketplace-production.up.railway.app',
|
|
38
|
+
apiKey: creds?.apiKey,
|
|
39
|
+
});
|
|
40
|
+
// If specific squad provided, update just that one
|
|
41
|
+
if (squad) {
|
|
42
|
+
try {
|
|
43
|
+
console.log(`Updating ${squad}...`);
|
|
44
|
+
const result = await client.installWithDeps(squad, 'latest', squadsDir);
|
|
45
|
+
console.log(`\u2713 Updated ${result.root}`);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
49
|
+
console.error(`\u2717 Update failed: ${message}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Otherwise: scan all, check for updates, confirm, update
|
|
55
|
+
const installed = scanInstalledSquads(squadsDir);
|
|
56
|
+
if (installed.length === 0) {
|
|
57
|
+
console.log('No registry squads installed.');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
console.log(`Checking ${installed.length} squad(s)...\n`);
|
|
61
|
+
const outdated = await checkOutdated(client, installed);
|
|
62
|
+
if (outdated.length === 0) {
|
|
63
|
+
console.log('All squads are up to date.');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log('Updates available:\n');
|
|
67
|
+
for (const o of outdated) {
|
|
68
|
+
console.log(` ${o.fullName} ${o.current} \u2192 ${o.latest}`);
|
|
69
|
+
}
|
|
70
|
+
console.log('');
|
|
71
|
+
if (!options.yes) {
|
|
72
|
+
const ok = await confirm(`Update ${outdated.length} squad(s)? (y/N) `);
|
|
73
|
+
if (!ok) {
|
|
74
|
+
console.log('Cancelled.');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
console.log('');
|
|
79
|
+
await updateSquads(client, outdated, squadsDir);
|
|
80
|
+
console.log('\nDone.');
|
|
81
|
+
});
|
package/dist/cli/src/index.js
CHANGED
|
@@ -25,6 +25,8 @@ import { publishCommand } from './commands/publish.js';
|
|
|
25
25
|
import { registryInstallCommand } from './commands/registry-install.js';
|
|
26
26
|
import { searchCommand } from './commands/search.js';
|
|
27
27
|
import { infoCommand } from './commands/info.js';
|
|
28
|
+
import { outdatedCommand } from './commands/outdated.js';
|
|
29
|
+
import { updateCommand } from './commands/update.js';
|
|
28
30
|
import { findPackageRoot } from './utils/config.js';
|
|
29
31
|
function getVersion() {
|
|
30
32
|
try {
|
|
@@ -121,4 +123,6 @@ program.addCommand(publishCommand);
|
|
|
121
123
|
program.addCommand(registryInstallCommand);
|
|
122
124
|
program.addCommand(searchCommand);
|
|
123
125
|
program.addCommand(infoCommand);
|
|
126
|
+
program.addCommand(outdatedCommand);
|
|
127
|
+
program.addCommand(updateCommand);
|
|
124
128
|
program.parse();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RegistryClient } from '@expxagents/registry-client';
|
|
2
|
-
const REGISTRY_URL = process.env.EXPX_REGISTRY_URL || 'https://
|
|
2
|
+
const REGISTRY_URL = process.env.EXPX_REGISTRY_URL || 'https://expxagents-marketplace-production.up.railway.app';
|
|
3
3
|
export async function registryRoutes(app, opts) {
|
|
4
4
|
const client = new RegistryClient({ registryUrl: REGISTRY_URL });
|
|
5
5
|
// GET /api/registry/categories
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-routes.js","sourceRoot":"","sources":["../../src/api/registry-routes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAM7D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,
|
|
1
|
+
{"version":3,"file":"registry-routes.js","sourceRoot":"","sources":["../../src/api/registry-routes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAM7D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,0DAA0D,CAAC;AAEjH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAoB,EAAE,IAA2B;IACpF,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,CAAC;IAEjE,+BAA+B;IAC/B,GAAG,CAAC,GAAG,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QAC7C,OAAO,MAAM,CAAC,aAAa,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,2BAA2B;IAC3B,GAAG,CAAC,GAAG,CAAC,sBAAsB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5C,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,KAA+B,CAAC;QAC/E,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,CAAC;YACD,QAAQ;YACR,IAAI,EAAE,IAAW;YACjB,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YACvC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;SAC3C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,+BAA+B;IAC/B,GAAG,CAAC,GAAG,CAA6B,0BAA0B,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5E,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,iDAAiD;QACjD,OAAO,EAAE,KAAK,EAAE,mEAAmE,EAAE,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,wCAAwC;IACxC,GAAG,CAAC,IAAI,CAA6B,kCAAkC,EAAE;QACvE,UAAU,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC;KAC9B,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACf,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;QAC1B,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAChF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,+CAA+C;IAC/C,GAAG,CAAC,IAAI,CAAC,8BAA8B,EAAE;QACvC,UAAU,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC;KAC9B,EAAE,KAAK,IAAI,EAAE;QACZ,OAAO,EAAE,KAAK,EAAE,+DAA+D,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IACtG,CAAC,CAAC,CAAC;IAEH,uDAAuD;IACvD,GAAG,CAAC,IAAI,CAA6B,+BAA+B,EAAE;QACpE,UAAU,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC;KAC9B,EAAE,KAAK,IAAI,EAAE;QACZ,OAAO,EAAE,KAAK,EAAE,mCAAmC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC"}
|