@kietdev/taiga-mcp 0.0.0-stage → 0.4.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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +285 -2
  3. package/dist/config.js +59 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/format.js +104 -0
  6. package/dist/format.js.map +1 -0
  7. package/dist/index.js +22 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/remote/app.js +238 -0
  10. package/dist/remote/app.js.map +1 -0
  11. package/dist/remote/config.js +52 -0
  12. package/dist/remote/config.js.map +1 -0
  13. package/dist/remote/guard.js +120 -0
  14. package/dist/remote/guard.js.map +1 -0
  15. package/dist/remote/pages.js +472 -0
  16. package/dist/remote/pages.js.map +1 -0
  17. package/dist/remote/provider.js +211 -0
  18. package/dist/remote/provider.js.map +1 -0
  19. package/dist/remote/seal.js +50 -0
  20. package/dist/remote/seal.js.map +1 -0
  21. package/dist/remote/taiga-auth.js +70 -0
  22. package/dist/remote/taiga-auth.js.map +1 -0
  23. package/dist/remote.js +23 -0
  24. package/dist/remote.js.map +1 -0
  25. package/dist/server.js +64 -0
  26. package/dist/server.js.map +1 -0
  27. package/dist/taiga/client.js +1274 -0
  28. package/dist/taiga/client.js.map +1 -0
  29. package/dist/taiga/files.js +331 -0
  30. package/dist/taiga/files.js.map +1 -0
  31. package/dist/taiga/http.js +237 -0
  32. package/dist/taiga/http.js.map +1 -0
  33. package/dist/taiga/types.js +6 -0
  34. package/dist/taiga/types.js.map +1 -0
  35. package/dist/tools/add-comment.js +24 -0
  36. package/dist/tools/add-comment.js.map +1 -0
  37. package/dist/tools/create-item.js +63 -0
  38. package/dist/tools/create-item.js.map +1 -0
  39. package/dist/tools/file-view.js +300 -0
  40. package/dist/tools/file-view.js.map +1 -0
  41. package/dist/tools/get-attachment.js +76 -0
  42. package/dist/tools/get-attachment.js.map +1 -0
  43. package/dist/tools/get-item.js +120 -0
  44. package/dist/tools/get-item.js.map +1 -0
  45. package/dist/tools/get-project.js +47 -0
  46. package/dist/tools/get-project.js.map +1 -0
  47. package/dist/tools/get-sprint.js +56 -0
  48. package/dist/tools/get-sprint.js.map +1 -0
  49. package/dist/tools/list-activity.js +102 -0
  50. package/dist/tools/list-activity.js.map +1 -0
  51. package/dist/tools/list-epics.js +58 -0
  52. package/dist/tools/list-epics.js.map +1 -0
  53. package/dist/tools/list-issues.js +78 -0
  54. package/dist/tools/list-issues.js.map +1 -0
  55. package/dist/tools/list-projects.js +22 -0
  56. package/dist/tools/list-projects.js.map +1 -0
  57. package/dist/tools/list-sprints.js +46 -0
  58. package/dist/tools/list-sprints.js.map +1 -0
  59. package/dist/tools/list-tasks.js +84 -0
  60. package/dist/tools/list-tasks.js.map +1 -0
  61. package/dist/tools/list-user-stories.js +85 -0
  62. package/dist/tools/list-user-stories.js.map +1 -0
  63. package/dist/tools/search.js +83 -0
  64. package/dist/tools/search.js.map +1 -0
  65. package/dist/tools/shared.js +90 -0
  66. package/dist/tools/shared.js.map +1 -0
  67. package/dist/tools/update-item.js +74 -0
  68. package/dist/tools/update-item.js.map +1 -0
  69. package/dist/tools/wiki.js +62 -0
  70. package/dist/tools/wiki.js.map +1 -0
  71. package/dist/tools/written.js +18 -0
  72. package/dist/tools/written.js.map +1 -0
  73. package/package.json +78 -4
