@judaharagao/opencode-ssh 1.0.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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -0
  3. package/dist/config/defaults.d.ts +11 -0
  4. package/dist/config/defaults.d.ts.map +1 -0
  5. package/dist/config/defaults.js +23 -0
  6. package/dist/config/defaults.js.map +1 -0
  7. package/dist/config/schema.d.ts +51 -0
  8. package/dist/config/schema.d.ts.map +1 -0
  9. package/dist/config/schema.js +41 -0
  10. package/dist/config/schema.js.map +1 -0
  11. package/dist/display/formatter.d.ts +41 -0
  12. package/dist/display/formatter.d.ts.map +1 -0
  13. package/dist/display/formatter.js +179 -0
  14. package/dist/display/formatter.js.map +1 -0
  15. package/dist/display/stream.d.ts +38 -0
  16. package/dist/display/stream.d.ts.map +1 -0
  17. package/dist/display/stream.js +102 -0
  18. package/dist/display/stream.js.map +1 -0
  19. package/dist/hooks.d.ts +13 -0
  20. package/dist/hooks.d.ts.map +1 -0
  21. package/dist/hooks.js +151 -0
  22. package/dist/hooks.js.map +1 -0
  23. package/dist/index.d.ts +7 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +19 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/security/allowlist.d.ts +23 -0
  28. package/dist/security/allowlist.d.ts.map +1 -0
  29. package/dist/security/allowlist.js +105 -0
  30. package/dist/security/allowlist.js.map +1 -0
  31. package/dist/security/blocklist.d.ts +35 -0
  32. package/dist/security/blocklist.d.ts.map +1 -0
  33. package/dist/security/blocklist.js +351 -0
  34. package/dist/security/blocklist.js.map +1 -0
  35. package/dist/security/policy.d.ts +40 -0
  36. package/dist/security/policy.d.ts.map +1 -0
  37. package/dist/security/policy.js +133 -0
  38. package/dist/security/policy.js.map +1 -0
  39. package/dist/security/validator.d.ts +32 -0
  40. package/dist/security/validator.d.ts.map +1 -0
  41. package/dist/security/validator.js +152 -0
  42. package/dist/security/validator.js.map +1 -0
  43. package/dist/ssh/audit.d.ts +47 -0
  44. package/dist/ssh/audit.d.ts.map +1 -0
  45. package/dist/ssh/audit.js +126 -0
  46. package/dist/ssh/audit.js.map +1 -0
  47. package/dist/ssh/connection.d.ts +73 -0
  48. package/dist/ssh/connection.d.ts.map +1 -0
  49. package/dist/ssh/connection.js +210 -0
  50. package/dist/ssh/connection.js.map +1 -0
  51. package/dist/ssh/executor.d.ts +49 -0
  52. package/dist/ssh/executor.d.ts.map +1 -0
  53. package/dist/ssh/executor.js +275 -0
  54. package/dist/ssh/executor.js.map +1 -0
  55. package/dist/ssh/manager.d.ts +43 -0
  56. package/dist/ssh/manager.d.ts.map +1 -0
  57. package/dist/ssh/manager.js +98 -0
  58. package/dist/ssh/manager.js.map +1 -0
  59. package/dist/ssh/sanitizer.d.ts +41 -0
  60. package/dist/ssh/sanitizer.d.ts.map +1 -0
  61. package/dist/ssh/sanitizer.js +108 -0
  62. package/dist/ssh/sanitizer.js.map +1 -0
  63. package/dist/tools.d.ts +7 -0
  64. package/dist/tools.d.ts.map +1 -0
  65. package/dist/tools.js +584 -0
  66. package/dist/tools.js.map +1 -0
  67. package/package.json +64 -0
