localwp-mcp 0.1.4 → 0.1.6
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 +96 -72
- package/dist/backup-management.d.ts +186 -0
- package/dist/backup-management.js +265 -0
- package/dist/backup-management.js.map +1 -0
- package/dist/backup.d.ts +116 -0
- package/dist/backup.js +232 -11
- package/dist/backup.js.map +1 -1
- package/dist/logs.d.ts +2 -1
- package/dist/logs.js +14 -1
- package/dist/logs.js.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.js +5 -2
- package/dist/server.js.map +1 -1
- package/dist/tool-schemas.d.ts +1 -5
- package/dist/tool-schemas.js +2 -2
- package/dist/tool-schemas.js.map +1 -1
- package/dist/tools/cleanup-backups.d.ts +2 -0
- package/dist/tools/cleanup-backups.js +55 -0
- package/dist/tools/cleanup-backups.js.map +1 -0
- package/dist/tools/delete-backup.d.ts +2 -0
- package/dist/tools/delete-backup.js +38 -0
- package/dist/tools/delete-backup.js.map +1 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-backups.d.ts +2 -0
- package/dist/tools/list-backups.js +29 -0
- package/dist/tools/list-backups.js.map +1 -0
- package/dist/tools/preview-restore-backup.d.ts +2 -0
- package/dist/tools/preview-restore-backup.js +52 -0
- package/dist/tools/preview-restore-backup.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +21 -0
- package/dist/version.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,52 +17,43 @@ It automatically finds your Local sites, uses the correct Local PHP and MySQL ru
|
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
20
|
-
Use `npx` in your MCP client:
|
|
20
|
+
Use `npx` in your MCP client:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"localwp": {
|
|
26
|
+
"command": "npx",
|
|
27
|
+
"args": ["-y", "localwp-mcp"],
|
|
28
|
+
"env": {
|
|
29
|
+
"LOCALWP_MCP_PROFILE": "safe"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`localwp-mcp` currently supports `STDIO` clients. Streamable HTTP is not supported yet.
|
|
37
|
+
|
|
38
|
+
If you want the MCP to focus on one site by default, set:
|
|
21
39
|
|
|
22
40
|
```json
|
|
23
|
-
{
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
npm install -g localwp-mcp
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Then use:
|
|
43
|
-
|
|
44
|
-
```json
|
|
45
|
-
{
|
|
46
|
-
"mcpServers": {
|
|
47
|
-
"localwp": {
|
|
48
|
-
"command": "localwp-mcp",
|
|
49
|
-
"env": {
|
|
50
|
-
"LOCALWP_MCP_PROFILE": "safe"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
If you want the MCP to focus on one site by default, set:
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
{
|
|
61
|
-
"LOCAL_SITE_NAME": "example-site"
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Access Modes
|
|
41
|
+
{
|
|
42
|
+
"LOCAL_SITE_NAME": "example-site"
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Client Setup
|
|
47
|
+
|
|
48
|
+
Step-by-step setup guides:
|
|
49
|
+
|
|
50
|
+
- [Codex Desktop](./docs/clients/codex.md)
|
|
51
|
+
- [Cursor](./docs/clients/cursor.md)
|
|
52
|
+
- [Claude Code](./docs/clients/claude-code.md)
|
|
53
|
+
- [OpenCode](./docs/clients/opencode.md)
|
|
54
|
+
- [Generic STDIO MCP Clients](./docs/clients/generic-stdio.md)
|
|
55
|
+
|
|
56
|
+
## Access Modes
|
|
66
57
|
|
|
67
58
|
`localwp-mcp` has 2 access modes:
|
|
68
59
|
|
|
@@ -100,11 +91,18 @@ Those will tell you:
|
|
|
100
91
|
|
|
101
92
|
### Inspect a Site
|
|
102
93
|
|
|
103
|
-
Use:
|
|
104
|
-
|
|
105
|
-
- `local_site_info`
|
|
106
|
-
- `local_doctor`
|
|
107
|
-
- `local_logs`
|
|
94
|
+
Use:
|
|
95
|
+
|
|
96
|
+
- `local_site_info`
|
|
97
|
+
- `local_doctor`
|
|
98
|
+
- `local_logs`
|
|
99
|
+
|
|
100
|
+
`local_logs` accepts:
|
|
101
|
+
|
|
102
|
+
- `site`
|
|
103
|
+
- `global`
|
|
104
|
+
- `all`
|
|
105
|
+
- aliases: `both`, `combined`
|
|
108
106
|
|
|
109
107
|
### Work With Site Files
|
|
110
108
|
|
|
@@ -153,21 +151,36 @@ Use:
|
|
|
153
151
|
- `mysql_schema`
|
|
154
152
|
Table listing and table description helpers
|
|
155
153
|
|
|
156
|
-
### Back Up or Restore a Site
|
|
157
|
-
|
|
158
|
-
Use:
|
|
159
|
-
|
|
160
|
-
- `backup_site`
|
|
161
|
-
- `
|
|
162
|
-
- `
|
|
163
|
-
- `
|
|
154
|
+
### Back Up or Restore a Site
|
|
155
|
+
|
|
156
|
+
Use:
|
|
157
|
+
|
|
158
|
+
- `backup_site`
|
|
159
|
+
- `list_backups`
|
|
160
|
+
- `delete_backup`
|
|
161
|
+
- `cleanup_backups`
|
|
162
|
+
- `db_export`
|
|
163
|
+
- `db_import`
|
|
164
|
+
- `preview_restore_backup`
|
|
165
|
+
- `restore_backup`
|
|
164
166
|
|
|
165
167
|
`backup_site` supports:
|
|
166
168
|
|
|
167
169
|
- `database`
|
|
168
170
|
- `full`
|
|
169
171
|
|
|
170
|
-
The `full` backup format is folder-based and includes the site's `app`, `conf`, and `logs` directories plus a fresh SQL dump.
|
|
172
|
+
The `full` backup format is folder-based and includes the site's `app`, `conf`, and `logs` directories plus a fresh SQL dump.
|
|
173
|
+
|
|
174
|
+
If you want to manage backup storage over time:
|
|
175
|
+
|
|
176
|
+
- `list_backups`
|
|
177
|
+
Shows the managed backup directories and SQL artifacts for a site.
|
|
178
|
+
- `delete_backup`
|
|
179
|
+
Removes one managed backup artifact in `full-access`.
|
|
180
|
+
- `cleanup_backups`
|
|
181
|
+
Cleans up older backups by age and/or retention count in `full-access`.
|
|
182
|
+
|
|
183
|
+
Use `preview_restore_backup` when you want to inspect the restore plan, effective restore mode, compatibility warnings, and current site state before making changes.
|
|
171
184
|
|
|
172
185
|
## Built-In Capabilities
|
|
173
186
|
|
|
@@ -189,11 +202,15 @@ The `full` backup format is folder-based and includes the site's `app`, `conf`,
|
|
|
189
202
|
- `execute_wp_cli`
|
|
190
203
|
- `mysql_query`
|
|
191
204
|
- `mysql_execute`
|
|
192
|
-
- `mysql_schema`
|
|
193
|
-
- `db_export`
|
|
194
|
-
- `db_import`
|
|
195
|
-
- `backup_site`
|
|
196
|
-
- `
|
|
205
|
+
- `mysql_schema`
|
|
206
|
+
- `db_export`
|
|
207
|
+
- `db_import`
|
|
208
|
+
- `backup_site`
|
|
209
|
+
- `list_backups`
|
|
210
|
+
- `delete_backup`
|
|
211
|
+
- `cleanup_backups`
|
|
212
|
+
- `preview_restore_backup`
|
|
213
|
+
- `restore_backup`
|
|
197
214
|
|
|
198
215
|
### MCP Resources
|
|
199
216
|
|
|
@@ -207,15 +224,22 @@ The `full` backup format is folder-based and includes the site's `app`, `conf`,
|
|
|
207
224
|
- `diagnose_local_site`
|
|
208
225
|
- `restore_local_site`
|
|
209
226
|
|
|
210
|
-
## Platform Support
|
|
211
|
-
|
|
212
|
-
`localwp-mcp` is designed for:
|
|
213
|
-
|
|
214
|
-
- macOS
|
|
215
|
-
- Windows
|
|
216
|
-
- Linux
|
|
217
|
-
|
|
218
|
-
It supports both current Local `lightning-services` layouts and older `site-binaries` layouts.
|
|
227
|
+
## Platform Support
|
|
228
|
+
|
|
229
|
+
`localwp-mcp` is designed for:
|
|
230
|
+
|
|
231
|
+
- macOS
|
|
232
|
+
- Windows
|
|
233
|
+
- Linux
|
|
234
|
+
|
|
235
|
+
It supports both current Local `lightning-services` layouts and older `site-binaries` layouts.
|
|
236
|
+
|
|
237
|
+
Live validation has been completed on:
|
|
238
|
+
|
|
239
|
+
- macOS
|
|
240
|
+
- Windows
|
|
241
|
+
|
|
242
|
+
Linux support is implemented in the code paths and tests, but has not yet been live-validated on a machine with LocalWP.
|
|
219
243
|
|
|
220
244
|
## Useful Environment Variables
|
|
221
245
|
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { BackupArtifactCategory, BackupScope, SiteContext } from "./types.js";
|
|
2
|
+
export declare const backupArtifactCategories: readonly ["full_backup", "database_backup", "pre_restore_backup", "database_export", "pre_import_backup", "sql_file"];
|
|
3
|
+
export declare const backupArtifactCategoryAliases: {
|
|
4
|
+
readonly full: readonly ["full_backup"];
|
|
5
|
+
readonly database: readonly ["database_backup"];
|
|
6
|
+
readonly pre_restore: readonly ["pre_restore_backup"];
|
|
7
|
+
readonly pre_import: readonly ["pre_import_backup"];
|
|
8
|
+
readonly managed_export: readonly ["database_export"];
|
|
9
|
+
readonly standalone_sql: readonly ["sql_file"];
|
|
10
|
+
readonly export: readonly ["database_export", "sql_file"];
|
|
11
|
+
readonly exports: readonly ["database_export", "sql_file"];
|
|
12
|
+
};
|
|
13
|
+
export declare const acceptedBackupArtifactCategoryInputs: readonly ["full_backup", "database_backup", "pre_restore_backup", "database_export", "pre_import_backup", "sql_file", ...string[]];
|
|
14
|
+
interface BackupArtifact {
|
|
15
|
+
path: string;
|
|
16
|
+
absolutePath: string;
|
|
17
|
+
kind: "file" | "directory";
|
|
18
|
+
category: BackupArtifactCategory;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
modifiedAt: string;
|
|
21
|
+
sizeBytes: number;
|
|
22
|
+
manifestPath: string | null;
|
|
23
|
+
manifestCreatedAt: string | null;
|
|
24
|
+
scope: BackupScope | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function listManagedBackups(context: SiteContext, options?: {
|
|
27
|
+
rootPath?: string;
|
|
28
|
+
}): Promise<{
|
|
29
|
+
site: {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
status: string;
|
|
33
|
+
domain: string | undefined;
|
|
34
|
+
path: string;
|
|
35
|
+
wpRoot: string;
|
|
36
|
+
localVersion: string | undefined;
|
|
37
|
+
phpVersion: string | null;
|
|
38
|
+
mysqlVersion: string | null;
|
|
39
|
+
httpPort: number | null;
|
|
40
|
+
mysqlPort: number | null;
|
|
41
|
+
};
|
|
42
|
+
selectionMethod: string | undefined;
|
|
43
|
+
accessProfile: import("./types.js").AccessProfile;
|
|
44
|
+
backupRoot: string;
|
|
45
|
+
categorySupport: {
|
|
46
|
+
canonical: ("full_backup" | "database_backup" | "pre_restore_backup" | "database_export" | "pre_import_backup" | "sql_file")[];
|
|
47
|
+
aliases: {
|
|
48
|
+
readonly full: readonly ["full_backup"];
|
|
49
|
+
readonly database: readonly ["database_backup"];
|
|
50
|
+
readonly pre_restore: readonly ["pre_restore_backup"];
|
|
51
|
+
readonly pre_import: readonly ["pre_import_backup"];
|
|
52
|
+
readonly managed_export: readonly ["database_export"];
|
|
53
|
+
readonly standalone_sql: readonly ["sql_file"];
|
|
54
|
+
readonly export: readonly ["database_export", "sql_file"];
|
|
55
|
+
readonly exports: readonly ["database_export", "sql_file"];
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
artifacts: BackupArtifact[];
|
|
59
|
+
}>;
|
|
60
|
+
export declare function deleteManagedBackup(context: SiteContext, options: {
|
|
61
|
+
backupPath: string;
|
|
62
|
+
rootPath?: string;
|
|
63
|
+
missingOk?: boolean;
|
|
64
|
+
}): Promise<{
|
|
65
|
+
targetPath: string;
|
|
66
|
+
deleted: boolean;
|
|
67
|
+
existedBefore: boolean;
|
|
68
|
+
missingOk: boolean;
|
|
69
|
+
site: {
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
status: string;
|
|
73
|
+
domain: string | undefined;
|
|
74
|
+
path: string;
|
|
75
|
+
wpRoot: string;
|
|
76
|
+
localVersion: string | undefined;
|
|
77
|
+
phpVersion: string | null;
|
|
78
|
+
mysqlVersion: string | null;
|
|
79
|
+
httpPort: number | null;
|
|
80
|
+
mysqlPort: number | null;
|
|
81
|
+
};
|
|
82
|
+
selectionMethod: string | undefined;
|
|
83
|
+
accessProfile: import("./types.js").AccessProfile;
|
|
84
|
+
backupRoot: string;
|
|
85
|
+
categorySupport: {
|
|
86
|
+
canonical: ("full_backup" | "database_backup" | "pre_restore_backup" | "database_export" | "pre_import_backup" | "sql_file")[];
|
|
87
|
+
aliases: {
|
|
88
|
+
readonly full: readonly ["full_backup"];
|
|
89
|
+
readonly database: readonly ["database_backup"];
|
|
90
|
+
readonly pre_restore: readonly ["pre_restore_backup"];
|
|
91
|
+
readonly pre_import: readonly ["pre_import_backup"];
|
|
92
|
+
readonly managed_export: readonly ["database_export"];
|
|
93
|
+
readonly standalone_sql: readonly ["sql_file"];
|
|
94
|
+
readonly export: readonly ["database_export", "sql_file"];
|
|
95
|
+
readonly exports: readonly ["database_export", "sql_file"];
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
artifacts: BackupArtifact[];
|
|
99
|
+
} | {
|
|
100
|
+
targetPath: string;
|
|
101
|
+
deleted: boolean;
|
|
102
|
+
existedBefore: boolean;
|
|
103
|
+
deletedArtifact: BackupArtifact;
|
|
104
|
+
missingOk: boolean;
|
|
105
|
+
site: {
|
|
106
|
+
id: string;
|
|
107
|
+
name: string;
|
|
108
|
+
status: string;
|
|
109
|
+
domain: string | undefined;
|
|
110
|
+
path: string;
|
|
111
|
+
wpRoot: string;
|
|
112
|
+
localVersion: string | undefined;
|
|
113
|
+
phpVersion: string | null;
|
|
114
|
+
mysqlVersion: string | null;
|
|
115
|
+
httpPort: number | null;
|
|
116
|
+
mysqlPort: number | null;
|
|
117
|
+
};
|
|
118
|
+
selectionMethod: string | undefined;
|
|
119
|
+
accessProfile: import("./types.js").AccessProfile;
|
|
120
|
+
backupRoot: string;
|
|
121
|
+
categorySupport: {
|
|
122
|
+
canonical: ("full_backup" | "database_backup" | "pre_restore_backup" | "database_export" | "pre_import_backup" | "sql_file")[];
|
|
123
|
+
aliases: {
|
|
124
|
+
readonly full: readonly ["full_backup"];
|
|
125
|
+
readonly database: readonly ["database_backup"];
|
|
126
|
+
readonly pre_restore: readonly ["pre_restore_backup"];
|
|
127
|
+
readonly pre_import: readonly ["pre_import_backup"];
|
|
128
|
+
readonly managed_export: readonly ["database_export"];
|
|
129
|
+
readonly standalone_sql: readonly ["sql_file"];
|
|
130
|
+
readonly export: readonly ["database_export", "sql_file"];
|
|
131
|
+
readonly exports: readonly ["database_export", "sql_file"];
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
artifacts: BackupArtifact[];
|
|
135
|
+
}>;
|
|
136
|
+
export declare function cleanupManagedBackups(context: SiteContext, options?: {
|
|
137
|
+
rootPath?: string;
|
|
138
|
+
categories?: string[];
|
|
139
|
+
olderThanDays?: number;
|
|
140
|
+
keepLatest?: number;
|
|
141
|
+
dryRun?: boolean;
|
|
142
|
+
}): Promise<{
|
|
143
|
+
categories: BackupArtifactCategory[];
|
|
144
|
+
requestedCategories: string[] | null;
|
|
145
|
+
olderThanDays: number | null;
|
|
146
|
+
keepLatest: number | null;
|
|
147
|
+
dryRun: boolean;
|
|
148
|
+
deletedCount: number;
|
|
149
|
+
reclaimedBytesEstimate: number;
|
|
150
|
+
deletedArtifacts: BackupArtifact[];
|
|
151
|
+
candidateArtifacts: BackupArtifact[];
|
|
152
|
+
retainedArtifacts: BackupArtifact[];
|
|
153
|
+
site: {
|
|
154
|
+
id: string;
|
|
155
|
+
name: string;
|
|
156
|
+
status: string;
|
|
157
|
+
domain: string | undefined;
|
|
158
|
+
path: string;
|
|
159
|
+
wpRoot: string;
|
|
160
|
+
localVersion: string | undefined;
|
|
161
|
+
phpVersion: string | null;
|
|
162
|
+
mysqlVersion: string | null;
|
|
163
|
+
httpPort: number | null;
|
|
164
|
+
mysqlPort: number | null;
|
|
165
|
+
};
|
|
166
|
+
selectionMethod: string | undefined;
|
|
167
|
+
accessProfile: import("./types.js").AccessProfile;
|
|
168
|
+
backupRoot: string;
|
|
169
|
+
categorySupport: {
|
|
170
|
+
canonical: ("full_backup" | "database_backup" | "pre_restore_backup" | "database_export" | "pre_import_backup" | "sql_file")[];
|
|
171
|
+
aliases: {
|
|
172
|
+
readonly full: readonly ["full_backup"];
|
|
173
|
+
readonly database: readonly ["database_backup"];
|
|
174
|
+
readonly pre_restore: readonly ["pre_restore_backup"];
|
|
175
|
+
readonly pre_import: readonly ["pre_import_backup"];
|
|
176
|
+
readonly managed_export: readonly ["database_export"];
|
|
177
|
+
readonly standalone_sql: readonly ["sql_file"];
|
|
178
|
+
readonly export: readonly ["database_export", "sql_file"];
|
|
179
|
+
readonly exports: readonly ["database_export", "sql_file"];
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
artifacts: BackupArtifact[];
|
|
183
|
+
}>;
|
|
184
|
+
export declare function normalizeBackupArtifactCategoryInputs(categories: string[] | undefined): BackupArtifactCategory[];
|
|
185
|
+
export declare function resolveManagedBackupRoot(context: SiteContext, rootPath?: string): string;
|
|
186
|
+
export {};
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { lstat, readFile, readdir, rm } from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { config } from "./config.js";
|
|
4
|
+
import { summarizeSite } from "./local-sites.js";
|
|
5
|
+
import { isReadablePath } from "./process-utils.js";
|
|
6
|
+
export const backupArtifactCategories = [
|
|
7
|
+
"full_backup",
|
|
8
|
+
"database_backup",
|
|
9
|
+
"pre_restore_backup",
|
|
10
|
+
"database_export",
|
|
11
|
+
"pre_import_backup",
|
|
12
|
+
"sql_file",
|
|
13
|
+
];
|
|
14
|
+
export const backupArtifactCategoryAliases = {
|
|
15
|
+
full: ["full_backup"],
|
|
16
|
+
database: ["database_backup"],
|
|
17
|
+
pre_restore: ["pre_restore_backup"],
|
|
18
|
+
pre_import: ["pre_import_backup"],
|
|
19
|
+
managed_export: ["database_export"],
|
|
20
|
+
standalone_sql: ["sql_file"],
|
|
21
|
+
export: ["database_export", "sql_file"],
|
|
22
|
+
exports: ["database_export", "sql_file"],
|
|
23
|
+
};
|
|
24
|
+
export const acceptedBackupArtifactCategoryInputs = [
|
|
25
|
+
...backupArtifactCategories,
|
|
26
|
+
...Object.keys(backupArtifactCategoryAliases),
|
|
27
|
+
];
|
|
28
|
+
export async function listManagedBackups(context, options = {}) {
|
|
29
|
+
const backupRoot = resolveManagedBackupRoot(context, options.rootPath);
|
|
30
|
+
if (!(await isReadablePath(backupRoot))) {
|
|
31
|
+
return {
|
|
32
|
+
site: summarizeSite(context.site),
|
|
33
|
+
selectionMethod: context.selectionMethod,
|
|
34
|
+
accessProfile: config.profile,
|
|
35
|
+
backupRoot,
|
|
36
|
+
categorySupport: buildBackupCategorySupport(),
|
|
37
|
+
artifacts: [],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const artifacts = await discoverBackupArtifacts(backupRoot);
|
|
41
|
+
return {
|
|
42
|
+
site: summarizeSite(context.site),
|
|
43
|
+
selectionMethod: context.selectionMethod,
|
|
44
|
+
accessProfile: config.profile,
|
|
45
|
+
backupRoot,
|
|
46
|
+
categorySupport: buildBackupCategorySupport(),
|
|
47
|
+
artifacts,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export async function deleteManagedBackup(context, options) {
|
|
51
|
+
const inventory = await listManagedBackups(context, {
|
|
52
|
+
rootPath: options.rootPath,
|
|
53
|
+
});
|
|
54
|
+
const normalizedBackupPath = normalizeBackupRelativePath(options.backupPath);
|
|
55
|
+
const artifact = inventory.artifacts.find((candidate) => candidate.path === normalizedBackupPath);
|
|
56
|
+
if (!artifact) {
|
|
57
|
+
if (options.missingOk) {
|
|
58
|
+
return {
|
|
59
|
+
...inventory,
|
|
60
|
+
targetPath: normalizedBackupPath,
|
|
61
|
+
deleted: false,
|
|
62
|
+
existedBefore: false,
|
|
63
|
+
missingOk: true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`No managed backup artifact matched '${options.backupPath}' under '${inventory.backupRoot}'.`);
|
|
67
|
+
}
|
|
68
|
+
await rm(artifact.absolutePath, { recursive: true, force: false });
|
|
69
|
+
return {
|
|
70
|
+
...inventory,
|
|
71
|
+
targetPath: artifact.path,
|
|
72
|
+
deleted: true,
|
|
73
|
+
existedBefore: true,
|
|
74
|
+
deletedArtifact: artifact,
|
|
75
|
+
missingOk: options.missingOk ?? false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export async function cleanupManagedBackups(context, options = {}) {
|
|
79
|
+
if (options.olderThanDays === undefined && options.keepLatest === undefined) {
|
|
80
|
+
throw new Error("cleanup_backups requires olderThanDays, keepLatest, or both.");
|
|
81
|
+
}
|
|
82
|
+
const inventory = await listManagedBackups(context, {
|
|
83
|
+
rootPath: options.rootPath,
|
|
84
|
+
});
|
|
85
|
+
const categories = new Set(normalizeBackupArtifactCategoryInputs(options.categories));
|
|
86
|
+
const eligibleArtifacts = inventory.artifacts.filter((artifact) => categories.has(artifact.category));
|
|
87
|
+
const keepLatest = options.keepLatest ? Math.max(options.keepLatest, 0) : 0;
|
|
88
|
+
const thresholdMs = options.olderThanDays !== undefined
|
|
89
|
+
? Date.now() - options.olderThanDays * 24 * 60 * 60 * 1000
|
|
90
|
+
: null;
|
|
91
|
+
const retainedPaths = new Set(eligibleArtifacts.slice(0, keepLatest).map((artifact) => artifact.path));
|
|
92
|
+
const cleanupCandidates = eligibleArtifacts.filter((artifact) => {
|
|
93
|
+
if (retainedPaths.has(artifact.path)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
if (thresholdMs === null) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return Date.parse(artifact.createdAt) < thresholdMs;
|
|
100
|
+
});
|
|
101
|
+
if (!options.dryRun) {
|
|
102
|
+
for (const artifact of cleanupCandidates) {
|
|
103
|
+
await rm(artifact.absolutePath, { recursive: true, force: false });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
...inventory,
|
|
108
|
+
categories: Array.from(categories),
|
|
109
|
+
requestedCategories: options.categories || null,
|
|
110
|
+
olderThanDays: options.olderThanDays ?? null,
|
|
111
|
+
keepLatest: options.keepLatest ?? null,
|
|
112
|
+
dryRun: options.dryRun ?? false,
|
|
113
|
+
deletedCount: options.dryRun ? 0 : cleanupCandidates.length,
|
|
114
|
+
reclaimedBytesEstimate: cleanupCandidates.reduce((total, artifact) => total + artifact.sizeBytes, 0),
|
|
115
|
+
deletedArtifacts: options.dryRun ? [] : cleanupCandidates,
|
|
116
|
+
candidateArtifacts: cleanupCandidates,
|
|
117
|
+
retainedArtifacts: eligibleArtifacts.filter((artifact) => retainedPaths.has(artifact.path)),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export function normalizeBackupArtifactCategoryInputs(categories) {
|
|
121
|
+
if (!categories || categories.length === 0) {
|
|
122
|
+
return [...backupArtifactCategories];
|
|
123
|
+
}
|
|
124
|
+
const normalized = new Set();
|
|
125
|
+
for (const rawCategory of categories) {
|
|
126
|
+
const category = rawCategory.trim().toLowerCase();
|
|
127
|
+
if (!category) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (isBackupArtifactCategory(category)) {
|
|
131
|
+
normalized.add(category);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const aliasTargets = backupArtifactCategoryAliases[category];
|
|
135
|
+
if (aliasTargets) {
|
|
136
|
+
for (const aliasTarget of aliasTargets) {
|
|
137
|
+
normalized.add(aliasTarget);
|
|
138
|
+
}
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
throw new Error(`Unknown backup category '${rawCategory}'. Accepted values: ${acceptedBackupArtifactCategoryInputs.join(", ")}.`);
|
|
142
|
+
}
|
|
143
|
+
if (normalized.size === 0) {
|
|
144
|
+
return [...backupArtifactCategories];
|
|
145
|
+
}
|
|
146
|
+
return [...normalized];
|
|
147
|
+
}
|
|
148
|
+
export function resolveManagedBackupRoot(context, rootPath) {
|
|
149
|
+
if (rootPath) {
|
|
150
|
+
return path.resolve(rootPath);
|
|
151
|
+
}
|
|
152
|
+
if (config.backupsDirOverride) {
|
|
153
|
+
return path.resolve(config.backupsDirOverride);
|
|
154
|
+
}
|
|
155
|
+
return path.join(context.site.absolutePath, "localwp-mcp-backups");
|
|
156
|
+
}
|
|
157
|
+
async function discoverBackupArtifacts(backupRoot) {
|
|
158
|
+
const entries = await readdir(backupRoot, { withFileTypes: true });
|
|
159
|
+
const artifacts = [];
|
|
160
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
161
|
+
const absoluteEntryPath = path.join(backupRoot, entry.name);
|
|
162
|
+
if (entry.isDirectory()) {
|
|
163
|
+
const manifestPath = path.join(absoluteEntryPath, "manifest.json");
|
|
164
|
+
if (await isReadablePath(manifestPath)) {
|
|
165
|
+
artifacts.push(await buildBackupDirectoryArtifact(backupRoot, absoluteEntryPath));
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (entry.name === "database-exports" || entry.name === "pre-import") {
|
|
169
|
+
artifacts.push(...(await collectSqlFileArtifacts(backupRoot, absoluteEntryPath, entry.name === "pre-import" ? "pre_import_backup" : "database_export")));
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (entry.isFile() && path.extname(entry.name).toLowerCase() === ".sql") {
|
|
174
|
+
artifacts.push(await buildSqlFileArtifact(backupRoot, absoluteEntryPath, "sql_file"));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return artifacts.sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt) ||
|
|
178
|
+
left.path.localeCompare(right.path));
|
|
179
|
+
}
|
|
180
|
+
async function collectSqlFileArtifacts(backupRoot, directoryPath, category) {
|
|
181
|
+
const sqlArtifacts = [];
|
|
182
|
+
const entries = await readdir(directoryPath, { withFileTypes: true });
|
|
183
|
+
for (const entry of entries) {
|
|
184
|
+
const absoluteEntryPath = path.join(directoryPath, entry.name);
|
|
185
|
+
if (entry.isDirectory()) {
|
|
186
|
+
sqlArtifacts.push(...(await collectSqlFileArtifacts(backupRoot, absoluteEntryPath, category)));
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (entry.isFile() && path.extname(entry.name).toLowerCase() === ".sql") {
|
|
190
|
+
sqlArtifacts.push(await buildSqlFileArtifact(backupRoot, absoluteEntryPath, category));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return sqlArtifacts;
|
|
194
|
+
}
|
|
195
|
+
async function buildBackupDirectoryArtifact(backupRoot, absolutePath) {
|
|
196
|
+
const manifestPath = path.join(absolutePath, "manifest.json");
|
|
197
|
+
const manifest = await readManifest(manifestPath);
|
|
198
|
+
const stats = await lstat(absolutePath);
|
|
199
|
+
return {
|
|
200
|
+
path: normalizeBackupRelativePath(path.relative(backupRoot, absolutePath)),
|
|
201
|
+
absolutePath,
|
|
202
|
+
kind: "directory",
|
|
203
|
+
category: inferDirectoryCategory(absolutePath, manifest.scope),
|
|
204
|
+
createdAt: manifest.createdAt || stats.mtime.toISOString(),
|
|
205
|
+
modifiedAt: stats.mtime.toISOString(),
|
|
206
|
+
sizeBytes: await calculatePathSize(absolutePath),
|
|
207
|
+
manifestPath,
|
|
208
|
+
manifestCreatedAt: manifest.createdAt || null,
|
|
209
|
+
scope: manifest.scope || null,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
async function buildSqlFileArtifact(backupRoot, absolutePath, category) {
|
|
213
|
+
const stats = await lstat(absolutePath);
|
|
214
|
+
return {
|
|
215
|
+
path: normalizeBackupRelativePath(path.relative(backupRoot, absolutePath)),
|
|
216
|
+
absolutePath,
|
|
217
|
+
kind: "file",
|
|
218
|
+
category,
|
|
219
|
+
createdAt: stats.mtime.toISOString(),
|
|
220
|
+
modifiedAt: stats.mtime.toISOString(),
|
|
221
|
+
sizeBytes: stats.size,
|
|
222
|
+
manifestPath: null,
|
|
223
|
+
manifestCreatedAt: null,
|
|
224
|
+
scope: null,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
async function readManifest(manifestPath) {
|
|
228
|
+
const raw = await readFile(manifestPath, "utf8");
|
|
229
|
+
return JSON.parse(raw);
|
|
230
|
+
}
|
|
231
|
+
function inferDirectoryCategory(absolutePath, scope) {
|
|
232
|
+
const directoryName = path.basename(absolutePath).toLowerCase();
|
|
233
|
+
if (directoryName.includes("pre-restore")) {
|
|
234
|
+
return "pre_restore_backup";
|
|
235
|
+
}
|
|
236
|
+
if (scope === "full") {
|
|
237
|
+
return "full_backup";
|
|
238
|
+
}
|
|
239
|
+
return "database_backup";
|
|
240
|
+
}
|
|
241
|
+
async function calculatePathSize(absolutePath) {
|
|
242
|
+
const stats = await lstat(absolutePath);
|
|
243
|
+
if (!stats.isDirectory()) {
|
|
244
|
+
return stats.size;
|
|
245
|
+
}
|
|
246
|
+
const entries = await readdir(absolutePath, { withFileTypes: true });
|
|
247
|
+
let total = 0;
|
|
248
|
+
for (const entry of entries) {
|
|
249
|
+
total += await calculatePathSize(path.join(absolutePath, entry.name));
|
|
250
|
+
}
|
|
251
|
+
return total;
|
|
252
|
+
}
|
|
253
|
+
function normalizeBackupRelativePath(relativePath) {
|
|
254
|
+
return relativePath.replaceAll(path.sep, "/");
|
|
255
|
+
}
|
|
256
|
+
function isBackupArtifactCategory(value) {
|
|
257
|
+
return backupArtifactCategories.includes(value);
|
|
258
|
+
}
|
|
259
|
+
function buildBackupCategorySupport() {
|
|
260
|
+
return {
|
|
261
|
+
canonical: [...backupArtifactCategories],
|
|
262
|
+
aliases: backupArtifactCategoryAliases,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=backup-management.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup-management.js","sourceRoot":"","sources":["../src/backup-management.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOpD,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,aAAa;IACb,iBAAiB;IACjB,oBAAoB;IACpB,iBAAiB;IACjB,mBAAmB;IACnB,UAAU;CAC0C,CAAC;AAEvD,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,IAAI,EAAE,CAAC,aAAa,CAAC;IACrB,QAAQ,EAAE,CAAC,iBAAiB,CAAC;IAC7B,WAAW,EAAE,CAAC,oBAAoB,CAAC;IACnC,UAAU,EAAE,CAAC,mBAAmB,CAAC;IACjC,cAAc,EAAE,CAAC,iBAAiB,CAAC;IACnC,cAAc,EAAE,CAAC,UAAU,CAAC;IAC5B,MAAM,EAAE,CAAC,iBAAiB,EAAE,UAAU,CAAC;IACvC,OAAO,EAAE,CAAC,iBAAiB,EAAE,UAAU,CAAC;CAC4B,CAAC;AAEvE,MAAM,CAAC,MAAM,oCAAoC,GAAG;IAClD,GAAG,wBAAwB;IAC3B,GAAG,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC;CACrC,CAAC;AAqBX,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAoB,EACpB,UAEI,EAAE;IAEN,MAAM,UAAU,GAAG,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEvE,IAAI,CAAC,CAAC,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QACxC,OAAO;YACL,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC;YACjC,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,aAAa,EAAE,MAAM,CAAC,OAAO;YAC7B,UAAU;YACV,eAAe,EAAE,0BAA0B,EAAE;YAC7C,SAAS,EAAE,EAAE;SACd,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,uBAAuB,CAAC,UAAU,CAAC,CAAC;IAE5D,OAAO;QACL,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC;QACjC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,aAAa,EAAE,MAAM,CAAC,OAAO;QAC7B,UAAU;QACV,eAAe,EAAE,0BAA0B,EAAE;QAC7C,SAAS;KACV,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAoB,EACpB,OAIC;IAED,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE;QAClD,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAC;IACH,MAAM,oBAAoB,GAAG,2BAA2B,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7E,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,IAAI,CACvC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,oBAAoB,CACvD,CAAC;IAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,OAAO;gBACL,GAAG,SAAS;gBACZ,UAAU,EAAE,oBAAoB;gBAChC,OAAO,EAAE,KAAK;gBACd,aAAa,EAAE,KAAK;gBACpB,SAAS,EAAE,IAAI;aAChB,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,KAAK,CACb,uCAAuC,OAAO,CAAC,UAAU,YAAY,SAAS,CAAC,UAAU,IAAI,CAC9F,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAEnE,OAAO;QACL,GAAG,SAAS;QACZ,UAAU,EAAE,QAAQ,CAAC,IAAI;QACzB,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,IAAI;QACnB,eAAe,EAAE,QAAQ;QACzB,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAoB,EACpB,UAMI,EAAE;IAEN,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CACb,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE;QAClD,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,qCAAqC,CAAC,OAAO,CAAC,UAAU,CAAC,CAC1D,CAAC;IACF,MAAM,iBAAiB,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAChE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAClC,CAAC;IACF,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5E,MAAM,WAAW,GACf,OAAO,CAAC,aAAa,KAAK,SAAS;QACjC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;QAC1D,CAAC,CAAC,IAAI,CAAC;IAEX,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CACxE,CAAC;IACF,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE;QAC9D,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,WAAW,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,MAAM,QAAQ,IAAI,iBAAiB,EAAE,CAAC;YACzC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,OAAO;QACL,GAAG,SAAS;QACZ,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;QAClC,mBAAmB,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;QAC/C,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,IAAI;QAC5C,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;QACtC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;QAC/B,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM;QAC3D,sBAAsB,EAAE,iBAAiB,CAAC,MAAM,CAC9C,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,KAAK,GAAG,QAAQ,CAAC,SAAS,EAC/C,CAAC,CACF;QACD,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB;QACzD,kBAAkB,EAAE,iBAAiB;QACrC,iBAAiB,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CACvD,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CACjC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qCAAqC,CACnD,UAAgC;IAEhC,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3C,OAAO,CAAC,GAAG,wBAAwB,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;IAErD,KAAK,MAAM,WAAW,IAAI,UAAU,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAElD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,SAAS;QACX,CAAC;QAED,IAAI,wBAAwB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACzB,SAAS;QACX,CAAC;QAED,MAAM,YAAY,GAChB,6BAA6B,CAC3B,QAAsD,CACvD,CAAC;QAEJ,IAAI,YAAY,EAAE,CAAC;YACjB,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;gBACvC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC9B,CAAC;YACD,SAAS;QACX,CAAC;QAED,MAAM,IAAI,KAAK,CACb,4BAA4B,WAAW,uBAAuB,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACjH,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,wBAAwB,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,OAAoB,EACpB,QAAiB;IAEjB,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;AACrE,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,UAAkB;IACvD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,MAAM,SAAS,GAAqB,EAAE,CAAC;IAEvC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACvF,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE5D,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAAC;YAEnE,IAAI,MAAM,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC;gBACvC,SAAS,CAAC,IAAI,CAAC,MAAM,4BAA4B,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAAC;gBAClF,SAAS;YACX,CAAC;YAED,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBACrE,SAAS,CAAC,IAAI,CACZ,GAAG,CAAC,MAAM,uBAAuB,CAC/B,UAAU,EACV,iBAAiB,EACjB,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,CACtE,CAAC,CACH,CAAC;YACJ,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;YACxE,SAAS,CAAC,IAAI,CAAC,MAAM,oBAAoB,CAAC,UAAU,EAAE,iBAAiB,EAAE,UAAU,CAAC,CAAC,CAAC;QACxF,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC,IAAI,CACnB,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACd,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;QACxD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CACtC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,uBAAuB,CACpC,UAAkB,EAClB,aAAqB,EACrB,QAAgC;IAEhC,MAAM,YAAY,GAAqB,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE/D,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,uBAAuB,CAAC,UAAU,EAAE,iBAAiB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC/F,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;YACxE,YAAY,CAAC,IAAI,CACf,MAAM,oBAAoB,CAAC,UAAU,EAAE,iBAAiB,EAAE,QAAQ,CAAC,CACpE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,4BAA4B,CACzC,UAAkB,EAClB,YAAoB;IAEpB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,CAAC;IAExC,OAAO;QACL,IAAI,EAAE,2BAA2B,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAC1E,YAAY;QACZ,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,sBAAsB,CAAC,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC;QAC9D,SAAS,EAAE,QAAQ,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE;QAC1D,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE;QACrC,SAAS,EAAE,MAAM,iBAAiB,CAAC,YAAY,CAAC;QAChD,YAAY;QACZ,iBAAiB,EAAE,QAAQ,CAAC,SAAS,IAAI,IAAI;QAC7C,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,IAAI;KAC9B,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,UAAkB,EAClB,YAAoB,EACpB,QAAgC;IAEhC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,CAAC;IAExC,OAAO;QACL,IAAI,EAAE,2BAA2B,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAC1E,YAAY;QACZ,IAAI,EAAE,MAAM;QACZ,QAAQ;QACR,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE;QACpC,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE;QACrC,SAAS,EAAE,KAAK,CAAC,IAAI;QACrB,YAAY,EAAE,IAAI;QAClB,iBAAiB,EAAE,IAAI;QACvB,KAAK,EAAE,IAAI;KACZ,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,YAAoB;IAC9C,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAA0B,CAAC;AAClD,CAAC;AAED,SAAS,sBAAsB,CAC7B,YAAoB,EACpB,KAA8B;IAE9B,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IAEhE,IAAI,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1C,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAED,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACrB,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,YAAoB;IACnD,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,CAAC;IAExC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,KAAK,IAAI,MAAM,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,2BAA2B,CAAC,YAAoB;IACvD,OAAO,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAa;IAC7C,OAAO,wBAAwB,CAAC,QAAQ,CAAC,KAA+B,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,0BAA0B;IACjC,OAAO;QACL,SAAS,EAAE,CAAC,GAAG,wBAAwB,CAAC;QACxC,OAAO,EAAE,6BAA6B;KACvC,CAAC;AACJ,CAAC"}
|