@johannes.latzel/llm-chat-file 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 +7 -0
- package/README.md +34 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +89 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +155 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/helpers.d.ts +9 -0
- package/dist/lib/helpers.d.ts.map +1 -0
- package/dist/lib/helpers.js +17 -0
- package/dist/lib/helpers.js.map +1 -0
- package/dist/lib/types.d.ts +23 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +9 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/workspace.d.ts +75 -0
- package/dist/lib/workspace.d.ts.map +1 -0
- package/dist/lib/workspace.js +168 -0
- package/dist/lib/workspace.js.map +1 -0
- package/dist/tools/create-folder.d.ts +12 -0
- package/dist/tools/create-folder.d.ts.map +1 -0
- package/dist/tools/create-folder.js +39 -0
- package/dist/tools/create-folder.js.map +1 -0
- package/dist/tools/delete-file.d.ts +12 -0
- package/dist/tools/delete-file.d.ts.map +1 -0
- package/dist/tools/delete-file.js +54 -0
- package/dist/tools/delete-file.js.map +1 -0
- package/dist/tools/entry-info.d.ts +9 -0
- package/dist/tools/entry-info.d.ts.map +1 -0
- package/dist/tools/entry-info.js +107 -0
- package/dist/tools/entry-info.js.map +1 -0
- package/dist/tools/file-access-info.d.ts +12 -0
- package/dist/tools/file-access-info.d.ts.map +1 -0
- package/dist/tools/file-access-info.js +23 -0
- package/dist/tools/file-access-info.js.map +1 -0
- package/dist/tools/list-directory.d.ts +15 -0
- package/dist/tools/list-directory.d.ts.map +1 -0
- package/dist/tools/list-directory.js +120 -0
- package/dist/tools/list-directory.js.map +1 -0
- package/dist/tools/move-file.d.ts +12 -0
- package/dist/tools/move-file.d.ts.map +1 -0
- package/dist/tools/move-file.js +71 -0
- package/dist/tools/move-file.js.map +1 -0
- package/dist/tools/read-file.d.ts +14 -0
- package/dist/tools/read-file.d.ts.map +1 -0
- package/dist/tools/read-file.js +86 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/search.d.ts +17 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +237 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/switch-workspace.d.ts +12 -0
- package/dist/tools/switch-workspace.d.ts.map +1 -0
- package/dist/tools/switch-workspace.js +34 -0
- package/dist/tools/switch-workspace.js.map +1 -0
- package/dist/tools/write-file.d.ts +15 -0
- package/dist/tools/write-file.d.ts.map +1 -0
- package/dist/tools/write-file.js +56 -0
- package/dist/tools/write-file.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import type { Workspace } from '../lib/workspace.js';
|
|
3
|
+
/** Tool that moves or renames files and directories within the allowed workspace directories. */
|
|
4
|
+
export declare class MoveFileTool extends Tool {
|
|
5
|
+
private ws;
|
|
6
|
+
/**
|
|
7
|
+
* @param workspace - Workspace instance for path resolution and access control.
|
|
8
|
+
*/
|
|
9
|
+
constructor(workspace: Workspace);
|
|
10
|
+
protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=move-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move-file.d.ts","sourceRoot":"","sources":["../../src/tools/move-file.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAEjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AAGnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,iGAAiG;AACjG,qBAAa,YAAa,SAAQ,IAAI;IAClC,OAAO,CAAC,EAAE,CAAY;IAEtB;;OAEG;gBACS,SAAS,EAAE,SAAS;cAehB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAsDvF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import * as fsp from 'node:fs/promises';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
/** Tool that moves or renames files and directories within the allowed workspace directories. */
|
|
5
|
+
export class MoveFileTool extends Tool {
|
|
6
|
+
ws;
|
|
7
|
+
/**
|
|
8
|
+
* @param workspace - Workspace instance for path resolution and access control.
|
|
9
|
+
*/
|
|
10
|
+
constructor(workspace) {
|
|
11
|
+
super('move_file', 'Moves or renames a file or directory.', new ToolParameters({
|
|
12
|
+
source: new ToolParameterProperty('Source path'),
|
|
13
|
+
destination: new ToolParameterProperty('Destination path')
|
|
14
|
+
}, ['source', 'destination']));
|
|
15
|
+
this.ws = workspace;
|
|
16
|
+
}
|
|
17
|
+
async onExecute(args) {
|
|
18
|
+
const srcRaw = args.source;
|
|
19
|
+
if (typeof srcRaw !== 'string' || !srcRaw.trim()) {
|
|
20
|
+
return {
|
|
21
|
+
result: 'Invalid or inaccessible source path (must be within writable directory)',
|
|
22
|
+
status: ResultStatus.Error
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const src = this.ws.normalize(srcRaw.trim());
|
|
26
|
+
if (!this.ws.canWrite(src)) {
|
|
27
|
+
return {
|
|
28
|
+
result: 'Invalid or inaccessible source path (must be within writable directory)',
|
|
29
|
+
status: ResultStatus.Error
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const destRaw = args.destination;
|
|
33
|
+
if (typeof destRaw !== 'string' || !destRaw.trim()) {
|
|
34
|
+
return {
|
|
35
|
+
result: 'Invalid or inaccessible destination path (must be within writable directory)',
|
|
36
|
+
status: ResultStatus.Error
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const dest = this.ws.normalize(destRaw.trim());
|
|
40
|
+
if (!this.ws.canWrite(dest)) {
|
|
41
|
+
return {
|
|
42
|
+
result: 'Invalid or inaccessible destination path (must be within writable directory)',
|
|
43
|
+
status: ResultStatus.Error
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const srcStat = await fsp.stat(src);
|
|
48
|
+
if (!srcStat.isFile() && !srcStat.isDirectory()) {
|
|
49
|
+
return { result: 'Source is not a file or directory', status: ResultStatus.Error };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return {
|
|
54
|
+
result: `Source not found: ${e.message}`,
|
|
55
|
+
status: ResultStatus.Error
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
await fsp.mkdir(path.dirname(dest), { recursive: true });
|
|
60
|
+
await fsp.rename(src, dest);
|
|
61
|
+
return {
|
|
62
|
+
result: `Moved: ${src} -> ${dest}`,
|
|
63
|
+
status: ResultStatus.Success
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return { result: `Error moving: ${e.message}`, status: ResultStatus.Error };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=move-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move-file.js","sourceRoot":"","sources":["../../src/tools/move-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import { FileConfiguration } from '../lib/config.js';
|
|
3
|
+
import type { Workspace } from '../lib/workspace.js';
|
|
4
|
+
export declare class ReadFileTool extends Tool {
|
|
5
|
+
private ws;
|
|
6
|
+
private fc;
|
|
7
|
+
/**
|
|
8
|
+
* @param workspace - Workspace instance for path resolution and access control.
|
|
9
|
+
* @param fileConfig - Optional file configuration (character limits). Defaults to a new `FileConfiguration`.
|
|
10
|
+
*/
|
|
11
|
+
constructor(workspace: Workspace, fileConfig?: FileConfiguration);
|
|
12
|
+
protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=read-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-file.d.ts","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAGjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,qBAAa,YAAa,SAAQ,IAAI;IAClC,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,EAAE,CAAoB;IAE9B;;;OAGG;gBACS,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,iBAAiB;cA2BhD,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAmEvF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { PropertyType, ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import * as fsp from 'node:fs/promises';
|
|
3
|
+
import { FileConfiguration } from '../lib/config.js';
|
|
4
|
+
import { isBinary } from '../lib/helpers.js';
|
|
5
|
+
/** Tool that reads the contents of a text file within the allowed workspace directories. */
|
|
6
|
+
const TRUNCATION_SUFFIX = '\n... [truncated]';
|
|
7
|
+
export class ReadFileTool extends Tool {
|
|
8
|
+
ws;
|
|
9
|
+
fc;
|
|
10
|
+
/**
|
|
11
|
+
* @param workspace - Workspace instance for path resolution and access control.
|
|
12
|
+
* @param fileConfig - Optional file configuration (character limits). Defaults to a new `FileConfiguration`.
|
|
13
|
+
*/
|
|
14
|
+
constructor(workspace, fileConfig) {
|
|
15
|
+
super('read_file', 'Reads the contents of a text file. Supports optional max characters and line ranges (start_line/end_line, 1-indexed).', new ToolParameters({
|
|
16
|
+
path: new ToolParameterProperty('File path'),
|
|
17
|
+
max_chars: new ToolParameterProperty('Maximum number of characters to return (default: config max)', PropertyType.Integer),
|
|
18
|
+
start_line: new ToolParameterProperty('First line number to read (1-indexed, inclusive)', PropertyType.Integer),
|
|
19
|
+
end_line: new ToolParameterProperty('Last line number to read (1-indexed, inclusive)', PropertyType.Integer)
|
|
20
|
+
}, ['path']));
|
|
21
|
+
this.ws = workspace;
|
|
22
|
+
this.fc = fileConfig ?? new FileConfiguration();
|
|
23
|
+
}
|
|
24
|
+
async onExecute(args) {
|
|
25
|
+
const raw = args.path;
|
|
26
|
+
if (typeof raw !== 'string' || !raw.trim()) {
|
|
27
|
+
return { result: 'Path must be a non-empty string', status: ResultStatus.Error };
|
|
28
|
+
}
|
|
29
|
+
const resolved = this.ws.normalize(raw.trim());
|
|
30
|
+
if (!this.ws.canRead(resolved)) {
|
|
31
|
+
return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
|
|
32
|
+
}
|
|
33
|
+
let stat;
|
|
34
|
+
try {
|
|
35
|
+
stat = await fsp.stat(resolved);
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return {
|
|
39
|
+
result: `File not found: ${e.message}`,
|
|
40
|
+
status: ResultStatus.Error
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (!stat.isFile()) {
|
|
44
|
+
return { result: 'Path is not a file', status: ResultStatus.Error };
|
|
45
|
+
}
|
|
46
|
+
if (stat.size > this.fc.maxFileSize) {
|
|
47
|
+
return {
|
|
48
|
+
result: `File too large (${stat.size} bytes, max ${this.fc.maxFileSize})`,
|
|
49
|
+
status: ResultStatus.Error
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (await isBinary(resolved)) {
|
|
53
|
+
return {
|
|
54
|
+
result: 'File appears to be binary; use read_file for text files only',
|
|
55
|
+
status: ResultStatus.Error
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const maxChars = typeof args.max_chars === 'number'
|
|
59
|
+
? Math.max(1, Math.min(args.max_chars, this.fc.maxCharsPerFile))
|
|
60
|
+
: this.fc.maxCharsPerFile;
|
|
61
|
+
const startLine = typeof args.start_line === 'number' ? Math.max(1, args.start_line) : undefined;
|
|
62
|
+
const endLine = typeof args.end_line === 'number' && args.end_line > 0 ? args.end_line : undefined;
|
|
63
|
+
try {
|
|
64
|
+
const content = await fsp.readFile(resolved, 'utf-8');
|
|
65
|
+
const lines = content.split('\n');
|
|
66
|
+
const sliceStart = startLine ? startLine - 1 : 0;
|
|
67
|
+
const sliceEnd = endLine ? Math.min(endLine, lines.length) : lines.length;
|
|
68
|
+
const sliced = lines.slice(sliceStart, sliceEnd);
|
|
69
|
+
let result = sliced.join('\n');
|
|
70
|
+
if (result.length > maxChars) {
|
|
71
|
+
result = result.slice(0, maxChars - TRUNCATION_SUFFIX.length) + TRUNCATION_SUFFIX;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
result: `--- ${resolved} (lines ${sliceStart + 1}-${Math.min(sliceEnd, lines.length)} of ${lines.length}) ---\n${result}`,
|
|
75
|
+
status: ResultStatus.Success
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
return {
|
|
80
|
+
result: `Error reading file: ${e.message}`,
|
|
81
|
+
status: ResultStatus.Error
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=read-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import { FileConfiguration, SearchConfiguration } from '../lib/config.js';
|
|
3
|
+
import type { Workspace } from '../lib/workspace.js';
|
|
4
|
+
/** Tool that searches for files and directories by name pattern, content pattern, and/or timestamps within the allowed workspace directories. */
|
|
5
|
+
export declare class SearchEntriesTool extends Tool {
|
|
6
|
+
private ws;
|
|
7
|
+
private sc;
|
|
8
|
+
private fc;
|
|
9
|
+
/**
|
|
10
|
+
* @param workspace - Workspace instance for path resolution and access checks.
|
|
11
|
+
* @param searchConfig - Optional search configuration (limits, timeout).
|
|
12
|
+
* @param fileConfig - Optional file configuration (max chars, max file size).
|
|
13
|
+
*/
|
|
14
|
+
constructor(workspace: Workspace, searchConfig?: SearchConfiguration, fileConfig?: FileConfiguration);
|
|
15
|
+
protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAGjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAerD,iJAAiJ;AACjJ,qBAAa,iBAAkB,SAAQ,IAAI;IACvC,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,EAAE,CAAsB;IAChC,OAAO,CAAC,EAAE,CAAoB;IAE9B;;;;OAIG;gBAEC,SAAS,EAAE,SAAS,EACpB,YAAY,CAAC,EAAE,mBAAmB,EAClC,UAAU,CAAC,EAAE,iBAAiB;cAgDlB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAmNvF"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { PropertyType, ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import * as fsp from 'node:fs/promises';
|
|
3
|
+
import { FileConfiguration, SearchConfiguration } from '../lib/config.js';
|
|
4
|
+
import { isBinary } from '../lib/helpers.js';
|
|
5
|
+
var SearchType;
|
|
6
|
+
(function (SearchType) {
|
|
7
|
+
SearchType["File"] = "file";
|
|
8
|
+
SearchType["Directory"] = "directory";
|
|
9
|
+
SearchType["Both"] = "both";
|
|
10
|
+
})(SearchType || (SearchType = {}));
|
|
11
|
+
function parseType(raw) {
|
|
12
|
+
if (raw === SearchType.File)
|
|
13
|
+
return SearchType.File;
|
|
14
|
+
if (raw === SearchType.Directory)
|
|
15
|
+
return SearchType.Directory;
|
|
16
|
+
if (raw === undefined || raw === null)
|
|
17
|
+
return SearchType.Both;
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
/** Tool that searches for files and directories by name pattern, content pattern, and/or timestamps within the allowed workspace directories. */
|
|
21
|
+
export class SearchEntriesTool extends Tool {
|
|
22
|
+
ws;
|
|
23
|
+
sc;
|
|
24
|
+
fc;
|
|
25
|
+
/**
|
|
26
|
+
* @param workspace - Workspace instance for path resolution and access checks.
|
|
27
|
+
* @param searchConfig - Optional search configuration (limits, timeout).
|
|
28
|
+
* @param fileConfig - Optional file configuration (max chars, max file size).
|
|
29
|
+
*/
|
|
30
|
+
constructor(workspace, searchConfig, fileConfig) {
|
|
31
|
+
super('search_entries', 'Searches for files and directories by name and/or content pattern. If no pattern is given, returns all entries. File content matches show the matching line; file name matches show the path; directories are suffixed with "/".', new ToolParameters({
|
|
32
|
+
path: new ToolParameterProperty('Directory to search in (default: workspace root)'),
|
|
33
|
+
type: new ToolParameterProperty('Type of entries to search. Accepted values: "file", "directory", "both" (default).'),
|
|
34
|
+
name_pattern: new ToolParameterProperty('JavaScript regex to match file/directory names against (case-insensitive)'),
|
|
35
|
+
content_pattern: new ToolParameterProperty('JavaScript regex to search file contents (files only, case-insensitive)'),
|
|
36
|
+
max_results: new ToolParameterProperty('Maximum number of results to return', PropertyType.Integer),
|
|
37
|
+
max_size: new ToolParameterProperty('Maximum file size in bytes (capped by configuration max)', PropertyType.Integer),
|
|
38
|
+
created_after: new ToolParameterProperty('ISO date string — files created after this time'),
|
|
39
|
+
created_before: new ToolParameterProperty('ISO date string — files created before this time'),
|
|
40
|
+
modified_after: new ToolParameterProperty('ISO date string — files modified after this time'),
|
|
41
|
+
modified_before: new ToolParameterProperty('ISO date string — files modified before this time')
|
|
42
|
+
}, []));
|
|
43
|
+
this.ws = workspace;
|
|
44
|
+
this.sc = searchConfig ?? new SearchConfiguration();
|
|
45
|
+
this.fc = fileConfig ?? new FileConfiguration();
|
|
46
|
+
}
|
|
47
|
+
async onExecute(args) {
|
|
48
|
+
const namePatternRaw = args.name_pattern;
|
|
49
|
+
const contentPatternRaw = args.content_pattern;
|
|
50
|
+
const hasName = typeof namePatternRaw === 'string' && namePatternRaw.trim().length > 0;
|
|
51
|
+
const hasContent = typeof contentPatternRaw === 'string' && contentPatternRaw.trim().length > 0;
|
|
52
|
+
let nameRegex;
|
|
53
|
+
if (hasName) {
|
|
54
|
+
try {
|
|
55
|
+
nameRegex = new RegExp(namePatternRaw.trim(), 'iu');
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
return {
|
|
59
|
+
result: `Invalid name_pattern regex: ${e.message}`,
|
|
60
|
+
status: ResultStatus.Error
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let contentRegex;
|
|
65
|
+
if (hasContent) {
|
|
66
|
+
try {
|
|
67
|
+
contentRegex = new RegExp(contentPatternRaw.trim(), 'iu');
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
return {
|
|
71
|
+
result: `Invalid content_pattern regex: ${e.message}`,
|
|
72
|
+
status: ResultStatus.Error
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const searchType = parseType(args.type);
|
|
77
|
+
if (searchType === null) {
|
|
78
|
+
return {
|
|
79
|
+
result: 'Invalid type parameter. Accepted values: "file", "directory", "both".',
|
|
80
|
+
status: ResultStatus.Error
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const raw = args.path;
|
|
84
|
+
let searchDir;
|
|
85
|
+
if (typeof raw === 'string' && raw.trim()) {
|
|
86
|
+
searchDir = this.ws.normalize(raw.trim());
|
|
87
|
+
if (!this.ws.canRead(searchDir)) {
|
|
88
|
+
return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
searchDir = this.ws.currentPath;
|
|
93
|
+
}
|
|
94
|
+
const maxResults = typeof args.max_results === 'number'
|
|
95
|
+
? Math.max(1, args.max_results)
|
|
96
|
+
: this.sc.maxSearchResults;
|
|
97
|
+
const maxSize = typeof args.max_size === 'number'
|
|
98
|
+
? Math.max(1, Math.min(args.max_size, this.fc.maxFileSize))
|
|
99
|
+
: undefined;
|
|
100
|
+
const createdAfter = typeof args.created_after === 'string' ? new Date(args.created_after) : undefined;
|
|
101
|
+
const createdBefore = typeof args.created_before === 'string' ? new Date(args.created_before) : undefined;
|
|
102
|
+
const modifiedAfter = typeof args.modified_after === 'string' ? new Date(args.modified_after) : undefined;
|
|
103
|
+
const modifiedBefore = typeof args.modified_before === 'string' ? new Date(args.modified_before) : undefined;
|
|
104
|
+
if (createdAfter && isNaN(createdAfter.getTime()))
|
|
105
|
+
return { result: 'Invalid created_after date', status: ResultStatus.Error };
|
|
106
|
+
if (createdBefore && isNaN(createdBefore.getTime()))
|
|
107
|
+
return { result: 'Invalid created_before date', status: ResultStatus.Error };
|
|
108
|
+
if (modifiedAfter && isNaN(modifiedAfter.getTime()))
|
|
109
|
+
return { result: 'Invalid modified_after date', status: ResultStatus.Error };
|
|
110
|
+
if (modifiedBefore && isNaN(modifiedBefore.getTime()))
|
|
111
|
+
return { result: 'Invalid modified_before date', status: ResultStatus.Error };
|
|
112
|
+
const hasTimestampFilter = !!(createdAfter ||
|
|
113
|
+
createdBefore ||
|
|
114
|
+
modifiedAfter ||
|
|
115
|
+
modifiedBefore);
|
|
116
|
+
const deadline = Date.now() + this.sc.timeoutMs;
|
|
117
|
+
const results = [];
|
|
118
|
+
let visited = 0;
|
|
119
|
+
const maxChars = this.fc.maxCharsPerFile;
|
|
120
|
+
const walkErrors = [];
|
|
121
|
+
try {
|
|
122
|
+
for await (const entry of this.ws.walk(searchDir, (dirPath, _err) => {
|
|
123
|
+
walkErrors.push(dirPath);
|
|
124
|
+
})) {
|
|
125
|
+
if (Date.now() > deadline) {
|
|
126
|
+
return {
|
|
127
|
+
result: `Search timed out after ${visited} entries, found ${results.length} matches`,
|
|
128
|
+
status: ResultStatus.Success
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
visited++;
|
|
132
|
+
if (visited >= this.sc.maxTotalEntries) {
|
|
133
|
+
return {
|
|
134
|
+
result: `Searched too many entries (${visited}), aborting. Found ${results.length} matches.`,
|
|
135
|
+
status: ResultStatus.Error
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (results.length >= maxResults)
|
|
139
|
+
break;
|
|
140
|
+
const isDir = entry.dirent.isDirectory();
|
|
141
|
+
const isFile = entry.dirent.isFile();
|
|
142
|
+
if (searchType === SearchType.File && !isFile)
|
|
143
|
+
continue;
|
|
144
|
+
if (searchType === SearchType.Directory && !isDir)
|
|
145
|
+
continue;
|
|
146
|
+
if (nameRegex && !nameRegex.test(entry.dirent.name))
|
|
147
|
+
continue;
|
|
148
|
+
if (isDir) {
|
|
149
|
+
if (nameRegex) {
|
|
150
|
+
results.push(entry.filePath + '/');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else if (isFile) {
|
|
154
|
+
let st;
|
|
155
|
+
const needsStat = hasTimestampFilter || (contentRegex && maxSize !== undefined);
|
|
156
|
+
if (needsStat) {
|
|
157
|
+
try {
|
|
158
|
+
st = await fsp.stat(entry.filePath);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (contentRegex && maxSize !== undefined && st && st.size > maxSize)
|
|
165
|
+
continue;
|
|
166
|
+
if (hasTimestampFilter && st) {
|
|
167
|
+
if (createdAfter && st.birthtime.getTime() < createdAfter.getTime())
|
|
168
|
+
continue;
|
|
169
|
+
if (createdBefore && st.birthtime.getTime() > createdBefore.getTime())
|
|
170
|
+
continue;
|
|
171
|
+
if (modifiedAfter && st.mtime.getTime() < modifiedAfter.getTime())
|
|
172
|
+
continue;
|
|
173
|
+
if (modifiedBefore && st.mtime.getTime() > modifiedBefore.getTime())
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (contentRegex) {
|
|
177
|
+
if (await isBinary(entry.filePath))
|
|
178
|
+
continue;
|
|
179
|
+
try {
|
|
180
|
+
const content = await fsp.readFile(entry.filePath, 'utf-8');
|
|
181
|
+
const truncated = content.slice(0, maxChars);
|
|
182
|
+
const lines = truncated.split('\n');
|
|
183
|
+
for (let i = 0; i < lines.length; i++) {
|
|
184
|
+
if (results.length >= maxResults)
|
|
185
|
+
break;
|
|
186
|
+
const line = lines[i];
|
|
187
|
+
if (line === undefined)
|
|
188
|
+
continue;
|
|
189
|
+
if (contentRegex.test(line)) {
|
|
190
|
+
const ts = hasTimestampFilter && st
|
|
191
|
+
? ` (created: ${st.birthtime.toISOString()}, modified: ${st.mtime.toISOString()})`
|
|
192
|
+
: '';
|
|
193
|
+
results.push(`${entry.filePath}:${i + 1}: ${line.trim().slice(0, 200)}${ts}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// skip unreadable files
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const ts = hasTimestampFilter && st
|
|
203
|
+
? ` (created: ${st.birthtime.toISOString()}, modified: ${st.mtime.toISOString()})`
|
|
204
|
+
: '';
|
|
205
|
+
results.push(entry.filePath + ts);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const errorWarning = walkErrors.length > 0
|
|
210
|
+
? `(Warning: could not read ${walkErrors.length} director${walkErrors.length === 1 ? 'y' : 'ies'}, results may be incomplete)\n\n`
|
|
211
|
+
: '';
|
|
212
|
+
if (visited > this.sc.maxDisplayEntries) {
|
|
213
|
+
return {
|
|
214
|
+
result: `${errorWarning}Searched ${visited} entries, found ${results.length} matches`,
|
|
215
|
+
status: ResultStatus.Success
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (results.length === 0) {
|
|
219
|
+
return {
|
|
220
|
+
result: `${errorWarning}No matching entries found`,
|
|
221
|
+
status: ResultStatus.Success
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
result: errorWarning + results.join('\n'),
|
|
226
|
+
status: ResultStatus.Success
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
return {
|
|
231
|
+
result: `Error searching: ${e.message}`,
|
|
232
|
+
status: ResultStatus.Error
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import type { Workspace } from '../lib/workspace.js';
|
|
3
|
+
/** Tool that changes the current workspace path to a new directory within configured accessible directories. */
|
|
4
|
+
export declare class SwitchWorkspaceTool extends Tool {
|
|
5
|
+
private ws;
|
|
6
|
+
/**
|
|
7
|
+
* @param workspace - Workspace instance for path resolution and workspace switching.
|
|
8
|
+
*/
|
|
9
|
+
constructor(workspace: Workspace);
|
|
10
|
+
protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=switch-workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"switch-workspace.d.ts","sourceRoot":"","sources":["../../src/tools/switch-workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAEjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,gHAAgH;AAChH,qBAAa,mBAAoB,SAAQ,IAAI;IACzC,OAAO,CAAC,EAAE,CAAY;IAEtB;;OAEG;gBACS,SAAS,EAAE,SAAS;cAchB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAkBvF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
|
|
2
|
+
/** Tool that changes the current workspace path to a new directory within configured accessible directories. */
|
|
3
|
+
export class SwitchWorkspaceTool extends Tool {
|
|
4
|
+
ws;
|
|
5
|
+
/**
|
|
6
|
+
* @param workspace - Workspace instance for path resolution and workspace switching.
|
|
7
|
+
*/
|
|
8
|
+
constructor(workspace) {
|
|
9
|
+
super('switch_workspace', 'Changes the current workspace path to a new directory within configured accessible directories. Must be called before any other filesystem tool when changing workspace. Do NOT call this tool in parallel with any other filesystem tool — call it first, then call the other tools sequentially.', new ToolParameters({
|
|
10
|
+
path: new ToolParameterProperty('Target directory path')
|
|
11
|
+
}, ['path']));
|
|
12
|
+
this.ws = workspace;
|
|
13
|
+
}
|
|
14
|
+
async onExecute(args) {
|
|
15
|
+
const raw = args.path;
|
|
16
|
+
if (typeof raw !== 'string' || !raw.trim()) {
|
|
17
|
+
return { result: 'path must be a non-empty string', status: ResultStatus.Error };
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
await this.ws.switchWorkspace(raw.trim());
|
|
21
|
+
return {
|
|
22
|
+
result: `Switched workspace to: ${this.ws.currentPath}`,
|
|
23
|
+
status: ResultStatus.Success
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
return {
|
|
28
|
+
result: e.message,
|
|
29
|
+
status: ResultStatus.Error
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=switch-workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"switch-workspace.js","sourceRoot":"","sources":["../../src/tools/switch-workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,YAAY,EACZ,IAAI,EACJ,qBAAqB,EACrB,cAAc,EACjB,MAAM,2BAA2B,CAAC;AAGnC,gHAAgH;AAChH,MAAM,OAAO,mBAAoB,SAAQ,IAAI;IACjC,EAAE,CAAY;IAEtB;;OAEG;IACH,YAAY,SAAoB;QAC5B,KAAK,CACD,kBAAkB,EAClB,oSAAoS,EACpS,IAAI,cAAc,CACd;YACI,IAAI,EAAE,IAAI,qBAAqB,CAAC,uBAAuB,CAAC;SAC3D,EACD,CAAC,MAAM,CAAC,CACX,CACJ,CAAC;QACF,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;IACxB,CAAC;IAES,KAAK,CAAC,SAAS,CAAC,IAA6B;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YACzC,OAAO,EAAE,MAAM,EAAE,iCAAiC,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;QACrF,CAAC;QACD,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1C,OAAO;gBACH,MAAM,EAAE,0BAA0B,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE;gBACvD,MAAM,EAAE,YAAY,CAAC,OAAO;aAC/B,CAAC;QACN,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO;gBACH,MAAM,EAAG,CAAW,CAAC,OAAO;gBAC5B,MAAM,EAAE,YAAY,CAAC,KAAK;aAC7B,CAAC;QACN,CAAC;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import { FileConfiguration } from '../lib/config.js';
|
|
3
|
+
import type { Workspace } from '../lib/workspace.js';
|
|
4
|
+
/** Tool that writes text content to a file within the allowed workspace directories. */
|
|
5
|
+
export declare class WriteFileTool extends Tool {
|
|
6
|
+
private ws;
|
|
7
|
+
private fc;
|
|
8
|
+
/**
|
|
9
|
+
* @param workspace - Workspace instance for path resolution and access control.
|
|
10
|
+
* @param fileConfig - Optional file configuration (character limits). Defaults to a new `FileConfiguration`.
|
|
11
|
+
*/
|
|
12
|
+
constructor(workspace: Workspace, fileConfig?: FileConfiguration);
|
|
13
|
+
protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=write-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-file.d.ts","sourceRoot":"","sources":["../../src/tools/write-file.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAEjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,wFAAwF;AACxF,qBAAa,aAAc,SAAQ,IAAI;IACnC,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,EAAE,CAAoB;IAE9B;;;OAGG;gBACS,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,iBAAiB;cAgBhD,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAkCvF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
|
|
2
|
+
import * as fsp from 'node:fs/promises';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { FileConfiguration } from '../lib/config.js';
|
|
5
|
+
/** Tool that writes text content to a file within the allowed workspace directories. */
|
|
6
|
+
export class WriteFileTool extends Tool {
|
|
7
|
+
ws;
|
|
8
|
+
fc;
|
|
9
|
+
/**
|
|
10
|
+
* @param workspace - Workspace instance for path resolution and access control.
|
|
11
|
+
* @param fileConfig - Optional file configuration (character limits). Defaults to a new `FileConfiguration`.
|
|
12
|
+
*/
|
|
13
|
+
constructor(workspace, fileConfig) {
|
|
14
|
+
super('write_file', 'Writes text content to a file. Creates parent directories automatically. Only for text files.', new ToolParameters({
|
|
15
|
+
path: new ToolParameterProperty('File path'),
|
|
16
|
+
content: new ToolParameterProperty('Text content to write')
|
|
17
|
+
}, ['path', 'content']));
|
|
18
|
+
this.ws = workspace;
|
|
19
|
+
this.fc = fileConfig ?? new FileConfiguration();
|
|
20
|
+
}
|
|
21
|
+
async onExecute(args) {
|
|
22
|
+
const raw = args.path;
|
|
23
|
+
if (typeof raw !== 'string' || !raw.trim()) {
|
|
24
|
+
return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
|
|
25
|
+
}
|
|
26
|
+
const resolved = this.ws.normalize(raw.trim());
|
|
27
|
+
if (!this.ws.canWrite(resolved)) {
|
|
28
|
+
return {
|
|
29
|
+
result: 'Invalid or inaccessible path (must be within writable directory)',
|
|
30
|
+
status: ResultStatus.Error
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const content = args.content;
|
|
34
|
+
if (typeof content !== 'string') {
|
|
35
|
+
return { result: 'content must be a string', status: ResultStatus.Error };
|
|
36
|
+
}
|
|
37
|
+
if (content.length > this.fc.maxCharsPerFile) {
|
|
38
|
+
return {
|
|
39
|
+
result: `Content exceeds max length of ${this.fc.maxCharsPerFile}`,
|
|
40
|
+
status: ResultStatus.Error
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await fsp.mkdir(path.dirname(resolved), { recursive: true });
|
|
45
|
+
await fsp.writeFile(resolved, content, 'utf-8');
|
|
46
|
+
return { result: `Written: ${resolved}`, status: ResultStatus.Success };
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
return {
|
|
50
|
+
result: `Error writing file: ${e.message}`,
|
|
51
|
+
status: ResultStatus.Error
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=write-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-file.js","sourceRoot":"","sources":["../../src/tools/write-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,YAAY,EACZ,IAAI,EACJ,qBAAqB,EACrB,cAAc,EACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,wFAAwF;AACxF,MAAM,OAAO,aAAc,SAAQ,IAAI;IAC3B,EAAE,CAAY;IACd,EAAE,CAAoB;IAE9B;;;OAGG;IACH,YAAY,SAAoB,EAAE,UAA8B;QAC5D,KAAK,CACD,YAAY,EACZ,+FAA+F,EAC/F,IAAI,cAAc,CACd;YACI,IAAI,EAAE,IAAI,qBAAqB,CAAC,WAAW,CAAC;YAC5C,OAAO,EAAE,IAAI,qBAAqB,CAAC,uBAAuB,CAAC;SAC9D,EACD,CAAC,MAAM,EAAE,SAAS,CAAC,CACtB,CACJ,CAAC;QACF,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;QACpB,IAAI,CAAC,EAAE,GAAG,UAAU,IAAI,IAAI,iBAAiB,EAAE,CAAC;IACpD,CAAC;IAES,KAAK,CAAC,SAAS,CAAC,IAA6B;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YACzC,OAAO,EAAE,MAAM,EAAE,8BAA8B,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;QAClF,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,OAAO;gBACH,MAAM,EAAE,kEAAkE;gBAC1E,MAAM,EAAE,YAAY,CAAC,KAAK;aAC7B,CAAC;QACN,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,EAAE,MAAM,EAAE,0BAA0B,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;QAC9E,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC;YAC3C,OAAO;gBACH,MAAM,EAAE,iCAAiC,IAAI,CAAC,EAAE,CAAC,eAAe,EAAE;gBAClE,MAAM,EAAE,YAAY,CAAC,KAAK;aAC7B,CAAC;QACN,CAAC;QAED,IAAI,CAAC;YACD,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7D,MAAM,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAChD,OAAO,EAAE,MAAM,EAAE,YAAY,QAAQ,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;QAC5E,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO;gBACH,MAAM,EAAE,uBAAwB,CAAW,CAAC,OAAO,EAAE;gBACrD,MAAM,EAAE,YAAY,CAAC,KAAK;aAC7B,CAAC;QACN,CAAC;IACL,CAAC;CACJ"}
|