@smwb/onreel-git-mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/chunk-3YP22YLA.js +50 -0
  2. package/dist/chunk-3YP22YLA.js.map +1 -0
  3. package/dist/chunk-4ZGRXIVA.js +259 -0
  4. package/dist/chunk-4ZGRXIVA.js.map +1 -0
  5. package/dist/chunk-C6PHNSDC.js +112 -0
  6. package/dist/chunk-C6PHNSDC.js.map +1 -0
  7. package/dist/chunk-GPRMI4PG.js +421 -0
  8. package/dist/chunk-GPRMI4PG.js.map +1 -0
  9. package/dist/chunk-L7CPE2SO.js +166 -0
  10. package/dist/chunk-L7CPE2SO.js.map +1 -0
  11. package/dist/contract/contract.const.d.ts +190 -0
  12. package/dist/contract/contract.lib.d.ts +22 -0
  13. package/dist/contract/contract.schema.d.ts +279 -0
  14. package/dist/contract/contract.types.d.ts +23 -0
  15. package/dist/files/files.const.d.ts +6 -0
  16. package/dist/files/files.lib.d.ts +14 -0
  17. package/dist/files/files.types.d.ts +13 -0
  18. package/dist/files/index.d.ts +3 -0
  19. package/dist/files/index.js +163 -0
  20. package/dist/files/index.js.map +1 -0
  21. package/dist/git/git.const.d.ts +4 -0
  22. package/dist/git/git.lib.d.ts +90 -0
  23. package/dist/git/git.types.d.ts +33 -0
  24. package/dist/git/index.d.ts +7 -0
  25. package/dist/git/index.js +97 -0
  26. package/dist/git/index.js.map +1 -0
  27. package/dist/git/paths.const.d.ts +5 -0
  28. package/dist/git/paths.lib.d.ts +39 -0
  29. package/dist/index.d.ts +6 -1
  30. package/dist/index.js +113 -2
  31. package/dist/index.js.map +1 -1
  32. package/dist/kernel/log.types.d.ts +5 -0
  33. package/dist/kernel/redact.lib.d.ts +25 -0
  34. package/dist/kernel/store.types.d.ts +109 -0
  35. package/dist/kernel/umask.lib.d.ts +7 -0
  36. package/dist/mcp/args.lib.d.ts +17 -0
  37. package/dist/mcp/envelope.lib.d.ts +20 -0
  38. package/dist/mcp/index.d.ts +8 -0
  39. package/dist/mcp/index.js +744 -0
  40. package/dist/mcp/index.js.map +1 -0
  41. package/dist/mcp/mcp.const.d.ts +23 -0
  42. package/dist/mcp/mcp.schema.d.ts +64 -0
  43. package/dist/mcp/mcp.types.d.ts +125 -0
  44. package/dist/mcp/principal.lib.d.ts +22 -0
  45. package/dist/mcp/server.lib.d.ts +3 -0
  46. package/dist/mcp/tools.lib.d.ts +16 -0
  47. package/dist/tracker/builtin.lib.d.ts +1 -0
  48. package/dist/tracker/github.lib.d.ts +2 -0
  49. package/dist/tracker/gitlab.lib.d.ts +2 -0
  50. package/dist/tracker/http.lib.d.ts +4 -0
  51. package/dist/tracker/index.d.ts +15 -0
  52. package/dist/tracker/index.js +840 -0
  53. package/dist/tracker/index.js.map +1 -0
  54. package/dist/tracker/jira.lib.d.ts +2 -0
  55. package/dist/tracker/local.lib.d.ts +2 -0
  56. package/dist/tracker/mapping.const.d.ts +26 -0
  57. package/dist/tracker/registry.lib.d.ts +5 -0
  58. package/dist/tracker/secrets.lib.d.ts +7 -0
  59. package/dist/tracker/sourcecraft.lib.d.ts +2 -0
  60. package/dist/tracker/tracker.const.d.ts +10 -0
  61. package/dist/tracker/tracker.lib.d.ts +21 -0
  62. package/dist/tracker/tracker.types.d.ts +77 -0
  63. package/dist/worktree/index.d.ts +4 -0
  64. package/dist/worktree/index.js +203 -0
  65. package/dist/worktree/index.js.map +1 -0
  66. package/dist/worktree/lifecycle.const.d.ts +2 -0
  67. package/dist/worktree/lifecycle.lib.d.ts +30 -0
  68. package/dist/worktree/lifecycle.types.d.ts +14 -0
  69. package/package.json +1 -1
