@smwb/onreel-git-mcp 0.10.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +147 -15
  2. package/dist/{chunk-3YP22YLA.js → chunk-33ISKIW7.js} +2 -2
  3. package/dist/{chunk-AGGJK2XZ.js → chunk-AJOAZMU4.js} +26 -1
  4. package/dist/{chunk-AGGJK2XZ.js.map → chunk-AJOAZMU4.js.map} +1 -1
  5. package/dist/{chunk-4ZGRXIVA.js → chunk-D5QUOIRC.js} +5 -1
  6. package/dist/{chunk-4ZGRXIVA.js.map → chunk-D5QUOIRC.js.map} +1 -1
  7. package/dist/{chunk-ZTME4HFD.js → chunk-OWLVTLYZ.js} +3 -3
  8. package/dist/contract/contract.schema.d.ts +12 -0
  9. package/dist/contract/contract.types.d.ts +2 -1
  10. package/dist/files/index.js +1 -1
  11. package/dist/git/index.d.ts +1 -1
  12. package/dist/git/index.js +7 -5
  13. package/dist/index.js +6 -3
  14. package/dist/index.js.map +1 -1
  15. package/dist/kernel/log.types.d.ts +4 -1
  16. package/dist/kernel/redact.lib.d.ts +12 -2
  17. package/dist/mcp/index.js +3 -3
  18. package/dist/tracker/declarative/declarative-call.lib.d.ts +28 -0
  19. package/dist/tracker/declarative/declarative-capabilities.lib.d.ts +20 -0
  20. package/dist/tracker/declarative/declarative-ci.lib.d.ts +4 -0
  21. package/dist/tracker/declarative/declarative-hint.const.d.ts +4 -0
  22. package/dist/tracker/declarative/declarative-issues.lib.d.ts +5 -0
  23. package/dist/tracker/declarative/declarative-model.const.d.ts +40 -0
  24. package/dist/tracker/declarative/declarative-model.types.d.ts +87 -0
  25. package/dist/tracker/declarative/declarative-parse.lib.d.ts +7 -0
  26. package/dist/tracker/declarative/declarative-pull.lib.d.ts +4 -0
  27. package/dist/tracker/declarative/declarative-request.lib.d.ts +49 -0
  28. package/dist/tracker/declarative/declarative-result.lib.d.ts +11 -0
  29. package/dist/tracker/declarative/declarative-rules.lib.d.ts +4 -0
  30. package/dist/tracker/declarative/declarative.const.d.ts +10 -0
  31. package/dist/tracker/declarative/declarative.lib.d.ts +19 -0
  32. package/dist/tracker/declarative/declarative.schema.d.ts +1324 -0
  33. package/dist/tracker/declarative/declarative.types.d.ts +22 -0
  34. package/dist/tracker/index.d.ts +6 -0
  35. package/dist/tracker/index.js +1941 -22
  36. package/dist/tracker/index.js.map +1 -1
  37. package/dist/tracker/registry.lib.d.ts +8 -1
  38. package/dist/worktree/index.js +4 -4
  39. package/package.json +2 -1
  40. /package/dist/{chunk-3YP22YLA.js.map → chunk-33ISKIW7.js.map} +0 -0
  41. /package/dist/{chunk-ZTME4HFD.js.map → chunk-OWLVTLYZ.js.map} +0 -0
package/README.md CHANGED
@@ -11,11 +11,12 @@
11
11
  против портов хранения, которые реализует потребитель;
12
12
  - **адаптеры трекеров** — единая модель задач и пять адаптеров: GitHub, GitLab, Jira, SourceCraft и локальный
13
13
  трекер поверх хранилища потребителя; у GitHub, GitLab и SourceCraft есть разделы pull request-ов и CI (D-005)
14
- и раздел репозитория — карточка и ветки (D-008);
14
+ и раздел репозитория — карточка и ветки (D-008); свои трекеры пользователя — декларативным конфигом без кода
15
+ (D-010, D-015);
15
16
  - **MCP stdio-сервер** — JSON-RPC 2.0 поверх stdin/stdout, инструменты строятся из таблицы команд вызывающего.
16
17
 
17
- Пакет ESM, рантайм-зависимость одна — `zod`. Публичная поверхность держится документированной здесь:
18
- новый субпуть или публичный символ добавляют вместе с записью в этом README.
18
+ Пакет ESM, рантайм-зависимости — `zod` и `yaml` (разбор конфигов своих трекеров). Публичная поверхность
19
+ держится документированной здесь: новый субпуть или публичный символ добавляют вместе с записью в этом README.
19
20
 
20
21
  ## Установка
21
22
 
