agent-plugkit 0.1.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 (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/dist/adapters/agent-plugins.d.ts +35 -0
  4. package/dist/adapters/agent-plugins.js +57 -0
  5. package/dist/adapters/agent-plugins.js.map +1 -0
  6. package/dist/adapters/claude.d.ts +64 -0
  7. package/dist/adapters/claude.js +123 -0
  8. package/dist/adapters/claude.js.map +1 -0
  9. package/dist/adapters/cli-validation.d.ts +12 -0
  10. package/dist/adapters/cli-validation.js +31 -0
  11. package/dist/adapters/cli-validation.js.map +1 -0
  12. package/dist/adapters/codex.d.ts +62 -0
  13. package/dist/adapters/codex.js +91 -0
  14. package/dist/adapters/codex.js.map +1 -0
  15. package/dist/adapters/marketplace-registration.d.ts +17 -0
  16. package/dist/adapters/marketplace-registration.js +369 -0
  17. package/dist/adapters/marketplace-registration.js.map +1 -0
  18. package/dist/adapters/marketplace.d.ts +11 -0
  19. package/dist/adapters/marketplace.js +96 -0
  20. package/dist/adapters/marketplace.js.map +1 -0
  21. package/dist/adapters/mcp.d.ts +17 -0
  22. package/dist/adapters/mcp.js +28 -0
  23. package/dist/adapters/mcp.js.map +1 -0
  24. package/dist/application/artifact-generation.d.ts +58 -0
  25. package/dist/application/artifact-generation.js +344 -0
  26. package/dist/application/artifact-generation.js.map +1 -0
  27. package/dist/application/local-release.d.ts +122 -0
  28. package/dist/application/local-release.js +940 -0
  29. package/dist/application/local-release.js.map +1 -0
  30. package/dist/application/marketplace-document-contract.d.ts +81 -0
  31. package/dist/application/marketplace-document-contract.js +2 -0
  32. package/dist/application/marketplace-document-contract.js.map +1 -0
  33. package/dist/application/marketplace-document.d.ts +45 -0
  34. package/dist/application/marketplace-document.js +557 -0
  35. package/dist/application/marketplace-document.js.map +1 -0
  36. package/dist/application/marketplace-registration-contract.d.ts +51 -0
  37. package/dist/application/marketplace-registration-contract.js +11 -0
  38. package/dist/application/marketplace-registration-contract.js.map +1 -0
  39. package/dist/application/marketplace-registration.d.ts +20 -0
  40. package/dist/application/marketplace-registration.js +304 -0
  41. package/dist/application/marketplace-registration.js.map +1 -0
  42. package/dist/application/marketplace.d.ts +9 -0
  43. package/dist/application/marketplace.js +32 -0
  44. package/dist/application/marketplace.js.map +1 -0
  45. package/dist/application/plugin-components-contract.d.ts +135 -0
  46. package/dist/application/plugin-components-contract.js +2 -0
  47. package/dist/application/plugin-components-contract.js.map +1 -0
  48. package/dist/application/plugin-components.d.ts +17 -0
  49. package/dist/application/plugin-components.js +1000 -0
  50. package/dist/application/plugin-components.js.map +1 -0
  51. package/dist/application/plugin-document-contract.d.ts +115 -0
  52. package/dist/application/plugin-document-contract.js +2 -0
  53. package/dist/application/plugin-document-contract.js.map +1 -0
  54. package/dist/application/plugin-document.d.ts +69 -0
  55. package/dist/application/plugin-document.js +1037 -0
  56. package/dist/application/plugin-document.js.map +1 -0
  57. package/dist/application/plugin-lifecycle-contract.d.ts +230 -0
  58. package/dist/application/plugin-lifecycle-contract.js +3 -0
  59. package/dist/application/plugin-lifecycle-contract.js.map +1 -0
  60. package/dist/application/plugin-lifecycle.d.ts +8 -0
  61. package/dist/application/plugin-lifecycle.js +1375 -0
  62. package/dist/application/plugin-lifecycle.js.map +1 -0
  63. package/dist/application/plugin-scaffold.d.ts +18 -0
  64. package/dist/application/plugin-scaffold.js +188 -0
  65. package/dist/application/plugin-scaffold.js.map +1 -0
  66. package/dist/application/plugin-source.d.ts +39 -0
  67. package/dist/application/plugin-source.js +59 -0
  68. package/dist/application/plugin-source.js.map +1 -0
  69. package/dist/application/skill-document-contract.d.ts +92 -0
  70. package/dist/application/skill-document-contract.js +8 -0
  71. package/dist/application/skill-document-contract.js.map +1 -0
  72. package/dist/application/skill-document-guided.d.ts +3 -0
  73. package/dist/application/skill-document-guided.js +53 -0
  74. package/dist/application/skill-document-guided.js.map +1 -0
  75. package/dist/application/skill-document.d.ts +19 -0
  76. package/dist/application/skill-document.js +368 -0
  77. package/dist/application/skill-document.js.map +1 -0
  78. package/dist/application/workspace-create-contract.d.ts +50 -0
  79. package/dist/application/workspace-create-contract.js +16 -0
  80. package/dist/application/workspace-create-contract.js.map +1 -0
  81. package/dist/application/workspace-create.d.ts +8 -0
  82. package/dist/application/workspace-create.js +656 -0
  83. package/dist/application/workspace-create.js.map +1 -0
  84. package/dist/application/workspace-health-contract.d.ts +133 -0
  85. package/dist/application/workspace-health-contract.js +272 -0
  86. package/dist/application/workspace-health-contract.js.map +1 -0
  87. package/dist/application/workspace-health.d.ts +10 -0
  88. package/dist/application/workspace-health.js +1114 -0
  89. package/dist/application/workspace-health.js.map +1 -0
  90. package/dist/application/workspace-operation-contract.d.ts +99 -0
  91. package/dist/application/workspace-operation-contract.js +450 -0
  92. package/dist/application/workspace-operation-contract.js.map +1 -0
  93. package/dist/application/workspace-operation.d.ts +42 -0
  94. package/dist/application/workspace-operation.js +517 -0
  95. package/dist/application/workspace-operation.js.map +1 -0
  96. package/dist/cli.d.ts +16 -0
  97. package/dist/cli.js +136 -0
  98. package/dist/cli.js.map +1 -0
  99. package/dist/commands/add.d.ts +8 -0
  100. package/dist/commands/add.js +77 -0
  101. package/dist/commands/add.js.map +1 -0
  102. package/dist/commands/build.d.ts +7 -0
  103. package/dist/commands/build.js +29 -0
  104. package/dist/commands/build.js.map +1 -0
  105. package/dist/commands/component-scaffold.d.ts +4 -0
  106. package/dist/commands/component-scaffold.js +14 -0
  107. package/dist/commands/component-scaffold.js.map +1 -0
  108. package/dist/commands/import-skill.d.ts +15 -0
  109. package/dist/commands/import-skill.js +41 -0
  110. package/dist/commands/import-skill.js.map +1 -0
  111. package/dist/commands/index-gen.d.ts +12 -0
  112. package/dist/commands/index-gen.js +33 -0
  113. package/dist/commands/index-gen.js.map +1 -0
  114. package/dist/commands/init-repo.d.ts +8 -0
  115. package/dist/commands/init-repo.js +53 -0
  116. package/dist/commands/init-repo.js.map +1 -0
  117. package/dist/commands/init.d.ts +9 -0
  118. package/dist/commands/init.js +33 -0
  119. package/dist/commands/init.js.map +1 -0
  120. package/dist/commands/install-repo.d.ts +20 -0
  121. package/dist/commands/install-repo.js +200 -0
  122. package/dist/commands/install-repo.js.map +1 -0
  123. package/dist/commands/release-local.d.ts +9 -0
  124. package/dist/commands/release-local.js +27 -0
  125. package/dist/commands/release-local.js.map +1 -0
  126. package/dist/commands/validate.d.ts +8 -0
  127. package/dist/commands/validate.js +45 -0
  128. package/dist/commands/validate.js.map +1 -0
  129. package/dist/core/errors.d.ts +5 -0
  130. package/dist/core/errors.js +12 -0
  131. package/dist/core/errors.js.map +1 -0
  132. package/dist/generated/skill-content.d.ts +3 -0
  133. package/dist/generated/skill-content.js +9 -0
  134. package/dist/generated/skill-content.js.map +1 -0
  135. package/dist/infrastructure/authorized-path.d.ts +20 -0
  136. package/dist/infrastructure/authorized-path.js +85 -0
  137. package/dist/infrastructure/authorized-path.js.map +1 -0
  138. package/dist/infrastructure/document-commit.d.ts +165 -0
  139. package/dist/infrastructure/document-commit.js +1352 -0
  140. package/dist/infrastructure/document-commit.js.map +1 -0
  141. package/dist/infrastructure/process-runner.d.ts +29 -0
  142. package/dist/infrastructure/process-runner.js +130 -0
  143. package/dist/infrastructure/process-runner.js.map +1 -0
  144. package/dist/infrastructure/vscode-user-settings.d.ts +42 -0
  145. package/dist/infrastructure/vscode-user-settings.js +509 -0
  146. package/dist/infrastructure/vscode-user-settings.js.map +1 -0
  147. package/dist/schema/plugin-yaml.d.ts +89 -0
  148. package/dist/schema/plugin-yaml.js +241 -0
  149. package/dist/schema/plugin-yaml.js.map +1 -0
  150. package/dist/utils/frontmatter.d.ts +30 -0
  151. package/dist/utils/frontmatter.js +60 -0
  152. package/dist/utils/frontmatter.js.map +1 -0
  153. package/dist/utils/helpers.d.ts +44 -0
  154. package/dist/utils/helpers.js +94 -0
  155. package/dist/utils/helpers.js.map +1 -0
  156. package/package.json +56 -0
@@ -0,0 +1,1037 @@
1
+ import { existsSync, lstatSync, readFileSync } from "node:fs";
2
+ import { CST, Document, isAlias, isMap, isScalar, parseDocument, } from "yaml";
3
+ import { AuthorizedPathError, authorizeExistingDirectory, resolveAuthorizedPath, } from "../infrastructure/authorized-path.js";
4
+ import { commitAuthorizedDocument, documentRevision, } from "../infrastructure/document-commit.js";
5
+ import { validatePluginYamlValue } from "./plugin-source.js";
6
+ import { PLUGIN_DOCUMENT_FILENAME, } from "./plugin-document-contract.js";
7
+ const UTF8_BOM = Buffer.from([0xef, 0xbb, 0xbf]);
8
+ export const MAX_PLUGIN_DOCUMENT_BYTES = 5 * 1024 * 1024;
9
+ function problem(code, technicalDetail) {
10
+ if (code === "PLUGIN_MISSING") {
11
+ return {
12
+ code,
13
+ title: "找不到插件基础信息",
14
+ message: "当前插件中没有可读取的源信息。",
15
+ impact: "无法打开结构化编辑;没有创建或修改任何文件。",
16
+ nextAction: "请在外部确认插件文件,然后重新加载。",
17
+ ...(technicalDetail === undefined ? {} : { technicalDetail }),
18
+ };
19
+ }
20
+ if (code === "PLUGIN_UNSAFE") {
21
+ return {
22
+ code,
23
+ title: "插件基础信息的位置不安全",
24
+ message: "插件源信息使用了无法确认授权边界的路径或链接。",
25
+ impact: "结构化编辑已停止,没有覆盖任何文件。",
26
+ nextAction: "请在外部修正文件位置,然后重新检查 Marketplace。",
27
+ ...(technicalDetail === undefined ? {} : { technicalDetail }),
28
+ };
29
+ }
30
+ if (code === "PLUGIN_UNAVAILABLE") {
31
+ return {
32
+ code,
33
+ title: "暂时无法读取插件基础信息",
34
+ message: "插件文件夹或源信息当前不可访问。",
35
+ impact: "结构化编辑已停止,没有覆盖任何文件。",
36
+ nextAction: "请检查本机权限和文件状态后重新加载。",
37
+ ...(technicalDetail === undefined ? {} : { technicalDetail }),
38
+ };
39
+ }
40
+ if (code === "INVALID_ENCODING") {
41
+ return {
42
+ code,
43
+ title: "插件基础信息的文字编码无法安全读取",
44
+ message: "插件源信息不是有效 UTF-8 文本。",
45
+ impact: "客户端不会用结构化表单覆盖这个文件。",
46
+ nextAction: "请在外部编辑器中转换为 UTF-8,然后重新加载。",
47
+ ...(technicalDetail === undefined ? {} : { technicalDetail }),
48
+ };
49
+ }
50
+ if (code === "INVALID_YAML") {
51
+ return {
52
+ code,
53
+ title: "插件基础信息无法解析",
54
+ message: "插件源信息不是可安全解释的文档。",
55
+ impact: "客户端不会用结构化表单覆盖原文件。",
56
+ nextAction: "请在外部编辑器中修正 YAML,然后重新加载。",
57
+ ...(technicalDetail === undefined ? {} : { technicalDetail }),
58
+ };
59
+ }
60
+ return {
61
+ code,
62
+ title: "这部分插件信息无法安全改写",
63
+ message: "目标字段位于无法局部确认的 YAML 结构中。",
64
+ impact: "客户端保留原文件和本机输入,不会重排或覆盖不明确的内容。",
65
+ nextAction: "请在外部编辑器中调整这部分结构,然后重新加载。",
66
+ ...(technicalDetail === undefined ? {} : { technicalDetail }),
67
+ };
68
+ }
69
+ function decodeUtf8(bytes) {
70
+ const bom = bytes.subarray(0, UTF8_BOM.length).equals(UTF8_BOM);
71
+ const content = bom ? bytes.subarray(UTF8_BOM.length) : bytes;
72
+ const decoder = new TextDecoder("utf-8", { fatal: true });
73
+ return { source: decoder.decode(content), bom };
74
+ }
75
+ function lineEnding(source) {
76
+ return source.includes("\r\n") ? "crlf" : "lf";
77
+ }
78
+ function isRecord(value) {
79
+ return value !== null && typeof value === "object" && !Array.isArray(value);
80
+ }
81
+ function stringValue(value) {
82
+ return typeof value === "string" ? value : "";
83
+ }
84
+ function stringList(value) {
85
+ return Array.isArray(value)
86
+ ? value.map((entry) => (typeof entry === "string" ? entry : ""))
87
+ : [];
88
+ }
89
+ function draftFromValue(value) {
90
+ const root = isRecord(value) ? value : {};
91
+ const author = isRecord(root.author) ? root.author : {};
92
+ const platform = isRecord(root.platform) ? root.platform : {};
93
+ const codex = isRecord(platform.codex) ? platform.codex : {};
94
+ const iface = isRecord(codex.interface) ? codex.interface : {};
95
+ return {
96
+ name: stringValue(root.name),
97
+ version: stringValue(root.version),
98
+ description: stringValue(root.description),
99
+ author: {
100
+ name: stringValue(author.name),
101
+ email: stringValue(author.email),
102
+ url: stringValue(author.url),
103
+ },
104
+ category: stringValue(root.category),
105
+ tags: stringList(root.tags),
106
+ codex: {
107
+ displayName: stringValue(iface.displayName),
108
+ shortDescription: stringValue(iface.shortDescription),
109
+ longDescription: stringValue(iface.longDescription),
110
+ category: stringValue(iface.category),
111
+ developerName: stringValue(iface.developerName),
112
+ capabilities: stringList(iface.capabilities),
113
+ defaultPrompts: stringList(iface.defaultPrompt),
114
+ },
115
+ };
116
+ }
117
+ function sameStrings(left, right) {
118
+ return (left.length === right.length &&
119
+ left.every((value, index) => value === right[index]));
120
+ }
121
+ function leafIsUnchanged(leaf) {
122
+ if (typeof leaf.value === "string") {
123
+ return typeof leaf.baseline === "string" && leaf.value === leaf.baseline;
124
+ }
125
+ return (typeof leaf.baseline !== "string" && sameStrings(leaf.value, leaf.baseline));
126
+ }
127
+ function sameDraft(left, right) {
128
+ return (left.name === right.name &&
129
+ left.version === right.version &&
130
+ left.description === right.description &&
131
+ left.author.name === right.author.name &&
132
+ left.author.email === right.author.email &&
133
+ left.author.url === right.author.url &&
134
+ left.category === right.category &&
135
+ sameStrings(left.tags, right.tags) &&
136
+ left.codex.displayName === right.codex.displayName &&
137
+ left.codex.shortDescription === right.codex.shortDescription &&
138
+ left.codex.longDescription === right.codex.longDescription &&
139
+ left.codex.category === right.codex.category &&
140
+ left.codex.developerName === right.codex.developerName &&
141
+ sameStrings(left.codex.capabilities, right.codex.capabilities) &&
142
+ sameStrings(left.codex.defaultPrompts, right.codex.defaultPrompts));
143
+ }
144
+ function parseBytes(bytes) {
145
+ const revision = documentRevision(bytes);
146
+ let decoded;
147
+ try {
148
+ decoded = decodeUtf8(bytes);
149
+ }
150
+ catch (error) {
151
+ return {
152
+ status: "invalid",
153
+ revision,
154
+ problem: problem("INVALID_ENCODING", error instanceof Error ? error.message : String(error)),
155
+ };
156
+ }
157
+ const document = parseDocument(decoded.source, {
158
+ keepSourceTokens: true,
159
+ logLevel: "silent",
160
+ });
161
+ if (document.errors.length > 0) {
162
+ return {
163
+ status: "invalid",
164
+ revision,
165
+ problem: problem("INVALID_YAML", document.errors.map((error) => error.message).join("\n")),
166
+ };
167
+ }
168
+ if (!isMap(document.contents)) {
169
+ return {
170
+ status: "invalid",
171
+ revision,
172
+ problem: problem("UNSAFE_STRUCTURE", "plugin.yaml must contain a mapping"),
173
+ };
174
+ }
175
+ let value;
176
+ try {
177
+ value = document.toJS({ maxAliasCount: 100 });
178
+ }
179
+ catch (error) {
180
+ return {
181
+ status: "invalid",
182
+ revision,
183
+ problem: problem("INVALID_YAML", error instanceof Error ? error.message : String(error)),
184
+ };
185
+ }
186
+ return {
187
+ status: "valid",
188
+ value: {
189
+ sourceBytes: bytes,
190
+ source: decoded.source,
191
+ document,
192
+ map: document.contents,
193
+ value,
194
+ draft: draftFromValue(value),
195
+ revision,
196
+ format: {
197
+ bom: decoded.bom,
198
+ lineEnding: lineEnding(decoded.source),
199
+ },
200
+ },
201
+ };
202
+ }
203
+ export function pluginDocumentRelativePath(pluginDirectoryName) {
204
+ if (pluginDirectoryName.length === 0 ||
205
+ pluginDirectoryName === "." ||
206
+ pluginDirectoryName === ".." ||
207
+ pluginDirectoryName.includes("/") ||
208
+ pluginDirectoryName.includes("\\")) {
209
+ throw new AuthorizedPathError("OUTSIDE_AUTHORIZED_ROOT", "插件目录标识不是单个已授权对象", pluginDirectoryName);
210
+ }
211
+ return `plugins/${pluginDirectoryName}/${PLUGIN_DOCUMENT_FILENAME}`;
212
+ }
213
+ function readPluginBytes(request, dependencies = {}) {
214
+ try {
215
+ const authorization = authorizeExistingDirectory(request.directory);
216
+ const relativePath = pluginDocumentRelativePath(request.pluginDirectoryName);
217
+ const path = resolveAuthorizedPath(authorization, relativePath);
218
+ if (!existsSync(path)) {
219
+ return {
220
+ status: "unavailable",
221
+ problem: problem("PLUGIN_MISSING"),
222
+ };
223
+ }
224
+ const sourceStat = (dependencies.statSource ?? lstatSync)(path);
225
+ if (!sourceStat.isFile()) {
226
+ return {
227
+ status: "unavailable",
228
+ problem: problem("PLUGIN_UNAVAILABLE", "plugin.yaml is not a regular file"),
229
+ };
230
+ }
231
+ if (sourceStat.size > MAX_PLUGIN_DOCUMENT_BYTES) {
232
+ return {
233
+ status: "unavailable",
234
+ problem: problem("PLUGIN_UNAVAILABLE", `plugin source exceeds ${MAX_PLUGIN_DOCUMENT_BYTES} bytes`),
235
+ };
236
+ }
237
+ const bytes = (dependencies.readSource ?? readFileSync)(path);
238
+ if (bytes.length > MAX_PLUGIN_DOCUMENT_BYTES) {
239
+ return {
240
+ status: "unavailable",
241
+ problem: problem("PLUGIN_UNAVAILABLE", `plugin source grew beyond ${MAX_PLUGIN_DOCUMENT_BYTES} bytes while reading`),
242
+ };
243
+ }
244
+ return { status: "loaded", bytes };
245
+ }
246
+ catch (error) {
247
+ const unsafe = error instanceof AuthorizedPathError &&
248
+ new Set([
249
+ "ABSOLUTE_PATH",
250
+ "OUTSIDE_AUTHORIZED_ROOT",
251
+ "UNSAFE_SYMLINK",
252
+ ]).has(error.code);
253
+ return {
254
+ status: "unavailable",
255
+ problem: problem(unsafe ? "PLUGIN_UNSAFE" : "PLUGIN_UNAVAILABLE", error instanceof Error ? error.message : String(error)),
256
+ };
257
+ }
258
+ }
259
+ function cloneValue(value) {
260
+ return structuredClone(value);
261
+ }
262
+ function recordAt(root, path, create) {
263
+ let cursor = root;
264
+ for (const key of path) {
265
+ const next = cursor[key];
266
+ if (isRecord(next)) {
267
+ cursor = next;
268
+ continue;
269
+ }
270
+ if (!create)
271
+ return undefined;
272
+ const created = {};
273
+ cursor[key] = created;
274
+ cursor = created;
275
+ }
276
+ return cursor;
277
+ }
278
+ function applyLeafToCandidate(root, leaf) {
279
+ if (leafIsUnchanged(leaf))
280
+ return;
281
+ const parentPath = leaf.path.slice(0, -1);
282
+ const key = leaf.path.at(-1);
283
+ if (key === undefined)
284
+ return;
285
+ if (leaf.optional &&
286
+ (typeof leaf.value === "string"
287
+ ? leaf.value.length === 0
288
+ : leaf.value.length === 0)) {
289
+ const parent = recordAt(root, parentPath, false);
290
+ if (parent !== undefined)
291
+ delete parent[key];
292
+ return;
293
+ }
294
+ const parent = recordAt(root, parentPath, true);
295
+ if (parent !== undefined) {
296
+ parent[key] = Array.isArray(leaf.value) ? [...leaf.value] : leaf.value;
297
+ }
298
+ }
299
+ function editableLeaves(baseline, draft) {
300
+ return [
301
+ {
302
+ path: ["name"],
303
+ field: "name",
304
+ optional: false,
305
+ value: draft.name,
306
+ baseline: baseline.name,
307
+ },
308
+ {
309
+ path: ["version"],
310
+ field: "version",
311
+ optional: false,
312
+ value: draft.version,
313
+ baseline: baseline.version,
314
+ },
315
+ {
316
+ path: ["description"],
317
+ field: "description",
318
+ optional: false,
319
+ value: draft.description,
320
+ baseline: baseline.description,
321
+ },
322
+ {
323
+ path: ["author", "name"],
324
+ field: "author.name",
325
+ optional: false,
326
+ value: draft.author.name,
327
+ baseline: baseline.author.name,
328
+ },
329
+ {
330
+ path: ["author", "email"],
331
+ field: "author.email",
332
+ optional: true,
333
+ value: draft.author.email,
334
+ baseline: baseline.author.email,
335
+ },
336
+ {
337
+ path: ["author", "url"],
338
+ field: "author.url",
339
+ optional: true,
340
+ value: draft.author.url,
341
+ baseline: baseline.author.url,
342
+ },
343
+ {
344
+ path: ["category"],
345
+ field: "category",
346
+ optional: true,
347
+ value: draft.category,
348
+ baseline: baseline.category,
349
+ },
350
+ {
351
+ path: ["tags"],
352
+ field: "tags",
353
+ optional: true,
354
+ value: draft.tags,
355
+ baseline: baseline.tags,
356
+ },
357
+ {
358
+ path: ["platform", "codex", "interface", "displayName"],
359
+ field: "codex.displayName",
360
+ optional: true,
361
+ value: draft.codex.displayName,
362
+ baseline: baseline.codex.displayName,
363
+ },
364
+ {
365
+ path: ["platform", "codex", "interface", "shortDescription"],
366
+ field: "codex.shortDescription",
367
+ optional: true,
368
+ value: draft.codex.shortDescription,
369
+ baseline: baseline.codex.shortDescription,
370
+ },
371
+ {
372
+ path: ["platform", "codex", "interface", "longDescription"],
373
+ field: "codex.longDescription",
374
+ optional: true,
375
+ value: draft.codex.longDescription,
376
+ baseline: baseline.codex.longDescription,
377
+ },
378
+ {
379
+ path: ["platform", "codex", "interface", "category"],
380
+ field: "codex.category",
381
+ optional: true,
382
+ value: draft.codex.category,
383
+ baseline: baseline.codex.category,
384
+ },
385
+ {
386
+ path: ["platform", "codex", "interface", "developerName"],
387
+ field: "codex.developerName",
388
+ optional: true,
389
+ value: draft.codex.developerName,
390
+ baseline: baseline.codex.developerName,
391
+ },
392
+ {
393
+ path: ["platform", "codex", "interface", "capabilities"],
394
+ field: "codex.capabilities",
395
+ optional: true,
396
+ value: draft.codex.capabilities,
397
+ baseline: baseline.codex.capabilities,
398
+ },
399
+ {
400
+ path: ["platform", "codex", "interface", "defaultPrompt"],
401
+ field: "codex.defaultPrompts",
402
+ optional: true,
403
+ value: draft.codex.defaultPrompts,
404
+ baseline: baseline.codex.defaultPrompts,
405
+ },
406
+ ];
407
+ }
408
+ function candidateValue(parsed, draft) {
409
+ const candidate = cloneValue(parsed.value);
410
+ if (!isRecord(candidate))
411
+ return candidate;
412
+ for (const leaf of editableLeaves(parsed.draft, draft)) {
413
+ applyLeafToCandidate(candidate, leaf);
414
+ }
415
+ return candidate;
416
+ }
417
+ function fieldForPath(path) {
418
+ if (path === "/name" || path.startsWith("/name/"))
419
+ return "name";
420
+ if (path === "/version" || path.startsWith("/version/"))
421
+ return "version";
422
+ if (path === "/description" || path.startsWith("/description/")) {
423
+ return "description";
424
+ }
425
+ if (path === "/author/name" || path.startsWith("/author/name/")) {
426
+ return "author.name";
427
+ }
428
+ if (path === "/author/email" || path.startsWith("/author/email/")) {
429
+ return "author.email";
430
+ }
431
+ if (path === "/author/url" || path.startsWith("/author/url/")) {
432
+ return "author.url";
433
+ }
434
+ if (path === "/category" || path.startsWith("/category/")) {
435
+ return "category";
436
+ }
437
+ if (path === "/tags" || path.startsWith("/tags/"))
438
+ return "tags";
439
+ const interfacePrefix = "/platform/codex/interface/";
440
+ if (!path.startsWith(interfacePrefix))
441
+ return "advanced";
442
+ const leaf = path.slice(interfacePrefix.length).split("/")[0];
443
+ const interfaceFields = {
444
+ displayName: "codex.displayName",
445
+ shortDescription: "codex.shortDescription",
446
+ longDescription: "codex.longDescription",
447
+ category: "codex.category",
448
+ developerName: "codex.developerName",
449
+ capabilities: "codex.capabilities",
450
+ defaultPrompt: "codex.defaultPrompts",
451
+ };
452
+ return interfaceFields[leaf ?? ""] ?? "advanced";
453
+ }
454
+ function fieldLabel(field) {
455
+ return {
456
+ name: "内部名称",
457
+ version: "版本",
458
+ description: "插件说明",
459
+ "author.name": "维护者名称",
460
+ "author.email": "维护者邮箱",
461
+ "author.url": "维护者主页",
462
+ category: "插件分类",
463
+ tags: "标签",
464
+ "codex.displayName": "Codex 显示名称",
465
+ "codex.shortDescription": "Codex 简短说明",
466
+ "codex.longDescription": "Codex 完整说明",
467
+ "codex.category": "Codex 分类",
468
+ "codex.developerName": "Codex 开发者名称",
469
+ "codex.capabilities": "Codex 能力标签",
470
+ "codex.defaultPrompts": "Codex 默认提示",
471
+ advanced: "高级插件结构",
472
+ }[field];
473
+ }
474
+ function technicalPath(issue) {
475
+ const base = issue.instancePath === "/" ? "" : issue.instancePath;
476
+ if (issue.keyword === "required" && issue.missingProperty !== undefined) {
477
+ return `${base}/${issue.missingProperty}` || "/";
478
+ }
479
+ if (issue.keyword === "additionalProperties" &&
480
+ issue.additionalProperty !== undefined) {
481
+ return `${base}/${issue.additionalProperty}` || "/";
482
+ }
483
+ return issue.instancePath || "/";
484
+ }
485
+ function schemaIssueKind(keyword) {
486
+ if (keyword === "required")
487
+ return "required";
488
+ if (keyword === "minLength" ||
489
+ keyword === "maxLength" ||
490
+ keyword === "minItems" ||
491
+ keyword === "maxItems") {
492
+ return "length";
493
+ }
494
+ if (keyword === "pattern")
495
+ return "pattern";
496
+ if (keyword === "enum")
497
+ return "enum";
498
+ if (keyword === "type")
499
+ return "type";
500
+ return "unsupported";
501
+ }
502
+ function schemaProductIssue(issue, index) {
503
+ const path = technicalPath(issue);
504
+ const field = fieldForPath(path);
505
+ const label = fieldLabel(field);
506
+ const kind = schemaIssueKind(issue.keyword);
507
+ const title = kind === "required"
508
+ ? `${label}还没有填写`
509
+ : kind === "length"
510
+ ? `${label}的长度不符合要求`
511
+ : kind === "pattern"
512
+ ? `${label}的格式不符合要求`
513
+ : kind === "enum"
514
+ ? `${label}使用了不支持的选项`
515
+ : kind === "type"
516
+ ? `${label}的内容类型无法理解`
517
+ : `${label}包含当前编辑器不能处理的内容`;
518
+ const message = kind === "required"
519
+ ? `${label}是完成保存所需的信息。`
520
+ : kind === "length"
521
+ ? `${label}的文字长度或条目数量超出允许范围。`
522
+ : kind === "pattern"
523
+ ? `${label}需要使用该字段支持的格式。`
524
+ : kind === "enum"
525
+ ? `${label}需要改为当前支持的选项。`
526
+ : kind === "type"
527
+ ? `${label}需要改为该字段支持的内容类型。`
528
+ : `${label}包含结构化编辑器无法安全处理的内容。`;
529
+ return {
530
+ id: `schema-${String(index + 1).padStart(3, "0")}`,
531
+ severity: "blocking",
532
+ kind,
533
+ field,
534
+ title,
535
+ message,
536
+ impact: field === "advanced"
537
+ ? "客户端不能安全提交其他字段,避免顺带保留一个无法验证的插件声明。"
538
+ : "保存会被阻止,已保存的插件信息保持不变。",
539
+ nextAction: field === "advanced"
540
+ ? "请在外部编辑器中修正技术详情标出的位置,再重新加载。"
541
+ : `请先修正${label},再保存更改。`,
542
+ technicalPath: path,
543
+ };
544
+ }
545
+ function normalizedListValue(value) {
546
+ return value.normalize("NFKC").trim().toLocaleLowerCase("en-US");
547
+ }
548
+ function duplicateIssues(field, values, startIndex) {
549
+ const firstByValue = new Map();
550
+ const issues = [];
551
+ for (const [index, value] of values.entries()) {
552
+ const normalized = normalizedListValue(value);
553
+ if (normalized.length === 0)
554
+ continue;
555
+ const first = firstByValue.get(normalized);
556
+ if (first === undefined) {
557
+ firstByValue.set(normalized, index);
558
+ continue;
559
+ }
560
+ const label = fieldLabel(field);
561
+ issues.push({
562
+ id: `attention-${String(startIndex + issues.length + 1).padStart(3, "0")}`,
563
+ severity: "attention",
564
+ kind: "duplicate",
565
+ field,
566
+ title: `${label}中有重复内容`,
567
+ message: `第 ${first + 1} 项和第 ${index + 1} 项表达了相同内容。`,
568
+ impact: "这不会改变文件格式有效性,但会让平台展示或检索结果重复。",
569
+ nextAction: `建议保留一项${label}内容;也可以先保存并稍后整理。`,
570
+ technicalPath: field === "tags"
571
+ ? `/tags/${index}`
572
+ : field === "codex.capabilities"
573
+ ? `/platform/codex/interface/capabilities/${index}`
574
+ : `/platform/codex/interface/defaultPrompt/${index}`,
575
+ });
576
+ }
577
+ return issues;
578
+ }
579
+ function qualityFor(parsed, draft, directoryName) {
580
+ const candidate = candidateValue(parsed, draft);
581
+ const validation = validatePluginYamlValue(candidate);
582
+ const issues = validation.status === "valid"
583
+ ? []
584
+ : validation.issues.map(schemaProductIssue);
585
+ if (draft.name.length > 0 && draft.name !== directoryName) {
586
+ issues.push({
587
+ id: `attention-${String(issues.length + 1).padStart(3, "0")}`,
588
+ severity: "attention",
589
+ kind: "cross-field",
590
+ field: "name",
591
+ title: "内部名称与插件文件夹不同",
592
+ message: `保存后内部名称会是“${draft.name}”,插件文件夹仍保持“${directoryName}”。`,
593
+ impact: "目录不会自动重命名;插件会进入名称待修复状态,相关平台生成结果需要重新生成。",
594
+ nextAction: `如需消除差异,请把内部名称改回“${directoryName}”;也可以保留差异后继续保存。`,
595
+ technicalPath: "/name",
596
+ });
597
+ }
598
+ const duplicateFields = [
599
+ ["tags", draft.tags],
600
+ ["codex.capabilities", draft.codex.capabilities],
601
+ ["codex.defaultPrompts", draft.codex.defaultPrompts],
602
+ ];
603
+ for (const [field, values] of duplicateFields) {
604
+ issues.push(...duplicateIssues(field, values, issues.length));
605
+ }
606
+ const blockingCount = issues.filter((issue) => issue.severity === "blocking").length;
607
+ const attentionCount = issues.length - blockingCount;
608
+ return {
609
+ state: blockingCount > 0
610
+ ? "invalid"
611
+ : attentionCount > 0
612
+ ? "attention"
613
+ : "valid",
614
+ blockingCount,
615
+ attentionCount,
616
+ firstAction: issues[0]?.nextAction ?? "当前字段满足保存要求,可以保存更改。",
617
+ issues,
618
+ };
619
+ }
620
+ export function analyzePluginEditorDraft(bytes, draft, context) {
621
+ const parsed = parseBytes(Buffer.from(bytes));
622
+ if (parsed.status === "invalid") {
623
+ return {
624
+ status: "invalid-source",
625
+ revision: parsed.revision,
626
+ problem: parsed.problem,
627
+ };
628
+ }
629
+ return {
630
+ status: "analyzed",
631
+ quality: qualityFor(parsed.value, draft, context.directoryName),
632
+ };
633
+ }
634
+ function pairForKey(map, key) {
635
+ return map.items.find((pair) => isScalar(pair.key) && pair.key.value === key);
636
+ }
637
+ function encodeScalar(value) {
638
+ const document = new Document(value);
639
+ return document
640
+ .toString({
641
+ blockQuote: false,
642
+ defaultStringType: "QUOTE_DOUBLE",
643
+ lineWidth: 0,
644
+ })
645
+ .replace(/\n$/, "");
646
+ }
647
+ function encodeValue(value) {
648
+ return typeof value === "string"
649
+ ? encodeScalar(value)
650
+ : `[${value.map(encodeScalar).join(", ")}]`;
651
+ }
652
+ function findParentMap(root, path) {
653
+ let map = root;
654
+ for (const [index, key] of path.entries()) {
655
+ const pair = pairForKey(map, key);
656
+ if (pair === undefined) {
657
+ return {
658
+ status: "found",
659
+ map,
660
+ missingPath: path.slice(index),
661
+ };
662
+ }
663
+ const node = pair.value;
664
+ if (node == null || isAlias(node) || !isMap(node)) {
665
+ return {
666
+ status: "unsafe",
667
+ detail: `${path.slice(0, index + 1).join(".")} is not a directly editable mapping`,
668
+ };
669
+ }
670
+ map = node;
671
+ }
672
+ return { status: "found", map, missingPath: [] };
673
+ }
674
+ function lineIndent(source, offset) {
675
+ const lineStart = source.lastIndexOf("\n", Math.max(0, offset - 1)) + 1;
676
+ const prefix = source.slice(lineStart, offset);
677
+ if (!/^[ \t]*$/.test(prefix)) {
678
+ throw new Error("无法确认 YAML mapping 的缩进");
679
+ }
680
+ return prefix;
681
+ }
682
+ function mapIndent(source, map, fallbackDepth) {
683
+ const first = map.items.find((pair) => isScalar(pair.key) && pair.key.range != null);
684
+ if (first === undefined || !isScalar(first.key) || first.key.range == null) {
685
+ return " ".repeat(fallbackDepth);
686
+ }
687
+ return lineIndent(source, first.key.range[0]);
688
+ }
689
+ function nestedBlockPair(path, value, indent) {
690
+ return path
691
+ .map((key, index) => {
692
+ const currentIndent = `${indent}${" ".repeat(index)}`;
693
+ return index === path.length - 1
694
+ ? `${currentIndent}${key}: ${encodeValue(value)}`
695
+ : `${currentIndent}${key}:`;
696
+ })
697
+ .join("\n");
698
+ }
699
+ function nestedFlowPair(path, value) {
700
+ let result = `${path.at(-1)}: ${encodeValue(value)}`;
701
+ for (let index = path.length - 2; index >= 0; index -= 1) {
702
+ result = `${path[index]}: { ${result} }`;
703
+ }
704
+ return result;
705
+ }
706
+ function insertionForMap(source, map, path, value, lineEndingValue, fallbackDepth) {
707
+ const range = map.range;
708
+ if (range == null) {
709
+ throw new Error("YAML Document API 没有提供 mapping source range");
710
+ }
711
+ if (map.flow) {
712
+ const close = source.lastIndexOf("}", range[1] - 1);
713
+ if (close < range[0]) {
714
+ throw new Error("无法定位 flow mapping 的结束位置");
715
+ }
716
+ const token = map.srcToken;
717
+ if (token === undefined || !CST.isCollection(token)) {
718
+ throw new Error("无法确认 flow mapping 的原始 token");
719
+ }
720
+ const trailingItem = token.items.at(-1);
721
+ const hasTrailingComma = trailingItem !== undefined &&
722
+ trailingItem.key === undefined &&
723
+ trailingItem.value === undefined &&
724
+ trailingItem.start.some((item) => item.type === "comma");
725
+ const empty = map.items.length === 0;
726
+ const prefix = hasTrailingComma || empty
727
+ ? /\s/.test(source[close - 1] ?? "")
728
+ ? ""
729
+ : " "
730
+ : ", ";
731
+ return {
732
+ start: close,
733
+ replacement: `${prefix}${nestedFlowPair(path, value)}`,
734
+ };
735
+ }
736
+ const eol = lineEndingValue === "crlf" ? "\r\n" : "\n";
737
+ const start = range[1];
738
+ const indent = mapIndent(source, map, fallbackDepth);
739
+ const lines = nestedBlockPair(path, value, indent);
740
+ const needsLeadingLine = start > 0 && source[start - 1] !== "\n";
741
+ const needsTrailingLine = start < source.length || source.endsWith("\n");
742
+ return {
743
+ start,
744
+ replacement: `${needsLeadingLine ? eol : ""}${lines}${needsTrailingLine ? eol : ""}`,
745
+ };
746
+ }
747
+ function replacementForExistingPair(source, pair, value) {
748
+ const node = pair.value;
749
+ if (node?.range == null) {
750
+ throw new Error("无法定位字段的原始文字范围");
751
+ }
752
+ const original = source.slice(node.range[0], node.range[1]);
753
+ const trailingLineEnding = original.endsWith("\r\n")
754
+ ? "\r\n"
755
+ : original.endsWith("\n")
756
+ ? "\n"
757
+ : "";
758
+ return {
759
+ start: node.range[0],
760
+ end: node.range[1],
761
+ replacement: `${encodeValue(value)}${trailingLineEnding}`,
762
+ };
763
+ }
764
+ function deletionForPair(source, map, pair) {
765
+ const keyRange = isScalar(pair.key) ? pair.key.range : undefined;
766
+ const valueRange = pair.value?.range;
767
+ const mapRange = map.range;
768
+ if (keyRange == null || valueRange == null || mapRange == null) {
769
+ throw new Error("无法定位需要清除的字段范围");
770
+ }
771
+ if (map.flow) {
772
+ const after = source.slice(valueRange[1], mapRange[1]);
773
+ const afterMatch = after.match(/^[ \t]*(,)[ \t]*/);
774
+ if (afterMatch !== null) {
775
+ return {
776
+ start: keyRange[0],
777
+ end: valueRange[1] + afterMatch[0].length,
778
+ };
779
+ }
780
+ const before = source.slice(mapRange[0], keyRange[0]);
781
+ const comma = before.lastIndexOf(",");
782
+ if (comma >= 0) {
783
+ return {
784
+ start: mapRange[0] + comma,
785
+ end: valueRange[1],
786
+ };
787
+ }
788
+ return { start: keyRange[0], end: valueRange[1] };
789
+ }
790
+ const start = source.lastIndexOf("\n", Math.max(0, keyRange[0] - 1)) + 1;
791
+ if (!/^[ \t]*$/.test(source.slice(start, keyRange[0]))) {
792
+ throw new Error("无法确认需要清除字段的行边界");
793
+ }
794
+ const valueAlreadyIncludesLineEnding = valueRange[1] > valueRange[0] && source[valueRange[1] - 1] === "\n";
795
+ const newline = valueAlreadyIncludesLineEnding
796
+ ? valueRange[1] - 1
797
+ : source.indexOf("\n", valueRange[1]);
798
+ return {
799
+ start,
800
+ end: newline < 0 ? source.length : newline + 1,
801
+ };
802
+ }
803
+ function patchLeaf(source, format, leaf) {
804
+ const document = parseDocument(source, {
805
+ keepSourceTokens: true,
806
+ logLevel: "silent",
807
+ });
808
+ if (document.errors.length > 0 || !isMap(document.contents)) {
809
+ return {
810
+ status: "unsafe",
811
+ detail: "source became unparseable during minimal patch",
812
+ };
813
+ }
814
+ const parentPath = leaf.path.slice(0, -1);
815
+ const key = leaf.path.at(-1);
816
+ if (key === undefined) {
817
+ return { status: "unsafe", detail: "empty owned field path" };
818
+ }
819
+ const parent = findParentMap(document.contents, parentPath);
820
+ if (parent.status === "unsafe")
821
+ return parent;
822
+ const fullMissingPath = [...parent.missingPath, key];
823
+ const pair = parent.missingPath.length === 0 ? pairForKey(parent.map, key) : undefined;
824
+ const clear = leaf.optional &&
825
+ (typeof leaf.value === "string"
826
+ ? leaf.value.length === 0
827
+ : leaf.value.length === 0);
828
+ try {
829
+ if (clear) {
830
+ if (pair === undefined)
831
+ return { status: "patched", source };
832
+ const deletion = deletionForPair(source, parent.map, pair);
833
+ if (!parent.map.flow && parent.map.items.length === 1) {
834
+ const eol = format.lineEnding === "crlf" ? "\r\n" : "\n";
835
+ const indent = mapIndent(source, parent.map, parentPath.length);
836
+ return {
837
+ status: "patched",
838
+ source: source.slice(0, deletion.start) +
839
+ `${indent}{}${deletion.end < source.length ? eol : ""}` +
840
+ source.slice(deletion.end),
841
+ };
842
+ }
843
+ return {
844
+ status: "patched",
845
+ source: source.slice(0, deletion.start) + source.slice(deletion.end),
846
+ };
847
+ }
848
+ if (pair !== undefined) {
849
+ const replacement = replacementForExistingPair(source, pair, leaf.value);
850
+ return {
851
+ status: "patched",
852
+ source: source.slice(0, replacement.start) +
853
+ replacement.replacement +
854
+ source.slice(replacement.end),
855
+ };
856
+ }
857
+ const insertion = insertionForMap(source, parent.map, fullMissingPath, leaf.value, format.lineEnding, leaf.path.length - fullMissingPath.length);
858
+ return {
859
+ status: "patched",
860
+ source: source.slice(0, insertion.start) +
861
+ insertion.replacement +
862
+ source.slice(insertion.start),
863
+ };
864
+ }
865
+ catch (error) {
866
+ return {
867
+ status: "unsafe",
868
+ detail: error instanceof Error ? error.message : String(error),
869
+ };
870
+ }
871
+ }
872
+ export function patchPluginDocumentBytes(bytes, draft, context) {
873
+ const parsed = parseBytes(Buffer.from(bytes));
874
+ if (parsed.status === "invalid") {
875
+ return {
876
+ status: "invalid-source",
877
+ revision: parsed.revision,
878
+ problem: parsed.problem,
879
+ };
880
+ }
881
+ const quality = qualityFor(parsed.value, draft, context.directoryName);
882
+ if (quality.blockingCount > 0) {
883
+ return {
884
+ status: "invalid-input",
885
+ revision: parsed.value.revision,
886
+ quality,
887
+ };
888
+ }
889
+ let source = parsed.value.source;
890
+ for (const leaf of editableLeaves(parsed.value.draft, draft)) {
891
+ if (leafIsUnchanged(leaf))
892
+ continue;
893
+ const patched = patchLeaf(source, parsed.value.format, leaf);
894
+ if (patched.status === "unsafe") {
895
+ return {
896
+ status: "invalid-source",
897
+ revision: parsed.value.revision,
898
+ problem: problem("UNSAFE_STRUCTURE", `${leaf.field}: ${patched.detail}`),
899
+ };
900
+ }
901
+ source = patched.source;
902
+ }
903
+ const encoded = Buffer.from(source, "utf8");
904
+ const nextBytes = parsed.value.format.bom
905
+ ? Buffer.concat([UTF8_BOM, encoded])
906
+ : encoded;
907
+ const reparsed = parseBytes(nextBytes);
908
+ if (reparsed.status === "invalid") {
909
+ return {
910
+ status: "invalid-source",
911
+ revision: parsed.value.revision,
912
+ problem: reparsed.problem,
913
+ };
914
+ }
915
+ const finalQuality = qualityFor(reparsed.value, draft, context.directoryName);
916
+ if (finalQuality.blockingCount > 0 ||
917
+ !sameDraft(reparsed.value.draft, draft)) {
918
+ return {
919
+ status: "invalid-source",
920
+ revision: parsed.value.revision,
921
+ problem: problem("UNSAFE_STRUCTURE", "patched document does not project back to the requested plugin fields"),
922
+ };
923
+ }
924
+ return {
925
+ status: "patched",
926
+ bytes: nextBytes,
927
+ draft: reparsed.value.draft,
928
+ quality: finalQuality,
929
+ };
930
+ }
931
+ export function loadPluginDocumentState(request, dependencies = {}) {
932
+ const loaded = readPluginBytes(request, dependencies);
933
+ if (loaded.status === "unavailable")
934
+ return loaded;
935
+ const parsed = parseBytes(loaded.bytes);
936
+ if (parsed.status === "invalid")
937
+ return parsed;
938
+ const quality = qualityFor(parsed.value, parsed.value.draft, request.pluginDirectoryName);
939
+ return {
940
+ status: "loaded",
941
+ result: {
942
+ status: "loaded",
943
+ draft: parsed.value.draft,
944
+ revision: parsed.value.revision,
945
+ format: parsed.value.format,
946
+ quality,
947
+ },
948
+ sourceBytes: Buffer.from(parsed.value.sourceBytes),
949
+ };
950
+ }
951
+ export function readPluginDocument(request, dependencies = {}) {
952
+ const loaded = loadPluginDocumentState(request, dependencies);
953
+ return loaded.status === "loaded" ? loaded.result : loaded;
954
+ }
955
+ export function savePluginDocument(request, dependencies = {}) {
956
+ const loaded = readPluginBytes(request, dependencies);
957
+ if (loaded.status === "unavailable") {
958
+ return {
959
+ status: "invalid-source",
960
+ problem: loaded.problem,
961
+ diskChanged: false,
962
+ changedPaths: [],
963
+ cleanupComplete: true,
964
+ };
965
+ }
966
+ const revision = documentRevision(loaded.bytes);
967
+ if (revision !== request.expectedRevision) {
968
+ return {
969
+ status: "conflict",
970
+ currentRevision: revision,
971
+ message: "已保存的插件信息已在外部变化;本机草稿仍保留,没有覆盖外部内容。",
972
+ diskChanged: false,
973
+ changedPaths: [],
974
+ cleanupComplete: true,
975
+ };
976
+ }
977
+ const patched = patchPluginDocumentBytes(loaded.bytes, request.draft, {
978
+ directoryName: request.pluginDirectoryName,
979
+ });
980
+ if (patched.status === "invalid-input") {
981
+ return {
982
+ status: "invalid-input",
983
+ quality: patched.quality,
984
+ diskChanged: false,
985
+ changedPaths: [],
986
+ cleanupComplete: true,
987
+ };
988
+ }
989
+ if (patched.status === "invalid-source") {
990
+ return {
991
+ status: "invalid-source",
992
+ problem: patched.problem,
993
+ diskChanged: false,
994
+ changedPaths: [],
995
+ cleanupComplete: true,
996
+ };
997
+ }
998
+ const relativePath = pluginDocumentRelativePath(request.pluginDirectoryName);
999
+ const commit = (dependencies.commit ?? commitAuthorizedDocument)({
1000
+ directory: request.directory,
1001
+ relativePath,
1002
+ expectedRevision: request.expectedRevision,
1003
+ nextBytes: patched.bytes,
1004
+ }, dependencies.commitDependencies);
1005
+ if (commit.status === "conflict") {
1006
+ return {
1007
+ status: "conflict",
1008
+ currentRevision: commit.currentRevision,
1009
+ message: "已保存的插件信息在提交前再次发生变化;本机草稿仍保留,没有覆盖外部内容。",
1010
+ diskChanged: false,
1011
+ changedPaths: [],
1012
+ cleanupComplete: true,
1013
+ };
1014
+ }
1015
+ if (commit.status === "busy") {
1016
+ return {
1017
+ status: "busy",
1018
+ message: "另一个写入正在处理这个 Marketplace,请稍后重试。",
1019
+ diskChanged: false,
1020
+ changedPaths: [],
1021
+ cleanupComplete: true,
1022
+ };
1023
+ }
1024
+ if (commit.status === "failed")
1025
+ return commit;
1026
+ return {
1027
+ status: "saved",
1028
+ draft: patched.draft,
1029
+ revision: commit.revision,
1030
+ savedAt: (dependencies.now ?? (() => new Date()))().toISOString(),
1031
+ diskChanged: commit.diskChanged,
1032
+ changedPaths: commit.changedPaths,
1033
+ cleanupComplete: true,
1034
+ quality: patched.quality,
1035
+ };
1036
+ }
1037
+ //# sourceMappingURL=plugin-document.js.map