@google/gemini-cli-core 0.1.7 → 0.1.8-rc.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/README.md +4 -0
- package/dist/google-gemini-cli-core-0.1.8-rc.0.tgz +0 -0
- package/dist/src/code_assist/oauth2.d.ts +7 -0
- package/dist/src/code_assist/oauth2.js +81 -2
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +20 -4
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/code_assist/server.d.ts +3 -3
- package/dist/src/code_assist/server.js +6 -6
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +12 -12
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/setup.d.ts +1 -1
- package/dist/src/code_assist/setup.js +2 -2
- package/dist/src/code_assist/setup.js.map +1 -1
- package/dist/src/config/config.d.ts +2 -1
- package/dist/src/config/config.js +22 -17
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/core/client.js +1 -3
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +2 -2
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +1 -2
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/turn.d.ts +2 -11
- package/dist/src/core/turn.js +1 -19
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +0 -45
- package/dist/src/core/turn.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +5 -6
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +1 -0
- package/dist/src/telemetry/index.js +1 -0
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.js +19 -0
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +41 -0
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +1 -0
- package/dist/src/telemetry/types.js +2 -0
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +67 -0
- package/dist/src/telemetry/uiTelemetry.js +131 -0
- package/dist/src/telemetry/uiTelemetry.js.map +1 -0
- package/dist/src/telemetry/uiTelemetry.test.d.ts +6 -0
- package/dist/src/telemetry/uiTelemetry.test.js +411 -0
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -0
- package/dist/src/tools/edit.d.ts +4 -0
- package/dist/src/tools/edit.js +15 -6
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +41 -3
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/grep.js +4 -2
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/grep.test.js +10 -10
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +1 -1
- package/dist/src/tools/mcp-client.js +15 -8
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +86 -11
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/modifiable-tool.js +5 -4
- package/dist/src/tools/modifiable-tool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +16 -3
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/tools/read-file.js +1 -2
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +1 -2
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +1 -0
- package/dist/src/tools/shell.js +93 -22
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.d.ts +6 -0
- package/dist/src/tools/shell.test.js +301 -0
- package/dist/src/tools/shell.test.js.map +1 -0
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/write-file.d.ts +4 -0
- package/dist/src/tools/write-file.js +13 -5
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +35 -0
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/utils/editor.d.ts +1 -1
- package/dist/src/utils/editor.js +19 -6
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +60 -27
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.js +5 -2
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.d.ts +35 -0
- package/dist/src/utils/memoryImportProcessor.js +141 -0
- package/dist/src/utils/memoryImportProcessor.js.map +1 -0
- package/dist/src/utils/memoryImportProcessor.test.d.ts +6 -0
- package/dist/src/utils/memoryImportProcessor.test.js +170 -0
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -0
- package/dist/src/utils/user_id.d.ts +9 -3
- package/dist/src/utils/user_id.js +38 -16
- package/dist/src/utils/user_id.js.map +1 -1
- package/dist/src/utils/user_id.test.d.ts +6 -0
- package/dist/src/utils/user_id.test.js +40 -0
- package/dist/src/utils/user_id.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/dist/google-gemini-cli-core-0.1.7.tgz +0 -0
- package/dist/src/tools/shell.json +0 -18
- package/dist/src/tools/shell.md +0 -16
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import * as fs from 'fs/promises';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { processImports, validateImportPath } from './memoryImportProcessor.js';
|
|
10
|
+
// Mock fs/promises
|
|
11
|
+
vi.mock('fs/promises');
|
|
12
|
+
const mockedFs = vi.mocked(fs);
|
|
13
|
+
// Mock console methods to capture warnings
|
|
14
|
+
const originalConsoleWarn = console.warn;
|
|
15
|
+
const originalConsoleError = console.error;
|
|
16
|
+
const originalConsoleDebug = console.debug;
|
|
17
|
+
describe('memoryImportProcessor', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
vi.clearAllMocks();
|
|
20
|
+
// Mock console methods
|
|
21
|
+
console.warn = vi.fn();
|
|
22
|
+
console.error = vi.fn();
|
|
23
|
+
console.debug = vi.fn();
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
// Restore console methods
|
|
27
|
+
console.warn = originalConsoleWarn;
|
|
28
|
+
console.error = originalConsoleError;
|
|
29
|
+
console.debug = originalConsoleDebug;
|
|
30
|
+
});
|
|
31
|
+
describe('processImports', () => {
|
|
32
|
+
it('should process basic md file imports', async () => {
|
|
33
|
+
const content = 'Some content @./test.md more content';
|
|
34
|
+
const basePath = '/test/path';
|
|
35
|
+
const importedContent = '# Imported Content\nThis is imported.';
|
|
36
|
+
mockedFs.access.mockResolvedValue(undefined);
|
|
37
|
+
mockedFs.readFile.mockResolvedValue(importedContent);
|
|
38
|
+
const result = await processImports(content, basePath, true);
|
|
39
|
+
expect(result).toContain('<!-- Imported from: ./test.md -->');
|
|
40
|
+
expect(result).toContain(importedContent);
|
|
41
|
+
expect(result).toContain('<!-- End of import from: ./test.md -->');
|
|
42
|
+
expect(mockedFs.readFile).toHaveBeenCalledWith(path.resolve(basePath, './test.md'), 'utf-8');
|
|
43
|
+
});
|
|
44
|
+
it('should warn and fail for non-md file imports', async () => {
|
|
45
|
+
const content = 'Some content @./instructions.txt more content';
|
|
46
|
+
const basePath = '/test/path';
|
|
47
|
+
const result = await processImports(content, basePath, true);
|
|
48
|
+
expect(console.warn).toHaveBeenCalledWith('[WARN] [ImportProcessor]', 'Import processor only supports .md files. Attempting to import non-md file: ./instructions.txt. This will fail.');
|
|
49
|
+
expect(result).toContain('<!-- Import failed: ./instructions.txt - Only .md files are supported -->');
|
|
50
|
+
expect(mockedFs.readFile).not.toHaveBeenCalled();
|
|
51
|
+
});
|
|
52
|
+
it('should handle circular imports', async () => {
|
|
53
|
+
const content = 'Content @./circular.md more content';
|
|
54
|
+
const basePath = '/test/path';
|
|
55
|
+
const circularContent = 'Circular @./main.md content';
|
|
56
|
+
mockedFs.access.mockResolvedValue(undefined);
|
|
57
|
+
mockedFs.readFile.mockResolvedValue(circularContent);
|
|
58
|
+
// Set up the import state to simulate we're already processing main.md
|
|
59
|
+
const importState = {
|
|
60
|
+
processedFiles: new Set(),
|
|
61
|
+
maxDepth: 10,
|
|
62
|
+
currentDepth: 0,
|
|
63
|
+
currentFile: '/test/path/main.md', // Simulate we're processing main.md
|
|
64
|
+
};
|
|
65
|
+
const result = await processImports(content, basePath, true, importState);
|
|
66
|
+
// The circular import should be detected when processing the nested import
|
|
67
|
+
expect(result).toContain('<!-- Circular import detected: ./main.md -->');
|
|
68
|
+
});
|
|
69
|
+
it('should handle file not found errors', async () => {
|
|
70
|
+
const content = 'Content @./nonexistent.md more content';
|
|
71
|
+
const basePath = '/test/path';
|
|
72
|
+
mockedFs.access.mockRejectedValue(new Error('File not found'));
|
|
73
|
+
const result = await processImports(content, basePath, true);
|
|
74
|
+
expect(result).toContain('<!-- Import failed: ./nonexistent.md - File not found -->');
|
|
75
|
+
expect(console.error).toHaveBeenCalledWith('[ERROR] [ImportProcessor]', 'Failed to import ./nonexistent.md: File not found');
|
|
76
|
+
});
|
|
77
|
+
it('should respect max depth limit', async () => {
|
|
78
|
+
const content = 'Content @./deep.md more content';
|
|
79
|
+
const basePath = '/test/path';
|
|
80
|
+
const deepContent = 'Deep @./deeper.md content';
|
|
81
|
+
mockedFs.access.mockResolvedValue(undefined);
|
|
82
|
+
mockedFs.readFile.mockResolvedValue(deepContent);
|
|
83
|
+
const importState = {
|
|
84
|
+
processedFiles: new Set(),
|
|
85
|
+
maxDepth: 1,
|
|
86
|
+
currentDepth: 1,
|
|
87
|
+
};
|
|
88
|
+
const result = await processImports(content, basePath, true, importState);
|
|
89
|
+
expect(console.warn).toHaveBeenCalledWith('[WARN] [ImportProcessor]', 'Maximum import depth (1) reached. Stopping import processing.');
|
|
90
|
+
expect(result).toBe(content);
|
|
91
|
+
});
|
|
92
|
+
it('should handle nested imports recursively', async () => {
|
|
93
|
+
const content = 'Main @./nested.md content';
|
|
94
|
+
const basePath = '/test/path';
|
|
95
|
+
const nestedContent = 'Nested @./inner.md content';
|
|
96
|
+
const innerContent = 'Inner content';
|
|
97
|
+
mockedFs.access.mockResolvedValue(undefined);
|
|
98
|
+
mockedFs.readFile
|
|
99
|
+
.mockResolvedValueOnce(nestedContent)
|
|
100
|
+
.mockResolvedValueOnce(innerContent);
|
|
101
|
+
const result = await processImports(content, basePath, true);
|
|
102
|
+
expect(result).toContain('<!-- Imported from: ./nested.md -->');
|
|
103
|
+
expect(result).toContain('<!-- Imported from: ./inner.md -->');
|
|
104
|
+
expect(result).toContain(innerContent);
|
|
105
|
+
});
|
|
106
|
+
it('should handle absolute paths in imports', async () => {
|
|
107
|
+
const content = 'Content @/absolute/path/file.md more content';
|
|
108
|
+
const basePath = '/test/path';
|
|
109
|
+
const importedContent = 'Absolute path content';
|
|
110
|
+
mockedFs.access.mockResolvedValue(undefined);
|
|
111
|
+
mockedFs.readFile.mockResolvedValue(importedContent);
|
|
112
|
+
const result = await processImports(content, basePath, true);
|
|
113
|
+
expect(result).toContain('<!-- Import failed: /absolute/path/file.md - Path traversal attempt -->');
|
|
114
|
+
});
|
|
115
|
+
it('should handle multiple imports in same content', async () => {
|
|
116
|
+
const content = 'Start @./first.md middle @./second.md end';
|
|
117
|
+
const basePath = '/test/path';
|
|
118
|
+
const firstContent = 'First content';
|
|
119
|
+
const secondContent = 'Second content';
|
|
120
|
+
mockedFs.access.mockResolvedValue(undefined);
|
|
121
|
+
mockedFs.readFile
|
|
122
|
+
.mockResolvedValueOnce(firstContent)
|
|
123
|
+
.mockResolvedValueOnce(secondContent);
|
|
124
|
+
const result = await processImports(content, basePath, true);
|
|
125
|
+
expect(result).toContain('<!-- Imported from: ./first.md -->');
|
|
126
|
+
expect(result).toContain('<!-- Imported from: ./second.md -->');
|
|
127
|
+
expect(result).toContain(firstContent);
|
|
128
|
+
expect(result).toContain(secondContent);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('validateImportPath', () => {
|
|
132
|
+
it('should reject URLs', () => {
|
|
133
|
+
expect(validateImportPath('https://example.com/file.md', '/base', [
|
|
134
|
+
'/allowed',
|
|
135
|
+
])).toBe(false);
|
|
136
|
+
expect(validateImportPath('http://example.com/file.md', '/base', ['/allowed'])).toBe(false);
|
|
137
|
+
expect(validateImportPath('file:///path/to/file.md', '/base', ['/allowed'])).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
it('should allow paths within allowed directories', () => {
|
|
140
|
+
expect(validateImportPath('./file.md', '/base', ['/base'])).toBe(true);
|
|
141
|
+
expect(validateImportPath('../file.md', '/base', ['/allowed'])).toBe(false);
|
|
142
|
+
expect(validateImportPath('/allowed/sub/file.md', '/base', ['/allowed'])).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
it('should reject paths outside allowed directories', () => {
|
|
145
|
+
expect(validateImportPath('/forbidden/file.md', '/base', ['/allowed'])).toBe(false);
|
|
146
|
+
expect(validateImportPath('../../../file.md', '/base', ['/base'])).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
it('should handle multiple allowed directories', () => {
|
|
149
|
+
expect(validateImportPath('./file.md', '/base', ['/allowed1', '/allowed2'])).toBe(false);
|
|
150
|
+
expect(validateImportPath('/allowed1/file.md', '/base', [
|
|
151
|
+
'/allowed1',
|
|
152
|
+
'/allowed2',
|
|
153
|
+
])).toBe(true);
|
|
154
|
+
expect(validateImportPath('/allowed2/file.md', '/base', [
|
|
155
|
+
'/allowed1',
|
|
156
|
+
'/allowed2',
|
|
157
|
+
])).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
it('should handle relative paths correctly', () => {
|
|
160
|
+
expect(validateImportPath('file.md', '/base', ['/base'])).toBe(true);
|
|
161
|
+
expect(validateImportPath('./file.md', '/base', ['/base'])).toBe(true);
|
|
162
|
+
expect(validateImportPath('../file.md', '/base', ['/parent'])).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
it('should handle absolute paths correctly', () => {
|
|
165
|
+
expect(validateImportPath('/allowed/file.md', '/base', ['/allowed'])).toBe(true);
|
|
166
|
+
expect(validateImportPath('/forbidden/file.md', '/base', ['/allowed'])).toBe(false);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
//# sourceMappingURL=memoryImportProcessor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoryImportProcessor.test.js","sourceRoot":"","sources":["../../../src/utils/memoryImportProcessor.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhF,mBAAmB;AACnB,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AACvB,MAAM,QAAQ,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAE/B,2CAA2C;AAC3C,MAAM,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;AACzC,MAAM,oBAAoB,GAAG,OAAO,CAAC,KAAK,CAAC;AAC3C,MAAM,oBAAoB,GAAG,OAAO,CAAC,KAAK,CAAC;AAE3C,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,uBAAuB;QACvB,OAAO,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,0BAA0B;QAC1B,OAAO,CAAC,IAAI,GAAG,mBAAmB,CAAC;QACnC,OAAO,CAAC,KAAK,GAAG,oBAAoB,CAAC;QACrC,OAAO,CAAC,KAAK,GAAG,oBAAoB,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,OAAO,GAAG,sCAAsC,CAAC;YACvD,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC9B,MAAM,eAAe,GAAG,uCAAuC,CAAC;YAEhE,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAC7C,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;YAErD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAE7D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,mCAAmC,CAAC,CAAC;YAC9D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,wCAAwC,CAAC,CAAC;YACnE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAC5C,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,WAAW,CAAC,EACnC,OAAO,CACR,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,OAAO,GAAG,+CAA+C,CAAC;YAChE,MAAM,QAAQ,GAAG,YAAY,CAAC;YAE9B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAE7D,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACvC,0BAA0B,EAC1B,iHAAiH,CAClH,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,2EAA2E,CAC5E,CAAC;YACF,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,OAAO,GAAG,qCAAqC,CAAC;YACtD,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC9B,MAAM,eAAe,GAAG,6BAA6B,CAAC;YAEtD,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAC7C,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;YAErD,uEAAuE;YACvE,MAAM,WAAW,GAAG;gBAClB,cAAc,EAAE,IAAI,GAAG,EAAU;gBACjC,QAAQ,EAAE,EAAE;gBACZ,YAAY,EAAE,CAAC;gBACf,WAAW,EAAE,oBAAoB,EAAE,oCAAoC;aACxE,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;YAE1E,2EAA2E;YAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,8CAA8C,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;YACnD,MAAM,OAAO,GAAG,wCAAwC,CAAC;YACzD,MAAM,QAAQ,GAAG,YAAY,CAAC;YAE9B,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAE/D,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAE7D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,2DAA2D,CAC5D,CAAC;YACF,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,oBAAoB,CACxC,2BAA2B,EAC3B,mDAAmD,CACpD,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,OAAO,GAAG,iCAAiC,CAAC;YAClD,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC9B,MAAM,WAAW,GAAG,2BAA2B,CAAC;YAEhD,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAC7C,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAEjD,MAAM,WAAW,GAAG;gBAClB,cAAc,EAAE,IAAI,GAAG,EAAU;gBACjC,QAAQ,EAAE,CAAC;gBACX,YAAY,EAAE,CAAC;aAChB,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;YAE1E,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACvC,0BAA0B,EAC1B,+DAA+D,CAChE,CAAC;YACF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,OAAO,GAAG,2BAA2B,CAAC;YAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC9B,MAAM,aAAa,GAAG,4BAA4B,CAAC;YACnD,MAAM,YAAY,GAAG,eAAe,CAAC;YAErC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAC7C,QAAQ,CAAC,QAAQ;iBACd,qBAAqB,CAAC,aAAa,CAAC;iBACpC,qBAAqB,CAAC,YAAY,CAAC,CAAC;YAEvC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAE7D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAC;YAChE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,oCAAoC,CAAC,CAAC;YAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,OAAO,GAAG,8CAA8C,CAAC;YAC/D,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC9B,MAAM,eAAe,GAAG,uBAAuB,CAAC;YAEhD,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAC7C,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;YAErD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAE7D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,yEAAyE,CAC1E,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,OAAO,GAAG,2CAA2C,CAAC;YAC5D,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC9B,MAAM,YAAY,GAAG,eAAe,CAAC;YACrC,MAAM,aAAa,GAAG,gBAAgB,CAAC;YAEvC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAC7C,QAAQ,CAAC,QAAQ;iBACd,qBAAqB,CAAC,YAAY,CAAC;iBACnC,qBAAqB,CAAC,aAAa,CAAC,CAAC;YAExC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAE7D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,oCAAoC,CAAC,CAAC;YAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAC;YAChE,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YACvC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;YAC5B,MAAM,CACJ,kBAAkB,CAAC,6BAA6B,EAAE,OAAO,EAAE;gBACzD,UAAU;aACX,CAAC,CACH,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACd,MAAM,CACJ,kBAAkB,CAAC,4BAA4B,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CACxE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACd,MAAM,CACJ,kBAAkB,CAAC,yBAAyB,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CACrE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;YACvD,MAAM,CAAC,kBAAkB,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvE,MAAM,CAAC,kBAAkB,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAClE,KAAK,CACN,CAAC;YACF,MAAM,CACJ,kBAAkB,CAAC,sBAAsB,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAClE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YACzD,MAAM,CACJ,kBAAkB,CAAC,oBAAoB,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAChE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACd,MAAM,CAAC,kBAAkB,CAAC,kBAAkB,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CACrE,KAAK,CACN,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,CACJ,kBAAkB,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CACrE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACd,MAAM,CACJ,kBAAkB,CAAC,mBAAmB,EAAE,OAAO,EAAE;gBAC/C,WAAW;gBACX,WAAW;aACZ,CAAC,CACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,MAAM,CACJ,kBAAkB,CAAC,mBAAmB,EAAE,OAAO,EAAE;gBAC/C,WAAW;gBACX,WAAW;aACZ,CAAC,CACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,CAAC,kBAAkB,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrE,MAAM,CAAC,kBAAkB,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvE,MAAM,CAAC,kBAAkB,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CACjE,KAAK,CACN,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,CACJ,kBAAkB,CAAC,kBAAkB,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAC9D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,MAAM,CACJ,kBAAkB,CAAC,oBAAoB,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAChE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
|
-
* Retrieves the
|
|
8
|
-
* This ID is used for unique user tracking.
|
|
7
|
+
* Retrieves the installation ID from a file, creating it if it doesn't exist.
|
|
8
|
+
* This ID is used for unique user installation tracking.
|
|
9
9
|
* @returns A UUID string for the user.
|
|
10
10
|
*/
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function getInstallationId(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Retrieves the obfuscated Google Account ID for the currently authenticated user.
|
|
14
|
+
* When OAuth is available, returns the user's cached Google Account ID. Otherwise, returns the installation ID.
|
|
15
|
+
* @returns A string ID for the user (Google Account ID if available, otherwise installation ID).
|
|
16
|
+
*/
|
|
17
|
+
export declare function getObfuscatedGoogleAccountId(): string;
|
|
@@ -10,40 +10,62 @@ import { randomUUID } from 'crypto';
|
|
|
10
10
|
import { GEMINI_DIR } from './paths.js';
|
|
11
11
|
const homeDir = os.homedir() ?? '';
|
|
12
12
|
const geminiDir = path.join(homeDir, GEMINI_DIR);
|
|
13
|
-
const
|
|
13
|
+
const installationIdFile = path.join(geminiDir, 'installation_id');
|
|
14
14
|
function ensureGeminiDirExists() {
|
|
15
15
|
if (!fs.existsSync(geminiDir)) {
|
|
16
16
|
fs.mkdirSync(geminiDir, { recursive: true });
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
function
|
|
20
|
-
if (fs.existsSync(
|
|
21
|
-
const
|
|
22
|
-
return
|
|
19
|
+
function readInstallationIdFromFile() {
|
|
20
|
+
if (fs.existsSync(installationIdFile)) {
|
|
21
|
+
const installationid = fs.readFileSync(installationIdFile, 'utf-8').trim();
|
|
22
|
+
return installationid || null;
|
|
23
23
|
}
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
|
-
function
|
|
27
|
-
fs.writeFileSync(
|
|
26
|
+
function writeInstallationIdToFile(installationId) {
|
|
27
|
+
fs.writeFileSync(installationIdFile, installationId, 'utf-8');
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
* Retrieves the
|
|
31
|
-
* This ID is used for unique user tracking.
|
|
30
|
+
* Retrieves the installation ID from a file, creating it if it doesn't exist.
|
|
31
|
+
* This ID is used for unique user installation tracking.
|
|
32
32
|
* @returns A UUID string for the user.
|
|
33
33
|
*/
|
|
34
|
-
export function
|
|
34
|
+
export function getInstallationId() {
|
|
35
35
|
try {
|
|
36
36
|
ensureGeminiDirExists();
|
|
37
|
-
let
|
|
38
|
-
if (!
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
let installationId = readInstallationIdFromFile();
|
|
38
|
+
if (!installationId) {
|
|
39
|
+
installationId = randomUUID();
|
|
40
|
+
writeInstallationIdToFile(installationId);
|
|
41
41
|
}
|
|
42
|
-
return
|
|
42
|
+
return installationId;
|
|
43
43
|
}
|
|
44
44
|
catch (error) {
|
|
45
|
-
console.error('Error accessing
|
|
45
|
+
console.error('Error accessing installation ID file, generating ephemeral ID:', error);
|
|
46
46
|
return '123456789';
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Retrieves the obfuscated Google Account ID for the currently authenticated user.
|
|
51
|
+
* When OAuth is available, returns the user's cached Google Account ID. Otherwise, returns the installation ID.
|
|
52
|
+
* @returns A string ID for the user (Google Account ID if available, otherwise installation ID).
|
|
53
|
+
*/
|
|
54
|
+
export function getObfuscatedGoogleAccountId() {
|
|
55
|
+
// Try to get cached Google Account ID first
|
|
56
|
+
try {
|
|
57
|
+
// Dynamically import to avoid circular dependencies
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, no-restricted-syntax
|
|
59
|
+
const { getCachedGoogleAccountId } = require('../code_assist/oauth2.js');
|
|
60
|
+
const googleAccountId = getCachedGoogleAccountId();
|
|
61
|
+
if (googleAccountId) {
|
|
62
|
+
return googleAccountId;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (_error) {
|
|
66
|
+
// If there's any error accessing Google Account ID, fall back to installation ID
|
|
67
|
+
}
|
|
68
|
+
// Fall back to installation ID when no Google Account ID is cached or on error
|
|
69
|
+
return getInstallationId();
|
|
70
|
+
}
|
|
49
71
|
//# sourceMappingURL=user_id.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user_id.js","sourceRoot":"","sources":["../../../src/utils/user_id.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;AACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACjD,MAAM,
|
|
1
|
+
{"version":3,"file":"user_id.js","sourceRoot":"","sources":["../../../src/utils/user_id.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;AACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACjD,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;AAEnE,SAAS,qBAAqB;IAC5B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC;AAED,SAAS,0BAA0B;IACjC,IAAI,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACtC,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3E,OAAO,cAAc,IAAI,IAAI,CAAC;IAChC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,yBAAyB,CAAC,cAAsB;IACvD,EAAE,CAAC,aAAa,CAAC,kBAAkB,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,qBAAqB,EAAE,CAAC;QACxB,IAAI,cAAc,GAAG,0BAA0B,EAAE,CAAC;QAElD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,cAAc,GAAG,UAAU,EAAE,CAAC;YAC9B,yBAAyB,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,cAAc,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,gEAAgE,EAChE,KAAK,CACN,CAAC;QACF,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B;IAC1C,4CAA4C;IAC5C,IAAI,CAAC;QACH,oDAAoD;QACpD,uFAAuF;QACvF,MAAM,EAAE,wBAAwB,EAAE,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;QACzE,MAAM,eAAe,GAAG,wBAAwB,EAAE,CAAC;QACnD,IAAI,eAAe,EAAE,CAAC;YACpB,OAAO,eAAe,CAAC;QACzB,CAAC;IACH,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,iFAAiF;IACnF,CAAC;IAED,+EAA+E;IAC/E,OAAO,iBAAiB,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import { getInstallationId, getObfuscatedGoogleAccountId } from './user_id.js';
|
|
8
|
+
describe('user_id', () => {
|
|
9
|
+
describe('getInstallationId', () => {
|
|
10
|
+
it('should return a valid UUID format string', () => {
|
|
11
|
+
const installationId = getInstallationId();
|
|
12
|
+
expect(installationId).toBeDefined();
|
|
13
|
+
expect(typeof installationId).toBe('string');
|
|
14
|
+
expect(installationId.length).toBeGreaterThan(0);
|
|
15
|
+
// Should return the same ID on subsequent calls (consistent)
|
|
16
|
+
const secondCall = getInstallationId();
|
|
17
|
+
expect(secondCall).toBe(installationId);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('getObfuscatedGoogleAccountId', () => {
|
|
21
|
+
it('should return a non-empty string', () => {
|
|
22
|
+
const result = getObfuscatedGoogleAccountId();
|
|
23
|
+
expect(result).toBeDefined();
|
|
24
|
+
expect(typeof result).toBe('string');
|
|
25
|
+
expect(result.length).toBeGreaterThan(0);
|
|
26
|
+
// Should be consistent on subsequent calls
|
|
27
|
+
const secondCall = getObfuscatedGoogleAccountId();
|
|
28
|
+
expect(secondCall).toBe(result);
|
|
29
|
+
});
|
|
30
|
+
it('should return the same as installation ID when no Google Account ID is cached', () => {
|
|
31
|
+
// In a clean test environment, there should be no cached Google Account ID
|
|
32
|
+
// so getObfuscatedGoogleAccountId should fall back to installation ID
|
|
33
|
+
const googleAccountIdResult = getObfuscatedGoogleAccountId();
|
|
34
|
+
const installationIdResult = getInstallationId();
|
|
35
|
+
// They should be the same when no Google Account ID is cached
|
|
36
|
+
expect(googleAccountIdResult).toBe(installationIdResult);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=user_id.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user_id.test.js","sourceRoot":"","sources":["../../../src/utils/user_id.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AAE/E,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAC;YAE3C,MAAM,CAAC,cAAc,CAAC,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7C,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YAEjD,6DAA6D;YAC7D,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC;YACvC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,MAAM,GAAG,4BAA4B,EAAE,CAAC;YAE9C,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YAEzC,2CAA2C;YAC3C,MAAM,UAAU,GAAG,4BAA4B,EAAE,CAAC;YAClD,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;YACvF,2EAA2E;YAC3E,sEAAsE;YACtE,MAAM,qBAAqB,GAAG,4BAA4B,EAAE,CAAC;YAC7D,MAAM,oBAAoB,GAAG,iBAAiB,EAAE,CAAC;YAEjD,8DAA8D;YAC9D,MAAM,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|