@bovinphang/craft-common 0.0.0-stage → 1.0.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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -2
  3. package/dist/content/catalog.d.ts +4 -0
  4. package/dist/content/resources.d.ts +2 -0
  5. package/dist/content/types.d.ts +21 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.js +668 -0
  8. package/dist/install/identity.d.ts +5 -0
  9. package/dist/install/planner.d.ts +6 -0
  10. package/dist/install/transaction.d.ts +7 -0
  11. package/dist/install/types.d.ts +44 -0
  12. package/docs/consuming-common.md +33 -0
  13. package/localized/zh-CN/methods/debug.md +13 -0
  14. package/localized/zh-CN/methods/doc-sync.md +12 -0
  15. package/localized/zh-CN/methods/refactoring.md +16 -0
  16. package/localized/zh-CN/methods/source-driven-development.md +19 -0
  17. package/localized/zh-CN/methods/tdd.md +13 -0
  18. package/localized/zh-CN/skills/cc-debug-methodology/SKILL.md +8 -0
  19. package/localized/zh-CN/skills/cc-debug-methodology/references/method.md +13 -0
  20. package/localized/zh-CN/skills/cc-doc-sync-core/SKILL.md +8 -0
  21. package/localized/zh-CN/skills/cc-doc-sync-core/references/method.md +12 -0
  22. package/localized/zh-CN/skills/cc-drawio-studio/SKILL.md +73 -0
  23. package/localized/zh-CN/skills/cc-drawio-studio/data/THIRD_PARTY_NOTICES.md +10 -0
  24. package/localized/zh-CN/skills/cc-drawio-studio/data/brand-icons.json +878 -0
  25. package/localized/zh-CN/skills/cc-drawio-studio/data/shape-index.json +83570 -0
  26. package/localized/zh-CN/skills/cc-drawio-studio/references/data-residency.md +27 -0
  27. package/localized/zh-CN/skills/cc-drawio-studio/references/diagram-patterns.md +60 -0
  28. package/localized/zh-CN/skills/cc-drawio-studio/references/flowchart-quality.md +49 -0
  29. package/localized/zh-CN/skills/cc-drawio-studio/references/layout-and-export.md +72 -0
  30. package/localized/zh-CN/skills/cc-drawio-studio/references/troubleshooting.md +29 -0
  31. package/localized/zh-CN/skills/cc-drawio-studio/references/xml-and-mermaid.md +58 -0
  32. package/localized/zh-CN/skills/cc-drawio-studio/scripts/brand-symbols.mjs +99 -0
  33. package/localized/zh-CN/skills/cc-drawio-studio/scripts/diagram-lint.mjs +205 -0
  34. package/localized/zh-CN/skills/cc-drawio-studio/scripts/diagram-url.mjs +102 -0
  35. package/localized/zh-CN/skills/cc-drawio-studio/scripts/drawio-export.mjs +134 -0
  36. package/localized/zh-CN/skills/cc-drawio-studio/scripts/layout-graph.mjs +107 -0
  37. package/localized/zh-CN/skills/cc-drawio-studio/scripts/png-embed-fix.mjs +26 -0
  38. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-go-packages.mjs +36 -0
  39. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-js-modules.mjs +52 -0
  40. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-python-classes.mjs +30 -0
  41. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-python-modules.mjs +55 -0
  42. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-rust-modules.mjs +35 -0
  43. package/localized/zh-CN/skills/cc-drawio-studio/scripts/scan-ts-modules.mjs +7 -0
  44. package/localized/zh-CN/skills/cc-drawio-studio/scripts/shape-query.mjs +121 -0
  45. package/localized/zh-CN/skills/cc-drawio-studio/scripts/studio-core.mjs +139 -0
  46. package/localized/zh-CN/skills/cc-drawio-studio/styles/built-in/corporate.json +49 -0
  47. package/localized/zh-CN/skills/cc-drawio-studio/styles/built-in/default.json +49 -0
  48. package/localized/zh-CN/skills/cc-drawio-studio/styles/built-in/handdrawn.json +49 -0
  49. package/localized/zh-CN/skills/cc-drawio-studio/styles/schema.json +112 -0
  50. package/localized/zh-CN/skills/cc-refactoring-core/SKILL.md +8 -0
  51. package/localized/zh-CN/skills/cc-refactoring-core/references/method.md +16 -0
  52. package/localized/zh-CN/skills/cc-source-driven-development/SKILL.md +8 -0
  53. package/localized/zh-CN/skills/cc-source-driven-development/references/method.md +19 -0
  54. package/localized/zh-CN/skills/cc-tdd-methodology/SKILL.md +8 -0
  55. package/localized/zh-CN/skills/cc-tdd-methodology/references/method.md +13 -0
  56. package/localized/zh-CN/skills/cc-technical-diagrams/SKILL.md +34 -0
  57. package/localized/zh-CN/skills/cc-technical-diagrams/assets/interactive-diagram.html +371 -0
  58. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/README.md +14 -0
  59. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/architecture.json +121 -0
  60. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/invalid-explicit.json +41 -0
  61. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/sequence.json +53 -0
  62. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/sequence.mmd +21 -0
  63. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/workflow.json +98 -0
  64. package/localized/zh-CN/skills/cc-technical-diagrams/assets/quality-examples/workflow.mmd +7 -0
  65. package/localized/zh-CN/skills/cc-technical-diagrams/references/diagram-workflows.md +81 -0
  66. package/localized/zh-CN/skills/cc-technical-diagrams/references/html-technical-diagrams.md +243 -0
  67. package/localized/zh-CN/skills/cc-technical-diagrams/references/png-qa-autofix.md +37 -0
  68. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/diagram-browser.mjs +153 -0
  69. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/diagram-layout.mjs +176 -0
  70. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/export-diagram.mjs +332 -0
  71. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/interactive-diagram-server.mjs +654 -0
  72. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/mermaid-render.mjs +64 -0
  73. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/png-qa.mjs +462 -0
  74. package/localized/zh-CN/skills/cc-technical-diagrams/scripts/tech-diagram-render.mjs +1537 -0
  75. package/localized/zh-CN/skills/eval_queries.json +160 -0
  76. package/localized/zh-CN/skills/metadata.json +223 -0
  77. package/localized/zh-CN/skills/relations.json +53 -0
  78. package/methods/debug.md +13 -0
  79. package/methods/doc-sync.md +12 -0
  80. package/methods/refactoring.md +16 -0
  81. package/methods/source-driven-development.md +19 -0
  82. package/methods/tdd.md +13 -0
  83. package/package.json +51 -4
  84. package/skills/cc-debug-methodology/SKILL.md +8 -0
  85. package/skills/cc-debug-methodology/references/method.md +13 -0
  86. package/skills/cc-doc-sync-core/SKILL.md +8 -0
  87. package/skills/cc-doc-sync-core/references/method.md +12 -0
  88. package/skills/cc-drawio-studio/SKILL.md +73 -0
  89. package/skills/cc-drawio-studio/data/THIRD_PARTY_NOTICES.md +10 -0
  90. package/skills/cc-drawio-studio/data/brand-icons.json +878 -0
  91. package/skills/cc-drawio-studio/data/shape-index.json +83570 -0
  92. package/skills/cc-drawio-studio/references/data-residency.md +27 -0
  93. package/skills/cc-drawio-studio/references/diagram-patterns.md +60 -0
  94. package/skills/cc-drawio-studio/references/flowchart-quality.md +49 -0
  95. package/skills/cc-drawio-studio/references/layout-and-export.md +72 -0
  96. package/skills/cc-drawio-studio/references/troubleshooting.md +29 -0
  97. package/skills/cc-drawio-studio/references/xml-and-mermaid.md +58 -0
  98. package/skills/cc-drawio-studio/scripts/brand-symbols.mjs +99 -0
  99. package/skills/cc-drawio-studio/scripts/diagram-lint.mjs +205 -0
  100. package/skills/cc-drawio-studio/scripts/diagram-url.mjs +102 -0
  101. package/skills/cc-drawio-studio/scripts/drawio-export.mjs +134 -0
  102. package/skills/cc-drawio-studio/scripts/layout-graph.mjs +107 -0
  103. package/skills/cc-drawio-studio/scripts/png-embed-fix.mjs +26 -0
  104. package/skills/cc-drawio-studio/scripts/scan-go-packages.mjs +36 -0
  105. package/skills/cc-drawio-studio/scripts/scan-js-modules.mjs +52 -0
  106. package/skills/cc-drawio-studio/scripts/scan-python-classes.mjs +30 -0
  107. package/skills/cc-drawio-studio/scripts/scan-python-modules.mjs +55 -0
  108. package/skills/cc-drawio-studio/scripts/scan-rust-modules.mjs +35 -0
  109. package/skills/cc-drawio-studio/scripts/scan-ts-modules.mjs +7 -0
  110. package/skills/cc-drawio-studio/scripts/shape-query.mjs +121 -0
  111. package/skills/cc-drawio-studio/scripts/studio-core.mjs +139 -0
  112. package/skills/cc-drawio-studio/styles/built-in/corporate.json +49 -0
  113. package/skills/cc-drawio-studio/styles/built-in/default.json +49 -0
  114. package/skills/cc-drawio-studio/styles/built-in/handdrawn.json +49 -0
  115. package/skills/cc-drawio-studio/styles/schema.json +112 -0
  116. package/skills/cc-refactoring-core/SKILL.md +8 -0
  117. package/skills/cc-refactoring-core/references/method.md +16 -0
  118. package/skills/cc-source-driven-development/SKILL.md +8 -0
  119. package/skills/cc-source-driven-development/references/method.md +19 -0
  120. package/skills/cc-tdd-methodology/SKILL.md +8 -0
  121. package/skills/cc-tdd-methodology/references/method.md +13 -0
  122. package/skills/cc-technical-diagrams/SKILL.md +34 -0
  123. package/skills/cc-technical-diagrams/assets/interactive-diagram.html +371 -0
  124. package/skills/cc-technical-diagrams/assets/quality-examples/README.md +14 -0
  125. package/skills/cc-technical-diagrams/assets/quality-examples/architecture.json +121 -0
  126. package/skills/cc-technical-diagrams/assets/quality-examples/invalid-explicit.json +41 -0
  127. package/skills/cc-technical-diagrams/assets/quality-examples/sequence.json +53 -0
  128. package/skills/cc-technical-diagrams/assets/quality-examples/sequence.mmd +21 -0
  129. package/skills/cc-technical-diagrams/assets/quality-examples/workflow.json +98 -0
  130. package/skills/cc-technical-diagrams/assets/quality-examples/workflow.mmd +7 -0
  131. package/skills/cc-technical-diagrams/references/diagram-workflows.md +81 -0
  132. package/skills/cc-technical-diagrams/references/html-technical-diagrams.md +243 -0
  133. package/skills/cc-technical-diagrams/references/png-qa-autofix.md +37 -0
  134. package/skills/cc-technical-diagrams/scripts/diagram-browser.mjs +153 -0
  135. package/skills/cc-technical-diagrams/scripts/diagram-layout.mjs +176 -0
  136. package/skills/cc-technical-diagrams/scripts/export-diagram.mjs +332 -0
  137. package/skills/cc-technical-diagrams/scripts/interactive-diagram-server.mjs +654 -0
  138. package/skills/cc-technical-diagrams/scripts/mermaid-render.mjs +64 -0
  139. package/skills/cc-technical-diagrams/scripts/png-qa.mjs +462 -0
  140. package/skills/cc-technical-diagrams/scripts/tech-diagram-render.mjs +1537 -0
  141. package/skills/eval_queries.json +160 -0
  142. package/skills/metadata.json +223 -0
  143. package/skills/relations.json +53 -0
