@smwb/onreel-git-mcp 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +408 -0
- package/dist/chunk-3YP22YLA.js +50 -0
- package/dist/chunk-3YP22YLA.js.map +1 -0
- package/dist/chunk-4ZGRXIVA.js +259 -0
- package/dist/chunk-4ZGRXIVA.js.map +1 -0
- package/dist/chunk-C6PHNSDC.js +112 -0
- package/dist/chunk-C6PHNSDC.js.map +1 -0
- package/dist/chunk-GPRMI4PG.js +421 -0
- package/dist/chunk-GPRMI4PG.js.map +1 -0
- package/dist/chunk-L7CPE2SO.js +166 -0
- package/dist/chunk-L7CPE2SO.js.map +1 -0
- package/dist/contract/contract.const.d.ts +190 -0
- package/dist/contract/contract.lib.d.ts +22 -0
- package/dist/contract/contract.schema.d.ts +279 -0
- package/dist/contract/contract.types.d.ts +23 -0
- package/dist/files/files.const.d.ts +6 -0
- package/dist/files/files.lib.d.ts +14 -0
- package/dist/files/files.types.d.ts +13 -0
- package/dist/files/index.d.ts +3 -0
- package/dist/files/index.js +163 -0
- package/dist/files/index.js.map +1 -0
- package/dist/git/git.const.d.ts +4 -0
- package/dist/git/git.lib.d.ts +90 -0
- package/dist/git/git.types.d.ts +33 -0
- package/dist/git/index.d.ts +7 -0
- package/dist/git/index.js +97 -0
- package/dist/git/index.js.map +1 -0
- package/dist/git/paths.const.d.ts +5 -0
- package/dist/git/paths.lib.d.ts +39 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +203 -2
- package/dist/index.js.map +1 -1
- package/dist/kernel/log.types.d.ts +5 -0
- package/dist/kernel/redact.lib.d.ts +25 -0
- package/dist/kernel/store.types.d.ts +109 -0
- package/dist/kernel/umask.lib.d.ts +7 -0
- package/dist/mcp/args.lib.d.ts +17 -0
- package/dist/mcp/envelope.lib.d.ts +20 -0
- package/dist/mcp/index.d.ts +8 -0
- package/dist/mcp/index.js +744 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcp.const.d.ts +23 -0
- package/dist/mcp/mcp.schema.d.ts +64 -0
- package/dist/mcp/mcp.types.d.ts +125 -0
- package/dist/mcp/principal.lib.d.ts +22 -0
- package/dist/mcp/server.lib.d.ts +3 -0
- package/dist/mcp/tools.lib.d.ts +16 -0
- package/dist/tracker/builtin.lib.d.ts +1 -0
- package/dist/tracker/github.lib.d.ts +2 -0
- package/dist/tracker/gitlab.lib.d.ts +2 -0
- package/dist/tracker/http.lib.d.ts +4 -0
- package/dist/tracker/index.d.ts +15 -0
- package/dist/tracker/index.js +840 -0
- package/dist/tracker/index.js.map +1 -0
- package/dist/tracker/jira.lib.d.ts +2 -0
- package/dist/tracker/local.lib.d.ts +2 -0
- package/dist/tracker/mapping.const.d.ts +26 -0
- package/dist/tracker/registry.lib.d.ts +5 -0
- package/dist/tracker/secrets.lib.d.ts +7 -0
- package/dist/tracker/sourcecraft.lib.d.ts +2 -0
- package/dist/tracker/tracker.const.d.ts +10 -0
- package/dist/tracker/tracker.lib.d.ts +21 -0
- package/dist/tracker/tracker.types.d.ts +77 -0
- package/dist/worktree/index.d.ts +4 -0
- package/dist/worktree/index.js +203 -0
- package/dist/worktree/index.js.map +1 -0
- package/dist/worktree/lifecycle.const.d.ts +2 -0
- package/dist/worktree/lifecycle.lib.d.ts +30 -0
- package/dist/worktree/lifecycle.types.d.ts +14 -0
- package/package.json +15 -3
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// src/contract/contract.const.ts
|
|
2
|
+
var HOST_DISCOVERY_FILE = "host.json";
|
|
3
|
+
var ENV_HOST_URL = "ONREEL_HOST_URL";
|
|
4
|
+
var ENV_TOKEN = "ONREEL_TOKEN";
|
|
5
|
+
var ENV_RUN_ID = "ONREEL_RUN_ID";
|
|
6
|
+
var ENV_TASK_ID = "ONREEL_TASK_ID";
|
|
7
|
+
var ENV_DISPATCH_ID = "ONREEL_DISPATCH_ID";
|
|
8
|
+
var ENV_SESSION_UMASK = "ONREEL_SESSION_UMASK";
|
|
9
|
+
var DEFAULT_SESSION_UMASK = 18;
|
|
10
|
+
var HOST_CONTRACT_VERSION = Object.freeze({ major: 1, minor: 14 });
|
|
11
|
+
var errorCatalog = {
|
|
12
|
+
// Общие.
|
|
13
|
+
invalid_request: { status: 400, summary: "\u0417\u0430\u043F\u0440\u043E\u0441 \u043D\u0435 \u043F\u0440\u043E\u0448\u0451\u043B \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u0441\u0445\u0435\u043C\u044B \u043A\u043E\u043D\u0442\u0440\u0430\u043A\u0442\u0430." },
|
|
14
|
+
unauthorized: { status: 401, summary: "\u041D\u0435\u0442 \u0442\u043E\u043A\u0435\u043D\u0430, \u0438\u043B\u0438 \u0442\u043E\u043A\u0435\u043D \u043D\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u0435\u043D, \u0438\u0441\u0442\u0451\u043A \u0438\u043B\u0438 \u043E\u0442\u043E\u0437\u0432\u0430\u043D." },
|
|
15
|
+
permission_denied: { status: 403, summary: "\u0417\u0430\u043F\u0440\u043E\u0441 \u0448\u0438\u0440\u0435 scope \u0442\u043E\u043A\u0435\u043D\u0430." },
|
|
16
|
+
not_found: { status: 404, summary: "\u0421\u0443\u0449\u043D\u043E\u0441\u0442\u044C \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430." },
|
|
17
|
+
contract_incompatible: { status: 426, summary: "\u0412\u0435\u0440\u0441\u0438\u0438 \u043A\u043E\u043D\u0442\u0440\u0430\u043A\u0442\u0430 \u0441\u0442\u043E\u0440\u043E\u043D \u043D\u0435\u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u044B (\u0440\u0430\u0437\u043D\u0438\u0446\u0430 major \u0431\u043E\u043B\u044C\u0448\u0435 \u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0439)." },
|
|
18
|
+
capability_unsupported: { status: 400, summary: "\u0413\u043B\u0430\u0433\u043E\u043B \u0438\u043B\u0438 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 capability, \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u043D\u0435\u0442 \u0432 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u043E\u043C \u043D\u0430\u0431\u043E\u0440\u0435." },
|
|
19
|
+
request_id_reused: { status: 409, summary: "\u0422\u043E\u0442 \u0436\u0435 requestId \u0443\u0436\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0434\u043B\u044F \u0434\u0440\u0443\u0433\u043E\u0433\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0430." },
|
|
20
|
+
rate_limited: { status: 429, summary: "\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u043D\u043E\u0433\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432." },
|
|
21
|
+
internal: { status: 500, summary: "\u0412\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u044F\u044F \u043E\u0448\u0438\u0431\u043A\u0430." },
|
|
22
|
+
unavailable: { status: 503, summary: "\u0421\u0435\u0440\u0432\u0438\u0441 \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: host \u0438\u043B\u0438 ptyd \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442." },
|
|
23
|
+
// Оркестрация: переходы, отчёты, доставка, бюджеты.
|
|
24
|
+
lifecycle_conflict: { status: 409, summary: "\u041F\u0435\u0440\u0435\u0445\u043E\u0434 \u0438\u0437 \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0438 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D." },
|
|
25
|
+
unknown_task: { status: 404, summary: "Task \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D." },
|
|
26
|
+
unknown_dispatch: { status: 404, summary: "Dispatch \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D." },
|
|
27
|
+
task_dispatch_mismatch: { status: 409, summary: "Dispatch \u043D\u0435 \u043F\u0440\u0438\u043D\u0430\u0434\u043B\u0435\u0436\u0438\u0442 \u044D\u0442\u043E\u0439 Task." },
|
|
28
|
+
stale_dispatch: { status: 409, summary: "\u041F\u043E\u043F\u044B\u0442\u043A\u0430 \u0443\u0436\u0435 \u043D\u0435 \u0430\u0432\u0442\u043E\u0440\u0438\u0442\u0435\u0442\u043D\u0430: \u0443 Task \u0434\u0440\u0443\u0433\u043E\u0439 \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 Dispatch." },
|
|
29
|
+
inactive_dispatch: { status: 409, summary: "Dispatch \u0443\u0436\u0435 settled." },
|
|
30
|
+
consumer_fenced: { status: 409, summary: "\u041F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044C mailbox \u0437\u0430\u043C\u0435\u043D\u0451\u043D: consumer_generation \u0432\u044B\u0440\u043E\u0441." },
|
|
31
|
+
budget_exceeded: { status: 409, summary: "\u041B\u0438\u043C\u0438\u0442 \u0431\u044E\u0434\u0436\u0435\u0442\u0430 \u0438\u0441\u0447\u0435\u0440\u043F\u0430\u043D." },
|
|
32
|
+
// ptyd.
|
|
33
|
+
unknown_session: { status: 404, summary: "\u0421\u0435\u0441\u0441\u0438\u044F ptyd \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430." },
|
|
34
|
+
session_exited: { status: 409, summary: "\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u0441\u0435\u0441\u0441\u0438\u0438 \u0443\u0436\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F." },
|
|
35
|
+
spawn_failed: { status: 422, summary: "\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u0441\u044F." },
|
|
36
|
+
// Liveness и lease: аренда владения Task и сессией (§6.8, D-008).
|
|
37
|
+
lease_conflict: { status: 409, summary: "Lease \u0437\u0430\u0434\u0430\u0447\u0438 \u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0440\u0443\u0433\u043E\u0439 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C \u043D\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u043E\u0439 \u043F\u043E\u043F\u044B\u0442\u043A\u0438." },
|
|
38
|
+
lease_fenced: { status: 409, summary: "\u0412\u043B\u0430\u0434\u0435\u043B\u0435\u0446 lease \u043D\u0435 \u0434\u043E\u043A\u0430\u0437\u0430\u043D: fencing-\u0442\u043E\u043A\u0435\u043D \u0443\u0441\u0442\u0430\u0440\u0435\u043B \u0438\u043B\u0438 \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u043B \u0441 \u0441\u0435\u0441\u0441\u0438\u0435\u0439 ptyd." },
|
|
39
|
+
lease_expired: { status: 409, summary: "Lease \u0437\u0430\u0434\u0430\u0447\u0438 \u0437\u0430\u043A\u0440\u044B\u0442: \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0451\u043D \u0438\u043B\u0438 \u043F\u043E\u043F\u044B\u0442\u043A\u0430 settled." },
|
|
40
|
+
// Резерв команд агентов (D-020, D-021): коды занимают сейчас, ядро команды — позже.
|
|
41
|
+
claim_conflict: { status: 409, summary: "\u0417\u0430\u0434\u0430\u0447\u0443 \u0443\u0436\u0435 \u0432\u0437\u044F\u043B \u0434\u0440\u0443\u0433\u043E\u0439 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A." },
|
|
42
|
+
ownership_conflict: { status: 409, summary: "\u0417\u043E\u043D\u0430 \u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0441\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u043F\u0435\u0440\u0435\u0441\u0435\u043A\u0430\u0435\u0442\u0441\u044F \u0441 \u0447\u0443\u0436\u0438\u043C claim." },
|
|
43
|
+
host_not_live: { status: 409, summary: "Home Dispatch \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0430 \u043D\u0435 live." },
|
|
44
|
+
claim_limit: { status: 409, summary: "\u0423 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0430 \u0443\u0436\u0435 \u043C\u0430\u043A\u0441\u0438\u043C\u0443\u043C \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0445 claims." },
|
|
45
|
+
not_assignee: { status: 403, summary: "\u0417\u0430\u0434\u0430\u0447\u0430 \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u043C\u0443 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0443." },
|
|
46
|
+
messaging_denied: { status: 403, summary: "\u041F\u043E\u043B\u0438\u0442\u0438\u043A\u0430 \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0437\u0430\u043F\u0440\u0435\u0449\u0430\u0435\u0442 \u044D\u0442\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435." },
|
|
47
|
+
plan_required: { status: 409, summary: "\u0417\u0430\u0434\u0430\u0447\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043E\u0434\u043E\u0431\u0440\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u043B\u0430\u043D\u0430." },
|
|
48
|
+
member_name_taken: { status: 409, summary: "\u0418\u043C\u044F \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0430 \u0432 Run \u0437\u0430\u043D\u044F\u0442\u043E." },
|
|
49
|
+
// Worktree и git-операции (задача worktree).
|
|
50
|
+
git_failed: { status: 422, summary: "Git-\u043A\u043E\u043C\u0430\u043D\u0434\u0430 \u043D\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043B\u0430\u0441\u044C: \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u043F\u043E\u0432\u0440\u0435\u0436\u0434\u0451\u043D, ref \u043D\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u043B\u0438 \u043A\u043E\u043C\u0430\u043D\u0434\u0430 \u043E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0430." },
|
|
51
|
+
// Реестр проектов (D-343): подключение, показ и снятие.
|
|
52
|
+
unknown_project: { status: 404, summary: "\u041F\u0440\u043E\u0435\u043A\u0442 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u0432 \u0440\u0435\u0435\u0441\u0442\u0440\u0435." },
|
|
53
|
+
project_exists: { status: 409, summary: "\u041F\u0443\u0442\u044C \u0438\u043B\u0438 id \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u0442\u044B \u0434\u0440\u0443\u0433\u0438\u043C \u043F\u0440\u043E\u0435\u043A\u0442\u043E\u043C." },
|
|
54
|
+
// Трекеры задач (D-353): отказ внешнего трекера и неизвестная задача.
|
|
55
|
+
tracker_failed: { status: 502, summary: "\u0412\u043D\u0435\u0448\u043D\u0438\u0439 \u0442\u0440\u0435\u043A\u0435\u0440 \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u0438\u043B\u0438 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B: \u0430\u0434\u0440\u0435\u0441, \u0442\u043E\u043A\u0435\u043D \u0438\u043B\u0438 \u043F\u0440\u0430\u0432\u0430 \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0442." },
|
|
56
|
+
unknown_issue: { status: 404, summary: "\u0417\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430 \u0432 \u0442\u0440\u0435\u043A\u0435\u0440\u0435 \u043F\u0440\u043E\u0435\u043A\u0442\u0430." },
|
|
57
|
+
// Агенты (D-091): манифесты, probe, запуск через Executor.
|
|
58
|
+
agent_unavailable: { status: 422, summary: "\u0410\u0433\u0435\u043D\u0442 \u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, probe \u043D\u0435 \u043F\u0440\u043E\u0448\u0451\u043B \u0438\u043B\u0438 fallback \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D." },
|
|
59
|
+
agent_untrusted: { status: 403, summary: "\u041C\u0430\u043D\u0438\u0444\u0435\u0441\u0442 \u0430\u0433\u0435\u043D\u0442\u0430 \u0438\u0437 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043D\u0435 \u0434\u043E\u0432\u0435\u0440\u0435\u043D." },
|
|
60
|
+
sandbox_unavailable: { status: 422, summary: "\u0417\u0430\u043F\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u043F\u0435\u0441\u043E\u0447\u043D\u0438\u0446\u044B \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: \u043D\u0435\u0442 \u043C\u0435\u0445\u0430\u043D\u0438\u0437\u043C\u0430 \u041E\u0421." }
|
|
61
|
+
};
|
|
62
|
+
var CONTRACT_ERROR_MESSAGE_MAX = 4e3;
|
|
63
|
+
var CONTRACT_ERROR_NEXT_STEP_MAX = 1e3;
|
|
64
|
+
var WORKTREE_FILES_MAX = 1e3;
|
|
65
|
+
var WORKTREE_FILES_REASONS = ["outside_worktree", "excluded", "not_directory", "unreadable", "missing_on_disk", "unsafe_path"];
|
|
66
|
+
|
|
67
|
+
// src/contract/contract.schema.ts
|
|
68
|
+
import { z } from "zod";
|
|
69
|
+
var idPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
70
|
+
var entityIdSchema = z.string().regex(idPattern);
|
|
71
|
+
var requestIdSchema = z.string().regex(idPattern);
|
|
72
|
+
var projectIdSchema = entityIdSchema;
|
|
73
|
+
var taskIdSchema = entityIdSchema;
|
|
74
|
+
var timestampSchema = z.number().int().nonnegative();
|
|
75
|
+
var contractVersionSchema = z.object({
|
|
76
|
+
major: z.number().int().nonnegative().max(65535),
|
|
77
|
+
minor: z.number().int().nonnegative().max(65535)
|
|
78
|
+
});
|
|
79
|
+
var errorCodeSchema = z.enum(Object.keys(errorCatalog));
|
|
80
|
+
var wireErrorCodeSchema = z.string().max(64).regex(/^[a-z][a-z0-9_]*$/);
|
|
81
|
+
var contractErrorSchema = z.object({
|
|
82
|
+
code: wireErrorCodeSchema,
|
|
83
|
+
message: z.string().min(1).max(CONTRACT_ERROR_MESSAGE_MAX),
|
|
84
|
+
/** Конкретные действия: команда CLI, глагол API или шаг для человека. Пустым не бывает. */
|
|
85
|
+
nextSteps: z.array(z.string().min(1).max(CONTRACT_ERROR_NEXT_STEP_MAX)).min(1).max(20),
|
|
86
|
+
data: z.record(z.string(), z.unknown()).optional()
|
|
87
|
+
});
|
|
88
|
+
var trackerKindSchema = z.string().regex(/^[a-z][a-z0-9-]{0,31}$/);
|
|
89
|
+
var issueKeySchema = z.string().min(1).max(200);
|
|
90
|
+
var issueStatusCategorySchema = z.enum(["open", "in_progress", "done", "declined"]);
|
|
91
|
+
var issuePrioritySchema = z.enum(["trivial", "minor", "normal", "critical", "blocker"]);
|
|
92
|
+
var issueLinkSchema = z.object({
|
|
93
|
+
url: z.string().min(1).max(2048),
|
|
94
|
+
title: z.string().min(1).max(500).optional()
|
|
95
|
+
});
|
|
96
|
+
var issueSchema = z.object({
|
|
97
|
+
key: issueKeySchema,
|
|
98
|
+
title: z.string().min(1).max(500),
|
|
99
|
+
description: z.string().max(1e5).optional(),
|
|
100
|
+
status: z.object({ category: issueStatusCategorySchema, name: z.string().min(1).max(200) }),
|
|
101
|
+
priority: issuePrioritySchema.optional(),
|
|
102
|
+
labels: z.array(z.string().min(1).max(200)).max(100),
|
|
103
|
+
/** Исполнитель — отображаемое имя или логин трекера, не id пользователя onreel. */
|
|
104
|
+
assignee: z.string().min(1).max(200).optional(),
|
|
105
|
+
/** Ссылки: страница задачи в трекере (`url` первой) и связанные. */
|
|
106
|
+
links: z.array(issueLinkSchema).max(100),
|
|
107
|
+
createdAt: timestampSchema.optional(),
|
|
108
|
+
updatedAt: timestampSchema.optional()
|
|
109
|
+
});
|
|
110
|
+
var issueCommentSchema = z.object({
|
|
111
|
+
id: z.string().min(1).max(200),
|
|
112
|
+
author: z.string().min(1).max(200).optional(),
|
|
113
|
+
body: z.string().max(1e5),
|
|
114
|
+
createdAt: timestampSchema.optional()
|
|
115
|
+
});
|
|
116
|
+
var trackerFieldNameSchema = z.enum(["url", "target", "user", "token"]);
|
|
117
|
+
var trackerFeaturesSchema = z.object({
|
|
118
|
+
create: z.boolean(),
|
|
119
|
+
update: z.boolean(),
|
|
120
|
+
comment: z.boolean(),
|
|
121
|
+
priority: z.boolean(),
|
|
122
|
+
labels: z.boolean(),
|
|
123
|
+
assignee: z.boolean(),
|
|
124
|
+
/** Категории, в которые можно перевести задачу; пусто — статус не меняется. */
|
|
125
|
+
statuses: z.array(issueStatusCategorySchema).max(4)
|
|
126
|
+
});
|
|
127
|
+
var trackerDescriptorSchema = z.object({
|
|
128
|
+
tracker: trackerKindSchema,
|
|
129
|
+
title: z.string().min(1).max(100),
|
|
130
|
+
fields: z.array(z.object({ name: trackerFieldNameSchema, required: z.boolean(), secret: z.boolean() })).max(4),
|
|
131
|
+
features: trackerFeaturesSchema
|
|
132
|
+
});
|
|
133
|
+
var worktreeIdSchema = entityIdSchema;
|
|
134
|
+
var worktreeNameSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{0,62}$/);
|
|
135
|
+
var gitShaSchema = z.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/);
|
|
136
|
+
var worktreeStatusSchema = z.enum(["preparing", "ready", "cleaning", "cleaned", "failed"]);
|
|
137
|
+
var worktreeSchema = z.object({
|
|
138
|
+
id: worktreeIdSchema,
|
|
139
|
+
/** Task worktree; у worktree проекта без задачи (D-345) поля нет. */
|
|
140
|
+
taskId: taskIdSchema.optional(),
|
|
141
|
+
/** Проект worktree: у worktree задачи — `projectId` её Run (мягкая ссылка, D-344), у worktree проекта — проект реестра. */
|
|
142
|
+
projectId: projectIdSchema.optional(),
|
|
143
|
+
/** Имя worktree проекта без задачи (D-345); у worktree задачи поля нет. */
|
|
144
|
+
name: worktreeNameSchema.optional(),
|
|
145
|
+
/** Ветка worktree: `onreel/<run>/<task>` (§6.9), `onreel/_project/<name>` (D-345) или заданная клиентом; не меняется. */
|
|
146
|
+
branch: z.string().min(1).max(200),
|
|
147
|
+
/** Базовый ref rebase по умолчанию: ветка репозитория при создании, а при detached HEAD — sha. */
|
|
148
|
+
baseRef: z.string().min(1).max(300),
|
|
149
|
+
/** Коммит базы на момент создания: от него создана ветка. */
|
|
150
|
+
baseSha: gitShaSchema,
|
|
151
|
+
/** Корень базового репозитория пользователя (realpath, D-077). */
|
|
152
|
+
repoPath: z.string().min(1).max(2048),
|
|
153
|
+
/** Путь самого worktree: `<dataDir>/workspaces/<repo>/<run>/<task>` (D-078) или `…/<repo>/_project/<name>` (D-345). */
|
|
154
|
+
path: z.string().min(1).max(2048),
|
|
155
|
+
status: worktreeStatusSchema,
|
|
156
|
+
/** HEAD worktree после создания, коммита или rebase; нет — пока неизвестна. */
|
|
157
|
+
headSha: gitShaSchema.optional(),
|
|
158
|
+
/** Причина состояния failed: вывод git или пояснение host. */
|
|
159
|
+
error: z.string().max(4e3).optional(),
|
|
160
|
+
createdAt: timestampSchema,
|
|
161
|
+
updatedAt: timestampSchema,
|
|
162
|
+
/** Момент очистки; ставится на переходе cleaning → cleaned. */
|
|
163
|
+
cleanedAt: timestampSchema.optional()
|
|
164
|
+
});
|
|
165
|
+
var worktreeStatusEntrySchema = z.object({
|
|
166
|
+
path: z.string().min(1).max(2048),
|
|
167
|
+
/** Состояние индекса (X из porcelain), пробел — чисто. */
|
|
168
|
+
index: z.string().max(2),
|
|
169
|
+
/** Состояние рабочего каталога (Y из porcelain), пробел — чисто. */
|
|
170
|
+
worktree: z.string().max(2)
|
|
171
|
+
});
|
|
172
|
+
function isRelativePosixPath(path) {
|
|
173
|
+
if (path.includes("\0") || path.includes("\\") || path.startsWith("/")) return false;
|
|
174
|
+
return path.split("/").every((part) => part !== "" && part !== "..");
|
|
175
|
+
}
|
|
176
|
+
var reviewPathSchema = z.string().min(1).max(4096).refine(isRelativePosixPath, { message: "\u043F\u0443\u0442\u044C \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u043C posix-\u043F\u0443\u0442\u0451\u043C \u0431\u0435\u0437 \xAB..\xBB, \u043F\u0443\u0441\u0442\u044B\u0445 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442 \u0438 NUL" });
|
|
177
|
+
var worktreeChangeStatusSchema = z.enum(["added", "modified", "deleted", "renamed", "copied", "type_changed", "conflicted"]);
|
|
178
|
+
var worktreeChangeSchema = z.object({
|
|
179
|
+
path: reviewPathSchema,
|
|
180
|
+
oldPath: reviewPathSchema.optional(),
|
|
181
|
+
status: worktreeChangeStatusSchema,
|
|
182
|
+
untracked: z.boolean().optional(),
|
|
183
|
+
binary: z.boolean().optional(),
|
|
184
|
+
additions: z.number().int().nonnegative().optional(),
|
|
185
|
+
deletions: z.number().int().nonnegative().optional()
|
|
186
|
+
});
|
|
187
|
+
var worktreeFileSideSchema = z.object({
|
|
188
|
+
size: z.number().int().nonnegative(),
|
|
189
|
+
binary: z.boolean(),
|
|
190
|
+
truncated: z.boolean(),
|
|
191
|
+
text: z.string().optional()
|
|
192
|
+
});
|
|
193
|
+
function hasGitSegment(path) {
|
|
194
|
+
return path.split("/").some((part) => part.toLowerCase() === ".git");
|
|
195
|
+
}
|
|
196
|
+
function hasDotSegment(path) {
|
|
197
|
+
return path.split("/").includes(".");
|
|
198
|
+
}
|
|
199
|
+
var worktreeDirPathSchema = reviewPathSchema.refine((path) => !hasGitSegment(path), { message: "\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 \u043A\u0430\u0442\u0430\u043B\u043E\u0433 .git \u043D\u0435 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442\u0441\u044F" }).refine((path) => !hasDotSegment(path), { message: "\u043F\u0443\u0442\u044C \u0431\u0435\u0437 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u044B \xAB.\xBB" });
|
|
200
|
+
var worktreeEntryKindSchema = z.enum(["file", "dir", "symlink", "other"]);
|
|
201
|
+
var worktreeEntrySchema = z.object({
|
|
202
|
+
/** Имя записи в каталоге. */
|
|
203
|
+
name: z.string().min(1).max(255).refine((name) => !name.includes("/") && !name.includes("\0"), { message: "\u0438\u043C\u044F \u0437\u0430\u043F\u0438\u0441\u0438 \u0431\u0435\u0437 \xAB/\xBB \u0438 NUL" }),
|
|
204
|
+
/** Путь от корня worktree: клиент передаёт его обратно в worktree.files и worktree.file как есть. */
|
|
205
|
+
path: worktreeDirPathSchema,
|
|
206
|
+
kind: worktreeEntryKindSchema,
|
|
207
|
+
/** Размер в байтах; только у `file`. */
|
|
208
|
+
size: z.number().int().nonnegative().optional()
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
export {
|
|
212
|
+
HOST_DISCOVERY_FILE,
|
|
213
|
+
ENV_HOST_URL,
|
|
214
|
+
ENV_TOKEN,
|
|
215
|
+
ENV_RUN_ID,
|
|
216
|
+
ENV_TASK_ID,
|
|
217
|
+
ENV_DISPATCH_ID,
|
|
218
|
+
ENV_SESSION_UMASK,
|
|
219
|
+
DEFAULT_SESSION_UMASK,
|
|
220
|
+
HOST_CONTRACT_VERSION,
|
|
221
|
+
errorCatalog,
|
|
222
|
+
CONTRACT_ERROR_MESSAGE_MAX,
|
|
223
|
+
CONTRACT_ERROR_NEXT_STEP_MAX,
|
|
224
|
+
WORKTREE_FILES_MAX,
|
|
225
|
+
WORKTREE_FILES_REASONS,
|
|
226
|
+
entityIdSchema,
|
|
227
|
+
requestIdSchema,
|
|
228
|
+
projectIdSchema,
|
|
229
|
+
taskIdSchema,
|
|
230
|
+
timestampSchema,
|
|
231
|
+
contractVersionSchema,
|
|
232
|
+
errorCodeSchema,
|
|
233
|
+
wireErrorCodeSchema,
|
|
234
|
+
contractErrorSchema,
|
|
235
|
+
trackerKindSchema,
|
|
236
|
+
issueKeySchema,
|
|
237
|
+
issueStatusCategorySchema,
|
|
238
|
+
issuePrioritySchema,
|
|
239
|
+
issueLinkSchema,
|
|
240
|
+
issueSchema,
|
|
241
|
+
issueCommentSchema,
|
|
242
|
+
trackerFieldNameSchema,
|
|
243
|
+
trackerFeaturesSchema,
|
|
244
|
+
trackerDescriptorSchema,
|
|
245
|
+
worktreeIdSchema,
|
|
246
|
+
worktreeNameSchema,
|
|
247
|
+
gitShaSchema,
|
|
248
|
+
worktreeStatusSchema,
|
|
249
|
+
worktreeSchema,
|
|
250
|
+
worktreeStatusEntrySchema,
|
|
251
|
+
reviewPathSchema,
|
|
252
|
+
worktreeChangeStatusSchema,
|
|
253
|
+
worktreeChangeSchema,
|
|
254
|
+
worktreeFileSideSchema,
|
|
255
|
+
worktreeDirPathSchema,
|
|
256
|
+
worktreeEntryKindSchema,
|
|
257
|
+
worktreeEntrySchema
|
|
258
|
+
};
|
|
259
|
+
//# sourceMappingURL=chunk-4ZGRXIVA.js.map
|
|
@@ -0,0 +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":[]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/git/paths.const.ts
|
|
2
|
+
var PROJECT_WORKTREE_SEGMENT = "_project";
|
|
3
|
+
|
|
4
|
+
// src/git/paths.lib.ts
|
|
5
|
+
import { createHash } from "crypto";
|
|
6
|
+
import { lstatSync, mkdirSync, realpathSync } from "fs";
|
|
7
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "path";
|
|
8
|
+
var PathEscapeError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "PathEscapeError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function isInside(root, candidate) {
|
|
15
|
+
const rel = relative(root, candidate);
|
|
16
|
+
return rel.length > 0 && !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel);
|
|
17
|
+
}
|
|
18
|
+
function workspacesRoot(dataDir) {
|
|
19
|
+
return join(resolve(dataDir), "workspaces");
|
|
20
|
+
}
|
|
21
|
+
function trashRoot(dataDir) {
|
|
22
|
+
return join(resolve(dataDir), "trash");
|
|
23
|
+
}
|
|
24
|
+
function safeComponent(id) {
|
|
25
|
+
return id.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
26
|
+
}
|
|
27
|
+
function repoSlug(repoRealPath) {
|
|
28
|
+
const hash = createHash("sha256").update(repoRealPath).digest("hex").slice(0, 10);
|
|
29
|
+
return `${safeComponent(basename(repoRealPath)).slice(0, 60) || "repo"}-${hash}`;
|
|
30
|
+
}
|
|
31
|
+
function worktreePathFor(dataDir, repoRealPath, runId, taskId) {
|
|
32
|
+
return join(workspacesRoot(dataDir), repoSlug(repoRealPath), safeComponent(runId), safeComponent(taskId));
|
|
33
|
+
}
|
|
34
|
+
function projectWorktreePathFor(dataDir, repoRealPath, name) {
|
|
35
|
+
return join(workspacesRoot(dataDir), repoSlug(repoRealPath), PROJECT_WORKTREE_SEGMENT, safeComponent(name));
|
|
36
|
+
}
|
|
37
|
+
function defaultProjectBranchFor(name, n = 1) {
|
|
38
|
+
const base = `onreel/${PROJECT_WORKTREE_SEGMENT}/${safeComponent(name)}`;
|
|
39
|
+
return n === 1 ? base : `${base}-${String(n)}`;
|
|
40
|
+
}
|
|
41
|
+
function defaultBranchFor(runId, taskId, n = 1) {
|
|
42
|
+
const base = `onreel/${safeComponent(runId)}/${safeComponent(taskId)}`;
|
|
43
|
+
return n === 1 ? base : `${base}-${String(n)}`;
|
|
44
|
+
}
|
|
45
|
+
function checkRepoPath(dataDir, repoPath) {
|
|
46
|
+
if (!isAbsolute(repoPath)) throw new PathEscapeError(`repoPath '${repoPath}' \u043D\u0435 \u0430\u0431\u0441\u043E\u043B\u044E\u0442\u043D\u044B\u0439`);
|
|
47
|
+
assertOutsideDataDir(dataDir, resolve(repoPath));
|
|
48
|
+
const real = realpathSync(repoPath);
|
|
49
|
+
assertOutsideDataDir(dataDir, real);
|
|
50
|
+
return real;
|
|
51
|
+
}
|
|
52
|
+
function assertOutsideDataDir(dataDir, path) {
|
|
53
|
+
for (const root of /* @__PURE__ */ new Set([resolve(dataDir), realpathSync(dataDir)])) {
|
|
54
|
+
if (path === root || isInside(root, path)) throw new PathEscapeError(`\u043F\u0443\u0442\u044C ${path} \u0432\u043D\u0443\u0442\u0440\u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 host`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function prepareWorktreeParent(dataDir, target) {
|
|
58
|
+
const root = workspacesRoot(dataDir);
|
|
59
|
+
if (!isInside(root, target)) throw new PathEscapeError(`\u043F\u0443\u0442\u044C ${target} \u0432\u043D\u0435 \u043A\u043E\u0440\u043D\u044F worktree ${root}`);
|
|
60
|
+
mkdirSync(root, { recursive: true, mode: 448 });
|
|
61
|
+
assertRealDir(root);
|
|
62
|
+
const realRoot = realpathSync(root);
|
|
63
|
+
if (!isInside(realpathSync(dataDir), realRoot)) throw new PathEscapeError(`\u043A\u043E\u0440\u0435\u043D\u044C worktree ${root} \u0443\u0432\u043E\u0434\u0438\u0442 \u0438\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0434\u0430\u043D\u043D\u044B\u0445`);
|
|
64
|
+
let current = root;
|
|
65
|
+
for (const part of relative(root, dirname(target)).split(sep).filter((segment) => segment.length > 0)) {
|
|
66
|
+
current = join(current, part);
|
|
67
|
+
if (!exists(current)) mkdirSync(current, { mode: 448 });
|
|
68
|
+
assertRealDir(current);
|
|
69
|
+
}
|
|
70
|
+
if (!isInside(realRoot, realpathSync(dirname(target)))) throw new PathEscapeError(`\u043A\u0430\u0442\u0430\u043B\u043E\u0433 ${dirname(target)} \u0443\u0432\u043E\u0434\u0438\u0442 \u0438\u0437 \u043A\u043E\u0440\u043D\u044F worktree`);
|
|
71
|
+
if (exists(target)) throw new PathEscapeError(`\u043A\u0430\u0442\u0430\u043B\u043E\u0433 ${target} \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442`);
|
|
72
|
+
}
|
|
73
|
+
function checkManagedPath(dataDir, path) {
|
|
74
|
+
const root = workspacesRoot(dataDir);
|
|
75
|
+
if (!isInside(root, path)) throw new PathEscapeError(`\u043F\u0443\u0442\u044C ${path} \u0432\u043D\u0435 \u043A\u043E\u0440\u043D\u044F worktree ${root}`);
|
|
76
|
+
if (!exists(path)) return false;
|
|
77
|
+
if (lstatSync(path).isSymbolicLink()) throw new PathEscapeError(`\u043A\u0430\u0442\u0430\u043B\u043E\u0433 worktree ${path} \u2014 \u0441\u0438\u043C\u043B\u0438\u043D\u043A`);
|
|
78
|
+
if (!isInside(realpathSync(root), realpathSync(path))) throw new PathEscapeError(`\u043A\u0430\u0442\u0430\u043B\u043E\u0433 ${path} \u0443\u0432\u043E\u0434\u0438\u0442 \u0438\u0437 \u043A\u043E\u0440\u043D\u044F worktree`);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
function assertRealDir(path) {
|
|
82
|
+
const stat = lstatSync(path);
|
|
83
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) throw new PathEscapeError(`${path} \u2014 \u043D\u0435 \u043A\u0430\u0442\u0430\u043B\u043E\u0433 (\u0438\u043B\u0438 \u0441\u0438\u043C\u043B\u0438\u043D\u043A)`);
|
|
84
|
+
}
|
|
85
|
+
function exists(path) {
|
|
86
|
+
try {
|
|
87
|
+
lstatSync(path);
|
|
88
|
+
return true;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error.code === "ENOENT") return false;
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
PROJECT_WORKTREE_SEGMENT,
|
|
97
|
+
PathEscapeError,
|
|
98
|
+
isInside,
|
|
99
|
+
workspacesRoot,
|
|
100
|
+
trashRoot,
|
|
101
|
+
safeComponent,
|
|
102
|
+
repoSlug,
|
|
103
|
+
worktreePathFor,
|
|
104
|
+
projectWorktreePathFor,
|
|
105
|
+
defaultProjectBranchFor,
|
|
106
|
+
defaultBranchFor,
|
|
107
|
+
checkRepoPath,
|
|
108
|
+
assertOutsideDataDir,
|
|
109
|
+
prepareWorktreeParent,
|
|
110
|
+
checkManagedPath
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=chunk-C6PHNSDC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/git/paths.const.ts","../src/git/paths.lib.ts"],"sourcesContent":["// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d,\n// src/host/worktree/paths.const.ts — дословная копия.\n\n/**\n * Сегмент пути и ветки, под которым лежат worktree проектов без задачи (D-345). `safeComponent(id)` ему не\n * равен: id начинается с буквы или цифры (`idPattern`), а `safeComponent` первый символ не меняет.\n */\nexport const PROJECT_WORKTREE_SEGMENT = '_project';\n","// Происхождение — onreel @ b73407be076a5df73931d1340f70910ca13c908d,\n// src/host/worktree/paths.lib.ts — дословная копия.\n\nimport { createHash } from 'node:crypto';\nimport { lstatSync, mkdirSync, realpathSync } from 'node:fs';\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport { PROJECT_WORKTREE_SEGMENT } from './paths.const.ts';\n\n// Пути worktree (D-077, D-078). Базовый репозиторий — проект пользователя где угодно на диске,\n// но не внутри каталога данных host. Сами worktree живут только под корнем\n// `<dataDir>/workspaces` в раскладке `<repo>/<run>/<task>` (§6.9), а worktree проекта без задачи —\n// `<repo>/_project/<name>` (D-345); trash — `<dataDir>/trash`.\n// Граница проверяется по realpath, а каталоги создаются по одному с lstat: симлинк внутри\n// корня не выводит host за его пределы.\n\n/** Путь вне разрешённого корня или через симлинк: отказ `permission_denied`. */\nexport class PathEscapeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PathEscapeError';\n }\n}\n\n/** candidate строго внутри root (лексически, оба пути уже нормализованы); сам root — не «внутри». */\nexport function isInside(root: string, candidate: string): boolean {\n const rel = relative(root, candidate);\n return rel.length > 0 && !rel.startsWith(`..${sep}`) && rel !== '..' && !isAbsolute(rel);\n}\n\nexport function workspacesRoot(dataDir: string): string {\n return join(resolve(dataDir), 'workspaces');\n}\n\nexport function trashRoot(dataDir: string): string {\n return join(resolve(dataDir), 'trash');\n}\n\n/** Компонента пути или ref из id: всё, кроме букв, цифр, `_` и `-`, заменяется на `-`. */\nexport function safeComponent(id: string): string {\n return id.replace(/[^A-Za-z0-9_-]/g, '-');\n}\n\n/** Имя каталога репозитория в раскладке: базовое имя плюс хэш realpath — разные репозитории не сходятся. */\nexport function repoSlug(repoRealPath: string): string {\n const hash = createHash('sha256').update(repoRealPath).digest('hex').slice(0, 10);\n return `${safeComponent(basename(repoRealPath)).slice(0, 60) || 'repo'}-${hash}`;\n}\n\n/** Каталог worktree: `<dataDir>/workspaces/<repo>/<run>/<task>` (§6.9). */\nexport function worktreePathFor(dataDir: string, repoRealPath: string, runId: string, taskId: string): string {\n return join(workspacesRoot(dataDir), repoSlug(repoRealPath), safeComponent(runId), safeComponent(taskId));\n}\n\n/** Каталог worktree проекта без задачи: `<dataDir>/workspaces/<repo>/_project/<name>` (D-345). */\nexport function projectWorktreePathFor(dataDir: string, repoRealPath: string, name: string): string {\n return join(workspacesRoot(dataDir), repoSlug(repoRealPath), PROJECT_WORKTREE_SEGMENT, safeComponent(name));\n}\n\n/** Ветка worktree проекта по умолчанию: `onreel/_project/<name>` (D-345); n > 1 — `-2`, `-3`… при занятой. */\nexport function defaultProjectBranchFor(name: string, n = 1): string {\n const base = `onreel/${PROJECT_WORKTREE_SEGMENT}/${safeComponent(name)}`;\n return n === 1 ? base : `${base}-${String(n)}`;\n}\n\n/** Ветка worktree по умолчанию: `onreel/<run>/<task>` (§6.9); n > 1 — следующий worktree той же Task. */\nexport function defaultBranchFor(runId: string, taskId: string, n = 1): string {\n const base = `onreel/${safeComponent(runId)}/${safeComponent(taskId)}`;\n return n === 1 ? base : `${base}-${String(n)}`;\n}\n\n/**\n * Проверяет repoPath клиента (D-077): абсолютный, существует, и ни сам путь, ни его realpath\n * не лежат в каталоге данных host (там ledger, токены и worktree host): симлинк из каталога\n * данных наружу тоже отказ. Возвращает realpath.\n */\nexport function checkRepoPath(dataDir: string, repoPath: string): string {\n if (!isAbsolute(repoPath)) throw new PathEscapeError(`repoPath '${repoPath}' не абсолютный`);\n assertOutsideDataDir(dataDir, resolve(repoPath));\n const real = realpathSync(repoPath);\n assertOutsideDataDir(dataDir, real);\n return real;\n}\n\n/** Путь не внутри каталога данных host — ни как он записан, ни по realpath каталога. */\nexport function assertOutsideDataDir(dataDir: string, path: string): void {\n for (const root of new Set([resolve(dataDir), realpathSync(dataDir)])) {\n if (path === root || isInside(root, path)) throw new PathEscapeError(`путь ${path} внутри каталога данных host`);\n }\n}\n\n/**\n * Готовит родителя будущего каталога worktree: корень workspaces и промежуточные каталоги\n * создаются по одному (0o700), каждый — настоящий каталог, не симлинк; realpath родителя\n * обязан лежать внутри realpath корня. Сам target существовать не должен.\n */\nexport function prepareWorktreeParent(dataDir: string, target: string): void {\n const root = workspacesRoot(dataDir);\n if (!isInside(root, target)) throw new PathEscapeError(`путь ${target} вне корня worktree ${root}`);\n mkdirSync(root, { recursive: true, mode: 0o700 });\n assertRealDir(root);\n const realRoot = realpathSync(root);\n if (!isInside(realpathSync(dataDir), realRoot)) throw new PathEscapeError(`корень worktree ${root} уводит из каталога данных`);\n let current = root;\n for (const part of relative(root, dirname(target)).split(sep).filter((segment) => segment.length > 0)) {\n current = join(current, part);\n if (!exists(current)) mkdirSync(current, { mode: 0o700 });\n assertRealDir(current);\n }\n if (!isInside(realRoot, realpathSync(dirname(target)))) throw new PathEscapeError(`каталог ${dirname(target)} уводит из корня worktree`);\n if (exists(target)) throw new PathEscapeError(`каталог ${target} уже существует`);\n}\n\n/**\n * Каталог worktree из ledger перед удалением: лексически и по realpath внутри корня workspaces,\n * сам — не симлинк. Нет каталога — false (его убрали руками).\n */\nexport function checkManagedPath(dataDir: string, path: string): boolean {\n const root = workspacesRoot(dataDir);\n if (!isInside(root, path)) throw new PathEscapeError(`путь ${path} вне корня worktree ${root}`);\n if (!exists(path)) return false;\n if (lstatSync(path).isSymbolicLink()) throw new PathEscapeError(`каталог worktree ${path} — симлинк`);\n if (!isInside(realpathSync(root), realpathSync(path))) throw new PathEscapeError(`каталог ${path} уводит из корня worktree`);\n return true;\n}\n\nfunction assertRealDir(path: string): void {\n const stat = lstatSync(path);\n if (stat.isSymbolicLink() || !stat.isDirectory()) throw new PathEscapeError(`${path} — не каталог (или симлинк)`);\n}\n\nfunction exists(path: string): boolean {\n try {\n lstatSync(path);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
|