@smwb/onreel-git-mcp 0.1.0 → 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 +408 -0
- package/dist/chunk-3YP22YLA.js +50 -0
- package/dist/chunk-3YP22YLA.js.map +1 -0
- package/dist/chunk-4ZGRXIVA.js +259 -0
- package/dist/chunk-4ZGRXIVA.js.map +1 -0
- package/dist/chunk-C6PHNSDC.js +112 -0
- package/dist/chunk-C6PHNSDC.js.map +1 -0
- package/dist/chunk-GPRMI4PG.js +421 -0
- package/dist/chunk-GPRMI4PG.js.map +1 -0
- package/dist/chunk-L7CPE2SO.js +166 -0
- package/dist/chunk-L7CPE2SO.js.map +1 -0
- package/dist/contract/contract.const.d.ts +190 -0
- package/dist/contract/contract.lib.d.ts +22 -0
- package/dist/contract/contract.schema.d.ts +279 -0
- package/dist/contract/contract.types.d.ts +23 -0
- package/dist/files/files.const.d.ts +6 -0
- package/dist/files/files.lib.d.ts +14 -0
- package/dist/files/files.types.d.ts +13 -0
- package/dist/files/index.d.ts +3 -0
- package/dist/files/index.js +163 -0
- package/dist/files/index.js.map +1 -0
- package/dist/git/git.const.d.ts +4 -0
- package/dist/git/git.lib.d.ts +90 -0
- package/dist/git/git.types.d.ts +33 -0
- package/dist/git/index.d.ts +7 -0
- package/dist/git/index.js +97 -0
- package/dist/git/index.js.map +1 -0
- package/dist/git/paths.const.d.ts +5 -0
- package/dist/git/paths.lib.d.ts +39 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +203 -2
- package/dist/index.js.map +1 -1
- package/dist/kernel/log.types.d.ts +5 -0
- package/dist/kernel/redact.lib.d.ts +25 -0
- package/dist/kernel/store.types.d.ts +109 -0
- package/dist/kernel/umask.lib.d.ts +7 -0
- package/dist/mcp/args.lib.d.ts +17 -0
- package/dist/mcp/envelope.lib.d.ts +20 -0
- package/dist/mcp/index.d.ts +8 -0
- package/dist/mcp/index.js +744 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcp.const.d.ts +23 -0
- package/dist/mcp/mcp.schema.d.ts +64 -0
- package/dist/mcp/mcp.types.d.ts +125 -0
- package/dist/mcp/principal.lib.d.ts +22 -0
- package/dist/mcp/server.lib.d.ts +3 -0
- package/dist/mcp/tools.lib.d.ts +16 -0
- package/dist/tracker/builtin.lib.d.ts +1 -0
- package/dist/tracker/github.lib.d.ts +2 -0
- package/dist/tracker/gitlab.lib.d.ts +2 -0
- package/dist/tracker/http.lib.d.ts +4 -0
- package/dist/tracker/index.d.ts +15 -0
- package/dist/tracker/index.js +840 -0
- package/dist/tracker/index.js.map +1 -0
- package/dist/tracker/jira.lib.d.ts +2 -0
- package/dist/tracker/local.lib.d.ts +2 -0
- package/dist/tracker/mapping.const.d.ts +26 -0
- package/dist/tracker/registry.lib.d.ts +5 -0
- package/dist/tracker/secrets.lib.d.ts +7 -0
- package/dist/tracker/sourcecraft.lib.d.ts +2 -0
- package/dist/tracker/tracker.const.d.ts +10 -0
- package/dist/tracker/tracker.lib.d.ts +21 -0
- package/dist/tracker/tracker.types.d.ts +77 -0
- package/dist/worktree/index.d.ts +4 -0
- package/dist/worktree/index.js +203 -0
- package/dist/worktree/index.js.map +1 -0
- package/dist/worktree/lifecycle.const.d.ts +2 -0
- package/dist/worktree/lifecycle.lib.d.ts +30 -0
- package/dist/worktree/lifecycle.types.d.ts +14 -0
- package/package.json +15 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tracker/tracker.const.ts","../../src/tracker/tracker.lib.ts","../../src/tracker/mapping.const.ts","../../src/tracker/http.lib.ts","../../src/tracker/registry.lib.ts","../../src/tracker/github.lib.ts","../../src/tracker/gitlab.lib.ts","../../src/tracker/jira.lib.ts","../../src/tracker/local.lib.ts","../../src/tracker/sourcecraft.lib.ts","../../src/tracker/builtin.lib.ts","../../src/tracker/secrets.lib.ts"],"sourcesContent":["// Константы адаптеров трекеров библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/tracker.const.ts — дословная копия.\n\n/** Потолок ожидания ответа внешнего трекера, мс. */\nexport const TRACKER_TIMEOUT_MS = 15_000;\n\n/** Размер страницы списка задач по умолчанию; потолок — 500 по контракту. */\nexport const TRACKER_PAGE_LIMIT_DEFAULT = 50;\n\n/** Сколько символов тела ответа трекера попадает в текст ошибки. */\nexport const TRACKER_ERROR_BODY_MAX = 300;\n\n/** Файл секретов подключений в каталоге данных host: токены трекеров по projectId, права 0600 (D-352). */\nexport const TRACKER_SECRETS_FILE = 'tracker-secrets.json';\n\n/** Автор комментариев, которые пользователь пишет в локальном трекере. */\nexport const TRACKER_LOCAL_AUTHOR = 'owner';\n","// Чистые помощники адаптеров трекеров библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/tracker.lib.ts — дословная копия, импорт типов из среза контракта библиотеки.\nimport type { IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';\nimport type { TrackerFailure } from './tracker.types.ts';\n\n/** Отказ адаптера: потребитель превращает его в ошибку своего контракта (D-353). */\nexport class TrackerError extends Error {\n readonly failure: TrackerFailure;\n constructor(failure: TrackerFailure, message: string) {\n super(message);\n this.name = 'TrackerError';\n this.failure = failure;\n }\n}\n\n/** Время трекера (ISO 8601) в миллисекунды; нет или не разобралось — undefined. */\nexport function toTimestamp(value: unknown): number | undefined {\n if (typeof value !== 'string') return undefined;\n const ms = Date.parse(value);\n return Number.isFinite(ms) && ms >= 0 ? ms : undefined;\n}\n\n/** Поле, которое задано: непустая строка. */\nexport function text(value: unknown): string | undefined {\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n\nexport function clipText(value: string, max: number): string {\n return value.length <= max ? value : value.slice(0, max);\n}\n\n/** Строки из массива неизвестного вида: непустые строки без дублей. */\nexport function stringList(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return [...new Set(value.filter((item): item is string => typeof item === 'string' && item !== ''))];\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Категория и приоритет по таблице; нет в таблице — запасное значение. */\nexport function lookup<T>(table: Readonly<Record<string, T>>, key: unknown, fallback: T): T {\n return typeof key === 'string' && Object.hasOwn(table, key) ? (table[key] as T) : fallback;\n}\n\nexport function requireTarget(target: string | undefined, hint: string): string {\n if (target === undefined) throw new TrackerError('invalid_request', `Не задан target подключения: ${hint}.`);\n return target;\n}\n\n/** Обратная таблица: категория → первое имя статуса трекера. */\nexport function nameOf(table: Readonly<Record<string, IssueStatusCategory>>, category: IssueStatusCategory): string | undefined {\n return Object.entries(table).find(([, value]) => value === category)?.[0];\n}\n\nexport function priorityName(table: Readonly<Record<string, IssuePriority>>, priority: IssuePriority): string | undefined {\n return Object.entries(table).find(([, value]) => value === priority)?.[0];\n}\n","// Таблицы сопоставления статусов и приоритетов трекеров библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/mapping.const.ts — дословная копия, импорт типов из среза контракта библиотеки.\nimport type { IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';\n\n// Сопоставление статусов и приоритетов трекеров с единой моделью (D-351). Ключ — слаг или имя у трекера,\n// значение — категория единой модели. Не найденное в таблице: статус → `open`, приоритет не заполняется.\n\n/** SourceCraft: системные статусы. На паузе — ещё не закончено, но и не в работе: `open`. */\nexport const SOURCECRAFT_STATUSES: Readonly<Record<string, IssueStatusCategory>> = {\n open: 'open',\n paused: 'open',\n inProgress: 'in_progress',\n closed: 'done',\n declined: 'declined',\n duplicate: 'declined',\n};\n\n/** Категория → слаг статуса SourceCraft при переводе задачи. */\nexport const SOURCECRAFT_STATUS_SLUGS: Readonly<Record<IssueStatusCategory, string>> = {\n open: 'open',\n in_progress: 'inProgress',\n done: 'closed',\n declined: 'declined',\n};\n\n/** Приоритеты SourceCraft совпадают с единой моделью по именам. */\nexport const SOURCECRAFT_PRIORITIES: Readonly<Record<string, IssuePriority>> = {\n trivial: 'trivial',\n minor: 'minor',\n normal: 'normal',\n critical: 'critical',\n blocker: 'blocker',\n};\n\n/** Jira: `statusCategory.key` статуса. */\nexport const JIRA_STATUS_CATEGORIES: Readonly<Record<string, IssueStatusCategory>> = {\n new: 'open',\n indeterminate: 'in_progress',\n done: 'done',\n};\n\n/** Jira: имена приоритетов схемы по умолчанию (регистр не важен, ключи в нижнем). */\nexport const JIRA_PRIORITIES: Readonly<Record<string, IssuePriority>> = {\n lowest: 'trivial',\n low: 'minor',\n medium: 'normal',\n high: 'critical',\n highest: 'blocker',\n};\n\n/** Категория → имя приоритета Jira при создании и правке. */\nexport const JIRA_PRIORITY_NAMES: Readonly<Record<IssuePriority, string>> = {\n trivial: 'Lowest',\n minor: 'Low',\n normal: 'Medium',\n critical: 'High',\n blocker: 'Highest',\n};\n\n/** Тип задачи Jira, который создаёт адаптер. */\nexport const JIRA_ISSUE_TYPE = 'Task';\n\n/** GitHub: причина закрытия `state_reason`. */\nexport const GITHUB_CLOSE_REASONS: Readonly<Record<string, IssueStatusCategory>> = {\n completed: 'done',\n not_planned: 'declined',\n duplicate: 'declined',\n};\n\n/** GitLab: приоритеты нет, но принято помечать задачи метками `priority::<имя>`; метка читается как приоритет. */\nexport const GITLAB_PRIORITY_LABELS: Readonly<Record<string, IssuePriority>> = {\n 'priority::trivial': 'trivial',\n 'priority::minor': 'minor',\n 'priority::normal': 'normal',\n 'priority::critical': 'critical',\n 'priority::blocker': 'blocker',\n};\n\n/** Метка GitLab, которой адаптер отмечает «в работе»: у GitLab только opened и closed. */\nexport const GITLAB_IN_PROGRESS_LABEL = 'status::in-progress';\n\n/** Метка GitHub для «в работе» — по той же причине. */\nexport const GITHUB_IN_PROGRESS_LABEL = 'in progress';\n\n/** Метки GitHub для приоритета: `priority: <имя>`. */\nexport const GITHUB_PRIORITY_LABELS: Readonly<Record<string, IssuePriority>> = {\n 'priority: trivial': 'trivial',\n 'priority: minor': 'minor',\n 'priority: normal': 'normal',\n 'priority: critical': 'critical',\n 'priority: blocker': 'blocker',\n};\n\nexport const ALL_STATUSES: readonly IssueStatusCategory[] = ['open', 'in_progress', 'done', 'declined'];\n","// JSON-запросы к REST внешних трекеров библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/http.lib.ts — дословная копия, redactSecretText — из ядра библиотеки\n// (общий redactor ядра, kernel/redact.lib.ts).\nimport { redactSecretText } from '../kernel/redact.lib.ts';\nimport { TRACKER_ERROR_BODY_MAX, TRACKER_TIMEOUT_MS } from './tracker.const.ts';\nimport { TrackerError } from './tracker.lib.ts';\nimport type { JsonRequest, JsonResponse } from './tracker.types.ts';\n\n// Запрос к REST внешнего трекера: JSON в обе стороны, таймаут, ошибки без токена (D-352, D-353).\n\nexport async function requestJson(url: string, request: JsonRequest): Promise<JsonResponse> {\n let response: Response;\n try {\n response = await request.fetch(url, {\n method: request.method ?? 'GET',\n headers: { accept: 'application/json', ...(request.body === undefined ? {} : { 'content-type': 'application/json' }), ...request.headers },\n ...(request.body === undefined ? {} : { body: JSON.stringify(request.body) }),\n signal: AbortSignal.timeout(TRACKER_TIMEOUT_MS),\n });\n } catch (error) {\n throw new TrackerError('failed', redactSecretText(`Трекер не ответил (${hostOf(url)}): ${error instanceof Error ? error.message : String(error)}`));\n }\n const raw = await response.text();\n if (!response.ok) {\n if (response.status === 404 && request.missingIsIssue === true) throw new TrackerError('unknown_issue', 'Задача не найдена в трекере.');\n const detail = redactSecretText(raw.replace(/\\s+/g, ' ').trim()).slice(0, TRACKER_ERROR_BODY_MAX);\n throw new TrackerError('failed', `Трекер ${hostOf(url)} ответил ${String(response.status)}${detail === '' ? '' : `: ${detail}`}.`);\n }\n if (raw === '') return { body: undefined, headers: response.headers };\n try {\n return { body: JSON.parse(raw) as unknown, headers: response.headers };\n } catch {\n throw new TrackerError('failed', `Трекер ${hostOf(url)} вернул не JSON.`);\n }\n}\n\n/** Адрес без пути и параметров: в тексте ошибки секретам неоткуда взяться. */\nfunction hostOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return 'трекер';\n }\n}\n\n/** Базовый адрес без хвостовых слэшей; не http и не https — отказ подключения. */\nexport function baseUrl(value: string | undefined, fallback: string): string {\n const raw = (value ?? fallback).replace(/\\/+$/, '');\n let parsed: URL;\n try {\n parsed = new URL(raw);\n } catch {\n throw new TrackerError('invalid_request', 'Адрес трекера — не URL.');\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') throw new TrackerError('invalid_request', 'Адрес трекера должен быть http или https.');\n return raw;\n}\n","// Реестр трекеров библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/registry.lib.ts — дословная копия. Побочных эффектов при импорте нет:\n// реестр пуст, наполняется только явными вызовами registerTracker/registerBuiltinTrackers (R-001).\nimport type { TrackerDefinition } from './tracker.types.ts';\n\n// Реестр трекеров (D-350): вид подключения → определение. Потребитель работает только через него;\n// новый трекер — модуль с `TrackerDefinition` и одна строка регистрации в `builtin.lib.ts`.\n\nconst definitions = new Map<string, TrackerDefinition>();\n\n/** Регистрирует трекер; повтор того же вида — ошибка сборки, а не замена. */\nexport function registerTracker(definition: TrackerDefinition): void {\n const kind = definition.descriptor.tracker;\n if (definitions.has(kind)) throw new Error(`Трекер ${kind} уже зарегистрирован.`);\n definitions.set(kind, definition);\n}\n\nexport function findTracker(kind: string): TrackerDefinition | undefined {\n return definitions.get(kind);\n}\n\nexport function listTrackers(): TrackerDefinition[] {\n return [...definitions.values()];\n}\n","// Адаптер GitHub библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/github.lib.ts — дословная копия, импорт типов из среза контракта библиотеки.\nimport type { Issue, IssueComment, IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';\nimport { baseUrl, requestJson } from './http.lib.ts';\nimport { GITHUB_CLOSE_REASONS, GITHUB_IN_PROGRESS_LABEL, GITHUB_PRIORITY_LABELS } from './mapping.const.ts';\nimport { TrackerError, isRecord, lookup, priorityName, requireTarget, stringList, text, toTimestamp } from './tracker.lib.ts';\nimport type { AdapterContext, IssuePatchInput, TrackerAdapter, TrackerDefinition } from './tracker.types.ts';\n\n// Трекер GitHub (D-350): задачи репозитория `owner/repo` через REST API. У GitHub статус — open или closed\n// с причиной: «в работе» держится меткой `in progress`, приоритет — меткой `priority: <имя>`; обе метки\n// читаются в поля единой модели и из `labels` убираются. Pull request-ы в списке задач пропускаются.\n\nconst DEFAULT_API = 'https://api.github.com';\nconst TARGET = /^[\\w.-]+\\/[\\w.-]+$/;\nconst NUMBER = /^[1-9]\\d{0,14}$/;\n\nexport function githubTracker(): TrackerDefinition {\n return {\n descriptor: {\n tracker: 'github',\n title: 'GitHub',\n fields: [\n { name: 'target', required: true, secret: false },\n { name: 'token', required: true, secret: true },\n { name: 'url', required: false, secret: false },\n ],\n features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: true, statuses: ['open', 'in_progress', 'done', 'declined'] },\n },\n create: (context) => createGithubAdapter(context),\n };\n}\n\nfunction createGithubAdapter({ connection, fetch }: AdapterContext): TrackerAdapter {\n const target = requireTarget(connection.target, 'owner/repo репозитория GitHub');\n if (!TARGET.test(target)) throw new TrackerError('invalid_request', 'target GitHub — owner/repo.');\n const root = `${baseUrl(connection.url, DEFAULT_API)}/repos/${target}/issues`;\n const headers = {\n accept: 'application/vnd.github+json',\n 'x-github-api-version': '2022-11-28',\n ...(connection.token === undefined || connection.token === '' ? {} : { authorization: `Bearer ${connection.token}` }),\n };\n const number = (key: string): string => {\n if (!NUMBER.test(key)) throw new TrackerError('unknown_issue', `В GitHub нет задачи ${key}.`);\n return key;\n };\n const rawOf = async (key: string): Promise<Record<string, unknown>> => {\n const { body } = await requestJson(`${root}/${number(key)}`, { fetch, headers, missingIsIssue: true });\n return isRecord(body) ? body : {};\n };\n return {\n list: async (query) => {\n const page = pageOf(query.cursor);\n const params = new URLSearchParams({ state: stateFilter(query.status), per_page: String(query.limit), page: String(page) });\n const { body, headers: responseHeaders } = await requestJson(`${root}?${params.toString()}`, { fetch, headers });\n const items = (Array.isArray(body) ? body : []).filter((item) => isRecord(item) && item.pull_request === undefined).map(issueOf);\n return {\n items: query.status === undefined ? items : items.filter((issue) => query.status?.includes(issue.status.category) === true),\n ...(/rel=\"next\"/.test(responseHeaders.get('link') ?? '') ? { nextCursor: String(page + 1) } : {}),\n };\n },\n get: async (key) => issueOf(await rawOf(key)),\n create: async (input) => {\n const labels = composeLabels(input.labels ?? [], input.priority, undefined);\n const body = {\n title: input.title,\n ...(input.description === undefined ? {} : { body: input.description }),\n ...(labels.length === 0 ? {} : { labels }),\n ...(input.assignee === undefined ? {} : { assignees: [input.assignee] }),\n };\n return issueOf((await requestJson(root, { fetch, headers, method: 'POST', body })).body);\n },\n update: async (key, patch) => {\n const current = patch.labels === undefined && patch.priority === undefined && patch.status === undefined ? undefined : issueOf(await rawOf(key));\n const response = await requestJson(`${root}/${number(key)}`, { fetch, headers, method: 'PATCH', body: patchBody(patch, current), missingIsIssue: true });\n return issueOf(response.body);\n },\n comments: async (key, query) => {\n const page = pageOf(query.cursor);\n const params = new URLSearchParams({ per_page: String(query.limit), page: String(page) });\n const { body, headers: responseHeaders } = await requestJson(`${root}/${number(key)}/comments?${params.toString()}`, { fetch, headers, missingIsIssue: true });\n return { items: (Array.isArray(body) ? body : []).map(commentOf), ...(/rel=\"next\"/.test(responseHeaders.get('link') ?? '') ? { nextCursor: String(page + 1) } : {}) };\n },\n comment: async (key, body) => commentOf((await requestJson(`${root}/${number(key)}/comments`, { fetch, headers, method: 'POST', body: { body }, missingIsIssue: true })).body),\n };\n}\n\nfunction pageOf(cursor: string | undefined): number {\n const page = cursor === undefined ? 1 : Number(cursor);\n return Number.isInteger(page) && page >= 1 ? page : 1;\n}\n\n/** Параметр `state` по запрошенным категориям; смешанный запрос — все, лишнее отсеет клиент. */\nfunction stateFilter(status: readonly IssueStatusCategory[] | undefined): string {\n if (status === undefined) return 'all';\n if (status.every((item) => item === 'open' || item === 'in_progress')) return 'open';\n if (status.every((item) => item === 'done' || item === 'declined')) return 'closed';\n return 'all';\n}\n\n/** Метки записи: пользовательские плюс служебные для приоритета и «в работе». */\nfunction composeLabels(plain: readonly string[], priority: IssuePriority | undefined, status: IssueStatusCategory | undefined): string[] {\n const priorityLabel = priority === undefined ? undefined : priorityName(GITHUB_PRIORITY_LABELS, priority);\n return [...plain, ...(priorityLabel === undefined ? [] : [priorityLabel]), ...(status === 'in_progress' ? [GITHUB_IN_PROGRESS_LABEL] : [])];\n}\n\nfunction patchBody(patch: IssuePatchInput, current: Issue | undefined): Record<string, unknown> {\n const status = patch.status ?? current?.status.category;\n const touchesLabels = patch.labels !== undefined || patch.priority !== undefined || patch.status !== undefined;\n return {\n ...(patch.title === undefined ? {} : { title: patch.title }),\n ...(patch.description === undefined ? {} : { body: patch.description }),\n ...(patch.status === undefined ? {} : { state: patch.status === 'done' || patch.status === 'declined' ? 'closed' : 'open', ...(patch.status === 'declined' ? { state_reason: 'not_planned' } : patch.status === 'done' ? { state_reason: 'completed' } : { state_reason: 'reopened' }) }),\n ...(touchesLabels ? { labels: composeLabels(patch.labels ?? current?.labels ?? [], patch.priority ?? current?.priority, status) } : {}),\n ...(patch.assignee === undefined ? {} : { assignees: patch.assignee === null ? [] : [patch.assignee] }),\n };\n}\n\nfunction issueOf(raw: unknown): Issue {\n const item = isRecord(raw) ? raw : {};\n const names = Array.isArray(item.labels) ? stringList(item.labels.map((label) => (isRecord(label) ? label.name : label))) : [];\n const closed = item.state === 'closed';\n const category: IssueStatusCategory = closed ? lookup(GITHUB_CLOSE_REASONS, item.state_reason, 'done') : names.includes(GITHUB_IN_PROGRESS_LABEL) ? 'in_progress' : 'open';\n const priority = names.map((name) => lookup<IssuePriority | undefined>(GITHUB_PRIORITY_LABELS, name, undefined)).find((value) => value !== undefined);\n const description = text(item.body);\n const assignee = isRecord(item.assignee) ? text(item.assignee.login) : undefined;\n const url = text(item.html_url);\n const createdAt = toTimestamp(item.created_at);\n const updatedAt = toTimestamp(item.updated_at);\n const number = typeof item.number === 'number' ? String(item.number) : '?';\n return {\n key: number,\n title: text(item.title) ?? number,\n ...(description === undefined ? {} : { description }),\n status: { category, name: closed ? `closed${typeof item.state_reason === 'string' ? ` (${item.state_reason})` : ''}` : category === 'in_progress' ? 'in progress' : 'open' },\n ...(priority === undefined ? {} : { priority }),\n labels: names.filter((name) => name !== GITHUB_IN_PROGRESS_LABEL && !Object.hasOwn(GITHUB_PRIORITY_LABELS, name)),\n ...(assignee === undefined ? {} : { assignee }),\n links: url === undefined ? [] : [{ url, title: 'GitHub' }],\n ...(createdAt === undefined ? {} : { createdAt }),\n ...(updatedAt === undefined ? {} : { updatedAt }),\n };\n}\n\nfunction commentOf(raw: unknown): IssueComment {\n const item = isRecord(raw) ? raw : {};\n const author = isRecord(item.user) ? text(item.user.login) : undefined;\n const createdAt = toTimestamp(item.created_at);\n return { id: typeof item.id === 'number' ? String(item.id) : '?', ...(author === undefined ? {} : { author }), body: typeof item.body === 'string' ? item.body : '', ...(createdAt === undefined ? {} : { createdAt }) };\n}\n","// Адаптер GitLab библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/gitlab.lib.ts — дословная копия, импорт типов из среза контракта библиотеки.\nimport type { Issue, IssueComment, IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';\nimport { baseUrl, requestJson } from './http.lib.ts';\nimport { GITLAB_IN_PROGRESS_LABEL, GITLAB_PRIORITY_LABELS } from './mapping.const.ts';\nimport { TrackerError, isRecord, lookup, priorityName, requireTarget, stringList, text, toTimestamp } from './tracker.lib.ts';\nimport type { AdapterContext, IssuePatchInput, TrackerAdapter, TrackerDefinition } from './tracker.types.ts';\n\n// Трекер GitLab (D-350): задачи проекта (`group/project` или числовой id) через REST API v4, токен — PRIVATE-TOKEN.\n// У GitLab статус — opened или closed: «в работе» держится меткой `status::in-progress`, приоритет —\n// меткой `priority::<имя>`; служебные метки читаются в поля единой модели и из `labels` убираются. «Отклонена»\n// у GitLab нет. Исполнитель у GitLab задаётся по id пользователя, поэтому только показывается.\n\nconst DEFAULT_URL = 'https://gitlab.com';\nconst TARGET = /^(\\d{1,15}|[\\w.-]+(\\/[\\w.-]+)+)$/;\nconst IID = /^[1-9]\\d{0,14}$/;\n\nexport function gitlabTracker(): TrackerDefinition {\n return {\n descriptor: {\n tracker: 'gitlab',\n title: 'GitLab',\n fields: [\n { name: 'target', required: true, secret: false },\n { name: 'token', required: true, secret: true },\n { name: 'url', required: false, secret: false },\n ],\n features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: false, statuses: ['open', 'in_progress', 'done'] },\n },\n create: (context) => createGitlabAdapter(context),\n };\n}\n\nfunction createGitlabAdapter({ connection, fetch }: AdapterContext): TrackerAdapter {\n const target = requireTarget(connection.target, 'путь проекта GitLab, group/project, или его id');\n if (!TARGET.test(target)) throw new TrackerError('invalid_request', 'target GitLab — group/project или числовой id.');\n const root = `${baseUrl(connection.url, DEFAULT_URL)}/api/v4/projects/${encodeURIComponent(target)}/issues`;\n const headers: Record<string, string> = connection.token === undefined || connection.token === '' ? {} : { 'private-token': connection.token };\n const iid = (key: string): string => {\n if (!IID.test(key)) throw new TrackerError('unknown_issue', `В GitLab нет задачи ${key}.`);\n return key;\n };\n const currentOf = async (key: string): Promise<Issue> => issueOf((await requestJson(`${root}/${iid(key)}`, { fetch, headers, missingIsIssue: true })).body);\n return {\n list: async (query) => {\n const page = pageOf(query.cursor);\n const params = new URLSearchParams({ state: stateFilter(query.status), per_page: String(query.limit), page: String(page), order_by: 'created_at', sort: 'desc' });\n const { body, headers: responseHeaders } = await requestJson(`${root}?${params.toString()}`, { fetch, headers });\n const items = (Array.isArray(body) ? body : []).map(issueOf);\n const next = responseHeaders.get('x-next-page');\n return {\n items: query.status === undefined ? items : items.filter((issue) => query.status?.includes(issue.status.category) === true),\n ...(next === null || next === '' ? {} : { nextCursor: next }),\n };\n },\n get: currentOf,\n create: async (input) => {\n const labels = composeLabels(input.labels ?? [], input.priority, undefined);\n const body = { title: input.title, ...(input.description === undefined ? {} : { description: input.description }), ...(labels.length === 0 ? {} : { labels: labels.join(',') }) };\n return issueOf((await requestJson(root, { fetch, headers, method: 'POST', body })).body);\n },\n update: async (key, patch) => {\n const current = patch.labels === undefined && patch.priority === undefined && patch.status === undefined ? undefined : await currentOf(key);\n return issueOf((await requestJson(`${root}/${iid(key)}`, { fetch, headers, method: 'PUT', body: patchBody(patch, current), missingIsIssue: true })).body);\n },\n comments: async (key, query) => {\n const page = pageOf(query.cursor);\n const params = new URLSearchParams({ sort: 'asc', order_by: 'created_at', per_page: String(query.limit), page: String(page) });\n const { body, headers: responseHeaders } = await requestJson(`${root}/${iid(key)}/notes?${params.toString()}`, { fetch, headers, missingIsIssue: true });\n const next = responseHeaders.get('x-next-page');\n // Системные заметки («изменил метку») — не комментарии.\n const items = (Array.isArray(body) ? body : []).filter((note) => isRecord(note) && note.system !== true).map(commentOf);\n return { items, ...(next === null || next === '' ? {} : { nextCursor: next }) };\n },\n comment: async (key, body) => commentOf((await requestJson(`${root}/${iid(key)}/notes`, { fetch, headers, method: 'POST', body: { body }, missingIsIssue: true })).body),\n };\n}\n\nfunction pageOf(cursor: string | undefined): number {\n const page = cursor === undefined ? 1 : Number(cursor);\n return Number.isInteger(page) && page >= 1 ? page : 1;\n}\n\nfunction stateFilter(status: readonly IssueStatusCategory[] | undefined): string {\n if (status === undefined) return 'all';\n if (status.every((item) => item === 'open' || item === 'in_progress')) return 'opened';\n if (status.every((item) => item === 'done' || item === 'declined')) return 'closed';\n return 'all';\n}\n\nfunction composeLabels(plain: readonly string[], priority: IssuePriority | undefined, status: IssueStatusCategory | undefined): string[] {\n const priorityLabel = priority === undefined ? undefined : priorityName(GITLAB_PRIORITY_LABELS, priority);\n return [...plain, ...(priorityLabel === undefined ? [] : [priorityLabel]), ...(status === 'in_progress' ? [GITLAB_IN_PROGRESS_LABEL] : [])];\n}\n\nfunction patchBody(patch: IssuePatchInput, current: Issue | undefined): Record<string, unknown> {\n const status = patch.status ?? current?.status.category;\n const touchesLabels = patch.labels !== undefined || patch.priority !== undefined || patch.status !== undefined;\n if (patch.status === 'declined') throw new TrackerError('invalid_request', 'В GitLab нет статуса «отклонена».');\n return {\n ...(patch.title === undefined ? {} : { title: patch.title }),\n ...(patch.description === undefined ? {} : { description: patch.description }),\n ...(patch.status === undefined ? {} : { state_event: patch.status === 'done' ? 'close' : 'reopen' }),\n ...(touchesLabels ? { labels: composeLabels(patch.labels ?? current?.labels ?? [], patch.priority ?? current?.priority, status).join(',') } : {}),\n };\n}\n\nfunction issueOf(raw: unknown): Issue {\n const item = isRecord(raw) ? raw : {};\n const names = stringList(item.labels);\n const closed = item.state === 'closed';\n const category: IssueStatusCategory = closed ? 'done' : names.includes(GITLAB_IN_PROGRESS_LABEL) ? 'in_progress' : 'open';\n const priority = names.map((name) => lookup<IssuePriority | undefined>(GITLAB_PRIORITY_LABELS, name, undefined)).find((value) => value !== undefined);\n const description = text(item.description);\n const assignees = Array.isArray(item.assignees) ? item.assignees : [];\n const first = assignees[0];\n const assignee = isRecord(first) ? text(first.username) : undefined;\n const url = text(item.web_url);\n const createdAt = toTimestamp(item.created_at);\n const updatedAt = toTimestamp(item.updated_at);\n const key = typeof item.iid === 'number' ? String(item.iid) : '?';\n return {\n key,\n title: text(item.title) ?? key,\n ...(description === undefined ? {} : { description }),\n status: { category, name: closed ? 'closed' : category === 'in_progress' ? 'in progress' : 'opened' },\n ...(priority === undefined ? {} : { priority }),\n labels: names.filter((name) => name !== GITLAB_IN_PROGRESS_LABEL && !Object.hasOwn(GITLAB_PRIORITY_LABELS, name)),\n ...(assignee === undefined ? {} : { assignee }),\n links: url === undefined ? [] : [{ url, title: 'GitLab' }],\n ...(createdAt === undefined ? {} : { createdAt }),\n ...(updatedAt === undefined ? {} : { updatedAt }),\n };\n}\n\nfunction commentOf(raw: unknown): IssueComment {\n const item = isRecord(raw) ? raw : {};\n const author = isRecord(item.author) ? text(item.author.username) : undefined;\n const createdAt = toTimestamp(item.created_at);\n return { id: typeof item.id === 'number' ? String(item.id) : '?', ...(author === undefined ? {} : { author }), body: typeof item.body === 'string' ? item.body : '', ...(createdAt === undefined ? {} : { createdAt }) };\n}\n","// Адаптер Jira библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/jira.lib.ts — дословная копия, импорт типов из среза контракта библиотеки.\nimport type { Issue, IssueComment, IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';\nimport { baseUrl, requestJson } from './http.lib.ts';\nimport { JIRA_ISSUE_TYPE, JIRA_PRIORITIES, JIRA_PRIORITY_NAMES, JIRA_STATUS_CATEGORIES } from './mapping.const.ts';\nimport { TrackerError, isRecord, lookup, requireTarget, stringList, text, toTimestamp } from './tracker.lib.ts';\nimport type { AdapterContext, IssuePatchInput, TrackerAdapter, TrackerDefinition } from './tracker.types.ts';\n\n// Трекер Jira (D-350): проект по ключу, REST API v2 (описание — обычный текст). Есть `user` — Basic (email и\n// API-токен Cloud), нет — Bearer (персональный токен Server и Data Center). Список — `/search/jql` Cloud, на\n// 404 — классический `/search` Server с курсором `s:<startAt>`. Статус — по `statusCategory`, меняется\n// переходом (transition) в нужную категорию; «отклонена» у Jira нет. Исполнитель у Cloud — accountId, поэтому\n// только показывается.\n\nconst PROJECT_KEY = /^[A-Z][A-Z0-9_]{0,49}$/;\nconst ISSUE_KEY = /^[A-Z][A-Z0-9_]{0,49}-[1-9]\\d{0,14}$/;\nconst FIELDS = 'summary,description,status,priority,labels,assignee,created,updated';\nconst CATEGORY_JQL: Readonly<Partial<Record<IssueStatusCategory, string>>> = { open: '\"To Do\"', in_progress: '\"In Progress\"', done: 'Done' };\n\nexport function jiraTracker(): TrackerDefinition {\n return {\n descriptor: {\n tracker: 'jira',\n title: 'Jira',\n fields: [\n { name: 'url', required: true, secret: false },\n { name: 'target', required: true, secret: false },\n { name: 'user', required: false, secret: false },\n { name: 'token', required: true, secret: true },\n ],\n features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: false, statuses: ['open', 'in_progress', 'done'] },\n },\n create: (context) => createJiraAdapter(context),\n };\n}\n\nfunction createJiraAdapter({ connection, fetch }: AdapterContext): TrackerAdapter {\n if (connection.url === undefined) throw new TrackerError('invalid_request', 'Не задан адрес Jira.');\n const project = requireTarget(connection.target, 'ключ проекта Jira, например PROJ');\n if (!PROJECT_KEY.test(project)) throw new TrackerError('invalid_request', 'target Jira — ключ проекта: заглавные буквы и цифры.');\n const site = baseUrl(connection.url, connection.url);\n const api = `${site}/rest/api/2`;\n const token = connection.token ?? '';\n const headers = { authorization: connection.user === undefined ? `Bearer ${token}` : `Basic ${Buffer.from(`${connection.user}:${token}`).toString('base64')}` };\n const key = (value: string): string => {\n if (!ISSUE_KEY.test(value)) throw new TrackerError('unknown_issue', `В Jira нет задачи ${value}.`);\n return value;\n };\n const toIssue = (raw: unknown): Issue => issueOf(raw, site);\n const load = async (value: string): Promise<Issue> => toIssue((await requestJson(`${api}/issue/${key(value)}?fields=${FIELDS}`, { fetch, headers, missingIsIssue: true })).body);\n return {\n list: async (query) => {\n const jql = listJql(project, query.status);\n if (jql === undefined) return { items: [] };\n if (query.cursor?.startsWith('s:') !== true) {\n const params = new URLSearchParams({ jql, fields: FIELDS, maxResults: String(query.limit) });\n if (query.cursor !== undefined) params.set('nextPageToken', query.cursor);\n try {\n const { body } = await requestJson(`${api}/search/jql?${params.toString()}`, { fetch, headers });\n const record = isRecord(body) ? body : {};\n const next = text(record.nextPageToken);\n return { items: (Array.isArray(record.issues) ? record.issues : []).map(toIssue), ...(next === undefined || record.isLast === true ? {} : { nextCursor: next }) };\n } catch (error) {\n if (!(error instanceof TrackerError) || !error.message.includes(' 404')) throw error;\n }\n }\n const startAt = query.cursor?.startsWith('s:') === true ? Number(query.cursor.slice(2)) : 0;\n const params = new URLSearchParams({ jql, fields: FIELDS, maxResults: String(query.limit), startAt: String(Number.isInteger(startAt) && startAt >= 0 ? startAt : 0) });\n const { body } = await requestJson(`${api}/search?${params.toString()}`, { fetch, headers });\n const record = isRecord(body) ? body : {};\n const issues = Array.isArray(record.issues) ? record.issues : [];\n const total = typeof record.total === 'number' ? record.total : 0;\n const at = typeof record.startAt === 'number' ? record.startAt : 0;\n return { items: issues.map(toIssue), ...(at + issues.length < total && issues.length > 0 ? { nextCursor: `s:${String(at + issues.length)}` } : {}) };\n },\n get: load,\n create: async (input) => {\n const fields = {\n project: { key: project },\n summary: input.title,\n issuetype: { name: JIRA_ISSUE_TYPE },\n ...(input.description === undefined ? {} : { description: input.description }),\n ...(input.priority === undefined ? {} : { priority: { name: JIRA_PRIORITY_NAMES[input.priority] } }),\n ...(input.labels === undefined ? {} : { labels: input.labels.map((label) => label.replace(/\\s+/g, '-')) }),\n };\n const { body } = await requestJson(`${api}/issue`, { fetch, headers, method: 'POST', body: { fields } });\n const created = isRecord(body) ? text(body.key) : undefined;\n if (created === undefined) throw new TrackerError('failed', 'Jira создала задачу, но не вернула её ключ.');\n return load(created);\n },\n update: async (value, patch) => {\n const fields = fieldsOf(patch);\n if (Object.keys(fields).length > 0) await requestJson(`${api}/issue/${key(value)}`, { fetch, headers, method: 'PUT', body: { fields }, missingIsIssue: true });\n if (patch.status !== undefined) await transition(api, key(value), patch.status, fetch, headers);\n return load(value);\n },\n comments: async (value, query) => {\n const startAt = query.cursor === undefined ? 0 : Number(query.cursor);\n const params = new URLSearchParams({ startAt: String(Number.isInteger(startAt) && startAt >= 0 ? startAt : 0), maxResults: String(query.limit), orderBy: 'created' });\n const { body } = await requestJson(`${api}/issue/${key(value)}/comment?${params.toString()}`, { fetch, headers, missingIsIssue: true });\n const record = isRecord(body) ? body : {};\n const items = Array.isArray(record.comments) ? record.comments : [];\n const total = typeof record.total === 'number' ? record.total : 0;\n const at = typeof record.startAt === 'number' ? record.startAt : 0;\n return { items: items.map(commentOf), ...(at + items.length < total && items.length > 0 ? { nextCursor: String(at + items.length) } : {}) };\n },\n comment: async (value, body) => commentOf((await requestJson(`${api}/issue/${key(value)}/comment`, { fetch, headers, method: 'POST', body: { body }, missingIsIssue: true })).body),\n };\n}\n\n/** JQL списка; undefined — запрошенные категории Jira не знает (результат пуст). */\nfunction listJql(project: string, status: readonly IssueStatusCategory[] | undefined): string | undefined {\n const base = `project = \"${project}\"`;\n if (status === undefined) return `${base} ORDER BY created DESC`;\n const names = status.flatMap((item) => CATEGORY_JQL[item] ?? []);\n return names.length === 0 ? undefined : `${base} AND statusCategory in (${names.join(', ')}) ORDER BY created DESC`;\n}\n\nfunction fieldsOf(patch: IssuePatchInput): Record<string, unknown> {\n return {\n ...(patch.title === undefined ? {} : { summary: patch.title }),\n ...(patch.description === undefined ? {} : { description: patch.description }),\n ...(patch.priority === undefined ? {} : { priority: { name: JIRA_PRIORITY_NAMES[patch.priority] } }),\n ...(patch.labels === undefined ? {} : { labels: patch.labels.map((label) => label.replace(/\\s+/g, '-')) }),\n };\n}\n\n/** Переводит задачу в категорию: берёт первый доступный переход, чей статус назначения лежит в ней. */\nasync function transition(api: string, key: string, category: IssueStatusCategory, fetch: AdapterContext['fetch'], headers: Record<string, string>): Promise<void> {\n const { body } = await requestJson(`${api}/issue/${key}/transitions`, { fetch, headers, missingIsIssue: true });\n const list = isRecord(body) && Array.isArray(body.transitions) ? body.transitions : [];\n const match = list.find((item) => {\n if (!isRecord(item) || !isRecord(item.to) || !isRecord(item.to.statusCategory)) return false;\n return lookup<IssueStatusCategory | undefined>(JIRA_STATUS_CATEGORIES, item.to.statusCategory.key, undefined) === category;\n });\n const id = isRecord(match) ? text(match.id) : undefined;\n if (id === undefined) throw new TrackerError('invalid_request', `Из текущего статуса Jira нет перехода в категорию ${category}.`);\n await requestJson(`${api}/issue/${key}/transitions`, { fetch, headers, method: 'POST', body: { transition: { id } }, missingIsIssue: true });\n}\n\nfunction issueOf(raw: unknown, site: string): Issue {\n const item = isRecord(raw) ? raw : {};\n const fields = isRecord(item.fields) ? item.fields : {};\n const status = isRecord(fields.status) ? fields.status : {};\n const category = isRecord(status.statusCategory) ? status.statusCategory.key : undefined;\n const priorityName = isRecord(fields.priority) && typeof fields.priority.name === 'string' ? fields.priority.name.toLowerCase() : undefined;\n const priority = lookup<IssuePriority | undefined>(JIRA_PRIORITIES, priorityName, undefined);\n const assignee = isRecord(fields.assignee) ? (text(fields.assignee.displayName) ?? text(fields.assignee.name)) : undefined;\n const description = text(fields.description);\n const createdAt = toTimestamp(fields.created);\n const updatedAt = toTimestamp(fields.updated);\n const key = text(item.key) ?? '?';\n return {\n key,\n title: text(fields.summary) ?? key,\n ...(description === undefined ? {} : { description }),\n status: { category: lookup(JIRA_STATUS_CATEGORIES, category, 'open'), name: text(status.name) ?? 'To Do' },\n ...(priority === undefined ? {} : { priority }),\n labels: stringList(fields.labels),\n ...(assignee === undefined ? {} : { assignee }),\n links: [{ url: `${site}/browse/${key}`, title: 'Jira' }],\n ...(createdAt === undefined ? {} : { createdAt }),\n ...(updatedAt === undefined ? {} : { updatedAt }),\n };\n}\n\nfunction commentOf(raw: unknown): IssueComment {\n const item = isRecord(raw) ? raw : {};\n const author = isRecord(item.author) ? text(item.author.displayName) : undefined;\n const createdAt = toTimestamp(item.created);\n return { id: text(item.id) ?? '?', ...(author === undefined ? {} : { author }), body: typeof item.body === 'string' ? item.body : '', ...(createdAt === undefined ? {} : { createdAt }) };\n}\n","// Локальный трекер библиотеки @smwb/onreel-git-mcp (часть E выделения, D-003).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/local.lib.ts — CRUD через функции ledger заменён на порт LocalIssueStore ядра\n// (kernel/store.types.ts): хранилище пишет потребитель (в onreel — поверх ledger), эталонная\n// реализация в памяти — в тестах библиотеки (test/helpers/tracker-fixtures.lib.ts).\nimport type { Issue } from '../contract/contract.types.ts';\nimport type { LocalCommentRow, LocalIssueRow } from '../kernel/store.types.ts';\nimport { ALL_STATUSES } from './mapping.const.ts';\nimport { TrackerError } from './tracker.lib.ts';\nimport type { AdapterContext, TrackerAdapter, TrackerDefinition } from './tracker.types.ts';\n\n// Локальный трекер (D-350): задачи в хранилище потребителя, без внешних сервисов. Номер задачи — её ключ.\n\nconst STATUS_NAMES = { open: 'Open', in_progress: 'In progress', done: 'Done', declined: 'Declined' } as const;\n\nexport function localTracker(): TrackerDefinition {\n return {\n descriptor: {\n tracker: 'local',\n title: 'Local',\n fields: [],\n features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: true, statuses: [...ALL_STATUSES] },\n },\n create: (context) => createLocalAdapter(context),\n };\n}\n\nfunction createLocalAdapter({ store, projectId, author }: AdapterContext): TrackerAdapter {\n const number = (key: string): number => {\n if (!/^[1-9]\\d{0,14}$/.test(key)) throw new TrackerError('unknown_issue', `В локальном трекере нет задачи ${key}.`);\n return Number(key);\n };\n const mustGet = (key: string): LocalIssueRow => {\n const row = store.getLocalIssue(projectId, number(key));\n if (row === undefined) throw new TrackerError('unknown_issue', `В локальном трекере нет задачи ${key}.`);\n return row;\n };\n return {\n list: (query) => {\n const page = store.listLocalIssues(projectId, { limit: query.limit, ...(query.cursor === undefined ? {} : { cursor: query.cursor }), ...(query.status === undefined ? {} : { status: query.status }) });\n return Promise.resolve({ items: page.items.map(toIssue), ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }) });\n },\n get: (key) => Promise.resolve(toIssue(mustGet(key))),\n create: (input) => Promise.resolve(toIssue(store.createLocalIssue(projectId, input))),\n update: (key, patch) => {\n const row = store.updateLocalIssue(projectId, mustGet(key).number, patch);\n if (row === undefined) throw new TrackerError('unknown_issue', `В локальном трекере нет задачи ${key}.`);\n return Promise.resolve(toIssue(row));\n },\n comments: (key, query) => {\n const page = store.listLocalComments(projectId, mustGet(key).number, { limit: query.limit, ...(query.cursor === undefined ? {} : { cursor: query.cursor }) });\n return Promise.resolve({ items: page.items.map(toComment), ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }) });\n },\n comment: (key, body) => Promise.resolve(toComment(store.addLocalComment(projectId, mustGet(key).number, body, author))),\n };\n}\n\nfunction toIssue(row: LocalIssueRow): Issue {\n return {\n key: String(row.number),\n title: row.title,\n ...(row.description === undefined ? {} : { description: row.description }),\n status: { category: row.status, name: STATUS_NAMES[row.status] },\n ...(row.priority === undefined ? {} : { priority: row.priority }),\n labels: row.labels,\n ...(row.assignee === undefined ? {} : { assignee: row.assignee }),\n links: [],\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nfunction toComment(row: LocalCommentRow): { id: string; author?: string; body: string; createdAt: number } {\n return { id: String(row.id), ...(row.author === undefined ? {} : { author: row.author }), body: row.body, createdAt: row.createdAt };\n}\n","// Адаптер SourceCraft библиотеки @smwb/onreel-git-mcp (часть E выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/sourcecraft.lib.ts — дословная копия, импорт типов из среза контракта библиотеки.\nimport type { Issue, IssueComment } from '../contract/contract.types.ts';\nimport { baseUrl, requestJson } from './http.lib.ts';\nimport { ALL_STATUSES, SOURCECRAFT_PRIORITIES, SOURCECRAFT_STATUSES, SOURCECRAFT_STATUS_SLUGS } from './mapping.const.ts';\nimport { TrackerError, isRecord, lookup, requireTarget, stringList, text, toTimestamp } from './tracker.lib.ts';\nimport type { AdapterContext, IssuePatchInput, TrackerAdapter, TrackerDefinition } from './tracker.types.ts';\n\n// Трекер SourceCraft (D-350): задачи репозитория `org/repo` через REST API, токен — Bearer. Исполнитель и метки\n// у SourceCraft задаются по id, а не по имени: адаптер их показывает, но не меняет.\n\nconst DEFAULT_API = 'https://api.sourcecraft.tech';\nconst DEFAULT_WEB = 'https://sourcecraft.dev';\nconst TARGET = /^[\\w.-]+\\/[\\w.-]+$/;\nconst SLUG = /^[\\w-]{1,100}$/;\n\nexport function sourcecraftTracker(): TrackerDefinition {\n return {\n descriptor: {\n tracker: 'sourcecraft',\n title: 'SourceCraft',\n fields: [\n { name: 'target', required: true, secret: false },\n { name: 'token', required: true, secret: true },\n { name: 'url', required: false, secret: false },\n ],\n features: { create: true, update: true, comment: true, priority: true, labels: false, assignee: false, statuses: [...ALL_STATUSES] },\n },\n create: (context) => createSourceCraftAdapter(context),\n };\n}\n\nfunction createSourceCraftAdapter({ connection, fetch }: AdapterContext): TrackerAdapter {\n const target = requireTarget(connection.target, 'org/repo репозитория SourceCraft');\n if (!TARGET.test(target)) throw new TrackerError('invalid_request', 'target SourceCraft — org/repo.');\n const api = baseUrl(connection.url, DEFAULT_API);\n const root = `${api}/repos/${target}/issues`;\n const headers = { authorization: `Bearer ${connection.token ?? ''}` };\n const web = connection.url === undefined ? `${DEFAULT_WEB}/${target}/issues` : undefined;\n const slug = (key: string): string => {\n if (!SLUG.test(key)) throw new TrackerError('unknown_issue', `В SourceCraft нет задачи ${key}.`);\n return key;\n };\n const toIssue = (raw: unknown): Issue => issueOf(raw, web);\n return {\n list: async (query) => {\n const params = new URLSearchParams({ page_size: String(query.limit) });\n if (query.cursor !== undefined) params.set('page_token', query.cursor);\n const { body } = await requestJson(`${root}?${params.toString()}`, { fetch, headers });\n const record = isRecord(body) ? body : {};\n const issues = (Array.isArray(record.issues) ? record.issues : []).map(toIssue);\n return { items: query.status === undefined ? issues : issues.filter((issue) => query.status?.includes(issue.status.category) === true), ...nextOf(record.next_page_token) };\n },\n get: async (key) => toIssue((await requestJson(`${root}/${slug(key)}`, { fetch, headers, missingIsIssue: true })).body),\n create: async (input) => {\n const body = { title: input.title, ...(input.description === undefined ? {} : { description: input.description }), ...(input.priority === undefined ? {} : { priority: input.priority }) };\n return toIssue((await requestJson(root, { fetch, headers, method: 'POST', body })).body);\n },\n update: async (key, patch) => toIssue((await requestJson(`${root}/${slug(key)}`, { fetch, headers, method: 'PATCH', body: patchBody(patch), missingIsIssue: true })).body),\n comments: async (key, query) => {\n const params = new URLSearchParams({ page_size: String(query.limit) });\n if (query.cursor !== undefined) params.set('page_token', query.cursor);\n const { body } = await requestJson(`${root}/${slug(key)}/comments?${params.toString()}`, { fetch, headers, missingIsIssue: true });\n const record = isRecord(body) ? body : {};\n return { items: (Array.isArray(record.issue_comments) ? record.issue_comments : []).map(commentOf), ...nextOf(record.next_page_token) };\n },\n comment: async (key, body) => commentOf((await requestJson(`${root}/${slug(key)}/comments`, { fetch, headers, method: 'POST', body: { body }, missingIsIssue: true })).body),\n };\n}\n\nfunction patchBody(patch: IssuePatchInput): Record<string, unknown> {\n return {\n ...(patch.title === undefined ? {} : { title: patch.title }),\n ...(patch.description === undefined ? {} : { description: patch.description }),\n ...(patch.status === undefined ? {} : { status_slug: SOURCECRAFT_STATUS_SLUGS[patch.status] }),\n ...(patch.priority === undefined ? {} : { priority: patch.priority }),\n };\n}\n\nfunction nextOf(token: unknown): { nextCursor?: string } {\n const value = text(token);\n return value === undefined ? {} : { nextCursor: value };\n}\n\nfunction issueOf(raw: unknown, web: string | undefined): Issue {\n const item = isRecord(raw) ? raw : {};\n const status = isRecord(item.status) ? item.status : {};\n const slug = text(item.slug) ?? '?';\n const statusSlug = text(status.slug) ?? 'open';\n const priority = lookup<Issue['priority']>(SOURCECRAFT_PRIORITIES, item.priority, undefined);\n const assignee = isRecord(item.assignee) ? text(item.assignee.slug) : undefined;\n const labels = Array.isArray(item.labels) ? stringList(item.labels.map((label) => (isRecord(label) ? label.name : undefined))) : [];\n const description = text(item.description);\n const createdAt = toTimestamp(item.created_at);\n const updatedAt = toTimestamp(item.updated_at);\n return {\n key: slug,\n title: text(item.title) ?? slug,\n ...(description === undefined ? {} : { description }),\n status: { category: lookup(SOURCECRAFT_STATUSES, statusSlug, 'open'), name: text(status.name) ?? statusSlug },\n ...(priority === undefined ? {} : { priority }),\n labels,\n ...(assignee === undefined ? {} : { assignee }),\n links: web === undefined ? [] : [{ url: `${web}/${slug}`, title: 'SourceCraft' }],\n ...(createdAt === undefined ? {} : { createdAt }),\n ...(updatedAt === undefined ? {} : { updatedAt }),\n };\n}\n\nfunction commentOf(raw: unknown): IssueComment {\n const item = isRecord(raw) ? raw : {};\n const author = isRecord(item.author) ? text(item.author.slug) : undefined;\n const createdAt = toTimestamp(item.created_at);\n return { id: text(item.id) ?? '?', ...(author === undefined ? {} : { author }), body: typeof item.body === 'string' ? item.body : '', ...(createdAt === undefined ? {} : { createdAt }) };\n}\n","// Встроенные трекеры библиотеки @smwb/onreel-git-mcp (часть E выделения, R-001).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/builtin.lib.ts — дословная копия. Регистрация была побочным эффектом импорта\n// guard.lib (onreel); в библиотеке она — только явный вызов registerBuiltinTrackers(), идемпотентный.\nimport { githubTracker } from './github.lib.ts';\nimport { gitlabTracker } from './gitlab.lib.ts';\nimport { jiraTracker } from './jira.lib.ts';\nimport { localTracker } from './local.lib.ts';\nimport { registerTracker } from './registry.lib.ts';\nimport { sourcecraftTracker } from './sourcecraft.lib.ts';\n\n// Трекеры, которые библиотека поставляет сама (D-350). Новый трекер добавляется модулем с\n// `TrackerDefinition` и строкой здесь: реестр и адаптеры не меняются.\n\nlet registered = false;\n\nexport function registerBuiltinTrackers(): void {\n if (registered) return;\n registered = true;\n for (const definition of [localTracker(), sourcecraftTracker(), githubTracker(), gitlabTracker(), jiraTracker()]) registerTracker(definition);\n}\n","// Хранение токенов подключений трекеров библиотеки @smwb/onreel-git-mcp (часть E выделения, D-352).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/host/tracker/secrets.lib.ts — копия; шов журнала host заменён на SecretSink ядра\n// (kernel/log.types.ts): по умолчанию defaultSecretSink общего redactor'а ядра (kernel/redact.lib.ts),\n// потребитель со своим журналом подставляет свой приёмник. Регистрация секрета не обязательна\n// для работы хранилища — это подсказка для маскирования текстов ошибок и журналов.\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { defaultSecretSink } from '../kernel/redact.lib.ts';\nimport type { SecretSink } from '../kernel/log.types.ts';\nimport { TRACKER_SECRETS_FILE } from './tracker.const.ts';\nimport { isRecord } from './tracker.lib.ts';\n\n// Секреты подключений (D-352): токены по projectId в файле 0600 каталога данных потребителя. В хранилище\n// задач, ответы и журналы они не попадают: наружу идёт только признак «токен есть», а значение при чтении\n// и записи регистрируется в приёмнике секретов как известный секрет (D-194).\n\n/** Записи идут по одной: чтение-правка-запись двух подключений не затирает друг друга. */\nlet queue: Promise<unknown> = Promise.resolve();\n\nfunction exclusive<T>(task: () => Promise<T>): Promise<T> {\n const run = queue.then(task, task);\n queue = run.catch(() => undefined);\n return run;\n}\n\nasync function readAll(dataDir: string): Promise<Record<string, string>> {\n let raw: string;\n try {\n raw = await readFile(join(dataDir, TRACKER_SECRETS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw error;\n }\n const parsed: unknown = JSON.parse(raw);\n if (!isRecord(parsed)) return {};\n return Object.fromEntries(Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === 'string'));\n}\n\nasync function writeAll(dataDir: string, secrets: Record<string, string>): Promise<void> {\n await mkdir(dataDir, { recursive: true, mode: 0o700 });\n const target = join(dataDir, TRACKER_SECRETS_FILE);\n const temp = `${target}.${String(process.pid)}.tmp`;\n await writeFile(temp, JSON.stringify(secrets), { mode: 0o600 });\n await rename(temp, target);\n}\n\n/** Токен проекта; undefined — не задан. Значение регистрируется в приёмнике секретов. */\nexport async function readTrackerToken(dataDir: string, projectId: string, sink: SecretSink = defaultSecretSink): Promise<string | undefined> {\n const token = (await readAll(dataDir))[projectId];\n if (token !== undefined) sink.register(token);\n return token;\n}\n\nexport function hasTrackerToken(dataDir: string, projectId: string): Promise<boolean> {\n return readAll(dataDir).then((secrets) => secrets[projectId] !== undefined);\n}\n\n/** Задаёт токен проекта; пустая строка убирает. */\nexport function writeTrackerToken(dataDir: string, projectId: string, token: string, sink: SecretSink = defaultSecretSink): Promise<void> {\n if (token !== '') sink.register(token);\n return exclusive(async () => {\n const secrets = await readAll(dataDir);\n if (token === '') delete secrets[projectId];\n else secrets[projectId] = token;\n await writeAll(dataDir, secrets);\n });\n}\n\nexport function deleteTrackerToken(dataDir: string, projectId: string, sink: SecretSink = defaultSecretSink): Promise<void> {\n return writeTrackerToken(dataDir, projectId, '', sink);\n}\n"],"mappings":"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","names":["TARGET","issueOf","pageOf","stateFilter","composeLabels","patchBody","commentOf","toIssue","issueOf","params","body","record","commentOf","priorityName","DEFAULT_API","TARGET","toIssue","issueOf","patchBody","commentOf"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';
|
|
2
|
+
/** SourceCraft: системные статусы. На паузе — ещё не закончено, но и не в работе: `open`. */
|
|
3
|
+
export declare const SOURCECRAFT_STATUSES: Readonly<Record<string, IssueStatusCategory>>;
|
|
4
|
+
/** Категория → слаг статуса SourceCraft при переводе задачи. */
|
|
5
|
+
export declare const SOURCECRAFT_STATUS_SLUGS: Readonly<Record<IssueStatusCategory, string>>;
|
|
6
|
+
/** Приоритеты SourceCraft совпадают с единой моделью по именам. */
|
|
7
|
+
export declare const SOURCECRAFT_PRIORITIES: Readonly<Record<string, IssuePriority>>;
|
|
8
|
+
/** Jira: `statusCategory.key` статуса. */
|
|
9
|
+
export declare const JIRA_STATUS_CATEGORIES: Readonly<Record<string, IssueStatusCategory>>;
|
|
10
|
+
/** Jira: имена приоритетов схемы по умолчанию (регистр не важен, ключи в нижнем). */
|
|
11
|
+
export declare const JIRA_PRIORITIES: Readonly<Record<string, IssuePriority>>;
|
|
12
|
+
/** Категория → имя приоритета Jira при создании и правке. */
|
|
13
|
+
export declare const JIRA_PRIORITY_NAMES: Readonly<Record<IssuePriority, string>>;
|
|
14
|
+
/** Тип задачи Jira, который создаёт адаптер. */
|
|
15
|
+
export declare const JIRA_ISSUE_TYPE = "Task";
|
|
16
|
+
/** GitHub: причина закрытия `state_reason`. */
|
|
17
|
+
export declare const GITHUB_CLOSE_REASONS: Readonly<Record<string, IssueStatusCategory>>;
|
|
18
|
+
/** GitLab: приоритеты нет, но принято помечать задачи метками `priority::<имя>`; метка читается как приоритет. */
|
|
19
|
+
export declare const GITLAB_PRIORITY_LABELS: Readonly<Record<string, IssuePriority>>;
|
|
20
|
+
/** Метка GitLab, которой адаптер отмечает «в работе»: у GitLab только opened и closed. */
|
|
21
|
+
export declare const GITLAB_IN_PROGRESS_LABEL = "status::in-progress";
|
|
22
|
+
/** Метка GitHub для «в работе» — по той же причине. */
|
|
23
|
+
export declare const GITHUB_IN_PROGRESS_LABEL = "in progress";
|
|
24
|
+
/** Метки GitHub для приоритета: `priority: <имя>`. */
|
|
25
|
+
export declare const GITHUB_PRIORITY_LABELS: Readonly<Record<string, IssuePriority>>;
|
|
26
|
+
export declare const ALL_STATUSES: readonly IssueStatusCategory[];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TrackerDefinition } from './tracker.types.ts';
|
|
2
|
+
/** Регистрирует трекер; повтор того же вида — ошибка сборки, а не замена. */
|
|
3
|
+
export declare function registerTracker(definition: TrackerDefinition): void;
|
|
4
|
+
export declare function findTracker(kind: string): TrackerDefinition | undefined;
|
|
5
|
+
export declare function listTrackers(): TrackerDefinition[];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SecretSink } from '../kernel/log.types.ts';
|
|
2
|
+
/** Токен проекта; undefined — не задан. Значение регистрируется в приёмнике секретов. */
|
|
3
|
+
export declare function readTrackerToken(dataDir: string, projectId: string, sink?: SecretSink): Promise<string | undefined>;
|
|
4
|
+
export declare function hasTrackerToken(dataDir: string, projectId: string): Promise<boolean>;
|
|
5
|
+
/** Задаёт токен проекта; пустая строка убирает. */
|
|
6
|
+
export declare function writeTrackerToken(dataDir: string, projectId: string, token: string, sink?: SecretSink): Promise<void>;
|
|
7
|
+
export declare function deleteTrackerToken(dataDir: string, projectId: string, sink?: SecretSink): Promise<void>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Потолок ожидания ответа внешнего трекера, мс. */
|
|
2
|
+
export declare const TRACKER_TIMEOUT_MS = 15000;
|
|
3
|
+
/** Размер страницы списка задач по умолчанию; потолок — 500 по контракту. */
|
|
4
|
+
export declare const TRACKER_PAGE_LIMIT_DEFAULT = 50;
|
|
5
|
+
/** Сколько символов тела ответа трекера попадает в текст ошибки. */
|
|
6
|
+
export declare const TRACKER_ERROR_BODY_MAX = 300;
|
|
7
|
+
/** Файл секретов подключений в каталоге данных host: токены трекеров по projectId, права 0600 (D-352). */
|
|
8
|
+
export declare const TRACKER_SECRETS_FILE = "tracker-secrets.json";
|
|
9
|
+
/** Автор комментариев, которые пользователь пишет в локальном трекере. */
|
|
10
|
+
export declare const TRACKER_LOCAL_AUTHOR = "owner";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IssuePriority, IssueStatusCategory } from '../contract/contract.types.ts';
|
|
2
|
+
import type { TrackerFailure } from './tracker.types.ts';
|
|
3
|
+
/** Отказ адаптера: потребитель превращает его в ошибку своего контракта (D-353). */
|
|
4
|
+
export declare class TrackerError extends Error {
|
|
5
|
+
readonly failure: TrackerFailure;
|
|
6
|
+
constructor(failure: TrackerFailure, message: string);
|
|
7
|
+
}
|
|
8
|
+
/** Время трекера (ISO 8601) в миллисекунды; нет или не разобралось — undefined. */
|
|
9
|
+
export declare function toTimestamp(value: unknown): number | undefined;
|
|
10
|
+
/** Поле, которое задано: непустая строка. */
|
|
11
|
+
export declare function text(value: unknown): string | undefined;
|
|
12
|
+
export declare function clipText(value: string, max: number): string;
|
|
13
|
+
/** Строки из массива неизвестного вида: непустые строки без дублей. */
|
|
14
|
+
export declare function stringList(value: unknown): string[];
|
|
15
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
16
|
+
/** Категория и приоритет по таблице; нет в таблице — запасное значение. */
|
|
17
|
+
export declare function lookup<T>(table: Readonly<Record<string, T>>, key: unknown, fallback: T): T;
|
|
18
|
+
export declare function requireTarget(target: string | undefined, hint: string): string;
|
|
19
|
+
/** Обратная таблица: категория → первое имя статуса трекера. */
|
|
20
|
+
export declare function nameOf(table: Readonly<Record<string, IssueStatusCategory>>, category: IssueStatusCategory): string | undefined;
|
|
21
|
+
export declare function priorityName(table: Readonly<Record<string, IssuePriority>>, priority: IssuePriority): string | undefined;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Issue, IssueComment, IssuePriority, IssueStatusCategory, TrackerDescriptor } from '../contract/contract.types.ts';
|
|
2
|
+
import type { LocalIssueStore } from '../kernel/store.types.ts';
|
|
3
|
+
export type TrackerFetch = (input: string, init?: {
|
|
4
|
+
method?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
body?: string;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}) => Promise<Response>;
|
|
9
|
+
/** Подключение проекта с расшифрованным секретом: живёт только в памяти на время вызова. */
|
|
10
|
+
export type TrackerConnectionData = {
|
|
11
|
+
url?: string | undefined;
|
|
12
|
+
target?: string | undefined;
|
|
13
|
+
user?: string | undefined;
|
|
14
|
+
token?: string | undefined;
|
|
15
|
+
};
|
|
16
|
+
export type AdapterContext = {
|
|
17
|
+
projectId: string;
|
|
18
|
+
connection: TrackerConnectionData;
|
|
19
|
+
fetch: TrackerFetch;
|
|
20
|
+
/** Порт хранения локальных задач: нужен только локальному трекеру. */
|
|
21
|
+
store: LocalIssueStore;
|
|
22
|
+
/** Автор комментариев локального трекера. */
|
|
23
|
+
author: string;
|
|
24
|
+
};
|
|
25
|
+
export type PageQuery = {
|
|
26
|
+
limit: number;
|
|
27
|
+
cursor?: string | undefined;
|
|
28
|
+
};
|
|
29
|
+
export type IssueQuery = PageQuery & {
|
|
30
|
+
status?: readonly IssueStatusCategory[] | undefined;
|
|
31
|
+
};
|
|
32
|
+
export type TrackerPage<T> = {
|
|
33
|
+
items: T[];
|
|
34
|
+
nextCursor?: string | undefined;
|
|
35
|
+
};
|
|
36
|
+
export type NewIssueInput = {
|
|
37
|
+
title: string;
|
|
38
|
+
description?: string | undefined;
|
|
39
|
+
priority?: IssuePriority | undefined;
|
|
40
|
+
labels?: readonly string[] | undefined;
|
|
41
|
+
assignee?: string | undefined;
|
|
42
|
+
};
|
|
43
|
+
export type IssuePatchInput = {
|
|
44
|
+
title?: string | undefined;
|
|
45
|
+
description?: string | undefined;
|
|
46
|
+
status?: IssueStatusCategory | undefined;
|
|
47
|
+
priority?: IssuePriority | undefined;
|
|
48
|
+
labels?: readonly string[] | undefined;
|
|
49
|
+
/** null снимает исполнителя. */
|
|
50
|
+
assignee?: string | null | undefined;
|
|
51
|
+
};
|
|
52
|
+
export type TrackerAdapter = {
|
|
53
|
+
list(query: IssueQuery): Promise<TrackerPage<Issue>>;
|
|
54
|
+
get(key: string): Promise<Issue>;
|
|
55
|
+
create(input: NewIssueInput): Promise<Issue>;
|
|
56
|
+
update(key: string, patch: IssuePatchInput): Promise<Issue>;
|
|
57
|
+
comments(key: string, query: PageQuery): Promise<TrackerPage<IssueComment>>;
|
|
58
|
+
comment(key: string, body: string): Promise<IssueComment>;
|
|
59
|
+
};
|
|
60
|
+
/** Подключаемый трекер: описание для интерфейса и фабрика адаптера (D-350). */
|
|
61
|
+
export type TrackerDefinition = {
|
|
62
|
+
descriptor: TrackerDescriptor;
|
|
63
|
+
create(context: AdapterContext): TrackerAdapter;
|
|
64
|
+
};
|
|
65
|
+
export type TrackerFailure = 'failed' | 'unknown_issue' | 'invalid_request';
|
|
66
|
+
export type JsonRequest = {
|
|
67
|
+
fetch: TrackerFetch;
|
|
68
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH';
|
|
69
|
+
headers?: Record<string, string>;
|
|
70
|
+
body?: unknown;
|
|
71
|
+
/** 404 — «нет такой задачи», а не «неверный адрес». */
|
|
72
|
+
missingIsIssue?: boolean;
|
|
73
|
+
};
|
|
74
|
+
export type JsonResponse = {
|
|
75
|
+
body: unknown;
|
|
76
|
+
headers: Headers;
|
|
77
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addWorktree,
|
|
3
|
+
branchSha,
|
|
4
|
+
deleteBranch,
|
|
5
|
+
headOf,
|
|
6
|
+
isDirty,
|
|
7
|
+
isGitPoolClosed,
|
|
8
|
+
pruneWorktrees
|
|
9
|
+
} from "../chunk-GPRMI4PG.js";
|
|
10
|
+
import {
|
|
11
|
+
checkManagedPath,
|
|
12
|
+
prepareWorktreeParent,
|
|
13
|
+
trashRoot
|
|
14
|
+
} from "../chunk-C6PHNSDC.js";
|
|
15
|
+
import "../chunk-3YP22YLA.js";
|
|
16
|
+
import "../chunk-4ZGRXIVA.js";
|
|
17
|
+
import "../chunk-L7CPE2SO.js";
|
|
18
|
+
|
|
19
|
+
// src/worktree/lifecycle.const.ts
|
|
20
|
+
var WORKTREE_RECONCILE_INTERVAL_MS = 3e4;
|
|
21
|
+
|
|
22
|
+
// src/worktree/lifecycle.lib.ts
|
|
23
|
+
import { randomUUID } from "crypto";
|
|
24
|
+
import { mkdirSync, readdirSync, renameSync } from "fs";
|
|
25
|
+
import { rm } from "fs/promises";
|
|
26
|
+
import { join } from "path";
|
|
27
|
+
var HOST_ACTOR = "host";
|
|
28
|
+
var locks = /* @__PURE__ */ new Map();
|
|
29
|
+
async function acquireWorktreeLock(id) {
|
|
30
|
+
const previous = locks.get(id) ?? Promise.resolve();
|
|
31
|
+
let release;
|
|
32
|
+
const current = new Promise((resolve) => release = resolve);
|
|
33
|
+
const chained = previous.then(() => current);
|
|
34
|
+
locks.set(id, chained);
|
|
35
|
+
await previous;
|
|
36
|
+
let released = false;
|
|
37
|
+
return () => {
|
|
38
|
+
if (released) return;
|
|
39
|
+
released = true;
|
|
40
|
+
release();
|
|
41
|
+
if (locks.get(id) === chained) locks.delete(id);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function withWorktreeLock(id, fn) {
|
|
45
|
+
const release = await acquireWorktreeLock(id);
|
|
46
|
+
try {
|
|
47
|
+
return await fn();
|
|
48
|
+
} finally {
|
|
49
|
+
release();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function isWorktreeBusy(id) {
|
|
53
|
+
return locks.has(id);
|
|
54
|
+
}
|
|
55
|
+
async function finishPreparing(services, id) {
|
|
56
|
+
await withWorktreeLock(id, async () => {
|
|
57
|
+
const worktree = services.store.getWorktree(id);
|
|
58
|
+
if (worktree?.status !== "preparing") return;
|
|
59
|
+
let headSha;
|
|
60
|
+
let foreignBranch = false;
|
|
61
|
+
try {
|
|
62
|
+
prepareWorktreeParent(services.dataDir, worktree.path);
|
|
63
|
+
foreignBranch = await branchSha(worktree.repoPath, worktree.branch) !== void 0;
|
|
64
|
+
await addWorktree(worktree.repoPath, worktree.path, worktree.branch, worktree.baseSha);
|
|
65
|
+
headSha = await headOf(worktree.path);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (isGitPoolClosed()) return;
|
|
68
|
+
services.store.inTransaction((tx) => {
|
|
69
|
+
tx.markWorktreeFailed(id, `\u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${messageOf(error)}`, { actor: HOST_ACTOR });
|
|
70
|
+
tx.requestWorktreeCleanup(id, { actor: HOST_ACTOR, discardChanges: true, foreignBranch, evidence: "\u043E\u0447\u0438\u0441\u0442\u043A\u0430 \u043F\u043E\u0441\u043B\u0435 \u043D\u0435\u0443\u0434\u0430\u0447\u043D\u043E\u0433\u043E \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F" });
|
|
71
|
+
});
|
|
72
|
+
await cleanUnderLock(services, id);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
services.store.inTransaction((tx) => tx.markWorktreeReady(id, headSha, { actor: HOST_ACTOR }));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async function finishCleaning(services, id) {
|
|
79
|
+
await withWorktreeLock(id, () => cleanUnderLock(services, id));
|
|
80
|
+
}
|
|
81
|
+
async function cleanUnderLock(services, id) {
|
|
82
|
+
const worktree = services.store.getWorktree(id);
|
|
83
|
+
if (worktree?.status !== "cleaning") return;
|
|
84
|
+
let trashed;
|
|
85
|
+
let flags;
|
|
86
|
+
try {
|
|
87
|
+
const present = checkManagedPath(services.dataDir, worktree.path);
|
|
88
|
+
flags = services.store.worktreeCleanupFlags(id);
|
|
89
|
+
if (present && !flags.discardChanges && await isDirty(worktree.path)) {
|
|
90
|
+
services.store.inTransaction(
|
|
91
|
+
(tx) => tx.markWorktreeFailed(id, "\u0432 worktree \u043D\u0435\u0437\u0430\u043A\u043E\u043C\u043C\u0438\u0447\u0435\u043D\u043D\u044B\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F: \u043E\u0447\u0438\u0441\u0442\u043A\u0430 \u0436\u0434\u0451\u0442 \u0440\u0435\u0448\u0435\u043D\u0438\u044F (worktree.remove \u0441 force: true \u0432\u044B\u0431\u0440\u043E\u0441\u0438\u0442 \u0438\u0445)", {
|
|
92
|
+
actor: HOST_ACTOR
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (present) {
|
|
98
|
+
const trash = trashRoot(services.dataDir);
|
|
99
|
+
mkdirSync(trash, { recursive: true, mode: 448 });
|
|
100
|
+
trashed = join(trash, `${worktree.id}-${randomUUID()}`);
|
|
101
|
+
renameSync(worktree.path, trashed);
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (isGitPoolClosed()) return;
|
|
105
|
+
services.store.inTransaction((tx) => tx.markWorktreeFailed(id, `\u043E\u0447\u0438\u0441\u0442\u043A\u0430 \u043D\u0435 \u0443\u0434\u0430\u043B\u0430\u0441\u044C: ${messageOf(error)}`, { actor: HOST_ACTOR }));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
await releaseGitState(services, worktree, flags.ownsBranch);
|
|
109
|
+
if (isGitPoolClosed()) return;
|
|
110
|
+
if (trashed !== void 0) {
|
|
111
|
+
await rm(trashed, { recursive: true, force: true }).catch((error) => {
|
|
112
|
+
services.log(`worktree ${worktree.id}: trash ${trashed ?? ""} \u043D\u0435 \u0443\u0434\u0430\u043B\u0451\u043D: ${messageOf(error)}`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
services.store.inTransaction((tx) => tx.completeWorktreeCleanup(id, { actor: HOST_ACTOR }));
|
|
116
|
+
}
|
|
117
|
+
async function releaseGitState(services, worktree, ownsBranch) {
|
|
118
|
+
try {
|
|
119
|
+
await pruneWorktrees(worktree.repoPath);
|
|
120
|
+
if (!ownsBranch) return;
|
|
121
|
+
const tip = await branchSha(worktree.repoPath, worktree.branch);
|
|
122
|
+
if (tip !== void 0 && tip === worktree.baseSha) await deleteBranch(worktree.repoPath, worktree.branch);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
services.log(`worktree ${worktree.id}: \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 ${worktree.repoPath} \u043D\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D \u043F\u043E\u0441\u043B\u0435 \u043E\u0447\u0438\u0441\u0442\u043A\u0438: ${messageOf(error)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function reconcileWorktreesAtStart(services) {
|
|
128
|
+
for (const worktree of services.store.worktreesInStatus(["preparing"])) {
|
|
129
|
+
services.store.inTransaction((tx) => {
|
|
130
|
+
tx.markWorktreeFailed(worktree.id, "host \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0449\u0435\u043D \u0432\u043E \u0432\u0440\u0435\u043C\u044F \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F worktree", { actor: HOST_ACTOR });
|
|
131
|
+
tx.requestWorktreeCleanup(worktree.id, { actor: HOST_ACTOR, discardChanges: true, evidence: "\u043E\u0447\u0438\u0441\u0442\u043A\u0430 \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u0435\u0440\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F" });
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function sweepWorktrees(services) {
|
|
136
|
+
for (const worktree of services.store.readyWorktreesOfSettledTasks()) {
|
|
137
|
+
if (isWorktreeBusy(worktree.id)) continue;
|
|
138
|
+
try {
|
|
139
|
+
services.store.inTransaction((tx) => tx.requestWorktreeCleanup(worktree.id, { actor: HOST_ACTOR, evidence: "Task \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430" }));
|
|
140
|
+
} catch (error) {
|
|
141
|
+
services.log(`worktree ${worktree.id}: \u043E\u0447\u0438\u0441\u0442\u043A\u0430 \u043F\u043E\u0441\u043B\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F Task \u043D\u0435 \u043D\u0430\u0447\u0430\u043B\u0430\u0441\u044C: ${messageOf(error)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const worktree of services.store.worktreesInStatus(["cleaning"])) {
|
|
145
|
+
if (isWorktreeBusy(worktree.id)) continue;
|
|
146
|
+
await finishCleaning(services, worktree.id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function emptyTrash(services) {
|
|
150
|
+
const trash = trashRoot(services.dataDir);
|
|
151
|
+
let entries;
|
|
152
|
+
try {
|
|
153
|
+
entries = readdirSync(trash);
|
|
154
|
+
} catch {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
await rm(join(trash, entry), { recursive: true, force: true }).catch((error) => {
|
|
159
|
+
services.log(`trash ${entry} \u043D\u0435 \u0443\u0434\u0430\u043B\u0451\u043D: ${messageOf(error)}`);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function startWorktreeReconciler(services, intervalMs = WORKTREE_RECONCILE_INTERVAL_MS) {
|
|
164
|
+
reconcileWorktreesAtStart(services);
|
|
165
|
+
let current = Promise.resolve();
|
|
166
|
+
let stopped = false;
|
|
167
|
+
const kick = () => {
|
|
168
|
+
if (stopped) return current;
|
|
169
|
+
current = current.then(() => sweepWorktrees(services)).catch((error) => {
|
|
170
|
+
services.log(`reconciler worktree: \u043F\u0440\u043E\u0445\u043E\u0434 \u043D\u0435 \u0443\u0434\u0430\u043B\u0441\u044F: ${messageOf(error)}`);
|
|
171
|
+
});
|
|
172
|
+
return current;
|
|
173
|
+
};
|
|
174
|
+
current = emptyTrash(services);
|
|
175
|
+
void kick();
|
|
176
|
+
const timer = setInterval(() => void kick(), intervalMs);
|
|
177
|
+
timer.unref();
|
|
178
|
+
return {
|
|
179
|
+
kick,
|
|
180
|
+
stop: async () => {
|
|
181
|
+
stopped = true;
|
|
182
|
+
clearInterval(timer);
|
|
183
|
+
await current;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function messageOf(error) {
|
|
188
|
+
return error instanceof Error ? error.message : String(error);
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
WORKTREE_RECONCILE_INTERVAL_MS,
|
|
192
|
+
acquireWorktreeLock,
|
|
193
|
+
emptyTrash,
|
|
194
|
+
finishCleaning,
|
|
195
|
+
finishPreparing,
|
|
196
|
+
isWorktreeBusy,
|
|
197
|
+
messageOf,
|
|
198
|
+
reconcileWorktreesAtStart,
|
|
199
|
+
startWorktreeReconciler,
|
|
200
|
+
sweepWorktrees,
|
|
201
|
+
withWorktreeLock
|
|
202
|
+
};
|
|
203
|
+
//# sourceMappingURL=index.js.map
|