@@ -0,0 +1,166 @@
1
+ // src/kernel/redact.lib.ts
2
+ var REDACTED = "***";
3
+ var SECRET_KEY_PATTERN = /(?:^|[^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])/;
4
+ var SECRET_KEY_SUFFIX = /(?:token|secret|passw(?:or)?d|passphrase|apikey)$/;
5
+ var ENV_KEY_SUFFIX = /(?:^|_)key$/;
6
+ var secretKeyCache = /* @__PURE__ */ new Map();
7
+ var SECRET_KEY_CACHE_MAX = 1e3;
8
+ var SECRET_KEY_CACHE_KEY_MAX_CHARS = 128;
9
+ function isSecretKey(key) {
10
+ const cacheable = key.length <= SECRET_KEY_CACHE_KEY_MAX_CHARS;
11
+ const cached = cacheable ? secretKeyCache.get(key) : void 0;
12
+ if (cached !== void 0) return cached;
13
+ const normalized = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-.]/g, "_").toLowerCase();
14
+ const envStyle = !/[a-z]/.test(key);
15
+ const secret = SECRET_KEY_PATTERN.test(normalized) || SECRET_KEY_SUFFIX.test(normalized) || envStyle && ENV_KEY_SUFFIX.test(normalized);
16
+ if (cacheable) {
17
+ if (secretKeyCache.size >= SECRET_KEY_CACHE_MAX) secretKeyCache.clear();
18
+ secretKeyCache.set(key, secret);
19
+ }
20
+ return secret;
21
+ }
22
+ var MIN_KNOWN_SECRET_CHARS = 8;
23
+ var PEM_PRIVATE_KEY = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g;
24
+ var ONREEL_TOKEN = /onr_[a-z]+_[A-Za-z0-9_-]{8,}/g;
25
+ var VENDOR_KEY = /\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;
26
+ var JWT = /(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g;
27
+ var URL_USERINFO = (
28
+ // oxlint-disable-next-line no-control-regex -- ESC перед маркером: строка цветного diff.
29
+ /(?:(?<![a-z0-9+.-])|(?<=(?:^|[^a-z0-9+.-]|\x1b\[[0-9;]*m)[+-]))([a-z][a-z0-9+.-]{0,31}:\/\/)[^\s/@:]+:[^\s/@]+@/gim
30
+ );
31
+ var AUTH_SCHEME = /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{8,}/gi;
32
+ var KEY_ASSIGNMENT = /(?<![A-Za-z0-9_.-])([A-Za-z][A-Za-z0-9_.-]{0,63})(["']?\s*[=:]\s*)(["']?)/g;
33
+ var ASSIGNMENT_VALUE = /((?:Bearer|Basic|Token)[ \t]+)?[^\s"'&,;}]+/iy;
34
+ var COOKIE_VALUE = /[^\r\n]+/y;
35
+ var COOKIE_KEY = /^(?:set[_.-]?)?cookie$/i;
36
+ var SECRET_KEYWORD = /token|secret|pass|pwd|key|auth|cookie|credential|session[_.-]?id|signature/i;
37
+ var CLI_USER_HEAD = /(?<![^\s])(?:-u[ \t]*|--user(?:[ \t]+|=))(["']?)([^\s:"']+):/g;
38
+ var CLI_PASSWORD = /[^\s"']+/y;
39
+ var QUOTED_VALUE_MAX_CHARS = 4096;
40
+ function quotedValueEnd(text, start, quote) {
41
+ const limit = Math.min(text.length, start + QUOTED_VALUE_MAX_CHARS);
42
+ for (let index = start; index < limit; index += 1) {
43
+ const char = text[index];
44
+ if (char === "\\") index += 1;
45
+ else if (char === quote) return index;
46
+ }
47
+ return limit;
48
+ }
49
+ function maskCliPasswords(text) {
50
+ if (!text.includes("-u")) return text;
51
+ const headPattern = new RegExp(CLI_USER_HEAD.source, "g");
52
+ const passwordPattern = new RegExp(CLI_PASSWORD.source, "y");
53
+ let result = "";
54
+ let copied = 0;
55
+ for (let head = headPattern.exec(text); head !== null; head = headPattern.exec(text)) {
56
+ let quote = head[1] ?? "";
57
+ let passwordStart = head.index + head[0].length;
58
+ const next = text[passwordStart];
59
+ if (quote === "" && (next === '"' || next === "'")) {
60
+ quote = next;
61
+ passwordStart += 1;
62
+ }
63
+ let passwordEnd;
64
+ if (quote !== "") {
65
+ passwordEnd = quotedValueEnd(text, passwordStart, quote);
66
+ } else {
67
+ passwordPattern.lastIndex = passwordStart;
68
+ const password = passwordPattern.exec(text);
69
+ passwordEnd = password === null ? passwordStart : passwordStart + password[0].length;
70
+ }
71
+ if (passwordEnd === passwordStart) continue;
72
+ result += text.slice(copied, passwordStart) + REDACTED;
73
+ copied = passwordEnd;
74
+ headPattern.lastIndex = copied;
75
+ }
76
+ return copied === 0 ? text : result + text.slice(copied);
77
+ }
78
+ function maskAssignments(text) {
79
+ if (!SECRET_KEYWORD.test(text)) return text;
80
+ const keyPattern = new RegExp(KEY_ASSIGNMENT.source, "g");
81
+ const valuePattern = new RegExp(ASSIGNMENT_VALUE.source, "iy");
82
+ const cookiePattern = new RegExp(COOKIE_VALUE.source, "y");
83
+ let result = "";
84
+ let copied = 0;
85
+ for (let match = keyPattern.exec(text); match !== null; match = keyPattern.exec(text)) {
86
+ const key = match[1] ?? "";
87
+ if (!isSecretKey(key)) continue;
88
+ const quote = match[3] ?? "";
89
+ const valueStart = match.index + match[0].length;
90
+ let scheme = "";
91
+ let valueEnd;
92
+ if (quote !== "") {
93
+ valueEnd = quotedValueEnd(text, valueStart, quote);
94
+ if (valueEnd === valueStart) continue;
95
+ } else {
96
+ const isCookie = COOKIE_KEY.test(key);
97
+ const pattern = isCookie ? cookiePattern : valuePattern;
98
+ pattern.lastIndex = valueStart;
99
+ const valueMatch = pattern.exec(text);
100
+ if (valueMatch === null) continue;
101
+ scheme = isCookie ? "" : valueMatch[1] ?? "";
102
+ valueEnd = valueStart + valueMatch[0].length;
103
+ }
104
+ result += text.slice(copied, valueStart) + scheme + REDACTED;
105
+ copied = valueEnd;
106
+ keyPattern.lastIndex = copied;
107
+ }
108
+ return copied === 0 ? text : result + text.slice(copied);
109
+ }
110
+ function createRedactor(options = {}) {
111
+ let known = [];
112
+ const addKnownSecret = (value) => {
113
+ if (value.length < MIN_KNOWN_SECRET_CHARS || known.includes(value)) return;
114
+ known = [...known, value].sort((a, b) => b.length - a.length);
115
+ };
116
+ for (const value of options.knownSecrets ?? []) addKnownSecret(value);
117
+ const redactText = (text) => {
118
+ let out = text;
119
+ for (const secret of known) {
120
+ if (out.includes(secret)) out = out.split(secret).join(secret.startsWith("onr_") ? "onr_***" : REDACTED);
121
+ }
122
+ out = out.replace(PEM_PRIVATE_KEY, `-----BEGIN PRIVATE KEY-----${REDACTED}`);
123
+ out = out.replace(ONREEL_TOKEN, "onr_***");
124
+ out = out.replace(VENDOR_KEY, REDACTED);
125
+ out = out.replace(JWT, REDACTED);
126
+ out = out.replace(URL_USERINFO, `$1${REDACTED}@`);
127
+ out = out.replace(AUTH_SCHEME, `$1 ${REDACTED}`);
128
+ return maskAssignments(maskCliPasswords(out));
129
+ };
130
+ const MAX_VALUE_DEPTH = 32;
131
+ const redactValue = (value, ancestors = []) => {
132
+ if (typeof value === "string") return redactText(value);
133
+ if (value === null || typeof value !== "object") return value;
134
+ if (ancestors.includes(value)) return "[Circular]";
135
+ if (ancestors.length >= MAX_VALUE_DEPTH) return "[Object]";
136
+ const path = [...ancestors, value];
137
+ if (Array.isArray(value)) return value.map((item) => redactValue(item, path));
138
+ const copy = {};
139
+ for (const key of Object.keys(value)) {
140
+ const item = value[key];
141
+ let maskedKey = redactText(key);
142
+ for (let n = 2; Object.hasOwn(copy, maskedKey); n += 1) maskedKey = `${redactText(key)}#${String(n)}`;
143
+ setOwn(copy, maskedKey, isSecretKey(key) && item !== null && typeof item !== "boolean" ? REDACTED : redactValue(item, path));
144
+ }
145
+ return copy;
146
+ };
147
+ return { redactText, redactValue: (value) => redactValue(value), addKnownSecret };
148
+ }
149
+ function setOwn(target, key, value) {
150
+ Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
151
+ }
152
+ var kernelRedactor = createRedactor();
153
+ function redactSecretText(text) {
154
+ return kernelRedactor.redactText(text);
155
+ }
156
+ var defaultSecretSink = {
157
+ register(secret) {
158
+ kernelRedactor.addKnownSecret(secret);
159
+ }
160
+ };
161
+
162
+ export {
163
+ redactSecretText,
164
+ defaultSecretSink
165
+ };
166
+ //# sourceMappingURL=chunk-L7CPE2SO.js.map
@@ -0,0 +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":[]}
@@ -0,0 +1,190 @@
1
+ import type { ContractVersion } from './contract.types.ts';
2
+ /** Имя файла обнаружения host в каталоге данных (onreel D-046). */
3
+ export declare const HOST_DISCOVERY_FILE = "host.json";
4
+ /** Адрес host для процесса агента: агенты не читают host.json, а получают адрес и свой токен в env. */
5
+ export declare const ENV_HOST_URL = "ONREEL_HOST_URL";
6
+ /** Токен агента в env его процесса. */
7
+ export declare const ENV_TOKEN = "ONREEL_TOKEN";
8
+ /** Идентификаторы Run, Task и попытки в env процесса агента (onreel D-226): воркер отчитывается ими в worker_done. */
9
+ export declare const ENV_RUN_ID = "ONREEL_RUN_ID";
10
+ export declare const ENV_TASK_ID = "ONREEL_TASK_ID";
11
+ export declare const ENV_DISPATCH_ID = "ONREEL_DISPATCH_ID";
12
+ /** Переменная окружения: восьмеричная маска пользователя («0022»), её первым записывает самый ранний процесс цепочки. */
13
+ export declare const ENV_SESSION_UMASK = "ONREEL_SESSION_UMASK";
14
+ /** Маска сессий, если переменная не задана или не разобралась: обычный umask пользователя. */
15
+ export declare const DEFAULT_SESSION_UMASK = 18;
16
+ /**
17
+ * Версия контракта host onreel на момент выделения (1.14). Заморожена для межпроцессной
18
+ * совместимости MCP-сервера с host onreel; история изменений — в onreel version.const.ts.
19
+ */
20
+ export declare const HOST_CONTRACT_VERSION: Readonly<ContractVersion>;
21
+ export declare const errorCatalog: {
22
+ readonly invalid_request: {
23
+ readonly status: 400;
24
+ readonly summary: 'Запрос не прошёл проверку схемы контракта.';
25
+ };
26
+ readonly unauthorized: {
27
+ readonly status: 401;
28
+ readonly summary: 'Нет токена, или токен недействителен, истёк или отозван.';
29
+ };
30
+ readonly permission_denied: {
31
+ readonly status: 403;
32
+ readonly summary: 'Запрос шире scope токена.';
33
+ };
34
+ readonly not_found: {
35
+ readonly status: 404;
36
+ readonly summary: 'Сущность не найдена.';
37
+ };
38
+ readonly contract_incompatible: {
39
+ readonly status: 426;
40
+ readonly summary: 'Версии контракта сторон несовместимы (разница major больше допустимой).';
41
+ };
42
+ readonly capability_unsupported: {
43
+ readonly status: 400;
44
+ readonly summary: 'Глагол или значение требует capability, которой нет в согласованном наборе.';
45
+ };
46
+ readonly request_id_reused: {
47
+ readonly status: 409;
48
+ readonly summary: 'Тот же requestId уже использован для другого запроса.';
49
+ };
50
+ readonly rate_limited: {
51
+ readonly status: 429;
52
+ readonly summary: 'Слишком много запросов.';
53
+ };
54
+ readonly internal: {
55
+ readonly status: 500;
56
+ readonly summary: 'Внутренняя ошибка.';
57
+ };
58
+ readonly unavailable: {
59
+ readonly status: 503;
60
+ readonly summary: 'Сервис временно недоступен: host или ptyd не отвечает.';
61
+ };
62
+ readonly lifecycle_conflict: {
63
+ readonly status: 409;
64
+ readonly summary: 'Переход из текущего состояния сущности не разрешён.';
65
+ };
66
+ readonly unknown_task: {
67
+ readonly status: 404;
68
+ readonly summary: 'Task не найден.';
69
+ };
70
+ readonly unknown_dispatch: {
71
+ readonly status: 404;
72
+ readonly summary: 'Dispatch не найден.';
73
+ };
74
+ readonly task_dispatch_mismatch: {
75
+ readonly status: 409;
76
+ readonly summary: 'Dispatch не принадлежит этой Task.';
77
+ };
78
+ readonly stale_dispatch: {
79
+ readonly status: 409;
80
+ readonly summary: 'Попытка уже не авторитетна: у Task другой активный Dispatch.';
81
+ };
82
+ readonly inactive_dispatch: {
83
+ readonly status: 409;
84
+ readonly summary: 'Dispatch уже settled.';
85
+ };
86
+ readonly consumer_fenced: {
87
+ readonly status: 409;
88
+ readonly summary: 'Потребитель mailbox заменён: consumer_generation вырос.';
89
+ };
90
+ readonly budget_exceeded: {
91
+ readonly status: 409;
92
+ readonly summary: 'Лимит бюджета исчерпан.';
93
+ };
94
+ readonly unknown_session: {
95
+ readonly status: 404;
96
+ readonly summary: 'Сессия ptyd не найдена.';
97
+ };
98
+ readonly session_exited: {
99
+ readonly status: 409;
100
+ readonly summary: 'Процесс сессии уже завершился.';
101
+ };
102
+ readonly spawn_failed: {
103
+ readonly status: 422;
104
+ readonly summary: 'Процесс не запустился.';
105
+ };
106
+ readonly lease_conflict: {
107
+ readonly status: 409;
108
+ readonly summary: 'Lease задачи держит другой исполнитель незавершённой попытки.';
109
+ };
110
+ readonly lease_fenced: {
111
+ readonly status: 409;
112
+ readonly summary: 'Владелец lease не доказан: fencing-токен устарел или процесс не совпал с сессией ptyd.';
113
+ };
114
+ readonly lease_expired: {
115
+ readonly status: 409;
116
+ readonly summary: 'Lease задачи закрыт: освобождён или попытка settled.';
117
+ };
118
+ readonly claim_conflict: {
119
+ readonly status: 409;
120
+ readonly summary: 'Задачу уже взял другой участник.';
121
+ };
122
+ readonly ownership_conflict: {
123
+ readonly status: 409;
124
+ readonly summary: 'Зона ответственности задачи пересекается с чужим claim.';
125
+ };
126
+ readonly host_not_live: {
127
+ readonly status: 409;
128
+ readonly summary: 'Home Dispatch участника не live.';
129
+ };
130
+ readonly claim_limit: {
131
+ readonly status: 409;
132
+ readonly summary: 'У участника уже максимум активных claims.';
133
+ };
134
+ readonly not_assignee: {
135
+ readonly status: 403;
136
+ readonly summary: 'Задача назначена другому участнику.';
137
+ };
138
+ readonly messaging_denied: {
139
+ readonly status: 403;
140
+ readonly summary: 'Политика команды запрещает это сообщение.';
141
+ };
142
+ readonly plan_required: {
143
+ readonly status: 409;
144
+ readonly summary: 'Задача требует одобренного плана.';
145
+ };
146
+ readonly member_name_taken: {
147
+ readonly status: 409;
148
+ readonly summary: 'Имя участника в Run занято.';
149
+ };
150
+ readonly git_failed: {
151
+ readonly status: 422;
152
+ readonly summary: 'Git-команда не выполнилась: репозиторий повреждён, ref не существует или команда отклонена.';
153
+ };
154
+ readonly unknown_project: {
155
+ readonly status: 404;
156
+ readonly summary: 'Проект не найден в реестре.';
157
+ };
158
+ readonly project_exists: {
159
+ readonly status: 409;
160
+ readonly summary: 'Путь или id уже заняты другим проектом.';
161
+ };
162
+ readonly tracker_failed: {
163
+ readonly status: 502;
164
+ readonly summary: 'Внешний трекер отказал или не ответил: адрес, токен или права не подходят.';
165
+ };
166
+ readonly unknown_issue: {
167
+ readonly status: 404;
168
+ readonly summary: 'Задача не найдена в трекере проекта.';
169
+ };
170
+ readonly agent_unavailable: {
171
+ readonly status: 422;
172
+ readonly summary: 'Агент не установлен, probe не прошёл или fallback невозможен.';
173
+ };
174
+ readonly agent_untrusted: {
175
+ readonly status: 403;
176
+ readonly summary: 'Манифест агента из репозитория проекта не доверен.';
177
+ };
178
+ readonly sandbox_unavailable: {
179
+ readonly status: 422;
180
+ readonly summary: 'Запрошенный уровень песочницы на этой машине недоступен: нет механизма ОС.';
181
+ };
182
+ };
183
+ export type ErrorCode = keyof typeof errorCatalog;
184
+ export declare const CONTRACT_ERROR_MESSAGE_MAX = 4000;
185
+ export declare const CONTRACT_ERROR_NEXT_STEP_MAX = 1000;
186
+ /** Максимум записей каталога в ответе worktree.files; остальное отсекается с `truncated`. */
187
+ export declare const WORKTREE_FILES_MAX = 1000;
188
+ /** Причины отказов worktree.files в `data.reason` (onreel D-367). */
189
+ export declare const WORKTREE_FILES_REASONS: readonly ['outside_worktree', 'excluded', 'not_directory', 'unreadable', 'missing_on_disk', 'unsafe_path'];
190
+ export type WorktreeFilesReason = (typeof WORKTREE_FILES_REASONS)[number];
@@ -0,0 +1,22 @@
1
+ import { type ErrorCode } from './contract.const.ts';
2
+ import type { ContractError, ContractVersion } from './contract.types.ts';
3
+ /** Обрезает строку до `max` символов с многоточием: в ошибку попадает и чужой ввод неизвестной длины. */
4
+ export declare function clip(text: string, max: number): string;
5
+ /**
6
+ * Собирает ошибку и проверяет её схемой: длинные `message` и шаги обрезаются, а пустой шаг —
7
+ * ошибка программиста, и функция бросает исключение, а не отдаёт клиенту ответ, который он не разберёт.
8
+ */
9
+ export declare function contractError(code: ErrorCode, message: string, nextSteps: [string, ...string[]], data?: Record<string, unknown>): ContractError;
10
+ export declare function formatVersion(version: ContractVersion): string;
11
+ /** Разбирает восьмеричную маску без префикса: 1-4 цифры 0-7, не больше 0o777; иначе undefined. Пробелы не срезаются. */
12
+ export declare function parseUmask(value: string | undefined): number | undefined;
13
+ /** Маска как четыре восьмеричные цифры: 0o22 -> «0022». */
14
+ export declare function formatUmask(mask: number): string;
15
+ /** Маска сессий из env; нет переменной или она неверна — DEFAULT_SESSION_UMASK. */
16
+ export declare function sessionUmask(env: Readonly<Record<string, string | undefined>>): number;
17
+ /**
18
+ * Записывает current в env, если там нет верной маски, и возвращает маску сессий.
19
+ * Верное значение, уже стоящее в env, не перезаписывается: его задал более ранний процесс цепочки
20
+ * (CLI, Electron, systemd), а значение неверное считается незаданным.
21
+ */
22
+ export declare function inheritSessionUmask(env: Record<string, string | undefined>, current: number): number;