package/dist/index.js ADDED
@@ -0,0 +1,668 @@
1
+ // src/content/catalog.ts
2
+ import fs2 from "node:fs";
3
+ import path2 from "node:path";
4
+ import { createHash as createHash2 } from "node:crypto";
5
+
6
+ // src/content/resources.ts
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+ import { createHash } from "node:crypto";
10
+ function collectSkillResources(skillRoot) {
11
+ const root = path.resolve(skillRoot);
12
+ const files = [];
13
+ function checkReference(from, reference) {
14
+ if (/^(?:https?:|mailto:|data:|#)/i.test(reference)) return;
15
+ const clean = decodeURIComponent(reference.split(/[?#]/)[0]);
16
+ if (!clean) return;
17
+ const target = path.resolve(path.dirname(from), clean);
18
+ const relative = path.relative(root, target);
19
+ if (path.isAbsolute(clean) || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
20
+ throw new Error(`\u8D44\u6E90\u5F15\u7528\u8D8A\u8FC7\u6280\u80FD\u8FB9\u754C\uFF1A${reference}`);
21
+ if (!fs.existsSync(target))
22
+ throw new Error(`\u7F3A\u5C11\u5F15\u7528\u8D44\u6E90\uFF1A${reference}\uFF08${from}\uFF09`);
23
+ }
24
+ function walk(dir) {
25
+ if (fs.lstatSync(dir).isSymbolicLink())
26
+ throw new Error(`\u6280\u80FD\u8D44\u6E90\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${dir}`);
27
+ for (const name of fs.readdirSync(dir).sort()) {
28
+ const file = path.join(dir, name);
29
+ const stat = fs.lstatSync(file);
30
+ if (stat.isSymbolicLink())
31
+ throw new Error(`\u6280\u80FD\u8D44\u6E90\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${file}`);
32
+ if (stat.isDirectory()) {
33
+ walk(file);
34
+ continue;
35
+ }
36
+ if (!stat.isFile()) throw new Error(`\u4E0D\u652F\u6301\u7684\u8D44\u6E90\u7C7B\u578B\uFF1A${file}`);
37
+ const content = fs.readFileSync(file);
38
+ files.push({
39
+ relativePath: path.relative(root, file).split(path.sep).join("/"),
40
+ hash: createHash("sha256").update(content).digest("hex")
41
+ });
42
+ if (file.endsWith(".md"))
43
+ for (const match of content.toString("utf8").matchAll(/\]\(([^\s)]+)(?:\s+"[^"]*")?\)/g))
44
+ checkReference(file, match[1]);
45
+ if (/\.[cm]?[jt]s$/.test(file)) {
46
+ for (const match of content.toString("utf8").matchAll(/(?:\bfrom\s*|\bimport\s*\(?\s*)["'](\.[^"']+)["']/g))
47
+ checkReference(file, match[1]);
48
+ }
49
+ }
50
+ }
51
+ walk(root);
52
+ return files.sort(
53
+ (a, b) => a.relativePath < b.relativePath ? -1 : a.relativePath > b.relativePath ? 1 : 0
54
+ );
55
+ }
56
+
57
+ // src/content/catalog.ts
58
+ function readMethodResource(packageRoot, method, language) {
59
+ const allowed = [
60
+ "source-driven-development",
61
+ "doc-sync",
62
+ "debug",
63
+ "refactoring",
64
+ "tdd"
65
+ ];
66
+ if (!allowed.includes(method))
67
+ throw new Error(`\u672A\u77E5\u516C\u5171\u65B9\u6CD5\uFF1A${String(method)}`);
68
+ if (language !== "en" && language !== "zh-CN")
69
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u8BED\u8A00\uFF1A${String(language)}`);
70
+ const root = language === "en" ? packageRoot : path2.join(packageRoot, "localized", language);
71
+ const file = path2.join(root, "methods", `${method}.md`);
72
+ if (fs2.lstatSync(file).isSymbolicLink())
73
+ throw new Error(`\u65B9\u6CD5\u8D44\u6E90\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${file}`);
74
+ return fs2.readFileSync(file);
75
+ }
76
+ function hash(content) {
77
+ return createHash2("sha256").update(content).digest("hex");
78
+ }
79
+ function readContentCatalog(packageRoot, language) {
80
+ if (language !== "en" && language !== "zh-CN")
81
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u8BED\u8A00\uFF1A${String(language)}`);
82
+ const root = fs2.realpathSync(packageRoot);
83
+ const pkg = JSON.parse(
84
+ fs2.readFileSync(path2.join(root, "package.json"), "utf8")
85
+ );
86
+ if (!pkg || typeof pkg !== "object") throw new Error("\u516C\u5171\u5305\u5143\u6570\u636E\u65E0\u6548");
87
+ const { name, version, repository } = pkg;
88
+ const repositoryUrl = typeof repository === "string" ? repository : repository && typeof repository === "object" ? repository.url : void 0;
89
+ if (name !== "@bovinphang/craft-common" || typeof version !== "string" || !/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(version) || typeof repositoryUrl !== "string")
90
+ throw new Error("\u516C\u5171\u5305\u540D\u79F0\u3001\u7248\u672C\u6216 repository \u65E0\u6548");
91
+ const contentRoot = language === "en" ? root : path2.join(root, "localized", language);
92
+ const skillsDir = path2.join(contentRoot, "skills");
93
+ if (!fs2.existsSync(skillsDir))
94
+ throw new Error(`\u7F3A\u5C11 ${language} \u516C\u5171\u5185\u5BB9\uFF1A${skillsDir}`);
95
+ if (fs2.lstatSync(skillsDir).isSymbolicLink())
96
+ throw new Error(`\u516C\u5171\u6280\u80FD\u76EE\u5F55\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${skillsDir}`);
97
+ const skills = fs2.readdirSync(skillsDir).sort().flatMap((id) => {
98
+ const skillRoot = path2.join(skillsDir, id);
99
+ const stat = fs2.lstatSync(skillRoot);
100
+ if (stat.isSymbolicLink())
101
+ throw new Error(`\u516C\u5171\u6280\u80FD\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${skillRoot}`);
102
+ if (!stat.isDirectory()) return [];
103
+ if (!/^cc-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(id))
104
+ throw new Error(`\u516C\u5171\u6280\u80FD ID \u65E0\u6548\uFF1A${id}`);
105
+ const files = collectSkillResources(skillRoot);
106
+ if (!files.some((file) => file.relativePath === "SKILL.md"))
107
+ throw new Error(`\u7F3A\u5C11\u516C\u5171\u6280\u80FD\u5165\u53E3\uFF1A${id}/SKILL.md`);
108
+ return [
109
+ { id, version, sourcePackage: name, repository: repositoryUrl, files }
110
+ ];
111
+ });
112
+ if (!skills.length) throw new Error(`\u6CA1\u6709 ${language} \u516C\u5171\u6280\u80FD`);
113
+ const content = {
114
+ schemaVersion: 1,
115
+ packageName: name,
116
+ packageVersion: version,
117
+ language,
118
+ skills
119
+ };
120
+ return {
121
+ ...content,
122
+ root: contentRoot,
123
+ digest: hash(JSON.stringify(content))
124
+ };
125
+ }
126
+
127
+ // src/install/identity.ts
128
+ import fs3 from "node:fs";
129
+ import path3 from "node:path";
130
+ import { createHash as createHash3 } from "node:crypto";
131
+ function canonicalPath(input) {
132
+ let current = path3.resolve(input);
133
+ const missing = [];
134
+ while (!fs3.existsSync(current)) {
135
+ const parent = path3.dirname(current);
136
+ if (parent === current) throw new Error(`\u65E0\u6CD5\u89E3\u6790\u771F\u5B9E\u8DEF\u5F84\uFF1A${input}`);
137
+ missing.unshift(path3.basename(current));
138
+ current = parent;
139
+ }
140
+ return path3.join(fs3.realpathSync.native(current), ...missing);
141
+ }
142
+ function resolveSharedIdentity(skillsDir) {
143
+ const target = canonicalPath(skillsDir);
144
+ const store = path3.join(path3.dirname(target), "craft-manifests");
145
+ if (fs3.existsSync(store) && fs3.lstatSync(store).isSymbolicLink())
146
+ throw new Error("\u516C\u5171\u8D26\u672C\u76EE\u5F55\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5");
147
+ const key = createHash3("sha256").update(process.platform === "win32" ? target.toLowerCase() : target).digest("hex");
148
+ const ledgerDir = path3.join(store, key);
149
+ if (fs3.existsSync(ledgerDir) && fs3.lstatSync(ledgerDir).isSymbolicLink())
150
+ throw new Error("\u516C\u5171\u8D26\u672C\u76EE\u5F55\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5");
151
+ return { target, ledgerDir };
152
+ }
153
+
154
+ // src/install/planner.ts
155
+ import fs4 from "node:fs";
156
+ import path4 from "node:path";
157
+ import { createHash as createHash4 } from "node:crypto";
158
+ var hashBytes = (bytes) => createHash4("sha256").update(bytes).digest("hex");
159
+ function consumerIdentity(consumer) {
160
+ if (!consumer.product || !["local", "global"].includes(consumer.scope) || !path4.isAbsolute(consumer.manifestPath))
161
+ throw new Error("\u516C\u5171\u4F9D\u8D56\u6D88\u8D39\u8005\u8EAB\u4EFD\u65E0\u6548");
162
+ const manifest = canonicalPath(consumer.manifestPath);
163
+ return hashBytes(
164
+ JSON.stringify([
165
+ consumer.product,
166
+ process.platform === "win32" ? manifest.toLowerCase() : manifest,
167
+ consumer.scope
168
+ ])
169
+ );
170
+ }
171
+ function safeManagedPath(root, relative) {
172
+ if (!relative || relative.includes("\\") || path4.isAbsolute(relative) || relative.split("/").some((part) => !part || part === "." || part === ".."))
173
+ throw new Error(`\u516C\u5171\u8D44\u6E90\u8DEF\u5F84\u8D8A\u754C\uFF1A${relative}`);
174
+ const target = path4.resolve(root, relative);
175
+ const remainder = path4.relative(path4.resolve(root), target);
176
+ if (!remainder || remainder.startsWith(`..${path4.sep}`) || remainder === ".." || path4.isAbsolute(remainder))
177
+ throw new Error(`\u516C\u5171\u8D44\u6E90\u8D8A\u8FC7\u8FB9\u754C\uFF1A${relative}`);
178
+ let current = target;
179
+ const boundary = path4.resolve(root);
180
+ while (true) {
181
+ if (fs4.existsSync(current) && fs4.lstatSync(current).isSymbolicLink())
182
+ throw new Error(`\u6258\u7BA1\u8DEF\u5F84\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${current}`);
183
+ if (current === boundary) break;
184
+ current = path4.dirname(current);
185
+ }
186
+ return target;
187
+ }
188
+ function currentHash(file) {
189
+ try {
190
+ const stat = fs4.lstatSync(file);
191
+ if (!stat.isFile() || stat.isSymbolicLink())
192
+ throw new Error(`\u6258\u7BA1\u8DEF\u5F84\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${file}`);
193
+ return hashBytes(fs4.readFileSync(file));
194
+ } catch (error) {
195
+ if (error.code === "ENOENT") return null;
196
+ throw error;
197
+ }
198
+ }
199
+ function readJson(file) {
200
+ const hash2 = currentHash(file);
201
+ if (hash2 === null) return void 0;
202
+ try {
203
+ return JSON.parse(fs4.readFileSync(file, "utf8"));
204
+ } catch {
205
+ throw new Error(`\u516C\u5171\u8D26\u672C JSON \u635F\u574F\uFF1A${file}`);
206
+ }
207
+ }
208
+ var object = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
209
+ function json(value) {
210
+ return Buffer.from(`${JSON.stringify(value, null, 2)}
211
+ `);
212
+ }
213
+ function planSharedDeployment(request) {
214
+ if (!["install", "update", "uninstall"].includes(request.action))
215
+ throw new Error("\u672A\u77E5\u516C\u5171\u5B89\u88C5\u64CD\u4F5C");
216
+ const { target, ledgerDir } = resolveSharedIdentity(request.skillsDir);
217
+ const id = consumerIdentity(request.consumer);
218
+ const catalog = request.catalog;
219
+ if (request.action !== "uninstall" && !catalog)
220
+ throw new Error("\u516C\u5171\u5B89\u88C5\u7F3A\u5C11\u5185\u5BB9\u76EE\u5F55");
221
+ const desired = /* @__PURE__ */ new Map();
222
+ if (catalog && request.action !== "uninstall") {
223
+ if (catalog.schemaVersion !== 1 || catalog.packageName !== "@bovinphang/craft-common" || !["en", "zh-CN"].includes(catalog.language))
224
+ throw new Error("\u516C\u5171\u5185\u5BB9\u76EE\u5F55\u65E0\u6548");
225
+ for (const skill of catalog.skills) {
226
+ if (!/^cc-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(skill.id))
227
+ throw new Error(`\u516C\u5171\u6280\u80FD\u8DEF\u5F84\u65E0\u6548\uFF1A${skill.id}`);
228
+ for (const file of skill.files) {
229
+ const relative = `${skill.id}/${file.relativePath}`;
230
+ safeManagedPath(target, relative);
231
+ const source = safeManagedPath(catalog.root, `skills/${relative}`);
232
+ const bytes = fs4.readFileSync(source);
233
+ if (hashBytes(bytes) !== file.hash)
234
+ throw new Error(`\u516C\u5171\u5185\u5BB9\u6765\u6E90\u6458\u8981\u4E0D\u5339\u914D\uFF1A${relative}`);
235
+ if (desired.has(relative))
236
+ throw new Error(`\u91CD\u590D\u516C\u5171\u8D44\u6E90\u8DEF\u5F84\uFF1A${relative}`);
237
+ desired.set(relative, bytes);
238
+ }
239
+ }
240
+ }
241
+ const manifestPath = safeManagedPath(ledgerDir, "craft-common.json");
242
+ const lockPath = safeManagedPath(ledgerDir, "dependencies.lock.json");
243
+ const rawManifest = readJson(manifestPath), rawLock = readJson(lockPath);
244
+ if (Boolean(rawManifest) !== Boolean(rawLock))
245
+ throw new Error("\u516C\u5171\u8D26\u672C\u4E0D\u5B8C\u6574\uFF0C\u4FDD\u7559\u8D44\u6E90\u7B49\u5F85\u4FEE\u590D");
246
+ let manifest;
247
+ let lock = { schemaVersion: 1, target, consumers: {} };
248
+ if (rawManifest !== void 0) {
249
+ if (!object(rawManifest) || rawManifest.schemaVersion !== 1 || rawManifest.target !== target || typeof rawManifest.packageVersion !== "string" || !["en", "zh-CN"].includes(String(rawManifest.language)) || typeof rawManifest.sourceDigest !== "string" || !object(rawManifest.files) || typeof rawManifest.residual !== "boolean" || !object(rawLock) || rawLock.schemaVersion !== 1 || rawLock.target !== target || !object(rawLock.consumers))
250
+ throw new Error("\u516C\u5171\u8D26\u672C\u7ED3\u6784\u6216\u76EE\u6807\u8EAB\u4EFD\u65E0\u6548");
251
+ for (const [relative, hash2] of Object.entries(rawManifest.files)) {
252
+ safeManagedPath(target, relative);
253
+ if (!relative.startsWith("cc-") || typeof hash2 !== "string" || !/^[a-f0-9]{64}$/.test(hash2))
254
+ throw new Error("\u516C\u5171\u8D26\u672C\u6587\u4EF6\u6240\u6709\u6743\u65E0\u6548");
255
+ }
256
+ for (const [key, entry] of Object.entries(rawLock.consumers)) {
257
+ if (!object(entry) || !object(entry.consumer) || typeof entry.requiredVersion !== "string" || typeof entry.digest !== "string" || !["en", "zh-CN"].includes(String(entry.language)))
258
+ throw new Error("\u516C\u5171\u6D88\u8D39\u8005\u8D26\u672C\u65E0\u6548");
259
+ const consumer = entry.consumer;
260
+ if (consumerIdentity(consumer) !== key)
261
+ throw new Error("\u516C\u5171\u6D88\u8D39\u8005\u6807\u8BC6\u4E0D\u5339\u914D");
262
+ if (entry.requiredVersion !== rawManifest.packageVersion || entry.language !== rawManifest.language || entry.digest !== rawManifest.sourceDigest)
263
+ throw new Error("\u516C\u5171\u4F9D\u8D56\u58F0\u660E\u4E0E\u5185\u5BB9\u8D26\u672C\u4E0D\u4E00\u81F4");
264
+ }
265
+ manifest = rawManifest;
266
+ lock = rawLock;
267
+ }
268
+ const plan = {
269
+ schemaVersion: 1,
270
+ target,
271
+ stateDigest: hashBytes(
272
+ JSON.stringify([rawManifest ?? null, rawLock ?? null])
273
+ ),
274
+ mutations: [],
275
+ conflicts: []
276
+ };
277
+ if (catalog && request.action !== "uninstall") {
278
+ const { root: _root, digest, ...content } = catalog;
279
+ if (hashBytes(JSON.stringify(content)) !== digest) {
280
+ plan.conflicts.push("\u516C\u5171\u5185\u5BB9\u76EE\u5F55\u6458\u8981\u65E0\u6548\uFF0C\u62D2\u7EDD\u8986\u76D6\u76F8\u540C\u7248\u672C");
281
+ return plan;
282
+ }
283
+ }
284
+ const mutate = (file, after) => {
285
+ const beforeHash = currentHash(file);
286
+ if (after === null ? beforeHash !== null : beforeHash !== hashBytes(after))
287
+ plan.mutations.push({
288
+ path: file,
289
+ beforeHash,
290
+ after
291
+ });
292
+ };
293
+ if (request.action === "uninstall") {
294
+ if (!manifest || !lock.consumers[id]) return plan;
295
+ if (request.retainConsumer) return plan;
296
+ delete lock.consumers[id];
297
+ if (Object.keys(lock.consumers).length) {
298
+ mutate(lockPath, json(lock));
299
+ return plan;
300
+ }
301
+ const residual = {};
302
+ for (const [relative, ownedHash] of Object.entries(manifest.files)) {
303
+ const dest = safeManagedPath(target, relative);
304
+ const actualHash = currentHash(dest);
305
+ if (actualHash === null) continue;
306
+ if (actualHash === ownedHash) mutate(dest, null);
307
+ else residual[relative] = ownedHash;
308
+ }
309
+ if (Object.keys(residual).length) {
310
+ mutate(
311
+ manifestPath,
312
+ json({ ...manifest, files: residual, residual: true })
313
+ );
314
+ mutate(lockPath, json(lock));
315
+ } else {
316
+ mutate(manifestPath, null);
317
+ mutate(lockPath, null);
318
+ }
319
+ return plan;
320
+ }
321
+ if (!catalog) throw new Error("\u516C\u5171\u5B89\u88C5\u7F3A\u5C11\u6765\u6E90");
322
+ const others = Object.entries(lock.consumers).filter(([key]) => key !== id);
323
+ if (manifest && (manifest.packageVersion !== catalog.packageVersion || manifest.language !== catalog.language || manifest.sourceDigest !== catalog.digest) && (others.length || request.action !== "update"))
324
+ plan.conflicts.push("\u516C\u5171\u7248\u672C\u3001\u8BED\u8A00\u6216\u6E90\u6458\u8981\u51B2\u7A81\uFF0C\u8BF7\u534F\u8C03\u6D88\u8D39\u8005\u540E\u66F4\u65B0");
325
+ for (const [relative, ownedHash] of Object.entries(manifest?.files ?? {})) {
326
+ const actualHash = currentHash(safeManagedPath(target, relative));
327
+ if (actualHash !== null && actualHash !== ownedHash)
328
+ plan.conflicts.push(`\u516C\u5171\u6258\u7BA1\u6587\u4EF6\u5DF2\u4FEE\u6539\uFF1A${relative}`);
329
+ }
330
+ for (const [relative] of desired) {
331
+ if (currentHash(safeManagedPath(target, relative)) !== null && !manifest?.files[relative])
332
+ plan.conflicts.push(`\u672A\u77E5\u6765\u6E90\u540C\u540D\u516C\u5171\u6587\u4EF6\uFF1A${relative}`);
333
+ }
334
+ if (plan.conflicts.length) return plan;
335
+ for (const [relative, bytes] of desired)
336
+ mutate(safeManagedPath(target, relative), bytes);
337
+ for (const relative of Object.keys(manifest?.files ?? {}))
338
+ if (!desired.has(relative)) mutate(safeManagedPath(target, relative), null);
339
+ lock.consumers[id] = {
340
+ consumer: {
341
+ ...request.consumer,
342
+ manifestPath: canonicalPath(request.consumer.manifestPath)
343
+ },
344
+ requiredVersion: catalog.packageVersion,
345
+ language: catalog.language,
346
+ digest: catalog.digest
347
+ };
348
+ const updated = {
349
+ schemaVersion: 1,
350
+ target,
351
+ packageVersion: catalog.packageVersion,
352
+ language: catalog.language,
353
+ sourceDigest: catalog.digest,
354
+ files: Object.fromEntries(
355
+ [...desired].map(([relative, bytes]) => [relative, hashBytes(bytes)])
356
+ ),
357
+ residual: false
358
+ };
359
+ mutate(manifestPath, json(updated));
360
+ mutate(lockPath, json(lock));
361
+ return plan;
362
+ }
363
+
364
+ // src/install/transaction.ts
365
+ import fs5 from "node:fs";
366
+ import path5 from "node:path";
367
+ import os from "node:os";
368
+ import { randomUUID } from "node:crypto";
369
+ var journalName = "transaction.json";
370
+ function atomicWrite(file, bytes, token) {
371
+ fs5.mkdirSync(path5.dirname(file), { recursive: true });
372
+ const temp = `${file}.${token}.tmp`;
373
+ try {
374
+ const mode = fs5.existsSync(file) ? fs5.statSync(file).mode & 511 : 384;
375
+ fs5.writeFileSync(temp, bytes, { flag: "wx", mode });
376
+ fs5.renameSync(temp, file);
377
+ } catch (error) {
378
+ try {
379
+ fs5.unlinkSync(temp);
380
+ } catch (cleanupError) {
381
+ if (cleanupError.code !== "ENOENT")
382
+ throw new AggregateError(
383
+ [error, cleanupError],
384
+ "\u539F\u5B50\u5199\u5165\u53CA\u4E34\u65F6\u6587\u4EF6\u6E05\u7406\u5931\u8D25"
385
+ );
386
+ }
387
+ throw error;
388
+ }
389
+ }
390
+ function release(lockFile, lease) {
391
+ if (!fs5.existsSync(lockFile)) return;
392
+ const existing = JSON.parse(fs5.readFileSync(lockFile, "utf8"));
393
+ if (existing.token !== lease.token)
394
+ throw new Error("\u516C\u5171\u5B89\u88C5\u9501\u5F52\u5C5E\u6539\u53D8\uFF0C\u4FDD\u7559\u73B0\u573A");
395
+ fs5.unlinkSync(lockFile);
396
+ }
397
+ function acquire(ledgerDir) {
398
+ const lockFile = safeManagedPath(ledgerDir, "install.lock");
399
+ fs5.mkdirSync(ledgerDir, { recursive: true, mode: 448 });
400
+ const lease = {
401
+ pid: process.pid,
402
+ host: os.hostname(),
403
+ token: randomUUID()
404
+ };
405
+ for (let attempt = 0; attempt < 2; attempt++) {
406
+ try {
407
+ fs5.writeFileSync(lockFile, JSON.stringify(lease), {
408
+ flag: "wx",
409
+ mode: 384
410
+ });
411
+ return { lockFile, lease };
412
+ } catch (error) {
413
+ if (error.code !== "EEXIST") throw error;
414
+ let previous;
415
+ try {
416
+ previous = JSON.parse(fs5.readFileSync(lockFile, "utf8"));
417
+ } catch {
418
+ throw new Error("\u65E0\u6CD5\u786E\u8BA4\u516C\u5171\u5B89\u88C5\u9501\u8EAB\u4EFD\uFF0C\u8BF7\u68C0\u67E5\u4FDD\u7559\u73B0\u573A");
419
+ }
420
+ if (previous.host !== os.hostname() || !Number.isSafeInteger(previous.pid) || previous.pid <= 0 || typeof previous.token !== "string")
421
+ throw new Error("\u65E0\u6CD5\u786E\u8BA4\u516C\u5171\u5B89\u88C5\u9501\u6240\u5C5E\u8FDB\u7A0B");
422
+ try {
423
+ process.kill(previous.pid, 0);
424
+ } catch (probe) {
425
+ if (probe.code !== "ESRCH")
426
+ throw new Error("\u65E0\u6CD5\u786E\u8BA4\u516C\u5171\u5B89\u88C5\u9501\u662F\u5426\u5931\u6548");
427
+ const reclaimFile = safeManagedPath(ledgerDir, "reclaim.lock");
428
+ try {
429
+ fs5.writeFileSync(reclaimFile, JSON.stringify(lease), {
430
+ flag: "wx",
431
+ mode: 384
432
+ });
433
+ } catch {
434
+ throw new Error("\u516C\u5171\u5B89\u88C5\u9501\u6B63\u5728\u6062\u590D\u6216\u6062\u590D\u9501\u9700\u4EBA\u5DE5\u68C0\u67E5");
435
+ }
436
+ try {
437
+ if (fs5.readFileSync(lockFile, "utf8") !== JSON.stringify(previous))
438
+ throw new Error("\u516C\u5171\u5B89\u88C5\u9501\u53D1\u751F\u5E76\u53D1\u53D8\u5316");
439
+ fs5.unlinkSync(lockFile);
440
+ } finally {
441
+ fs5.unlinkSync(reclaimFile);
442
+ }
443
+ continue;
444
+ }
445
+ throw new Error(`\u516C\u5171\u5B89\u88C5\u6B63\u5728\u6267\u884C\uFF0C\u9501\u8FDB\u7A0B ${previous.pid}`);
446
+ }
447
+ }
448
+ throw new Error("\u65E0\u6CD5\u53D6\u5F97\u516C\u5171\u5B89\u88C5\u9501");
449
+ }
450
+ function validateDestination(file, target, ledgerDir, productRoots) {
451
+ const absolute = path5.resolve(file);
452
+ const sharedRelative = path5.relative(target, absolute).split(path5.sep).join("/");
453
+ if (sharedRelative.startsWith("cc-") && !sharedRelative.startsWith("../")) {
454
+ safeManagedPath(target, sharedRelative);
455
+ return;
456
+ }
457
+ if (["craft-common.json", "dependencies.lock.json"].some(
458
+ (name) => absolute === path5.join(ledgerDir, name)
459
+ )) {
460
+ safeManagedPath(ledgerDir, path5.basename(absolute));
461
+ return;
462
+ }
463
+ for (const root of productRoots) {
464
+ const relative = path5.relative(root, absolute).split(path5.sep).join("/");
465
+ if (relative && relative !== ".." && !relative.startsWith("../") && !path5.isAbsolute(relative)) {
466
+ safeManagedPath(root, relative);
467
+ return;
468
+ }
469
+ }
470
+ throw new Error(`\u4E8B\u52A1\u5199\u5165\u8D8A\u8FC7\u5141\u8BB8\u8FB9\u754C\uFF1A${file}`);
471
+ }
472
+ function validateJournal(value, target, ledgerDir, productRoots) {
473
+ const journal = value;
474
+ if (!journal || journal.schemaVersion !== 1 || journal.target !== target || !["prepared", "committed"].includes(journal.phase) || !/^[a-f0-9-]{36}$/.test(journal.token) || !Array.isArray(journal.entries))
475
+ throw new Error("\u516C\u5171\u4E8B\u52A1\u8BB0\u5F55\u65E0\u6548\uFF0C\u4FDD\u7559\u73B0\u573A");
476
+ const paths = /* @__PURE__ */ new Set();
477
+ for (const entry of journal.entries) {
478
+ if (!entry || typeof entry.path !== "string" || !path5.isAbsolute(entry.path) || paths.has(entry.path))
479
+ throw new Error("\u516C\u5171\u4E8B\u52A1\u8DEF\u5F84\u65E0\u6548");
480
+ paths.add(entry.path);
481
+ validateDestination(entry.path, target, ledgerDir, productRoots);
482
+ for (const side of ["before", "after"]) {
483
+ const bytes = entry[side], hash2 = entry[`${side}Hash`];
484
+ if (bytes === null ? hash2 !== null : typeof bytes !== "string" || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(
485
+ bytes
486
+ ) || hashBytes(Buffer.from(bytes, "base64")) !== hash2)
487
+ throw new Error("\u516C\u5171\u4E8B\u52A1\u5185\u5BB9\u6458\u8981\u65E0\u6548");
488
+ }
489
+ }
490
+ return journal;
491
+ }
492
+ function restore(journal, token) {
493
+ for (const entry of [...journal.entries].reverse()) {
494
+ const actual = currentHash(entry.path);
495
+ if (actual !== entry.beforeHash && actual !== entry.afterHash)
496
+ throw new Error(`\u4E8B\u52A1\u671F\u95F4\u6587\u4EF6\u88AB\u4FEE\u6539\uFF0C\u4FDD\u7559\u6062\u590D\u8BB0\u5F55\uFF1A${entry.path}`);
497
+ if (actual === entry.beforeHash) continue;
498
+ if (entry.before === null) fs5.unlinkSync(entry.path);
499
+ else atomicWrite(entry.path, Buffer.from(entry.before, "base64"), token);
500
+ }
501
+ }
502
+ function recoverUnderLease(target, ledgerDir, token, productRoots) {
503
+ const file = safeManagedPath(ledgerDir, journalName);
504
+ if (!fs5.existsSync(file)) return "clean";
505
+ const journal = validateJournal(
506
+ JSON.parse(fs5.readFileSync(file, "utf8")),
507
+ target,
508
+ ledgerDir,
509
+ productRoots
510
+ );
511
+ if (journal.phase === "prepared") restore(journal, token);
512
+ else
513
+ for (const entry of journal.entries)
514
+ if (currentHash(entry.path) !== entry.afterHash)
515
+ throw new Error(`\u5DF2\u63D0\u4EA4\u4E8B\u52A1\u5B58\u5728\u5916\u90E8\u4FEE\u6539\uFF0C\u4FDD\u7559\u6062\u590D\u8BB0\u5F55\uFF1A${entry.path}`);
516
+ fs5.unlinkSync(file);
517
+ return "recovered";
518
+ }
519
+ async function recoverSharedTransaction(skillsDir, allowedProductRoots = []) {
520
+ const { target, ledgerDir } = resolveSharedIdentity(skillsDir);
521
+ if (!fs5.existsSync(ledgerDir)) return "clean";
522
+ const { lockFile, lease } = acquire(ledgerDir);
523
+ try {
524
+ return recoverUnderLease(
525
+ target,
526
+ ledgerDir,
527
+ lease.token,
528
+ allowedProductRoots.map(canonicalPath)
529
+ );
530
+ } finally {
531
+ release(lockFile, lease);
532
+ }
533
+ }
534
+ function normalizeProductPath(file, roots) {
535
+ const absolute = path5.resolve(file);
536
+ const candidates = roots.flatMap((root) => {
537
+ const real = canonicalPath(root);
538
+ return [
539
+ { boundary: path5.resolve(root), real },
540
+ { boundary: real, real }
541
+ ];
542
+ }).sort((a, b) => b.boundary.length - a.boundary.length);
543
+ for (const { boundary, real } of candidates) {
544
+ const relative = path5.relative(boundary, absolute);
545
+ if (!relative || relative === ".." || relative.startsWith(`..${path5.sep}`) || path5.isAbsolute(relative))
546
+ continue;
547
+ let current = absolute;
548
+ while (current !== boundary) {
549
+ try {
550
+ if (fs5.lstatSync(current).isSymbolicLink())
551
+ throw new Error(`\u4EA7\u54C1\u6258\u7BA1\u8DEF\u5F84\u4E0D\u5141\u8BB8\u7B26\u53F7\u94FE\u63A5\uFF1A${current}`);
552
+ } catch (error) {
553
+ if (error.code !== "ENOENT") throw error;
554
+ }
555
+ current = path5.dirname(current);
556
+ }
557
+ return safeManagedPath(real, relative.split(path5.sep).join("/"));
558
+ }
559
+ throw new Error(`\u4EA7\u54C1\u5199\u5165\u8D8A\u8FC7\u5141\u8BB8\u8FB9\u754C\uFF1A${file}`);
560
+ }
561
+ async function executeSharedPlan(request, options) {
562
+ const { target, ledgerDir } = resolveSharedIdentity(request.skillsDir);
563
+ const roots = (options.allowedProductRoots ?? []).map(canonicalPath);
564
+ const products = (options.productMutations ?? []).map((mutation) => ({
565
+ ...mutation,
566
+ path: normalizeProductPath(
567
+ mutation.path,
568
+ options.allowedProductRoots ?? []
569
+ )
570
+ }));
571
+ for (const mutation of products) {
572
+ const file = mutation.path;
573
+ const shared = path5.relative(target, file).split(path5.sep).join("/");
574
+ const ledger = path5.relative(ledgerDir, file).split(path5.sep).join("/");
575
+ if (shared.toLowerCase().startsWith("cc-") && !shared.startsWith("../") || !ledger || ledger !== ".." && !ledger.startsWith("../") && !path5.isAbsolute(ledger))
576
+ throw new Error(`\u4EA7\u54C1\u4E0D\u80FD\u8BA4\u9886\u516C\u5171\u8D44\u6E90\u6216\u8D26\u672C\uFF1A${file}`);
577
+ validateDestination(file, target, ledgerDir, roots);
578
+ }
579
+ if (options.dryRun) {
580
+ if (fs5.existsSync(path5.join(ledgerDir, journalName)))
581
+ throw new Error("\u5B58\u5728\u5F85\u6062\u590D\u516C\u5171\u4E8B\u52A1\uFF0Cdry-run \u4E0D\u6267\u884C\u6062\u590D");
582
+ const plan = planSharedDeployment(request);
583
+ if (plan.conflicts.length) throw new Error(plan.conflicts.join("\n"));
584
+ for (const entry of products) {
585
+ validateDestination(entry.path, target, ledgerDir, roots);
586
+ if (currentHash(entry.path) !== entry.beforeHash)
587
+ throw new Error(`\u4EA7\u54C1\u6587\u4EF6\u5728\u89C4\u5212\u540E\u5DF2\u4FEE\u6539\uFF1A${entry.path}`);
588
+ }
589
+ return plan;
590
+ }
591
+ if (!fs5.existsSync(path5.join(ledgerDir, journalName))) {
592
+ const initial = planSharedDeployment(request);
593
+ if (initial.conflicts.length) throw new Error(initial.conflicts.join("\n"));
594
+ }
595
+ const { lockFile, lease } = acquire(ledgerDir);
596
+ const journalFile = safeManagedPath(ledgerDir, journalName);
597
+ let journal;
598
+ try {
599
+ recoverUnderLease(target, ledgerDir, lease.token, roots);
600
+ const plan = planSharedDeployment(request);
601
+ if (plan.conflicts.length) throw new Error(plan.conflicts.join("\n"));
602
+ const mutations = [...plan.mutations, ...products];
603
+ const paths = /* @__PURE__ */ new Set();
604
+ const entries = mutations.map((mutation) => {
605
+ const file = path5.resolve(mutation.path);
606
+ validateDestination(file, target, ledgerDir, roots);
607
+ if (paths.has(file)) throw new Error(`\u516C\u5171\u4E0E\u4EA7\u54C1\u5199\u5165\u91CD\u590D\u8BA4\u9886\uFF1A${file}`);
608
+ paths.add(file);
609
+ if (currentHash(file) !== mutation.beforeHash)
610
+ throw new Error(`\u4EA7\u54C1\u6216\u516C\u5171\u6587\u4EF6\u5728\u89C4\u5212\u540E\u5DF2\u4FEE\u6539\uFF1A${file}`);
611
+ return {
612
+ path: file,
613
+ before: mutation.beforeHash === null ? null : fs5.readFileSync(file).toString("base64"),
614
+ after: mutation.after?.toString("base64") ?? null,
615
+ beforeHash: mutation.beforeHash,
616
+ afterHash: mutation.after === null ? null : hashBytes(mutation.after)
617
+ };
618
+ });
619
+ if (!entries.length) return plan;
620
+ journal = {
621
+ schemaVersion: 1,
622
+ target,
623
+ phase: "prepared",
624
+ token: lease.token,
625
+ entries
626
+ };
627
+ atomicWrite(journalFile, Buffer.from(JSON.stringify(journal)), lease.token);
628
+ for (const entry of entries) {
629
+ if (currentHash(entry.path) !== entry.beforeHash)
630
+ throw new Error(`\u4E8B\u52A1\u6267\u884C\u524D\u6587\u4EF6\u5DF2\u4FEE\u6539\uFF1A${entry.path}`);
631
+ if (entry.after === null) fs5.unlinkSync(entry.path);
632
+ else
633
+ atomicWrite(
634
+ entry.path,
635
+ Buffer.from(entry.after, "base64"),
636
+ lease.token
637
+ );
638
+ }
639
+ journal.phase = "committed";
640
+ atomicWrite(journalFile, Buffer.from(JSON.stringify(journal)), lease.token);
641
+ fs5.unlinkSync(journalFile);
642
+ return plan;
643
+ } catch (error) {
644
+ if (journal?.phase === "prepared") {
645
+ try {
646
+ restore(journal, lease.token);
647
+ fs5.unlinkSync(journalFile);
648
+ } catch (rollbackError) {
649
+ throw new AggregateError(
650
+ [error, rollbackError],
651
+ "\u516C\u5171\u5B89\u88C5\u5931\u8D25\u4E14\u9700\u4FDD\u7559\u4E8B\u52A1\u8BB0\u5F55\u7EE7\u7EED\u6062\u590D"
652
+ );
653
+ }
654
+ }
655
+ throw error;
656
+ } finally {
657
+ release(lockFile, lease);
658
+ }
659
+ }
660
+ export {
661
+ collectSkillResources,
662
+ executeSharedPlan,
663
+ planSharedDeployment,
664
+ readContentCatalog,
665
+ readMethodResource,
666
+ recoverSharedTransaction,
667
+ resolveSharedIdentity
668
+ };
@@ -0,0 +1,5 @@
1
+ export declare function canonicalPath(input: string): string;
2
+ export declare function resolveSharedIdentity(skillsDir: string): {
3
+ target: string;
4
+ ledgerDir: string;
5
+ };
@@ -0,0 +1,6 @@
1
+ import type { CommonPlan, Consumer, SharedRequest } from "./types.js";
2
+ export declare const hashBytes: (bytes: string | Buffer) => string;
3
+ export declare function consumerIdentity(consumer: Consumer): string;
4
+ export declare function safeManagedPath(root: string, relative: string): string;
5
+ export declare function currentHash(file: string): string | null;
6
+ export declare function planSharedDeployment(request: SharedRequest): CommonPlan;
@@ -0,0 +1,7 @@
1
+ import type { CommonPlan, FileMutation, SharedRequest } from "./types.js";
2
+ export declare function recoverSharedTransaction(skillsDir: string, allowedProductRoots?: string[]): Promise<"clean" | "recovered">;
3
+ export declare function executeSharedPlan(request: SharedRequest, options: {
4
+ dryRun: boolean;
5
+ productMutations?: FileMutation[];
6
+ allowedProductRoots?: string[];
7
+ }): Promise<CommonPlan>;