package/dist/tools.js ADDED
@@ -0,0 +1,584 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { SshSessionManager } from "./ssh/manager.js";
3
+ import { executeCommand } from "./ssh/executor.js";
4
+ import { validateCommand } from "./security/validator.js";
5
+ import { getPolicy, addBlocklistPattern, removeBlocklistPattern, addAllowlistPattern, removeAllowlistPattern } from "./security/policy.js";
6
+ import { readAuditLog, formatAuditLog, getAuditStats } from "./ssh/audit.js";
7
+ import { sanitizeHost, sanitizeUsername, sanitizePath } from "./ssh/sanitizer.js";
8
+ import { formatExecResult, formatSessionList, formatConnectResult, formatDisconnectResult, formatCheckResult, formatSecurityPolicy, } from "./display/formatter.js";
9
+ // ── Shared session manager (one per plugin instance) ──
10
+ let sessionManager = null;
11
+ function getManager(maxSessions = 5) {
12
+ if (!sessionManager) {
13
+ sessionManager = new SshSessionManager({ maxSessions });
14
+ }
15
+ return sessionManager;
16
+ }
17
+ /**
18
+ * Create all SSH tools for the plugin.
19
+ */
20
+ export function createSshTools(mode = "full", maxSessions = 5, defaultTimeout = 30, extraBlocklist = []) {
21
+ return {
22
+ // ═══════════════════════════════════════════════════════════════
23
+ // ssh.connect — Establish SSH connection
24
+ // ═══════════════════════════════════════════════════════════════
25
+ "ssh.connect": tool({
26
+ description: "Establish an SSH connection to a remote server. Returns a session_id for use with other ssh.* tools. " +
27
+ "Supports password and key-based authentication. Credentials are never stored in logs or output.",
28
+ args: {
29
+ host: tool.schema.string().describe("Remote server hostname or IP address"),
30
+ port: tool.schema.number().optional().describe("SSH port (default: 22)"),
31
+ username: tool.schema.string().describe("SSH username"),
32
+ auth_method: tool.schema.enum(["password", "key"]).describe("Authentication method"),
33
+ password: tool.schema.string().optional().describe("Password (only for password auth)"),
34
+ key_path: tool.schema.string().optional().describe("Path to private key file (default: ~/.ssh/id_rsa)"),
35
+ passphrase: tool.schema.string().optional().describe("Passphrase for the private key"),
36
+ alias: tool.schema.string().optional().describe("Friendly name for this session"),
37
+ },
38
+ async execute(args, ctx) {
39
+ // ── Input validation ──
40
+ const hostCheck = sanitizeHost(args.host);
41
+ if (!hostCheck.clean) {
42
+ return `❌ Invalid host: ${hostCheck.reason}`;
43
+ }
44
+ const userCheck = sanitizeUsername(args.username);
45
+ if (!userCheck.clean) {
46
+ return `❌ Invalid username: ${userCheck.reason}`;
47
+ }
48
+ // ── Ask for permission ──
49
+ await ctx.ask({
50
+ permission: "ssh.connect",
51
+ patterns: [`${args.username}@${args.host}:${args.port || 22}`],
52
+ always: [`${args.username}@${args.host}:${args.port || 22}`],
53
+ metadata: {
54
+ host: args.host,
55
+ port: args.port || 22,
56
+ username: args.username,
57
+ auth_method: args.auth_method,
58
+ alias: args.alias,
59
+ },
60
+ });
61
+ // ── Connect ──
62
+ const manager = getManager(maxSessions);
63
+ const config = {
64
+ host: args.host,
65
+ port: args.port || 22,
66
+ username: args.username,
67
+ authMethod: args.auth_method,
68
+ password: args.password,
69
+ keyPath: args.key_path,
70
+ passphrase: args.passphrase,
71
+ alias: args.alias,
72
+ timeout: 10000,
73
+ };
74
+ try {
75
+ const sessionId = await manager.createSession(config);
76
+ return formatConnectResult(sessionId, args.host, args.port || 22, args.username, args.alias);
77
+ }
78
+ catch (error) {
79
+ const msg = error instanceof Error ? error.message : String(error);
80
+ return [
81
+ `## ❌ SSH Connection Failed`,
82
+ "",
83
+ `**Host:** ${args.username}@${args.host}:${args.port || 22}`,
84
+ `**Error:** ${msg}`,
85
+ "",
86
+ "Troubleshooting:",
87
+ "- Verify the host is reachable",
88
+ "- Check that the username is correct",
89
+ "- Ensure SSH is running on the remote host",
90
+ "- Verify your credentials or key file",
91
+ ].join("\n");
92
+ }
93
+ },
94
+ }),
95
+ // ═══════════════════════════════════════════════════════════════
96
+ // ssh.disconnect — Close SSH session
97
+ // ═══════════════════════════════════════════════════════════════
98
+ "ssh.disconnect": tool({
99
+ description: "Close an active SSH session and free resources.",
100
+ args: {
101
+ session_id: tool.schema.string().describe("Session ID or alias to disconnect"),
102
+ },
103
+ async execute(args, ctx) {
104
+ await ctx.ask({
105
+ permission: "ssh.disconnect",
106
+ patterns: [args.session_id],
107
+ always: [],
108
+ metadata: { session_id: args.session_id },
109
+ });
110
+ const manager = getManager(maxSessions);
111
+ const closed = await manager.closeSession(args.session_id);
112
+ if (!closed) {
113
+ return `Session \`${args.session_id}\` not found or already closed.`;
114
+ }
115
+ return formatDisconnectResult(args.session_id);
116
+ },
117
+ }),
118
+ // ═══════════════════════════════════════════════════════════════
119
+ // ssh.list_sessions — List active SSH sessions
120
+ // ═══════════════════════════════════════════════════════════════
121
+ "ssh.list_sessions": tool({
122
+ description: "List all active SSH sessions with their status, host, and uptime.",
123
+ args: {},
124
+ async execute(_args, _ctx) {
125
+ const manager = getManager(maxSessions);
126
+ const sessions = manager.listSessions();
127
+ return formatSessionList(sessions);
128
+ },
129
+ }),
130
+ // ═══════════════════════════════════════════════════════════════
131
+ // ssh.exec — Execute command on remote server
132
+ // ═══════════════════════════════════════════════════════════════
133
+ "ssh.exec": tool({
134
+ description: "Execute a command on a remote server via SSH. " +
135
+ "Destructive commands (rm -rf /, mkfs, etc.) are automatically blocked with no exceptions. " +
136
+ "Risky commands (DROP TABLE, sudo su, systemctl stop, etc.) require your explicit approval every time. " +
137
+ "All output is streamed and displayed in real-time.",
138
+ args: {
139
+ session_id: tool.schema.string().describe("Session ID or alias"),
140
+ command: tool.schema.string().describe("Command to execute on the remote server"),
141
+ cwd: tool.schema.string().optional().describe("Working directory for the command"),
142
+ timeout: tool.schema.number().optional().describe("Timeout in seconds (default: 30)"),
143
+ sudo: tool.schema.boolean().optional().describe("Run command with sudo"),
144
+ },
145
+ async execute(args, ctx) {
146
+ // ── Validate session ──
147
+ const manager = getManager(maxSessions);
148
+ const connection = manager.getSession(args.session_id);
149
+ if (!connection) {
150
+ return `❌ Session \`${args.session_id}\` not found. Use ssh.list_sessions to see active sessions.`;
151
+ }
152
+ if (!connection.connected) {
153
+ return `❌ Session \`${args.session_id}\` is disconnected. Reconnect with ssh.connect.`;
154
+ }
155
+ // ── Validate command against blocklist BEFORE asking for permission ──
156
+ const validation = validateCommand(args.command, mode, extraBlocklist);
157
+ // DESTRUCTIVE: 100% blocked, no permission prompt, no override
158
+ if (validation.level === "destructive") {
159
+ return [
160
+ "## 🚫 Command Blocked (Destructive)",
161
+ "",
162
+ `**Command:** \`${args.command}\``,
163
+ `**Reason:** ${validation.reason}`,
164
+ `**Rule:** ${validation.matched_rule}`,
165
+ "",
166
+ "This command is **permanently blocked** for safety.",
167
+ "Destructive commands cannot be executed under any circumstances.",
168
+ ].join("\n");
169
+ }
170
+ // NOT IN ALLOWLIST: blocked in restricted/read_only mode
171
+ if (validation.level === "not_in_allowlist") {
172
+ return [
173
+ "## 🔒 Command Not Allowed",
174
+ "",
175
+ `**Command:** \`${args.command}\``,
176
+ `**Reason:** ${validation.reason}`,
177
+ "",
178
+ "This command is not in the allowlist for the current security mode.",
179
+ "Use ssh.security_policy to view or modify the policy.",
180
+ ].join("\n");
181
+ }
182
+ // RISKY: Ask for permission EVERY TIME (no "always" pattern)
183
+ if (validation.level === "risky") {
184
+ await ctx.ask({
185
+ permission: "ssh.exec.risky",
186
+ patterns: [args.command],
187
+ always: [], // Empty = ask EVERY time
188
+ metadata: {
189
+ session_id: args.session_id,
190
+ command: args.command,
191
+ host: connection.config.host,
192
+ username: connection.config.username,
193
+ reason: validation.reason,
194
+ rule: validation.matched_rule,
195
+ },
196
+ });
197
+ }
198
+ else {
199
+ // Safe command: ask once with "always" pattern
200
+ await ctx.ask({
201
+ permission: "ssh.exec",
202
+ patterns: [args.command],
203
+ always: [args.command],
204
+ metadata: {
205
+ session_id: args.session_id,
206
+ command: args.command,
207
+ host: connection.config.host,
208
+ username: connection.config.username,
209
+ },
210
+ });
211
+ }
212
+ // ── Execute ──
213
+ const result = await executeCommand(connection, args.command, mode, extraBlocklist, args.session_id, {
214
+ cwd: args.cwd,
215
+ timeout: args.timeout || defaultTimeout,
216
+ sudo: args.sudo,
217
+ }, ctx.directory);
218
+ return formatExecResult(result);
219
+ },
220
+ }),
221
+ // ═══════════════════════════════════════════════════════════════
222
+ // ssh.exec_batch — Execute multiple commands in sequence
223
+ // ═══════════════════════════════════════════════════════════════
224
+ "ssh.exec_batch": tool({
225
+ description: "Execute multiple commands in sequence on a remote server. " +
226
+ "Each command is validated against the security policy. " +
227
+ "If stop_on_error is true, execution stops on the first failed command.",
228
+ args: {
229
+ session_id: tool.schema.string().describe("Session ID or alias"),
230
+ commands: tool.schema.string().describe("Commands separated by newlines"),
231
+ stop_on_error: tool.schema.boolean().optional().describe("Stop if a command fails (default: true)"),
232
+ cwd: tool.schema.string().optional().describe("Working directory"),
233
+ timeout: tool.schema.number().optional().describe("Timeout per command in seconds"),
234
+ },
235
+ async execute(args, ctx) {
236
+ const manager = getManager(maxSessions);
237
+ const connection = manager.getSession(args.session_id);
238
+ if (!connection) {
239
+ return `❌ Session \`${args.session_id}\` not found.`;
240
+ }
241
+ const commands = args.commands.split("\n").filter((c) => c.trim());
242
+ if (commands.length === 0) {
243
+ return "No commands to execute.";
244
+ }
245
+ // ── Validate all commands first ──
246
+ const validations = [];
247
+ for (const cmd of commands) {
248
+ const v = validateCommand(cmd.trim(), mode, extraBlocklist);
249
+ validations.push({ command: cmd.trim(), validation: v });
250
+ }
251
+ // Check for destructive commands
252
+ const destructive = validations.filter((v) => v.validation.level === "destructive");
253
+ if (destructive.length > 0) {
254
+ const lines = ["## 🚫 Batch Blocked (Destructive Commands Found)", ""];
255
+ for (const d of destructive) {
256
+ lines.push(`- \`${d.command}\`: ${d.validation.reason}`);
257
+ }
258
+ lines.push("");
259
+ lines.push("Destructive commands cannot be executed. Remove them and try again.");
260
+ return lines.join("\n");
261
+ }
262
+ // Check for risky commands and ask permission
263
+ const risky = validations.filter((v) => v.validation.level === "risky");
264
+ if (risky.length > 0) {
265
+ await ctx.ask({
266
+ permission: "ssh.exec_batch.risky",
267
+ patterns: risky.map((r) => r.command),
268
+ always: [], // Ask EVERY time for risky batch commands
269
+ metadata: {
270
+ session_id: args.session_id,
271
+ commands: risky.map((r) => ({
272
+ command: r.command,
273
+ reason: r.validation.reason,
274
+ rule: r.validation.matched_rule,
275
+ })),
276
+ host: connection.config.host,
277
+ },
278
+ });
279
+ }
280
+ else {
281
+ // Safe batch
282
+ await ctx.ask({
283
+ permission: "ssh.exec_batch",
284
+ patterns: commands.map((c) => c.trim()),
285
+ always: [],
286
+ metadata: {
287
+ session_id: args.session_id,
288
+ command_count: commands.length,
289
+ host: connection.config.host,
290
+ },
291
+ });
292
+ }
293
+ // ── Execute commands sequentially ──
294
+ const results = [];
295
+ const stopOnError = args.stop_on_error !== false;
296
+ for (const cmd of commands) {
297
+ const result = await executeCommand(connection, cmd.trim(), mode, extraBlocklist, args.session_id, {
298
+ cwd: args.cwd,
299
+ timeout: args.timeout || defaultTimeout,
300
+ }, ctx.directory);
301
+ results.push(result);
302
+ if (stopOnError && result.exitCode !== 0) {
303
+ break;
304
+ }
305
+ }
306
+ // ── Format batch results ──
307
+ const lines = [`## SSH Batch Results (${results.length}/${commands.length} executed)\n`];
308
+ for (let i = 0; i < results.length; i++) {
309
+ const r = results[i];
310
+ const icon = r.exitCode === 0 ? "✅" : "❌";
311
+ lines.push(`${icon} **${i + 1}.** \`${r.command}\` — exit: ${r.exitCode}`);
312
+ if (r.stdout.trim()) {
313
+ lines.push(` ${r.stdout.trim().split("\n")[0]}`);
314
+ }
315
+ if (r.stderr.trim()) {
316
+ lines.push(` stderr: ${r.stderr.trim().split("\n")[0]}`);
317
+ }
318
+ lines.push("");
319
+ }
320
+ return lines.join("\n");
321
+ },
322
+ }),
323
+ // ═══════════════════════════════════════════════════════════════
324
+ // ssh.upload — Upload file via SCP
325
+ // ═══════════════════════════════════════════════════════════════
326
+ "ssh.upload": tool({
327
+ description: "Upload a local file to the remote server via SCP/SFTP.",
328
+ args: {
329
+ session_id: tool.schema.string().describe("Session ID or alias"),
330
+ local_path: tool.schema.string().describe("Local file path"),
331
+ remote_path: tool.schema.string().describe("Remote destination path"),
332
+ },
333
+ async execute(args, ctx) {
334
+ const manager = getManager(maxSessions);
335
+ const connection = manager.getSession(args.session_id);
336
+ if (!connection) {
337
+ return `❌ Session \`${args.session_id}\` not found.`;
338
+ }
339
+ // Validate paths
340
+ const pathCheck = sanitizePath(args.local_path);
341
+ if (!pathCheck.clean)
342
+ return `❌ Invalid local path: ${pathCheck.reason}`;
343
+ const remoteCheck = sanitizePath(args.remote_path);
344
+ if (!remoteCheck.clean)
345
+ return `❌ Invalid remote path: ${remoteCheck.reason}`;
346
+ await ctx.ask({
347
+ permission: "ssh.upload",
348
+ patterns: [args.local_path, args.remote_path],
349
+ always: [],
350
+ metadata: {
351
+ session_id: args.session_id,
352
+ local_path: args.local_path,
353
+ remote_path: args.remote_path,
354
+ host: connection.config.host,
355
+ },
356
+ });
357
+ try {
358
+ const sftp = await connection.sftp();
359
+ await new Promise((resolve, reject) => {
360
+ sftp.fastPut(args.local_path, args.remote_path, (err) => {
361
+ if (err)
362
+ reject(err);
363
+ else
364
+ resolve();
365
+ });
366
+ });
367
+ return [
368
+ "## 📤 File Uploaded",
369
+ "",
370
+ `- **Local:** \`${args.local_path}\``,
371
+ `- **Remote:** \`${args.remote_path}\``,
372
+ `- **Session:** \`${args.session_id}\``,
373
+ ].join("\n");
374
+ }
375
+ catch (error) {
376
+ const msg = error instanceof Error ? error.message : String(error);
377
+ return `❌ Upload failed: ${msg}`;
378
+ }
379
+ },
380
+ }),
381
+ // ═══════════════════════════════════════════════════════════════
382
+ // ssh.download — Download file via SCP
383
+ // ═══════════════════════════════════════════════════════════════
384
+ "ssh.download": tool({
385
+ description: "Download a file from the remote server to local via SCP/SFTP.",
386
+ args: {
387
+ session_id: tool.schema.string().describe("Session ID or alias"),
388
+ remote_path: tool.schema.string().describe("Remote file path"),
389
+ local_path: tool.schema.string().describe("Local destination path"),
390
+ },
391
+ async execute(args, ctx) {
392
+ const manager = getManager(maxSessions);
393
+ const connection = manager.getSession(args.session_id);
394
+ if (!connection) {
395
+ return `❌ Session \`${args.session_id}\` not found.`;
396
+ }
397
+ const pathCheck = sanitizePath(args.local_path);
398
+ if (!pathCheck.clean)
399
+ return `❌ Invalid local path: ${pathCheck.reason}`;
400
+ const remoteCheck = sanitizePath(args.remote_path);
401
+ if (!remoteCheck.clean)
402
+ return `❌ Invalid remote path: ${remoteCheck.reason}`;
403
+ await ctx.ask({
404
+ permission: "ssh.download",
405
+ patterns: [args.remote_path, args.local_path],
406
+ always: [],
407
+ metadata: {
408
+ session_id: args.session_id,
409
+ remote_path: args.remote_path,
410
+ local_path: args.local_path,
411
+ host: connection.config.host,
412
+ },
413
+ });
414
+ try {
415
+ const sftp = await connection.sftp();
416
+ await new Promise((resolve, reject) => {
417
+ sftp.fastGet(args.remote_path, args.local_path, (err) => {
418
+ if (err)
419
+ reject(err);
420
+ else
421
+ resolve();
422
+ });
423
+ });
424
+ return [
425
+ "## 📥 File Downloaded",
426
+ "",
427
+ `- **Remote:** \`${args.remote_path}\``,
428
+ `- **Local:** \`${args.local_path}\``,
429
+ `- **Session:** \`${args.session_id}\``,
430
+ ].join("\n");
431
+ }
432
+ catch (error) {
433
+ const msg = error instanceof Error ? error.message : String(error);
434
+ return `❌ Download failed: ${msg}`;
435
+ }
436
+ },
437
+ }),
438
+ // ═══════════════════════════════════════════════════════════════
439
+ // ssh.check_command — Validate command safety (dry-run)
440
+ // ═══════════════════════════════════════════════════════════════
441
+ "ssh.check_command": tool({
442
+ description: "Check if a command is safe to execute without actually running it. " +
443
+ "Returns the safety level, any matched blocklist rules, and suggestions. " +
444
+ "Use this to preview command safety before execution.",
445
+ args: {
446
+ command: tool.schema.string().describe("Command to validate"),
447
+ },
448
+ async execute(args, _ctx) {
449
+ const validation = validateCommand(args.command, mode, extraBlocklist);
450
+ return formatCheckResult(args.command, validation);
451
+ },
452
+ }),
453
+ // ═══════════════════════════════════════════════════════════════
454
+ // ssh.security_policy — View/manage security policy
455
+ // ═══════════════════════════════════════════════════════════════
456
+ "ssh.security_policy": tool({
457
+ description: "View or modify the SSH security policy. " +
458
+ "Can add/remove patterns from the blocklist or allowlist, " +
459
+ "and view the current security mode.",
460
+ args: {
461
+ action: tool.schema
462
+ .enum(["view", "add_blocklist", "remove_blocklist", "add_allowlist", "remove_allowlist"])
463
+ .describe("Action to perform"),
464
+ pattern: tool.schema.string().optional().describe("Regex pattern to add/remove"),
465
+ },
466
+ async execute(args, ctx) {
467
+ if (!args.pattern && args.action !== "view") {
468
+ return "❌ A pattern is required for add/remove actions.";
469
+ }
470
+ if (args.pattern) {
471
+ // Validate regex
472
+ try {
473
+ new RegExp(args.pattern);
474
+ }
475
+ catch {
476
+ return `❌ Invalid regex pattern: \`${args.pattern}\``;
477
+ }
478
+ }
479
+ await ctx.ask({
480
+ permission: "ssh.security_policy",
481
+ patterns: [`${args.action}: ${args.pattern || ""}`],
482
+ always: [],
483
+ metadata: {
484
+ action: args.action,
485
+ pattern: args.pattern,
486
+ },
487
+ });
488
+ switch (args.action) {
489
+ case "view": {
490
+ const policy = getPolicy(ctx.directory, mode);
491
+ return formatSecurityPolicy(policy);
492
+ }
493
+ case "add_blocklist": {
494
+ const policy = addBlocklistPattern(ctx.directory, args.pattern);
495
+ return [
496
+ "## ✅ Blocklist Pattern Added",
497
+ "",
498
+ `Added: \`${args.pattern}\``,
499
+ "",
500
+ formatSecurityPolicy(policy),
501
+ ].join("\n");
502
+ }
503
+ case "remove_blocklist": {
504
+ const policy = removeBlocklistPattern(ctx.directory, args.pattern);
505
+ return [
506
+ "## ✅ Blocklist Pattern Removed",
507
+ "",
508
+ `Removed: \`${args.pattern}\``,
509
+ "",
510
+ formatSecurityPolicy(policy),
511
+ ].join("\n");
512
+ }
513
+ case "add_allowlist": {
514
+ const policy = addAllowlistPattern(ctx.directory, args.pattern);
515
+ return [
516
+ "## ✅ Allowlist Pattern Added",
517
+ "",
518
+ `Added: \`${args.pattern}\``,
519
+ "",
520
+ formatSecurityPolicy(policy),
521
+ ].join("\n");
522
+ }
523
+ case "remove_allowlist": {
524
+ const policy = removeAllowlistPattern(ctx.directory, args.pattern);
525
+ return [
526
+ "## ✅ Allowlist Pattern Removed",
527
+ "",
528
+ `Removed: \`${args.pattern}\``,
529
+ "",
530
+ formatSecurityPolicy(policy),
531
+ ].join("\n");
532
+ }
533
+ default:
534
+ return `❌ Unknown action: ${args.action}`;
535
+ }
536
+ },
537
+ }),
538
+ // ═══════════════════════════════════════════════════════════════
539
+ // ssh.audit_log — View command audit trail
540
+ // ═══════════════════════════════════════════════════════════════
541
+ "ssh.audit_log": tool({
542
+ description: "View the SSH command audit log. Shows all executed, blocked, and approved commands " +
543
+ "with timestamps, results, and details. Supports filtering by session and command.",
544
+ args: {
545
+ limit: tool.schema.number().optional().describe("Maximum entries to show (default: 20)"),
546
+ session_id: tool.schema.string().optional().describe("Filter by session ID"),
547
+ command_filter: tool.schema.string().optional().describe("Filter by command text"),
548
+ },
549
+ async execute(args, ctx) {
550
+ const entries = readAuditLog(ctx.directory, {
551
+ limit: args.limit || 20,
552
+ sessionId: args.session_id,
553
+ commandFilter: args.command_filter,
554
+ });
555
+ if (entries.length === 0) {
556
+ // Show stats even if no entries match
557
+ const stats = getAuditStats(ctx.directory);
558
+ if (stats.total === 0) {
559
+ return "📋 No audit entries yet. Commands executed via ssh.exec will be logged here.";
560
+ }
561
+ return [
562
+ `## SSH Audit Stats`,
563
+ "",
564
+ `- **Total:** ${stats.total}`,
565
+ `- **Success:** ${stats.success}`,
566
+ `- **Blocked:** ${stats.blocked}`,
567
+ `- **Approved (risky):** ${stats.approved}`,
568
+ `- **Errors:** ${stats.error}`,
569
+ "",
570
+ "No entries match the current filters.",
571
+ ].join("\n");
572
+ }
573
+ // Show stats header
574
+ const stats = getAuditStats(ctx.directory);
575
+ const header = [
576
+ `### Stats: ${stats.total} total | ${stats.success} ✅ | ${stats.blocked} 🚫 | ${stats.approved} ⚠️ | ${stats.error} ❌`,
577
+ "",
578
+ ].join("\n");
579
+ return header + formatAuditLog(entries);
580
+ },
581
+ }),
582
+ };
583
+ }
584
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAuB,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAmB,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,eAAe,EAA0B,MAAM,yBAAyB,CAAA;AACjF,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,sBAAsB,EAAgB,MAAM,sBAAsB,CAAA;AACxJ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC5E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,wBAAwB,CAAA;AAI/B,yDAAyD;AACzD,IAAI,cAAc,GAA6B,IAAI,CAAA;AAEnD,SAAS,UAAU,CAAC,cAAsB,CAAC;IACzC,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,cAAc,GAAG,IAAI,iBAAiB,CAAC,EAAE,WAAW,EAAE,CAAC,CAAA;IACzD,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAqB,MAAM,EAC3B,cAAsB,CAAC,EACvB,iBAAyB,EAAE,EAC3B,iBAA2B,EAAE;IAE7B,OAAO;QACL,kEAAkE;QAClE,yCAAyC;QACzC,kEAAkE;QAClE,aAAa,EAAE,IAAI,CAAC;YAClB,WAAW,EACT,uGAAuG;gBACvG,iGAAiG;YACnG,IAAI,EAAE;gBACJ,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;gBAC3E,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;gBACxE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;gBACvD,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC;gBACpF,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;gBACvF,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;gBACvG,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;gBACtF,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;aAClF;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBACrB,yBAAyB;gBACzB,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACzC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;oBACrB,OAAO,mBAAmB,SAAS,CAAC,MAAM,EAAE,CAAA;gBAC9C,CAAC;gBAED,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBACjD,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;oBACrB,OAAO,uBAAuB,SAAS,CAAC,MAAM,EAAE,CAAA;gBAClD,CAAC;gBAED,2BAA2B;gBAC3B,MAAM,GAAG,CAAC,GAAG,CAAC;oBACZ,UAAU,EAAE,aAAa;oBACzB,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;oBAC9D,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;oBAC5D,QAAQ,EAAE;wBACR,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;wBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,WAAW,EAAE,IAAI,CAAC,WAAW;wBAC7B,KAAK,EAAE,IAAI,CAAC,KAAK;qBAClB;iBACF,CAAC,CAAA;gBAEF,gBAAgB;gBAChB,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;gBACvC,MAAM,MAAM,GAAwB;oBAClC,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;oBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,UAAU,EAAE,IAAI,CAAC,WAAW;oBAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,OAAO,EAAE,IAAI,CAAC,QAAQ;oBACtB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,OAAO,EAAE,KAAK;iBACf,CAAA;gBAED,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;oBACrD,OAAO,mBAAmB,CACxB,SAAS,EACT,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,IAAI,IAAI,EAAE,EACf,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,KAAK,CACX,CAAA;gBACH,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;oBAClE,OAAO;wBACL,4BAA4B;wBAC5B,EAAE;wBACF,aAAa,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE;wBAC5D,cAAc,GAAG,EAAE;wBACnB,EAAE;wBACF,kBAAkB;wBAClB,gCAAgC;wBAChC,sCAAsC;wBACtC,4CAA4C;wBAC5C,uCAAuC;qBACxC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACd,CAAC;YACH,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,qCAAqC;QACrC,kEAAkE;QAClE,gBAAgB,EAAE,IAAI,CAAC;YACrB,WAAW,EAAE,iDAAiD;YAC9D,IAAI,EAAE;gBACJ,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;aAC/E;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBACrB,MAAM,GAAG,CAAC,GAAG,CAAC;oBACZ,UAAU,EAAE,gBAAgB;oBAC5B,QAAQ,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC;oBAC3B,MAAM,EAAE,EAAE;oBACV,QAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;iBAC1C,CAAC,CAAA;gBAEF,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;gBACvC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBAE1D,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,OAAO,aAAa,IAAI,CAAC,UAAU,iCAAiC,CAAA;gBACtE,CAAC;gBAED,OAAO,sBAAsB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YAChD,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,+CAA+C;QAC/C,kEAAkE;QAClE,mBAAmB,EAAE,IAAI,CAAC;YACxB,WAAW,EAAE,mEAAmE;YAChF,IAAI,EAAE,EAAE;YACR,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI;gBACvB,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;gBACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE,CAAA;gBACvC,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAA;YACpC,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,8CAA8C;QAC9C,kEAAkE;QAClE,UAAU,EAAE,IAAI,CAAC;YACf,WAAW,EACT,gDAAgD;gBAChD,4FAA4F;gBAC5F,wGAAwG;gBACxG,oDAAoD;YACtD,IAAI,EAAE;gBACJ,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;gBAChE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;gBACjF,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;gBAClF,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;gBACrF,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;aACzE;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBACrB,yBAAyB;gBACzB,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;gBACvC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBACtD,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,OAAO,eAAe,IAAI,CAAC,UAAU,6DAA6D,CAAA;gBACpG,CAAC;gBAED,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;oBAC1B,OAAO,eAAe,IAAI,CAAC,UAAU,iDAAiD,CAAA;gBACxF,CAAC;gBAED,wEAAwE;gBACxE,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;gBAEtE,+DAA+D;gBAC/D,IAAI,UAAU,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;oBACvC,OAAO;wBACL,qCAAqC;wBACrC,EAAE;wBACF,kBAAkB,IAAI,CAAC,OAAO,IAAI;wBAClC,eAAe,UAAU,CAAC,MAAM,EAAE;wBAClC,aAAa,UAAU,CAAC,YAAY,EAAE;wBACtC,EAAE;wBACF,qDAAqD;wBACrD,kEAAkE;qBACnE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACd,CAAC;gBAED,yDAAyD;gBACzD,IAAI,UAAU,CAAC,KAAK,KAAK,kBAAkB,EAAE,CAAC;oBAC5C,OAAO;wBACL,2BAA2B;wBAC3B,EAAE;wBACF,kBAAkB,IAAI,CAAC,OAAO,IAAI;wBAClC,eAAe,UAAU,CAAC,MAAM,EAAE;wBAClC,EAAE;wBACF,qEAAqE;wBACrE,uDAAuD;qBACxD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACd,CAAC;gBAED,6DAA6D;gBAC7D,IAAI,UAAU,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;oBACjC,MAAM,GAAG,CAAC,GAAG,CAAC;wBACZ,UAAU,EAAE,gBAAgB;wBAC5B,QAAQ,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;wBACxB,MAAM,EAAE,EAAE,EAAE,yBAAyB;wBACrC,QAAQ,EAAE;4BACR,UAAU,EAAE,IAAI,CAAC,UAAU;4BAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;4BACrB,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI;4BAC5B,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,QAAQ;4BACpC,MAAM,EAAE,UAAU,CAAC,MAAM;4BACzB,IAAI,EAAE,UAAU,CAAC,YAAY;yBAC9B;qBACF,CAAC,CAAA;gBACJ,CAAC;qBAAM,CAAC;oBACN,+CAA+C;oBAC/C,MAAM,GAAG,CAAC,GAAG,CAAC;wBACZ,UAAU,EAAE,UAAU;wBACtB,QAAQ,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;wBACxB,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;wBACtB,QAAQ,EAAE;4BACR,UAAU,EAAE,IAAI,CAAC,UAAU;4BAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;4BACrB,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI;4BAC5B,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,QAAQ;yBACrC;qBACF,CAAC,CAAA;gBACJ,CAAC;gBAED,gBAAgB;gBAChB,MAAM,MAAM,GAAG,MAAM,cAAc,CACjC,UAAU,EACV,IAAI,CAAC,OAAO,EACZ,IAAI,EACJ,cAAc,EACd,IAAI,CAAC,UAAU,EACf;oBACE,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,cAAc;oBACvC,IAAI,EAAE,IAAI,CAAC,IAAI;iBAChB,EACD,GAAG,CAAC,SAAS,CACd,CAAA;gBAED,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAA;YACjC,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,yDAAyD;QACzD,kEAAkE;QAClE,gBAAgB,EAAE,IAAI,CAAC;YACrB,WAAW,EACT,4DAA4D;gBAC5D,yDAAyD;gBACzD,wEAAwE;YAC1E,IAAI,EAAE;gBACJ,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;gBAChE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;gBACzE,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;gBACnG,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;gBAClE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;aACpF;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBACrB,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;gBACvC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBACtD,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,OAAO,eAAe,IAAI,CAAC,UAAU,eAAe,CAAA;gBACtD,CAAC;gBAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;gBAClE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC1B,OAAO,yBAAyB,CAAA;gBAClC,CAAC;gBAED,oCAAoC;gBACpC,MAAM,WAAW,GAA+E,EAAE,CAAA;gBAClG,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;oBAC3B,MAAM,CAAC,GAAG,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;oBAC3D,WAAW,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAA;gBAC1D,CAAC;gBAED,iCAAiC;gBACjC,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,KAAK,aAAa,CAAC,CAAA;gBACnF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC3B,MAAM,KAAK,GAAG,CAAC,kDAAkD,EAAE,EAAE,CAAC,CAAA;oBACtE,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;wBAC5B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;oBAC1D,CAAC;oBACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;oBACd,KAAK,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAA;oBACjF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACzB,CAAC;gBAED,8CAA8C;gBAC9C,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,KAAK,OAAO,CAAC,CAAA;gBACvE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrB,MAAM,GAAG,CAAC,GAAG,CAAC;wBACZ,UAAU,EAAE,sBAAsB;wBAClC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;wBACrC,MAAM,EAAE,EAAE,EAAE,0CAA0C;wBACtD,QAAQ,EAAE;4BACR,UAAU,EAAE,IAAI,CAAC,UAAU;4BAC3B,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gCAC1B,OAAO,EAAE,CAAC,CAAC,OAAO;gCAClB,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM;gCAC3B,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,YAAY;6BAChC,CAAC,CAAC;4BACH,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI;yBAC7B;qBACF,CAAC,CAAA;gBACJ,CAAC;qBAAM,CAAC;oBACN,aAAa;oBACb,MAAM,GAAG,CAAC,GAAG,CAAC;wBACZ,UAAU,EAAE,gBAAgB;wBAC5B,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;wBACvC,MAAM,EAAE,EAAE;wBACV,QAAQ,EAAE;4BACR,UAAU,EAAE,IAAI,CAAC,UAAU;4BAC3B,aAAa,EAAE,QAAQ,CAAC,MAAM;4BAC9B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI;yBAC7B;qBACF,CAAC,CAAA;gBACJ,CAAC;gBAED,sCAAsC;gBACtC,MAAM,OAAO,GAAiB,EAAE,CAAA;gBAChC,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,KAAK,KAAK,CAAA;gBAEhD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;oBAC3B,MAAM,MAAM,GAAG,MAAM,cAAc,CACjC,UAAU,EACV,GAAG,CAAC,IAAI,EAAE,EACV,IAAI,EACJ,cAAc,EACd,IAAI,CAAC,UAAU,EACf;wBACE,GAAG,EAAE,IAAI,CAAC,GAAG;wBACb,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,cAAc;qBACxC,EACD,GAAG,CAAC,SAAS,CACd,CAAA;oBAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;oBAEpB,IAAI,WAAW,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;wBACzC,MAAK;oBACP,CAAC;gBACH,CAAC;gBAED,6BAA6B;gBAC7B,MAAM,KAAK,GAAG,CAAC,yBAAyB,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,cAAc,CAAC,CAAA;gBACxF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACxC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;oBACpB,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;oBACzC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAA;oBAC1E,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;wBACpB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;oBACpD,CAAC;oBACD,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;wBACpB,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;oBAC5D,CAAC;oBACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBAChB,CAAC;gBAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACzB,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,mCAAmC;QACnC,kEAAkE;QAClE,YAAY,EAAE,IAAI,CAAC;YACjB,WAAW,EAAE,wDAAwD;YACrE,IAAI,EAAE;gBACJ,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;gBAChE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;gBAC5D,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;aACtE;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBACrB,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;gBACvC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBACtD,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,OAAO,eAAe,IAAI,CAAC,UAAU,eAAe,CAAA;gBACtD,CAAC;gBAED,iBAAiB;gBACjB,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBAC/C,IAAI,CAAC,SAAS,CAAC,KAAK;oBAAE,OAAO,yBAAyB,SAAS,CAAC,MAAM,EAAE,CAAA;gBAExE,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;gBAClD,IAAI,CAAC,WAAW,CAAC,KAAK;oBAAE,OAAO,0BAA0B,WAAW,CAAC,MAAM,EAAE,CAAA;gBAE7E,MAAM,GAAG,CAAC,GAAG,CAAC;oBACZ,UAAU,EAAE,YAAY;oBACxB,QAAQ,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC;oBAC7C,MAAM,EAAE,EAAE;oBACV,QAAQ,EAAE;wBACR,UAAU,EAAE,IAAI,CAAC,UAAU;wBAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;wBAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;wBAC7B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI;qBAC7B;iBACF,CAAC,CAAA;gBAEF,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,CAAA;oBACpC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;wBAC1C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,GAAiB,EAAE,EAAE;4BACpE,IAAI,GAAG;gCAAE,MAAM,CAAC,GAAG,CAAC,CAAA;;gCACf,OAAO,EAAE,CAAA;wBAChB,CAAC,CAAC,CAAA;oBACJ,CAAC,CAAC,CAAA;oBAEF,OAAO;wBACL,qBAAqB;wBACrB,EAAE;wBACF,kBAAkB,IAAI,CAAC,UAAU,IAAI;wBACrC,mBAAmB,IAAI,CAAC,WAAW,IAAI;wBACvC,oBAAoB,IAAI,CAAC,UAAU,IAAI;qBACxC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACd,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;oBAClE,OAAO,oBAAoB,GAAG,EAAE,CAAA;gBAClC,CAAC;YACH,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,uCAAuC;QACvC,kEAAkE;QAClE,cAAc,EAAE,IAAI,CAAC;YACnB,WAAW,EAAE,+DAA+D;YAC5E,IAAI,EAAE;gBACJ,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;gBAChE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;gBAC9D,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;aACpE;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBACrB,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;gBACvC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBACtD,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,OAAO,eAAe,IAAI,CAAC,UAAU,eAAe,CAAA;gBACtD,CAAC;gBAED,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBAC/C,IAAI,CAAC,SAAS,CAAC,KAAK;oBAAE,OAAO,yBAAyB,SAAS,CAAC,MAAM,EAAE,CAAA;gBAExE,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;gBAClD,IAAI,CAAC,WAAW,CAAC,KAAK;oBAAE,OAAO,0BAA0B,WAAW,CAAC,MAAM,EAAE,CAAA;gBAE7E,MAAM,GAAG,CAAC,GAAG,CAAC;oBACZ,UAAU,EAAE,cAAc;oBAC1B,QAAQ,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC;oBAC7C,MAAM,EAAE,EAAE;oBACV,QAAQ,EAAE;wBACR,UAAU,EAAE,IAAI,CAAC,UAAU;wBAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;wBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;wBAC3B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI;qBAC7B;iBACF,CAAC,CAAA;gBAEF,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,CAAA;oBACpC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;wBAC1C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,GAAiB,EAAE,EAAE;4BACpE,IAAI,GAAG;gCAAE,MAAM,CAAC,GAAG,CAAC,CAAA;;gCACf,OAAO,EAAE,CAAA;wBAChB,CAAC,CAAC,CAAA;oBACJ,CAAC,CAAC,CAAA;oBAEF,OAAO;wBACL,uBAAuB;wBACvB,EAAE;wBACF,mBAAmB,IAAI,CAAC,WAAW,IAAI;wBACvC,kBAAkB,IAAI,CAAC,UAAU,IAAI;wBACrC,oBAAoB,IAAI,CAAC,UAAU,IAAI;qBACxC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACd,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;oBAClE,OAAO,sBAAsB,GAAG,EAAE,CAAA;gBACpC,CAAC;YACH,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,wDAAwD;QACxD,kEAAkE;QAClE,mBAAmB,EAAE,IAAI,CAAC;YACxB,WAAW,EACT,qEAAqE;gBACrE,0EAA0E;gBAC1E,sDAAsD;YACxD,IAAI,EAAE;gBACJ,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;aAC9D;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI;gBACtB,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;gBACtE,OAAO,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;YACpD,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,oDAAoD;QACpD,kEAAkE;QAClE,qBAAqB,EAAE,IAAI,CAAC;YAC1B,WAAW,EACT,0CAA0C;gBAC1C,2DAA2D;gBAC3D,qCAAqC;YACvC,IAAI,EAAE;gBACJ,MAAM,EAAE,IAAI,CAAC,MAAM;qBAChB,IAAI,CAAC,CAAC,MAAM,EAAE,eAAe,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC;qBACxF,QAAQ,CAAC,mBAAmB,CAAC;gBAChC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;aACjF;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBACrB,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;oBAC5C,OAAO,iDAAiD,CAAA;gBAC1D,CAAC;gBAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACjB,iBAAiB;oBACjB,IAAI,CAAC;wBACH,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;oBAC1B,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO,8BAA8B,IAAI,CAAC,OAAO,IAAI,CAAA;oBACvD,CAAC;gBACH,CAAC;gBAED,MAAM,GAAG,CAAC,GAAG,CAAC;oBACZ,UAAU,EAAE,qBAAqB;oBACjC,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;oBACnD,MAAM,EAAE,EAAE;oBACV,QAAQ,EAAE;wBACR,MAAM,EAAE,IAAI,CAAC,MAAM;wBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;qBACtB;iBACF,CAAC,CAAA;gBAEF,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;oBACpB,KAAK,MAAM,CAAC,CAAC,CAAC;wBACZ,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;wBAC7C,OAAO,oBAAoB,CAAC,MAAM,CAAC,CAAA;oBACrC,CAAC;oBACD,KAAK,eAAe,CAAC,CAAC,CAAC;wBACrB,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,OAAQ,CAAC,CAAA;wBAChE,OAAO;4BACL,8BAA8B;4BAC9B,EAAE;4BACF,YAAY,IAAI,CAAC,OAAO,IAAI;4BAC5B,EAAE;4BACF,oBAAoB,CAAC,MAAM,CAAC;yBAC7B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACd,CAAC;oBACD,KAAK,kBAAkB,CAAC,CAAC,CAAC;wBACxB,MAAM,MAAM,GAAG,sBAAsB,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,OAAQ,CAAC,CAAA;wBACnE,OAAO;4BACL,gCAAgC;4BAChC,EAAE;4BACF,cAAc,IAAI,CAAC,OAAO,IAAI;4BAC9B,EAAE;4BACF,oBAAoB,CAAC,MAAM,CAAC;yBAC7B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACd,CAAC;oBACD,KAAK,eAAe,CAAC,CAAC,CAAC;wBACrB,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,OAAQ,CAAC,CAAA;wBAChE,OAAO;4BACL,8BAA8B;4BAC9B,EAAE;4BACF,YAAY,IAAI,CAAC,OAAO,IAAI;4BAC5B,EAAE;4BACF,oBAAoB,CAAC,MAAM,CAAC;yBAC7B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACd,CAAC;oBACD,KAAK,kBAAkB,CAAC,CAAC,CAAC;wBACxB,MAAM,MAAM,GAAG,sBAAsB,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,OAAQ,CAAC,CAAA;wBACnE,OAAO;4BACL,gCAAgC;4BAChC,EAAE;4BACF,cAAc,IAAI,CAAC,OAAO,IAAI;4BAC9B,EAAE;4BACF,oBAAoB,CAAC,MAAM,CAAC;yBAC7B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACd,CAAC;oBACD;wBACE,OAAO,qBAAqB,IAAI,CAAC,MAAM,EAAE,CAAA;gBAC7C,CAAC;YACH,CAAC;SACF,CAAC;QAEF,kEAAkE;QAClE,2CAA2C;QAC3C,kEAAkE;QAClE,eAAe,EAAE,IAAI,CAAC;YACpB,WAAW,EACT,qFAAqF;gBACrF,mFAAmF;YACrF,IAAI,EAAE;gBACJ,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;gBACxF,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;gBAC5E,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;aACnF;YACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBACrB,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE;oBAC1C,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;oBACvB,SAAS,EAAE,IAAI,CAAC,UAAU;oBAC1B,aAAa,EAAE,IAAI,CAAC,cAAc;iBACnC,CAAC,CAAA;gBAEF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzB,sCAAsC;oBACtC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;oBAC1C,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;wBACtB,OAAO,8EAA8E,CAAA;oBACvF,CAAC;oBAED,OAAO;wBACL,oBAAoB;wBACpB,EAAE;wBACF,gBAAgB,KAAK,CAAC,KAAK,EAAE;wBAC7B,kBAAkB,KAAK,CAAC,OAAO,EAAE;wBACjC,kBAAkB,KAAK,CAAC,OAAO,EAAE;wBACjC,2BAA2B,KAAK,CAAC,QAAQ,EAAE;wBAC3C,iBAAiB,KAAK,CAAC,KAAK,EAAE;wBAC9B,EAAE;wBACF,uCAAuC;qBACxC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACd,CAAC;gBAED,oBAAoB;gBACpB,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;gBAC1C,MAAM,MAAM,GAAG;oBACb,cAAc,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,OAAO,QAAQ,KAAK,CAAC,OAAO,SAAS,KAAK,CAAC,QAAQ,SAAS,KAAK,CAAC,KAAK,IAAI;oBACtH,EAAE;iBACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAEZ,OAAO,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;YACzC,CAAC;SACF,CAAC;KACH,CAAA;AACH,CAAC"}