@smwb/onreel-git-mcp 0.0.0-stage → 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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunk-3YP22YLA.js +50 -0
  3. package/dist/chunk-3YP22YLA.js.map +1 -0
  4. package/dist/chunk-4ZGRXIVA.js +259 -0
  5. package/dist/chunk-4ZGRXIVA.js.map +1 -0
  6. package/dist/chunk-C6PHNSDC.js +112 -0
  7. package/dist/chunk-C6PHNSDC.js.map +1 -0
  8. package/dist/chunk-GPRMI4PG.js +421 -0
  9. package/dist/chunk-GPRMI4PG.js.map +1 -0
  10. package/dist/chunk-L7CPE2SO.js +166 -0
  11. package/dist/chunk-L7CPE2SO.js.map +1 -0
  12. package/dist/contract/contract.const.d.ts +190 -0
  13. package/dist/contract/contract.lib.d.ts +22 -0
  14. package/dist/contract/contract.schema.d.ts +279 -0
  15. package/dist/contract/contract.types.d.ts +23 -0
  16. package/dist/files/files.const.d.ts +6 -0
  17. package/dist/files/files.lib.d.ts +14 -0
  18. package/dist/files/files.types.d.ts +13 -0
  19. package/dist/files/index.d.ts +3 -0
  20. package/dist/files/index.js +163 -0
  21. package/dist/files/index.js.map +1 -0
  22. package/dist/git/git.const.d.ts +4 -0
  23. package/dist/git/git.lib.d.ts +90 -0
  24. package/dist/git/git.types.d.ts +33 -0
  25. package/dist/git/index.d.ts +7 -0
  26. package/dist/git/index.js +97 -0
  27. package/dist/git/index.js.map +1 -0
  28. package/dist/git/paths.const.d.ts +5 -0
  29. package/dist/git/paths.lib.d.ts +39 -0
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.js +117 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/kernel/log.types.d.ts +5 -0
  34. package/dist/kernel/redact.lib.d.ts +25 -0
  35. package/dist/kernel/store.types.d.ts +109 -0
  36. package/dist/kernel/umask.lib.d.ts +7 -0
  37. package/dist/mcp/args.lib.d.ts +17 -0
  38. package/dist/mcp/envelope.lib.d.ts +20 -0
  39. package/dist/mcp/index.d.ts +8 -0
  40. package/dist/mcp/index.js +744 -0
  41. package/dist/mcp/index.js.map +1 -0
  42. package/dist/mcp/mcp.const.d.ts +23 -0
  43. package/dist/mcp/mcp.schema.d.ts +64 -0
  44. package/dist/mcp/mcp.types.d.ts +125 -0
  45. package/dist/mcp/principal.lib.d.ts +22 -0
  46. package/dist/mcp/server.lib.d.ts +3 -0
  47. package/dist/mcp/tools.lib.d.ts +16 -0
  48. package/dist/tracker/builtin.lib.d.ts +1 -0
  49. package/dist/tracker/github.lib.d.ts +2 -0
  50. package/dist/tracker/gitlab.lib.d.ts +2 -0
  51. package/dist/tracker/http.lib.d.ts +4 -0
  52. package/dist/tracker/index.d.ts +15 -0
  53. package/dist/tracker/index.js +840 -0
  54. package/dist/tracker/index.js.map +1 -0
  55. package/dist/tracker/jira.lib.d.ts +2 -0
  56. package/dist/tracker/local.lib.d.ts +2 -0
  57. package/dist/tracker/mapping.const.d.ts +26 -0
  58. package/dist/tracker/registry.lib.d.ts +5 -0
  59. package/dist/tracker/secrets.lib.d.ts +7 -0
  60. package/dist/tracker/sourcecraft.lib.d.ts +2 -0
  61. package/dist/tracker/tracker.const.d.ts +10 -0
  62. package/dist/tracker/tracker.lib.d.ts +21 -0
  63. package/dist/tracker/tracker.types.d.ts +77 -0
  64. package/dist/worktree/index.d.ts +4 -0
  65. package/dist/worktree/index.js +203 -0
  66. package/dist/worktree/index.js.map +1 -0
  67. package/dist/worktree/lifecycle.const.d.ts +2 -0
  68. package/dist/worktree/lifecycle.lib.d.ts +30 -0
  69. package/dist/worktree/lifecycle.types.d.ts +14 -0
  70. package/package.json +74 -4
  71. package/README.md +0 -3