@@ -37,7 +38,7 @@ CI и тесты идут на образе `node:24-bookworm`; в репози
37
38
  | `…/git` | `runGit`, `GitError`, управление пулом, операции над репозиторием и worktree, безопасные пути, маскирование секретов | [git](#git) |
38
39
  | `…/files` | `listWorktreeDir`, `DirRefusal` | [files](#files) |
39
40
  | `…/worktree` | замки, `finishPreparing`/`finishCleaning`, reconciler, корзина — против порта `WorktreeStore` | [worktree](#worktree) |
40
- | `…/tracker` | типы портов, реестр, `registerBuiltinTrackers()`, пять фабрик адаптеров, файловое хранилище токенов | [tracker](#tracker) |
41
+ | `…/tracker` | типы портов, реестр, `registerBuiltinTrackers()`, пять фабрик адаптеров, свои трекеры из конфига (`definitionFromConfig`, `setUserTrackers`), файловое хранилище токенов | [tracker](#tracker) |
41
42
  | `…/mcp` | `serveMcp`, `mcpToolSpecs`, сборщики инструментов, выбор принципала | [mcp](#mcp) |
42
43
  | `…/package.json` | манифест пакета | [package.json](#packagejson) |
43
44
 
@@ -121,7 +122,7 @@ R1–R8 (URL с `user:pass@`, `Authorization:`, известные ключи д
121
122
  в приёмнике секретов:
122
123
 
123
124
  ```ts
124
- import { defaultSecretSink, redactSecretText, type SecretSink } from '@smwb/onreel-git-mcp/git';
125
+ import { defaultSecretSink, redactSecretText, setSecretSink, type SecretSink } from '@smwb/onreel-git-mcp/git';
125
126
 
126
127
  defaultSecretSink.register(process.env.MYopaqueValue ?? ''); // короче 8 символов — игнорируется
127
128
  console.log(redactSecretText('клон https://user:секрет@git.example/o/r и MYopaqueValue'));
@@ -133,9 +134,28 @@ const sink: SecretSink = {
133
134
  known.add(secret);
134
135
  },
135
136
  };
137
+
138
+ // Одна точка на процесс: после этого вызова известные секреты получает и sink потребителя.
139
+ setSecretSink(sink);
140
+ setSecretSink(undefined); // убирает приёмник потребителя: redactor ядра эти значения уже знает
136
141
  ```
137
142
 
138
- Значения `SecretSink`, `defaultSecretSink` и `redactSecretText` реэкспортированы из `…/git`; отдельного
143
+ **Приёмник потребителя (D-009).** `setSecretSink(sink)` задаёт приёмник секретов потребителя на весь
144
+ процесс: значения, которые встречают адаптеры трекеров и хранилище токенов, библиотека пишет и в свой
145
+ redactor, и в `sink` — регистрировать их второй раз не нужно. Реестр ядра при этом не отключается:
146
+ тексты, которые библиотека отдаёт наружу сама (JSON-RPC-ошибка `serveMcp`, `GitError`, `TrackerError`
147
+ с телом ответа, окно лога CI), замаскированы и тогда, когда журнал потребителя их не видит. Новый `sink`
148
+ сразу получает значения, которые библиотека встретила до вызова, поэтому порядок «сначала токен, потом
149
+ `setSecretSink`» не оставляет секрета незамаскированным у потребителя. `setSecretSink(undefined)` убирает
150
+ приёмник потребителя; обратно переносить нечего — redactor ядра значения уже знает. Порог 8 символов
151
+ держит ядро, а в `sink` значение уходит как есть: свой порог потребитель задаёт сам. `sink` вправе
152
+ делегировать значение обратно в `defaultSecretSink` — так потребитель зеркалит секрет в реестр
153
+ библиотеки: внутри `register` самого приёмника библиотека обратный вызов пропускает, поэтому рекурсии
154
+ нет, а значение в реестре ядра оказывается до вызова приёмника. Вызов явный (R-001), состояние
155
+ модульное — как redactor и пул git, на процесс одно.
156
+
157
+ Значения `SecretSink`, `defaultSecretSink`, `redactSecretText` и `setSecretSink` реэкспортированы из
158
+ `…/git`; `setSecretSink` и тип `SecretSink` — также из `…/tracker`, рядом с хранилищем токенов. Отдельного
139
159
  субпути `…/kernel` у пакета нет.
140
160
 
141
161
  ## files
@@ -260,7 +280,8 @@ try {
260
280
  Сопоставление статусов и приоритетов — открытые таблицы (`SOURCECRAFT_STATUSES`, `JIRA_PRIORITIES`,
261
281
  `GITHUB_PRIORITY_LABELS`, `GITLAB_PRIORITY_LABELS`, …), чтобы потребитель видел, во что именно преводится его
262
282
  статус. Свои трекеры добавляют в реестр явным `registerTracker(definition)`; повторный `kind` — исключение, а не
263
- молчаливая замена. Типы порта `LocalIssueStore` (и его строк) реэкспортированы из `…/tracker` — реализацию, как и
283
+ молчаливая замена. Трекеры пользователя из конфигов — отдельный заменяемый набор
284
+ `setUserTrackers(definitions)` (подраздел «Свои трекеры из конфига»). Типы порта `LocalIssueStore` (и его строк) реэкспортированы из `…/tracker` — реализацию, как и
264
285
  для `WorktreeStore`, пишет потребитель; эталон в памяти — `test/helpers/tracker-fixtures.lib.ts`. Порт требует:
265
286
  комментарий — только к существующей задаче, иначе отказ с кодом `unknown_issue` (сиротских комментариев хранилище
266
287
  не держит, D-011); `limit` страницы — целое от 1 до 500, по умолчанию 100, значение вне диапазона прижимается
@@ -638,6 +659,95 @@ GitLab читает `^` в начале и `$` в конце `search` как я
638
659
  `gitlabRepo()` и `sourcecraftRepo()`. Всё это выходит из субпути `…/tracker` — нового субпути нет (D-001, D-005).
639
660
  За модель отвечает запись D-008, отказ до запроса — правило R-002.
640
661
 
662
+ ### Свои трекеры из конфига
663
+
664
+ Свой трекер или git-хостинг (Gitea, Forgejo, самописный) подключают без кода и релиза: декларативный конфиг
665
+ `format: 1` — данные, а не код, их собирает в обычный `TrackerDefinition` один доверенный интерпретатор библиотеки
666
+ (D-010). Условий, скриптов и вычислений в конфиге нет: только шаблоны запросов, пути JSON и таблицы значений.
667
+
668
+ ```ts
669
+ import {
670
+ definitionFromConfig,
671
+ setUserTrackers,
672
+ TRACKER_CONFIG_HINT,
673
+ TrackerConfigError,
674
+ validateTrackerConfig,
675
+ } from '@smwb/onreel-git-mcp/tracker';
676
+
677
+ const check = validateTrackerConfig(yamlText); // текст YAML или уже разобранный объект
678
+ if (!check.ok) for (const issue of check.issues) console.log(`${issue.path}: ${issue.message}`); // 'issues.list.query.state: …'
679
+
680
+ try {
681
+ const shadowed = setUserTrackers([definitionFromConfig(yamlText)]); // набор своих целиком; пустой массив снимает
682
+ if (shadowed.length > 0) console.log(`конфиг не действует, вид занят встроенным трекером: ${shadowed.join(', ')}`);
683
+ } catch (error) {
684
+ if (error instanceof TrackerConfigError) console.log(error.issues, error.secret);
685
+ else throw error;
686
+ }
687
+ // Агенту, который пишет конфиг, отдают TRACKER_CONFIG_HINT: формат, правила безопасности и рабочий пример.
688
+ ```
689
+
690
+ - **Проверка.** `validateTrackerConfig(input): TrackerConfigCheck` — без побочных эффектов: `{ ok: true, config }`
691
+ или `{ ok: false, issues, secret }`, где `issues` — все ошибки сразу (не больше `TRACKER_CONFIG_ISSUES_MAX`, 100),
692
+ каждая `TrackerConfigIssue` — `{ path, message }` с путём поля через точку (`fields.0.secret`, пусто — весь
693
+ конфиг), а `secret` — среди ошибок секрет в тексте. Форму держит строгая схема `trackerConfigSchema` (неизвестное
694
+ поле — ошибка; типы `TrackerConfig`, `TrackerConfigInput`, `TrackerConfigOperation`, `TrackerConfigValue`,
695
+ `TrackerConfigBodyValue`, `TrackerConfigPagination`, `TrackerConfigAuth`, `TrackerConfigField`), сообщения — по-русски.
696
+ - **Интерпретатор.** `definitionFromConfig(input): TrackerDefinition` проверяет конфиг (неверный — `TrackerConfigError`
697
+ с `issues` и `secret`) и отдаёт определение с `descriptor.source: 'user'`, разделами `issues`, `pulls` и `ci`
698
+ (если они описаны) и `origin(connection)` — origin `http.base`. Возможности (`ListCapabilities`,
699
+ `PullRequestCapabilities`, `CiCapabilities`, `TrackerFeatures`) выводятся из операций: нет операции или входа в её
700
+ шаблонах — нет возможности, вызов отказывает `unsupported` до запроса. Фильтр списка есть, если его вход стоит в
701
+ шаблонах или `result` заполняет поля для проверки по странице (`clientSide`); вход через таблицу значений
702
+ проверяется и по странице. Каждый элемент ответа проходит схему единой модели (`issueSchema`,
703
+ `pullRequestSchema`, `ciRunSchema`, …): не легло — `failed` с операцией и полем.
704
+ - **Реестр (D-015).** `setUserTrackers(definitions): string[]` заменяет набор своих определений целиком. Определения из кода
705
+ (`registerTracker`, `registerBuiltinTrackers`) побеждают: `findTracker` и `listTrackers` отдают их, а затенённые
706
+ виды возвращаются вызывающему. Повтор `kind` внутри набора — исключение, прежний набор остаётся.
707
+ `TrackerDescriptor.source` — `'builtin' | 'user'` (`trackerSourceSchema`, тип `TrackerSource` корневого субпути);
708
+ нет поля — определение из кода.
709
+ - **Подсказка.** `TRACKER_CONFIG_HINT` — текст для агента (структура, операции и их входы, поля моделей, правила
710
+ безопасности) с рабочим примером `TRACKER_CONFIG_EXAMPLE` (задачи, PR и CI в стиле Gitea; пути — образец, их
711
+ сверяют с документацией своей версии сервиса). `BUILTIN_TRACKER_KINDS` — виды, которые конфиг не занимает.
712
+
713
+ Формат коротко (полностью — в подсказке):
714
+
715
+ ```yaml
716
+ format: 1
717
+ kind: gitea # слаг вида; title — имя в интерфейсе
718
+ title: Gitea
719
+ fields: # поля формы подключения: url, target, user, token (секретно — только token)
720
+ - { name: url, required: true, secret: false }
721
+ - { name: target, required: true, secret: false }
722
+ - { name: token, required: true, secret: true }
723
+ http:
724
+ base: "{{url}}/api/v1" # или https://api.example.test/v1; токен уходит только на этот origin
725
+ auth: { kind: bearer } # bearer | { kind: header, header, prefix } | basic | none
726
+ headers: { Accept: application/json }
727
+ pagination: { style: page, page: page, size: limit, next: link } # page | cursor | none
728
+ issues: # list и get обязательны; create, update, comments, comment — по желанию
729
+ list:
730
+ path: "/repos/{{target|path}}/issues"
731
+ query: { state: "{{status}}" }
732
+ inputs: { status: { open: open, in_progress: open, done: closed, declined: closed, "*": all } }
733
+ result: &issue { key: $.number, title: $.title, status.category: { path: $.state, map: { open: open, closed: done } } }
734
+ get: { path: "/repos/{{target|path}}/issues/{{key}}", result: *issue }
735
+ pulls: { … } # list, get; create, update, merge, comments, comment
736
+ ci: { … } # runs, run; workflows, jobs, job, log, trigger, rerun, rerunFailed, rerunJob, cancel, cancelJob
737
+ ```
738
+
739
+ Шаблоны подставляют только поля подключения `{{url}}`, `{{target}}`, `{{user}}` (объявленные в `fields`) и входы
740
+ операции (ветки PR — `{{sourceBranch}}` и `{{targetBranch}}`); `{{token}}` нет. В пути значение — один
741
+ percent-кодированный сегмент, `{{имя|path}}` — сегменты через «/» без пустых, «.» и «..»; «\» и управляющие
742
+ символы — отказ, а входу операции нельзя и «/» вне `|path`, и закодированного `%2F`/`%5C` (поле подключения в
743
+ простом плейсхолдере уходит с «/» как `%2F`, как проект `o%2Fr` у GitLab). Нет значения — параметр
744
+ query или поле тела пропускаются, в пути — отказ. Поле тела, равное одному плейсхолдеру, сохраняет тип. `inputs` —
745
+ таблицы «значение единой модели → значение API», ключ `"*"` у фильтров списков — «несколько сразу», `"null"` у
746
+ assignee. `result` — путь JSON (`$.a.b`, `$.items[0]`, `$.labels[*].name`), `{ path, map, default, exists }` или
747
+ цепочка звеньев. Пределы — `TRACKER_CONFIG_MAX_BYTES` (256 КиБ), `TRACKER_CONFIG_YAML_ALIAS_MAX` (не больше ~100
748
+ разворачиваний ссылок YAML с учётом вложенности: якорь считается тоже, поэтому сотня ссылок на один якорь — уже
749
+ отказ), `TRACKER_CONFIG_DEPTH_MAX` (16 уровней вложенности под корнем конфига). Правила безопасности — в разделе «Безопасность».
750
+
641
751
  ## mcp
642
752
 
643
753
  `serveMcp` — построчный JSON-RPC 2.0 на stdio, только инструменты: `initialize`, `ping`, `tools/list`,
@@ -782,7 +892,8 @@ console.log(manifest.name, manifest.version);
782
892
  `<dataDir>/tracker-secrets.json` с правами **0600** (каталог — 0700), запись идёт через временный файл и
783
893
  `rename`, одиночная запись сериализуется очередью, чтобы два подключения не затерли друг друга. `deleteTrackerToken`
784
894
  и пустая строка убирают значение. Наружу из хранилища выходит только признак «токен есть» (`hasTrackerToken`),
785
- а при чтении и записи значение регистрируется в приёмнике секретов.
895
+ а при чтении и записи значение регистрируется в приёмнике секретов — redactor ядра и, если он задан,
896
+ приёмнике потребителя (см. «Маскирование секретов» ниже).
786
897
  - **Токен — только своему origin.** `origin(connection)` в `TrackerDefinition` — origin API, куда адаптер (и его
787
898
  разделы `pulls`, `ci` и `repo`) ходит с токеном: `https://api.github.com`, `https://gitlab.com`,
788
899
  `https://api.sourcecraft.tech` по умолчанию, иначе origin `url` подключения; у Jira `url` обязателен. Потребитель
@@ -799,11 +910,31 @@ console.log(manifest.name, manifest.version);
799
910
  заголовков запроса; иной метод — `failed` без перехода. Так 302 GitHub на подписанный адрес лога не уносит
800
911
  токен, какой бы `fetch` ни пришёл в контексте. Больше `TRACKER_REDIRECT_MAX` переходов подряд и редирект не на
801
912
  http/https — `failed`.
913
+ - **Конфиг своего трекера — данные.** `definitionFromConfig` не исполняет кода пользователя: шаблоны подставляют
914
+ только поля подключения и входы операции, плейсхолдера `{{token}}` нет — токен подставляет только `http.auth`, и
915
+ только для запросов на origin `http.base` (он же — `origin(connection)`; запросы идут через `requestJson` и
916
+ `requestText` с этим origin, редиректы — по правилам выше). Значения в пути кодируются как сегменты (`..`, `.`
917
+ и пустые сегменты — отказ), путь операции начинается с одного «/» и не содержит «?», «#», «%», «\» и сегментов
918
+ «.»/«..», а собранный адрес сверяется с origin и путём базы до сети. После `{{url}}` в базе — только путь.
919
+ Секретные заголовки в `http.headers` (Authorization, Cookie, X-Api-Key, *-Token…) и секретные параметры query
920
+ запрещены; текст, похожий на секрет (значения, ключи, комментарии YAML, адрес с паролем), — ошибка с признаком
921
+ `secret`. Конфиг не больше 256 КиБ, ссылки YAML и вложенность ограничены, ключи `__proto__`, `constructor` и
922
+ `prototype` запрещены. Каждый ответ API проходит схему единой модели, ссылки — только http(s).
923
+ Библиотека гарантирует «токен — только на origin `http.base`», но сам этот origin выбирает автор конфига: литеральная
924
+ `http.base` (`https://attacker.example`, `https://169.254.169.254/latest`) проходит проверку, и токен уйдёт туда, а
925
+ `result` вынесет агенту поля ответа внутреннего адреса. Поэтому потребитель, принимая конфиг (особенно написанный
926
+ агентом), показывает пользователю `definitionFromConfig(config).origin(connection)` и привязывает токен к этому
927
+ origin до первого запроса (как при правке `url`, см. «Токен — только своему origin»); политика исходящих запросов
928
+ (частные диапазоны, loopback, метаданные облака) — в `fetch`, который потребитель передаёт в контексте.
802
929
  - **Маскирование секретов.** `redactSecretText` и `SecretSink` (по умолчанию `defaultSecretSink`) — один redactor
803
930
  на библиотеку: его применяют `GitError` (D-272), `requestJson` (тексты отказов трекера) и внутренний отказ
804
931
  `serveMcp`. Свой журнал потребителя подключают приёмником `SecretSink`; известные значения добавляют до
805
932
  форматирования текста, короткая строка (менее 8 символов) не регистрируется — она дала бы слишком много ложных
806
- срабатываний.
933
+ срабатываний. `setSecretSink(sink)` (D-009) задаёт приёмник потребителя на весь процесс: известные секреты,
934
+ включая токены из хранилища, библиотека пишет и в свой redactor, и в `sink`, поэтому второй раз регистрировать
935
+ их не нужно, а тексты, которые библиотека отдаёт наружу сама, замаскированы и без журнала потребителя. Новый
936
+ `sink` сразу получает значения, встреченные до вызова. В `sink` значение уходит как есть: порог 8 символов
937
+ держит redactor ядра, свой порог потребитель задаёт сам.
807
938
  - **Ограничение путей.** `checkRepoPath` отказывает базовому репозиторию, который абсолютным путём или по realpath
808
939
  лежит внутри каталога данных (там ledger и токены), `prepareWorktreeParent` создаёт каталоги по одному и не
809
940
  принимает симлинк, `checkManagedPath` перед удалением проверяет и сам каталог, и его realpath. `listWorktreeDir`
@@ -821,12 +952,13 @@ console.log(manifest.name, manifest.version);
821
952
  - Node.js >= 24, Linux; macOS/Windows не тестируются (см. `/proc` и групповой `SIGKILL` выше).
822
953
  - Публикуется собранный ESM и `.d.ts` из `dist/` (D-002) — суффиксы `.ts` в импортах исходников стирает сборка,
823
954
  их не нужно править в потребителе.
824
- - `sideEffects: false`, один рантайм-зависимый пакет — `zod`.
825
- - Реестр трекеров, пул git, замки worktree и redactor — модульная глобальна на процесс: несколько независимых
826
- «хостов» в одном процессе делят их. Субпути пакета делят такое состояние, потому что модуль двух и более точек
827
- входа tsup выносит в общий chunk; свою копию дал бы только бандл без splitting — инвариант одного определения
828
- в собранном пакете держит проверка (D-013). Две независимо установленные копии пакета состояние не делят.
829
- Запросы к внешним API идут `fetch` из контекста; узлы без TLS-цепей внешнего трекера — задача вызывающего.
955
+ - `sideEffects: false`, рантайм-зависимости — `zod` и `yaml`.
956
+ - Реестр трекеров, пул git, замки worktree, redactor и приёмник секретов потребителя (`setSecretSink`) —
957
+ модульная глобальна на процесс: несколько независимых «хостов» в одном процессе делят их. Субпути пакета
958
+ делят такое состояние, потому что модуль двух и более точек входа tsup выносит в общий chunk; свою копию
959
+ дал бы только бандл без splitting — инвариант одного определения в собранном пакете держит проверка (D-013).
960
+ Две независимо установленные копии пакета состояние не делят. Запросы к внешним API идут `fetch` из
961
+ контекста; узлы без TLS-цепей внешнего трекера — задача вызывающего.
830
962
  - Имена `ONREEL_*` и `HOST_CONTRACT_VERSION` (`1.14`) заморожены: они держат interop с onreel.
831
963
 
832
964
  ## Как внести изменение
@@ -4,7 +4,7 @@ import {
4
4
  DEFAULT_SESSION_UMASK,
5
5
  ENV_SESSION_UMASK,
6
6
  contractErrorSchema
7
- } from "./chunk-4ZGRXIVA.js";
7
+ } from "./chunk-D5QUOIRC.js";
8
8
 
9
9
  // src/contract/contract.lib.ts
10
10
  function clip(text, max) {
@@ -47,4 +47,4 @@ export {
47
47
  sessionUmask,
48
48
  inheritSessionUmask
49
49
  };
50
- //# sourceMappingURL=chunk-3YP22YLA.js.map
50
+ //# sourceMappingURL=chunk-33ISKIW7.js.map
@@ -153,15 +153,40 @@ var kernelRedactor = createRedactor();
153
153
  function redactSecretText(text) {
154
154
  return kernelRedactor.redactText(text);
155
155
  }
156
+ var consumerSecretSink;
157
+ var seenSecrets = [];
158
+ var dispatchingToConsumer = false;
159
+ function rememberSecret(secret) {
160
+ if (!seenSecrets.includes(secret)) seenSecrets = [...seenSecrets, secret];
161
+ }
162
+ function dispatchToConsumer(secret) {
163
+ const sink = consumerSecretSink;
164
+ if (sink === void 0 || dispatchingToConsumer) return;
165
+ dispatchingToConsumer = true;
166
+ try {
167
+ sink.register(secret);
168
+ } finally {
169
+ dispatchingToConsumer = false;
170
+ }
171
+ }
172
+ function setSecretSink(sink) {
173
+ consumerSecretSink = sink;
174
+ if (sink === void 0) return;
175
+ for (const secret of seenSecrets) dispatchToConsumer(secret);
176
+ }
156
177
  var defaultSecretSink = {
157
178
  register(secret) {
179
+ rememberSecret(secret);
158
180
  kernelRedactor.addKnownSecret(secret);
181
+ dispatchToConsumer(secret);
159
182
  }
160
183
  };
161
184
 
162
185
  export {
163
186
  REDACTED,
187
+ isSecretKey,
164
188
  redactSecretText,
189
+ setSecretSink,
165
190
  defaultSecretSink
166
191
  };
167
- //# sourceMappingURL=chunk-AGGJK2XZ.js.map
192
+ //# sourceMappingURL=chunk-AJOAZMU4.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/kernel/redact.lib.ts"],"sourcesContent":["// Маскирование секретов в текстах библиотеки @smwb/onreel-git-mcp (заморожено в части B выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/contract/log/log.lib.ts — движок createRedactor (правила R1–R8), дословная копия;\n// src/contract/log/log.const.ts — REDACTED, SECRET_KEY_PATTERN, дословная копия;\n// src/contract/log/log.types.ts — тип Redactor, дословная копия;\n// src/host/redact.lib.ts — redactSecretText; в onreel она работает на redactor'е журнала host\n// (hostLogger().redactor), которого в библиотеке нет, поэтому здесь — общий redactor уровня\n// модуля; регистрация известных секретов идёт через defaultSecretSink (шов SecretSink,\n// kernel/log.types.ts) на том же redactor'е — семантика onreel сохранена 1:1.\n// Побочных эффектов при импорте нет: redactor — чистый объект в памяти (R-001).\nimport type { SecretSink } from './log.types.ts';\n\nexport const REDACTED = '***';\n\n/** Имя ключа секретное, если после приведения к snake_case оно содержит одно из слов целиком. */\nexport const SECRET_KEY_PATTERN =\n /(?:^|[^a-z])(?:token|secrets?|passw(?:or)?d|pass|passphrase|pwd|api_?key|apikey|authorization|auth|cookie|set_cookie|credentials?|private_?key|access_?key|session_?id|signature)(?:$|[^a-z])/;\n\n/** Redactor журнала: маскирует свободный текст и структурные значения, принимает известные секреты. */\nexport type Redactor = {\n /** Маскирует свободный текст: известные значения и шаблоны R1–R8. */\n redactText(text: string): string;\n /** Глубокая копия: ключи по isSecretKey → '***' (любое значение, кроме null и boolean); строки и сами имена ключей → redactText. */\n redactValue(value: unknown): unknown;\n /** Регистрирует известное значение секрета; короче 8 символов — игнорируется. */\n addKnownSecret(value: string): void;\n};\n\n// ---------------------------------------------------------------------------------------------\n// Маскирование (D-194)\n// ---------------------------------------------------------------------------------------------\n\n/** Однозначные слова как окончание слитного имени: `PGPASSWORD`, `MYSQLPASSWORD`, `APITOKEN`, `dbSecret`. */\nconst SECRET_KEY_SUFFIX = /(?:token|secret|passw(?:or)?d|passphrase|apikey)$/;\n/** Голое `key` как последнее слово — секрет только у имён в стиле env (`OPENAI_KEY`): у `push_key`, `manifest_key` это идентификаторы. */\nconst ENV_KEY_SUFFIX = /(?:^|_)key$/;\n\nconst secretKeyCache = new Map<string, boolean>();\nconst SECRET_KEY_CACHE_MAX = 1_000;\n/** Длинные имена в кеш не попадают: ключ data размером с мегабайт, помноженный на 1000 записей, держал бы гигабайт. */\nconst SECRET_KEY_CACHE_KEY_MAX_CHARS = 128;\n\n/** camelCase → snake, '-' и '.' → '_', нижний регистр, затем SECRET_KEY_PATTERN и окончания слитных имён. */\nexport function isSecretKey(key: string): boolean {\n const cacheable = key.length <= SECRET_KEY_CACHE_KEY_MAX_CHARS;\n const cached = cacheable ? secretKeyCache.get(key) : undefined;\n if (cached !== undefined) return cached;\n const normalized = key\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[-.]/g, '_')\n .toLowerCase();\n const envStyle = !/[a-z]/.test(key);\n const secret = SECRET_KEY_PATTERN.test(normalized) || SECRET_KEY_SUFFIX.test(normalized) || (envStyle && ENV_KEY_SUFFIX.test(normalized));\n if (cacheable) {\n if (secretKeyCache.size >= SECRET_KEY_CACHE_MAX) secretKeyCache.clear();\n secretKeyCache.set(key, secret);\n }\n return secret;\n}\n\nconst MIN_KNOWN_SECRET_CHARS = 8;\n\n// Шаблоны R1–R7 применяются после R8. Все линейные по длине входа: у каждого начало совпадения\n// привязано к границе (\\b или lookbehind), чтобы длинный ряд символов не перебирался с каждой позиции.\nconst PEM_PRIVATE_KEY = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g;\nconst ONREEL_TOKEN = /onr_[a-z]+_[A-Za-z0-9_-]{8,}/g;\nconst VENDOR_KEY =\n /\\b(?:sk-(?:ant-|proj-)?[A-Za-z0-9_-]{20,}|gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{30,}|xox[abposr]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|glpat-[A-Za-z0-9_-]{20,})\\b/g;\nconst JWT = /(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{8,}\\.eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}/g;\n// Перед схемой допустим один маркер `+`/`-`, если он начинает токен (начало строки, несхемный символ или\n// SGR-escape): так маскируется URL в строке diff, в том числе цветной (`\\x1b[32m+https://u:p@h`).\nconst URL_USERINFO =\n // oxlint-disable-next-line no-control-regex -- ESC перед маркером: строка цветного diff.\n /(?:(?<![a-z0-9+.-])|(?<=(?:^|[^a-z0-9+.-]|\\x1b\\[[0-9;]*m)[+-]))([a-z][a-z0-9+.-]{0,31}:\\/\\/)[^\\s/@:]+:[^\\s/@]+@/gim;\nconst AUTH_SCHEME = /\\b(Bearer|Basic|Token)\\s+[A-Za-z0-9._~+/=-]{8,}/gi;\n// R3 разбит на поиск «ключ + разделитель» и ручное чтение значения: значение несекретного ключа\n// не съедается, и `url=http://h/?access_token=abc` всё равно доходит до access_token.\nconst KEY_ASSIGNMENT = /(?<![A-Za-z0-9_.-])([A-Za-z][A-Za-z0-9_.-]{0,63})([\"']?\\s*[=:]\\s*)([\"']?)/g;\nconst ASSIGNMENT_VALUE = /((?:Bearer|Basic|Token)[ \\t]+)?[^\\s\"'&,;}]+/iy;\n// Значение cookie — вся строка заголовка: `Cookie: theme=dark; sid=…`, а не до первой `;`.\nconst COOKIE_VALUE = /[^\\r\\n]+/y;\nconst COOKIE_KEY = /^(?:set[_.-]?)?cookie$/i;\n// Грубый предфильтр R3: нет ни одного слова из SECRET_KEY_PATTERN — разбирать присваивания незачем.\nconst SECRET_KEYWORD = /token|secret|pass|pwd|key|auth|cookie|credential|session[_.-]?id|signature/i;\n// Пароль в аргументах curl и подобных: `-u user:pass`, `-uuser:pass`, `--user user:pass`, `--user=user:pass`.\n// Имя пользователя остаётся. Здесь только голова до двоеточия: классы разделителя и имени не пересекаются\n// (общий `[ \\t=]+` и `[^\\s:\"']+` оба принимали `=` и давали квадратичный перебор на ` -u ====…`), а сам\n// пароль читает maskCliPasswords, чтобы пароль в кавычках с пробелами маскировался целиком.\nconst CLI_USER_HEAD = /(?<![^\\s])(?:-u[ \\t]*|--user(?:[ \\t]+|=))([\"']?)([^\\s:\"']+):/g;\nconst CLI_PASSWORD = /[^\\s\"']+/y;\n/** Потолок значения в кавычках: поиск закрывающей кавычки остаётся линейным по входу. */\nconst QUOTED_VALUE_MAX_CHARS = 4_096;\n\n/**\n * Конец значения в кавычках: позиция закрывающей кавычки того же вида, `\\`-экранирование учитывается.\n * Закрывающей нет в пределах потолка — значение режется по потолку или концу текста: лучше замаскировать\n * лишнее, чем оставить хвост секрета.\n */\nfunction quotedValueEnd(text: string, start: number, quote: string): number {\n const limit = Math.min(text.length, start + QUOTED_VALUE_MAX_CHARS);\n for (let index = start; index < limit; index += 1) {\n const char = text[index];\n if (char === '\\\\') index += 1;\n else if (char === quote) return index;\n }\n return limit;\n}\n\n/** Пароль после `-u user:` / `--user user:`: слово без кавычек либо значение в кавычках целиком, с пробелами внутри. */\nfunction maskCliPasswords(text: string): string {\n if (!text.includes('-u')) return text;\n const headPattern = new RegExp(CLI_USER_HEAD.source, 'g');\n const passwordPattern = new RegExp(CLI_PASSWORD.source, 'y');\n let result = '';\n let copied = 0;\n for (let head = headPattern.exec(text); head !== null; head = headPattern.exec(text)) {\n let quote = head[1] ?? '';\n let passwordStart = head.index + head[0].length;\n // Кавычки только вокруг пароля: `-u admin:\"two words\"`.\n const next = text[passwordStart];\n if (quote === '' && (next === '\"' || next === \"'\")) {\n quote = next;\n passwordStart += 1;\n }\n let passwordEnd: number;\n if (quote !== '') {\n passwordEnd = quotedValueEnd(text, passwordStart, quote);\n } else {\n passwordPattern.lastIndex = passwordStart;\n const password = passwordPattern.exec(text);\n passwordEnd = password === null ? passwordStart : passwordStart + password[0].length;\n }\n // Пустой пароль маскировать нечем; поиск продолжается сразу за головой.\n if (passwordEnd === passwordStart) continue;\n result += text.slice(copied, passwordStart) + REDACTED;\n copied = passwordEnd;\n headPattern.lastIndex = copied;\n }\n return copied === 0 ? text : result + text.slice(copied);\n}\n\nfunction maskAssignments(text: string): string {\n if (!SECRET_KEYWORD.test(text)) return text;\n const keyPattern = new RegExp(KEY_ASSIGNMENT.source, 'g');\n const valuePattern = new RegExp(ASSIGNMENT_VALUE.source, 'iy');\n const cookiePattern = new RegExp(COOKIE_VALUE.source, 'y');\n let result = '';\n let copied = 0;\n for (let match = keyPattern.exec(text); match !== null; match = keyPattern.exec(text)) {\n const key = match[1] ?? '';\n if (!isSecretKey(key)) continue;\n const quote = match[3] ?? '';\n const valueStart = match.index + match[0].length;\n let scheme = '';\n let valueEnd: number;\n if (quote !== '') {\n // Значение в кавычках целиком, с пробелами внутри; закрывающая кавычка остаётся в тексте.\n valueEnd = quotedValueEnd(text, valueStart, quote);\n if (valueEnd === valueStart) continue;\n } else {\n const isCookie = COOKIE_KEY.test(key);\n const pattern = isCookie ? cookiePattern : valuePattern;\n pattern.lastIndex = valueStart;\n const valueMatch = pattern.exec(text);\n if (valueMatch === null) continue;\n scheme = isCookie ? '' : (valueMatch[1] ?? '');\n valueEnd = valueStart + valueMatch[0].length;\n }\n result += text.slice(copied, valueStart) + scheme + REDACTED;\n copied = valueEnd;\n keyPattern.lastIndex = copied;\n }\n return copied === 0 ? text : result + text.slice(copied);\n}\n\nexport function createRedactor(options: { knownSecrets?: readonly string[] } = {}): Redactor {\n // Длинные значения первыми: короткое значение не должно оставить хвост длинного, которое его содержит.\n let known: string[] = [];\n const addKnownSecret = (value: string): void => {\n if (value.length < MIN_KNOWN_SECRET_CHARS || known.includes(value)) return;\n known = [...known, value].sort((a, b) => b.length - a.length);\n };\n for (const value of options.knownSecrets ?? []) addKnownSecret(value);\n\n const redactText = (text: string): string => {\n let out = text;\n for (const secret of known) {\n // Токен onreel и как известное значение остаётся `onr_***`: так его узнают по журналу (D-047).\n if (out.includes(secret)) out = out.split(secret).join(secret.startsWith('onr_') ? 'onr_***' : REDACTED);\n }\n out = out.replace(PEM_PRIVATE_KEY, `-----BEGIN PRIVATE KEY-----${REDACTED}`);\n out = out.replace(ONREEL_TOKEN, 'onr_***');\n out = out.replace(VENDOR_KEY, REDACTED);\n out = out.replace(JWT, REDACTED);\n out = out.replace(URL_USERINFO, `$1${REDACTED}@`);\n out = out.replace(AUTH_SCHEME, `$1 ${REDACTED}`);\n return maskAssignments(maskCliPasswords(out));\n };\n\n const MAX_VALUE_DEPTH = 32;\n const redactValue = (value: unknown, ancestors: object[] = []): unknown => {\n if (typeof value === 'string') return redactText(value);\n if (value === null || typeof value !== 'object') return value;\n if (ancestors.includes(value)) return '[Circular]';\n if (ancestors.length >= MAX_VALUE_DEPTH) return '[Object]';\n const path = [...ancestors, value];\n if (Array.isArray(value)) return value.map((item) => redactValue(item, path));\n const copy: Record<string, unknown> = {};\n for (const key of Object.keys(value)) {\n const item = (value as Record<string, unknown>)[key];\n // Сам ключ тоже текст: словарь «токен → состояние» не должен выдать токен именем ключа.\n let maskedKey = redactText(key);\n // Два разных секрета дают одно замаскированное имя: суффикс сохраняет обе записи.\n for (let n = 2; Object.hasOwn(copy, maskedKey); n += 1) maskedKey = `${redactText(key)}#${String(n)}`;\n setOwn(copy, maskedKey, isSecretKey(key) && item !== null && typeof item !== 'boolean' ? REDACTED : redactValue(item, path));\n }\n return copy;\n };\n\n return { redactText, redactValue: (value) => redactValue(value), addKnownSecret };\n}\n\n/** Присваивание без сеттера прототипа: ключ `__proto__` из данных остаётся обычным ключом. */\nfunction setOwn(target: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });\n}\n\n// ---------------------------------------------------------------------------------------------\n// Redactor уровня библиотеки: общий для redactSecretText и defaultSecretSink, как redactor\n// журнала host в onreel общий для redactSecretText и registerLogSecret.\n// ---------------------------------------------------------------------------------------------\n\nconst kernelRedactor = createRedactor();\n\n/** Свободный текст через redactor ядра: известные секреты (defaultSecretSink) и шаблоны R1–R8. */\nexport function redactSecretText(text: string): string {\n return kernelRedactor.redactText(text);\n}\n\n/**\n * Эталонный SecretSink библиотеки: регистрирует известные секреты в redactor ядра, которым\n * маскирует redactSecretText. Потребитель со своим журналом может подменить шов своей реализацией.\n */\nexport const defaultSecretSink: SecretSink = {\n register(secret: string): void {\n kernelRedactor.addKnownSecret(secret);\n },\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/kernel/redact.lib.ts"],"sourcesContent":["// Маскирование секретов в текстах библиотеки @smwb/onreel-git-mcp (заморожено в части B выделения).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d:\n// src/contract/log/log.lib.ts — движок createRedactor (правила R1–R8), дословная копия;\n// src/contract/log/log.const.ts — REDACTED, SECRET_KEY_PATTERN, дословная копия;\n// src/contract/log/log.types.ts — тип Redactor, дословная копия;\n// src/host/redact.lib.ts — redactSecretText; в onreel она работает на redactor'е журнала host\n// (hostLogger().redactor), которого в библиотеке нет, поэтому здесь — общий redactor уровня\n// модуля; регистрация известных секретов идёт через defaultSecretSink (шов SecretSink,\n// kernel/log.types.ts) на том же redactor'е — семантика onreel сохранена 1:1.\n// Побочных эффектов при импорте нет: redactor — чистый объект в памяти (R-001).\nimport type { SecretSink } from './log.types.ts';\n\nexport const REDACTED = '***';\n\n/** Имя ключа секретное, если после приведения к snake_case оно содержит одно из слов целиком. */\nexport const SECRET_KEY_PATTERN =\n /(?:^|[^a-z])(?:token|secrets?|passw(?:or)?d|pass|passphrase|pwd|api_?key|apikey|authorization|auth|cookie|set_cookie|credentials?|private_?key|access_?key|session_?id|signature)(?:$|[^a-z])/;\n\n/** Redactor журнала: маскирует свободный текст и структурные значения, принимает известные секреты. */\nexport type Redactor = {\n /** Маскирует свободный текст: известные значения и шаблоны R1–R8. */\n redactText(text: string): string;\n /** Глубокая копия: ключи по isSecretKey → '***' (любое значение, кроме null и boolean); строки и сами имена ключей → redactText. */\n redactValue(value: unknown): unknown;\n /** Регистрирует известное значение секрета; короче 8 символов — игнорируется. */\n addKnownSecret(value: string): void;\n};\n\n// ---------------------------------------------------------------------------------------------\n// Маскирование (D-194)\n// ---------------------------------------------------------------------------------------------\n\n/** Однозначные слова как окончание слитного имени: `PGPASSWORD`, `MYSQLPASSWORD`, `APITOKEN`, `dbSecret`. */\nconst SECRET_KEY_SUFFIX = /(?:token|secret|passw(?:or)?d|passphrase|apikey)$/;\n/** Голое `key` как последнее слово — секрет только у имён в стиле env (`OPENAI_KEY`): у `push_key`, `manifest_key` это идентификаторы. */\nconst ENV_KEY_SUFFIX = /(?:^|_)key$/;\n\nconst secretKeyCache = new Map<string, boolean>();\nconst SECRET_KEY_CACHE_MAX = 1_000;\n/** Длинные имена в кеш не попадают: ключ data размером с мегабайт, помноженный на 1000 записей, держал бы гигабайт. */\nconst SECRET_KEY_CACHE_KEY_MAX_CHARS = 128;\n\n/** camelCase → snake, '-' и '.' → '_', нижний регистр, затем SECRET_KEY_PATTERN и окончания слитных имён. */\nexport function isSecretKey(key: string): boolean {\n const cacheable = key.length <= SECRET_KEY_CACHE_KEY_MAX_CHARS;\n const cached = cacheable ? secretKeyCache.get(key) : undefined;\n if (cached !== undefined) return cached;\n const normalized = key\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[-.]/g, '_')\n .toLowerCase();\n const envStyle = !/[a-z]/.test(key);\n const secret = SECRET_KEY_PATTERN.test(normalized) || SECRET_KEY_SUFFIX.test(normalized) || (envStyle && ENV_KEY_SUFFIX.test(normalized));\n if (cacheable) {\n if (secretKeyCache.size >= SECRET_KEY_CACHE_MAX) secretKeyCache.clear();\n secretKeyCache.set(key, secret);\n }\n return secret;\n}\n\nconst MIN_KNOWN_SECRET_CHARS = 8;\n\n// Шаблоны R1–R7 применяются после R8. Все линейные по длине входа: у каждого начало совпадения\n// привязано к границе (\\b или lookbehind), чтобы длинный ряд символов не перебирался с каждой позиции.\nconst PEM_PRIVATE_KEY = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g;\nconst ONREEL_TOKEN = /onr_[a-z]+_[A-Za-z0-9_-]{8,}/g;\nconst VENDOR_KEY =\n /\\b(?:sk-(?:ant-|proj-)?[A-Za-z0-9_-]{20,}|gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{30,}|xox[abposr]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|glpat-[A-Za-z0-9_-]{20,})\\b/g;\nconst JWT = /(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{8,}\\.eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}/g;\n// Перед схемой допустим один маркер `+`/`-`, если он начинает токен (начало строки, несхемный символ или\n// SGR-escape): так маскируется URL в строке diff, в том числе цветной (`\\x1b[32m+https://u:p@h`).\nconst URL_USERINFO =\n // oxlint-disable-next-line no-control-regex -- ESC перед маркером: строка цветного diff.\n /(?:(?<![a-z0-9+.-])|(?<=(?:^|[^a-z0-9+.-]|\\x1b\\[[0-9;]*m)[+-]))([a-z][a-z0-9+.-]{0,31}:\\/\\/)[^\\s/@:]+:[^\\s/@]+@/gim;\nconst AUTH_SCHEME = /\\b(Bearer|Basic|Token)\\s+[A-Za-z0-9._~+/=-]{8,}/gi;\n// R3 разбит на поиск «ключ + разделитель» и ручное чтение значения: значение несекретного ключа\n// не съедается, и `url=http://h/?access_token=abc` всё равно доходит до access_token.\nconst KEY_ASSIGNMENT = /(?<![A-Za-z0-9_.-])([A-Za-z][A-Za-z0-9_.-]{0,63})([\"']?\\s*[=:]\\s*)([\"']?)/g;\nconst ASSIGNMENT_VALUE = /((?:Bearer|Basic|Token)[ \\t]+)?[^\\s\"'&,;}]+/iy;\n// Значение cookie — вся строка заголовка: `Cookie: theme=dark; sid=…`, а не до первой `;`.\nconst COOKIE_VALUE = /[^\\r\\n]+/y;\nconst COOKIE_KEY = /^(?:set[_.-]?)?cookie$/i;\n// Грубый предфильтр R3: нет ни одного слова из SECRET_KEY_PATTERN — разбирать присваивания незачем.\nconst SECRET_KEYWORD = /token|secret|pass|pwd|key|auth|cookie|credential|session[_.-]?id|signature/i;\n// Пароль в аргументах curl и подобных: `-u user:pass`, `-uuser:pass`, `--user user:pass`, `--user=user:pass`.\n// Имя пользователя остаётся. Здесь только голова до двоеточия: классы разделителя и имени не пересекаются\n// (общий `[ \\t=]+` и `[^\\s:\"']+` оба принимали `=` и давали квадратичный перебор на ` -u ====…`), а сам\n// пароль читает maskCliPasswords, чтобы пароль в кавычках с пробелами маскировался целиком.\nconst CLI_USER_HEAD = /(?<![^\\s])(?:-u[ \\t]*|--user(?:[ \\t]+|=))([\"']?)([^\\s:\"']+):/g;\nconst CLI_PASSWORD = /[^\\s\"']+/y;\n/** Потолок значения в кавычках: поиск закрывающей кавычки остаётся линейным по входу. */\nconst QUOTED_VALUE_MAX_CHARS = 4_096;\n\n/**\n * Конец значения в кавычках: позиция закрывающей кавычки того же вида, `\\`-экранирование учитывается.\n * Закрывающей нет в пределах потолка — значение режется по потолку или концу текста: лучше замаскировать\n * лишнее, чем оставить хвост секрета.\n */\nfunction quotedValueEnd(text: string, start: number, quote: string): number {\n const limit = Math.min(text.length, start + QUOTED_VALUE_MAX_CHARS);\n for (let index = start; index < limit; index += 1) {\n const char = text[index];\n if (char === '\\\\') index += 1;\n else if (char === quote) return index;\n }\n return limit;\n}\n\n/** Пароль после `-u user:` / `--user user:`: слово без кавычек либо значение в кавычках целиком, с пробелами внутри. */\nfunction maskCliPasswords(text: string): string {\n if (!text.includes('-u')) return text;\n const headPattern = new RegExp(CLI_USER_HEAD.source, 'g');\n const passwordPattern = new RegExp(CLI_PASSWORD.source, 'y');\n let result = '';\n let copied = 0;\n for (let head = headPattern.exec(text); head !== null; head = headPattern.exec(text)) {\n let quote = head[1] ?? '';\n let passwordStart = head.index + head[0].length;\n // Кавычки только вокруг пароля: `-u admin:\"two words\"`.\n const next = text[passwordStart];\n if (quote === '' && (next === '\"' || next === \"'\")) {\n quote = next;\n passwordStart += 1;\n }\n let passwordEnd: number;\n if (quote !== '') {\n passwordEnd = quotedValueEnd(text, passwordStart, quote);\n } else {\n passwordPattern.lastIndex = passwordStart;\n const password = passwordPattern.exec(text);\n passwordEnd = password === null ? passwordStart : passwordStart + password[0].length;\n }\n // Пустой пароль маскировать нечем; поиск продолжается сразу за головой.\n if (passwordEnd === passwordStart) continue;\n result += text.slice(copied, passwordStart) + REDACTED;\n copied = passwordEnd;\n headPattern.lastIndex = copied;\n }\n return copied === 0 ? text : result + text.slice(copied);\n}\n\nfunction maskAssignments(text: string): string {\n if (!SECRET_KEYWORD.test(text)) return text;\n const keyPattern = new RegExp(KEY_ASSIGNMENT.source, 'g');\n const valuePattern = new RegExp(ASSIGNMENT_VALUE.source, 'iy');\n const cookiePattern = new RegExp(COOKIE_VALUE.source, 'y');\n let result = '';\n let copied = 0;\n for (let match = keyPattern.exec(text); match !== null; match = keyPattern.exec(text)) {\n const key = match[1] ?? '';\n if (!isSecretKey(key)) continue;\n const quote = match[3] ?? '';\n const valueStart = match.index + match[0].length;\n let scheme = '';\n let valueEnd: number;\n if (quote !== '') {\n // Значение в кавычках целиком, с пробелами внутри; закрывающая кавычка остаётся в тексте.\n valueEnd = quotedValueEnd(text, valueStart, quote);\n if (valueEnd === valueStart) continue;\n } else {\n const isCookie = COOKIE_KEY.test(key);\n const pattern = isCookie ? cookiePattern : valuePattern;\n pattern.lastIndex = valueStart;\n const valueMatch = pattern.exec(text);\n if (valueMatch === null) continue;\n scheme = isCookie ? '' : (valueMatch[1] ?? '');\n valueEnd = valueStart + valueMatch[0].length;\n }\n result += text.slice(copied, valueStart) + scheme + REDACTED;\n copied = valueEnd;\n keyPattern.lastIndex = copied;\n }\n return copied === 0 ? text : result + text.slice(copied);\n}\n\nexport function createRedactor(options: { knownSecrets?: readonly string[] } = {}): Redactor {\n // Длинные значения первыми: короткое значение не должно оставить хвост длинного, которое его содержит.\n let known: string[] = [];\n const addKnownSecret = (value: string): void => {\n if (value.length < MIN_KNOWN_SECRET_CHARS || known.includes(value)) return;\n known = [...known, value].sort((a, b) => b.length - a.length);\n };\n for (const value of options.knownSecrets ?? []) addKnownSecret(value);\n\n const redactText = (text: string): string => {\n let out = text;\n for (const secret of known) {\n // Токен onreel и как известное значение остаётся `onr_***`: так его узнают по журналу (D-047).\n if (out.includes(secret)) out = out.split(secret).join(secret.startsWith('onr_') ? 'onr_***' : REDACTED);\n }\n out = out.replace(PEM_PRIVATE_KEY, `-----BEGIN PRIVATE KEY-----${REDACTED}`);\n out = out.replace(ONREEL_TOKEN, 'onr_***');\n out = out.replace(VENDOR_KEY, REDACTED);\n out = out.replace(JWT, REDACTED);\n out = out.replace(URL_USERINFO, `$1${REDACTED}@`);\n out = out.replace(AUTH_SCHEME, `$1 ${REDACTED}`);\n return maskAssignments(maskCliPasswords(out));\n };\n\n const MAX_VALUE_DEPTH = 32;\n const redactValue = (value: unknown, ancestors: object[] = []): unknown => {\n if (typeof value === 'string') return redactText(value);\n if (value === null || typeof value !== 'object') return value;\n if (ancestors.includes(value)) return '[Circular]';\n if (ancestors.length >= MAX_VALUE_DEPTH) return '[Object]';\n const path = [...ancestors, value];\n if (Array.isArray(value)) return value.map((item) => redactValue(item, path));\n const copy: Record<string, unknown> = {};\n for (const key of Object.keys(value)) {\n const item = (value as Record<string, unknown>)[key];\n // Сам ключ тоже текст: словарь «токен → состояние» не должен выдать токен именем ключа.\n let maskedKey = redactText(key);\n // Два разных секрета дают одно замаскированное имя: суффикс сохраняет обе записи.\n for (let n = 2; Object.hasOwn(copy, maskedKey); n += 1) maskedKey = `${redactText(key)}#${String(n)}`;\n setOwn(copy, maskedKey, isSecretKey(key) && item !== null && typeof item !== 'boolean' ? REDACTED : redactValue(item, path));\n }\n return copy;\n };\n\n return { redactText, redactValue: (value) => redactValue(value), addKnownSecret };\n}\n\n/** Присваивание без сеттера прототипа: ключ `__proto__` из данных остаётся обычным ключом. */\nfunction setOwn(target: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });\n}\n\n// ---------------------------------------------------------------------------------------------\n// Redactor уровня библиотеки: общий для redactSecretText и defaultSecretSink, как redactor\n// журнала host в onreel общий для redactSecretText и registerLogSecret. Приёмник потребителя на\n// весь процесс (setSecretSink, D-009) — добавление библиотеки: в onreel секрет регистрирует\n// registerLogSecret журнала host напрямую, а потребитель библиотеки иначе писал бы значение дважды.\n// ---------------------------------------------------------------------------------------------\n\nconst kernelRedactor = createRedactor();\n\n/** Свободный текст через redactor ядра: известные секреты (defaultSecretSink) и шаблоны R1–R8. */\nexport function redactSecretText(text: string): string {\n return kernelRedactor.redactText(text);\n}\n\n// Приёмник потребителя — состояние модуля, как redactor: на процесс один действующий.\nlet consumerSecretSink: SecretSink | undefined;\n// Значения, которые библиотека уже встретила: новый приёмник получает их сразу, иначе секрет,\n// встреченный до setSecretSink, в маскировке потребителя не появился бы (D-009).\nlet seenSecrets: string[] = [];\n// Защита от повторного входа: приёмник потребителя вправе делегировать значение обратно в\n// defaultSecretSink — так он зеркалит секрет в реестр библиотеки, как делал до появления\n// setSecretSink, — и без флага цепочка «библиотека → приёмник → библиотека» не завершается. Флаг\n// общий для обоих путей записи в приёмник: и defaultSecretSink.register, и перенос встреченных\n// значений в setSecretSink вызывают register потребителя.\nlet dispatchingToConsumer = false;\n\nfunction rememberSecret(secret: string): void {\n if (!seenSecrets.includes(secret)) seenSecrets = [...seenSecrets, secret];\n}\n\n/**\n * Отдаёт значение в приёмник потребителя, если он задан. Внутри `register` самого приёмника вызов\n * пропускается: пропустить нечего, вложенный вызов исходит от приёмника, значит значение у него уже\n * есть. В redactor ядра значение записано до этого вызова, поэтому маскировка текстов библиотеки не\n * зависит от того, дошёл ли обратный вызов.\n */\nfunction dispatchToConsumer(secret: string): void {\n const sink = consumerSecretSink;\n if (sink === undefined || dispatchingToConsumer) return;\n dispatchingToConsumer = true;\n try {\n sink.register(secret);\n } finally {\n // Бросивший приёмник не должен оставлять флаг: иначе следующие секреты молча перестали бы до него доходить.\n dispatchingToConsumer = false;\n }\n}\n\n/**\n * Задаёт приёмник секретов потребителя на весь процесс (D-009): известные значения библиотека пишет\n * и в свой redactor, и в `sink`, поэтому потребитель со своим журналом не регистрирует их сам. `sink`\n * сразу получает значения, которые библиотека встретила до вызова. `undefined` убирает приёмник\n * потребителя; redactor ядра эти значения уже знает, поэтому маскировка текстов библиотеки не\n * меняется. Приёмник может делегировать обратно в `defaultSecretSink`: перенос значений защищён от\n * повторного входа так же, как запись нового секрета. Вызов явный (R-001).\n */\nexport function setSecretSink(sink: SecretSink | undefined): void {\n consumerSecretSink = sink;\n if (sink === undefined) return;\n for (const secret of seenSecrets) dispatchToConsumer(secret);\n}\n\n/**\n * Приёмник секретов библиотеки: известное значение пополняет redactor ядра — им redactSecretText\n * маскирует тексты, которые библиотека отдаёт наружу сама, — и уходит в приёмник потребителя\n * (setSecretSink), если он задан. Его параметром по умолчанию берут функции хранилища токенов трекеров.\n */\nexport const defaultSecretSink: SecretSink = {\n register(secret: string): void {\n rememberSecret(secret);\n kernelRedactor.addKnownSecret(secret);\n dispatchToConsumer(secret);\n },\n};\n"],"mappings":"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","names":[]}
@@ -124,9 +124,12 @@ var trackerFeaturesSchema = z.object({
124
124
  /** Категории, в которые можно перевести задачу; пусто — статус не меняется. */
125
125
  statuses: z.array(issueStatusCategorySchema).max(4)
126
126
  });
127
+ var trackerSourceSchema = z.enum(["builtin", "user"]);
127
128
  var trackerDescriptorSchema = z.object({
128
129
  tracker: trackerKindSchema,
129
130
  title: z.string().min(1).max(100),
131
+ /** Нет поля — определение из кода (builtin). */
132
+ source: trackerSourceSchema.optional(),
130
133
  fields: z.array(z.object({ name: trackerFieldNameSchema, required: z.boolean(), secret: z.boolean() })).max(4),
131
134
  features: trackerFeaturesSchema
132
135
  });
@@ -241,6 +244,7 @@ export {
241
244
  issueCommentSchema,
242
245
  trackerFieldNameSchema,
243
246
  trackerFeaturesSchema,
247
+ trackerSourceSchema,
244
248
  trackerDescriptorSchema,
245
249
  worktreeIdSchema,
246
250
  worktreeNameSchema,
@@ -256,4 +260,4 @@ export {
256
260
  worktreeEntryKindSchema,
257
261
  worktreeEntrySchema
258
262
  };
259
- //# sourceMappingURL=chunk-4ZGRXIVA.js.map
263
+ //# sourceMappingURL=chunk-D5QUOIRC.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/contract/contract.const.ts","../src/contract/contract.schema.ts"],"sourcesContent":["// Константы среза контракта onreel для библиотеки @smwb/onreel-git-mcp (заморожены в части B\n// выделения). Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d, дословные копии:\n// src/contract/discovery/discovery.const.ts — HOST_DISCOVERY_FILE, ENV_HOST_URL, ENV_TOKEN,\n// ENV_RUN_ID, ENV_TASK_ID, ENV_DISPATCH_ID;\n// src/contract/umask.const.ts — ENV_SESSION_UMASK, DEFAULT_SESSION_UMASK;\n// src/contract/errors.const.ts — errorCatalog, ErrorCode, CONTRACT_ERROR_*;\n// src/contract/version/version.const.ts — HOST_CONTRACT_VERSION;\n// src/contract/worktree-files.const.ts — WORKTREE_FILES_MAX, WORKTREE_FILES_REASONS.\n// Имена переменных ONREEL_* заморожены как есть: их смена ломает interop onreel ↔ библиотека.\nimport type { ContractVersion } from './contract.types.ts';\n\n/** Имя файла обнаружения host в каталоге данных (onreel D-046). */\nexport const HOST_DISCOVERY_FILE = 'host.json';\n\n/** Адрес host для процесса агента: агенты не читают host.json, а получают адрес и свой токен в env. */\nexport const ENV_HOST_URL = 'ONREEL_HOST_URL';\n/** Токен агента в env его процесса. */\nexport const ENV_TOKEN = 'ONREEL_TOKEN';\n/** Идентификаторы Run, Task и попытки в env процесса агента (onreel D-226): воркер отчитывается ими в worker_done. */\nexport const ENV_RUN_ID = 'ONREEL_RUN_ID';\nexport const ENV_TASK_ID = 'ONREEL_TASK_ID';\nexport const ENV_DISPATCH_ID = 'ONREEL_DISPATCH_ID';\n\n/** Переменная окружения: восьмеричная маска пользователя («0022»), её первым записывает самый ранний процесс цепочки. */\nexport const ENV_SESSION_UMASK = 'ONREEL_SESSION_UMASK';\n\n/** Маска сессий, если переменная не задана или не разобралась: обычный umask пользователя. */\nexport const DEFAULT_SESSION_UMASK = 0o022;\n\n/**\n * Версия контракта host onreel на момент выделения (1.14). Заморожена для межпроцессной\n * совместимости MCP-сервера с host onreel; история изменений — в onreel version.const.ts.\n */\nexport const HOST_CONTRACT_VERSION: Readonly<ContractVersion> = Object.freeze({ major: 1, minor: 14 });\n\n// Ошибки контракта (onreel D-053): стабильный код, сообщение и обязательные `nextSteps` — что делать\n// дальше. Каталог задаёт известные коды и HTTP-статус. На проводе код — открытая строка: клиент\n// версии N-1 не падает на коде, появившемся позже, а показывает `message` и `nextSteps`.\n// Копия дословная: урезание каталога рассинхронизировало бы коды с onreel.\n\nexport const errorCatalog = {\n // Общие.\n invalid_request: { status: 400, summary: 'Запрос не прошёл проверку схемы контракта.' },\n unauthorized: { status: 401, summary: 'Нет токена, или токен недействителен, истёк или отозван.' },\n permission_denied: { status: 403, summary: 'Запрос шире scope токена.' },\n not_found: { status: 404, summary: 'Сущность не найдена.' },\n contract_incompatible: { status: 426, summary: 'Версии контракта сторон несовместимы (разница major больше допустимой).' },\n capability_unsupported: { status: 400, summary: 'Глагол или значение требует capability, которой нет в согласованном наборе.' },\n request_id_reused: { status: 409, summary: 'Тот же requestId уже использован для другого запроса.' },\n rate_limited: { status: 429, summary: 'Слишком много запросов.' },\n internal: { status: 500, summary: 'Внутренняя ошибка.' },\n unavailable: { status: 503, summary: 'Сервис временно недоступен: host или ptyd не отвечает.' },\n // Оркестрация: переходы, отчёты, доставка, бюджеты.\n lifecycle_conflict: { status: 409, summary: 'Переход из текущего состояния сущности не разрешён.' },\n unknown_task: { status: 404, summary: 'Task не найден.' },\n unknown_dispatch: { status: 404, summary: 'Dispatch не найден.' },\n task_dispatch_mismatch: { status: 409, summary: 'Dispatch не принадлежит этой Task.' },\n stale_dispatch: { status: 409, summary: 'Попытка уже не авторитетна: у Task другой активный Dispatch.' },\n inactive_dispatch: { status: 409, summary: 'Dispatch уже settled.' },\n consumer_fenced: { status: 409, summary: 'Потребитель mailbox заменён: consumer_generation вырос.' },\n budget_exceeded: { status: 409, summary: 'Лимит бюджета исчерпан.' },\n // ptyd.\n unknown_session: { status: 404, summary: 'Сессия ptyd не найдена.' },\n session_exited: { status: 409, summary: 'Процесс сессии уже завершился.' },\n spawn_failed: { status: 422, summary: 'Процесс не запустился.' },\n // Liveness и lease: аренда владения Task и сессией (§6.8, D-008).\n lease_conflict: { status: 409, summary: 'Lease задачи держит другой исполнитель незавершённой попытки.' },\n lease_fenced: { status: 409, summary: 'Владелец lease не доказан: fencing-токен устарел или процесс не совпал с сессией ptyd.' },\n lease_expired: { status: 409, summary: 'Lease задачи закрыт: освобождён или попытка settled.' },\n // Резерв команд агентов (D-020, D-021): коды занимают сейчас, ядро команды — позже.\n claim_conflict: { status: 409, summary: 'Задачу уже взял другой участник.' },\n ownership_conflict: { status: 409, summary: 'Зона ответственности задачи пересекается с чужим claim.' },\n host_not_live: { status: 409, summary: 'Home Dispatch участника не live.' },\n claim_limit: { status: 409, summary: 'У участника уже максимум активных claims.' },\n not_assignee: { status: 403, summary: 'Задача назначена другому участнику.' },\n messaging_denied: { status: 403, summary: 'Политика команды запрещает это сообщение.' },\n plan_required: { status: 409, summary: 'Задача требует одобренного плана.' },\n member_name_taken: { status: 409, summary: 'Имя участника в Run занято.' },\n // Worktree и git-операции (задача worktree).\n git_failed: { status: 422, summary: 'Git-команда не выполнилась: репозиторий повреждён, ref не существует или команда отклонена.' },\n // Реестр проектов (D-343): подключение, показ и снятие.\n unknown_project: { status: 404, summary: 'Проект не найден в реестре.' },\n project_exists: { status: 409, summary: 'Путь или id уже заняты другим проектом.' },\n // Трекеры задач (D-353): отказ внешнего трекера и неизвестная задача.\n tracker_failed: { status: 502, summary: 'Внешний трекер отказал или не ответил: адрес, токен или права не подходят.' },\n unknown_issue: { status: 404, summary: 'Задача не найдена в трекере проекта.' },\n // Агенты (D-091): манифесты, probe, запуск через Executor.\n agent_unavailable: { status: 422, summary: 'Агент не установлен, probe не прошёл или fallback невозможен.' },\n agent_untrusted: { status: 403, summary: 'Манифест агента из репозитория проекта не доверен.' },\n sandbox_unavailable: { status: 422, summary: 'Запрошенный уровень песочницы на этой машине недоступен: нет механизма ОС.' },\n} as const satisfies Record<string, { status: number; summary: string }>;\n\nexport type ErrorCode = keyof typeof errorCatalog;\n\nexport const CONTRACT_ERROR_MESSAGE_MAX = 4000;\nexport const CONTRACT_ERROR_NEXT_STEP_MAX = 1000;\n\n/** Максимум записей каталога в ответе worktree.files; остальное отсекается с `truncated`. */\nexport const WORKTREE_FILES_MAX = 1000;\n\n/** Причины отказов worktree.files в `data.reason` (onreel D-367). */\nexport const WORKTREE_FILES_REASONS = ['outside_worktree', 'excluded', 'not_directory', 'unreadable', 'missing_on_disk', 'unsafe_path'] as const;\n\nexport type WorktreeFilesReason = (typeof WORKTREE_FILES_REASONS)[number];\n","// Схемы zod среза контракта onreel для библиотеки @smwb/onreel-git-mcp (заморожены в части B\n// выделения). Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d, дословные копии:\n// src/contract/ids.schema.ts — примитивы идентификаторов и времени;\n// src/contract/version/version.schema.ts — contractVersionSchema;\n// src/contract/errors.schema.ts — схемы ошибки контракта;\n// src/contract/tracker.schema.ts — единая модель задач трекеров (без trackerConnectionSchema:\n// подключения хранит потребитель, модули библиотеки эту схему не используют);\n// src/contract/worktree.schema.ts — запись Worktree и строка porcelain;\n// src/contract/review.schema.ts — reviewPathSchema и модель изменений (без замечаний ревью);\n// src/contract/worktree-files.schema.ts — записи каталога worktree.\n// Выведенные типы — в contract.types.ts (правило фасетов: schema — только схемы).\nimport { z } from 'zod';\nimport { CONTRACT_ERROR_MESSAGE_MAX, CONTRACT_ERROR_NEXT_STEP_MAX, errorCatalog, type ErrorCode } from './contract.const.ts';\n\n// Схемы объектов не строгие (`z.object` отбрасывает лишние поля): старый читатель не падает\n// на optional-поле новой версии (onreel D-011).\n\n// --- Идентификаторы и общие примитивы ---\n\nconst idPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;\n\n/** Идентификатор сущности: Run, Task, Dispatch, письма, gate, сессии и т. п. */\nexport const entityIdSchema = z.string().regex(idPattern);\n\n/** Ключ идемпотентности запроса: его создаёт клиент, повтор с тем же ключом возвращает receipt. */\nexport const requestIdSchema = z.string().regex(idPattern);\n\nexport const projectIdSchema = entityIdSchema;\nexport const taskIdSchema = entityIdSchema;\n\n/** Время — миллисекунды Unix epoch, как INTEGER в хранилище. */\nexport const timestampSchema = z.number().int().nonnegative();\n\n// --- Версия контракта ---\n\nexport const contractVersionSchema = z.object({\n major: z.number().int().nonnegative().max(65535),\n minor: z.number().int().nonnegative().max(65535),\n});\n\n// --- Ошибка контракта ---\n\nexport const errorCodeSchema = z.enum(Object.keys(errorCatalog) as [ErrorCode, ...ErrorCode[]]);\n\n/** Код на проводе: известный код каталога или код более новой версии контракта. */\nexport const wireErrorCodeSchema = z.string().max(64).regex(/^[a-z][a-z0-9_]*$/);\n\nexport const contractErrorSchema = z.object({\n code: wireErrorCodeSchema,\n message: z.string().min(1).max(CONTRACT_ERROR_MESSAGE_MAX),\n /** Конкретные действия: команда CLI, глагол API или шаг для человека. Пустым не бывает. */\n nextSteps: z.array(z.string().min(1).max(CONTRACT_ERROR_NEXT_STEP_MAX)).min(1).max(20),\n data: z.record(z.string(), z.unknown()).optional(),\n});\n\n// --- Единая модель задач трекеров (onreel D-350, D-351) ---\n\n/** Вид трекера — открытый слаг: новый трекер добавляется подключением на host без правки контракта. */\nexport const trackerKindSchema = z.string().regex(/^[a-z][a-z0-9-]{0,31}$/);\n\n/** Ключ задачи в своём трекере: `76`, `PROJ-12`, `#5`; для клиента непрозрачная строка. */\nexport const issueKeySchema = z.string().min(1).max(200);\n\n/** Категория статуса единой модели; своё имя статуса трекер отдаёт отдельно, в `status.name`. */\nexport const issueStatusCategorySchema = z.enum(['open', 'in_progress', 'done', 'declined']);\n\n/** Приоритет единой модели; трекер без приоритетов не заполняет поле. */\nexport const issuePrioritySchema = z.enum(['trivial', 'minor', 'normal', 'critical', 'blocker']);\n\nexport const issueLinkSchema = z.object({\n url: z.string().min(1).max(2048),\n title: z.string().min(1).max(500).optional(),\n});\n\nexport const issueSchema = z.object({\n key: issueKeySchema,\n title: z.string().min(1).max(500),\n description: z.string().max(100_000).optional(),\n status: z.object({ category: issueStatusCategorySchema, name: z.string().min(1).max(200) }),\n priority: issuePrioritySchema.optional(),\n labels: z.array(z.string().min(1).max(200)).max(100),\n /** Исполнитель — отображаемое имя или логин трекера, не id пользователя onreel. */\n assignee: z.string().min(1).max(200).optional(),\n /** Ссылки: страница задачи в трекере (`url` первой) и связанные. */\n links: z.array(issueLinkSchema).max(100),\n createdAt: timestampSchema.optional(),\n updatedAt: timestampSchema.optional(),\n});\n\nexport const issueCommentSchema = z.object({\n id: z.string().min(1).max(200),\n author: z.string().min(1).max(200).optional(),\n body: z.string().max(100_000),\n createdAt: timestampSchema.optional(),\n});\n\n/** Поля подключения, которые descriptor может запросить у пользователя. */\nexport const trackerFieldNameSchema = z.enum(['url', 'target', 'user', 'token']);\n\n/** Что трекер умеет: клиент прячет недоступные действия, а host отказывает в них `invalid_request`. */\nexport const trackerFeaturesSchema = z.object({\n create: z.boolean(),\n update: z.boolean(),\n comment: z.boolean(),\n priority: z.boolean(),\n labels: z.boolean(),\n assignee: z.boolean(),\n /** Категории, в которые можно перевести задачу; пусто — статус не меняется. */\n statuses: z.array(issueStatusCategorySchema).max(4),\n});\n\nexport const trackerDescriptorSchema = z.object({\n tracker: trackerKindSchema,\n title: z.string().min(1).max(100),\n fields: z.array(z.object({ name: trackerFieldNameSchema, required: z.boolean(), secret: z.boolean() })).max(4),\n features: trackerFeaturesSchema,\n});\n\n// --- Worktree (onreel §6.9, D-050) ---\n\n/** Идентификатор Worktree: `wt-<uuid>`, как другие entityId контракта. */\nexport const worktreeIdSchema = entityIdSchema;\n\n/**\n * Имя worktree проекта без задачи (onreel D-345): часть каталога `<repo>/_project/<name>` и ветки\n * `onreel/_project/<name>`, поэтому только строчные латинские буквы, цифры и `-`; уникально среди\n * неочищенных worktree проекта.\n */\nexport const worktreeNameSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/);\n\n/** Коммит git: 40 hex (SHA-1) или 64 hex (SHA-256 репозитории). */\nexport const gitShaSchema = z.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/);\n\n/** Состояние Worktree (onreel D-050): preparing — receipt принят, git ещё создаёт; ready — рабочий;\n * cleaning — очистка принята, диск ещё освобождается; cleaned — очищен (терминальное);\n * failed — создание или очистка не справились, причина в error, ждёт решения (worktree.remove). */\nexport const worktreeStatusSchema = z.enum(['preparing', 'ready', 'cleaning', 'cleaned', 'failed']);\n\n/** Запись Worktree в хранилище: привязка к Task или проекту плюс git-факты (ветка, база, пути, голова). */\nexport const worktreeSchema = z.object({\n id: worktreeIdSchema,\n /** Task worktree; у worktree проекта без задачи (D-345) поля нет. */\n taskId: taskIdSchema.optional(),\n /** Проект worktree: у worktree задачи — `projectId` её Run (мягкая ссылка, D-344), у worktree проекта — проект реестра. */\n projectId: projectIdSchema.optional(),\n /** Имя worktree проекта без задачи (D-345); у worktree задачи поля нет. */\n name: worktreeNameSchema.optional(),\n /** Ветка worktree: `onreel/<run>/<task>` (§6.9), `onreel/_project/<name>` (D-345) или заданная клиентом; не меняется. */\n branch: z.string().min(1).max(200),\n /** Базовый ref rebase по умолчанию: ветка репозитория при создании, а при detached HEAD — sha. */\n baseRef: z.string().min(1).max(300),\n /** Коммит базы на момент создания: от него создана ветка. */\n baseSha: gitShaSchema,\n /** Корень базового репозитория пользователя (realpath, D-077). */\n repoPath: z.string().min(1).max(2048),\n /** Путь самого worktree: `<dataDir>/workspaces/<repo>/<run>/<task>` (D-078) или `…/<repo>/_project/<name>` (D-345). */\n path: z.string().min(1).max(2048),\n status: worktreeStatusSchema,\n /** HEAD worktree после создания, коммита или rebase; нет — пока неизвестна. */\n headSha: gitShaSchema.optional(),\n /** Причина состояния failed: вывод git или пояснение host. */\n error: z.string().max(4000).optional(),\n createdAt: timestampSchema,\n updatedAt: timestampSchema,\n /** Момент очистки; ставится на переходе cleaning → cleaned. */\n cleanedAt: timestampSchema.optional(),\n});\n\n/** Одна строка `git status --porcelain=v1`: две колонки XY и путь. */\nexport const worktreeStatusEntrySchema = z.object({\n path: z.string().min(1).max(2048),\n /** Состояние индекса (X из porcelain), пробел — чисто. */\n index: z.string().max(2),\n /** Состояние рабочего каталога (Y из porcelain), пробел — чисто. */\n worktree: z.string().max(2),\n});\n\n// --- Пути ревью и модель изменений (onreel #30) ---\n\nfunction isRelativePosixPath(path: string): boolean {\n if (path.includes('\\0') || path.includes('\\\\') || path.startsWith('/')) return false;\n return path.split('/').every((part) => part !== '' && part !== '..');\n}\n\n/** Путь внутри worktree: posix, относительный, без «..», NUL, обратных слэшей и ведущего «/». */\nexport const reviewPathSchema = z\n .string()\n .min(1)\n .max(4096)\n .refine(isRelativePosixPath, { message: 'путь должен быть относительным posix-путём без «..», пустых компонент и NUL' });\n\nexport const worktreeChangeStatusSchema = z.enum(['added', 'modified', 'deleted', 'renamed', 'copied', 'type_changed', 'conflicted']);\n\nexport const worktreeChangeSchema = z.object({\n path: reviewPathSchema,\n oldPath: reviewPathSchema.optional(),\n status: worktreeChangeStatusSchema,\n untracked: z.boolean().optional(),\n binary: z.boolean().optional(),\n additions: z.number().int().nonnegative().optional(),\n deletions: z.number().int().nonnegative().optional(),\n});\n\n/** Одна сторона файла; текста нет при `binary` или `truncated`. */\nexport const worktreeFileSideSchema = z.object({\n size: z.number().int().nonnegative(),\n binary: z.boolean(),\n truncated: z.boolean(),\n text: z.string().optional(),\n});\n\n// --- Список файлов worktree (onreel #64, D-367) ---\n\nfunction hasGitSegment(path: string): boolean {\n return path.split('/').some((part) => part.toLowerCase() === '.git');\n}\n\nfunction hasDotSegment(path: string): boolean {\n return path.split('/').includes('.');\n}\n\n/**\n * Путь worktree.files — каталога запроса и записи ответа: путь ревью (D-320) без компоненты `.git` в любом\n * регистре и без компоненты «.». У каждой записи одно написание пути: `./a` и `a` — не два файла.\n */\nexport const worktreeDirPathSchema = reviewPathSchema\n .refine((path) => !hasGitSegment(path), { message: 'служебный каталог .git не показывается' })\n .refine((path) => !hasDotSegment(path), { message: 'путь без компоненты «.»' });\n\n/** Вид записи: `dir` раскрывается, `file` и `symlink` открываются просмотром, `other` (fifo, сокет, устройство) — нет. */\nexport const worktreeEntryKindSchema = z.enum(['file', 'dir', 'symlink', 'other']);\n\nexport const worktreeEntrySchema = z.object({\n /** Имя записи в каталоге. */\n name: z\n .string()\n .min(1)\n .max(255)\n .refine((name) => !name.includes('/') && !name.includes('\\0'), { message: 'имя записи без «/» и NUL' }),\n /** Путь от корня worktree: клиент передаёт его обратно в worktree.files и worktree.file как есть. */\n path: worktreeDirPathSchema,\n kind: worktreeEntryKindSchema,\n /** Размер в байтах; только у `file`. */\n size: z.number().int().nonnegative().optional(),\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/contract/contract.const.ts","../src/contract/contract.schema.ts"],"sourcesContent":["// Константы среза контракта onreel для библиотеки @smwb/onreel-git-mcp (заморожены в части B\n// выделения). Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d, дословные копии:\n// src/contract/discovery/discovery.const.ts — HOST_DISCOVERY_FILE, ENV_HOST_URL, ENV_TOKEN,\n// ENV_RUN_ID, ENV_TASK_ID, ENV_DISPATCH_ID;\n// src/contract/umask.const.ts — ENV_SESSION_UMASK, DEFAULT_SESSION_UMASK;\n// src/contract/errors.const.ts — errorCatalog, ErrorCode, CONTRACT_ERROR_*;\n// src/contract/version/version.const.ts — HOST_CONTRACT_VERSION;\n// src/contract/worktree-files.const.ts — WORKTREE_FILES_MAX, WORKTREE_FILES_REASONS.\n// Имена переменных ONREEL_* заморожены как есть: их смена ломает interop onreel ↔ библиотека.\nimport type { ContractVersion } from './contract.types.ts';\n\n/** Имя файла обнаружения host в каталоге данных (onreel D-046). */\nexport const HOST_DISCOVERY_FILE = 'host.json';\n\n/** Адрес host для процесса агента: агенты не читают host.json, а получают адрес и свой токен в env. */\nexport const ENV_HOST_URL = 'ONREEL_HOST_URL';\n/** Токен агента в env его процесса. */\nexport const ENV_TOKEN = 'ONREEL_TOKEN';\n/** Идентификаторы Run, Task и попытки в env процесса агента (onreel D-226): воркер отчитывается ими в worker_done. */\nexport const ENV_RUN_ID = 'ONREEL_RUN_ID';\nexport const ENV_TASK_ID = 'ONREEL_TASK_ID';\nexport const ENV_DISPATCH_ID = 'ONREEL_DISPATCH_ID';\n\n/** Переменная окружения: восьмеричная маска пользователя («0022»), её первым записывает самый ранний процесс цепочки. */\nexport const ENV_SESSION_UMASK = 'ONREEL_SESSION_UMASK';\n\n/** Маска сессий, если переменная не задана или не разобралась: обычный umask пользователя. */\nexport const DEFAULT_SESSION_UMASK = 0o022;\n\n/**\n * Версия контракта host onreel на момент выделения (1.14). Заморожена для межпроцессной\n * совместимости MCP-сервера с host onreel; история изменений — в onreel version.const.ts.\n */\nexport const HOST_CONTRACT_VERSION: Readonly<ContractVersion> = Object.freeze({ major: 1, minor: 14 });\n\n// Ошибки контракта (onreel D-053): стабильный код, сообщение и обязательные `nextSteps` — что делать\n// дальше. Каталог задаёт известные коды и HTTP-статус. На проводе код — открытая строка: клиент\n// версии N-1 не падает на коде, появившемся позже, а показывает `message` и `nextSteps`.\n// Копия дословная: урезание каталога рассинхронизировало бы коды с onreel.\n\nexport const errorCatalog = {\n // Общие.\n invalid_request: { status: 400, summary: 'Запрос не прошёл проверку схемы контракта.' },\n unauthorized: { status: 401, summary: 'Нет токена, или токен недействителен, истёк или отозван.' },\n permission_denied: { status: 403, summary: 'Запрос шире scope токена.' },\n not_found: { status: 404, summary: 'Сущность не найдена.' },\n contract_incompatible: { status: 426, summary: 'Версии контракта сторон несовместимы (разница major больше допустимой).' },\n capability_unsupported: { status: 400, summary: 'Глагол или значение требует capability, которой нет в согласованном наборе.' },\n request_id_reused: { status: 409, summary: 'Тот же requestId уже использован для другого запроса.' },\n rate_limited: { status: 429, summary: 'Слишком много запросов.' },\n internal: { status: 500, summary: 'Внутренняя ошибка.' },\n unavailable: { status: 503, summary: 'Сервис временно недоступен: host или ptyd не отвечает.' },\n // Оркестрация: переходы, отчёты, доставка, бюджеты.\n lifecycle_conflict: { status: 409, summary: 'Переход из текущего состояния сущности не разрешён.' },\n unknown_task: { status: 404, summary: 'Task не найден.' },\n unknown_dispatch: { status: 404, summary: 'Dispatch не найден.' },\n task_dispatch_mismatch: { status: 409, summary: 'Dispatch не принадлежит этой Task.' },\n stale_dispatch: { status: 409, summary: 'Попытка уже не авторитетна: у Task другой активный Dispatch.' },\n inactive_dispatch: { status: 409, summary: 'Dispatch уже settled.' },\n consumer_fenced: { status: 409, summary: 'Потребитель mailbox заменён: consumer_generation вырос.' },\n budget_exceeded: { status: 409, summary: 'Лимит бюджета исчерпан.' },\n // ptyd.\n unknown_session: { status: 404, summary: 'Сессия ptyd не найдена.' },\n session_exited: { status: 409, summary: 'Процесс сессии уже завершился.' },\n spawn_failed: { status: 422, summary: 'Процесс не запустился.' },\n // Liveness и lease: аренда владения Task и сессией (§6.8, D-008).\n lease_conflict: { status: 409, summary: 'Lease задачи держит другой исполнитель незавершённой попытки.' },\n lease_fenced: { status: 409, summary: 'Владелец lease не доказан: fencing-токен устарел или процесс не совпал с сессией ptyd.' },\n lease_expired: { status: 409, summary: 'Lease задачи закрыт: освобождён или попытка settled.' },\n // Резерв команд агентов (D-020, D-021): коды занимают сейчас, ядро команды — позже.\n claim_conflict: { status: 409, summary: 'Задачу уже взял другой участник.' },\n ownership_conflict: { status: 409, summary: 'Зона ответственности задачи пересекается с чужим claim.' },\n host_not_live: { status: 409, summary: 'Home Dispatch участника не live.' },\n claim_limit: { status: 409, summary: 'У участника уже максимум активных claims.' },\n not_assignee: { status: 403, summary: 'Задача назначена другому участнику.' },\n messaging_denied: { status: 403, summary: 'Политика команды запрещает это сообщение.' },\n plan_required: { status: 409, summary: 'Задача требует одобренного плана.' },\n member_name_taken: { status: 409, summary: 'Имя участника в Run занято.' },\n // Worktree и git-операции (задача worktree).\n git_failed: { status: 422, summary: 'Git-команда не выполнилась: репозиторий повреждён, ref не существует или команда отклонена.' },\n // Реестр проектов (D-343): подключение, показ и снятие.\n unknown_project: { status: 404, summary: 'Проект не найден в реестре.' },\n project_exists: { status: 409, summary: 'Путь или id уже заняты другим проектом.' },\n // Трекеры задач (D-353): отказ внешнего трекера и неизвестная задача.\n tracker_failed: { status: 502, summary: 'Внешний трекер отказал или не ответил: адрес, токен или права не подходят.' },\n unknown_issue: { status: 404, summary: 'Задача не найдена в трекере проекта.' },\n // Агенты (D-091): манифесты, probe, запуск через Executor.\n agent_unavailable: { status: 422, summary: 'Агент не установлен, probe не прошёл или fallback невозможен.' },\n agent_untrusted: { status: 403, summary: 'Манифест агента из репозитория проекта не доверен.' },\n sandbox_unavailable: { status: 422, summary: 'Запрошенный уровень песочницы на этой машине недоступен: нет механизма ОС.' },\n} as const satisfies Record<string, { status: number; summary: string }>;\n\nexport type ErrorCode = keyof typeof errorCatalog;\n\nexport const CONTRACT_ERROR_MESSAGE_MAX = 4000;\nexport const CONTRACT_ERROR_NEXT_STEP_MAX = 1000;\n\n/** Максимум записей каталога в ответе worktree.files; остальное отсекается с `truncated`. */\nexport const WORKTREE_FILES_MAX = 1000;\n\n/** Причины отказов worktree.files в `data.reason` (onreel D-367). */\nexport const WORKTREE_FILES_REASONS = ['outside_worktree', 'excluded', 'not_directory', 'unreadable', 'missing_on_disk', 'unsafe_path'] as const;\n\nexport type WorktreeFilesReason = (typeof WORKTREE_FILES_REASONS)[number];\n","// Схемы zod среза контракта onreel для библиотеки @smwb/onreel-git-mcp (заморожены в части B\n// выделения). Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d, дословные копии:\n// src/contract/ids.schema.ts — примитивы идентификаторов и времени;\n// src/contract/version/version.schema.ts — contractVersionSchema;\n// src/contract/errors.schema.ts — схемы ошибки контракта;\n// src/contract/tracker.schema.ts — единая модель задач трекеров (без trackerConnectionSchema:\n// подключения хранит потребитель, модули библиотеки эту схему не используют);\n// src/contract/worktree.schema.ts — запись Worktree и строка porcelain;\n// src/contract/review.schema.ts — reviewPathSchema и модель изменений (без замечаний ревью);\n// src/contract/worktree-files.schema.ts — записи каталога worktree.\n// Выведенные типы — в contract.types.ts (правило фасетов: schema — только схемы).\nimport { z } from 'zod';\nimport { CONTRACT_ERROR_MESSAGE_MAX, CONTRACT_ERROR_NEXT_STEP_MAX, errorCatalog, type ErrorCode } from './contract.const.ts';\n\n// Схемы объектов не строгие (`z.object` отбрасывает лишние поля): старый читатель не падает\n// на optional-поле новой версии (onreel D-011).\n\n// --- Идентификаторы и общие примитивы ---\n\nconst idPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;\n\n/** Идентификатор сущности: Run, Task, Dispatch, письма, gate, сессии и т. п. */\nexport const entityIdSchema = z.string().regex(idPattern);\n\n/** Ключ идемпотентности запроса: его создаёт клиент, повтор с тем же ключом возвращает receipt. */\nexport const requestIdSchema = z.string().regex(idPattern);\n\nexport const projectIdSchema = entityIdSchema;\nexport const taskIdSchema = entityIdSchema;\n\n/** Время — миллисекунды Unix epoch, как INTEGER в хранилище. */\nexport const timestampSchema = z.number().int().nonnegative();\n\n// --- Версия контракта ---\n\nexport const contractVersionSchema = z.object({\n major: z.number().int().nonnegative().max(65535),\n minor: z.number().int().nonnegative().max(65535),\n});\n\n// --- Ошибка контракта ---\n\nexport const errorCodeSchema = z.enum(Object.keys(errorCatalog) as [ErrorCode, ...ErrorCode[]]);\n\n/** Код на проводе: известный код каталога или код более новой версии контракта. */\nexport const wireErrorCodeSchema = z.string().max(64).regex(/^[a-z][a-z0-9_]*$/);\n\nexport const contractErrorSchema = z.object({\n code: wireErrorCodeSchema,\n message: z.string().min(1).max(CONTRACT_ERROR_MESSAGE_MAX),\n /** Конкретные действия: команда CLI, глагол API или шаг для человека. Пустым не бывает. */\n nextSteps: z.array(z.string().min(1).max(CONTRACT_ERROR_NEXT_STEP_MAX)).min(1).max(20),\n data: z.record(z.string(), z.unknown()).optional(),\n});\n\n// --- Единая модель задач трекеров (onreel D-350, D-351) ---\n\n/** Вид трекера — открытый слаг: новый трекер добавляется подключением на host без правки контракта. */\nexport const trackerKindSchema = z.string().regex(/^[a-z][a-z0-9-]{0,31}$/);\n\n/** Ключ задачи в своём трекере: `76`, `PROJ-12`, `#5`; для клиента непрозрачная строка. */\nexport const issueKeySchema = z.string().min(1).max(200);\n\n/** Категория статуса единой модели; своё имя статуса трекер отдаёт отдельно, в `status.name`. */\nexport const issueStatusCategorySchema = z.enum(['open', 'in_progress', 'done', 'declined']);\n\n/** Приоритет единой модели; трекер без приоритетов не заполняет поле. */\nexport const issuePrioritySchema = z.enum(['trivial', 'minor', 'normal', 'critical', 'blocker']);\n\nexport const issueLinkSchema = z.object({\n url: z.string().min(1).max(2048),\n title: z.string().min(1).max(500).optional(),\n});\n\nexport const issueSchema = z.object({\n key: issueKeySchema,\n title: z.string().min(1).max(500),\n description: z.string().max(100_000).optional(),\n status: z.object({ category: issueStatusCategorySchema, name: z.string().min(1).max(200) }),\n priority: issuePrioritySchema.optional(),\n labels: z.array(z.string().min(1).max(200)).max(100),\n /** Исполнитель — отображаемое имя или логин трекера, не id пользователя onreel. */\n assignee: z.string().min(1).max(200).optional(),\n /** Ссылки: страница задачи в трекере (`url` первой) и связанные. */\n links: z.array(issueLinkSchema).max(100),\n createdAt: timestampSchema.optional(),\n updatedAt: timestampSchema.optional(),\n});\n\nexport const issueCommentSchema = z.object({\n id: z.string().min(1).max(200),\n author: z.string().min(1).max(200).optional(),\n body: z.string().max(100_000),\n createdAt: timestampSchema.optional(),\n});\n\n/** Поля подключения, которые descriptor может запросить у пользователя. */\nexport const trackerFieldNameSchema = z.enum(['url', 'target', 'user', 'token']);\n\n/** Что трекер умеет: клиент прячет недоступные действия, а host отказывает в них `invalid_request`. */\nexport const trackerFeaturesSchema = z.object({\n create: z.boolean(),\n update: z.boolean(),\n comment: z.boolean(),\n priority: z.boolean(),\n labels: z.boolean(),\n assignee: z.boolean(),\n /** Категории, в которые можно перевести задачу; пусто — статус не меняется. */\n statuses: z.array(issueStatusCategorySchema).max(4),\n});\n\n/**\n * Откуда определение трекера: builtin — код библиотеки или потребителя, user — декларативный конфиг пользователя\n * (definitionFromConfig). Своё (#25): в срезе onreel 1.14 поля нет.\n */\nexport const trackerSourceSchema = z.enum(['builtin', 'user']);\n\nexport const trackerDescriptorSchema = z.object({\n tracker: trackerKindSchema,\n title: z.string().min(1).max(100),\n /** Нет поля — определение из кода (builtin). */\n source: trackerSourceSchema.optional(),\n fields: z.array(z.object({ name: trackerFieldNameSchema, required: z.boolean(), secret: z.boolean() })).max(4),\n features: trackerFeaturesSchema,\n});\n\n// --- Worktree (onreel §6.9, D-050) ---\n\n/** Идентификатор Worktree: `wt-<uuid>`, как другие entityId контракта. */\nexport const worktreeIdSchema = entityIdSchema;\n\n/**\n * Имя worktree проекта без задачи (onreel D-345): часть каталога `<repo>/_project/<name>` и ветки\n * `onreel/_project/<name>`, поэтому только строчные латинские буквы, цифры и `-`; уникально среди\n * неочищенных worktree проекта.\n */\nexport const worktreeNameSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/);\n\n/** Коммит git: 40 hex (SHA-1) или 64 hex (SHA-256 репозитории). */\nexport const gitShaSchema = z.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/);\n\n/** Состояние Worktree (onreel D-050): preparing — receipt принят, git ещё создаёт; ready — рабочий;\n * cleaning — очистка принята, диск ещё освобождается; cleaned — очищен (терминальное);\n * failed — создание или очистка не справились, причина в error, ждёт решения (worktree.remove). */\nexport const worktreeStatusSchema = z.enum(['preparing', 'ready', 'cleaning', 'cleaned', 'failed']);\n\n/** Запись Worktree в хранилище: привязка к Task или проекту плюс git-факты (ветка, база, пути, голова). */\nexport const worktreeSchema = z.object({\n id: worktreeIdSchema,\n /** Task worktree; у worktree проекта без задачи (D-345) поля нет. */\n taskId: taskIdSchema.optional(),\n /** Проект worktree: у worktree задачи — `projectId` её Run (мягкая ссылка, D-344), у worktree проекта — проект реестра. */\n projectId: projectIdSchema.optional(),\n /** Имя worktree проекта без задачи (D-345); у worktree задачи поля нет. */\n name: worktreeNameSchema.optional(),\n /** Ветка worktree: `onreel/<run>/<task>` (§6.9), `onreel/_project/<name>` (D-345) или заданная клиентом; не меняется. */\n branch: z.string().min(1).max(200),\n /** Базовый ref rebase по умолчанию: ветка репозитория при создании, а при detached HEAD — sha. */\n baseRef: z.string().min(1).max(300),\n /** Коммит базы на момент создания: от него создана ветка. */\n baseSha: gitShaSchema,\n /** Корень базового репозитория пользователя (realpath, D-077). */\n repoPath: z.string().min(1).max(2048),\n /** Путь самого worktree: `<dataDir>/workspaces/<repo>/<run>/<task>` (D-078) или `…/<repo>/_project/<name>` (D-345). */\n path: z.string().min(1).max(2048),\n status: worktreeStatusSchema,\n /** HEAD worktree после создания, коммита или rebase; нет — пока неизвестна. */\n headSha: gitShaSchema.optional(),\n /** Причина состояния failed: вывод git или пояснение host. */\n error: z.string().max(4000).optional(),\n createdAt: timestampSchema,\n updatedAt: timestampSchema,\n /** Момент очистки; ставится на переходе cleaning → cleaned. */\n cleanedAt: timestampSchema.optional(),\n});\n\n/** Одна строка `git status --porcelain=v1`: две колонки XY и путь. */\nexport const worktreeStatusEntrySchema = z.object({\n path: z.string().min(1).max(2048),\n /** Состояние индекса (X из porcelain), пробел — чисто. */\n index: z.string().max(2),\n /** Состояние рабочего каталога (Y из porcelain), пробел — чисто. */\n worktree: z.string().max(2),\n});\n\n// --- Пути ревью и модель изменений (onreel #30) ---\n\nfunction isRelativePosixPath(path: string): boolean {\n if (path.includes('\\0') || path.includes('\\\\') || path.startsWith('/')) return false;\n return path.split('/').every((part) => part !== '' && part !== '..');\n}\n\n/** Путь внутри worktree: posix, относительный, без «..», NUL, обратных слэшей и ведущего «/». */\nexport const reviewPathSchema = z\n .string()\n .min(1)\n .max(4096)\n .refine(isRelativePosixPath, { message: 'путь должен быть относительным posix-путём без «..», пустых компонент и NUL' });\n\nexport const worktreeChangeStatusSchema = z.enum(['added', 'modified', 'deleted', 'renamed', 'copied', 'type_changed', 'conflicted']);\n\nexport const worktreeChangeSchema = z.object({\n path: reviewPathSchema,\n oldPath: reviewPathSchema.optional(),\n status: worktreeChangeStatusSchema,\n untracked: z.boolean().optional(),\n binary: z.boolean().optional(),\n additions: z.number().int().nonnegative().optional(),\n deletions: z.number().int().nonnegative().optional(),\n});\n\n/** Одна сторона файла; текста нет при `binary` или `truncated`. */\nexport const worktreeFileSideSchema = z.object({\n size: z.number().int().nonnegative(),\n binary: z.boolean(),\n truncated: z.boolean(),\n text: z.string().optional(),\n});\n\n// --- Список файлов worktree (onreel #64, D-367) ---\n\nfunction hasGitSegment(path: string): boolean {\n return path.split('/').some((part) => part.toLowerCase() === '.git');\n}\n\nfunction hasDotSegment(path: string): boolean {\n return path.split('/').includes('.');\n}\n\n/**\n * Путь worktree.files — каталога запроса и записи ответа: путь ревью (D-320) без компоненты `.git` в любом\n * регистре и без компоненты «.». У каждой записи одно написание пути: `./a` и `a` — не два файла.\n */\nexport const worktreeDirPathSchema = reviewPathSchema\n .refine((path) => !hasGitSegment(path), { message: 'служебный каталог .git не показывается' })\n .refine((path) => !hasDotSegment(path), { message: 'путь без компоненты «.»' });\n\n/** Вид записи: `dir` раскрывается, `file` и `symlink` открываются просмотром, `other` (fifo, сокет, устройство) — нет. */\nexport const worktreeEntryKindSchema = z.enum(['file', 'dir', 'symlink', 'other']);\n\nexport const worktreeEntrySchema = z.object({\n /** Имя записи в каталоге. */\n name: z\n .string()\n .min(1)\n .max(255)\n .refine((name) => !name.includes('/') && !name.includes('\\0'), { message: 'имя записи без «/» и NUL' }),\n /** Путь от корня worktree: клиент передаёт его обратно в worktree.files и worktree.file как есть. */\n path: worktreeDirPathSchema,\n kind: worktreeEntryKindSchema,\n /** Размер в байтах; только у `file`. */\n size: z.number().int().nonnegative().optional(),\n});\n"],"mappings":"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","names":[]}