@@ -0,0 +1,472 @@
1
+ /** The few pages a person sees: the front page, the sign-in form, and a dead end. No scripts. */
2
+ import { createHash } from 'node:crypto';
3
+ import { SERVER_INFO } from '../server.js';
4
+ /**
5
+ * The one script any page carries: copy buttons on the front page. The buttons start hidden, so
6
+ * without scripts nobody sees a button that does nothing. Allowed by its hash in the
7
+ * Content-Security-Policy, so no other script can run.
8
+ */
9
+ const COPY_SCRIPT = "(function(){var s=document.getElementById('copy-status');" +
10
+ "document.querySelectorAll('button[data-copy]').forEach(function(b){b.hidden=false;" +
11
+ "b.addEventListener('click',function(){var t=document.getElementById(b.getAttribute('data-copy'));" +
12
+ 'if(!t||!navigator.clipboard)return;navigator.clipboard.writeText(t.textContent).then(function(){' +
13
+ "b.classList.add('done');if(s)s.textContent=s.getAttribute('data-done');" +
14
+ "setTimeout(function(){b.classList.remove('done');if(s)s.textContent='';},1600);});});});})();";
15
+ /** For the Content-Security-Policy: the only script allowed to run. */
16
+ export const SCRIPT_HASH = `'sha256-${createHash('sha256').update(COPY_SCRIPT).digest('base64')}'`;
17
+ const COPY_ICON = '<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>' +
18
+ '<svg class="ok" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg>';
19
+ /** Text to copy, in a box with a copy button (shown once the script runs). */
20
+ function snippet(tag, id, text, label) {
21
+ return `<div class="snippet"><${tag} id="${id}">${escape(text)}</${tag}><button type="button" class="copy" data-copy="${id}" aria-label="${escape(label)}" title="${escape(label)}" hidden>${COPY_ICON}</button></div>`;
22
+ }
23
+ const TEXT = {
24
+ en: {
25
+ signInTitle: 'Sign in to Taiga',
26
+ wants: (app, host) => `${app} is asking to read your Taiga projects at ${host}. It can only read: nothing in Taiga will be changed.`,
27
+ wantsAny: (app) => `${app} is asking to read your Taiga projects. It can only read: nothing in Taiga will be changed.`,
28
+ wantsWrite: (app, host) => `${app} is asking for access to your Taiga projects at ${host}. It can read them, and change them only if you tick the box below.`,
29
+ wantsWriteAny: (app) => `${app} is asking for access to your Taiga projects. It can read them, and change them only if you tick the box below.`,
30
+ allowWrite: 'Also let it create items, change them and comment, as me',
31
+ allowWriteHint: 'Changes appear in Taiga under your name. Nothing can be deleted. Leave it unticked to allow reading only.',
32
+ anApp: 'An app',
33
+ address: 'Taiga address',
34
+ addressHint: 'The address you open Taiga at. For Taiga cloud: tree.taiga.io',
35
+ badAddress: 'No Taiga could be reached at that address. Use the address you open Taiga at.',
36
+ username: 'Username or email',
37
+ password: 'Password',
38
+ submit: 'Sign in and allow',
39
+ passwordNote: 'Your password is sent to Taiga once to sign you in. This server does not store it.',
40
+ returnsTo: (host) => `After signing in you are sent back to ${host}.`,
41
+ returnsLocal: 'After signing in you are sent back to an app on this computer.',
42
+ onlyIfYou: 'Continue only if you started this connection yourself, from that app.',
43
+ rejected: 'Taiga did not accept that username and password. Try again.',
44
+ unavailable: 'Taiga could not be reached just now. Try again in a moment.',
45
+ tooMany: 'Too many attempts. Wait a few minutes and try again.',
46
+ expiredTitle: 'This sign-in page has expired',
47
+ expiredBody: 'Go back to the app you came from and connect to Taiga again.',
48
+ homeTitle: 'Taiga for AI assistants',
49
+ pageTitle: 'Taiga for AI assistants: ask Claude about your projects',
50
+ description: 'Connect Claude or any MCP app to Taiga and ask about projects, sprints, user stories, tasks and issues in plain words. Read-only, with your own Taiga account.',
51
+ readOnly: 'Read-only',
52
+ readWrite: 'Reads · writes if you allow',
53
+ descriptionWrite: 'Connect Claude or any MCP app to Taiga and ask about projects, sprints, user stories, tasks and issues in plain words, or have it create and update them. With your own Taiga account.',
54
+ writeFact: [
55
+ 'Changes only if you allow',
56
+ 'Reading by default. Creating, changing and commenting only if you tick a box at sign-in. Nothing can be deleted.',
57
+ ],
58
+ writeExample: 'Create a task “Write the release notes” on story 42 and assign it to me.',
59
+ homeLead: (host) => `Ask Claude, or another AI assistant, about your projects, sprints, user stories, tasks, issues and wiki at ${host}, in plain words.`,
60
+ homeLeadAny: 'Ask Claude, or another AI assistant, about your Taiga projects, sprints, user stories, tasks, issues and wiki in plain words: on Taiga cloud or on your company’s own Taiga.',
61
+ facts: [
62
+ ['Read-only', 'It looks things up. It cannot change anything in Taiga.'],
63
+ ['Your own account', 'You sign in as yourself and see what Taiga lets you see.'],
64
+ ['Nothing stored', 'No database. Your password goes to Taiga once, to sign in.'],
65
+ ],
66
+ connect: 'Connect',
67
+ asks: 'What you can ask',
68
+ examples: [
69
+ 'Which stories in the current sprint are not done yet?',
70
+ 'What is Linh working on this sprint?',
71
+ 'Find the story about exporting invoices as PDF.',
72
+ 'Which tasks are left on story 42?',
73
+ 'How many tasks did I finish in October?',
74
+ 'Where did we write down the release steps?',
75
+ ],
76
+ builtBy: 'Built by',
77
+ signInStepAny: 'Press Connect, enter the address of your Taiga, and sign in with your Taiga account.',
78
+ steps: [
79
+ 'Copy this address:',
80
+ 'In Claude, open Settings → Connectors → Add custom connector, and paste the address.',
81
+ 'Press Connect and sign in with your Taiga account.',
82
+ 'Ask in plain words. Claude looks the answer up in Taiga.',
83
+ ],
84
+ otherApps: 'Other AI apps',
85
+ webApps: 'In the browser',
86
+ toolApps: 'In an editor or a terminal',
87
+ copy: 'Copy',
88
+ copied: 'Copied',
89
+ otherAppsLead: 'Any app that supports remote MCP servers connects with the same address, and asks you to sign in to Taiga the first time. Menus move between versions: if a step looks different, look for “MCP”, “connector” or “tools” in the app’s settings.',
90
+ howTo: {
91
+ claude: 'The steps above, on claude.ai or in the Claude desktop app.',
92
+ lechat: 'Connectors → + Add Connector → Custom MCP Connector tab. Name it taiga (no spaces), paste the address, press Connect and sign in. Only the administrator of the Le Chat account can add connectors; on Free and Pro plans that is the account owner.',
93
+ perplexity: 'Plugins in the sidebar (or Customize → Connectors) → Add custom → Add MCP connector → Remote. Name it Taiga, paste the address, set Authentication to OAuth and Transport to Streamable HTTP, tick the acknowledgement and press Add. Then open the connector card to sign in. In an organization, an administrator must first allow members to add custom connectors.',
94
+ claudeCode: 'In a terminal, then type /mcp in Claude Code and choose taiga to sign in:',
95
+ chatgpt: 'Settings → Apps & Connectors → Advanced settings: turn on Developer mode. Then Create, paste the address, choose OAuth and sign in. Needs a plan with developer mode.',
96
+ vscode: 'Command palette → “MCP: Add Server” → HTTP, paste the address and name it taiga. Or put this in .vscode/mcp.json. Use it from Copilot Chat in Agent mode.',
97
+ cursor: 'Settings → MCP → Add new MCP server, or put this in ~/.cursor/mcp.json:',
98
+ gemini: 'Put this in ~/.gemini/settings.json, then type /mcp auth taiga to sign in:',
99
+ codex: 'In a terminal:',
100
+ windsurf: 'Put this in ~/.codeium/windsurf/mcp_config.json:',
101
+ otherName: 'Other MCP apps',
102
+ other: 'Use the address as a remote or “Streamable HTTP” server. Sign-in is OAuth, found by the app itself; there is no key to paste.',
103
+ },
104
+ },
105
+ vi: {
106
+ signInTitle: 'Đăng nhập Taiga',
107
+ wants: (app, host) => `${app} xin quyền đọc các project Taiga của bạn tại ${host}. Chỉ đọc: không có gì trên Taiga bị thay đổi.`,
108
+ wantsAny: (app) => `${app} xin quyền đọc các project Taiga của bạn. Chỉ đọc: không có gì trên Taiga bị thay đổi.`,
109
+ wantsWrite: (app, host) => `${app} xin quyền truy cập các project Taiga của bạn tại ${host}. Ứng dụng được đọc, và chỉ được sửa nếu bạn tick ô bên dưới.`,
110
+ wantsWriteAny: (app) => `${app} xin quyền truy cập các project Taiga của bạn. Ứng dụng được đọc, và chỉ được sửa nếu bạn tick ô bên dưới.`,
111
+ allowWrite: 'Cho phép cả tạo mục mới, sửa và bình luận dưới tên tôi',
112
+ allowWriteHint: 'Mọi thay đổi hiện trên Taiga dưới tên bạn. Không xóa được gì. Để trống nếu chỉ cho phép đọc.',
113
+ anApp: 'Một ứng dụng',
114
+ address: 'Địa chỉ Taiga',
115
+ addressHint: 'Địa chỉ bạn vẫn mở Taiga. Với Taiga cloud: tree.taiga.io',
116
+ badAddress: 'Không kết nối được Taiga ở địa chỉ này. Hãy dùng địa chỉ bạn vẫn mở Taiga.',
117
+ username: 'Tên đăng nhập hoặc email',
118
+ password: 'Mật khẩu',
119
+ submit: 'Đăng nhập và cho phép',
120
+ passwordNote: 'Mật khẩu được gửi tới Taiga một lần để đăng nhập. Máy chủ này không lưu mật khẩu.',
121
+ returnsTo: (host) => `Sau khi đăng nhập, bạn được đưa về ${host}.`,
122
+ returnsLocal: 'Sau khi đăng nhập, bạn được đưa về một ứng dụng trên máy này.',
123
+ onlyIfYou: 'Chỉ tiếp tục nếu chính bạn vừa bắt đầu kết nối này từ ứng dụng đó.',
124
+ rejected: 'Taiga không chấp nhận tên đăng nhập và mật khẩu này. Hãy thử lại.',
125
+ unavailable: 'Hiện chưa kết nối được tới Taiga. Hãy thử lại sau ít phút.',
126
+ tooMany: 'Bạn đã thử quá nhiều lần. Hãy đợi vài phút rồi thử lại.',
127
+ expiredTitle: 'Trang đăng nhập đã hết hạn',
128
+ expiredBody: 'Hãy quay lại ứng dụng bạn đang dùng và kết nối Taiga lại từ đầu.',
129
+ homeTitle: 'Taiga cho trợ lý AI',
130
+ pageTitle: 'Taiga cho trợ lý AI: hỏi Claude về project của bạn',
131
+ description: 'Kết nối Claude hoặc ứng dụng MCP bất kỳ với Taiga để hỏi về project, sprint, user story, task và issue bằng lời bình thường. Chỉ đọc, dùng chính tài khoản Taiga của bạn.',
132
+ readOnly: 'Chỉ đọc',
133
+ readWrite: 'Đọc · sửa nếu bạn cho phép',
134
+ descriptionWrite: 'Kết nối Claude hoặc ứng dụng MCP bất kỳ với Taiga để hỏi về project, sprint, user story, task và issue bằng lời bình thường, hoặc nhờ tạo và cập nhật chúng. Dùng chính tài khoản Taiga của bạn.',
135
+ writeFact: [
136
+ 'Chỉ sửa khi bạn cho phép',
137
+ 'Mặc định chỉ đọc. Chỉ tạo, sửa và bình luận nếu bạn tick một ô khi đăng nhập. Không xóa được gì.',
138
+ ],
139
+ writeExample: 'Tạo task “Viết release note” cho story 42 và giao cho mình.',
140
+ homeLead: (host) => `Hỏi Claude, hoặc trợ lý AI khác, về project, sprint, user story, task, issue và wiki của bạn tại ${host}, bằng lời bình thường.`,
141
+ homeLeadAny: 'Hỏi Claude, hoặc trợ lý AI khác, về project, sprint, user story, task, issue và wiki Taiga của bạn bằng lời bình thường: trên Taiga cloud hoặc Taiga riêng của công ty bạn.',
142
+ facts: [
143
+ ['Chỉ đọc', 'Chỉ tra cứu. Không thay đổi được gì trên Taiga.'],
144
+ ['Tài khoản của bạn', 'Bạn đăng nhập bằng chính mình và chỉ thấy những gì Taiga cho bạn thấy.'],
145
+ ['Không lưu gì', 'Không có cơ sở dữ liệu. Mật khẩu chỉ gửi tới Taiga một lần để đăng nhập.'],
146
+ ],
147
+ connect: 'Kết nối',
148
+ asks: 'Bạn có thể hỏi gì',
149
+ examples: [
150
+ 'Sprint hiện tại còn story nào chưa xong?',
151
+ 'Sprint này Linh đang làm những gì?',
152
+ 'Tìm story về xuất hóa đơn ra PDF.',
153
+ 'Story 42 còn những task nào?',
154
+ 'Tháng 10 mình đã xong bao nhiêu task?',
155
+ 'Các bước release được ghi ở đâu?',
156
+ ],
157
+ builtBy: 'Thực hiện bởi',
158
+ signInStepAny: 'Bấm Connect, nhập địa chỉ Taiga của bạn rồi đăng nhập bằng tài khoản Taiga.',
159
+ steps: [
160
+ 'Sao chép địa chỉ này:',
161
+ 'Trong Claude, mở Settings → Connectors → Add custom connector rồi dán địa chỉ vào.',
162
+ 'Bấm Connect và đăng nhập bằng tài khoản Taiga của bạn.',
163
+ 'Hỏi bằng lời bình thường. Claude sẽ tra câu trả lời trên Taiga.',
164
+ ],
165
+ otherApps: 'Ứng dụng AI khác',
166
+ webApps: 'Trên trình duyệt',
167
+ toolApps: 'Trong editor hoặc terminal',
168
+ copy: 'Sao chép',
169
+ copied: 'Đã sao chép',
170
+ otherAppsLead: 'Ứng dụng nào hỗ trợ MCP server từ xa cũng kết nối bằng cùng địa chỉ này, và lần đầu sẽ yêu cầu bạn đăng nhập Taiga. Menu có thể thay đổi giữa các phiên bản: nếu không thấy đúng như dưới đây, hãy tìm mục “MCP”, “connector” hoặc “tools” trong cài đặt của ứng dụng.',
171
+ howTo: {
172
+ claude: 'Làm theo các bước ở trên, trên claude.ai hoặc trong app Claude cho máy tính.',
173
+ lechat: 'Connectors → + Add Connector → tab Custom MCP Connector. Đặt tên taiga (không dấu cách), dán địa chỉ, bấm Connect rồi đăng nhập. Chỉ quản trị viên của tài khoản Le Chat mới thêm được connector; với gói Free và Pro, đó là chủ tài khoản.',
174
+ perplexity: 'Plugins ở thanh bên (hoặc Customize → Connectors) → Add custom → Add MCP connector → Remote. Đặt tên Taiga, dán địa chỉ, chọn Authentication là OAuth và Transport là Streamable HTTP, tick ô xác nhận rồi bấm Add. Sau đó mở thẻ connector để đăng nhập. Trong tổ chức, quản trị viên phải bật cho thành viên tự thêm connector trước.',
175
+ claudeCode: 'Chạy trong terminal, rồi gõ /mcp trong Claude Code và chọn taiga để đăng nhập:',
176
+ chatgpt: 'Settings → Apps & Connectors → Advanced settings: bật Developer mode. Sau đó bấm Create, dán địa chỉ, chọn OAuth và đăng nhập. Cần gói có Developer mode.',
177
+ vscode: 'Command palette → “MCP: Add Server” → HTTP, dán địa chỉ và đặt tên taiga. Hoặc thêm đoạn này vào .vscode/mcp.json. Dùng trong Copilot Chat ở chế độ Agent.',
178
+ cursor: 'Settings → MCP → Add new MCP server, hoặc thêm đoạn này vào ~/.cursor/mcp.json:',
179
+ gemini: 'Thêm đoạn này vào ~/.gemini/settings.json, rồi gõ /mcp auth taiga để đăng nhập:',
180
+ codex: 'Chạy trong terminal:',
181
+ windsurf: 'Thêm đoạn này vào ~/.codeium/windsurf/mcp_config.json:',
182
+ otherName: 'Ứng dụng MCP khác',
183
+ other: 'Dùng địa chỉ này như một server từ xa hoặc “Streamable HTTP”. Đăng nhập bằng OAuth, ứng dụng tự tìm ra; không có key nào cần dán.',
184
+ },
185
+ },
186
+ };
187
+ const escape = (value) => value.replace(/[&<>"']/g, (char) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[char] ?? char);
188
+ /**
189
+ * The colours of the author's site (kietnguyen.io.vn): blue for what can be pressed, cyan for
190
+ * focus and links, a gradient on the heading, on deep navy in the dark and near-white in the light.
191
+ */
192
+ const STYLE = `
193
+ :root { color-scheme: light dark; --bg: #f8fafc; --card: #ffffff; --inset: #eef3f9; --text: #0b1f3a; --muted: #4b5a70;
194
+ --line: rgb(11 31 58 / 0.14); --field: rgb(11 31 58 / 0.4); --primary: #2563eb; --primary-hover: #1d4ed8;
195
+ --ring: #2563eb; --glow: rgb(37 99 235 / 0.11); --link: #1d4ed8; --grid: rgb(11 31 58 / 0.035);
196
+ --grad: linear-gradient(100deg, #1d4ed8 0%, #0e7490 60%, #2563eb 100%);
197
+ --error-bg: #fdecec; --error: #a12626; }
198
+ @media (prefers-color-scheme: dark) { :root { --bg: #0a0f1a; --card: #0f1e36; --inset: #122543; --text: #e6edf5;
199
+ --muted: #a3b1c2; --line: rgb(192 199 209 / 0.16); --field: rgb(192 199 209 / 0.42); --ring: #22d3ee;
200
+ --glow: rgb(37 99 235 / 0.16); --link: #67e8f9; --grid: rgb(148 197 255 / 0.035);
201
+ --grad: linear-gradient(100deg, #60a5fa 0%, #22d3ee 55%, #a5f3fc 100%);
202
+ --error-bg: rgb(248 113 113 / 0.14); --error: #fca5a5; } }
203
+ * { box-sizing: border-box; }
204
+ body { margin: 0; min-height: 100vh; padding: 24px 16px; color: var(--text);
205
+ background:
206
+ radial-gradient(48rem 26rem at 50% -8rem, var(--glow), transparent 70%) no-repeat,
207
+ linear-gradient(var(--grid) 1px, transparent 1px) 0 0 / 100% 32px,
208
+ linear-gradient(90deg, var(--grid) 1px, transparent 1px) 0 0 / 32px 100%,
209
+ var(--bg);
210
+ font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
211
+ main { position: relative; max-width: 440px; margin: 6vh auto 0; padding: 28px 24px; background: var(--card);
212
+ border: 1px solid var(--line); border-radius: 16px; box-shadow: 0 24px 60px -32px var(--glow), 0 0 0 1px var(--glow); }
213
+ main::before { content: ""; position: absolute; top: -1px; left: 24px; right: 24px; height: 1px;
214
+ background: linear-gradient(90deg, transparent, var(--ring), transparent); opacity: 0.7; }
215
+ main.wide { max-width: 600px; }
216
+ h1 { margin: 0 0 8px; font-size: 24px; font-weight: 600; line-height: 1.25; letter-spacing: -0.01em;
217
+ color: var(--primary); background: var(--grad); -webkit-background-clip: text; background-clip: text;
218
+ -webkit-text-fill-color: transparent; }
219
+ .wide h1 { font-size: 30px; }
220
+ h2 { margin: 24px 0 10px; font-size: 13px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
221
+ color: var(--muted); }
222
+ a { color: var(--link); text-underline-offset: 2px; }
223
+ a:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 4px; }
224
+ .brand { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 20px; font-size: 14px; font-weight: 600; }
225
+ .brand img { display: block; border-radius: 8px; }
226
+ .brand .name { letter-spacing: 0.01em; }
227
+ .pill { padding: 2px 9px; font-size: 12px; font-weight: 600; color: var(--link); background: var(--inset);
228
+ border: 1px solid var(--line); border-radius: 999px; }
229
+ .lang { margin-left: auto; font-size: 13px; font-weight: 400; color: var(--muted); }
230
+ .lang a { margin-left: 8px; }
231
+ .lang [aria-current] { color: var(--text); font-weight: 600; text-decoration: none; }
232
+ .lead { font-size: 17px; }
233
+ .facts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 20px 0 0; padding: 0; list-style: none; }
234
+ .facts li { margin: 0; padding: 12px; font-size: 13px; line-height: 1.45; color: var(--muted); background: var(--inset);
235
+ border: 1px solid var(--line); border-radius: 12px; }
236
+ .facts strong { display: block; margin-bottom: 4px; font-size: 14px; color: var(--text); }
237
+ .asks { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 0 0 20px; padding: 0; list-style: none; }
238
+ .asks li { margin: 0; padding: 9px 12px; font-size: 14px; line-height: 1.4; color: var(--text);
239
+ border: 1px solid var(--line); border-left: 2px solid var(--ring); border-radius: 10px; }
240
+ footer { margin-top: 20px; padding-top: 14px; font-size: 13px; color: var(--muted); border-top: 1px solid var(--line); }
241
+ @media (max-width: 520px) { .facts, .asks { grid-template-columns: 1fr; } .wide h1 { font-size: 26px; } }
242
+ p { margin: 0 0 16px; color: var(--muted); }
243
+ label { display: block; margin: 0 0 14px; font-size: 14px; font-weight: 600; }
244
+ input { display: block; width: 100%; margin-top: 6px; padding: 10px 12px; font: inherit; color: inherit;
245
+ background: transparent; border: 1px solid var(--field); border-radius: 10px; }
246
+ input:focus-visible, button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
247
+ .check { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; font-weight: 600;
248
+ background: var(--inset); border-radius: 10px; }
249
+ .check input { flex: none; width: 18px; height: 18px; margin: 2px 0 0; padding: 0; accent-color: var(--primary); }
250
+ button { width: 100%; margin: 6px 0 16px; padding: 11px 16px; font: inherit; font-weight: 600; cursor: pointer;
251
+ color: #ffffff; background: var(--primary); border: 0; border-radius: 10px; }
252
+ button:hover { background: var(--primary-hover); }
253
+ .error { padding: 10px 12px; color: var(--error); background: var(--error-bg); border-radius: 10px; }
254
+ .note { margin: 0; font-size: 13px; }
255
+ .returns { padding: 10px 12px; font-size: 14px; color: var(--text); background: var(--inset);
256
+ border: 1px solid var(--line); border-radius: 10px; }
257
+ small { display: block; margin-top: 6px; font-size: 13px; font-weight: 400; color: var(--muted); }
258
+ ol { margin: 0 0 16px; padding-left: 22px; }
259
+ li { margin-bottom: 10px; }
260
+ li::marker { color: var(--ring); font-weight: 600; }
261
+ code { display: block; margin-top: 6px; padding: 10px 12px; overflow-wrap: anywhere; user-select: all;
262
+ font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text); background: var(--inset);
263
+ border: 1px solid var(--line); border-radius: 10px; }
264
+ pre { margin: 8px 0 0; padding: 10px 12px; overflow-x: auto; user-select: all; white-space: pre;
265
+ font: 13px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text); background: var(--inset);
266
+ border: 1px solid var(--line); border-radius: 10px; }
267
+ .apps { margin: 0 0 16px; }
268
+ .group { margin: 16px 0 8px; font-size: 13px; font-weight: 600; color: var(--text); }
269
+ .snippet { display: flex; align-items: flex-start; gap: 6px; margin-top: 6px; background: var(--inset);
270
+ border: 1px solid var(--line); border-radius: 10px; }
271
+ .snippet code, .snippet pre { flex: 1; min-width: 0; margin: 0; background: none; border: 0; }
272
+ .copy { flex: none; display: grid; place-items: center; width: 30px; height: 30px; margin: 4px 4px 4px 0; padding: 0;
273
+ color: var(--muted); background: var(--card); border: 1px solid var(--line); border-radius: 8px; }
274
+ .copy:hover { color: var(--text); background: var(--inset); }
275
+ .copy[hidden] { display: none; }
276
+ .copy svg { width: 16px; height: 16px; }
277
+ .copy .ok, .copy.done .icon { display: none; }
278
+ .copy.done .ok { display: block; color: var(--ring); }
279
+ .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
280
+ details { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 8px; }
281
+ details[open] { border-color: var(--field); }
282
+ summary { padding: 9px 12px; font-size: 14px; font-weight: 600; cursor: pointer; list-style-position: inside; }
283
+ summary::marker { color: var(--ring); }
284
+ summary:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 10px; }
285
+ details > p, details > .snippet { margin-left: 12px; margin-right: 12px; }
286
+ details > .snippet { margin-top: 8px; }
287
+ details > p { margin-top: 0; margin-bottom: 0; font-size: 14px; }
288
+ details > :last-child { margin-bottom: 12px; }
289
+ `;
290
+ const LOCALE = { en: 'en_US', vi: 'vi_VN' };
291
+ function listedHead(lang, title, listed) {
292
+ const other = lang === 'en' ? 'vi' : 'en';
293
+ return `<meta name="description" content="${escape(listed.description)}">
294
+ <link rel="canonical" href="${escape(listed.url)}">
295
+ <link rel="alternate" hreflang="en" href="${escape(listed.alternates.en)}">
296
+ <link rel="alternate" hreflang="vi" href="${escape(listed.alternates.vi)}">
297
+ <link rel="alternate" hreflang="x-default" href="${escape(listed.alternates.default)}">
298
+ <meta property="og:type" content="website">
299
+ <meta property="og:site_name" content="${escape(listed.siteName)}">
300
+ <meta property="og:title" content="${escape(title)}">
301
+ <meta property="og:description" content="${escape(listed.description)}">
302
+ <meta property="og:url" content="${escape(listed.url)}">
303
+ <meta property="og:image" content="${escape(listed.image)}">
304
+ <meta property="og:image:width" content="1200">
305
+ <meta property="og:image:height" content="630">
306
+ <meta property="og:image:alt" content="${escape(listed.siteName)}">
307
+ <meta property="og:locale" content="${LOCALE[lang]}">
308
+ <meta property="og:locale:alternate" content="${LOCALE[other]}">
309
+ <meta name="twitter:card" content="summary_large_image">
310
+ <meta name="twitter:title" content="${escape(title)}">
311
+ <meta name="twitter:description" content="${escape(listed.description)}">
312
+ <meta name="twitter:image" content="${escape(listed.image)}">
313
+ `;
314
+ }
315
+ function page(lang, title, body, options = {}) {
316
+ return `<!doctype html>
317
+ <html lang="${lang}">
318
+ <head>
319
+ <meta charset="utf-8">
320
+ <meta name="viewport" content="width=device-width, initial-scale=1">
321
+ <meta name="robots" content="${options.listed ? 'index, follow' : 'noindex'}">
322
+ <title>${escape(title)}</title>
323
+ ${options.listed ? listedHead(lang, title, options.listed) : ''}<meta name="theme-color" content="#f8fafc" media="(prefers-color-scheme: light)">
324
+ <meta name="theme-color" content="#0a0f1a" media="(prefers-color-scheme: dark)">
325
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml">
326
+ <link rel="icon" href="/favicon.ico" sizes="32x32">
327
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png">
328
+ <style>${STYLE}</style>
329
+ </head>
330
+ <body>
331
+ <main${options.wide ? ' class="wide"' : ''}>
332
+ ${body}
333
+ </main>
334
+ ${options.script ? `<script>${COPY_SCRIPT}</script>\n` : ''}</body>
335
+ </html>
336
+ `;
337
+ }
338
+ /** The mark and the name, at the top of every page. `extra` goes to the right of them. */
339
+ const brand = (extra = '') => `<div class="brand"><img src="/favicon.svg" width="32" height="32" alt=""><span class="name">taiga-mcp</span>${extra}</div>`;
340
+ export function loginPage(view) {
341
+ const t = TEXT[view.lang];
342
+ const app = view.app ?? t.anApp;
343
+ const hostname = view.returnsTo.replace(/:\d+$/, '');
344
+ const returns = ['localhost', '127.0.0.1', '[::1]'].includes(hostname)
345
+ ? t.returnsLocal
346
+ : t.returnsTo(view.returnsTo);
347
+ const asksAddress = view.fixedHost === undefined;
348
+ const address = view.address ?? '';
349
+ // The cursor starts in the first field that still needs typing.
350
+ const focusAddress = asksAddress && address === '';
351
+ const addressField = `<label>${escape(t.address)}
352
+ <input name="address" inputmode="url" autocomplete="url" autocapitalize="none" spellcheck="false" required placeholder="https://taiga.example.com"${focusAddress ? ' autofocus' : ''} value="${escape(address)}">
353
+ <small>${escape(t.addressHint)}</small>
354
+ </label>
355
+ `;
356
+ return page(view.lang, t.signInTitle, `${brand()}
357
+ <h1>${escape(t.signInTitle)}</h1>
358
+ <p>${escape(view.mayWrite
359
+ ? view.fixedHost === undefined
360
+ ? t.wantsWriteAny(app)
361
+ : t.wantsWrite(app, view.fixedHost)
362
+ : view.fixedHost === undefined
363
+ ? t.wantsAny(app)
364
+ : t.wants(app, view.fixedHost))}</p>
365
+ <p class="returns">${escape(returns)} ${escape(t.onlyIfYou)}</p>
366
+ ${view.error ? `<p class="error" role="alert">${escape(t[view.error])}</p>` : ''}
367
+ <form method="post" action="/login">
368
+ <input type="hidden" name="request" value="${escape(view.request)}">
369
+ ${asksAddress ? addressField : ''}<label>${escape(t.username)}
370
+ <input name="username" autocomplete="username" autocapitalize="none" spellcheck="false" required${focusAddress ? '' : ' autofocus'} value="${escape(view.username ?? '')}">
371
+ </label>
372
+ <label>${escape(t.password)}
373
+ <input name="password" type="password" autocomplete="current-password" required>
374
+ </label>
375
+ ${view.mayWrite
376
+ ? `<label class="check"><input type="checkbox" name="write"${view.write ? ' checked' : ''}><span>${escape(t.allowWrite)}<small>${escape(t.allowWriteHint)}</small></span></label>
377
+ `
378
+ : ''}<button type="submit">${escape(t.submit)}</button>
379
+ </form>
380
+ <p class="note">${escape(t.passwordNote)}</p>`);
381
+ }
382
+ export function expiredPage(lang) {
383
+ const t = TEXT[lang];
384
+ return page(lang, t.expiredTitle, `${brand()}\n<h1>${escape(t.expiredTitle)}</h1>\n<p>${escape(t.expiredBody)}</p>`);
385
+ }
386
+ /**
387
+ * How to add the server in AI apps other than Claude, each as a closed section: what to do,
388
+ * and the command or configuration to copy, with this server's address already in it.
389
+ */
390
+ function otherApps(t, mcpUrl) {
391
+ const how = t.howTo;
392
+ const json = (value) => JSON.stringify(value, null, 2);
393
+ const web = [
394
+ ['Claude', how.claude],
395
+ ['ChatGPT', how.chatgpt],
396
+ ['Le Chat (Mistral)', how.lechat],
397
+ ['Perplexity', how.perplexity],
398
+ ];
399
+ const tools = [
400
+ ['Claude Code', how.claudeCode, `claude mcp add --transport http taiga ${mcpUrl}`],
401
+ ['VS Code (GitHub Copilot)', how.vscode, json({ servers: { taiga: { type: 'http', url: mcpUrl } } })],
402
+ ['Cursor', how.cursor, json({ mcpServers: { taiga: { url: mcpUrl } } })],
403
+ ['Windsurf', how.windsurf, json({ mcpServers: { taiga: { serverUrl: mcpUrl } } })],
404
+ ['Gemini CLI', how.gemini, json({ mcpServers: { taiga: { httpUrl: mcpUrl } } })],
405
+ ['Codex CLI', how.codex, `codex mcp add taiga --url ${mcpUrl}\ncodex mcp login taiga`],
406
+ [how.otherName, how.other],
407
+ ];
408
+ let n = 0;
409
+ const details = (apps) => apps
410
+ .map(([name, what, copy]) => {
411
+ n += 1;
412
+ return `<details><summary>${escape(name)}</summary><p>${escape(what)}</p>${copy === undefined ? '' : snippet('pre', `copy-${n}`, copy, t.copy)}</details>`;
413
+ })
414
+ .join('\n');
415
+ return `<h3 class="group">${escape(t.webApps)}</h3>
416
+ <div class="apps">
417
+ ${details(web)}
418
+ </div>
419
+ <h3 class="group">${escape(t.toolApps)}</h3>
420
+ <div class="apps">
421
+ ${details(tools)}
422
+ </div>`;
423
+ }
424
+ /** Who made this, for the foot of the front page. */
425
+ const AUTHOR = { name: 'Kiet Nguyen', url: 'https://kietnguyen.io.vn' };
426
+ /**
427
+ * The front page: what this is, how to connect, and what to ask. It is the one page meant to be
428
+ * found, so it carries a description and a preview image. `fixedHost` as in the sign-in page;
429
+ * `publicUrl` is the address of this server, `chosen` the language picked with `?lang=`, if any.
430
+ */
431
+ export function homePage(lang, fixedHost, publicUrl, chosen,
432
+ /** The server offers changes to whoever allows them at sign-in. */
433
+ allowWrites = false) {
434
+ const t = TEXT[lang];
435
+ const facts = allowWrites ? [t.writeFact, ...t.facts.slice(1)] : t.facts;
436
+ const examples = allowWrites ? [...t.examples, t.writeExample] : t.examples;
437
+ const at = (path) => new URL(path, publicUrl).href;
438
+ const [copy, add, signIn, ask] = t.steps;
439
+ const steps = [add, fixedHost === undefined ? t.signInStepAny : signIn, ask];
440
+ const language = (code, label) => `<a href="/?lang=${code}" hreflang="${code}" lang="${code}"${code === lang ? ' aria-current="true"' : ''}>${label}</a>`;
441
+ return page(lang, t.pageTitle, `${brand(`<span class="pill">${escape(allowWrites ? t.readWrite : t.readOnly)}</span><nav class="lang" aria-label="Language">${language('en', 'English')}${language('vi', 'Tiếng Việt')}</nav>`)}
442
+ <h1>${escape(t.homeTitle)}</h1>
443
+ <p class="lead">${escape(fixedHost === undefined ? t.homeLeadAny : t.homeLead(fixedHost))}</p>
444
+ <ul class="facts">
445
+ ${facts.map(([name, what]) => `<li><strong>${escape(name)}</strong>${escape(what)}</li>`).join('\n')}
446
+ </ul>
447
+ <h2>${escape(t.connect)}</h2>
448
+ <ol>
449
+ <li>${escape(copy)}${snippet('code', 'copy-address', at('/mcp'), t.copy)}</li>
450
+ ${steps.map((step) => `<li>${escape(step)}</li>`).join('\n')}
451
+ </ol>
452
+ <h2>${escape(t.asks)}</h2>
453
+ <ul class="asks">
454
+ ${examples.map((example) => `<li>${escape(example)}</li>`).join('\n')}
455
+ </ul>
456
+ <h2>${escape(t.otherApps)}</h2>
457
+ <p class="note">${escape(t.otherAppsLead)}</p>
458
+ ${otherApps(t, at('/mcp'))}
459
+ <p id="copy-status" class="sr-only" role="status" data-done="${escape(t.copied)}"></p>
460
+ <footer>taiga-mcp ${escape(SERVER_INFO.version)} · ${escape(t.builtBy)} <a href="${AUTHOR.url}" rel="author">${AUTHOR.name}</a></footer>`, {
461
+ wide: true,
462
+ script: true,
463
+ listed: {
464
+ description: allowWrites ? t.descriptionWrite : t.description,
465
+ url: at(chosen === undefined ? '/' : `/?lang=${chosen}`),
466
+ alternates: { en: at('/?lang=en'), vi: at('/?lang=vi'), default: at('/') },
467
+ image: at('/og.png'),
468
+ siteName: t.homeTitle,
469
+ },
470
+ });
471
+ }
472
+ //# sourceMappingURL=pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/remote/pages.ts"],"names":[],"mappings":"AAAA,iGAAiG;AAEjG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;GAIG;AACH,MAAM,WAAW,GACf,2DAA2D;IAC3D,oFAAoF;IACpF,mGAAmG;IACnG,kGAAkG;IAClG,yEAAyE;IACzE,+FAA+F,CAAC;AAElG,uEAAuE;AACvE,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;AAEnG,MAAM,SAAS,GACb,wPAAwP;IACxP,qMAAqM,CAAC;AAExM,8EAA8E;AAC9E,SAAS,OAAO,CAAC,GAAmB,EAAE,EAAU,EAAE,IAAY,EAAE,KAAa;IAC3E,OAAO,yBAAyB,GAAG,QAAQ,EAAE,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,kDAAkD,EAAE,iBAAiB,MAAM,CAAC,KAAK,CAAC,YAAY,MAAM,CAAC,KAAK,CAAC,YAAY,SAAS,iBAAiB,CAAC;AAC1N,CAAC;AAID,MAAM,IAAI,GAAG;IACX,EAAE,EAAE;QACF,WAAW,EAAE,kBAAkB;QAC/B,KAAK,EAAE,CAAC,GAAW,EAAE,IAAY,EAAE,EAAE,CACnC,GAAG,GAAG,6CAA6C,IAAI,uDAAuD;QAChH,QAAQ,EAAE,CAAC,GAAW,EAAE,EAAE,CACxB,GAAG,GAAG,6FAA6F;QACrG,UAAU,EAAE,CAAC,GAAW,EAAE,IAAY,EAAE,EAAE,CACxC,GAAG,GAAG,mDAAmD,IAAI,qEAAqE;QACpI,aAAa,EAAE,CAAC,GAAW,EAAE,EAAE,CAC7B,GAAG,GAAG,iHAAiH;QACzH,UAAU,EAAE,0DAA0D;QACtE,cAAc,EACZ,2GAA2G;QAC7G,KAAK,EAAE,QAAQ;QACf,OAAO,EAAE,eAAe;QACxB,WAAW,EAAE,+DAA+D;QAC5E,UAAU,EAAE,+EAA+E;QAC3F,QAAQ,EAAE,mBAAmB;QAC7B,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,mBAAmB;QAC3B,YAAY,EAAE,oFAAoF;QAClG,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,yCAAyC,IAAI,GAAG;QAC7E,YAAY,EAAE,gEAAgE;QAC9E,SAAS,EAAE,uEAAuE;QAClF,QAAQ,EAAE,6DAA6D;QACvE,WAAW,EAAE,6DAA6D;QAC1E,OAAO,EAAE,sDAAsD;QAC/D,YAAY,EAAE,+BAA+B;QAC7C,WAAW,EAAE,8DAA8D;QAC3E,SAAS,EAAE,yBAAyB;QACpC,SAAS,EAAE,yDAAyD;QACpE,WAAW,EACT,gKAAgK;QAClK,QAAQ,EAAE,WAAW;QACrB,SAAS,EAAE,6BAA6B;QACxC,gBAAgB,EACd,wLAAwL;QAC1L,SAAS,EAAE;YACT,2BAA2B;YAC3B,kHAAkH;SACnH;QACD,YAAY,EAAE,0EAA0E;QACxF,QAAQ,EAAE,CAAC,IAAY,EAAE,EAAE,CACzB,8GAA8G,IAAI,mBAAmB;QACvI,WAAW,EACT,8KAA8K;QAChL,KAAK,EAAE;YACL,CAAC,WAAW,EAAE,yDAAyD,CAAC;YACxE,CAAC,kBAAkB,EAAE,0DAA0D,CAAC;YAChF,CAAC,gBAAgB,EAAE,4DAA4D,CAAC;SACjF;QACD,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,kBAAkB;QACxB,QAAQ,EAAE;YACR,uDAAuD;YACvD,sCAAsC;YACtC,iDAAiD;YACjD,mCAAmC;YACnC,yCAAyC;YACzC,4CAA4C;SAC7C;QACD,OAAO,EAAE,UAAU;QACnB,aAAa,EAAE,sFAAsF;QACrG,KAAK,EAAE;YACL,oBAAoB;YACpB,sFAAsF;YACtF,oDAAoD;YACpD,0DAA0D;SAC3D;QACD,SAAS,EAAE,eAAe;QAC1B,OAAO,EAAE,gBAAgB;QACzB,QAAQ,EAAE,4BAA4B;QACtC,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,QAAQ;QAChB,aAAa,EACX,iPAAiP;QACnP,KAAK,EAAE;YACL,MAAM,EAAE,6DAA6D;YACrE,MAAM,EACJ,sPAAsP;YACxP,UAAU,EACR,wWAAwW;YAC1W,UAAU,EAAE,2EAA2E;YACvF,OAAO,EACL,uKAAuK;YACzK,MAAM,EACJ,2JAA2J;YAC7J,MAAM,EAAE,yEAAyE;YACjF,MAAM,EAAE,4EAA4E;YACpF,KAAK,EAAE,gBAAgB;YACvB,QAAQ,EAAE,kDAAkD;YAC5D,SAAS,EAAE,gBAAgB;YAC3B,KAAK,EACH,+HAA+H;SAClI;KACF;IACD,EAAE,EAAE;QACF,WAAW,EAAE,iBAAiB;QAC9B,KAAK,EAAE,CAAC,GAAW,EAAE,IAAY,EAAE,EAAE,CACnC,GAAG,GAAG,gDAAgD,IAAI,gDAAgD;QAC5G,QAAQ,EAAE,CAAC,GAAW,EAAE,EAAE,CACxB,GAAG,GAAG,wFAAwF;QAChG,UAAU,EAAE,CAAC,GAAW,EAAE,IAAY,EAAE,EAAE,CACxC,GAAG,GAAG,qDAAqD,IAAI,+DAA+D;QAChI,aAAa,EAAE,CAAC,GAAW,EAAE,EAAE,CAC7B,GAAG,GAAG,4GAA4G;QACpH,UAAU,EAAE,wDAAwD;QACpE,cAAc,EACZ,8FAA8F;QAChG,KAAK,EAAE,cAAc;QACrB,OAAO,EAAE,eAAe;QACxB,WAAW,EAAE,0DAA0D;QACvE,UAAU,EAAE,4EAA4E;QACxF,QAAQ,EAAE,0BAA0B;QACpC,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,uBAAuB;QAC/B,YAAY,EAAE,mFAAmF;QACjG,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,sCAAsC,IAAI,GAAG;QAC1E,YAAY,EAAE,+DAA+D;QAC7E,SAAS,EAAE,oEAAoE;QAC/E,QAAQ,EAAE,mEAAmE;QAC7E,WAAW,EAAE,4DAA4D;QACzE,OAAO,EAAE,yDAAyD;QAClE,YAAY,EAAE,4BAA4B;QAC1C,WAAW,EAAE,kEAAkE;QAC/E,SAAS,EAAE,qBAAqB;QAChC,SAAS,EAAE,oDAAoD;QAC/D,WAAW,EACT,2KAA2K;QAC7K,QAAQ,EAAE,SAAS;QACnB,SAAS,EAAE,4BAA4B;QACvC,gBAAgB,EACd,kMAAkM;QACpM,SAAS,EAAE;YACT,0BAA0B;YAC1B,kGAAkG;SACnG;QACD,YAAY,EAAE,6DAA6D;QAC3E,QAAQ,EAAE,CAAC,IAAY,EAAE,EAAE,CACzB,oGAAoG,IAAI,yBAAyB;QACnI,WAAW,EACT,6KAA6K;QAC/K,KAAK,EAAE;YACL,CAAC,SAAS,EAAE,iDAAiD,CAAC;YAC9D,CAAC,mBAAmB,EAAE,wEAAwE,CAAC;YAC/F,CAAC,cAAc,EAAE,0EAA0E,CAAC;SAC7F;QACD,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,mBAAmB;QACzB,QAAQ,EAAE;YACR,0CAA0C;YAC1C,oCAAoC;YACpC,mCAAmC;YACnC,8BAA8B;YAC9B,uCAAuC;YACvC,kCAAkC;SACnC;QACD,OAAO,EAAE,eAAe;QACxB,aAAa,EAAE,6EAA6E;QAC5F,KAAK,EAAE;YACL,uBAAuB;YACvB,oFAAoF;YACpF,wDAAwD;YACxD,iEAAiE;SAClE;QACD,SAAS,EAAE,kBAAkB;QAC7B,OAAO,EAAE,kBAAkB;QAC3B,QAAQ,EAAE,4BAA4B;QACtC,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,aAAa;QACrB,aAAa,EACX,wQAAwQ;QAC1Q,KAAK,EAAE;YACL,MAAM,EAAE,8EAA8E;YACtF,MAAM,EACJ,6OAA6O;YAC/O,UAAU,EACR,yUAAyU;YAC3U,UAAU,EAAE,gFAAgF;YAC5F,OAAO,EACL,2JAA2J;YAC7J,MAAM,EACJ,4JAA4J;YAC9J,MAAM,EAAE,iFAAiF;YACzF,MAAM,EAAE,iFAAiF;YACzF,KAAK,EAAE,sBAAsB;YAC7B,QAAQ,EAAE,wDAAwD;YAClE,SAAS,EAAE,mBAAmB;YAC9B,KAAK,EACH,mIAAmI;SACtI;KACF;CACO,CAAC;AAIX,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE,CAC/B,KAAK,CAAC,OAAO,CACX,UAAU,EACV,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAClG,CAAC;AAEJ;;;GAGG;AACH,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiGb,CAAC;AAaF,MAAM,MAAM,GAAyB,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;AAElE,SAAS,UAAU,CAAC,IAAU,EAAE,KAAa,EAAE,MAAc;IAC3D,MAAM,KAAK,GAAS,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,OAAO,qCAAqC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;8BAC1C,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;4CACJ,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;4CAC5B,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;mDACrB,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC;;yCAE3C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;qCAC3B,MAAM,CAAC,KAAK,CAAC;2CACP,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;mCAClC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;qCAChB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;;;yCAGhB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;sCAC1B,MAAM,CAAC,IAAI,CAAC;gDACF,MAAM,CAAC,KAAK,CAAC;;sCAEvB,MAAM,CAAC,KAAK,CAAC;4CACP,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;sCAChC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;CACzD,CAAC;AACF,CAAC;AAED,SAAS,IAAI,CACX,IAAU,EACV,KAAa,EACb,IAAY,EACZ,UAAiE,EAAE;IAEnE,OAAO;cACK,IAAI;;;;+BAIa,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;SAClE,MAAM,CAAC,KAAK,CAAC;EACpB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;;;;;SAKtD,KAAK;;;OAGP,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE;EACxC,IAAI;;EAEJ,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,WAAW,aAAa,CAAC,CAAC,CAAC,EAAE;;CAE1D,CAAC;AACF,CAAC;AAED,0FAA0F;AAC1F,MAAM,KAAK,GAAG,CAAC,KAAK,GAAG,EAAE,EAAE,EAAE,CAC3B,+GAA+G,KAAK,QAAQ,CAAC;AA0B/H,MAAM,UAAU,SAAS,CAAC,IAAe;IACvC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACpE,CAAC,CAAC,CAAC,CAAC,YAAY;QAChB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;IACnC,gEAAgE;IAChE,MAAM,YAAY,GAAG,WAAW,IAAI,OAAO,KAAK,EAAE,CAAC;IACnD,MAAM,YAAY,GAAG,UAAU,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;oJACkG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,WAAW,MAAM,CAAC,OAAO,CAAC;SACrM,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;;CAE7B,CAAC;IACA,OAAO,IAAI,CACT,IAAI,CAAC,IAAI,EACT,CAAC,CAAC,WAAW,EACb,GAAG,KAAK,EAAE;MACR,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;KACtB,MAAM,CACL,IAAI,CAAC,QAAQ;QACX,CAAC,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS;YAC5B,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC;YACtB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC;QACrC,CAAC,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS;YAC5B,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YACjB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CACnC;qBACgB,MAAM,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;EACzD,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,iCAAiC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;;6CAEnC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;EAC/D,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,UAAU,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;kGACqC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,WAAW,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;;SAE/J,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;;;EAIzB,IAAI,CAAC,QAAQ;QACX,CAAC,CAAC,2DAA2D,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC;CAC5J;QACG,CAAC,CAAC,EACN,yBAAyB,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;;kBAEvB,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAC3C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAU;IACpC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,IAAI,CACT,IAAI,EACJ,CAAC,CAAC,YAAY,EACd,GAAG,KAAK,EAAE,SAAS,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAClF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,CAAsB,EAAE,MAAc;IACvD,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC;IACpB,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAEhE,MAAM,GAAG,GAAmB;QAC1B,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC;QACtB,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC;QACxB,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC;QACjC,CAAC,YAAY,EAAE,GAAG,CAAC,UAAU,CAAC;KAC/B,CAAC;IACF,MAAM,KAAK,GAAmB;QAC5B,CAAC,aAAa,EAAE,GAAG,CAAC,UAAU,EAAE,yCAAyC,MAAM,EAAE,CAAC;QAClF,CAAC,0BAA0B,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QACrG,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QACxE,CAAC,UAAU,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QAClF,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QAChF,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,EAAE,6BAA6B,MAAM,yBAAyB,CAAC;QACtF,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC;KAC3B,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,OAAO,GAAG,CAAC,IAAoB,EAAE,EAAE,CACvC,IAAI;SACD,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QAC1B,CAAC,IAAI,CAAC,CAAC;QACP,OAAO,qBAAqB,MAAM,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;IAC7J,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,OAAO,qBAAqB,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;;EAE7C,OAAO,CAAC,GAAG,CAAC;;oBAEM,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;;EAEpC,OAAO,CAAC,KAAK,CAAC;OACT,CAAC;AACR,CAAC;AAED,qDAAqD;AACrD,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,0BAA0B,EAAW,CAAC;AAEjF;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,IAAU,EACV,SAA6B,EAC7B,SAAc,EACd,MAAyB;AACzB,mEAAmE;AACnE,WAAW,GAAG,KAAK;IAEnB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACzE,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC5E,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC;IAC3D,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;IACzC,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7E,MAAM,QAAQ,GAAG,CAAC,IAAU,EAAE,KAAa,EAAE,EAAE,CAC7C,mBAAmB,IAAI,eAAe,IAAI,WAAW,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,MAAM,CAAC;IAC1H,OAAO,IAAI,CACT,IAAI,EACJ,CAAC,CAAC,SAAS,EACX,GAAG,KAAK,CAAC,sBAAsB,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,kDAAkD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;MAC9L,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;kBACP,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;;EAEvF,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,MAAM,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;MAE9F,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;;MAEjB,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;EACtE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;MAEtD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;;EAElB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;MAE/D,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;kBACP,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;EACvC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;+DACqC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;oBAC3D,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,MAAM,CAAC,GAAG,kBAAkB,MAAM,CAAC,IAAI,eAAe,EACrI;QACE,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE;YACN,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW;YAC7D,GAAG,EAAE,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,MAAM,EAAE,CAAC;YACxD,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE;YAC1E,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC;YACpB,QAAQ,EAAE,CAAC,CAAC,SAAS;SACtB;KACF,CACF,CAAC;AACJ,CAAC"}