@grigoreo-dev/otask-mcp 1.2.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.
- package/README.md +168 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -0
- package/dist/docs/parser.d.ts +3 -0
- package/dist/docs/parser.d.ts.map +1 -0
- package/dist/docs/parser.js +123 -0
- package/dist/docs/parser.js.map +1 -0
- package/dist/docs/types.d.ts +29 -0
- package/dist/docs/types.d.ts.map +1 -0
- package/dist/docs/types.js +2 -0
- package/dist/docs/types.js.map +1 -0
- package/dist/http-server.d.ts +3 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +92 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-http.d.ts +3 -0
- package/dist/mcp-http.d.ts.map +1 -0
- package/dist/mcp-http.js +98 -0
- package/dist/mcp-http.js.map +1 -0
- package/dist/proxy/headers.d.ts +3 -0
- package/dist/proxy/headers.d.ts.map +1 -0
- package/dist/proxy/headers.js +37 -0
- package/dist/proxy/headers.js.map +1 -0
- package/dist/proxy/read-body.d.ts +3 -0
- package/dist/proxy/read-body.d.ts.map +1 -0
- package/dist/proxy/read-body.js +18 -0
- package/dist/proxy/read-body.js.map +1 -0
- package/dist/schemas/task.d.ts +241 -0
- package/dist/schemas/task.d.ts.map +1 -0
- package/dist/schemas/task.js +95 -0
- package/dist/schemas/task.js.map +1 -0
- package/dist/schemas/workspace.d.ts +66 -0
- package/dist/schemas/workspace.d.ts.map +1 -0
- package/dist/schemas/workspace.js +30 -0
- package/dist/schemas/workspace.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +13 -0
- package/dist/server.js.map +1 -0
- package/dist/services/api.d.ts +23 -0
- package/dist/services/api.d.ts.map +1 -0
- package/dist/services/api.js +215 -0
- package/dist/services/api.js.map +1 -0
- package/dist/services/auth.d.ts +23 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/auth.js +122 -0
- package/dist/services/auth.js.map +1 -0
- package/dist/services/client.d.ts +21 -0
- package/dist/services/client.d.ts.map +1 -0
- package/dist/services/client.js +17 -0
- package/dist/services/client.js.map +1 -0
- package/dist/services/format.d.ts +6 -0
- package/dist/services/format.d.ts.map +1 -0
- package/dist/services/format.js +8 -0
- package/dist/services/format.js.map +1 -0
- package/dist/services/http-auth-context.d.ts +7 -0
- package/dist/services/http-auth-context.d.ts.map +1 -0
- package/dist/services/http-auth-context.js +9 -0
- package/dist/services/http-auth-context.js.map +1 -0
- package/dist/services/project-guard.d.ts +35 -0
- package/dist/services/project-guard.d.ts.map +1 -0
- package/dist/services/project-guard.js +85 -0
- package/dist/services/project-guard.js.map +1 -0
- package/dist/services/task-mapper.d.ts +90 -0
- package/dist/services/task-mapper.d.ts.map +1 -0
- package/dist/services/task-mapper.js +148 -0
- package/dist/services/task-mapper.js.map +1 -0
- package/dist/tools/add-comment.d.ts +4 -0
- package/dist/tools/add-comment.d.ts.map +1 -0
- package/dist/tools/add-comment.js +43 -0
- package/dist/tools/add-comment.js.map +1 -0
- package/dist/tools/archive-task.d.ts +4 -0
- package/dist/tools/archive-task.d.ts.map +1 -0
- package/dist/tools/archive-task.js +43 -0
- package/dist/tools/archive-task.js.map +1 -0
- package/dist/tools/create-task.d.ts +4 -0
- package/dist/tools/create-task.d.ts.map +1 -0
- package/dist/tools/create-task.js +45 -0
- package/dist/tools/create-task.js.map +1 -0
- package/dist/tools/get-task.d.ts +4 -0
- package/dist/tools/get-task.d.ts.map +1 -0
- package/dist/tools/get-task.js +44 -0
- package/dist/tools/get-task.js.map +1 -0
- package/dist/tools/helpers.d.ts +15 -0
- package/dist/tools/helpers.d.ts.map +1 -0
- package/dist/tools/helpers.js +19 -0
- package/dist/tools/helpers.js.map +1 -0
- package/dist/tools/list-board.d.ts +4 -0
- package/dist/tools/list-board.d.ts.map +1 -0
- package/dist/tools/list-board.js +52 -0
- package/dist/tools/list-board.js.map +1 -0
- package/dist/tools/list-comments.d.ts +4 -0
- package/dist/tools/list-comments.d.ts.map +1 -0
- package/dist/tools/list-comments.js +41 -0
- package/dist/tools/list-comments.js.map +1 -0
- package/dist/tools/list-members.d.ts +4 -0
- package/dist/tools/list-members.d.ts.map +1 -0
- package/dist/tools/list-members.js +39 -0
- package/dist/tools/list-members.js.map +1 -0
- package/dist/tools/list-project-tasks.d.ts +4 -0
- package/dist/tools/list-project-tasks.d.ts.map +1 -0
- package/dist/tools/list-project-tasks.js +52 -0
- package/dist/tools/list-project-tasks.js.map +1 -0
- package/dist/tools/list-projects.d.ts +4 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +40 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/list-tags.d.ts +4 -0
- package/dist/tools/list-tags.d.ts.map +1 -0
- package/dist/tools/list-tags.js +39 -0
- package/dist/tools/list-tags.js.map +1 -0
- package/dist/tools/move-task.d.ts +4 -0
- package/dist/tools/move-task.d.ts.map +1 -0
- package/dist/tools/move-task.js +59 -0
- package/dist/tools/move-task.js.map +1 -0
- package/dist/tools/register.d.ts +4 -0
- package/dist/tools/register.d.ts.map +1 -0
- package/dist/tools/register.js +8 -0
- package/dist/tools/register.js.map +1 -0
- package/dist/tools/registry.d.ts +15 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +28 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +29 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/update-task.d.ts +4 -0
- package/dist/tools/update-task.d.ts.map +1 -0
- package/dist/tools/update-task.js +96 -0
- package/dist/tools/update-task.js.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# otask-mcp-server
|
|
2
|
+
|
|
3
|
+
[MCP-сервер](https://modelcontextprotocol.io) для [O!task API](https://api.otask.ru/docs). Отдаёт операции O!task (воркспейс/задачи) как MCP-инструменты для агентов (Cursor, OpenCode, n8n MCP Client Tool).
|
|
4
|
+
|
|
5
|
+
## Установка
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g @grigoreo-dev/otask-mcp
|
|
9
|
+
# или: npx @grigoreo-dev/otask-mcp
|
|
10
|
+
# HTTP: npx otask-mcp-http # bin name stays unscoped
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Публикация на npm: push tag `vX.Y.Z` (версия в tag = `package.json`). CI: `.github/workflows/publish.yml` (OIDC Trusted Publisher, без `NPM_TOKEN`).
|
|
14
|
+
|
|
15
|
+
## Режимы
|
|
16
|
+
|
|
17
|
+
| Режим | Команда | Как выбирается | Авторизация клиента |
|
|
18
|
+
|-------|---------|----------------|---------------------|
|
|
19
|
+
| **stdio** | `bun start` | Локальный процесс; всегда берёт credentials из env сервера | Без HTTP; задайте `OTASK_*` в env |
|
|
20
|
+
| **HTTP gateway** | `bun run start:http` | Есть `OTASK_AUTH_KEY` **или** `OTASK_EMAIL`+`OTASK_PASSWORD` | `Authorization: Bearer <MCP_AUTH_TOKEN>` |
|
|
21
|
+
| **HTTP passthrough** | `bun run start:http` | Нет credentials `OTASK_*` в env | `Authorization: Bearer <токен O!task>` (проксируется в API) |
|
|
22
|
+
|
|
23
|
+
Определение: `hasServerOtaskCredentials()` — gateway, если задан статический ключ или email+password; иначе passthrough. Проверка: `GET /health` → `authMode: "gateway" | "passthrough"`.
|
|
24
|
+
|
|
25
|
+
stdio требует `OTASK_*` (без них падает при старте). HTTP gateway требует `MCP_AUTH_TOKEN`, если заданы `OTASK_*` (иначе падает при старте).
|
|
26
|
+
|
|
27
|
+
## Переменные окружения
|
|
28
|
+
|
|
29
|
+
| Переменная | Где используется | Когда обязательна | Назначение |
|
|
30
|
+
|------------|------------------|-------------------|------------|
|
|
31
|
+
| `OTASK_AUTH_KEY` | stdio, HTTP gateway | stdio **или** gateway (альтернатива: email/password) | Статический O!task Bearer, которым пользуется сервер |
|
|
32
|
+
| `OTASK_EMAIL` | stdio, HTTP gateway | вместе с `OTASK_PASSWORD` как альтернатива ключу | Логин для получения токена |
|
|
33
|
+
| `OTASK_PASSWORD` | stdio, HTTP gateway | вместе с `OTASK_EMAIL` | Пароль для логина |
|
|
34
|
+
| `MCP_AUTH_TOKEN` | HTTP gateway | режим gateway | Общий секрет, который должен отправлять клиент; **не** токен O!task |
|
|
35
|
+
| `OTASK_ALLOWED_PROJECTS` | stdio, HTTP gateway | опционально | Список slug и/или числовых ID проектов через запятую |
|
|
36
|
+
| `PORT` | HTTP | опционально (по умолчанию `3847`) | Порт прослушивания |
|
|
37
|
+
| `HOST` | HTTP | опционально (по умолчанию `0.0.0.0`) | Адрес привязки |
|
|
38
|
+
|
|
39
|
+
## HTTP-заголовки
|
|
40
|
+
|
|
41
|
+
| Заголовок | Режим | Назначение |
|
|
42
|
+
|-----------|-------|------------|
|
|
43
|
+
| `Authorization: Bearer …` | gateway | Должен совпадать с `MCP_AUTH_TOKEN` |
|
|
44
|
+
| `Authorization: Bearer …` | passthrough | Токен O!task API; проксируется на каждый запрос к API |
|
|
45
|
+
| `X-Otask-Allowed-Projects` | **только passthrough** | Slug/ID через запятую; в gateway игнорируется (используйте env) |
|
|
46
|
+
|
|
47
|
+
## Эндпоинты (HTTP)
|
|
48
|
+
|
|
49
|
+
| | |
|
|
50
|
+
|---|---|
|
|
51
|
+
| MCP | `POST`/`GET` `/mcp` (Streamable HTTP) |
|
|
52
|
+
| Health | `GET /health` → `{ ok, mode, authMode, projectGuard }`, где `projectGuard` — `"env" \| "header" \| "off"` |
|
|
53
|
+
|
|
54
|
+
Пример деплоя: `https://otask-mcp.grigoreo.dev/mcp` (порт `3847` в Docker).
|
|
55
|
+
|
|
56
|
+
## Примеры для n8n
|
|
57
|
+
|
|
58
|
+
### Gateway (credentials O!task хранит сервер)
|
|
59
|
+
|
|
60
|
+
Env сервера:
|
|
61
|
+
|
|
62
|
+
```env
|
|
63
|
+
OTASK_AUTH_KEY=...
|
|
64
|
+
MCP_AUTH_TOKEN=super-secret-mcp-token
|
|
65
|
+
OTASK_ALLOWED_PROJECTS=my-project,42
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
n8n **MCP Client Tool**:
|
|
69
|
+
|
|
70
|
+
- URL: `https://otask-mcp.example/mcp`
|
|
71
|
+
- Transport: **HTTP Streamable**
|
|
72
|
+
- Credential / header: `Authorization: Bearer super-secret-mcp-token`
|
|
73
|
+
- **Не** отправляйте `X-Otask-Allowed-Projects` (allow-list берётся из env сервера)
|
|
74
|
+
|
|
75
|
+
### Passthrough (токен O!task у клиента)
|
|
76
|
+
|
|
77
|
+
Env сервера: без `OTASK_AUTH_KEY` / `OTASK_EMAIL` / `OTASK_PASSWORD`.
|
|
78
|
+
|
|
79
|
+
n8n **MCP Client Tool**:
|
|
80
|
+
|
|
81
|
+
- URL: `https://otask-mcp.example/mcp`
|
|
82
|
+
- Transport: **HTTP Streamable**
|
|
83
|
+
- Credential / header: `Authorization: Bearer <тот же токен, что и для api.otask.ru>`
|
|
84
|
+
- Опционально: `X-Otask-Allowed-Projects: eng-backlog,99`
|
|
85
|
+
|
|
86
|
+
### Stdio (Cursor / OpenCode)
|
|
87
|
+
|
|
88
|
+
```env
|
|
89
|
+
OTASK_AUTH_KEY=...
|
|
90
|
+
# или OTASK_EMAIL + OTASK_PASSWORD
|
|
91
|
+
OTASK_ALLOWED_PROJECTS=my-project
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
bun start
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Инструменты
|
|
99
|
+
|
|
100
|
+
Регистрируются в `src/tools/registry.ts` (краткие intent для агентов):
|
|
101
|
+
|
|
102
|
+
| Инструмент | Назначение |
|
|
103
|
+
|------------|------------|
|
|
104
|
+
| `otask_get_task` | Получить одну задачу по workspace + slug задачи (посмотреть поля перед обновлением) |
|
|
105
|
+
| `otask_update_task` | Обновить существующую задачу (name, board, performers, tags, description, …) |
|
|
106
|
+
| `otask_list_projects` | Список проектов воркспейса (с фильтром по allow-list, если задан) |
|
|
107
|
+
| `otask_list_project_tasks` | Список задач в проекте |
|
|
108
|
+
| `otask_list_board` | Список досок/колонок (статусов) проекта — узнать `board_id` / `board_column_id` |
|
|
109
|
+
| `otask_list_members` | Список участников воркспейса (ID исполнителей для назначения) |
|
|
110
|
+
| `otask_list_tags` | Список тегов воркспейса для меток |
|
|
111
|
+
| `otask_list_comments` | Список комментариев к задаче |
|
|
112
|
+
| `otask_add_comment` | Добавить комментарий (опционально `parent_id` для ответов) |
|
|
113
|
+
| `otask_create_task` | Создать задачу (обязательно: `ws_slug`, `project_id`, `name`, `board_id`, `board_column_id`, `end_at`) |
|
|
114
|
+
| `otask_move_task` | Переместить задачу в другую колонку доски (статус) |
|
|
115
|
+
| `otask_archive_task` | Архивировать задачу |
|
|
116
|
+
|
|
117
|
+
Если активен allow-list проектов, project-scoped инструменты проверяют членство (или фильтруют списки); пустой allow-list = доступны все проекты.
|
|
118
|
+
|
|
119
|
+
## Allow-list проектов
|
|
120
|
+
|
|
121
|
+
Формат: **slug** и/или **числовые ID** через запятую (пробелы обрезаются). Пусто / не задано = выключено.
|
|
122
|
+
|
|
123
|
+
### Gateway / stdio — env
|
|
124
|
+
|
|
125
|
+
```env
|
|
126
|
+
OTASK_ALLOWED_PROJECTS=product-roadmap,eng-backlog,42
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`GET /health` → `projectGuard: "env"`, если значение непустое.
|
|
130
|
+
|
|
131
|
+
### Passthrough — заголовок запроса
|
|
132
|
+
|
|
133
|
+
```http
|
|
134
|
+
X-Otask-Allowed-Projects: product-roadmap,42
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`GET /health` (с этим заголовком) → `projectGuard: "header"`. Gateway этот заголовок игнорирует и всегда использует `OTASK_ALLOWED_PROJECTS`.
|
|
138
|
+
|
|
139
|
+
## Снимок API-документации
|
|
140
|
+
|
|
141
|
+
Пересобрать локальный каталог API из HTML-доки O!task (Scribe):
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
bun run docs:parse
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Пишет в `docs/catalog/` с живой HTML-страницы доки (или `bun run docs:parse --file path` для офлайн HTML).
|
|
148
|
+
|
|
149
|
+
## Разработка
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
bun install
|
|
153
|
+
bun run build # tsc → dist/
|
|
154
|
+
bun test # bun test
|
|
155
|
+
bun start # stdio MCP
|
|
156
|
+
bun run start:http # Streamable HTTP MCP
|
|
157
|
+
bun run dev # stdio hot reload
|
|
158
|
+
bun run dev:http # HTTP hot reload
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Добавление инструмента
|
|
162
|
+
|
|
163
|
+
1. `src/services/api.ts` / `client.ts` — метод API при необходимости
|
|
164
|
+
2. `src/schemas/` — Zod input schema
|
|
165
|
+
3. `src/tools/my-tool.ts` — `createMyTool({ api, guard })` → `ToolDefinition`
|
|
166
|
+
4. `src/tools/registry.ts` — добавить factory в `toolFactories`
|
|
167
|
+
|
|
168
|
+
`server.ts` / `register.ts` править не нужно — регистрация централизована.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,yBAAyB,CAAC;AACnD,eAAO,MAAM,eAAe,QAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,sBAAsB,CAAC;AACnD,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/docs/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAIZ,MAAM,YAAY,CAAC;AAsIpB,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA0B5D"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const METHODS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
2
|
+
function stripTags(html) {
|
|
3
|
+
return html
|
|
4
|
+
.replace(/<script[\s\S]*?<\/script>/gi, " ")
|
|
5
|
+
.replace(/<style[\s\S]*?<\/style>/gi, " ")
|
|
6
|
+
.replace(/<[^>]+>/g, " ")
|
|
7
|
+
.replace(/ /g, " ")
|
|
8
|
+
.replace(/&/g, "&")
|
|
9
|
+
.replace(/</g, "<")
|
|
10
|
+
.replace(/>/g, ">")
|
|
11
|
+
.replace(/"/g, '"')
|
|
12
|
+
.replace(/'/g, "'")
|
|
13
|
+
.replace(/\s+/g, " ")
|
|
14
|
+
.trim();
|
|
15
|
+
}
|
|
16
|
+
function parseSidebar(html) {
|
|
17
|
+
const scopes = [];
|
|
18
|
+
const parts = html.split(/(?=<li[^>]*class="[^"]*sidebar__menu-item[^"]*")/i);
|
|
19
|
+
for (const part of parts) {
|
|
20
|
+
if (!/sidebar__menu-item/i.test(part))
|
|
21
|
+
continue;
|
|
22
|
+
const scopeMatch = part.match(/href=["']#([^"']+)["'][^>]*class=["'][^"']*sidebar__menu-link[^"']*["'][^>]*>[\s\S]*?<span[^>]*class=["'][^"']*sidebar__menu-link-name[^"']*["'][^>]*>([\s\S]*?)<\/span>/i);
|
|
23
|
+
if (!scopeMatch)
|
|
24
|
+
continue;
|
|
25
|
+
const id = scopeMatch[1];
|
|
26
|
+
const title = stripTags(scopeMatch[2]);
|
|
27
|
+
const endpointIds = [];
|
|
28
|
+
const endpointTitles = new Map();
|
|
29
|
+
const subRe = /href=["']#([^"']+)["'][^>]*class=["'][^"']*sidebar__submenu-link[^"']*["'][^>]*>([\s\S]*?)<\/a>/gi;
|
|
30
|
+
let sm;
|
|
31
|
+
while ((sm = subRe.exec(part)) !== null) {
|
|
32
|
+
const epId = sm[1];
|
|
33
|
+
if (epId === id)
|
|
34
|
+
continue;
|
|
35
|
+
endpointIds.push(epId);
|
|
36
|
+
endpointTitles.set(epId, stripTags(sm[2]));
|
|
37
|
+
}
|
|
38
|
+
scopes.push({ id, title, endpointIds, endpointTitles });
|
|
39
|
+
}
|
|
40
|
+
return scopes;
|
|
41
|
+
}
|
|
42
|
+
function findIdIndex(html, id) {
|
|
43
|
+
const patterns = [`id="${id}"`, `id='${id}'`];
|
|
44
|
+
let best = -1;
|
|
45
|
+
for (const p of patterns) {
|
|
46
|
+
const idx = html.indexOf(p);
|
|
47
|
+
if (idx !== -1 && (best === -1 || idx < best))
|
|
48
|
+
best = idx;
|
|
49
|
+
}
|
|
50
|
+
return best;
|
|
51
|
+
}
|
|
52
|
+
function sliceForEndpoint(html, endpointId, allEndpointIds) {
|
|
53
|
+
const start = findIdIndex(html, endpointId);
|
|
54
|
+
if (start === -1)
|
|
55
|
+
return "";
|
|
56
|
+
let end = html.length;
|
|
57
|
+
for (const otherId of allEndpointIds) {
|
|
58
|
+
if (otherId === endpointId)
|
|
59
|
+
continue;
|
|
60
|
+
const idx = findIdIndex(html, otherId);
|
|
61
|
+
if (idx > start && idx < end)
|
|
62
|
+
end = idx;
|
|
63
|
+
}
|
|
64
|
+
return html.slice(start, end);
|
|
65
|
+
}
|
|
66
|
+
function parseEndpointSlice(slice, id, sidebarTitle) {
|
|
67
|
+
if (!slice)
|
|
68
|
+
return null;
|
|
69
|
+
const text = stripTags(slice);
|
|
70
|
+
const methodMatch = text.match(/\b(GET|POST|PUT|PATCH|DELETE)\b/);
|
|
71
|
+
const method = methodMatch?.[1] ?? "GET";
|
|
72
|
+
if (!METHODS.has(method))
|
|
73
|
+
return null;
|
|
74
|
+
const pathMatch = text.match(/\/api\/v1\/[^\s<"]+/);
|
|
75
|
+
const path = pathMatch?.[0]?.replace(/[.,;)]+$/, "") ?? "";
|
|
76
|
+
const authRequired = /Требуется аутентификация/i.test(text) ||
|
|
77
|
+
/required authentication/i.test(text);
|
|
78
|
+
let title = sidebarTitle ?? "";
|
|
79
|
+
if (!title) {
|
|
80
|
+
const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
81
|
+
const hMatch = slice.match(new RegExp(`id=["']${escaped}["'][^>]*>([\\s\\S]*?)<\\/h[1-6]>`, "i"));
|
|
82
|
+
if (hMatch)
|
|
83
|
+
title = stripTags(hMatch[1]);
|
|
84
|
+
}
|
|
85
|
+
const pathParams = [];
|
|
86
|
+
for (const m of path.matchAll(/\{([^}]+)\}/g)) {
|
|
87
|
+
pathParams.push({ name: m[1], required: true });
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
id,
|
|
91
|
+
title,
|
|
92
|
+
method,
|
|
93
|
+
path,
|
|
94
|
+
authRequired,
|
|
95
|
+
pathParams,
|
|
96
|
+
queryParams: [],
|
|
97
|
+
bodyParams: [],
|
|
98
|
+
docsAnchor: `#${id}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export function parseOtaskDocsHtml(html) {
|
|
102
|
+
const sidebarScopes = parseSidebar(html);
|
|
103
|
+
const allEndpointIds = sidebarScopes.flatMap((s) => s.endpointIds);
|
|
104
|
+
const scopes = sidebarScopes.map((s) => {
|
|
105
|
+
const endpoints = [];
|
|
106
|
+
for (const epId of s.endpointIds) {
|
|
107
|
+
const slice = sliceForEndpoint(html, epId, allEndpointIds);
|
|
108
|
+
const record = parseEndpointSlice(slice, epId, s.endpointTitles.get(epId));
|
|
109
|
+
if (record)
|
|
110
|
+
endpoints.push(record);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
id: s.id,
|
|
114
|
+
title: s.title,
|
|
115
|
+
endpoints,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
generatedAt: new Date().toISOString(),
|
|
120
|
+
scopes,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/docs/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
2
|
+
export interface DocParam {
|
|
3
|
+
name: string;
|
|
4
|
+
type?: string;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
description?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface EndpointRecord {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
method: HttpMethod;
|
|
12
|
+
path: string;
|
|
13
|
+
authRequired: boolean;
|
|
14
|
+
pathParams: DocParam[];
|
|
15
|
+
queryParams: DocParam[];
|
|
16
|
+
bodyParams: DocParam[];
|
|
17
|
+
responseExample?: unknown;
|
|
18
|
+
docsAnchor: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ScopeCatalog {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
endpoints: EndpointRecord[];
|
|
24
|
+
}
|
|
25
|
+
export interface DocsCatalog {
|
|
26
|
+
generatedAt: string;
|
|
27
|
+
scopes: ScopeCatalog[];
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/docs/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,WAAW,EAAE,QAAQ,EAAE,CAAC;IACxB,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/docs/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { API_BASE_URL } from "./constants.js";
|
|
4
|
+
import { pickForwardRequestHeaders, pickForwardResponseHeaders } from "./proxy/headers.js";
|
|
5
|
+
import { readBody } from "./proxy/read-body.js";
|
|
6
|
+
function getAuthToken() {
|
|
7
|
+
const token = process.env.AUTH_TOKEN?.trim();
|
|
8
|
+
if (!token) {
|
|
9
|
+
throw new Error("AUTH_TOKEN environment variable is required for HTTP proxy mode");
|
|
10
|
+
}
|
|
11
|
+
return token;
|
|
12
|
+
}
|
|
13
|
+
function getPort() {
|
|
14
|
+
const raw = process.env.PORT?.trim() || "3847";
|
|
15
|
+
const port = Number.parseInt(raw, 10);
|
|
16
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
|
17
|
+
throw new Error(`Invalid PORT: ${raw}`);
|
|
18
|
+
}
|
|
19
|
+
return port;
|
|
20
|
+
}
|
|
21
|
+
function extractBearerToken(req) {
|
|
22
|
+
const auth = req.headers.authorization;
|
|
23
|
+
if (!auth?.startsWith("Bearer ")) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return auth.slice("Bearer ".length).trim();
|
|
27
|
+
}
|
|
28
|
+
function sendJson(res, status, body) {
|
|
29
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
30
|
+
res.end(JSON.stringify(body));
|
|
31
|
+
}
|
|
32
|
+
async function handleRequest(req, res, authToken) {
|
|
33
|
+
const method = req.method ?? "GET";
|
|
34
|
+
const requestUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
35
|
+
if (requestUrl.pathname === "/health") {
|
|
36
|
+
sendJson(res, 200, { ok: true });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!requestUrl.pathname.startsWith("/api/")) {
|
|
40
|
+
sendJson(res, 404, {
|
|
41
|
+
error: "Not found",
|
|
42
|
+
hint: "Proxy only forwards /api/* to O!task API",
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const clientToken = extractBearerToken(req);
|
|
47
|
+
if (!clientToken || clientToken !== authToken) {
|
|
48
|
+
sendJson(res, 401, {
|
|
49
|
+
error: "Unauthorized",
|
|
50
|
+
hint: "Send Authorization: Bearer <AUTH_TOKEN>",
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const upstreamUrl = `${API_BASE_URL}${requestUrl.pathname}${requestUrl.search}`;
|
|
55
|
+
const forwardHeaders = pickForwardRequestHeaders(req.headers);
|
|
56
|
+
forwardHeaders.Authorization = `Bearer ${authToken}`;
|
|
57
|
+
const body = await readBody(req);
|
|
58
|
+
const upstream = await fetch(upstreamUrl, {
|
|
59
|
+
method,
|
|
60
|
+
headers: forwardHeaders,
|
|
61
|
+
body,
|
|
62
|
+
});
|
|
63
|
+
const responseHeaders = pickForwardResponseHeaders(upstream.headers);
|
|
64
|
+
res.writeHead(upstream.status, responseHeaders);
|
|
65
|
+
if (method === "HEAD") {
|
|
66
|
+
res.end();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const responseBody = Buffer.from(await upstream.arrayBuffer());
|
|
70
|
+
res.end(responseBody);
|
|
71
|
+
}
|
|
72
|
+
function main() {
|
|
73
|
+
const authToken = getAuthToken();
|
|
74
|
+
const port = getPort();
|
|
75
|
+
const server = http.createServer((req, res) => {
|
|
76
|
+
handleRequest(req, res, authToken).catch((error) => {
|
|
77
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
78
|
+
if (!res.headersSent) {
|
|
79
|
+
sendJson(res, 502, { error: "Upstream proxy error", message });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
res.end();
|
|
83
|
+
console.error("Proxy error after headers sent:", message);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
server.listen(port, () => {
|
|
87
|
+
console.error(`O!task HTTP proxy listening on http://localhost:${port}`);
|
|
88
|
+
console.error(`Forwarding /api/* -> ${API_BASE_URL}/api/*`);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
main();
|
|
92
|
+
//# sourceMappingURL=http-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.js","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createEnvAuthResolver, validateStdioAuthConfig } from "./services/auth.js";
|
|
4
|
+
import { projectGuardFromEnv } from "./services/project-guard.js";
|
|
5
|
+
import { createMcpServer } from "./server.js";
|
|
6
|
+
async function main() {
|
|
7
|
+
try {
|
|
8
|
+
validateStdioAuthConfig();
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12
|
+
console.error(`ERROR: ${message}`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const server = createMcpServer(createEnvAuthResolver(), projectGuardFromEnv());
|
|
16
|
+
const transport = new StdioServerTransport();
|
|
17
|
+
await server.connect(transport);
|
|
18
|
+
}
|
|
19
|
+
main().catch((error) => {
|
|
20
|
+
console.error("Server error:", error);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,uBAAuB,EAAE,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,CAC5B,qBAAqB,EAAE,EACvB,mBAAmB,EAAE,CACtB,CAAC;IACF,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-http.d.ts","sourceRoot":"","sources":["../src/mcp-http.ts"],"names":[],"mappings":""}
|
package/dist/mcp-http.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { createMcpServer } from "./server.js";
|
|
5
|
+
import { authorizeHttpMcpRequest, createEnvAuthResolver, createPassthroughAuthResolver, extractBearerToken, extractProjectAllowListHeader, getHttpAuthMode, validateHttpAuthConfig, } from "./services/auth.js";
|
|
6
|
+
import { projectGuardMode, resolveHttpProjectGuard, } from "./services/project-guard.js";
|
|
7
|
+
function getPort() {
|
|
8
|
+
const raw = process.env.PORT?.trim() || "3847";
|
|
9
|
+
const port = Number.parseInt(raw, 10);
|
|
10
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
|
11
|
+
throw new Error(`Invalid PORT: ${raw}`);
|
|
12
|
+
}
|
|
13
|
+
return port;
|
|
14
|
+
}
|
|
15
|
+
function sendJson(res, status, body) {
|
|
16
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
17
|
+
res.end(JSON.stringify(body));
|
|
18
|
+
}
|
|
19
|
+
async function handleMcpRequest(req, res) {
|
|
20
|
+
const clientBearer = extractBearerToken(req.headers.authorization);
|
|
21
|
+
const authResult = authorizeHttpMcpRequest(clientBearer);
|
|
22
|
+
if (!authResult.ok) {
|
|
23
|
+
sendJson(res, authResult.status, {
|
|
24
|
+
error: authResult.error,
|
|
25
|
+
hint: authResult.hint,
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const auth = authResult.otaskBearer
|
|
30
|
+
? createPassthroughAuthResolver(authResult.otaskBearer)
|
|
31
|
+
: createEnvAuthResolver();
|
|
32
|
+
const authMode = getHttpAuthMode();
|
|
33
|
+
const headerRaw = extractProjectAllowListHeader(req.headers);
|
|
34
|
+
const guard = resolveHttpProjectGuard({
|
|
35
|
+
authMode,
|
|
36
|
+
env: process.env,
|
|
37
|
+
headerRaw,
|
|
38
|
+
});
|
|
39
|
+
const transport = new StreamableHTTPServerTransport({
|
|
40
|
+
sessionIdGenerator: undefined,
|
|
41
|
+
enableJsonResponse: true,
|
|
42
|
+
});
|
|
43
|
+
const server = createMcpServer(auth, guard);
|
|
44
|
+
res.on("close", () => {
|
|
45
|
+
void transport.close();
|
|
46
|
+
});
|
|
47
|
+
await server.connect(transport);
|
|
48
|
+
await transport.handleRequest(req, res);
|
|
49
|
+
}
|
|
50
|
+
async function handleRequest(req, res) {
|
|
51
|
+
const requestUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
52
|
+
if (requestUrl.pathname === "/health") {
|
|
53
|
+
const authMode = getHttpAuthMode();
|
|
54
|
+
const headerRaw = extractProjectAllowListHeader(req.headers);
|
|
55
|
+
const guard = resolveHttpProjectGuard({
|
|
56
|
+
authMode,
|
|
57
|
+
env: process.env,
|
|
58
|
+
headerRaw,
|
|
59
|
+
});
|
|
60
|
+
sendJson(res, 200, {
|
|
61
|
+
ok: true,
|
|
62
|
+
mode: "mcp-streamable-http",
|
|
63
|
+
authMode,
|
|
64
|
+
projectGuard: projectGuardMode(authMode, guard),
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (requestUrl.pathname === "/mcp") {
|
|
69
|
+
await handleMcpRequest(req, res);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
sendJson(res, 404, {
|
|
73
|
+
error: "Not found",
|
|
74
|
+
hint: "MCP endpoint: POST/GET /mcp",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function main() {
|
|
78
|
+
validateHttpAuthConfig();
|
|
79
|
+
const port = getPort();
|
|
80
|
+
const host = process.env.HOST?.trim() || "0.0.0.0";
|
|
81
|
+
http
|
|
82
|
+
.createServer((req, res) => {
|
|
83
|
+
handleRequest(req, res).catch((error) => {
|
|
84
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
85
|
+
if (!res.headersSent) {
|
|
86
|
+
sendJson(res, 500, { error: "MCP server error", message });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
res.end();
|
|
90
|
+
console.error("MCP HTTP error:", message);
|
|
91
|
+
});
|
|
92
|
+
})
|
|
93
|
+
.listen(port, host, () => {
|
|
94
|
+
console.error(`O!task MCP HTTP http://${host}:${port}/mcp [${getHttpAuthMode()}]`);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
main();
|
|
98
|
+
//# sourceMappingURL=mcp-http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-http.js","sourceRoot":"","sources":["../src/mcp-http.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,6BAA6B,EAC7B,kBAAkB,EAClB,6BAA6B,EAC7B,eAAe,EACf,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,6BAA6B,CAAC;AAErC,SAAS,OAAO;IACd,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAa;IAClE,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC9D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,GAAoB,EAAE,GAAmB;IACvE,MAAM,YAAY,GAAG,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,uBAAuB,CAAC,YAAY,CAAC,CAAC;IAEzD,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;QACnB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,EAAE;YAC/B,KAAK,EAAE,UAAU,CAAC,KAAK;YACvB,IAAI,EAAE,UAAU,CAAC,IAAI;SACtB,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW;QACjC,CAAC,CAAC,6BAA6B,CAAC,UAAU,CAAC,WAAW,CAAC;QACvD,CAAC,CAAC,qBAAqB,EAAE,CAAC;IAE5B,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,6BAA6B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,uBAAuB,CAAC;QACpC,QAAQ;QACR,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;QAClD,kBAAkB,EAAE,SAAS;QAC7B,kBAAkB,EAAE,IAAI;KACzB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5C,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACnB,KAAK,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAoB,EAAE,GAAmB;IACpE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC,CAAC;IAExF,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;QACnC,MAAM,SAAS,GAAG,6BAA6B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,uBAAuB,CAAC;YACpC,QAAQ;YACR,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,SAAS;SACV,CAAC,CAAC;QACH,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;YACjB,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,qBAAqB;YAC3B,QAAQ;YACR,YAAY,EAAE,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC;SAChD,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QACnC,MAAM,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACjC,OAAO;IACT,CAAC;IAED,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;QACjB,KAAK,EAAE,WAAW;QAClB,IAAI,EAAE,6BAA6B;KACpC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,IAAI;IACX,sBAAsB,EAAE,CAAC;IAEzB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAEnD,IAAI;SACD,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACzB,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACtC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC3D,OAAO;YACT,CAAC;YACD,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;SACD,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;QACvB,OAAO,CAAC,KAAK,CACX,0BAA0B,IAAI,IAAI,IAAI,SAAS,eAAe,EAAE,GAAG,CACpE,CAAC;IACJ,CAAC,CAAC,CAAC;AACP,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function pickForwardRequestHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string>;
|
|
2
|
+
export declare function pickForwardResponseHeaders(headers: Headers): Record<string, string | string[]>;
|
|
3
|
+
//# sourceMappingURL=headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/proxy/headers.ts"],"names":[],"mappings":"AAWA,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBxB;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,GACf,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAWnC"}
|