@codesys/mcp-toolkit 1.5.8 → 2.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 (110) hide show
  1. package/CHANGELOG.md +153 -189
  2. package/LICENSE +21 -21
  3. package/README.md +269 -152
  4. package/dist/actions.d.ts +28 -0
  5. package/dist/actions.js +98 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/bridge/bridge.d.ts +105 -0
  8. package/dist/bridge/bridge.js +400 -0
  9. package/dist/bridge/bridge.js.map +1 -0
  10. package/dist/cli.d.ts +2 -0
  11. package/dist/cli.js +196 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/codesys/cache.d.ts +48 -0
  14. package/dist/codesys/cache.js +82 -0
  15. package/dist/codesys/cache.js.map +1 -0
  16. package/dist/codesys/changes.d.ts +57 -0
  17. package/dist/codesys/changes.js +133 -0
  18. package/dist/codesys/changes.js.map +1 -0
  19. package/dist/codesys/decl.d.ts +45 -0
  20. package/dist/codesys/decl.js +251 -0
  21. package/dist/codesys/decl.js.map +1 -0
  22. package/dist/codesys/devices.d.ts +34 -0
  23. package/dist/codesys/devices.js +82 -0
  24. package/dist/codesys/devices.js.map +1 -0
  25. package/dist/codesys/diff.d.ts +13 -0
  26. package/dist/codesys/diff.js +61 -0
  27. package/dist/codesys/diff.js.map +1 -0
  28. package/dist/codesys/install.d.ts +40 -0
  29. package/dist/codesys/install.js +144 -0
  30. package/dist/codesys/install.js.map +1 -0
  31. package/dist/codesys/library-browse.d.ts +19 -0
  32. package/dist/codesys/library-browse.js +57 -0
  33. package/dist/codesys/library-browse.js.map +1 -0
  34. package/dist/codesys/outline.d.ts +32 -0
  35. package/dist/codesys/outline.js +219 -0
  36. package/dist/codesys/outline.js.map +1 -0
  37. package/dist/codesys/simulation.d.ts +34 -0
  38. package/dist/codesys/simulation.js +57 -0
  39. package/dist/codesys/simulation.js.map +1 -0
  40. package/dist/codesys/st.d.ts +36 -0
  41. package/dist/codesys/st.js +69 -0
  42. package/dist/codesys/st.js.map +1 -0
  43. package/dist/daemon.d.ts +46 -0
  44. package/dist/daemon.js +173 -0
  45. package/dist/daemon.js.map +1 -0
  46. package/dist/driver.d.ts +25 -0
  47. package/dist/driver.js +33 -0
  48. package/dist/driver.js.map +1 -0
  49. package/dist/panel/sim-panel.html +357 -0
  50. package/dist/panel/viewer.html +126 -0
  51. package/dist/panel-types.d.ts +20 -0
  52. package/dist/panel-types.js +3 -0
  53. package/dist/panel-types.js.map +1 -0
  54. package/dist/panel.d.ts +27 -0
  55. package/dist/panel.js +208 -0
  56. package/dist/panel.js.map +1 -0
  57. package/dist/server.d.ts +5 -0
  58. package/dist/server.js +33 -974
  59. package/dist/server.js.map +1 -0
  60. package/dist/tools.d.ts +103 -0
  61. package/dist/tools.js +793 -0
  62. package/dist/tools.js.map +1 -0
  63. package/dist/viewer.d.ts +14 -0
  64. package/dist/viewer.js +125 -0
  65. package/dist/viewer.js.map +1 -0
  66. package/package.json +66 -51
  67. package/watcher/mcp_compat.py +147 -0
  68. package/watcher/mcp_ops.py +1178 -0
  69. package/watcher/mcp_sim.py +246 -0
  70. package/watcher/watcher.py +255 -0
  71. package/.claude/settings.local.json +0 -29
  72. package/CLAUDE.md +0 -26
  73. package/CONTRIBUTING.md +0 -72
  74. package/README.md.enhanced +0 -155
  75. package/codesys-mcp-toolkit-1.2.0.tgz +0 -0
  76. package/codesys-mcp-toolkit-1.2.1.tgz +0 -0
  77. package/codesys-mcp-toolkit-1.5.7.tgz +0 -0
  78. package/codesys-mcp-toolkit-1.5.8.tgz +0 -0
  79. package/dist/bin.js +0 -75
  80. package/dist/check_session_paths.js +0 -149
  81. package/dist/codesys_interop.js +0 -435
  82. package/dist/direct_session_command.js +0 -293
  83. package/dist/scriptLoader.js +0 -165
  84. package/dist/scripts/check_status.py +0 -21
  85. package/dist/scripts/compile_project.py +0 -65
  86. package/dist/scripts/create_method.py +0 -80
  87. package/dist/scripts/create_pou.py +0 -102
  88. package/dist/scripts/create_project.py +0 -116
  89. package/dist/scripts/create_property.py +0 -78
  90. package/dist/scripts/ensure_project_open.py +0 -154
  91. package/dist/scripts/find_object_by_path.py +0 -111
  92. package/dist/scripts/get_pou_code.py +0 -91
  93. package/dist/scripts/get_project_structure.py +0 -72
  94. package/dist/scripts/open_project.py +0 -25
  95. package/dist/scripts/save_project.py +0 -29
  96. package/dist/scripts/session_status.py +0 -281
  97. package/dist/scripts/set_pou_code.py +0 -109
  98. package/dist/scripts/start_session.py +0 -809
  99. package/dist/scripts/stop_session.py +0 -324
  100. package/dist/sessionManager.js +0 -798
  101. package/docs/configuration.md +0 -141
  102. package/docs/index.md +0 -26
  103. package/docs/installation.md +0 -84
  104. package/docs/session_management.md +0 -110
  105. package/examples/README.md +0 -78
  106. package/examples/sample_config.json +0 -32
  107. package/replacements_session_status.txt +0 -16
  108. package/scriptDoc.txt +0 -8679
  109. package/scripts/build.js +0 -25
  110. package/scripts/fix_fstrings.py +0 -76
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulation.js","sourceRoot":"","sources":["../../src/codesys/simulation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /** Structured Text helpers: object kinds and declaration-header checks. */
2
+ export type ObjectKind = "pou" | "method" | "property" | "accessor" | "action" | "dut" | "gvl" | "interface" | "interface_method" | "task_config" | "task" | "task_call" | "library_manager" | "application" | "folder" | "device" | "other";
3
+ export type CreateKind = "program" | "function_block" | "function" | "method" | "property" | "action" | "interface" | "dut" | "gvl" | "folder";
4
+ export interface TreeNode {
5
+ path: string;
6
+ name: string;
7
+ kind: ObjectKind;
8
+ sub?: string;
9
+ st?: boolean;
10
+ depth: number;
11
+ declaration?: string;
12
+ implementation?: string;
13
+ /** Build properties that are set (excluded from build also when a folder excludes it). */
14
+ build?: {
15
+ excluded?: boolean;
16
+ link_always?: boolean;
17
+ external?: boolean;
18
+ };
19
+ /** PLCopenXML of a graphical body (st false), so a checkpoint can restore it. */
20
+ xml?: string;
21
+ }
22
+ export declare function stripLeading(text: string): string;
23
+ export declare function declarationHeader(decl: string): {
24
+ keyword: string;
25
+ name?: string;
26
+ } | undefined;
27
+ /**
28
+ * Check that a new declaration keeps the object's kind and name (issue #5:
29
+ * replacing a POU declaration without its `PROGRAM X` header silently
30
+ * changed the POU). Returns an error message, or undefined when it's fine.
31
+ */
32
+ export declare function checkDeclaration(kind: ObjectKind, sub: string | undefined, name: string, decl: string): string | undefined;
33
+ /** Normalise CODESYS text for display/comparison. */
34
+ export declare function normalizeText(text: string | undefined): string | undefined;
35
+ /** Human label for a node kind. */
36
+ export declare function kindLabel(n: Pick<TreeNode, "kind" | "sub">): string;
@@ -0,0 +1,69 @@
1
+ /** Structured Text helpers: object kinds and declaration-header checks. */
2
+ const LEAD = /^(?:\s|\{[^}]*\}|\/\/[^\n]*(?:\n|$)|\(\*[\s\S]*?\*\))*/;
3
+ const MODIFIERS = "(?:(?:PUBLIC|PRIVATE|PROTECTED|INTERNAL|ABSTRACT|FINAL)\\s+)*";
4
+ const HEADER = new RegExp(`^(PROGRAM|FUNCTION_BLOCK|FUNCTION|METHOD|PROPERTY|INTERFACE|TYPE|VAR_GLOBAL|VAR)\\b\\s*${MODIFIERS}([A-Za-z_][A-Za-z0-9_]*)?`, "i");
5
+ export function stripLeading(text) {
6
+ return text.replace(LEAD, "");
7
+ }
8
+ export function declarationHeader(decl) {
9
+ const m = HEADER.exec(stripLeading(decl));
10
+ if (!m)
11
+ return undefined;
12
+ const keyword = m[1].toUpperCase();
13
+ const name = keyword === "VAR" || keyword === "VAR_GLOBAL" ? undefined : m[2];
14
+ return { keyword, name };
15
+ }
16
+ const POU_KEYWORD = {
17
+ program: "PROGRAM",
18
+ function_block: "FUNCTION_BLOCK",
19
+ function: "FUNCTION",
20
+ };
21
+ /**
22
+ * Check that a new declaration keeps the object's kind and name (issue #5:
23
+ * replacing a POU declaration without its `PROGRAM X` header silently
24
+ * changed the POU). Returns an error message, or undefined when it's fine.
25
+ */
26
+ export function checkDeclaration(kind, sub, name, decl) {
27
+ const h = declarationHeader(decl);
28
+ const expect = (kw) => `The declaration of ${name} must start with '${kw} ${name}'` +
29
+ (h ? ` (found '${h.keyword}${h.name ? " " + h.name : ""}').` : " (found no header).");
30
+ const sameName = () => (h?.name ?? "").toLowerCase() === name.toLowerCase();
31
+ switch (kind) {
32
+ case "pou": {
33
+ const kw = sub ? POU_KEYWORD[sub] : undefined;
34
+ const allowed = kw ? [kw] : Object.values(POU_KEYWORD);
35
+ if (!h || !allowed.includes(h.keyword) || !sameName()) {
36
+ return expect(kw ?? "PROGRAM|FUNCTION_BLOCK|FUNCTION") + " To change a POU's type, create a new object.";
37
+ }
38
+ return undefined;
39
+ }
40
+ case "method":
41
+ case "interface_method":
42
+ return h?.keyword === "METHOD" && sameName() ? undefined : expect("METHOD");
43
+ case "property":
44
+ return h?.keyword === "PROPERTY" && sameName() ? undefined : expect("PROPERTY");
45
+ case "interface":
46
+ return h?.keyword === "INTERFACE" && sameName() ? undefined : expect("INTERFACE");
47
+ case "dut":
48
+ return h?.keyword === "TYPE" && sameName() ? undefined : expect("TYPE");
49
+ case "gvl":
50
+ return h?.keyword === "VAR_GLOBAL" ? undefined : `A GVL declaration must start with VAR_GLOBAL (found '${h?.keyword ?? "nothing"}').`;
51
+ case "accessor":
52
+ return !h || h.keyword === "VAR" ? undefined : `A property accessor declaration holds only VAR ... END_VAR (found '${h.keyword}').`;
53
+ default:
54
+ return undefined;
55
+ }
56
+ }
57
+ /** Normalise CODESYS text for display/comparison. */
58
+ export function normalizeText(text) {
59
+ return text?.replace(/\r\n?/g, "\n");
60
+ }
61
+ /** Human label for a node kind. */
62
+ export function kindLabel(n) {
63
+ if (n.kind === "pou")
64
+ return n.sub ?? "pou";
65
+ if (n.kind === "dut")
66
+ return n.sub ? `dut:${n.sub}` : "dut";
67
+ return n.kind;
68
+ }
69
+ //# sourceMappingURL=st.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"st.js","sourceRoot":"","sources":["../../src/codesys/st.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import net from "node:net";
2
+ export interface DaemonInfo {
3
+ pid: number;
4
+ port: number;
5
+ token: string;
6
+ key: string;
7
+ started: string;
8
+ }
9
+ export declare function sessionKey(parts: unknown): string;
10
+ export declare function daemonPaths(dir: string, key: string): {
11
+ info: string;
12
+ lock: string;
13
+ log: string;
14
+ };
15
+ /**
16
+ * Run the owner's listener. Each client that sends the token on its first line
17
+ * is handed to onConnection with the rest of its stream. onIdle runs once no
18
+ * client has been connected for idleMs.
19
+ */
20
+ export declare function serveDaemon(o: {
21
+ dir: string;
22
+ key: string;
23
+ idleMs: number;
24
+ onConnection: (socket: net.Socket) => Promise<void>;
25
+ onIdle: () => void;
26
+ log: (m: string) => void;
27
+ }): Promise<{
28
+ info: DaemonInfo;
29
+ close: () => void;
30
+ }>;
31
+ /**
32
+ * Connect to the owner of this session, starting it if there is none.
33
+ * Several processes may start at the same moment; a lock file makes exactly
34
+ * one of them spawn the owner, and the rest wait for it.
35
+ */
36
+ export declare function connectDaemon(o: {
37
+ dir: string;
38
+ key: string;
39
+ spawnDaemon: (logFile: string) => void;
40
+ waitMs?: number;
41
+ }): Promise<{
42
+ socket: net.Socket;
43
+ info: DaemonInfo;
44
+ spawned: boolean;
45
+ log: string;
46
+ }>;
package/dist/daemon.js ADDED
@@ -0,0 +1,173 @@
1
+ /**
2
+ * One shared CODESYS session per configuration.
3
+ *
4
+ * Claude Desktop can start the server several times (one process per chat
5
+ * surface). Each process used to launch its own CODESYS, so the panel card
6
+ * could talk to a process that never opened the project. Now the first
7
+ * process starts a background owner (the same CLI with --daemon) that runs
8
+ * CODESYS and the browser viewer; every stdio process just pipes its
9
+ * MCP traffic to the owner over a local socket. The owner stops itself when
10
+ * no client has been connected for a while.
11
+ *
12
+ * The owner listens on 127.0.0.1 only and requires a random token, which is
13
+ * in a file under the user's profile.
14
+ */
15
+ import { createHash, randomUUID } from "node:crypto";
16
+ import { mkdirSync, openSync, closeSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
17
+ import net from "node:net";
18
+ import { join } from "node:path";
19
+ export function sessionKey(parts) {
20
+ return createHash("sha256").update(JSON.stringify(parts)).digest("hex").slice(0, 12);
21
+ }
22
+ export function daemonPaths(dir, key) {
23
+ return { info: join(dir, `daemon-${key}.json`), lock: join(dir, `daemon-${key}.lock`), log: join(dir, `daemon-${key}.log`) };
24
+ }
25
+ function readInfo(file) {
26
+ try {
27
+ return JSON.parse(readFileSync(file, "utf8"));
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ function alive(pid) {
34
+ try {
35
+ process.kill(pid, 0);
36
+ return true;
37
+ }
38
+ catch (e) {
39
+ return e.code === "EPERM";
40
+ }
41
+ }
42
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
43
+ /**
44
+ * Run the owner's listener. Each client that sends the token on its first line
45
+ * is handed to onConnection with the rest of its stream. onIdle runs once no
46
+ * client has been connected for idleMs.
47
+ */
48
+ export async function serveDaemon(o) {
49
+ const token = randomUUID();
50
+ const paths = daemonPaths(o.dir, o.key);
51
+ let clients = 0;
52
+ let idle;
53
+ const armIdle = () => {
54
+ clearTimeout(idle);
55
+ if (clients === 0)
56
+ idle = setTimeout(o.onIdle, o.idleMs);
57
+ };
58
+ const server = net.createServer((socket) => {
59
+ socket.on("error", () => undefined);
60
+ let buf = Buffer.alloc(0);
61
+ const onData = (d) => {
62
+ buf = Buffer.concat([buf, d]);
63
+ const nl = buf.indexOf(10);
64
+ if (nl < 0) {
65
+ if (buf.length > 256)
66
+ socket.destroy();
67
+ return;
68
+ }
69
+ socket.off("data", onData);
70
+ socket.pause();
71
+ if (buf.subarray(0, nl).toString("utf8").trim() !== token) {
72
+ socket.destroy();
73
+ return;
74
+ }
75
+ const rest = buf.subarray(nl + 1);
76
+ if (rest.length)
77
+ socket.unshift(rest);
78
+ clients++;
79
+ clearTimeout(idle);
80
+ socket.on("close", () => {
81
+ clients--;
82
+ armIdle();
83
+ });
84
+ o.onConnection(socket)
85
+ .then(() => socket.resume())
86
+ .catch((e) => {
87
+ o.log(`client failed: ${e.message}`);
88
+ socket.destroy();
89
+ });
90
+ };
91
+ socket.on("data", onData);
92
+ });
93
+ await new Promise((res, rej) => {
94
+ server.once("error", rej);
95
+ server.listen(0, "127.0.0.1", () => res());
96
+ });
97
+ const info = { pid: process.pid, port: server.address().port, token, key: o.key, started: new Date().toISOString() };
98
+ mkdirSync(o.dir, { recursive: true });
99
+ try {
100
+ // Exclusive: if two owners race, the second one gives up.
101
+ writeFileSync(paths.info, JSON.stringify(info), { flag: "wx", mode: 0o600 });
102
+ }
103
+ catch {
104
+ server.close();
105
+ throw new Error(`another process owns this session (${paths.info})`);
106
+ }
107
+ armIdle();
108
+ return {
109
+ info,
110
+ close: () => {
111
+ server.close();
112
+ // Only remove the file if it is still ours.
113
+ if (readInfo(paths.info)?.token === token)
114
+ rmSync(paths.info, { force: true });
115
+ },
116
+ };
117
+ }
118
+ function tryConnect(info) {
119
+ return new Promise((resolve) => {
120
+ const s = net.connect(info.port, "127.0.0.1");
121
+ s.once("connect", () => {
122
+ s.write(`${info.token}\n`);
123
+ resolve(s);
124
+ });
125
+ s.once("error", () => resolve(undefined));
126
+ });
127
+ }
128
+ /**
129
+ * Connect to the owner of this session, starting it if there is none.
130
+ * Several processes may start at the same moment; a lock file makes exactly
131
+ * one of them spawn the owner, and the rest wait for it.
132
+ */
133
+ export async function connectDaemon(o) {
134
+ const paths = daemonPaths(o.dir, o.key);
135
+ mkdirSync(o.dir, { recursive: true });
136
+ const deadline = Date.now() + (o.waitMs ?? 20_000);
137
+ let spawned = false;
138
+ while (Date.now() < deadline) {
139
+ const info = readInfo(paths.info);
140
+ if (info) {
141
+ const socket = await tryConnect(info);
142
+ if (socket) {
143
+ if (spawned)
144
+ rmSync(paths.lock, { force: true });
145
+ return { socket, info, spawned, log: paths.log };
146
+ }
147
+ if (!alive(info.pid))
148
+ rmSync(paths.info, { force: true });
149
+ }
150
+ else if (!spawned) {
151
+ try {
152
+ closeSync(openSync(paths.lock, "wx"));
153
+ spawned = true;
154
+ o.spawnDaemon(paths.log);
155
+ }
156
+ catch {
157
+ // Someone else is starting it; a lock older than 30 s is left over from a crash.
158
+ try {
159
+ if (Date.now() - statSync(paths.lock).mtimeMs > 30_000)
160
+ rmSync(paths.lock, { force: true });
161
+ }
162
+ catch {
163
+ /* gone already */
164
+ }
165
+ }
166
+ }
167
+ await sleep(150);
168
+ }
169
+ if (spawned)
170
+ rmSync(paths.lock, { force: true });
171
+ throw new Error(`no shared session came up within ${Math.round((o.waitMs ?? 20_000) / 1000)} s (see ${paths.log})`);
172
+ }
173
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Who drives: Claude or the user. "Pause Claude" in the panel holds Claude's
3
+ * tools that change the project or the simulation, so the user can explore
4
+ * without Claude acting at the same time. Reads stay allowed.
5
+ */
6
+ /** The panel view that fits what Claude is doing. "review" is decided by the panel. */
7
+ export type View = "code";
8
+ /** Which view a tool belongs to (undefined for reads, which don't move the panel). */
9
+ export declare function viewOf(tool: string): View | undefined;
10
+ export declare class Driver {
11
+ held: boolean;
12
+ since?: string;
13
+ /** What Claude did last that moves the panel, and when (ms since epoch). */
14
+ phase?: {
15
+ view: View;
16
+ tool: string;
17
+ at: number;
18
+ };
19
+ /** When Claude last called any tool (ms since epoch). */
20
+ lastCallAt?: number;
21
+ /** Called for every tool Claude calls. */
22
+ note(tool: string): void;
23
+ hold(on: boolean): void;
24
+ }
25
+ export declare function heldMessage(d: Driver, tool: string): string;
package/dist/driver.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Who drives: Claude or the user. "Pause Claude" in the panel holds Claude's
3
+ * tools that change the project or the simulation, so the user can explore
4
+ * without Claude acting at the same time. Reads stay allowed.
5
+ */
6
+ const CODE_TOOLS = new Set(["open_project", "create_project", "write_object", "create_object", "delete_object", "rename_object", "revert_object", "compile", "add_library", "remove_library", "configure_task"]);
7
+ /** Which view a tool belongs to (undefined for reads, which don't move the panel). */
8
+ export function viewOf(tool) {
9
+ return CODE_TOOLS.has(tool) ? "code" : undefined;
10
+ }
11
+ export class Driver {
12
+ held = false;
13
+ since;
14
+ /** What Claude did last that moves the panel, and when (ms since epoch). */
15
+ phase;
16
+ /** When Claude last called any tool (ms since epoch). */
17
+ lastCallAt;
18
+ /** Called for every tool Claude calls. */
19
+ note(tool) {
20
+ this.lastCallAt = Date.now();
21
+ const view = viewOf(tool);
22
+ if (view)
23
+ this.phase = { view, tool, at: this.lastCallAt };
24
+ }
25
+ hold(on) {
26
+ this.held = on;
27
+ this.since = on ? new Date().toISOString() : undefined;
28
+ }
29
+ }
30
+ export function heldMessage(d, tool) {
31
+ return `paused_by_user: The user paused Claude in the panel (since ${d.since}), so ${tool} is held. Wait for them to resume, or ask in the chat what they found. Reading tools still work.`;
32
+ }
33
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAEjN,sFAAsF;AACtF,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACnD,CAAC;AAED,MAAM,OAAO,MAAM;IACjB,IAAI,GAAG,KAAK,CAAC;IACb,KAAK,CAAU;IACf,4EAA4E;IAC5E,KAAK,CAA4C;IAEjD,yDAAyD;IACzD,UAAU,CAAU;IAEpB,0CAA0C;IAC1C,IAAI,CAAC,IAAY;QACf,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,IAAI;YAAE,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IAC7D,CAAC;IAED,IAAI,CAAC,EAAW;QACd,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,IAAY;IACjD,OAAO,8DAA8D,CAAC,CAAC,KAAK,SAAS,IAAI,kGAAkG,CAAC;AAC9L,CAAC"}