@@ -0,0 +1,421 @@
1
+ import {
2
+ PathEscapeError,
3
+ isInside
4
+ } from "./chunk-C6PHNSDC.js";
5
+ import {
6
+ sessionUmask
7
+ } from "./chunk-3YP22YLA.js";
8
+ import {
9
+ redactSecretText
10
+ } from "./chunk-L7CPE2SO.js";
11
+
12
+ // src/git/git.const.ts
13
+ var GIT_TIMEOUT_MS = 12e4;
14
+ var GIT_CHECKOUT_TIMEOUT_MS = 6e5;
15
+
16
+ // src/git/git.lib.ts
17
+ import { spawn } from "child_process";
18
+ import { constants, existsSync } from "fs";
19
+ import { lstat, open, readlink, realpath } from "fs/promises";
20
+ import { basename, dirname, join, resolve } from "path";
21
+
22
+ // src/kernel/umask.lib.ts
23
+ function withUmask(mask, action) {
24
+ const previous = process.umask(mask);
25
+ try {
26
+ return action();
27
+ } finally {
28
+ process.umask(previous);
29
+ }
30
+ }
31
+
32
+ // src/git/git.lib.ts
33
+ var GIT_OUTPUT_MAX_BYTES = 16 * 1024 * 1024;
34
+ var GIT_CONCURRENCY = 4;
35
+ var GitError = class extends Error {
36
+ args;
37
+ exitCode;
38
+ stderr;
39
+ timedOut;
40
+ #rawStdout;
41
+ #stdout;
42
+ constructor(args, exitCode, stderr, stdout = "", timedOut = false) {
43
+ const maskedArgs = args.map((arg) => redactSecretText(arg));
44
+ const maskedStderr = redactSecretText(stderr);
45
+ const what = timedOut ? "\u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u0441\u044F \u0432 \u0442\u0430\u0439\u043C\u0430\u0443\u0442" : `\u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441 \u043A\u043E\u0434\u043E\u043C ${String(exitCode)}`;
46
+ super(`git ${maskedArgs.join(" ").slice(0, 300)} ${what}: ${maskedStderr.slice(0, 500)}`);
47
+ this.name = "GitError";
48
+ this.args = maskedArgs;
49
+ this.exitCode = exitCode;
50
+ this.stderr = maskedStderr;
51
+ this.#rawStdout = stdout;
52
+ this.timedOut = timedOut;
53
+ }
54
+ get stdout() {
55
+ if (this.#stdout === void 0) {
56
+ this.#stdout = redactSecretText(this.#rawStdout);
57
+ this.#rawStdout = "";
58
+ }
59
+ return this.#stdout;
60
+ }
61
+ };
62
+ var active = 0;
63
+ var waiting = [];
64
+ var running = /* @__PURE__ */ new Set();
65
+ var closed = false;
66
+ function acquireSlot() {
67
+ if (closed) return Promise.reject(new Error("\u043F\u0443\u043B git \u0437\u0430\u043A\u0440\u044B\u0442: host \u043E\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442\u0441\u044F"));
68
+ if (active < GIT_CONCURRENCY) {
69
+ active += 1;
70
+ return Promise.resolve();
71
+ }
72
+ return new Promise((start, fail) => waiting.push({ start, fail }));
73
+ }
74
+ function releaseSlot() {
75
+ const next = waiting.shift();
76
+ if (next === void 0) active -= 1;
77
+ else next.start();
78
+ }
79
+ function closeGitPool() {
80
+ closed = true;
81
+ for (const item of waiting.splice(0)) item.fail(new Error("\u043F\u0443\u043B git \u0437\u0430\u043A\u0440\u044B\u0442: host \u043E\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442\u0441\u044F"));
82
+ for (const kill of running) kill();
83
+ }
84
+ function isGitPoolClosed() {
85
+ return closed;
86
+ }
87
+ function reopenGitPool() {
88
+ closed = false;
89
+ }
90
+ function gitEnv(extra = {}) {
91
+ const env = {};
92
+ for (const [key, value] of Object.entries(process.env)) {
93
+ if (!key.startsWith("GIT_")) env[key] = value;
94
+ }
95
+ return { ...env, GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C", ...extra };
96
+ }
97
+ async function runGit(args, cwd, options = {}) {
98
+ await acquireSlot();
99
+ try {
100
+ return await spawnGit(args, cwd, options);
101
+ } finally {
102
+ releaseSlot();
103
+ }
104
+ }
105
+ function spawnGit(args, cwd, options) {
106
+ return new Promise((resolvePromise, reject) => {
107
+ const child = withUmask(
108
+ sessionUmask(process.env),
109
+ () => spawn("git", [...args], {
110
+ cwd,
111
+ env: gitEnv(options.env),
112
+ stdio: ["ignore", "pipe", "pipe"],
113
+ detached: process.platform !== "win32",
114
+ windowsHide: true
115
+ })
116
+ );
117
+ const stdout = [];
118
+ const stderr = [];
119
+ let size = 0;
120
+ let settled = false;
121
+ const finish = (error, run) => {
122
+ if (settled) return;
123
+ settled = true;
124
+ clearTimeout(timer);
125
+ running.delete(kill);
126
+ if (error !== void 0) reject(error);
127
+ else if (run !== void 0) resolvePromise(run);
128
+ };
129
+ const kill = () => {
130
+ try {
131
+ if (child.pid !== void 0 && process.platform !== "win32") process.kill(-child.pid, "SIGKILL");
132
+ else child.kill("SIGKILL");
133
+ } catch {
134
+ }
135
+ child.stdout.destroy();
136
+ child.stderr.destroy();
137
+ };
138
+ running.add(kill);
139
+ const timer = setTimeout(() => {
140
+ kill();
141
+ finish(new GitError(args, null, Buffer.concat(stderr).toString("utf8"), "", true));
142
+ }, options.timeoutMs ?? GIT_TIMEOUT_MS);
143
+ child.stdout.on("data", (chunk) => {
144
+ size += chunk.length;
145
+ if (size > GIT_OUTPUT_MAX_BYTES) {
146
+ kill();
147
+ finish(new GitError(args, null, `\u0432\u044B\u0432\u043E\u0434 \u0431\u043E\u043B\u044C\u0448\u0435 ${String(GIT_OUTPUT_MAX_BYTES)} \u0431\u0430\u0439\u0442`));
148
+ return;
149
+ }
150
+ stdout.push(chunk);
151
+ });
152
+ child.stderr.on("data", (chunk) => {
153
+ if (stderr.length < 64) stderr.push(chunk);
154
+ });
155
+ child.once("error", (error) => finish(error));
156
+ child.once("close", (code) => {
157
+ if (closed && code === null) {
158
+ finish(new Error("git \u043F\u0440\u0435\u0440\u0432\u0430\u043D: host \u043E\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442\u0441\u044F"));
159
+ return;
160
+ }
161
+ const run = { code: code ?? -1, stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8") };
162
+ if (run.code === 0 || options.okCodes?.includes(run.code) === true) finish(void 0, run);
163
+ else finish(new GitError(args, code, run.stderr, run.stdout));
164
+ });
165
+ });
166
+ }
167
+ async function git(args, cwd, options = {}) {
168
+ return (await runGit(args, cwd, options)).stdout;
169
+ }
170
+ async function repoToplevel(path) {
171
+ return (await git(["rev-parse", "--show-toplevel"], path)).trim();
172
+ }
173
+ async function resolveCommit(cwd, ref) {
174
+ const run = await runGit(["rev-parse", "--verify", "--quiet", "--end-of-options", `${ref}^{commit}`], cwd, { okCodes: [1, 128] });
175
+ return run.code === 0 ? run.stdout.trim() : void 0;
176
+ }
177
+ async function currentBranch(repoPath) {
178
+ const run = await runGit(["symbolic-ref", "--quiet", "--short", "HEAD"], repoPath, { okCodes: [1] });
179
+ return run.code === 0 ? run.stdout.trim() : void 0;
180
+ }
181
+ async function isValidBranchName(repoPath, branch) {
182
+ const run = await runGit(["check-ref-format", "--branch", branch], repoPath, { okCodes: [1, 128] });
183
+ return run.code === 0;
184
+ }
185
+ async function branchSha(repoPath, branch) {
186
+ const run = await runGit(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], repoPath, { okCodes: [1] });
187
+ return run.code === 0 ? run.stdout.trim() : void 0;
188
+ }
189
+ async function addWorktree(repoPath, worktreePath, branch, baseSha) {
190
+ await git(["worktree", "add", "--no-track", "-b", branch, "--end-of-options", worktreePath, baseSha], repoPath, {
191
+ timeoutMs: GIT_CHECKOUT_TIMEOUT_MS
192
+ });
193
+ }
194
+ async function pruneWorktrees(repoPath) {
195
+ await git(["worktree", "prune"], repoPath);
196
+ }
197
+ async function deleteBranch(repoPath, branch) {
198
+ await git(["branch", "-D", "--end-of-options", branch], repoPath);
199
+ }
200
+ async function headOf(worktreePath) {
201
+ return (await git(["rev-parse", "--verify", "HEAD"], worktreePath)).trim();
202
+ }
203
+ async function statusEntries(worktreePath, untracked = "normal") {
204
+ const out = await git(["status", "--porcelain=v1", "-z", `--untracked-files=${untracked}`], worktreePath);
205
+ const records = out.split("\0");
206
+ const entries = [];
207
+ for (let i = 0; i < records.length; i += 1) {
208
+ const record = records[i] ?? "";
209
+ if (record.length < 4) continue;
210
+ const index = record[0] ?? " ";
211
+ const worktree = record[1] ?? " ";
212
+ entries.push({ path: record.slice(3), index, worktree });
213
+ if (index === "R" || index === "C") i += 1;
214
+ }
215
+ return entries;
216
+ }
217
+ async function isDirty(worktreePath) {
218
+ return (await statusEntries(worktreePath)).length > 0;
219
+ }
220
+ async function diffAgainst(worktreePath, sha) {
221
+ return git(["diff", "--end-of-options", sha], worktreePath);
222
+ }
223
+ async function commitChanges(worktreePath, message, paths) {
224
+ const literal = { env: { GIT_LITERAL_PATHSPECS: "1" } };
225
+ const only = paths === void 0 || paths.length === 0 ? [] : ["--", ...paths];
226
+ await git(only.length === 0 ? ["add", "-A"] : ["add", ...only], worktreePath, literal);
227
+ const staged = await runGit(["diff", "--cached", "--quiet", ...only], worktreePath, { ...literal, okCodes: [1] });
228
+ if (staged.code === 0) return { committed: false, headSha: await headOf(worktreePath) };
229
+ await git(["commit", "-m", message, ...only], worktreePath, literal);
230
+ return { committed: true, headSha: await headOf(worktreePath) };
231
+ }
232
+ async function operationInProgress(worktreePath) {
233
+ const markers = ["rebase-merge", "rebase-apply", "MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD"];
234
+ const out = await git(["rev-parse", ...markers.flatMap((marker) => ["--git-path", marker])], worktreePath);
235
+ const paths = out.split("\n").filter((line) => line.length > 0);
236
+ for (const [i, marker] of markers.entries()) {
237
+ const path = paths[i];
238
+ if (path !== void 0 && existsSync(resolve(worktreePath, path))) return marker;
239
+ }
240
+ return void 0;
241
+ }
242
+ async function rebaseOnto(worktreePath, baseSha) {
243
+ const operation = await operationInProgress(worktreePath);
244
+ if (operation !== void 0) return { kind: "busy", operation };
245
+ const dirty = await statusEntries(worktreePath, "no");
246
+ if (dirty.length > 0) return { kind: "dirty", paths: dirty.map((entry) => entry.path) };
247
+ const before = await headOf(worktreePath);
248
+ try {
249
+ await git(["rebase", "--no-autostash", "--end-of-options", baseSha], worktreePath);
250
+ return { kind: "done", conflict: false, conflicts: [], headSha: await headOf(worktreePath) };
251
+ } catch (error) {
252
+ const conflicts = await unmergedPaths(worktreePath);
253
+ if (await operationInProgress(worktreePath) !== void 0) {
254
+ await runGit(["rebase", "--abort"], worktreePath, { okCodes: [1, 128] });
255
+ }
256
+ if (conflicts.length > 0) return { kind: "done", conflict: true, conflicts, headSha: before };
257
+ throw error;
258
+ }
259
+ }
260
+ async function unmergedPaths(worktreePath) {
261
+ try {
262
+ const out = await git(["diff", "--name-only", "-z", "--diff-filter=U"], worktreePath);
263
+ return [...new Set(out.split("\0").filter((path) => path.length > 0))];
264
+ } catch {
265
+ return [];
266
+ }
267
+ }
268
+ var CHANGE_STATUS = {
269
+ A: "added",
270
+ M: "modified",
271
+ D: "deleted",
272
+ R: "renamed",
273
+ C: "copied",
274
+ T: "type_changed",
275
+ U: "conflicted"
276
+ };
277
+ var BINARY_SNIFF_BYTES = 8192;
278
+ async function changedFiles(worktreePath, sha) {
279
+ const base = ["diff", "--no-color", "--no-ext-diff", "--no-textconv", "-M"];
280
+ const [names, numstat] = await Promise.all([
281
+ git([...base, "--name-status", "-z", "--end-of-options", sha], worktreePath),
282
+ git([...base, "--numstat", "-z", "--end-of-options", sha], worktreePath)
283
+ ]);
284
+ const counts = /* @__PURE__ */ new Map();
285
+ const numRecords = numstat.split("\0");
286
+ for (let i = 0; i < numRecords.length; i += 1) {
287
+ const match = /^(\d+|-)\t(\d+|-)\t([\s\S]*)$/.exec(numRecords[i] ?? "");
288
+ if (match === null) continue;
289
+ let path = match[3] ?? "";
290
+ if (path === "") {
291
+ path = numRecords[i + 2] ?? "";
292
+ i += 2;
293
+ }
294
+ counts.set(path, match[1] === "-" ? { binary: true } : { additions: Number(match[1]), deletions: Number(match[2]) });
295
+ }
296
+ const files = [];
297
+ const records = names.split("\0");
298
+ for (let i = 0; i < records.length; i += 1) {
299
+ const code = records[i] ?? "";
300
+ if (code === "") continue;
301
+ const status = CHANGE_STATUS[code[0] ?? ""];
302
+ const first = records[i + 1] ?? "";
303
+ const renamed = code[0] === "R" || code[0] === "C";
304
+ const path = renamed ? records[i + 2] ?? "" : first;
305
+ i += renamed ? 2 : 1;
306
+ if (status === void 0 || path === "") continue;
307
+ files.push({ path, ...renamed ? { oldPath: first } : {}, status, ...counts.get(path) });
308
+ }
309
+ return files;
310
+ }
311
+ async function untrackedFiles(worktreePath) {
312
+ const out = await git(["ls-files", "--others", "--exclude-standard", "-z"], worktreePath);
313
+ return out.split("\0").filter((path) => path.length > 0);
314
+ }
315
+ function sideOfBytes(bytes) {
316
+ const head = bytes.subarray(0, BINARY_SNIFF_BYTES);
317
+ if (head.includes(0)) return { size: bytes.length, binary: true, truncated: false };
318
+ return { size: bytes.length, binary: false, truncated: false, text: bytes.toString("utf8") };
319
+ }
320
+ async function showBlob(worktreePath, sha, path, maxBytes) {
321
+ const object = `${sha}:${path}`;
322
+ const sized = await runGit(["cat-file", "-s", object], worktreePath, { okCodes: [128] });
323
+ if (sized.code !== 0) return void 0;
324
+ const size = Number(sized.stdout.trim());
325
+ if (size > maxBytes) {
326
+ const type = await runGit(["cat-file", "-t", object], worktreePath, { okCodes: [128] });
327
+ return type.code === 0 && type.stdout.trim() === "blob" ? { size, binary: false, truncated: true } : void 0;
328
+ }
329
+ const blob = await runGit(["cat-file", "blob", object], worktreePath, { okCodes: [128] });
330
+ if (blob.code !== 0) return void 0;
331
+ if (blob.stdout.slice(0, BINARY_SNIFF_BYTES).includes("\0")) return { size, binary: true, truncated: false };
332
+ return { size, binary: false, truncated: false, text: blob.stdout };
333
+ }
334
+ async function readWorktreeFile(worktreePath, path, maxBytes) {
335
+ const root = await realpath(worktreePath);
336
+ const lexical = resolve(root, path);
337
+ if (!isInside(root, lexical)) throw new PathEscapeError(`\u043F\u0443\u0442\u044C '${path}' \u0432\u044B\u0445\u043E\u0434\u0438\u0442 \u0437\u0430 worktree`);
338
+ let parent;
339
+ try {
340
+ parent = await realpath(dirname(lexical));
341
+ } catch (error) {
342
+ if (isMissing(error)) return void 0;
343
+ throw error;
344
+ }
345
+ if (parent !== root && !isInside(root, parent)) throw new PathEscapeError(`\u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u043F\u0443\u0442\u0438 '${path}' \u0432\u044B\u0445\u043E\u0434\u0438\u0442 \u0437\u0430 worktree`);
346
+ const target = join(parent, basename(lexical));
347
+ let info;
348
+ try {
349
+ info = await lstat(target);
350
+ } catch (error) {
351
+ if (isMissing(error)) return void 0;
352
+ throw error;
353
+ }
354
+ if (info.isSymbolicLink()) {
355
+ const link = await readlink(target);
356
+ const destination = resolve(parent, link);
357
+ let real = destination;
358
+ try {
359
+ real = await realpath(destination);
360
+ } catch (error) {
361
+ if (!isMissing(error)) throw error;
362
+ }
363
+ if (real !== root && !isInside(root, real)) throw new PathEscapeError(`\u0441\u0438\u043C\u043B\u0438\u043D\u043A '${path}' \u0443\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u044B worktree`);
364
+ return { size: Buffer.byteLength(link), binary: false, truncated: false, text: link };
365
+ }
366
+ if (!info.isFile()) return void 0;
367
+ if (info.size > maxBytes) {
368
+ const sniff = await readHead(target, BINARY_SNIFF_BYTES);
369
+ return { size: info.size, binary: sniff.includes(0), truncated: !sniff.includes(0) };
370
+ }
371
+ const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
372
+ try {
373
+ return sideOfBytes(await handle.readFile());
374
+ } finally {
375
+ await handle.close();
376
+ }
377
+ }
378
+ async function readHead(file, bytes) {
379
+ const handle = await open(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
380
+ try {
381
+ const buffer = Buffer.alloc(bytes);
382
+ const { bytesRead } = await handle.read(buffer, 0, bytes, 0);
383
+ return buffer.subarray(0, bytesRead);
384
+ } finally {
385
+ await handle.close();
386
+ }
387
+ }
388
+ function isMissing(error) {
389
+ const code = error?.code;
390
+ return code === "ENOENT" || code === "ENOTDIR";
391
+ }
392
+
393
+ export {
394
+ GIT_TIMEOUT_MS,
395
+ GIT_CHECKOUT_TIMEOUT_MS,
396
+ GitError,
397
+ closeGitPool,
398
+ isGitPoolClosed,
399
+ reopenGitPool,
400
+ runGit,
401
+ repoToplevel,
402
+ resolveCommit,
403
+ currentBranch,
404
+ isValidBranchName,
405
+ branchSha,
406
+ addWorktree,
407
+ pruneWorktrees,
408
+ deleteBranch,
409
+ headOf,
410
+ statusEntries,
411
+ isDirty,
412
+ diffAgainst,
413
+ commitChanges,
414
+ operationInProgress,
415
+ rebaseOnto,
416
+ changedFiles,
417
+ untrackedFiles,
418
+ showBlob,
419
+ readWorktreeFile
420
+ };
421
+ //# sourceMappingURL=chunk-GPRMI4PG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/git/git.const.ts","../src/git/git.lib.ts","../src/kernel/umask.lib.ts"],"sourcesContent":["// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d,\n// src/host/worktree/git.const.ts — дословная копия.\n\n/** Таймаут обычной команды: локальный репозиторий отвечает за секунды, зависание — сбой. */\nexport const GIT_TIMEOUT_MS = 120_000;\n\n/** `git worktree add` выкладывает всё дерево: на больших репозиториях это минуты. */\nexport const GIT_CHECKOUT_TIMEOUT_MS = 600_000;\n","// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d,\n// src/host/worktree/git.lib.ts — дословная копия; импорты перенацелены на швы ядра\n// этой библиотеки: sessionUmask — срез контракта (src/contract), withUmask — src/kernel,\n// redactSecretText — src/kernel (часть B выделения). Публичный API и поведение — как в onreel.\n\nimport { spawn } from 'node:child_process';\nimport { constants, existsSync } from 'node:fs';\nimport { lstat, open, readlink, realpath } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { sessionUmask } from '../contract/contract.lib.ts';\nimport type { WorktreeChangeStatus, WorktreeStatusEntry } from '../contract/contract.types.ts';\nimport { withUmask } from '../kernel/umask.lib.ts';\nimport { redactSecretText } from '../kernel/redact.lib.ts';\nimport { GIT_TIMEOUT_MS, GIT_CHECKOUT_TIMEOUT_MS } from './git.const.ts';\nimport type { GitRunOptions, GitRun, CommitOutcome, RebaseOutcome, ChangedFile, FileSide } from './git.types.ts';\nimport { PathEscapeError, isInside } from './paths.lib.ts';\n\n// Git-операции worktree (D-050): каждая команда — дочерний процесс `git` пользователя,\n// асинхронно, с таймаутом и ограничением параллелизма (пул §3.1: «worker_threads или child»,\n// задания можно отменить). Event loop host не блокируется: HTTP, WS и связь с ptyd работают,\n// пока git занят. Аргументы — массивом без shell; всё, что пришло от клиента, git получает\n// только после `--end-of-options` или уже резолвленным в sha.\n\nconst GIT_OUTPUT_MAX_BYTES = 16 * 1024 * 1024;\n\n/** Одновременных процессов git: остальные ждут в очереди. */\nconst GIT_CONCURRENCY = 4;\n\n/**\n * Git-команда не выполнилась: код, вывод и признак таймаута — для ошибки `git_failed` и журнала.\n * Аргументы и вывод маскируются здесь, до сообщения (D-272): stderr git и хуков несёт\n * `https://user:token@host`, а текст ошибки уходит в ответ, receipt, `worktrees.error` и события.\n * Поля `args` и `stderr` хранят уже замаскированные значения; `stdout` (до 16 МиБ, его никто не\n * читает) маскируется при первом чтении, чтобы не держать event loop на каждом сбое git.\n */\nexport class GitError extends Error {\n readonly args: readonly string[];\n readonly exitCode: number | null;\n readonly stderr: string;\n readonly timedOut: boolean;\n #rawStdout: string;\n #stdout: string | undefined;\n\n constructor(args: readonly string[], exitCode: number | null, stderr: string, stdout = '', timedOut = false) {\n const maskedArgs = args.map((arg) => redactSecretText(arg));\n const maskedStderr = redactSecretText(stderr);\n const what = timedOut ? 'не уложился в таймаут' : `завершился с кодом ${String(exitCode)}`;\n // Обрезка — после маскирования: иначе срез мог бы оставить префикс секрета, который шаблон уже не узнает.\n super(`git ${maskedArgs.join(' ').slice(0, 300)} ${what}: ${maskedStderr.slice(0, 500)}`);\n this.name = 'GitError';\n this.args = maskedArgs;\n this.exitCode = exitCode;\n this.stderr = maskedStderr;\n this.#rawStdout = stdout;\n this.timedOut = timedOut;\n }\n\n get stdout(): string {\n if (this.#stdout === undefined) {\n this.#stdout = redactSecretText(this.#rawStdout);\n this.#rawStdout = '';\n }\n return this.#stdout;\n }\n}\n\n// --- Пул процессов ---\n\nlet active = 0;\nconst waiting: Array<{ start: () => void; fail: (error: Error) => void }> = [];\nconst running = new Set<() => void>();\nlet closed = false;\n\nfunction acquireSlot(): Promise<void> {\n if (closed) return Promise.reject(new Error('пул git закрыт: host останавливается'));\n if (active < GIT_CONCURRENCY) {\n active += 1;\n return Promise.resolve();\n }\n return new Promise((start, fail) => waiting.push({ start, fail }));\n}\n\nfunction releaseSlot(): void {\n const next = waiting.shift();\n if (next === undefined) active -= 1;\n else next.start();\n}\n\n/** Остановка host: очередь отклоняется, запущенные git убиваются (задания пула отменяемы, §3.1). */\nexport function closeGitPool(): void {\n closed = true;\n for (const item of waiting.splice(0)) item.fail(new Error('пул git закрыт: host останавливается'));\n for (const kill of running) kill();\n}\n\n/** Пул закрыт остановкой host: сбой git тогда не повод писать failed — доделает старт. */\nexport function isGitPoolClosed(): boolean {\n return closed;\n}\n\n/** Только для тестов: снова открыть пул после closeGitPool. */\nexport function reopenGitPool(): void {\n closed = false;\n}\n\n/**\n * Окружение git: без унаследованных `GIT_*` (host, запущенный из хука или с GIT_DIR, иначе\n * работал бы с чужим репозиторием), без запросов на терминал, с языком C — коды и вывод\n * не зависят от локали пользователя; необязательные блокировки индекса выключены.\n */\nfunction gitEnv(extra: Record<string, string> = {}): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (!key.startsWith('GIT_')) env[key] = value;\n }\n return { ...env, GIT_TERMINAL_PROMPT: '0', GIT_OPTIONAL_LOCKS: '0', LC_ALL: 'C', ...extra };\n}\n\n/** Запуск git в cwd: ненулевой код вне okCodes, таймаут или переполнение вывода — GitError. */\nexport async function runGit(args: readonly string[], cwd: string, options: GitRunOptions = {}): Promise<GitRun> {\n await acquireSlot();\n try {\n return await spawnGit(args, cwd, options);\n } finally {\n releaseSlot();\n }\n}\n\nfunction spawnGit(args: readonly string[], cwd: string, options: GitRunOptions): Promise<GitRun> {\n return new Promise((resolvePromise, reject) => {\n // Своя группа процессов: таймаут убивает и git, и его потомков (хуки, pinentry, fsmonitor).\n // git пишет в дерево пользователя: дитя получает его umask (D-330), а host остаётся под 077.\n // Env читается при каждом вызове: маску задаёт самый ранний процесс цепочки, тесты её меняют.\n const child = withUmask(sessionUmask(process.env), () =>\n spawn('git', [...args], {\n cwd,\n env: gitEnv(options.env),\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: process.platform !== 'win32',\n windowsHide: true,\n }),\n );\n const stdout: Buffer[] = [];\n const stderr: Buffer[] = [];\n let size = 0;\n let settled = false;\n const finish = (error: Error | undefined, run?: GitRun): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n running.delete(kill);\n if (error !== undefined) reject(error);\n else if (run !== undefined) resolvePromise(run);\n };\n const kill = (): void => {\n try {\n if (child.pid !== undefined && process.platform !== 'win32') process.kill(-child.pid, 'SIGKILL');\n else child.kill('SIGKILL');\n } catch {\n // Процесс уже вышел.\n }\n child.stdout.destroy();\n child.stderr.destroy();\n };\n running.add(kill);\n const timer = setTimeout(() => {\n kill();\n finish(new GitError(args, null, Buffer.concat(stderr).toString('utf8'), '', true));\n }, options.timeoutMs ?? GIT_TIMEOUT_MS);\n child.stdout.on('data', (chunk: Buffer) => {\n size += chunk.length;\n if (size > GIT_OUTPUT_MAX_BYTES) {\n kill();\n finish(new GitError(args, null, `вывод больше ${String(GIT_OUTPUT_MAX_BYTES)} байт`));\n return;\n }\n stdout.push(chunk);\n });\n child.stderr.on('data', (chunk: Buffer) => {\n if (stderr.length < 64) stderr.push(chunk);\n });\n child.once('error', (error) => finish(error));\n child.once('close', (code) => {\n if (closed && code === null) {\n finish(new Error('git прерван: host останавливается'));\n return;\n }\n const run = { code: code ?? -1, stdout: Buffer.concat(stdout).toString('utf8'), stderr: Buffer.concat(stderr).toString('utf8') };\n if (run.code === 0 || options.okCodes?.includes(run.code) === true) finish(undefined, run);\n else finish(new GitError(args, code, run.stderr, run.stdout));\n });\n });\n}\n\nasync function git(args: readonly string[], cwd: string, options: GitRunOptions = {}): Promise<string> {\n return (await runGit(args, cwd, options)).stdout;\n}\n\n// --- Репозиторий и ref'ы ---\n\n/** Корень рабочего дерева репозитория, в котором лежит path; не репозиторий или bare — GitError. */\nexport async function repoToplevel(path: string): Promise<string> {\n return (await git(['rev-parse', '--show-toplevel'], path)).trim();\n}\n\n/** Ref резолвится в коммит: sha; нет такого ref — undefined. Ref идёт после `--end-of-options`. */\nexport async function resolveCommit(cwd: string, ref: string): Promise<string | undefined> {\n const run = await runGit(['rev-parse', '--verify', '--quiet', '--end-of-options', `${ref}^{commit}`], cwd, { okCodes: [1, 128] });\n return run.code === 0 ? run.stdout.trim() : undefined;\n}\n\n/** Текущая ветка репозитория; HEAD отсоединён — undefined. */\nexport async function currentBranch(repoPath: string): Promise<string | undefined> {\n const run = await runGit(['symbolic-ref', '--quiet', '--short', 'HEAD'], repoPath, { okCodes: [1] });\n return run.code === 0 ? run.stdout.trim() : undefined;\n}\n\n/** Имя проходит `git check-ref-format --branch`. */\nexport async function isValidBranchName(repoPath: string, branch: string): Promise<boolean> {\n const run = await runGit(['check-ref-format', '--branch', branch], repoPath, { okCodes: [1, 128] });\n return run.code === 0;\n}\n\n/** Sha локальной ветки; ветки нет — undefined. */\nexport async function branchSha(repoPath: string, branch: string): Promise<string | undefined> {\n const run = await runGit(['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], repoPath, { okCodes: [1] });\n return run.code === 0 ? run.stdout.trim() : undefined;\n}\n\n// --- Создание и очистка ---\n\n/** `git worktree add --no-track -b <branch> <path> <sha>` (§6.9): ветка без upstream. */\nexport async function addWorktree(repoPath: string, worktreePath: string, branch: string, baseSha: string): Promise<void> {\n await git(['worktree', 'add', '--no-track', '-b', branch, '--end-of-options', worktreePath, baseSha], repoPath, {\n timeoutMs: GIT_CHECKOUT_TIMEOUT_MS,\n });\n}\n\n/** Снимает с учёта репозитория worktree, чьих каталогов больше нет (после переноса в trash). */\nexport async function pruneWorktrees(repoPath: string): Promise<void> {\n await git(['worktree', 'prune'], repoPath);\n}\n\n/** Удаляет локальную ветку. */\nexport async function deleteBranch(repoPath: string, branch: string): Promise<void> {\n await git(['branch', '-D', '--end-of-options', branch], repoPath);\n}\n\n/** HEAD worktree: sha (40 или 64 hex). */\nexport async function headOf(worktreePath: string): Promise<string> {\n return (await git(['rev-parse', '--verify', 'HEAD'], worktreePath)).trim();\n}\n\n// --- Ревью ---\n\n/**\n * `git status --porcelain=v1 -z` → записи ревью. С -z имена приходят байт в байт, без\n * кавычек и экранирования; у переименования и копии следом идёт запись исходного пути.\n */\nexport async function statusEntries(worktreePath: string, untracked: 'normal' | 'no' = 'normal'): Promise<WorktreeStatusEntry[]> {\n const out = await git(['status', '--porcelain=v1', '-z', `--untracked-files=${untracked}`], worktreePath);\n const records = out.split('\\0');\n const entries: WorktreeStatusEntry[] = [];\n for (let i = 0; i < records.length; i += 1) {\n const record = records[i] ?? '';\n if (record.length < 4) continue;\n const index = record[0] ?? ' ';\n const worktree = record[1] ?? ' ';\n entries.push({ path: record.slice(3), index, worktree });\n if (index === 'R' || index === 'C') i += 1;\n }\n return entries;\n}\n\n/** Есть незакоммиченные изменения (включая неотслеживаемые файлы вне .gitignore). */\nexport async function isDirty(worktreePath: string): Promise<boolean> {\n return (await statusEntries(worktreePath)).length > 0;\n}\n\n/** `git diff <sha>`: индекс и рабочий каталог против коммита. */\nexport async function diffAgainst(worktreePath: string, sha: string): Promise<string> {\n return git(['diff', '--end-of-options', sha], worktreePath);\n}\n\n/**\n * Коммитит изменения: `git add -A` и `git commit`, или только переданные пути — `git add` и\n * `git commit --only` по ним, буквально (pathspec-магия вида `:(top)` от клиента не работает),\n * так что прежде добавленное в индекс в коммит не попадает. Нечего коммитить — committed: false.\n */\nexport async function commitChanges(worktreePath: string, message: string, paths?: readonly string[]): Promise<CommitOutcome> {\n const literal = { env: { GIT_LITERAL_PATHSPECS: '1' } };\n const only = paths === undefined || paths.length === 0 ? [] : ['--', ...paths];\n await git(only.length === 0 ? ['add', '-A'] : ['add', ...only], worktreePath, literal);\n const staged = await runGit(['diff', '--cached', '--quiet', ...only], worktreePath, { ...literal, okCodes: [1] });\n if (staged.code === 0) return { committed: false, headSha: await headOf(worktreePath) };\n await git(['commit', '-m', message, ...only], worktreePath, literal);\n return { committed: true, headSha: await headOf(worktreePath) };\n}\n\n/** Незавершённая операция git в worktree: rebase, merge, cherry-pick или revert; нет — undefined. */\nexport async function operationInProgress(worktreePath: string): Promise<string | undefined> {\n const markers = ['rebase-merge', 'rebase-apply', 'MERGE_HEAD', 'CHERRY_PICK_HEAD', 'REVERT_HEAD'];\n const out = await git(['rev-parse', ...markers.flatMap((marker) => ['--git-path', marker])], worktreePath);\n const paths = out.split('\\n').filter((line) => line.length > 0);\n for (const [i, marker] of markers.entries()) {\n const path = paths[i];\n if (path !== undefined && existsSync(resolve(worktreePath, path))) return marker;\n }\n return undefined;\n}\n\n/**\n * Rebase worktree на коммит. Чужой незавершённый rebase или merge и изменения отслеживаемых\n * файлов — отказ без действий: работу агента вызов не трогает. Конфликт — список путей и\n * `git rebase --abort` только для rebase, начатого этим вызовом; HEAD остаётся прежним.\n */\nexport async function rebaseOnto(worktreePath: string, baseSha: string): Promise<RebaseOutcome> {\n const operation = await operationInProgress(worktreePath);\n if (operation !== undefined) return { kind: 'busy', operation };\n const dirty = await statusEntries(worktreePath, 'no');\n if (dirty.length > 0) return { kind: 'dirty', paths: dirty.map((entry) => entry.path) };\n const before = await headOf(worktreePath);\n try {\n await git(['rebase', '--no-autostash', '--end-of-options', baseSha], worktreePath);\n return { kind: 'done', conflict: false, conflicts: [], headSha: await headOf(worktreePath) };\n } catch (error) {\n const conflicts = await unmergedPaths(worktreePath);\n // Rebase до вызова не шёл (проверено выше): всё, что сейчас в процессе, начато этим вызовом.\n if ((await operationInProgress(worktreePath)) !== undefined) {\n await runGit(['rebase', '--abort'], worktreePath, { okCodes: [1, 128] });\n }\n if (conflicts.length > 0) return { kind: 'done', conflict: true, conflicts, headSha: before };\n throw error;\n }\n}\n\n/** Пути с неразрешёнными конфликтами (mid-rebase); пусто — конфликта нет. */\nasync function unmergedPaths(worktreePath: string): Promise<string[]> {\n try {\n const out = await git(['diff', '--name-only', '-z', '--diff-filter=U'], worktreePath);\n return [...new Set(out.split('\\0').filter((path) => path.length > 0))];\n } catch {\n return [];\n }\n}\n\n// --- Изменения для ревью (#30, D-320) ---\n\nconst CHANGE_STATUS: Readonly<Record<string, WorktreeChangeStatus>> = {\n A: 'added',\n M: 'modified',\n D: 'deleted',\n R: 'renamed',\n C: 'copied',\n T: 'type_changed',\n U: 'conflicted',\n};\n\n/** Сколько начальных байт смотрим на NUL, чтобы признать файл бинарным (как git). */\nconst BINARY_SNIFF_BYTES = 8192;\n\n/**\n * Файлы отслеживаемого дерева, изменённые против коммита: статус из `--name-status -z -M`,\n * счётчики строк и признак бинарного (`-\\t-`) из `--numstat -z -M`. Пути — байт в байт (-z);\n * у переименования и копии в записи два пути (исходный, затем новый). Неотслеживаемые файлы\n * здесь не видны: их отдаёт untrackedFiles.\n */\nexport async function changedFiles(worktreePath: string, sha: string): Promise<ChangedFile[]> {\n const base = ['diff', '--no-color', '--no-ext-diff', '--no-textconv', '-M'];\n const [names, numstat] = await Promise.all([\n git([...base, '--name-status', '-z', '--end-of-options', sha], worktreePath),\n git([...base, '--numstat', '-z', '--end-of-options', sha], worktreePath),\n ]);\n const counts = new Map<string, { additions?: number; deletions?: number; binary?: boolean }>();\n const numRecords = numstat.split('\\0');\n for (let i = 0; i < numRecords.length; i += 1) {\n const match = /^(\\d+|-)\\t(\\d+|-)\\t([\\s\\S]*)$/.exec(numRecords[i] ?? '');\n if (match === null) continue;\n let path = match[3] ?? '';\n // Переименование: путь в записи пуст, дальше идут исходный и новый пути.\n if (path === '') {\n path = numRecords[i + 2] ?? '';\n i += 2;\n }\n counts.set(path, match[1] === '-' ? { binary: true } : { additions: Number(match[1]), deletions: Number(match[2]) });\n }\n const files: ChangedFile[] = [];\n const records = names.split('\\0');\n for (let i = 0; i < records.length; i += 1) {\n const code = records[i] ?? '';\n if (code === '') continue;\n const status = CHANGE_STATUS[code[0] ?? ''];\n const first = records[i + 1] ?? '';\n const renamed = code[0] === 'R' || code[0] === 'C';\n const path = renamed ? (records[i + 2] ?? '') : first;\n i += renamed ? 2 : 1;\n if (status === undefined || path === '') continue;\n files.push({ path, ...(renamed ? { oldPath: first } : {}), status, ...counts.get(path) });\n }\n return files;\n}\n\n/** Неотслеживаемые файлы вне .gitignore: `ls-files --others --exclude-standard -z`. */\nexport async function untrackedFiles(worktreePath: string): Promise<string[]> {\n const out = await git(['ls-files', '--others', '--exclude-standard', '-z'], worktreePath);\n return out.split('\\0').filter((path) => path.length > 0);\n}\n\n/** Сторона файла из байтов: бинарный (NUL в начале) текст не несёт; текст — UTF-8 с заменой\n * неверных последовательностей (U+FFFD), CRLF сохраняется как есть. */\nfunction sideOfBytes(bytes: Buffer): FileSide {\n const head = bytes.subarray(0, BINARY_SNIFF_BYTES);\n if (head.includes(0)) return { size: bytes.length, binary: true, truncated: false };\n return { size: bytes.length, binary: false, truncated: false, text: bytes.toString('utf8') };\n}\n\n/**\n * Содержимое файла в коммите (`git cat-file`); файла там нет (или это не блоб) — undefined.\n * Больше maxBytes — усечённая сторона без текста: объект целиком не читается, поэтому бинарность\n * такого файла неизвестна (binary: false).\n */\nexport async function showBlob(worktreePath: string, sha: string, path: string, maxBytes: number): Promise<FileSide | undefined> {\n const object = `${sha}:${path}`;\n const sized = await runGit(['cat-file', '-s', object], worktreePath, { okCodes: [128] });\n if (sized.code !== 0) return undefined;\n const size = Number(sized.stdout.trim());\n if (size > maxBytes) {\n // Каталог (tree) блобом не является, даже если он велик.\n const type = await runGit(['cat-file', '-t', object], worktreePath, { okCodes: [128] });\n return type.code === 0 && type.stdout.trim() === 'blob' ? { size, binary: false, truncated: true } : undefined;\n }\n // Вывод — строка utf8 (runGit декодирует): размер в байтах берём из cat-file, NUL виден в строке.\n const blob = await runGit(['cat-file', 'blob', object], worktreePath, { okCodes: [128] });\n if (blob.code !== 0) return undefined;\n if (blob.stdout.slice(0, BINARY_SNIFF_BYTES).includes('\\0')) return { size, binary: true, truncated: false };\n return { size, binary: false, truncated: false, text: blob.stdout };\n}\n\n/**\n * Рабочее состояние файла для ревью. Корень и родитель файла резолвятся realpath и обязаны\n * лежать внутри worktree (иначе PathEscapeError). Симлинк не разыменовывается: показывается\n * текст ссылки, как у git, но ссылка, чья цель выходит за worktree, — PathEscapeError.\n * Нет файла, это каталог или не обычный файл (fifo, сокет) — undefined; бинарный и больший\n * maxBytes файлы текста не несут.\n */\nexport async function readWorktreeFile(worktreePath: string, path: string, maxBytes: number): Promise<FileSide | undefined> {\n const root = await realpath(worktreePath);\n const lexical = resolve(root, path);\n if (!isInside(root, lexical)) throw new PathEscapeError(`путь '${path}' выходит за worktree`);\n let parent: string;\n try {\n parent = await realpath(dirname(lexical));\n } catch (error) {\n if (isMissing(error)) return undefined;\n throw error;\n }\n if (parent !== root && !isInside(root, parent)) throw new PathEscapeError(`каталог пути '${path}' выходит за worktree`);\n const target = join(parent, basename(lexical));\n let info;\n try {\n info = await lstat(target);\n } catch (error) {\n if (isMissing(error)) return undefined;\n throw error;\n }\n if (info.isSymbolicLink()) {\n const link = await readlink(target);\n const destination = resolve(parent, link);\n let real = destination;\n try {\n real = await realpath(destination);\n } catch (error) {\n if (!isMissing(error)) throw error;\n }\n if (real !== root && !isInside(root, real)) throw new PathEscapeError(`симлинк '${path}' указывает за пределы worktree`);\n return { size: Buffer.byteLength(link), binary: false, truncated: false, text: link };\n }\n if (!info.isFile()) return undefined;\n if (info.size > maxBytes) {\n const sniff = await readHead(target, BINARY_SNIFF_BYTES);\n return { size: info.size, binary: sniff.includes(0), truncated: !sniff.includes(0) };\n }\n const handle = await open(target, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\n try {\n return sideOfBytes(await handle.readFile());\n } finally {\n await handle.close();\n }\n}\n\nasync function readHead(file: string, bytes: number): Promise<Buffer> {\n const handle = await open(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));\n try {\n const buffer = Buffer.alloc(bytes);\n const { bytesRead } = await handle.read(buffer, 0, bytes, 0);\n return buffer.subarray(0, bytesRead);\n } finally {\n await handle.close();\n }\n}\n\nfunction isMissing(error: unknown): boolean {\n const code = (error as { code?: unknown } | null)?.code;\n return code === 'ENOENT' || code === 'ENOTDIR';\n}\n","// Временная смена umask процесса на время запуска дитяти (onreel D-330).\n// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d, src/ptyd/umask.lib.ts,\n// дословная копия.\n\n/**\n * Выполняет action под umask mask и возвращает прежний umask, даже если action бросил.\n * Только для синхронных действий: асинхронная функция вернула бы управление до своего завершения,\n * и umask восстановился бы раньше, чем отработал её код. В worker-потоках process.umask(mask)\n * бросает ERR_WORKER_UNSUPPORTED_OPERATION: ptyd и host запускают процессы из главного потока.\n */\nexport function withUmask<T>(mask: number, action: () => T): T {\n const previous = process.umask(mask);\n try {\n return action();\n } finally {\n process.umask(previous);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAIO,IAAM,iBAAiB;AAGvB,IAAM,0BAA0B;;;ACFvC,SAAS,aAAa;AACtB,SAAS,WAAW,kBAAkB;AACtC,SAAS,OAAO,MAAM,UAAU,gBAAgB;AAChD,SAAS,UAAU,SAAS,MAAM,eAAe;;;ACE1C,SAAS,UAAa,MAAc,QAAoB;AAC7D,QAAM,WAAW,QAAQ,MAAM,IAAI;AACnC,MAAI;AACF,WAAO,OAAO;AAAA,EAChB,UAAE;AACA,YAAQ,MAAM,QAAQ;AAAA,EACxB;AACF;;;ADMA,IAAM,uBAAuB,KAAK,OAAO;AAGzC,IAAM,kBAAkB;AASjB,IAAM,WAAN,cAAuB,MAAM;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EAEA,YAAY,MAAyB,UAAyB,QAAgB,SAAS,IAAI,WAAW,OAAO;AAC3G,UAAM,aAAa,KAAK,IAAI,CAAC,QAAQ,iBAAiB,GAAG,CAAC;AAC1D,UAAM,eAAe,iBAAiB,MAAM;AAC5C,UAAM,OAAO,WAAW,oHAA0B,sGAAsB,OAAO,QAAQ,CAAC;AAExF,UAAM,OAAO,WAAW,KAAK,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,IAAI,IAAI,KAAK,aAAa,MAAM,GAAG,GAAG,CAAC,EAAE;AACxF,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,IAAI,SAAiB;AACnB,QAAI,KAAK,YAAY,QAAW;AAC9B,WAAK,UAAU,iBAAiB,KAAK,UAAU;AAC/C,WAAK,aAAa;AAAA,IACpB;AACA,WAAO,KAAK;AAAA,EACd;AACF;AAIA,IAAI,SAAS;AACb,IAAM,UAAsE,CAAC;AAC7E,IAAM,UAAU,oBAAI,IAAgB;AACpC,IAAI,SAAS;AAEb,SAAS,cAA6B;AACpC,MAAI,OAAQ,QAAO,QAAQ,OAAO,IAAI,MAAM,8JAAsC,CAAC;AACnF,MAAI,SAAS,iBAAiB;AAC5B,cAAU;AACV,WAAO,QAAQ,QAAQ;AAAA,EACzB;AACA,SAAO,IAAI,QAAQ,CAAC,OAAO,SAAS,QAAQ,KAAK,EAAE,OAAO,KAAK,CAAC,CAAC;AACnE;AAEA,SAAS,cAAoB;AAC3B,QAAM,OAAO,QAAQ,MAAM;AAC3B,MAAI,SAAS,OAAW,WAAU;AAAA,MAC7B,MAAK,MAAM;AAClB;AAGO,SAAS,eAAqB;AACnC,WAAS;AACT,aAAW,QAAQ,QAAQ,OAAO,CAAC,EAAG,MAAK,KAAK,IAAI,MAAM,8JAAsC,CAAC;AACjG,aAAW,QAAQ,QAAS,MAAK;AACnC;AAGO,SAAS,kBAA2B;AACzC,SAAO;AACT;AAGO,SAAS,gBAAsB;AACpC,WAAS;AACX;AAOA,SAAS,OAAO,QAAgC,CAAC,GAAsB;AACrE,QAAM,MAAyB,CAAC;AAChC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG,GAAG;AACtD,QAAI,CAAC,IAAI,WAAW,MAAM,EAAG,KAAI,GAAG,IAAI;AAAA,EAC1C;AACA,SAAO,EAAE,GAAG,KAAK,qBAAqB,KAAK,oBAAoB,KAAK,QAAQ,KAAK,GAAG,MAAM;AAC5F;AAGA,eAAsB,OAAO,MAAyB,KAAa,UAAyB,CAAC,GAAoB;AAC/G,QAAM,YAAY;AAClB,MAAI;AACF,WAAO,MAAM,SAAS,MAAM,KAAK,OAAO;AAAA,EAC1C,UAAE;AACA,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,SAAS,MAAyB,KAAa,SAAyC;AAC/F,SAAO,IAAI,QAAQ,CAAC,gBAAgB,WAAW;AAI7C,UAAM,QAAQ;AAAA,MAAU,aAAa,QAAQ,GAAG;AAAA,MAAG,MACjD,MAAM,OAAO,CAAC,GAAG,IAAI,GAAG;AAAA,QACtB;AAAA,QACA,KAAK,OAAO,QAAQ,GAAG;AAAA,QACvB,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,QAChC,UAAU,QAAQ,aAAa;AAAA,QAC/B,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AACA,UAAM,SAAmB,CAAC;AAC1B,UAAM,SAAmB,CAAC;AAC1B,QAAI,OAAO;AACX,QAAI,UAAU;AACd,UAAM,SAAS,CAAC,OAA0B,QAAuB;AAC/D,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,cAAQ,OAAO,IAAI;AACnB,UAAI,UAAU,OAAW,QAAO,KAAK;AAAA,eAC5B,QAAQ,OAAW,gBAAe,GAAG;AAAA,IAChD;AACA,UAAM,OAAO,MAAY;AACvB,UAAI;AACF,YAAI,MAAM,QAAQ,UAAa,QAAQ,aAAa,QAAS,SAAQ,KAAK,CAAC,MAAM,KAAK,SAAS;AAAA,YAC1F,OAAM,KAAK,SAAS;AAAA,MAC3B,QAAQ;AAAA,MAER;AACA,YAAM,OAAO,QAAQ;AACrB,YAAM,OAAO,QAAQ;AAAA,IACvB;AACA,YAAQ,IAAI,IAAI;AAChB,UAAM,QAAQ,WAAW,MAAM;AAC7B,WAAK;AACL,aAAO,IAAI,SAAS,MAAM,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,GAAG,IAAI,IAAI,CAAC;AAAA,IACnF,GAAG,QAAQ,aAAa,cAAc;AACtC,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,cAAQ,MAAM;AACd,UAAI,OAAO,sBAAsB;AAC/B,aAAK;AACL,eAAO,IAAI,SAAS,MAAM,MAAM,uEAAgB,OAAO,oBAAoB,CAAC,2BAAO,CAAC;AACpF;AAAA,MACF;AACA,aAAO,KAAK,KAAK;AAAA,IACnB,CAAC;AACD,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,UAAI,OAAO,SAAS,GAAI,QAAO,KAAK,KAAK;AAAA,IAC3C,CAAC;AACD,UAAM,KAAK,SAAS,CAAC,UAAU,OAAO,KAAK,CAAC;AAC5C,UAAM,KAAK,SAAS,CAAC,SAAS;AAC5B,UAAI,UAAU,SAAS,MAAM;AAC3B,eAAO,IAAI,MAAM,iJAAmC,CAAC;AACrD;AAAA,MACF;AACA,YAAM,MAAM,EAAE,MAAM,QAAQ,IAAI,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,GAAG,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,EAAE;AAC/H,UAAI,IAAI,SAAS,KAAK,QAAQ,SAAS,SAAS,IAAI,IAAI,MAAM,KAAM,QAAO,QAAW,GAAG;AAAA,UACpF,QAAO,IAAI,SAAS,MAAM,MAAM,IAAI,QAAQ,IAAI,MAAM,CAAC;AAAA,IAC9D,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,IAAI,MAAyB,KAAa,UAAyB,CAAC,GAAoB;AACrG,UAAQ,MAAM,OAAO,MAAM,KAAK,OAAO,GAAG;AAC5C;AAKA,eAAsB,aAAa,MAA+B;AAChE,UAAQ,MAAM,IAAI,CAAC,aAAa,iBAAiB,GAAG,IAAI,GAAG,KAAK;AAClE;AAGA,eAAsB,cAAc,KAAa,KAA0C;AACzF,QAAM,MAAM,MAAM,OAAO,CAAC,aAAa,YAAY,WAAW,oBAAoB,GAAG,GAAG,WAAW,GAAG,KAAK,EAAE,SAAS,CAAC,GAAG,GAAG,EAAE,CAAC;AAChI,SAAO,IAAI,SAAS,IAAI,IAAI,OAAO,KAAK,IAAI;AAC9C;AAGA,eAAsB,cAAc,UAA+C;AACjF,QAAM,MAAM,MAAM,OAAO,CAAC,gBAAgB,WAAW,WAAW,MAAM,GAAG,UAAU,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;AACnG,SAAO,IAAI,SAAS,IAAI,IAAI,OAAO,KAAK,IAAI;AAC9C;AAGA,eAAsB,kBAAkB,UAAkB,QAAkC;AAC1F,QAAM,MAAM,MAAM,OAAO,CAAC,oBAAoB,YAAY,MAAM,GAAG,UAAU,EAAE,SAAS,CAAC,GAAG,GAAG,EAAE,CAAC;AAClG,SAAO,IAAI,SAAS;AACtB;AAGA,eAAsB,UAAU,UAAkB,QAA6C;AAC7F,QAAM,MAAM,MAAM,OAAO,CAAC,aAAa,YAAY,WAAW,cAAc,MAAM,EAAE,GAAG,UAAU,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;AACjH,SAAO,IAAI,SAAS,IAAI,IAAI,OAAO,KAAK,IAAI;AAC9C;AAKA,eAAsB,YAAY,UAAkB,cAAsB,QAAgB,SAAgC;AACxH,QAAM,IAAI,CAAC,YAAY,OAAO,cAAc,MAAM,QAAQ,oBAAoB,cAAc,OAAO,GAAG,UAAU;AAAA,IAC9G,WAAW;AAAA,EACb,CAAC;AACH;AAGA,eAAsB,eAAe,UAAiC;AACpE,QAAM,IAAI,CAAC,YAAY,OAAO,GAAG,QAAQ;AAC3C;AAGA,eAAsB,aAAa,UAAkB,QAA+B;AAClF,QAAM,IAAI,CAAC,UAAU,MAAM,oBAAoB,MAAM,GAAG,QAAQ;AAClE;AAGA,eAAsB,OAAO,cAAuC;AAClE,UAAQ,MAAM,IAAI,CAAC,aAAa,YAAY,MAAM,GAAG,YAAY,GAAG,KAAK;AAC3E;AAQA,eAAsB,cAAc,cAAsB,YAA6B,UAA0C;AAC/H,QAAM,MAAM,MAAM,IAAI,CAAC,UAAU,kBAAkB,MAAM,qBAAqB,SAAS,EAAE,GAAG,YAAY;AACxG,QAAM,UAAU,IAAI,MAAM,IAAI;AAC9B,QAAM,UAAiC,CAAC;AACxC,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,UAAM,SAAS,QAAQ,CAAC,KAAK;AAC7B,QAAI,OAAO,SAAS,EAAG;AACvB,UAAM,QAAQ,OAAO,CAAC,KAAK;AAC3B,UAAM,WAAW,OAAO,CAAC,KAAK;AAC9B,YAAQ,KAAK,EAAE,MAAM,OAAO,MAAM,CAAC,GAAG,OAAO,SAAS,CAAC;AACvD,QAAI,UAAU,OAAO,UAAU,IAAK,MAAK;AAAA,EAC3C;AACA,SAAO;AACT;AAGA,eAAsB,QAAQ,cAAwC;AACpE,UAAQ,MAAM,cAAc,YAAY,GAAG,SAAS;AACtD;AAGA,eAAsB,YAAY,cAAsB,KAA8B;AACpF,SAAO,IAAI,CAAC,QAAQ,oBAAoB,GAAG,GAAG,YAAY;AAC5D;AAOA,eAAsB,cAAc,cAAsB,SAAiB,OAAmD;AAC5H,QAAM,UAAU,EAAE,KAAK,EAAE,uBAAuB,IAAI,EAAE;AACtD,QAAM,OAAO,UAAU,UAAa,MAAM,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK;AAC7E,QAAM,IAAI,KAAK,WAAW,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,cAAc,OAAO;AACrF,QAAM,SAAS,MAAM,OAAO,CAAC,QAAQ,YAAY,WAAW,GAAG,IAAI,GAAG,cAAc,EAAE,GAAG,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC;AAChH,MAAI,OAAO,SAAS,EAAG,QAAO,EAAE,WAAW,OAAO,SAAS,MAAM,OAAO,YAAY,EAAE;AACtF,QAAM,IAAI,CAAC,UAAU,MAAM,SAAS,GAAG,IAAI,GAAG,cAAc,OAAO;AACnE,SAAO,EAAE,WAAW,MAAM,SAAS,MAAM,OAAO,YAAY,EAAE;AAChE;AAGA,eAAsB,oBAAoB,cAAmD;AAC3F,QAAM,UAAU,CAAC,gBAAgB,gBAAgB,cAAc,oBAAoB,aAAa;AAChG,QAAM,MAAM,MAAM,IAAI,CAAC,aAAa,GAAG,QAAQ,QAAQ,CAAC,WAAW,CAAC,cAAc,MAAM,CAAC,CAAC,GAAG,YAAY;AACzG,QAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;AAC9D,aAAW,CAAC,GAAG,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC3C,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,SAAS,UAAa,WAAW,QAAQ,cAAc,IAAI,CAAC,EAAG,QAAO;AAAA,EAC5E;AACA,SAAO;AACT;AAOA,eAAsB,WAAW,cAAsB,SAAyC;AAC9F,QAAM,YAAY,MAAM,oBAAoB,YAAY;AACxD,MAAI,cAAc,OAAW,QAAO,EAAE,MAAM,QAAQ,UAAU;AAC9D,QAAM,QAAQ,MAAM,cAAc,cAAc,IAAI;AACpD,MAAI,MAAM,SAAS,EAAG,QAAO,EAAE,MAAM,SAAS,OAAO,MAAM,IAAI,CAAC,UAAU,MAAM,IAAI,EAAE;AACtF,QAAM,SAAS,MAAM,OAAO,YAAY;AACxC,MAAI;AACF,UAAM,IAAI,CAAC,UAAU,kBAAkB,oBAAoB,OAAO,GAAG,YAAY;AACjF,WAAO,EAAE,MAAM,QAAQ,UAAU,OAAO,WAAW,CAAC,GAAG,SAAS,MAAM,OAAO,YAAY,EAAE;AAAA,EAC7F,SAAS,OAAO;AACd,UAAM,YAAY,MAAM,cAAc,YAAY;AAElD,QAAK,MAAM,oBAAoB,YAAY,MAAO,QAAW;AAC3D,YAAM,OAAO,CAAC,UAAU,SAAS,GAAG,cAAc,EAAE,SAAS,CAAC,GAAG,GAAG,EAAE,CAAC;AAAA,IACzE;AACA,QAAI,UAAU,SAAS,EAAG,QAAO,EAAE,MAAM,QAAQ,UAAU,MAAM,WAAW,SAAS,OAAO;AAC5F,UAAM;AAAA,EACR;AACF;AAGA,eAAe,cAAc,cAAyC;AACpE,MAAI;AACF,UAAM,MAAM,MAAM,IAAI,CAAC,QAAQ,eAAe,MAAM,iBAAiB,GAAG,YAAY;AACpF,WAAO,CAAC,GAAG,IAAI,IAAI,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC;AAAA,EACvE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAIA,IAAM,gBAAgE;AAAA,EACpE,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAGA,IAAM,qBAAqB;AAQ3B,eAAsB,aAAa,cAAsB,KAAqC;AAC5F,QAAM,OAAO,CAAC,QAAQ,cAAc,iBAAiB,iBAAiB,IAAI;AAC1E,QAAM,CAAC,OAAO,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,IACzC,IAAI,CAAC,GAAG,MAAM,iBAAiB,MAAM,oBAAoB,GAAG,GAAG,YAAY;AAAA,IAC3E,IAAI,CAAC,GAAG,MAAM,aAAa,MAAM,oBAAoB,GAAG,GAAG,YAAY;AAAA,EACzE,CAAC;AACD,QAAM,SAAS,oBAAI,IAA0E;AAC7F,QAAM,aAAa,QAAQ,MAAM,IAAI;AACrC,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,UAAM,QAAQ,gCAAgC,KAAK,WAAW,CAAC,KAAK,EAAE;AACtE,QAAI,UAAU,KAAM;AACpB,QAAI,OAAO,MAAM,CAAC,KAAK;AAEvB,QAAI,SAAS,IAAI;AACf,aAAO,WAAW,IAAI,CAAC,KAAK;AAC5B,WAAK;AAAA,IACP;AACA,WAAO,IAAI,MAAM,MAAM,CAAC,MAAM,MAAM,EAAE,QAAQ,KAAK,IAAI,EAAE,WAAW,OAAO,MAAM,CAAC,CAAC,GAAG,WAAW,OAAO,MAAM,CAAC,CAAC,EAAE,CAAC;AAAA,EACrH;AACA,QAAM,QAAuB,CAAC;AAC9B,QAAM,UAAU,MAAM,MAAM,IAAI;AAChC,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,UAAM,OAAO,QAAQ,CAAC,KAAK;AAC3B,QAAI,SAAS,GAAI;AACjB,UAAM,SAAS,cAAc,KAAK,CAAC,KAAK,EAAE;AAC1C,UAAM,QAAQ,QAAQ,IAAI,CAAC,KAAK;AAChC,UAAM,UAAU,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM;AAC/C,UAAM,OAAO,UAAW,QAAQ,IAAI,CAAC,KAAK,KAAM;AAChD,SAAK,UAAU,IAAI;AACnB,QAAI,WAAW,UAAa,SAAS,GAAI;AACzC,UAAM,KAAK,EAAE,MAAM,GAAI,UAAU,EAAE,SAAS,MAAM,IAAI,CAAC,GAAI,QAAQ,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;AAAA,EAC1F;AACA,SAAO;AACT;AAGA,eAAsB,eAAe,cAAyC;AAC5E,QAAM,MAAM,MAAM,IAAI,CAAC,YAAY,YAAY,sBAAsB,IAAI,GAAG,YAAY;AACxF,SAAO,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;AACzD;AAIA,SAAS,YAAY,OAAyB;AAC5C,QAAM,OAAO,MAAM,SAAS,GAAG,kBAAkB;AACjD,MAAI,KAAK,SAAS,CAAC,EAAG,QAAO,EAAE,MAAM,MAAM,QAAQ,QAAQ,MAAM,WAAW,MAAM;AAClF,SAAO,EAAE,MAAM,MAAM,QAAQ,QAAQ,OAAO,WAAW,OAAO,MAAM,MAAM,SAAS,MAAM,EAAE;AAC7F;AAOA,eAAsB,SAAS,cAAsB,KAAa,MAAc,UAAiD;AAC/H,QAAM,SAAS,GAAG,GAAG,IAAI,IAAI;AAC7B,QAAM,QAAQ,MAAM,OAAO,CAAC,YAAY,MAAM,MAAM,GAAG,cAAc,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC;AACvF,MAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,QAAM,OAAO,OAAO,MAAM,OAAO,KAAK,CAAC;AACvC,MAAI,OAAO,UAAU;AAEnB,UAAM,OAAO,MAAM,OAAO,CAAC,YAAY,MAAM,MAAM,GAAG,cAAc,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC;AACtF,WAAO,KAAK,SAAS,KAAK,KAAK,OAAO,KAAK,MAAM,SAAS,EAAE,MAAM,QAAQ,OAAO,WAAW,KAAK,IAAI;AAAA,EACvG;AAEA,QAAM,OAAO,MAAM,OAAO,CAAC,YAAY,QAAQ,MAAM,GAAG,cAAc,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC;AACxF,MAAI,KAAK,SAAS,EAAG,QAAO;AAC5B,MAAI,KAAK,OAAO,MAAM,GAAG,kBAAkB,EAAE,SAAS,IAAI,EAAG,QAAO,EAAE,MAAM,QAAQ,MAAM,WAAW,MAAM;AAC3G,SAAO,EAAE,MAAM,QAAQ,OAAO,WAAW,OAAO,MAAM,KAAK,OAAO;AACpE;AASA,eAAsB,iBAAiB,cAAsB,MAAc,UAAiD;AAC1H,QAAM,OAAO,MAAM,SAAS,YAAY;AACxC,QAAM,UAAU,QAAQ,MAAM,IAAI;AAClC,MAAI,CAAC,SAAS,MAAM,OAAO,EAAG,OAAM,IAAI,gBAAgB,6BAAS,IAAI,oEAAuB;AAC5F,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,SAAS,QAAQ,OAAO,CAAC;AAAA,EAC1C,SAAS,OAAO;AACd,QAAI,UAAU,KAAK,EAAG,QAAO;AAC7B,UAAM;AAAA,EACR;AACA,MAAI,WAAW,QAAQ,CAAC,SAAS,MAAM,MAAM,EAAG,OAAM,IAAI,gBAAgB,wEAAiB,IAAI,oEAAuB;AACtH,QAAM,SAAS,KAAK,QAAQ,SAAS,OAAO,CAAC;AAC7C,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,MAAM,MAAM;AAAA,EAC3B,SAAS,OAAO;AACd,QAAI,UAAU,KAAK,EAAG,QAAO;AAC7B,UAAM;AAAA,EACR;AACA,MAAI,KAAK,eAAe,GAAG;AACzB,UAAM,OAAO,MAAM,SAAS,MAAM;AAClC,UAAM,cAAc,QAAQ,QAAQ,IAAI;AACxC,QAAI,OAAO;AACX,QAAI;AACF,aAAO,MAAM,SAAS,WAAW;AAAA,IACnC,SAAS,OAAO;AACd,UAAI,CAAC,UAAU,KAAK,EAAG,OAAM;AAAA,IAC/B;AACA,QAAI,SAAS,QAAQ,CAAC,SAAS,MAAM,IAAI,EAAG,OAAM,IAAI,gBAAgB,+CAAY,IAAI,2HAAiC;AACvH,WAAO,EAAE,MAAM,OAAO,WAAW,IAAI,GAAG,QAAQ,OAAO,WAAW,OAAO,MAAM,KAAK;AAAA,EACtF;AACA,MAAI,CAAC,KAAK,OAAO,EAAG,QAAO;AAC3B,MAAI,KAAK,OAAO,UAAU;AACxB,UAAM,QAAQ,MAAM,SAAS,QAAQ,kBAAkB;AACvD,WAAO,EAAE,MAAM,KAAK,MAAM,QAAQ,MAAM,SAAS,CAAC,GAAG,WAAW,CAAC,MAAM,SAAS,CAAC,EAAE;AAAA,EACrF;AACA,QAAM,SAAS,MAAM,KAAK,QAAQ,UAAU,YAAY,UAAU,cAAc,EAAE;AAClF,MAAI;AACF,WAAO,YAAY,MAAM,OAAO,SAAS,CAAC;AAAA,EAC5C,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;AAEA,eAAe,SAAS,MAAc,OAAgC;AACpE,QAAM,SAAS,MAAM,KAAK,MAAM,UAAU,YAAY,UAAU,cAAc,EAAE;AAChF,MAAI;AACF,UAAM,SAAS,OAAO,MAAM,KAAK;AACjC,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC;AAC3D,WAAO,OAAO,SAAS,GAAG,SAAS;AAAA,EACrC,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;AAEA,SAAS,UAAU,OAAyB;AAC1C,QAAM,OAAQ,OAAqC;AACnD,SAAO,SAAS,YAAY,SAAS;AACvC;","names":[]}