@yawlabs/tailscale-mcp 0.1.6 → 0.2.1
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 +104 -14
- package/dist/api.js +2 -2
- package/dist/api.js.map +1 -1
- package/dist/index.js +59 -8
- package/dist/index.js.map +1 -1
- package/dist/tools/acl.d.ts +28 -0
- package/dist/tools/acl.js +33 -4
- package/dist/tools/acl.js.map +1 -1
- package/dist/tools/audit.d.ts +14 -0
- package/dist/tools/audit.js +19 -15
- package/dist/tools/audit.js.map +1 -1
- package/dist/tools/devices.d.ts +91 -0
- package/dist/tools/devices.js +102 -4
- package/dist/tools/devices.js.map +1 -1
- package/dist/tools/dns.d.ts +56 -0
- package/dist/tools/dns.js +57 -1
- package/dist/tools/dns.js.map +1 -1
- package/dist/tools/invites.d.ts +56 -0
- package/dist/tools/invites.js +59 -9
- package/dist/tools/invites.js.map +1 -1
- package/dist/tools/keys.d.ts +28 -0
- package/dist/tools/keys.js +29 -1
- package/dist/tools/keys.js.map +1 -1
- package/dist/tools/log-streaming.d.ts +90 -0
- package/dist/tools/log-streaming.js +89 -0
- package/dist/tools/log-streaming.js.map +1 -0
- package/dist/tools/network-lock.d.ts +7 -0
- package/dist/tools/network-lock.js +7 -0
- package/dist/tools/network-lock.js.map +1 -1
- package/dist/tools/oauth-clients.d.ts +118 -0
- package/dist/tools/oauth-clients.js +102 -0
- package/dist/tools/oauth-clients.js.map +1 -0
- package/dist/tools/posture.d.ts +35 -0
- package/dist/tools/posture.js +36 -1
- package/dist/tools/posture.js.map +1 -1
- package/dist/tools/services.d.ts +124 -0
- package/dist/tools/services.js +106 -0
- package/dist/tools/services.js.map +1 -0
- package/dist/tools/status.d.ts +7 -0
- package/dist/tools/status.js +7 -0
- package/dist/tools/status.js.map +1 -1
- package/dist/tools/tailnet.d.ts +28 -0
- package/dist/tools/tailnet.js +28 -0
- package/dist/tools/tailnet.js.map +1 -1
- package/dist/tools/users.d.ts +42 -0
- package/dist/tools/users.js +45 -2
- package/dist/tools/users.js.map +1 -1
- package/dist/tools/webhooks.d.ts +42 -0
- package/dist/tools/webhooks.js +43 -1
- package/dist/tools/webhooks.js.map +1 -1
- package/dist/tools/workload-identity.d.ts +118 -0
- package/dist/tools/workload-identity.js +105 -0
- package/dist/tools/workload-identity.js.map +1 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@yawlabs/tailscale-mcp)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://github.com/YawLabs/tailscale-mcp/stargazers)
|
|
6
|
+
[](https://github.com/YawLabs/tailscale-mcp/actions/workflows/ci.yml)
|
|
6
7
|
|
|
7
|
-
**Manage your Tailscale tailnet from Claude Code, Cursor, and any MCP client.**
|
|
8
|
+
**Manage your Tailscale tailnet from Claude Code, Cursor, and any MCP client.** 81 tools + 4 resources. One env var. Works on first try.
|
|
8
9
|
|
|
9
10
|
Built and maintained by [YawLabs](https://yaw.sh).
|
|
10
11
|
|
|
@@ -14,29 +15,55 @@ Other Tailscale MCP servers were vibe-coded in a weekend and abandoned. This one
|
|
|
14
15
|
|
|
15
16
|
- **Preserves ACL formatting** — reads and writes HuJSON (comments, trailing commas, indentation). Others compact your carefully formatted policy into a single line.
|
|
16
17
|
- **Safe ACL updates** — uses ETags to prevent overwriting concurrent changes. No silent data loss.
|
|
18
|
+
- **Tool annotations** — every tool declares `readOnlyHint`, `destructiveHint`, and `idempotentHint`, so MCP clients skip confirmation dialogs for safe operations.
|
|
19
|
+
- **MCP Resources** — exposes tailnet status, device list, ACL policy, and DNS config as browsable resources.
|
|
17
20
|
- **Zero restarts** — the server always starts, even with missing credentials. Auth errors surface as clear tool-call errors, not silent crashes that force you to restart your AI assistant.
|
|
18
21
|
- **One env var setup** — no config files, no setup wizards, no multi-step flows.
|
|
19
22
|
- **Every tool verified** — no placeholder endpoints that 404. If it's in the tool list, it works.
|
|
20
23
|
|
|
21
24
|
## Quick start
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
**1. Set your API key**
|
|
27
|
+
|
|
28
|
+
Get an API key from [Tailscale Admin Console > Settings > Keys](https://login.tailscale.com/admin/settings/keys) and add it to your shell profile (`~/.bashrc`, `~/.zshrc`, or system environment variables):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
export TAILSCALE_API_KEY="tskey-api-..."
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**2. Create `.mcp.json` in your project root**
|
|
35
|
+
|
|
36
|
+
macOS / Linux / WSL:
|
|
24
37
|
|
|
25
38
|
```json
|
|
26
39
|
{
|
|
27
40
|
"mcpServers": {
|
|
28
41
|
"tailscale": {
|
|
29
42
|
"command": "npx",
|
|
30
|
-
"args": ["-y", "@yawlabs/tailscale-mcp"]
|
|
31
|
-
"env": {
|
|
32
|
-
"TAILSCALE_API_KEY": "tskey-api-..."
|
|
33
|
-
}
|
|
43
|
+
"args": ["-y", "@yawlabs/tailscale-mcp"]
|
|
34
44
|
}
|
|
35
45
|
}
|
|
36
46
|
}
|
|
37
47
|
```
|
|
38
48
|
|
|
39
|
-
|
|
49
|
+
Windows:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"tailscale": {
|
|
55
|
+
"command": "cmd",
|
|
56
|
+
"args": ["/c", "npx", "-y", "@yawlabs/tailscale-mcp"]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
> **Tip:** This file is safe to commit — it contains no secrets. Teammates who set their own `TAILSCALE_API_KEY` will get the MCP server automatically.
|
|
63
|
+
|
|
64
|
+
**3. Restart and approve**
|
|
65
|
+
|
|
66
|
+
Restart Claude Code (or your MCP client) and approve the Tailscale MCP server when prompted.
|
|
40
67
|
|
|
41
68
|
That's it. Now ask your AI assistant:
|
|
42
69
|
|
|
@@ -45,13 +72,13 @@ That's it. Now ask your AI assistant:
|
|
|
45
72
|
```
|
|
46
73
|
┌────────────┬─────────┬────────────────┬──────────────────────┐
|
|
47
74
|
│ Hostname │ OS │ Tailscale IP │ Last Seen │
|
|
48
|
-
|
|
75
|
+
���────────────┼─────────┼────────────────┼──────────────────────┤
|
|
49
76
|
│ web-prod │ Linux │ 100.x.x.1 │ 2026-04-03 21:09 UTC │
|
|
50
|
-
|
|
51
|
-
│ db-staging │ Linux │ 100.x.x.2 │ 2026-04-03 21:09 UTC
|
|
52
|
-
|
|
77
|
+
├────────────┼─────────┼���───────────────┼──────────────────────┤
|
|
78
|
+
│ db-staging │ Linux │ 100.x.x.2 │ 2026-04-03 21:09 UTC ��
|
|
79
|
+
���────────────┼──────���──┼────────────────┼──────────────────────┤
|
|
53
80
|
│ dev-laptop │ macOS │ 100.x.x.3 │ 2026-04-03 21:09 UTC │
|
|
54
|
-
|
|
81
|
+
└────────────┴─────────┴──���─────────────┴──────────────────────┘
|
|
55
82
|
```
|
|
56
83
|
|
|
57
84
|
> "Show me the ACL policy"
|
|
@@ -64,7 +91,7 @@ Pulls the audit log so you can see exactly who did what and when.
|
|
|
64
91
|
|
|
65
92
|
## Authentication
|
|
66
93
|
|
|
67
|
-
**API key (recommended):** Set `TAILSCALE_API_KEY
|
|
94
|
+
**API key (recommended):** Set `TAILSCALE_API_KEY` in your shell profile. Simplest option, works immediately. You can also pass it inline via the `"env"` field in your MCP config if you prefer a self-contained setup.
|
|
68
95
|
|
|
69
96
|
**OAuth (scoped access):** For fine-grained permissions, set `TAILSCALE_OAUTH_CLIENT_ID` and `TAILSCALE_OAUTH_CLIENT_SECRET` instead. Create an OAuth client at [Tailscale Admin Console > Settings > OAuth](https://login.tailscale.com/admin/settings/oauth).
|
|
70
97
|
|
|
@@ -72,7 +99,18 @@ The server checks for an API key first, then falls back to OAuth. If neither is
|
|
|
72
99
|
|
|
73
100
|
**Tailnet:** Uses your default tailnet automatically. Set `TAILSCALE_TAILNET` to specify one explicitly.
|
|
74
101
|
|
|
75
|
-
##
|
|
102
|
+
## Resources (4)
|
|
103
|
+
|
|
104
|
+
MCP Resources expose read-only data that clients can browse without tool calls.
|
|
105
|
+
|
|
106
|
+
| Resource | URI | Description |
|
|
107
|
+
|----------|-----|-------------|
|
|
108
|
+
| Tailnet Status | `tailscale://tailnet/status` | Device count and tailnet settings |
|
|
109
|
+
| Devices | `tailscale://tailnet/devices` | All devices with status and IPs |
|
|
110
|
+
| ACL Policy | `tailscale://tailnet/acl` | Full ACL policy (HuJSON preserved) |
|
|
111
|
+
| DNS Config | `tailscale://tailnet/dns` | Nameservers, search paths, split DNS, MagicDNS |
|
|
112
|
+
|
|
113
|
+
## Tools (81)
|
|
76
114
|
|
|
77
115
|
<details>
|
|
78
116
|
<summary><strong>Status</strong> (1 tool)</summary>
|
|
@@ -206,6 +244,57 @@ The server checks for an API key first, then falls back to OAuth. If neither is
|
|
|
206
244
|
|
|
207
245
|
</details>
|
|
208
246
|
|
|
247
|
+
<details>
|
|
248
|
+
<summary><strong>Tailscale Services</strong> (5 tools)</summary>
|
|
249
|
+
|
|
250
|
+
| Tool | Description |
|
|
251
|
+
|------|-------------|
|
|
252
|
+
| `tailscale_list_services` | List all Tailscale Services in your tailnet |
|
|
253
|
+
| `tailscale_get_service` | Get details for a specific service |
|
|
254
|
+
| `tailscale_update_service` | Update a service's configuration |
|
|
255
|
+
| `tailscale_delete_service` | Delete a service |
|
|
256
|
+
| `tailscale_list_service_hosts` | List devices hosting a service |
|
|
257
|
+
|
|
258
|
+
</details>
|
|
259
|
+
|
|
260
|
+
<details>
|
|
261
|
+
<summary><strong>Log Streaming</strong> (4 tools)</summary>
|
|
262
|
+
|
|
263
|
+
| Tool | Description |
|
|
264
|
+
|------|-------------|
|
|
265
|
+
| `tailscale_list_log_stream_configs` | List log streaming configurations |
|
|
266
|
+
| `tailscale_get_log_stream_config` | Get log streaming config for a log type |
|
|
267
|
+
| `tailscale_set_log_stream_config` | Set where logs are sent (Axiom, Datadog, Splunk, etc.) |
|
|
268
|
+
| `tailscale_delete_log_stream_config` | Delete a log streaming configuration |
|
|
269
|
+
|
|
270
|
+
</details>
|
|
271
|
+
|
|
272
|
+
<details>
|
|
273
|
+
<summary><strong>Workload Identity</strong> (5 tools)</summary>
|
|
274
|
+
|
|
275
|
+
| Tool | Description |
|
|
276
|
+
|------|-------------|
|
|
277
|
+
| `tailscale_list_workload_identities` | List federated workload identity providers |
|
|
278
|
+
| `tailscale_get_workload_identity` | Get a workload identity provider |
|
|
279
|
+
| `tailscale_create_workload_identity` | Create an OIDC federation provider (GitHub Actions, GitLab CI, etc.) |
|
|
280
|
+
| `tailscale_update_workload_identity` | Update a workload identity provider |
|
|
281
|
+
| `tailscale_delete_workload_identity` | Delete a workload identity provider |
|
|
282
|
+
|
|
283
|
+
</details>
|
|
284
|
+
|
|
285
|
+
<details>
|
|
286
|
+
<summary><strong>OAuth Clients</strong> (5 tools)</summary>
|
|
287
|
+
|
|
288
|
+
| Tool | Description |
|
|
289
|
+
|------|-------------|
|
|
290
|
+
| `tailscale_list_oauth_clients` | List OAuth clients |
|
|
291
|
+
| `tailscale_get_oauth_client` | Get an OAuth client |
|
|
292
|
+
| `tailscale_create_oauth_client` | Create an OAuth client for programmatic API access |
|
|
293
|
+
| `tailscale_update_oauth_client` | Update an OAuth client |
|
|
294
|
+
| `tailscale_delete_oauth_client` | Delete an OAuth client |
|
|
295
|
+
|
|
296
|
+
</details>
|
|
297
|
+
|
|
209
298
|
<details>
|
|
210
299
|
<summary><strong>Device Invites</strong> (4 tools)</summary>
|
|
211
300
|
|
|
@@ -255,6 +344,7 @@ git clone https://github.com/YawLabs/tailscale-mcp.git
|
|
|
255
344
|
cd tailscale-mcp
|
|
256
345
|
npm install
|
|
257
346
|
npm run build
|
|
347
|
+
npm run lint
|
|
258
348
|
npm test
|
|
259
349
|
```
|
|
260
350
|
|
package/dist/api.js
CHANGED
|
@@ -59,7 +59,7 @@ async function getOAuthAccessToken(clientId, clientSecret) {
|
|
|
59
59
|
async function getAuthHeader() {
|
|
60
60
|
const config = getConfig();
|
|
61
61
|
if (config.apiKey) {
|
|
62
|
-
return `Basic ${Buffer.from(config.apiKey
|
|
62
|
+
return `Basic ${Buffer.from(`${config.apiKey}:`).toString("base64")}`;
|
|
63
63
|
}
|
|
64
64
|
const token = await getOAuthAccessToken(config.oauthClientId, config.oauthClientSecret);
|
|
65
65
|
return `Bearer ${token}`;
|
|
@@ -77,7 +77,7 @@ export async function apiRequest(method, path, body, options) {
|
|
|
77
77
|
Authorization: auth,
|
|
78
78
|
};
|
|
79
79
|
if (options?.accept) {
|
|
80
|
-
headers
|
|
80
|
+
headers.Accept = options.accept;
|
|
81
81
|
}
|
|
82
82
|
if (options?.ifMatch) {
|
|
83
83
|
headers["If-Match"] = options.ifMatch;
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,QAAQ,GAAG,kCAAkC,CAAC;AACpD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAOlC,IAAI,UAAU,GAAsB,IAAI,CAAC;AACzC,IAAI,mBAAmB,GAA2B,IAAI,CAAC;AAEvD,SAAS,SAAS;IAChB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC7C,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IAC5D,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC;IACpE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,GAAG,CAAC;IAErD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,aAAa,IAAI,iBAAiB,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,uCAAuC;YACrC,iGAAiG,CACpG,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC;AAC/D,CAAC;AAED,KAAK,UAAU,mBAAmB,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import {
|
|
5
|
-
import { deviceTools } from "./tools/devices.js";
|
|
5
|
+
import { apiGet, getTailnet } from "./api.js";
|
|
6
6
|
import { aclTools } from "./tools/acl.js";
|
|
7
|
+
import { auditTools } from "./tools/audit.js";
|
|
8
|
+
import { deviceTools } from "./tools/devices.js";
|
|
7
9
|
import { dnsTools } from "./tools/dns.js";
|
|
10
|
+
import { inviteTools } from "./tools/invites.js";
|
|
8
11
|
import { keyTools } from "./tools/keys.js";
|
|
9
|
-
import {
|
|
10
|
-
import { tailnetTools } from "./tools/tailnet.js";
|
|
11
|
-
import { webhookTools } from "./tools/webhooks.js";
|
|
12
|
+
import { logStreamingTools } from "./tools/log-streaming.js";
|
|
12
13
|
import { networkLockTools } from "./tools/network-lock.js";
|
|
14
|
+
import { oauthClientTools } from "./tools/oauth-clients.js";
|
|
13
15
|
import { postureTools } from "./tools/posture.js";
|
|
14
|
-
import {
|
|
16
|
+
import { serviceTools } from "./tools/services.js";
|
|
15
17
|
import { statusTools } from "./tools/status.js";
|
|
16
|
-
import {
|
|
18
|
+
import { tailnetTools } from "./tools/tailnet.js";
|
|
19
|
+
import { userTools } from "./tools/users.js";
|
|
20
|
+
import { webhookTools } from "./tools/webhooks.js";
|
|
21
|
+
import { workloadIdentityTools } from "./tools/workload-identity.js";
|
|
17
22
|
const require = createRequire(import.meta.url);
|
|
18
23
|
const { version } = require("../package.json");
|
|
19
24
|
const allTools = [
|
|
@@ -29,13 +34,18 @@ const allTools = [
|
|
|
29
34
|
...postureTools,
|
|
30
35
|
...auditTools,
|
|
31
36
|
...inviteTools,
|
|
37
|
+
...serviceTools,
|
|
38
|
+
...logStreamingTools,
|
|
39
|
+
...workloadIdentityTools,
|
|
40
|
+
...oauthClientTools,
|
|
32
41
|
];
|
|
33
42
|
const server = new McpServer({
|
|
34
43
|
name: "@yawlabs/tailscale-mcp",
|
|
35
44
|
version,
|
|
36
45
|
});
|
|
46
|
+
// Register all tools with annotations
|
|
37
47
|
for (const tool of allTools) {
|
|
38
|
-
server.tool(tool.name, tool.description, tool.inputSchema.shape, async (input) => {
|
|
48
|
+
server.tool(tool.name, tool.description, tool.inputSchema.shape, tool.annotations, async (input) => {
|
|
39
49
|
try {
|
|
40
50
|
const result = await tool.handler(input);
|
|
41
51
|
const response = result;
|
|
@@ -64,6 +74,47 @@ for (const tool of allTools) {
|
|
|
64
74
|
}
|
|
65
75
|
});
|
|
66
76
|
}
|
|
77
|
+
// Register MCP Resources
|
|
78
|
+
server.resource("tailnet-status", "tailscale://tailnet/status", { description: "Current tailnet status including device count and settings", mimeType: "application/json" }, async (uri) => {
|
|
79
|
+
const [devicesRes, settingsRes] = await Promise.all([
|
|
80
|
+
apiGet(`/tailnet/${getTailnet()}/devices?fields=id`),
|
|
81
|
+
apiGet(`/tailnet/${getTailnet()}/settings`),
|
|
82
|
+
]);
|
|
83
|
+
const data = {
|
|
84
|
+
tailnet: getTailnet(),
|
|
85
|
+
deviceCount: devicesRes.ok ? (devicesRes.data?.devices?.length ?? 0) : "error",
|
|
86
|
+
settings: settingsRes.ok ? settingsRes.data : undefined,
|
|
87
|
+
};
|
|
88
|
+
return { contents: [{ uri: uri.href, text: JSON.stringify(data, null, 2), mimeType: "application/json" }] };
|
|
89
|
+
});
|
|
90
|
+
server.resource("tailnet-devices", "tailscale://tailnet/devices", { description: "List of all devices in the tailnet with their status", mimeType: "application/json" }, async (uri) => {
|
|
91
|
+
const res = await apiGet(`/tailnet/${getTailnet()}/devices`);
|
|
92
|
+
const text = res.ok ? JSON.stringify(res.data, null, 2) : JSON.stringify({ error: res.error });
|
|
93
|
+
return { contents: [{ uri: uri.href, text, mimeType: "application/json" }] };
|
|
94
|
+
});
|
|
95
|
+
server.resource("tailnet-acl", "tailscale://tailnet/acl", { description: "Current ACL policy (HuJSON with comments preserved)", mimeType: "application/hujson" }, async (uri) => {
|
|
96
|
+
const res = await apiGet(`/tailnet/${getTailnet()}/acl`, { acceptRaw: true, accept: "application/hujson" });
|
|
97
|
+
const text = res.ok ? (res.rawBody ?? "") : `Error: ${res.error}`;
|
|
98
|
+
return { contents: [{ uri: uri.href, text, mimeType: "application/hujson" }] };
|
|
99
|
+
});
|
|
100
|
+
server.resource("tailnet-dns", "tailscale://tailnet/dns", {
|
|
101
|
+
description: "DNS configuration including nameservers, search paths, split DNS, and MagicDNS status",
|
|
102
|
+
mimeType: "application/json",
|
|
103
|
+
}, async (uri) => {
|
|
104
|
+
const [nameservers, searchPaths, splitDns, preferences] = await Promise.all([
|
|
105
|
+
apiGet(`/tailnet/${getTailnet()}/dns/nameservers`),
|
|
106
|
+
apiGet(`/tailnet/${getTailnet()}/dns/searchpaths`),
|
|
107
|
+
apiGet(`/tailnet/${getTailnet()}/dns/split-dns`),
|
|
108
|
+
apiGet(`/tailnet/${getTailnet()}/dns/preferences`),
|
|
109
|
+
]);
|
|
110
|
+
const data = {
|
|
111
|
+
nameservers: nameservers.ok ? nameservers.data : undefined,
|
|
112
|
+
searchPaths: searchPaths.ok ? searchPaths.data : undefined,
|
|
113
|
+
splitDns: splitDns.ok ? splitDns.data : undefined,
|
|
114
|
+
preferences: preferences.ok ? preferences.data : undefined,
|
|
115
|
+
};
|
|
116
|
+
return { contents: [{ uri: uri.href, text: JSON.stringify(data, null, 2), mimeType: "application/json" }] };
|
|
117
|
+
});
|
|
67
118
|
async function main() {
|
|
68
119
|
const transport = new StdioServerTransport();
|
|
69
120
|
await server.connect(transport);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/acl.d.ts
CHANGED
|
@@ -2,11 +2,25 @@ import { z } from "zod";
|
|
|
2
2
|
export declare const aclTools: readonly [{
|
|
3
3
|
readonly name: "tailscale_get_acl";
|
|
4
4
|
readonly description: "Get the current ACL policy for your tailnet. Returns the raw policy text with original formatting preserved, including comments and trailing commas (HuJSON). Also returns an ETag — you must pass it to tailscale_update_acl to safely update the policy.";
|
|
5
|
+
readonly annotations: {
|
|
6
|
+
readonly title: "Get ACL policy";
|
|
7
|
+
readonly readOnlyHint: true;
|
|
8
|
+
readonly destructiveHint: false;
|
|
9
|
+
readonly idempotentHint: true;
|
|
10
|
+
readonly openWorldHint: true;
|
|
11
|
+
};
|
|
5
12
|
readonly inputSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
6
13
|
readonly handler: () => Promise<import("../api.js").ApiResponse<unknown>>;
|
|
7
14
|
}, {
|
|
8
15
|
readonly name: "tailscale_update_acl";
|
|
9
16
|
readonly description: "Update the ACL policy for your tailnet. Accepts the full policy as a string to preserve formatting, comments, and trailing commas (HuJSON). You MUST pass the ETag from tailscale_get_acl to prevent overwriting concurrent changes. Always get the current ACL first, make targeted edits to the text, and pass the full modified text back.";
|
|
17
|
+
readonly annotations: {
|
|
18
|
+
readonly title: "Update ACL policy";
|
|
19
|
+
readonly readOnlyHint: false;
|
|
20
|
+
readonly destructiveHint: false;
|
|
21
|
+
readonly idempotentHint: true;
|
|
22
|
+
readonly openWorldHint: true;
|
|
23
|
+
};
|
|
10
24
|
readonly inputSchema: z.ZodObject<{
|
|
11
25
|
policy: z.ZodString;
|
|
12
26
|
etag: z.ZodString;
|
|
@@ -24,6 +38,13 @@ export declare const aclTools: readonly [{
|
|
|
24
38
|
}, {
|
|
25
39
|
readonly name: "tailscale_validate_acl";
|
|
26
40
|
readonly description: "Validate an ACL policy without applying it. Returns any errors found, or confirms the policy is valid.";
|
|
41
|
+
readonly annotations: {
|
|
42
|
+
readonly title: "Validate ACL policy";
|
|
43
|
+
readonly readOnlyHint: true;
|
|
44
|
+
readonly destructiveHint: false;
|
|
45
|
+
readonly idempotentHint: true;
|
|
46
|
+
readonly openWorldHint: true;
|
|
47
|
+
};
|
|
27
48
|
readonly inputSchema: z.ZodObject<{
|
|
28
49
|
policy: z.ZodString;
|
|
29
50
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -37,6 +58,13 @@ export declare const aclTools: readonly [{
|
|
|
37
58
|
}, {
|
|
38
59
|
readonly name: "tailscale_preview_acl";
|
|
39
60
|
readonly description: "Preview the ACL rules that would apply to a specific user or IP address if a proposed policy were applied.";
|
|
61
|
+
readonly annotations: {
|
|
62
|
+
readonly title: "Preview ACL rules";
|
|
63
|
+
readonly readOnlyHint: true;
|
|
64
|
+
readonly destructiveHint: false;
|
|
65
|
+
readonly idempotentHint: true;
|
|
66
|
+
readonly openWorldHint: true;
|
|
67
|
+
};
|
|
40
68
|
readonly inputSchema: z.ZodObject<{
|
|
41
69
|
policy: z.ZodString;
|
|
42
70
|
type: z.ZodEnum<["user", "ipport"]>;
|
package/dist/tools/acl.js
CHANGED
|
@@ -4,6 +4,13 @@ export const aclTools = [
|
|
|
4
4
|
{
|
|
5
5
|
name: "tailscale_get_acl",
|
|
6
6
|
description: "Get the current ACL policy for your tailnet. Returns the raw policy text with original formatting preserved, including comments and trailing commas (HuJSON). Also returns an ETag — you must pass it to tailscale_update_acl to safely update the policy.",
|
|
7
|
+
annotations: {
|
|
8
|
+
title: "Get ACL policy",
|
|
9
|
+
readOnlyHint: true,
|
|
10
|
+
destructiveHint: false,
|
|
11
|
+
idempotentHint: true,
|
|
12
|
+
openWorldHint: true,
|
|
13
|
+
},
|
|
7
14
|
inputSchema: z.object({}),
|
|
8
15
|
handler: async () => {
|
|
9
16
|
const res = await apiGet(`/tailnet/${getTailnet()}/acl`, {
|
|
@@ -22,13 +29,18 @@ export const aclTools = [
|
|
|
22
29
|
{
|
|
23
30
|
name: "tailscale_update_acl",
|
|
24
31
|
description: "Update the ACL policy for your tailnet. Accepts the full policy as a string to preserve formatting, comments, and trailing commas (HuJSON). You MUST pass the ETag from tailscale_get_acl to prevent overwriting concurrent changes. Always get the current ACL first, make targeted edits to the text, and pass the full modified text back.",
|
|
32
|
+
annotations: {
|
|
33
|
+
title: "Update ACL policy",
|
|
34
|
+
readOnlyHint: false,
|
|
35
|
+
destructiveHint: false,
|
|
36
|
+
idempotentHint: true,
|
|
37
|
+
openWorldHint: true,
|
|
38
|
+
},
|
|
25
39
|
inputSchema: z.object({
|
|
26
40
|
policy: z
|
|
27
41
|
.string()
|
|
28
42
|
.describe("The full ACL policy text. Preserve existing formatting, comments, and structure. Only modify the specific parts that need to change."),
|
|
29
|
-
etag: z
|
|
30
|
-
.string()
|
|
31
|
-
.describe("The ETag from tailscale_get_acl. Required to prevent concurrent edit conflicts."),
|
|
43
|
+
etag: z.string().describe("The ETag from tailscale_get_acl. Required to prevent concurrent edit conflicts."),
|
|
32
44
|
}),
|
|
33
45
|
handler: async (input) => {
|
|
34
46
|
return apiPost(`/tailnet/${getTailnet()}/acl`, undefined, {
|
|
@@ -41,6 +53,13 @@ export const aclTools = [
|
|
|
41
53
|
{
|
|
42
54
|
name: "tailscale_validate_acl",
|
|
43
55
|
description: "Validate an ACL policy without applying it. Returns any errors found, or confirms the policy is valid.",
|
|
56
|
+
annotations: {
|
|
57
|
+
title: "Validate ACL policy",
|
|
58
|
+
readOnlyHint: true,
|
|
59
|
+
destructiveHint: false,
|
|
60
|
+
idempotentHint: true,
|
|
61
|
+
openWorldHint: true,
|
|
62
|
+
},
|
|
44
63
|
inputSchema: z.object({
|
|
45
64
|
policy: z.string().describe("The full ACL policy text to validate"),
|
|
46
65
|
}),
|
|
@@ -58,6 +77,13 @@ export const aclTools = [
|
|
|
58
77
|
{
|
|
59
78
|
name: "tailscale_preview_acl",
|
|
60
79
|
description: "Preview the ACL rules that would apply to a specific user or IP address if a proposed policy were applied.",
|
|
80
|
+
annotations: {
|
|
81
|
+
title: "Preview ACL rules",
|
|
82
|
+
readOnlyHint: true,
|
|
83
|
+
destructiveHint: false,
|
|
84
|
+
idempotentHint: true,
|
|
85
|
+
openWorldHint: true,
|
|
86
|
+
},
|
|
61
87
|
inputSchema: z.object({
|
|
62
88
|
policy: z.string().describe("The proposed ACL policy text to preview"),
|
|
63
89
|
type: z
|
|
@@ -69,7 +95,10 @@ export const aclTools = [
|
|
|
69
95
|
}),
|
|
70
96
|
handler: async (input) => {
|
|
71
97
|
const params = new URLSearchParams({ type: input.type, previewFor: input.previewFor });
|
|
72
|
-
return apiPost(`/tailnet/${getTailnet()}/acl/preview?${params}`, undefined, {
|
|
98
|
+
return apiPost(`/tailnet/${getTailnet()}/acl/preview?${params}`, undefined, {
|
|
99
|
+
rawBody: input.policy,
|
|
100
|
+
contentType: "application/hujson",
|
|
101
|
+
});
|
|
73
102
|
},
|
|
74
103
|
},
|
|
75
104
|
];
|
package/dist/tools/acl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acl.js","sourceRoot":"","sources":["../../src/tools/acl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAExD,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB;QACE,IAAI,EAAE,mBAAmB;QACzB,WAAW,EACT,4PAA4P;QAC9P,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,UAAU,EAAE,MAAM,EAAE;gBACvD,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,oBAAoB;aAC7B,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;gBACvB,OAAO;oBACL,GAAG,GAAG;oBACN,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,kBAAkB,GAAG,CAAC,IAAI,oEAAoE;iBACtH,CAAC;YACJ,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EACT,+UAA+U;QACjV,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,QAAQ,CACP,sIAAsI,CACvI;YACH,IAAI,EAAE,CAAC
|
|
1
|
+
{"version":3,"file":"acl.js","sourceRoot":"","sources":["../../src/tools/acl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAExD,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB;QACE,IAAI,EAAE,mBAAmB;QACzB,WAAW,EACT,4PAA4P;QAC9P,WAAW,EAAE;YACX,KAAK,EAAE,gBAAgB;YACvB,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;QACD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,UAAU,EAAE,MAAM,EAAE;gBACvD,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,oBAAoB;aAC7B,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;gBACvB,OAAO;oBACL,GAAG,GAAG;oBACN,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,kBAAkB,GAAG,CAAC,IAAI,oEAAoE;iBACtH,CAAC;YACJ,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EACT,+UAA+U;QACjV,WAAW,EAAE;YACX,KAAK,EAAE,mBAAmB;YAC1B,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;QACD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,QAAQ,CACP,sIAAsI,CACvI;YACH,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iFAAiF,CAAC;SAC7G,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAuC,EAAE,EAAE;YACzD,OAAO,OAAO,CAAC,YAAY,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE;gBACxD,OAAO,EAAE,KAAK,CAAC,MAAM;gBACrB,WAAW,EAAE,oBAAoB;gBACjC,OAAO,EAAE,KAAK,CAAC,IAAI;aACpB,CAAC,CAAC;QACL,CAAC;KACF;IACD;QACE,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EACT,wGAAwG;QAC1G,WAAW,EAAE;YACX,KAAK,EAAE,qBAAqB;YAC5B,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;QACD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;SACpE,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAyB,EAAE,EAAE;YAC3C,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,YAAY,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE;gBAC5E,OAAO,EAAE,KAAK,CAAC,MAAM;gBACrB,WAAW,EAAE,oBAAoB;aAClC,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBACxB,OAAO,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,EAAE,CAAC;YAC/D,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EACT,4GAA4G;QAC9G,WAAW,EAAE;YACX,KAAK,EAAE,mBAAmB;YAC1B,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;QACD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;YACtE,IAAI,EAAE,CAAC;iBACJ,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;iBACxB,QAAQ,CAAC,+EAA+E,CAAC;YAC5F,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,sFAAsF,CAAC;SACpG,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAA2D,EAAE,EAAE;YAC7E,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YACvF,OAAO,OAAO,CAAC,YAAY,UAAU,EAAE,gBAAgB,MAAM,EAAE,EAAE,SAAS,EAAE;gBAC1E,OAAO,EAAE,KAAK,CAAC,MAAM;gBACrB,WAAW,EAAE,oBAAoB;aAClC,CAAC,CAAC;QACL,CAAC;KACF;CACO,CAAC"}
|
package/dist/tools/audit.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ import { z } from "zod";
|
|
|
2
2
|
export declare const auditTools: readonly [{
|
|
3
3
|
readonly name: "tailscale_get_audit_log";
|
|
4
4
|
readonly description: "Get the tailnet audit/configuration log. Shows who changed what and when — useful for troubleshooting and compliance.";
|
|
5
|
+
readonly annotations: {
|
|
6
|
+
readonly title: "Get audit log";
|
|
7
|
+
readonly readOnlyHint: true;
|
|
8
|
+
readonly destructiveHint: false;
|
|
9
|
+
readonly idempotentHint: true;
|
|
10
|
+
readonly openWorldHint: true;
|
|
11
|
+
};
|
|
5
12
|
readonly inputSchema: z.ZodObject<{
|
|
6
13
|
start: z.ZodString;
|
|
7
14
|
end: z.ZodOptional<z.ZodString>;
|
|
@@ -19,6 +26,13 @@ export declare const auditTools: readonly [{
|
|
|
19
26
|
}, {
|
|
20
27
|
readonly name: "tailscale_get_network_flow_logs";
|
|
21
28
|
readonly description: "Get network traffic flow logs showing connections between devices. Shows source/destination nodes, timestamps, and traffic metadata — useful for security monitoring and debugging connectivity.";
|
|
29
|
+
readonly annotations: {
|
|
30
|
+
readonly title: "Get network flow logs";
|
|
31
|
+
readonly readOnlyHint: true;
|
|
32
|
+
readonly destructiveHint: false;
|
|
33
|
+
readonly idempotentHint: true;
|
|
34
|
+
readonly openWorldHint: true;
|
|
35
|
+
};
|
|
22
36
|
readonly inputSchema: z.ZodObject<{
|
|
23
37
|
start: z.ZodString;
|
|
24
38
|
end: z.ZodOptional<z.ZodString>;
|
package/dist/tools/audit.js
CHANGED
|
@@ -3,7 +3,7 @@ import { apiGet, getTailnet } from "../api.js";
|
|
|
3
3
|
/** Validate that a string is a valid RFC3339 date-time. */
|
|
4
4
|
function assertRFC3339(value, label) {
|
|
5
5
|
const d = new Date(value);
|
|
6
|
-
if (isNaN(d.getTime())) {
|
|
6
|
+
if (Number.isNaN(d.getTime())) {
|
|
7
7
|
throw new Error(`${label} must be a valid RFC3339 date-time (e.g. '2026-04-01T00:00:00Z'), got: '${value}'`);
|
|
8
8
|
}
|
|
9
9
|
}
|
|
@@ -11,14 +11,16 @@ export const auditTools = [
|
|
|
11
11
|
{
|
|
12
12
|
name: "tailscale_get_audit_log",
|
|
13
13
|
description: "Get the tailnet audit/configuration log. Shows who changed what and when — useful for troubleshooting and compliance.",
|
|
14
|
+
annotations: {
|
|
15
|
+
title: "Get audit log",
|
|
16
|
+
readOnlyHint: true,
|
|
17
|
+
destructiveHint: false,
|
|
18
|
+
idempotentHint: true,
|
|
19
|
+
openWorldHint: true,
|
|
20
|
+
},
|
|
14
21
|
inputSchema: z.object({
|
|
15
|
-
start: z
|
|
16
|
-
|
|
17
|
-
.describe("Start time in RFC3339 format (e.g. '2026-04-01T00:00:00Z'). Required."),
|
|
18
|
-
end: z
|
|
19
|
-
.string()
|
|
20
|
-
.optional()
|
|
21
|
-
.describe("End time in RFC3339 format. Defaults to now."),
|
|
22
|
+
start: z.string().describe("Start time in RFC3339 format (e.g. '2026-04-01T00:00:00Z'). Required."),
|
|
23
|
+
end: z.string().optional().describe("End time in RFC3339 format. Defaults to now."),
|
|
22
24
|
}),
|
|
23
25
|
handler: async (input) => {
|
|
24
26
|
assertRFC3339(input.start, "start");
|
|
@@ -33,14 +35,16 @@ export const auditTools = [
|
|
|
33
35
|
{
|
|
34
36
|
name: "tailscale_get_network_flow_logs",
|
|
35
37
|
description: "Get network traffic flow logs showing connections between devices. Shows source/destination nodes, timestamps, and traffic metadata — useful for security monitoring and debugging connectivity.",
|
|
38
|
+
annotations: {
|
|
39
|
+
title: "Get network flow logs",
|
|
40
|
+
readOnlyHint: true,
|
|
41
|
+
destructiveHint: false,
|
|
42
|
+
idempotentHint: true,
|
|
43
|
+
openWorldHint: true,
|
|
44
|
+
},
|
|
36
45
|
inputSchema: z.object({
|
|
37
|
-
start: z
|
|
38
|
-
|
|
39
|
-
.describe("Start time in RFC3339 format (e.g. '2026-04-01T00:00:00Z'). Required."),
|
|
40
|
-
end: z
|
|
41
|
-
.string()
|
|
42
|
-
.optional()
|
|
43
|
-
.describe("End time in RFC3339 format. Defaults to now."),
|
|
46
|
+
start: z.string().describe("Start time in RFC3339 format (e.g. '2026-04-01T00:00:00Z'). Required."),
|
|
47
|
+
end: z.string().optional().describe("End time in RFC3339 format. Defaults to now."),
|
|
44
48
|
}),
|
|
45
49
|
handler: async (input) => {
|
|
46
50
|
assertRFC3339(input.start, "start");
|
package/dist/tools/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/tools/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE/C,2DAA2D;AAC3D,SAAS,aAAa,CAAC,KAAa,EAAE,KAAa;IACjD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/tools/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE/C,2DAA2D;AAC3D,SAAS,aAAa,CAAC,KAAa,EAAE,KAAa;IACjD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,2EAA2E,KAAK,GAAG,CAAC,CAAC;IAC/G,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB;QACE,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EACT,uHAAuH;QACzH,WAAW,EAAE;YACX,KAAK,EAAE,eAAe;YACtB,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;QACD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uEAAuE,CAAC;YACnG,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;SACpF,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAsC,EAAE,EAAE;YACxD,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACpC,IAAI,KAAK,CAAC,GAAG;gBAAE,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC3D,IAAI,KAAK,CAAC,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5C,OAAO,MAAM,CAAC,YAAY,UAAU,EAAE,0BAA0B,MAAM,EAAE,CAAC,CAAC;QAC5E,CAAC;KACF;IACD;QACE,IAAI,EAAE,iCAAiC;QACvC,WAAW,EACT,kMAAkM;QACpM,WAAW,EAAE;YACX,KAAK,EAAE,uBAAuB;YAC9B,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;QACD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uEAAuE,CAAC;YACnG,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;SACpF,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAsC,EAAE,EAAE;YACxD,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACpC,IAAI,KAAK,CAAC,GAAG;gBAAE,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC3D,IAAI,KAAK,CAAC,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5C,OAAO,MAAM,CAAC,YAAY,UAAU,EAAE,oBAAoB,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC;KACF;CACO,CAAC"}
|