dsh-plugin-prompt-tool 0.8.1 → 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.
- package/README.md +58 -50
- package/engine/character-tools.mjs +3 -6
- package/engine/compositions/library/agent-instructions.yml +1 -1
- package/engine/compositions/library/command-goal.yml +1 -1
- package/engine/compositions/library/compaction.yml +1 -1
- package/engine/compositions/library/delegation-ptc.yml +1 -1
- package/engine/compositions/library/delegation.yml +1 -1
- package/engine/compositions/library/persistent-shell.yml +1 -1
- package/engine/compositions/library/planning.yml +1 -1
- package/engine/compositions/library/skill-filesystem-cordis.yml +1 -1
- package/engine/compositions/library/skill-filesystem.yml +1 -1
- package/engine/compositions/library/tool-ask-user.yml +1 -1
- package/engine/compositions/library/tool-bash.yml +1 -1
- package/engine/compositions/library/tool-cordis.yml +1 -1
- package/engine/compositions/library/tool-fs-search.yml +1 -1
- package/engine/compositions/library/tool-fs.yml +1 -1
- package/engine/compositions/library/tool-goal.yml +1 -1
- package/engine/compositions/library/tool-jobs.yml +1 -1
- package/engine/compositions/library/tool-plugin-manager-disabled.yml +1 -1
- package/engine/compositions/library/tool-plugin-manager.yml +1 -1
- package/engine/compositions/library/tool-present.yml +1 -1
- package/engine/compositions/library/tool-presentation.yml +1 -1
- package/engine/compositions/library/tool-pwsh.yml +1 -1
- package/engine/compositions/library/tool-skill.yml +1 -1
- package/engine/compositions/library/tool-todo.yml +1 -1
- package/engine/compositions/library/tool-web.yml +1 -1
- package/engine/compositions/source/local/prompt-config-engine.yml +1 -1
- package/engine/executor.mjs +39 -26
- package/engine/instruction-hint.mjs +6 -2
- package/engine/layers.mjs +38 -30
- package/engine/order.mjs +17 -0
- package/engine/schema.mjs +16 -6
- package/engine/session-var-tools.mjs +3 -6
- package/engine/st-render.mjs +8 -4
- package/engine/subagent-tool-policy.mjs +25 -10
- package/engine/world-book-tools.mjs +3 -6
- package/lib/client.js +3451 -3519
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +233 -272
- package/lib/index.mjs +1020 -1226
- package/lib/{preset-package-BnJqrU4R.mjs → module-package-Cm3fHHlp.mjs} +997 -594
- package/lib/{paths-CY5psBqE.d.mts → paths-BgL42sSM.d.mts} +191 -149
- package/lib/preset-transfer.d.mts +2 -2
- package/lib/preset-transfer.mjs +2 -2
- package/modules/ponytail/configs/0000-ponytail-rules.yml +102 -0
- package/modules/ponytail/configs/0010-ponytail-level-full.yml +19 -0
- package/modules/ponytail/configs/0020-ponytail-level-lite.yml +19 -0
- package/modules/ponytail/configs/0030-ponytail-level-ultra.yml +19 -0
- package/modules/ponytail/module.yml +172 -0
- package/package.json +46 -80
- package/templates/20-system-section.yml +4 -1
- package/templates/70-subagent-maintenance.yml +1 -1
- package/templates/tools/50-delegate-worldbook.yml +1 -1
- package/preset/pt-cordis/preset.yml +0 -43
- package/preset/pt-cordis/skills/cordis-composition-reference/SKILL.md +0 -29
- package/preset/pt-cordis/skills/cordis-composition-reference/references/packages.md +0 -501
- package/preset/pt-cordis/skills/cordis-plugin-development/SKILL.md +0 -38
- package/preset/pt-cordis/skills/cordis-plugin-development/references/host-plugin.md +0 -60
- package/preset/pt-cordis/skills/cordis-plugin-development/references/mcp-bundle.md +0 -5
- package/preset/pt-cordis/skills/cordis-plugin-development/references/ui-plugin.md +0 -13
- package/preset/pt-cordis/skills/cordis-plugin-development/references/verification.md +0 -5
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/client.js +0 -22
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/cordis.patch.yml +0 -3
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/index.js +0 -2
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/decoration/package.json +0 -15
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/mcp/cordis.patch.yml +0 -8
- package/preset/pt-cordis/skills/cordis-plugin-development/templates/mcp/package.json +0 -7
- package/preset/pt-cordis/skills/editing-cordis-compositions/SKILL.md +0 -84
- package/preset/pt-custom/preset.yml +0 -15
- package/preset/pt-minimal/preset.yml +0 -27
- package/preset/pt-ptc/preset.yml +0 -45
- package/preset/pt-standard/preset.yml +0 -43
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { appendFileSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
1
|
import { basename, dirname, isAbsolute, join, posix, resolve, sep } from "node:path";
|
|
2
|
+
import { appendFileSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
import { Pair, Scalar, YAMLMap, YAMLSeq, parse, parseDocument, stringify } from "yaml";
|
|
5
|
+
import { Pair, Scalar, YAMLMap, YAMLSeq, isMap, parse, parseDocument, stringify } from "yaml";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
8
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
@@ -29,177 +29,196 @@ const DSH_HOME = resolveDshHome();
|
|
|
29
29
|
* 插件不再内置任何技能:包内没有 skills 目录,也不再有安装副本与内容账本。 */
|
|
30
30
|
const USER_SKILLS_DIR = join(DSH_HOME, "skills");
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* 插件自有**存储根**(DSH_HOME/.prompt-tool)。
|
|
33
|
+
*
|
|
34
|
+
* 根下按物分目录:`modules/` 放模块定义与物化产物,`.characters/` 放角色卡库。
|
|
35
|
+
* 与模块**同级的兄弟物**从模块路径上溯一级定位(`charactersDir(moduleRoot)` 就是这么做),
|
|
36
|
+
* 不从这里读——模块级常量在 import 时按 `DSH_HOME` 冻结,测试内改环境变量对它无效。
|
|
37
|
+
* 旧根 `.agent-presets/` 只读不删,本插件不再读写它。
|
|
38
|
+
*/
|
|
39
|
+
const STORAGE_ROOT = join(DSH_HOME, ".prompt-tool");
|
|
40
|
+
/**
|
|
41
|
+
* 存储根下的**模块根**:一个模块一个目录(`modules/<id>/`),定义与物化产物都在其内。
|
|
42
|
+
*
|
|
43
|
+
* 这是 `resolveModuleDir` / `assertModuleDirectory` / `listModules` 的扫描根,
|
|
44
|
+
* 也是「本插件管理的模块」身份判定的基准。模块目录必须是它的**直接子目录**
|
|
45
|
+
* (`assertModuleDirectory` 的一层深度契约由此成立)。
|
|
36
46
|
*/
|
|
37
|
-
const
|
|
38
|
-
|
|
47
|
+
const MODULES_DIR = join(STORAGE_ROOT, "modules");
|
|
48
|
+
/**
|
|
49
|
+
* 模块定义文件名:`<模块根>/<id>/module.yml`。
|
|
50
|
+
*
|
|
51
|
+
* 与物化产物(`configs/`、`custom-tools/`、`triggers.yml`、`subagent-tools/`)同目录。
|
|
52
|
+
* 旧形态的 `preset.yml` **不做兼容读取**:实测真实数据(beta 形态遗留)已经是本名,
|
|
53
|
+
* 且仓库从 `02754cc` 起就没有 `preset.yml` 形态的用户数据;读到旧名目录时由
|
|
54
|
+
* `listModules` 给出明确诊断,而不是静默跳过。
|
|
55
|
+
*/
|
|
56
|
+
const MODULE_DEFINITION_FILE = "module.yml";
|
|
57
|
+
/** 模块物化目录名:提示词配置切片的落点(原 `prompt-configs/`)。 */
|
|
58
|
+
const MODULE_CONFIGS_DIR = "configs";
|
|
39
59
|
//#endregion
|
|
40
|
-
//#region src/shared/
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
/** 类型、校验、卡片归属、草稿默认值和组合行映射的唯一运行时目录。 */
|
|
44
|
-
const ENGINE_PARAM_DEFINITIONS = {
|
|
60
|
+
//#region src/shared/legacy-prompt-params.ts
|
|
61
|
+
/** 旧规则快捷参数只用于读取与旧 API 兼容;新编辑器直接保存规则实例。 */
|
|
62
|
+
const LEGACY_PROMPT_PARAM_DEFINITIONS = {
|
|
45
63
|
firstTurnAnchor: {
|
|
46
64
|
kind: "boolean",
|
|
47
|
-
|
|
48
|
-
card: "prompt-defaults"
|
|
65
|
+
targets: ["near-anchor"]
|
|
49
66
|
},
|
|
50
67
|
firstTurnText: {
|
|
51
68
|
kind: "string",
|
|
52
|
-
|
|
53
|
-
card: "prompt-defaults"
|
|
69
|
+
targets: ["near-anchor", "prompt-injector"]
|
|
54
70
|
},
|
|
55
71
|
firstTurnCustom: {
|
|
56
72
|
kind: "boolean",
|
|
57
|
-
|
|
58
|
-
card: "prompt-defaults"
|
|
73
|
+
targets: ["near-anchor"]
|
|
59
74
|
},
|
|
60
75
|
guideText: {
|
|
61
76
|
kind: "string",
|
|
62
|
-
|
|
63
|
-
card: "prompt-defaults"
|
|
77
|
+
targets: ["router-guide"]
|
|
64
78
|
},
|
|
65
79
|
guideCustom: {
|
|
66
80
|
kind: "boolean",
|
|
67
|
-
|
|
68
|
-
card: "prompt-defaults"
|
|
81
|
+
targets: ["router-guide"]
|
|
69
82
|
},
|
|
70
83
|
guideEnabled: {
|
|
71
84
|
kind: "boolean",
|
|
72
|
-
|
|
73
|
-
card: "prompt-defaults"
|
|
85
|
+
targets: ["router-guide"]
|
|
74
86
|
},
|
|
75
87
|
injectPrompt: {
|
|
76
88
|
kind: "boolean",
|
|
77
|
-
|
|
78
|
-
card: "prompt-defaults"
|
|
89
|
+
targets: ["prompt-injector"]
|
|
79
90
|
},
|
|
91
|
+
firstTurnWord: {
|
|
92
|
+
kind: "string",
|
|
93
|
+
targets: ["prompt-injector"]
|
|
94
|
+
},
|
|
95
|
+
buildPattern: {
|
|
96
|
+
kind: "pattern",
|
|
97
|
+
targets: ["near-anchor"]
|
|
98
|
+
},
|
|
99
|
+
complexPattern: {
|
|
100
|
+
kind: "pattern",
|
|
101
|
+
targets: ["near-anchor", "router-guide"]
|
|
102
|
+
},
|
|
103
|
+
firstTurnBuild: {
|
|
104
|
+
kind: "string",
|
|
105
|
+
targets: ["near-anchor", "prompt-injector"]
|
|
106
|
+
},
|
|
107
|
+
firstTurnInspect: {
|
|
108
|
+
kind: "string",
|
|
109
|
+
targets: ["near-anchor", "prompt-injector"]
|
|
110
|
+
},
|
|
111
|
+
firstTurnDeep: {
|
|
112
|
+
kind: "string",
|
|
113
|
+
targets: ["near-anchor", "prompt-injector"]
|
|
114
|
+
},
|
|
115
|
+
guideWeak: {
|
|
116
|
+
kind: "string",
|
|
117
|
+
targets: ["router-guide"]
|
|
118
|
+
},
|
|
119
|
+
guideDeep: {
|
|
120
|
+
kind: "string",
|
|
121
|
+
targets: ["router-guide"]
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const LEGACY_PROMPT_PARAM_KEYS = Object.keys(LEGACY_PROMPT_PARAM_DEFINITIONS);
|
|
125
|
+
const isLegacyPromptParam = (key) => Object.hasOwn(LEGACY_PROMPT_PARAM_DEFINITIONS, key);
|
|
126
|
+
const legacyPromptParamPath = (key) => [
|
|
127
|
+
"layerSettings",
|
|
128
|
+
"pre-step",
|
|
129
|
+
key
|
|
130
|
+
];
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/shared/engine-params.ts
|
|
133
|
+
const FINITE_NUMBER = (value) => Number.isFinite(value) ? void 0 : "必须是有限数字";
|
|
134
|
+
const POSITIVE_INTEGER = (value) => Number.isSafeInteger(value) && value > 0 ? void 0 : "必须是正整数";
|
|
135
|
+
/** 类型、校验、卡片归属、草稿默认值和组合行映射的唯一运行时目录。 */
|
|
136
|
+
const ENGINE_PARAM_DEFINITIONS = {
|
|
80
137
|
modelProvider: {
|
|
81
138
|
kind: "string",
|
|
82
139
|
defaultValue: "",
|
|
83
|
-
card: "main-model"
|
|
140
|
+
card: "main-model",
|
|
141
|
+
storageLayer: "agent-request"
|
|
84
142
|
},
|
|
85
143
|
modelName: {
|
|
86
144
|
kind: "string",
|
|
87
145
|
defaultValue: "",
|
|
88
|
-
card: "main-model"
|
|
146
|
+
card: "main-model",
|
|
147
|
+
storageLayer: "agent-request"
|
|
89
148
|
},
|
|
90
149
|
subagentModelProvider: {
|
|
91
150
|
kind: "string",
|
|
92
151
|
defaultValue: "",
|
|
93
|
-
card: "subagent-model"
|
|
152
|
+
card: "subagent-model",
|
|
153
|
+
storageLayer: "subagent-start"
|
|
94
154
|
},
|
|
95
155
|
subagentModelName: {
|
|
96
156
|
kind: "string",
|
|
97
157
|
defaultValue: "",
|
|
98
|
-
card: "subagent-model"
|
|
158
|
+
card: "subagent-model",
|
|
159
|
+
storageLayer: "subagent-start"
|
|
99
160
|
},
|
|
100
161
|
modelReasoningEffort: {
|
|
101
162
|
kind: "string",
|
|
102
163
|
defaultValue: "",
|
|
103
|
-
card: "main-model"
|
|
164
|
+
card: "main-model",
|
|
165
|
+
storageLayer: "agent-request"
|
|
104
166
|
},
|
|
105
167
|
modelTemperature: {
|
|
106
168
|
kind: "number",
|
|
107
169
|
check: FINITE_NUMBER,
|
|
108
170
|
defaultValue: "",
|
|
109
|
-
card: "main-model"
|
|
171
|
+
card: "main-model",
|
|
172
|
+
storageLayer: "agent-request"
|
|
110
173
|
},
|
|
111
174
|
modelMaxTokens: {
|
|
112
175
|
kind: "number",
|
|
113
176
|
check: POSITIVE_INTEGER,
|
|
114
177
|
defaultValue: "",
|
|
115
|
-
card: "main-model"
|
|
178
|
+
card: "main-model",
|
|
179
|
+
storageLayer: "agent-request"
|
|
116
180
|
},
|
|
117
181
|
subagentReasoningEffort: {
|
|
118
182
|
kind: "string",
|
|
119
183
|
defaultValue: "",
|
|
120
|
-
card: "subagent-model"
|
|
184
|
+
card: "subagent-model",
|
|
185
|
+
storageLayer: "subagent-start"
|
|
121
186
|
},
|
|
122
187
|
subagentTemperature: {
|
|
123
188
|
kind: "number",
|
|
124
189
|
check: FINITE_NUMBER,
|
|
125
190
|
defaultValue: "",
|
|
126
|
-
card: "subagent-model"
|
|
191
|
+
card: "subagent-model",
|
|
192
|
+
storageLayer: "subagent-start"
|
|
127
193
|
},
|
|
128
194
|
subagentMaxTokens: {
|
|
129
195
|
kind: "number",
|
|
130
196
|
check: POSITIVE_INTEGER,
|
|
131
197
|
defaultValue: "",
|
|
132
|
-
card: "subagent-model"
|
|
198
|
+
card: "subagent-model",
|
|
199
|
+
storageLayer: "subagent-start"
|
|
133
200
|
},
|
|
134
201
|
maxDepth: {
|
|
135
202
|
kind: "max-depth",
|
|
136
203
|
defaultValue: "",
|
|
137
|
-
card: "subagent-tools"
|
|
138
|
-
|
|
139
|
-
firstTurnWord: {
|
|
140
|
-
kind: "string",
|
|
141
|
-
defaultValue: "",
|
|
142
|
-
card: "prompt-defaults"
|
|
143
|
-
},
|
|
144
|
-
buildPattern: {
|
|
145
|
-
kind: "pattern",
|
|
146
|
-
defaultValue: "",
|
|
147
|
-
card: "prompt-defaults"
|
|
148
|
-
},
|
|
149
|
-
complexPattern: {
|
|
150
|
-
kind: "pattern",
|
|
151
|
-
defaultValue: "",
|
|
152
|
-
card: "prompt-defaults"
|
|
153
|
-
},
|
|
154
|
-
firstTurnBuild: {
|
|
155
|
-
kind: "string",
|
|
156
|
-
defaultValue: "",
|
|
157
|
-
card: "prompt-defaults"
|
|
158
|
-
},
|
|
159
|
-
firstTurnInspect: {
|
|
160
|
-
kind: "string",
|
|
161
|
-
defaultValue: "",
|
|
162
|
-
card: "prompt-defaults"
|
|
163
|
-
},
|
|
164
|
-
firstTurnDeep: {
|
|
165
|
-
kind: "string",
|
|
166
|
-
defaultValue: "",
|
|
167
|
-
card: "prompt-defaults"
|
|
168
|
-
},
|
|
169
|
-
guideWeak: {
|
|
170
|
-
kind: "string",
|
|
171
|
-
defaultValue: "",
|
|
172
|
-
card: "prompt-defaults"
|
|
173
|
-
},
|
|
174
|
-
guideDeep: {
|
|
175
|
-
kind: "string",
|
|
176
|
-
defaultValue: "",
|
|
177
|
-
card: "prompt-defaults"
|
|
204
|
+
card: "subagent-tools",
|
|
205
|
+
storageLayer: "subagent-start"
|
|
178
206
|
},
|
|
179
207
|
instructionHint: {
|
|
180
208
|
kind: "boolean",
|
|
181
209
|
defaultValue: false,
|
|
182
210
|
card: "prompt-defaults",
|
|
211
|
+
storageLayer: "pre-step",
|
|
183
212
|
module: {
|
|
184
213
|
row: "instruction-hint",
|
|
185
214
|
key: "enabled"
|
|
186
215
|
}
|
|
187
216
|
},
|
|
188
|
-
strReplaceEditorMaxOutputChars: {
|
|
189
|
-
kind: "number",
|
|
190
|
-
check: POSITIVE_INTEGER,
|
|
191
|
-
defaultValue: 16e3,
|
|
192
|
-
card: "str-replace-editor",
|
|
193
|
-
module: {
|
|
194
|
-
row: "str-replace-editor",
|
|
195
|
-
key: "maxOutputChars",
|
|
196
|
-
mode: "editor-default"
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
217
|
toolGitBashEnabled: {
|
|
200
218
|
kind: "boolean",
|
|
201
219
|
defaultValue: true,
|
|
202
220
|
card: "tool-git-bash",
|
|
221
|
+
storageLayer: "tool-pipeline",
|
|
203
222
|
module: {
|
|
204
223
|
row: "tool-git-bash",
|
|
205
224
|
key: "enabled"
|
|
@@ -216,6 +235,7 @@ const ENGINE_PARAM_DEFINITIONS = {
|
|
|
216
235
|
],
|
|
217
236
|
defaultValue: "",
|
|
218
237
|
card: "tool-config-engine",
|
|
238
|
+
storageLayer: "tool-pipeline",
|
|
219
239
|
module: {
|
|
220
240
|
row: "tool-config-engine",
|
|
221
241
|
key: "requireApproval"
|
|
@@ -224,7 +244,7 @@ const ENGINE_PARAM_DEFINITIONS = {
|
|
|
224
244
|
};
|
|
225
245
|
const ENGINE_PARAM_KEYS = Object.keys(ENGINE_PARAM_DEFINITIONS);
|
|
226
246
|
/** writePreset.runtimeOf 实际透传键:全部引擎参数可直接进入 writer。 */
|
|
227
|
-
const WRITER_PARAM_KEYS = ENGINE_PARAM_KEYS;
|
|
247
|
+
const WRITER_PARAM_KEYS = [...ENGINE_PARAM_KEYS, ...LEGACY_PROMPT_PARAM_KEYS];
|
|
228
248
|
/** 已有逗号分隔/flow-array 语义,client 与生成器共享。 */
|
|
229
249
|
function engineParamList(value) {
|
|
230
250
|
if (Array.isArray(value)) return value.map(String).filter((item) => item.length > 0);
|
|
@@ -240,10 +260,6 @@ function buildEngineModuleParams(params) {
|
|
|
240
260
|
const binding = definition.module;
|
|
241
261
|
if (binding === void 0) continue;
|
|
242
262
|
let value = params[key];
|
|
243
|
-
if (binding.mode === "editor-default") {
|
|
244
|
-
if (typeof value === "string" && value.trim().length > 0) value = Number(value);
|
|
245
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) continue;
|
|
246
|
-
}
|
|
247
263
|
if (value === void 0 || value === null) continue;
|
|
248
264
|
if (definition.kind === "boolean") value = value === true;
|
|
249
265
|
else if (definition.kind === "string-list") value = engineParamList(value);
|
|
@@ -321,11 +337,11 @@ function validateEngineParamValues(overrides) {
|
|
|
321
337
|
const errors = [];
|
|
322
338
|
for (const [key, value] of Object.entries(overrides)) {
|
|
323
339
|
if (value === void 0 || value === null) continue;
|
|
324
|
-
const rule = Object.hasOwn(ENGINE_PARAM_DEFINITIONS, key) ? ENGINE_PARAM_DEFINITIONS[key] : void 0;
|
|
340
|
+
const rule = Object.hasOwn(ENGINE_PARAM_DEFINITIONS, key) ? ENGINE_PARAM_DEFINITIONS[key] : isLegacyPromptParam(key) ? LEGACY_PROMPT_PARAM_DEFINITIONS[key] : void 0;
|
|
325
341
|
if (rule === void 0) {
|
|
326
342
|
errors.push({
|
|
327
343
|
key,
|
|
328
|
-
message: `${key}:
|
|
344
|
+
message: `${key}: 未知参数键,请按当前参数定义更新模块`
|
|
329
345
|
});
|
|
330
346
|
continue;
|
|
331
347
|
}
|
|
@@ -405,12 +421,6 @@ const ENGINE_CAPABILITIES = [
|
|
|
405
421
|
}
|
|
406
422
|
}
|
|
407
423
|
},
|
|
408
|
-
{
|
|
409
|
-
id: "str-replace-editor",
|
|
410
|
-
moduleKeys: ["filesystem-editor"],
|
|
411
|
-
rowIds: ["str-replace-editor"],
|
|
412
|
-
displayLayer: "tool-pipeline"
|
|
413
|
-
},
|
|
414
424
|
{
|
|
415
425
|
id: "tool-config-engine",
|
|
416
426
|
moduleKeys: ["tool-config-engine"],
|
|
@@ -426,7 +436,7 @@ const ENGINE_CAPABILITIES = [
|
|
|
426
436
|
];
|
|
427
437
|
/**
|
|
428
438
|
* 非能力编辑组的主归属。能力组不在这里重复登记:它们的 displayLayer 由能力定义本身承载
|
|
429
|
-
*
|
|
439
|
+
* (例如工具批准策略的 card 就是能力 id `tool-config-engine`,已随能力登记)。
|
|
430
440
|
*/
|
|
431
441
|
const ENGINE_EDITOR_GROUPS = [
|
|
432
442
|
{
|
|
@@ -452,9 +462,8 @@ const ENGINE_EDITOR_GROUPS = [
|
|
|
452
462
|
},
|
|
453
463
|
{
|
|
454
464
|
id: "subagent-model",
|
|
455
|
-
displayLayer: "
|
|
456
|
-
|
|
457
|
-
hook: "subagent-start"
|
|
465
|
+
displayLayer: "agent-request",
|
|
466
|
+
hook: "agent-request"
|
|
458
467
|
},
|
|
459
468
|
{
|
|
460
469
|
id: "subagent-tools",
|
|
@@ -493,10 +502,10 @@ function engineRecipe(id) {
|
|
|
493
502
|
return ENGINE_RECIPES.find((recipe) => recipe.id === id);
|
|
494
503
|
}
|
|
495
504
|
/**
|
|
496
|
-
*
|
|
505
|
+
* 模块里显式写了的引擎参数 / 行配置所隐含的模块:**参数在 ⇒ 装配在**。
|
|
497
506
|
*
|
|
498
507
|
* 只认显式声明(`params` 的登记参数键与 `moduleConfigs` 的行键),组合源自带的默认值不算——
|
|
499
|
-
*
|
|
508
|
+
* 否则任何模块都会把全部能力装回来。返回能力拥有的模块 id(去重,顺序稳定)。
|
|
500
509
|
* 装配入口、模块事实与"移除能力"三处共用这一份派生,避免各自判断漂移。
|
|
501
510
|
*/
|
|
502
511
|
function impliedModulesForParams(params, moduleConfigs) {
|
|
@@ -519,7 +528,7 @@ function impliedModulesForParams(params, moduleConfigs) {
|
|
|
519
528
|
if (moduleConfigs !== void 0 && moduleConfigs !== null) for (const rowId of Object.keys(moduleConfigs)) addRow(rowId);
|
|
520
529
|
return [...implied];
|
|
521
530
|
}
|
|
522
|
-
/**
|
|
531
|
+
/** 显式模块声明中的实际能力;包含当前参数与顶层策略所需的装配。
|
|
523
532
|
* 创建补声明由 host 单独检查 declaredModules;官方组合行不伪装成可编辑能力。 */
|
|
524
533
|
function isEngineCapabilityPresent(id, facts) {
|
|
525
534
|
if (facts === void 0 || facts.sourceMode !== "explicit") return false;
|
|
@@ -582,10 +591,15 @@ function personaRowConfig(persona) {
|
|
|
582
591
|
}
|
|
583
592
|
//#endregion
|
|
584
593
|
//#region src/shared/preset-ids.ts
|
|
585
|
-
/**
|
|
586
|
-
|
|
594
|
+
/**
|
|
595
|
+
* 包内模块与用户副本共用同名 ID;避免与宿主官方预设重名。
|
|
596
|
+
*
|
|
597
|
+
* 内置模块随包分发在仓库根的 `modules/`。旧的内置预设 `pt-*` 已整体退场——它们是官方
|
|
598
|
+
* 预设形态的遗留,重构后不再读取也不再维护;这里指向当前随包分发的内置模块作为兜底。
|
|
599
|
+
*/
|
|
600
|
+
const DEFAULT_MODULE_ID = "ponytail";
|
|
587
601
|
//#endregion
|
|
588
|
-
//#region src/host/
|
|
602
|
+
//#region src/host/module-install.ts
|
|
589
603
|
function assertPresetId(id) {
|
|
590
604
|
if (typeof id !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(id)) throw new Error(`非法预设 id:${String(id)};必须为小写字母、数字和连字符`);
|
|
591
605
|
}
|
|
@@ -624,7 +638,7 @@ function canonicalPresetRoot(root, allowMissing = false) {
|
|
|
624
638
|
return realpathSync(absoluteRoot);
|
|
625
639
|
}
|
|
626
640
|
/** 根与目标先解析真实路径,拒绝链接目录及定义身份不一致。 */
|
|
627
|
-
function
|
|
641
|
+
function assertModuleDirectory(root, id, allowMissing = false) {
|
|
628
642
|
assertPresetId(id);
|
|
629
643
|
const canonicalRoot = canonicalPresetRoot(root, allowMissing);
|
|
630
644
|
const target = join(canonicalRoot, id);
|
|
@@ -635,7 +649,7 @@ function assertPresetDirectory(root, id, allowMissing = false) {
|
|
|
635
649
|
const stat = lstatSync(target);
|
|
636
650
|
if (stat.isSymbolicLink() || !stat.isDirectory() || dirname(realpathSync(target)) !== canonicalRoot) throw new Error(`预设路径包含链接或越界:${id}`);
|
|
637
651
|
if (!readdirSync(canonicalRoot).includes(id)) throw new Error(`预设目录身份不匹配:${id}`);
|
|
638
|
-
const definition = join(target,
|
|
652
|
+
const definition = join(target, MODULE_DEFINITION_FILE);
|
|
639
653
|
const fileStat = lstatSync(definition);
|
|
640
654
|
if (fileStat.isSymbolicLink() || !fileStat.isFile()) throw new Error(`预设定义不是普通文件:${id}`);
|
|
641
655
|
const doc = parseDocument(readFileSync(definition, "utf8"), { logLevel: "silent" });
|
|
@@ -676,7 +690,7 @@ function engineModuleFileNames(dir) {
|
|
|
676
690
|
const ENGINE_MANAGED_PATHS = {
|
|
677
691
|
"prompt-config-engine.mjs": {
|
|
678
692
|
field: "configsDir",
|
|
679
|
-
directory:
|
|
693
|
+
directory: MODULE_CONFIGS_DIR
|
|
680
694
|
},
|
|
681
695
|
"tool-config-engine.mjs": {
|
|
682
696
|
field: "configsDir",
|
|
@@ -752,12 +766,10 @@ function rewritePresetEngineReferences(raw, outputId, engineFiles) {
|
|
|
752
766
|
return changed ? doc.toString() : raw;
|
|
753
767
|
}
|
|
754
768
|
//#endregion
|
|
755
|
-
//#region src/host/
|
|
756
|
-
/**
|
|
769
|
+
//#region src/host/module-layer-settings.ts
|
|
770
|
+
/** 模块磁盘参数:按稳定存储层归属,运行时仍使用 EngineParams 平铺接口。 */
|
|
757
771
|
const ENGINE_PARAM_LAYERS = Object.fromEntries(ENGINE_PARAM_KEYS.map((key) => {
|
|
758
|
-
|
|
759
|
-
if (layer === void 0) throw new Error(`引擎参数未登记所属层:${key}`);
|
|
760
|
-
return [key, layer];
|
|
772
|
+
return [key, ENGINE_PARAM_DEFINITIONS[key].storageLayer];
|
|
761
773
|
}));
|
|
762
774
|
var PresetLayerSettingsError = class extends Error {
|
|
763
775
|
code = "preset-layer-settings-invalid";
|
|
@@ -766,7 +778,7 @@ var PresetLayerSettingsError = class extends Error {
|
|
|
766
778
|
this.name = "PresetLayerSettingsError";
|
|
767
779
|
}
|
|
768
780
|
};
|
|
769
|
-
const isRecord$
|
|
781
|
+
const isRecord$4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
770
782
|
function engineParamPath(key) {
|
|
771
783
|
if (!Object.prototype.hasOwnProperty.call(ENGINE_PARAM_LAYERS, key)) throw new Error(`未知引擎参数:${key}`);
|
|
772
784
|
return [
|
|
@@ -781,11 +793,11 @@ function readLayerSettings(value) {
|
|
|
781
793
|
throw new PresetLayerSettingsError(message);
|
|
782
794
|
};
|
|
783
795
|
if (value === void 0) return {};
|
|
784
|
-
if (!isRecord$
|
|
796
|
+
if (!isRecord$4(value)) return invalid("layerSettings 必须是对象");
|
|
785
797
|
const params = {};
|
|
786
798
|
for (const [layer, settings] of Object.entries(value)) {
|
|
787
799
|
if (!ENGINE_LAYER_ORDER.includes(layer)) invalid(`未知插入点:layerSettings.${layer}`);
|
|
788
|
-
if (!isRecord$
|
|
800
|
+
if (!isRecord$4(settings)) return invalid(`layerSettings.${layer} 必须是对象`);
|
|
789
801
|
for (const [key, entry] of Object.entries(settings)) {
|
|
790
802
|
if (!Object.prototype.hasOwnProperty.call(ENGINE_PARAM_LAYERS, key)) continue;
|
|
791
803
|
const expected = ENGINE_PARAM_LAYERS[key];
|
|
@@ -797,7 +809,7 @@ function readLayerSettings(value) {
|
|
|
797
809
|
}
|
|
798
810
|
/** 仅消费当前 layerSettings;其余段作为未知字段留存,不投影为运行时参数。 */
|
|
799
811
|
function readPresetLayerSettings(source) {
|
|
800
|
-
if (!isRecord$
|
|
812
|
+
if (!isRecord$4(source)) throw new PresetLayerSettingsError("preset.yml 必须是对象");
|
|
801
813
|
return readLayerSettings(source.layerSettings);
|
|
802
814
|
}
|
|
803
815
|
//#endregion
|
|
@@ -7990,6 +8002,22 @@ function getSessionVar(session, key) {
|
|
|
7990
8002
|
return vars === void 0 || !Object.hasOwn(vars, String(key)) ? void 0 : vars[String(key)];
|
|
7991
8003
|
}
|
|
7992
8004
|
//#endregion
|
|
8005
|
+
//#region engine/order.mjs
|
|
8006
|
+
/** 加载器私有元数据:不进入 YAML、公共配置或客户端载荷。 */
|
|
8007
|
+
const FILE_SEQUENCE = Symbol("prompt-tool:file-sequence");
|
|
8008
|
+
const compareText = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
8009
|
+
/** 模块配置按持久序号执行;无物化来源的独立调用保留原 order 语义。 */
|
|
8010
|
+
function compareConfigSequence(a, b) {
|
|
8011
|
+
const difference = (a.sequence ?? a.order ?? 0) - (b.sequence ?? b.order ?? 0);
|
|
8012
|
+
if (difference !== 0) return difference;
|
|
8013
|
+
if (a.sourceModuleId === void 0 && b.sourceModuleId === void 0) return 0;
|
|
8014
|
+
return compareText(a.sourceModuleId ?? "", b.sourceModuleId ?? "") || compareText(a.id, b.id);
|
|
8015
|
+
}
|
|
8016
|
+
/** 官方文本段的定位仍由 order 决定;序号只提供同位置的稳定顺序。 */
|
|
8017
|
+
function compareTextPlacement(a, b) {
|
|
8018
|
+
return a.order - b.order || compareConfigSequence(a, b);
|
|
8019
|
+
}
|
|
8020
|
+
//#endregion
|
|
7993
8021
|
//#region engine/st-render.mjs
|
|
7994
8022
|
/** ST 模板变量帧:只求值显式导入的模板,不决定任何官方插入点的执行顺序。 */
|
|
7995
8023
|
function visible(config, agent) {
|
|
@@ -8098,7 +8126,7 @@ function attachStRenderers(configs) {
|
|
|
8098
8126
|
frame.evaluate = evaluate;
|
|
8099
8127
|
if (session) sessions.set(session, frame);
|
|
8100
8128
|
}
|
|
8101
|
-
for (const config of templates.filter((config) => approved(config) && config.strategy === "static" && config.dedupe === "none" && !frame.text.has(config)).sort(
|
|
8129
|
+
if (target.sequence === void 0 || eligible === void 0) for (const config of templates.filter((config) => approved(config) && config.strategy === "static" && config.dedupe === "none" && !frame.text.has(config)).sort(compareConfigSequence)) frame.evaluate(config);
|
|
8102
8130
|
if (token && typeof token === "object") tokens.set(token, frame);
|
|
8103
8131
|
if (!frame.text.has(target)) frame.evaluate(target);
|
|
8104
8132
|
return frame.text.get(target) ?? "";
|
|
@@ -8154,6 +8182,15 @@ function loadTemplate(file, baseUrl = import.meta.url, presetRootUrl) {
|
|
|
8154
8182
|
return { text: raw };
|
|
8155
8183
|
}
|
|
8156
8184
|
/**
|
|
8185
|
+
* 完整 YAML 解析(vendored yaml 包):提示词配置文件直接使用标准 YAML。
|
|
8186
|
+
* 支持缩进 map、列表、block scalar、行尾注释、引号转义等全部语法。
|
|
8187
|
+
*/
|
|
8188
|
+
function parsePromptConfigYaml(raw) {
|
|
8189
|
+
const parsed = parse$1(raw);
|
|
8190
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new TypeError(`${name$2}: prompt config yaml must contain a single object`);
|
|
8191
|
+
return parsed;
|
|
8192
|
+
}
|
|
8193
|
+
/**
|
|
8157
8194
|
* 枚举提示词配置候选文件:**两条加载路径共用同一份枚举规则**。
|
|
8158
8195
|
*
|
|
8159
8196
|
* 扩展名、排序与跳过名单必须两侧一致——否则 host(编辑/列举)与引擎(注入)会看到不同的
|
|
@@ -8169,6 +8206,44 @@ function loadTemplate(file, baseUrl = import.meta.url, presetRootUrl) {
|
|
|
8169
8206
|
function promptConfigFileNames(entries) {
|
|
8170
8207
|
return entries.filter((entry) => entry.isFile() && /\.(ya?ml|json)$/i.test(entry.name)).sort((a, b) => a.name.localeCompare(b.name)).map((entry) => entry.name).filter((fileName) => fileName !== "variables.yml");
|
|
8171
8208
|
}
|
|
8209
|
+
/**
|
|
8210
|
+
* 从提示词配置模块目录加载全部提示词配置描述:按文件名排序扫描 *.yml / *.yaml / *.json。
|
|
8211
|
+
* 文件名用数字前缀表达引擎执行顺序(00-…、10-…)。
|
|
8212
|
+
*/
|
|
8213
|
+
function loadPromptConfigFiles(dirUrl) {
|
|
8214
|
+
let entries;
|
|
8215
|
+
try {
|
|
8216
|
+
entries = readdirSync(dirUrl, { withFileTypes: true });
|
|
8217
|
+
} catch (error) {
|
|
8218
|
+
throw new TypeError(`${name$2}: configsDir ${String(dirUrl)} is not readable: ${String(error?.message ?? error)}`);
|
|
8219
|
+
}
|
|
8220
|
+
let presetVariables = {};
|
|
8221
|
+
try {
|
|
8222
|
+
const parsed = parse$1(readFileSync(new URL("variables.yml", dirUrl), "utf8"));
|
|
8223
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) presetVariables = parsed;
|
|
8224
|
+
} catch {}
|
|
8225
|
+
const specs = [];
|
|
8226
|
+
for (const fileName of promptConfigFileNames(entries)) {
|
|
8227
|
+
const raw = readFileSync(new URL(fileName, dirUrl), "utf8");
|
|
8228
|
+
const spec = /\.json$/i.test(fileName) ? JSON.parse(raw) : parsePromptConfigYaml(raw);
|
|
8229
|
+
const prefix = /^(\d+)-/.exec(fileName);
|
|
8230
|
+
if (prefix !== null && spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
|
|
8231
|
+
const sequence = Number(prefix[1]);
|
|
8232
|
+
if (!Number.isSafeInteger(sequence)) throw new TypeError(`${name$2}: unsafe config sequence in ${fileName}`);
|
|
8233
|
+
Object.defineProperty(spec, FILE_SEQUENCE, { value: sequence });
|
|
8234
|
+
}
|
|
8235
|
+
specs.push(spec);
|
|
8236
|
+
}
|
|
8237
|
+
for (const spec of specs) {
|
|
8238
|
+
if (spec === null || typeof spec !== "object" || Array.isArray(spec)) continue;
|
|
8239
|
+
const own = spec.variables !== null && typeof spec.variables === "object" && !Array.isArray(spec.variables) ? spec.variables : {};
|
|
8240
|
+
spec.variables = {
|
|
8241
|
+
...presetVariables,
|
|
8242
|
+
...own
|
|
8243
|
+
};
|
|
8244
|
+
}
|
|
8245
|
+
return specs;
|
|
8246
|
+
}
|
|
8172
8247
|
const KNOWN_STRATEGIES = /* @__PURE__ */ new Set([
|
|
8173
8248
|
"static",
|
|
8174
8249
|
"placeholder",
|
|
@@ -8860,6 +8935,9 @@ function createPromptConfigs(specs, options = {}) {
|
|
|
8860
8935
|
templatePatch,
|
|
8861
8936
|
params
|
|
8862
8937
|
};
|
|
8938
|
+
const sequence = (Object.hasOwn(options.configOrder ?? {}, spec.id) ? options.configOrder[spec.id] : void 0) ?? spec[FILE_SEQUENCE] ?? (typeof options.sourceModuleId === "string" ? index * 10 : void 0);
|
|
8939
|
+
if (sequence !== void 0) config.sequence = sequence;
|
|
8940
|
+
if (typeof options.sourceModuleId === "string") config.sourceModuleId = options.sourceModuleId;
|
|
8863
8941
|
config.matchScan = match === void 0 ? void 0 : createAnchorMatcher(match).scan;
|
|
8864
8942
|
config.resolve = bindResolver(config, options.strategyDir);
|
|
8865
8943
|
return config;
|
|
@@ -8867,6 +8945,7 @@ function createPromptConfigs(specs, options = {}) {
|
|
|
8867
8945
|
config,
|
|
8868
8946
|
fileOrder
|
|
8869
8947
|
})).sort((a, b) => {
|
|
8948
|
+
if (a.config.sequence !== void 0 || b.config.sequence !== void 0) return compareConfigSequence(a.config, b.config) || a.fileOrder - b.fileOrder;
|
|
8870
8949
|
const aAnchor = a.config.configKind === "anchor" ? 0 : 1;
|
|
8871
8950
|
const bAnchor = b.config.configKind === "anchor" ? 0 : 1;
|
|
8872
8951
|
if (aAnchor !== bAnchor) return aAnchor - bAnchor;
|
|
@@ -8933,6 +9012,10 @@ function modelRequestConfigs(params) {
|
|
|
8933
9012
|
params: { patch: mainPatch }
|
|
8934
9013
|
});
|
|
8935
9014
|
const subagentPatch = patchOf("subagent");
|
|
9015
|
+
if (typeof params.subagentModelProvider === "string" && params.subagentModelProvider.trim().length > 0 && typeof params.subagentModelName === "string" && params.subagentModelName.trim().length > 0) {
|
|
9016
|
+
subagentPatch.provider = params.subagentModelProvider.trim();
|
|
9017
|
+
subagentPatch.model = params.subagentModelName.trim();
|
|
9018
|
+
}
|
|
8936
9019
|
if (Object.keys(subagentPatch).length > 0) configs.push({
|
|
8937
9020
|
id: "subagent-model-params",
|
|
8938
9021
|
name: "模型参数(子代理)",
|
|
@@ -9079,6 +9162,112 @@ function listPromptConfigSpecs(dir) {
|
|
|
9079
9162
|
return specs;
|
|
9080
9163
|
}
|
|
9081
9164
|
//#endregion
|
|
9165
|
+
//#region src/host/legacy-prompt-params.ts
|
|
9166
|
+
/** 旧层参数到规则实例的唯一兼容投影;读取、导入物化与旧 API 保存共用。 */
|
|
9167
|
+
const isRecord$3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9168
|
+
const text = (value) => value === void 0 || value === null ? "" : String(value);
|
|
9169
|
+
function readLegacyPromptParams(source) {
|
|
9170
|
+
const params = {};
|
|
9171
|
+
if (!isRecord$3(source.layerSettings)) return params;
|
|
9172
|
+
for (const [layer, settings] of Object.entries(source.layerSettings)) {
|
|
9173
|
+
if (!isRecord$3(settings)) continue;
|
|
9174
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
9175
|
+
if (!isLegacyPromptParam(key)) continue;
|
|
9176
|
+
if (layer !== "pre-step") throw new PresetLayerSettingsError(`${key} 必须位于 layerSettings.pre-step`);
|
|
9177
|
+
params[key] = value === "on" ? true : value === "off" ? false : value;
|
|
9178
|
+
}
|
|
9179
|
+
}
|
|
9180
|
+
return params;
|
|
9181
|
+
}
|
|
9182
|
+
function legacyBody(source, moduleDir, supplied) {
|
|
9183
|
+
if (supplied !== void 0) return supplied;
|
|
9184
|
+
if (moduleDir !== void 0) try {
|
|
9185
|
+
const file = join(moduleDir, "preset.md");
|
|
9186
|
+
if (lstatSync(file).isSymbolicLink()) throw new Error(`旧模块正文不能是链接:${file}`);
|
|
9187
|
+
return readFileSync(file, "utf8");
|
|
9188
|
+
} catch (error) {
|
|
9189
|
+
if (error.code !== "ENOENT") throw error;
|
|
9190
|
+
}
|
|
9191
|
+
return text(source.content?.presetText);
|
|
9192
|
+
}
|
|
9193
|
+
function resolveLegacyPromptConfigs(source, options = {}) {
|
|
9194
|
+
const params = readLegacyPromptParams(source);
|
|
9195
|
+
for (const [key, value] of Object.entries(options.overrides ?? {})) if (isLegacyPromptParam(key) && value !== void 0 && value !== null) params[key] = value;
|
|
9196
|
+
const keys = LEGACY_PROMPT_PARAM_KEYS.filter((key) => Object.hasOwn(params, key));
|
|
9197
|
+
const applies = (id) => keys.some((key) => LEGACY_PROMPT_PARAM_DEFINITIONS[key].targets.includes(id));
|
|
9198
|
+
const provided = (key) => options.partial !== true || Object.hasOwn(params, key);
|
|
9199
|
+
const projected = (source.promptConfigs ?? []).map((config) => {
|
|
9200
|
+
if (!isRecord$3(config) || !applies(config.id)) return config;
|
|
9201
|
+
const next = {
|
|
9202
|
+
...config,
|
|
9203
|
+
params: { ...config.params }
|
|
9204
|
+
};
|
|
9205
|
+
if (config.id === "near-anchor") {
|
|
9206
|
+
if (provided("firstTurnAnchor")) next.enabled = params.firstTurnAnchor === true;
|
|
9207
|
+
if (provided("firstTurnCustom")) next.params.useCustom = params.firstTurnCustom === true;
|
|
9208
|
+
if (provided("firstTurnText")) next.params.text = text(params.firstTurnText);
|
|
9209
|
+
for (const key of [
|
|
9210
|
+
"buildPattern",
|
|
9211
|
+
"complexPattern",
|
|
9212
|
+
"firstTurnBuild",
|
|
9213
|
+
"firstTurnInspect",
|
|
9214
|
+
"firstTurnDeep"
|
|
9215
|
+
]) if (provided(key)) next.params[key] = text(params[key]);
|
|
9216
|
+
} else if (config.id === "router-guide") {
|
|
9217
|
+
if (provided("guideEnabled")) next.enabled = params.guideEnabled === true;
|
|
9218
|
+
if (provided("guideCustom") || provided("guideEnabled")) {
|
|
9219
|
+
const useCustom = provided("guideCustom") ? params.guideCustom === true : next.params.useCustom === true;
|
|
9220
|
+
next.params.useCustom = useCustom;
|
|
9221
|
+
next.modelScope = useCustom ? "all" : "flash";
|
|
9222
|
+
}
|
|
9223
|
+
if (provided("guideText")) next.params.text = text(params.guideText);
|
|
9224
|
+
for (const key of [
|
|
9225
|
+
"complexPattern",
|
|
9226
|
+
"guideWeak",
|
|
9227
|
+
"guideDeep"
|
|
9228
|
+
]) if (provided(key)) next.params[key] = text(params[key]);
|
|
9229
|
+
}
|
|
9230
|
+
return next;
|
|
9231
|
+
});
|
|
9232
|
+
const configs = mergePromptConfigs(projected, options.promptConfigs).map((config) => {
|
|
9233
|
+
if (config.id !== "prompt-injector" || !applies(config.id)) return config;
|
|
9234
|
+
const body = legacyBody(source, options.moduleDir, options.prompt);
|
|
9235
|
+
const explicitWord = text(options.partial === true && Object.hasOwn(params, "firstTurnWord") ? params.firstTurnWord : config.params?.firstTurnWord ?? params.firstTurnWord);
|
|
9236
|
+
const anchor = projected.find((entry) => entry.id === "near-anchor");
|
|
9237
|
+
const signalWord = (value) => {
|
|
9238
|
+
const sentence = /the exact sentence:\s*([A-Za-z]+)/i.exec(value);
|
|
9239
|
+
const first = /^\P{L}*([\p{L}]+)/u.exec(value.trim());
|
|
9240
|
+
return (sentence?.[1] ?? first?.[1])?.toLowerCase();
|
|
9241
|
+
};
|
|
9242
|
+
const derivedWords = [...new Set([
|
|
9243
|
+
"firstTurnText",
|
|
9244
|
+
"firstTurnBuild",
|
|
9245
|
+
"firstTurnInspect",
|
|
9246
|
+
"firstTurnDeep"
|
|
9247
|
+
].map((key) => signalWord(text(Object.hasOwn(params, key) || options.partial !== true ? params[key] : anchor?.params?.[key === "firstTurnText" ? "text" : key]))).filter((word) => word !== void 0))];
|
|
9248
|
+
const { text: _text, texts: _texts, ...rest } = config;
|
|
9249
|
+
return {
|
|
9250
|
+
...rest,
|
|
9251
|
+
enabled: body.trim().length > 0 && (provided("injectPrompt") ? params.injectPrompt !== false : config.enabled !== false),
|
|
9252
|
+
params: {
|
|
9253
|
+
...config.params,
|
|
9254
|
+
text: body,
|
|
9255
|
+
firstTurnWord: explicitWord,
|
|
9256
|
+
anchorWords: explicitWord.length > 0 ? [explicitWord] : derivedWords
|
|
9257
|
+
}
|
|
9258
|
+
};
|
|
9259
|
+
});
|
|
9260
|
+
const ids = new Set(configs.map((config) => config.id));
|
|
9261
|
+
const consumedKeys = keys.filter((key) => LEGACY_PROMPT_PARAM_DEFINITIONS[key].targets.some((id) => ids.has(id)));
|
|
9262
|
+
const unhandled = keys.filter((key) => !consumedKeys.includes(key));
|
|
9263
|
+
return {
|
|
9264
|
+
configs,
|
|
9265
|
+
consumedKeys,
|
|
9266
|
+
active: keys.length > 0,
|
|
9267
|
+
warnings: unhandled.length === 0 ? [] : [`旧规则参数没有承接配置,原值保留:${unhandled.join(", ")}`]
|
|
9268
|
+
};
|
|
9269
|
+
}
|
|
9270
|
+
//#endregion
|
|
9082
9271
|
//#region src/host/text-file.ts
|
|
9083
9272
|
/** 共用原子文本替换;来源授权与内容合法性由各领域调用方负责。 */
|
|
9084
9273
|
function atomicWriteTextFile(file, content, options = {}) {
|
|
@@ -9099,24 +9288,38 @@ function atomicWriteTextFile(file, content, options = {}) {
|
|
|
9099
9288
|
//#endregion
|
|
9100
9289
|
//#region src/host/manifest.ts
|
|
9101
9290
|
/**
|
|
9102
|
-
* manifest —
|
|
9291
|
+
* manifest — 模块模板单一参数 YAML(module.yml)的加载与引擎参数解析。
|
|
9103
9292
|
*
|
|
9104
|
-
*
|
|
9293
|
+
* 一个模块 = 一个 module.yml:
|
|
9105
9294
|
* - modules/layerSettings/content/meta 全部是直读参数,无模板语法;
|
|
9106
9295
|
* - layerSettings 展平为内部 params,供引擎生成默认提示词配置,promptConfigs 仅为可选覆盖;
|
|
9107
9296
|
* - 组合模块的行级 config 由参数桥 buildModuleConfigsFromParams 按 params
|
|
9108
9297
|
* 构造对象合并(取代旧 __TOKEN__ 文本渲染,无占位符、无文本往返),
|
|
9109
9298
|
* params(UI/基础层)优先于 moduleConfigs 行级直写(旧作者锁定语义已移除)。
|
|
9110
|
-
*
|
|
9299
|
+
* 本模块负责参数归一化与引擎模块配置装配;所有模块专属行为都在引擎内部。
|
|
9300
|
+
*/
|
|
9301
|
+
/**
|
|
9302
|
+
* 模块目录确实存在于本插件存储根(身份判定与官方登记状态无关)。
|
|
9303
|
+
*
|
|
9304
|
+
* 启用表过滤、工具写入目标、TUI 目标共用这一条判据:本插件管理的模块 = 存储根里含
|
|
9305
|
+
* `module.yml` 的目录,与「宿主有没有把它登记成官方预设」是两回事。
|
|
9306
|
+
*/
|
|
9307
|
+
function moduleDirExists(root, id) {
|
|
9308
|
+
return statSync(join(root, id, MODULE_DEFINITION_FILE), { throwIfNoEntry: false }) !== void 0;
|
|
9309
|
+
}
|
|
9310
|
+
/**
|
|
9311
|
+
* 包根 `modules/` 目录:随包分发的**内置模块库**(模块目录形态与存储根同构)。
|
|
9312
|
+
*
|
|
9313
|
+
* 兼容源码运行(`src/host/`)与打包运行(`lib/`)。旧的内置预设目录 `preset/` 已整体
|
|
9314
|
+
* 退场——那些是官方预设形态的遗留,重构后不再读取也不再维护。
|
|
9111
9315
|
*/
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
const candidates = [new URL("../preset/", import.meta.url), new URL("../../preset/", import.meta.url)];
|
|
9316
|
+
function packageModulesDir() {
|
|
9317
|
+
const candidates = [new URL("../modules/", import.meta.url), new URL("../../modules/", import.meta.url)];
|
|
9115
9318
|
for (const candidate of candidates) {
|
|
9116
9319
|
const dir = fileURLToPath(candidate);
|
|
9117
9320
|
if (existsSync(dir)) return dir;
|
|
9118
9321
|
}
|
|
9119
|
-
throw new Error("prompt-tool: cannot locate package
|
|
9322
|
+
throw new Error("prompt-tool: cannot locate package modules/ directory");
|
|
9120
9323
|
}
|
|
9121
9324
|
/** 包根 engine/ 目录(插件引擎,与配置文件夹分离):兼容源码与打包运行。 */
|
|
9122
9325
|
function packageEngineDir() {
|
|
@@ -9129,20 +9332,20 @@ function packageEngineDir() {
|
|
|
9129
9332
|
}
|
|
9130
9333
|
/**
|
|
9131
9334
|
* preset.yml 读缓存:按 mtime+size 签名失效。load()/describe/param-overrides 等
|
|
9132
|
-
* 每次请求读盘解析,加缓存后同一文件只解析一次;写盘路径(
|
|
9335
|
+
* 每次请求读盘解析,加缓存后同一文件只解析一次;写盘路径(saveModuleParams /
|
|
9133
9336
|
* withPresetDoc)与外部编辑(stat 签名变化)都会正确失效。
|
|
9134
9337
|
*/
|
|
9135
9338
|
const presetSpecCache = /* @__PURE__ */ new Map();
|
|
9136
9339
|
/** 写盘后失效缓存(调用方在写完 preset.yml 后调用;不调用也安全——stat 签名兜底)。 */
|
|
9137
|
-
function
|
|
9138
|
-
presetSpecCache.delete(join(dir,
|
|
9340
|
+
function invalidateModuleSpec(dir) {
|
|
9341
|
+
presetSpecCache.delete(join(dir, MODULE_DEFINITION_FILE));
|
|
9139
9342
|
}
|
|
9140
9343
|
/**
|
|
9141
|
-
*
|
|
9344
|
+
* 从模块 module.yml 的 modules 清单移除一个模块 id(YAML Document 保留注释
|
|
9142
9345
|
* 与其余字段)。返回是否发生修改;文件缺失或没有该模块时返回 false。
|
|
9143
9346
|
*/
|
|
9144
9347
|
function removePresetModule(dir, moduleId) {
|
|
9145
|
-
const file = join(dir,
|
|
9348
|
+
const file = join(dir, MODULE_DEFINITION_FILE);
|
|
9146
9349
|
if (!existsSync(file)) return false;
|
|
9147
9350
|
const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
9148
9351
|
if (doc.errors.length > 0) return false;
|
|
@@ -9153,12 +9356,21 @@ function removePresetModule(dir, moduleId) {
|
|
|
9153
9356
|
if (kept.length === modules.items.length) return false;
|
|
9154
9357
|
modules.items = kept;
|
|
9155
9358
|
atomicWriteTextFile(file, doc.toString());
|
|
9156
|
-
|
|
9359
|
+
invalidateModuleSpec(dir);
|
|
9157
9360
|
return true;
|
|
9158
9361
|
}
|
|
9159
|
-
/**
|
|
9160
|
-
function
|
|
9161
|
-
const
|
|
9362
|
+
/** 加载某个模块模板的单一参数文件 modules/<name>/module.yml。 */
|
|
9363
|
+
function loadModuleSpec(dir, options = {}) {
|
|
9364
|
+
const project = (spec) => {
|
|
9365
|
+
if (options.legacyParams === false) return spec;
|
|
9366
|
+
const legacy = resolveLegacyPromptConfigs(spec, { moduleDir: dir });
|
|
9367
|
+
return legacy.active ? {
|
|
9368
|
+
...spec,
|
|
9369
|
+
...Array.isArray(spec.promptConfigs) ? { promptConfigs: legacy.configs } : {},
|
|
9370
|
+
legacyParamWarnings: legacy.warnings
|
|
9371
|
+
} : spec;
|
|
9372
|
+
};
|
|
9373
|
+
const file = join(dir, MODULE_DEFINITION_FILE);
|
|
9162
9374
|
let stat;
|
|
9163
9375
|
try {
|
|
9164
9376
|
stat = statSync(file);
|
|
@@ -9166,7 +9378,7 @@ function loadPresetSpec(dir) {
|
|
|
9166
9378
|
throw new Error(`preset.yml not found in ${dir}(预设模板不存在或目录不完整)`);
|
|
9167
9379
|
}
|
|
9168
9380
|
const cached = presetSpecCache.get(file);
|
|
9169
|
-
if (cached !== void 0 && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) return cached.spec;
|
|
9381
|
+
if (cached !== void 0 && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) return project(cached.spec);
|
|
9170
9382
|
const raw = readFileSync(file, "utf8");
|
|
9171
9383
|
let parsed;
|
|
9172
9384
|
try {
|
|
@@ -9174,58 +9386,43 @@ function loadPresetSpec(dir) {
|
|
|
9174
9386
|
} catch (error) {
|
|
9175
9387
|
throw new Error(`preset ${file} YAML 解析失败: ${String(error.message ?? error)}`);
|
|
9176
9388
|
}
|
|
9177
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`preset ${join(dir,
|
|
9389
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`preset ${join(dir, MODULE_DEFINITION_FILE)} is not a YAML map`);
|
|
9178
9390
|
if (typeof parsed.id !== "string" || parsed.id.length === 0) parsed.id = basename(dir);
|
|
9179
9391
|
const params = readPresetLayerSettings(parsed);
|
|
9392
|
+
readLegacyPromptParams(parsed);
|
|
9180
9393
|
if (parsed.layerSettings !== void 0 || parsed.params !== void 0) parsed.params = params;
|
|
9181
9394
|
presetSpecCache.set(file, {
|
|
9182
9395
|
mtimeMs: stat.mtimeMs,
|
|
9183
9396
|
size: stat.size,
|
|
9184
9397
|
spec: parsed
|
|
9185
9398
|
});
|
|
9186
|
-
return parsed;
|
|
9187
|
-
}
|
|
9188
|
-
/** 读取预设模板内容资产(presetText / agentsText);模板缺失时静默降级。
|
|
9189
|
-
* 模板目录按 resolvePresetDir 解析(用户自定义预设优先,包内模板回退)。 */
|
|
9190
|
-
function loadPresetContent(template = DEFAULT_PRESET_ID, presetRoot = userPresetsDir()) {
|
|
9191
|
-
try {
|
|
9192
|
-
const spec = loadPresetSpec(resolvePresetDir(template, presetRoot));
|
|
9193
|
-
return {
|
|
9194
|
-
presetText: typeof spec.content?.presetText === "string" ? spec.content.presetText : "",
|
|
9195
|
-
agentsText: typeof spec.content?.agentsText === "string" ? spec.content.agentsText : ""
|
|
9196
|
-
};
|
|
9197
|
-
} catch {
|
|
9198
|
-
return {
|
|
9199
|
-
presetText: "",
|
|
9200
|
-
agentsText: ""
|
|
9201
|
-
};
|
|
9202
|
-
}
|
|
9399
|
+
return project(parsed);
|
|
9203
9400
|
}
|
|
9204
9401
|
const asString = (value, fallback = "") => {
|
|
9205
9402
|
if (typeof value === "string") return value;
|
|
9206
9403
|
if (value === void 0 || value === null) return fallback;
|
|
9207
9404
|
return String(value);
|
|
9208
9405
|
};
|
|
9209
|
-
/**
|
|
9210
|
-
function
|
|
9211
|
-
return
|
|
9406
|
+
/** 模块根:本插件的存储根(~/.dsh/.prompt-tool),导入/新建/种子化的模块都放这里。 */
|
|
9407
|
+
function userModulesDir() {
|
|
9408
|
+
return MODULES_DIR;
|
|
9212
9409
|
}
|
|
9213
9410
|
/** 只按合法目录身份定位;现存坏身份必须报错,不能绕到同名模板。 */
|
|
9214
9411
|
function findPresetDir(scanDir, template) {
|
|
9215
|
-
const exact =
|
|
9412
|
+
const exact = assertModuleDirectory(scanDir, template, true);
|
|
9216
9413
|
return presetPathExists(exact) ? exact : void 0;
|
|
9217
9414
|
}
|
|
9218
9415
|
/**
|
|
9219
|
-
*
|
|
9416
|
+
* 解析模块模板目录:当前模块根优先,包内模板回退;不读取其他部署根的同名模块。
|
|
9220
9417
|
* 目录名与声明的 preset.yml id 一致;不扫描其他目录的 id 别名。
|
|
9221
9418
|
*/
|
|
9222
|
-
function
|
|
9223
|
-
return findPresetDir(
|
|
9419
|
+
function resolveModuleDir(template, moduleRoot = userModulesDir()) {
|
|
9420
|
+
return findPresetDir(moduleRoot, template) ?? findPresetDir(packageModulesDir(), template) ?? join(packageModulesDir(), template);
|
|
9224
9421
|
}
|
|
9225
|
-
/**
|
|
9422
|
+
/** 模块目录是否含组合源:modules、composition 或官方 agent.cordis.yml。 */
|
|
9226
9423
|
function isRenderablePresetDir(dir) {
|
|
9227
9424
|
try {
|
|
9228
|
-
const spec =
|
|
9425
|
+
const spec = loadModuleSpec(dir);
|
|
9229
9426
|
if (Array.isArray(spec.modules)) return true;
|
|
9230
9427
|
if (typeof spec.composition === "string" && spec.composition.length > 0) return true;
|
|
9231
9428
|
} catch (error) {
|
|
@@ -9234,13 +9431,13 @@ function isRenderablePresetDir(dir) {
|
|
|
9234
9431
|
}
|
|
9235
9432
|
return existsSync(join(dir, "agent.cordis.yml"));
|
|
9236
9433
|
}
|
|
9237
|
-
/**
|
|
9238
|
-
function
|
|
9434
|
+
/** 本插件模块清单;所有目录按同一身份与组合源规则列举。 */
|
|
9435
|
+
function listModules(moduleRoot = userModulesDir()) {
|
|
9239
9436
|
const scan = (dir) => {
|
|
9240
9437
|
try {
|
|
9241
9438
|
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^[a-z0-9][a-z0-9-]*$/.test(entry.name)).flatMap((entry) => {
|
|
9242
9439
|
try {
|
|
9243
|
-
const spec =
|
|
9440
|
+
const spec = loadModuleSpec(assertModuleDirectory(dir, entry.name));
|
|
9244
9441
|
if (typeof spec.id !== "string" || spec.id.length === 0) return [];
|
|
9245
9442
|
return [{
|
|
9246
9443
|
id: entry.name,
|
|
@@ -9259,15 +9456,15 @@ function listPresets(presetRoot = userPresetsDir()) {
|
|
|
9259
9456
|
}
|
|
9260
9457
|
};
|
|
9261
9458
|
const byId = /* @__PURE__ */ new Map();
|
|
9262
|
-
for (const preset of scan(
|
|
9459
|
+
for (const preset of scan(moduleRoot)) byId.set(preset.id, preset);
|
|
9263
9460
|
return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
9264
9461
|
}
|
|
9265
|
-
/**
|
|
9462
|
+
/** 插件目录模板清单(「新建」选择器与首次种子化数据源;不直接出现在模块列表)。 */
|
|
9266
9463
|
function listBuiltinTemplates() {
|
|
9267
9464
|
try {
|
|
9268
|
-
return readdirSync(
|
|
9465
|
+
return readdirSync(packageModulesDir(), { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
9269
9466
|
try {
|
|
9270
|
-
const spec =
|
|
9467
|
+
const spec = loadModuleSpec(join(packageModulesDir(), entry.name));
|
|
9271
9468
|
return [{
|
|
9272
9469
|
id: entry.name,
|
|
9273
9470
|
name: spec.name
|
|
@@ -9280,16 +9477,16 @@ function listBuiltinTemplates() {
|
|
|
9280
9477
|
return [];
|
|
9281
9478
|
}
|
|
9282
9479
|
}
|
|
9283
|
-
/**
|
|
9284
|
-
function ensurePresetSeed(root =
|
|
9480
|
+
/** 按包内同名目录补建缺失的内置模块;已有目录的定义、生成物和资源保持原样。 */
|
|
9481
|
+
function ensurePresetSeed(root = userModulesDir()) {
|
|
9285
9482
|
const created = [];
|
|
9286
9483
|
try {
|
|
9287
9484
|
mkdirSync(root, { recursive: true });
|
|
9288
|
-
for (const entry of readdirSync(
|
|
9485
|
+
for (const entry of readdirSync(packageModulesDir(), { withFileTypes: true })) {
|
|
9289
9486
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
9290
9487
|
const target = join(root, entry.name);
|
|
9291
9488
|
if (existsSync(target)) continue;
|
|
9292
|
-
cpSync(join(
|
|
9489
|
+
cpSync(join(packageModulesDir(), entry.name), target, { recursive: true });
|
|
9293
9490
|
created.push(entry.name);
|
|
9294
9491
|
}
|
|
9295
9492
|
} catch {}
|
|
@@ -9298,14 +9495,14 @@ function ensurePresetSeed(root = userPresetsDir()) {
|
|
|
9298
9495
|
/** 完整复制到隐藏候选;只更新定义 ID 和已知共享引擎配置位置。 */
|
|
9299
9496
|
function copyPresetDirectory(source, root, targetId) {
|
|
9300
9497
|
assertPresetTree(source);
|
|
9301
|
-
|
|
9302
|
-
const target =
|
|
9498
|
+
loadModuleSpec(source);
|
|
9499
|
+
const target = assertModuleDirectory(root, targetId, true);
|
|
9303
9500
|
if (presetPathExists(target)) throw new Error(`目标预设已存在:${targetId}`);
|
|
9304
9501
|
mkdirSync(root, { recursive: true });
|
|
9305
9502
|
const candidate = mkdtempSync(join(root, `.${targetId}.copy-`));
|
|
9306
9503
|
try {
|
|
9307
9504
|
cpSync(source, candidate, { recursive: true });
|
|
9308
|
-
const definition = join(candidate,
|
|
9505
|
+
const definition = join(candidate, MODULE_DEFINITION_FILE);
|
|
9309
9506
|
const doc = parseDocument(readFileSync(definition, "utf8"), { logLevel: "silent" });
|
|
9310
9507
|
if (doc.errors.length > 0 || !(doc.contents instanceof YAMLMap)) throw new Error("预设定义必须是合法 YAML 对象");
|
|
9311
9508
|
if (doc.get("id") !== targetId) {
|
|
@@ -9340,17 +9537,17 @@ function copyPresetDirectory(source, root, targetId) {
|
|
|
9340
9537
|
});
|
|
9341
9538
|
}
|
|
9342
9539
|
}
|
|
9343
|
-
/**
|
|
9344
|
-
function cloneBuiltinPreset(id, autoSuffix = false,
|
|
9540
|
+
/** 从包内同名目录复制模块;autoSuffix=true 时递增目录名并同步定义身份。 */
|
|
9541
|
+
function cloneBuiltinPreset(id, autoSuffix = false, moduleRoot = userModulesDir()) {
|
|
9345
9542
|
try {
|
|
9346
9543
|
assertPresetId(id);
|
|
9347
|
-
const builtin = findPresetDir(
|
|
9544
|
+
const builtin = findPresetDir(packageModulesDir(), id);
|
|
9348
9545
|
if (builtin === void 0) return {
|
|
9349
9546
|
ok: false,
|
|
9350
9547
|
message: `预设 ${id} 不是包内置预设`
|
|
9351
9548
|
};
|
|
9352
9549
|
let targetId = id;
|
|
9353
|
-
let target = join(
|
|
9550
|
+
let target = join(moduleRoot, targetId);
|
|
9354
9551
|
if (presetPathExists(target)) {
|
|
9355
9552
|
if (!autoSuffix) return {
|
|
9356
9553
|
ok: false,
|
|
@@ -9358,11 +9555,11 @@ function cloneBuiltinPreset(id, autoSuffix = false, presetRoot = userPresetsDir(
|
|
|
9358
9555
|
};
|
|
9359
9556
|
for (let suffix = 2;; suffix++) {
|
|
9360
9557
|
targetId = `${id}-${suffix}`;
|
|
9361
|
-
target = join(
|
|
9558
|
+
target = join(moduleRoot, targetId);
|
|
9362
9559
|
if (!presetPathExists(target)) break;
|
|
9363
9560
|
}
|
|
9364
9561
|
}
|
|
9365
|
-
copyPresetDirectory(builtin,
|
|
9562
|
+
copyPresetDirectory(builtin, moduleRoot, targetId);
|
|
9366
9563
|
return {
|
|
9367
9564
|
ok: true,
|
|
9368
9565
|
id: targetId
|
|
@@ -9374,14 +9571,14 @@ function cloneBuiltinPreset(id, autoSuffix = false, presetRoot = userPresetsDir(
|
|
|
9374
9571
|
};
|
|
9375
9572
|
}
|
|
9376
9573
|
}
|
|
9377
|
-
/**
|
|
9574
|
+
/** 复制用户模块目录为新模块(id 自动递增:<id>-copy / <id>-copy-2 / …)。
|
|
9378
9575
|
* 复制的是用户目录完整副本(preset.yml / agent.cordis.yml / prompt-configs /
|
|
9379
9576
|
* 内容资产 / 覆盖文件),与「从内置模板新建」互补:后者还原模板,前者备份现状。 */
|
|
9380
|
-
function
|
|
9577
|
+
function duplicateUserModule(id, moduleRoot = userModulesDir()) {
|
|
9381
9578
|
try {
|
|
9382
9579
|
assertPresetId(id);
|
|
9383
|
-
const root = resolve(
|
|
9384
|
-
const source =
|
|
9580
|
+
const root = resolve(moduleRoot);
|
|
9581
|
+
const source = assertModuleDirectory(root, id);
|
|
9385
9582
|
let targetId = `${id}-copy`;
|
|
9386
9583
|
let target = join(root, targetId);
|
|
9387
9584
|
for (let suffix = 2; presetPathExists(target); suffix++) {
|
|
@@ -9400,8 +9597,8 @@ function duplicateUserPreset(id, presetRoot = userPresetsDir()) {
|
|
|
9400
9597
|
};
|
|
9401
9598
|
}
|
|
9402
9599
|
}
|
|
9403
|
-
/**
|
|
9404
|
-
function
|
|
9600
|
+
/** 在系统文件管理器中打开模块目录(尽力而为:无桌面环境时打开失败也返回路径供 UI 展示)。 */
|
|
9601
|
+
function openModuleLocation(id, moduleRoot = userModulesDir()) {
|
|
9405
9602
|
const isBareId = typeof id === "string" && /^[a-z0-9][a-z0-9-]*$/.test(id);
|
|
9406
9603
|
const isCardPath = typeof id === "string" && /^\/\.[a-zA-Z0-9_-]+\/[^/\\]+$/.test(id);
|
|
9407
9604
|
if (!isBareId && !isCardPath) return {
|
|
@@ -9409,8 +9606,8 @@ function openPresetLocation(id, presetRoot = userPresetsDir()) {
|
|
|
9409
9606
|
message: `非法预设 id:${id}`,
|
|
9410
9607
|
path: ""
|
|
9411
9608
|
};
|
|
9412
|
-
const dir = resolve(join(
|
|
9413
|
-
const rootResolved = resolve(
|
|
9609
|
+
const dir = resolve(join(moduleRoot, id));
|
|
9610
|
+
const rootResolved = resolve(moduleRoot);
|
|
9414
9611
|
if (dir !== rootResolved && !dir.startsWith(rootResolved + sep)) return {
|
|
9415
9612
|
ok: false,
|
|
9416
9613
|
message: `预设路径越界:${id}`,
|
|
@@ -9422,7 +9619,7 @@ function openPresetLocation(id, presetRoot = userPresetsDir()) {
|
|
|
9422
9619
|
path: dir
|
|
9423
9620
|
};
|
|
9424
9621
|
try {
|
|
9425
|
-
if (isBareId)
|
|
9622
|
+
if (isBareId) assertModuleDirectory(moduleRoot, id);
|
|
9426
9623
|
const command = process.platform === "win32" ? "explorer" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
9427
9624
|
spawn(command, [dir], {
|
|
9428
9625
|
detached: true,
|
|
@@ -9445,24 +9642,41 @@ function deleteYamlPath(doc, path) {
|
|
|
9445
9642
|
if (doc.hasIn(path)) doc.deleteIn(path);
|
|
9446
9643
|
}
|
|
9447
9644
|
/**
|
|
9448
|
-
*
|
|
9645
|
+
* 保存模块参数:写 layerSettings(merge)/ promptConfigs(整体替换)。
|
|
9449
9646
|
* parseDocument 保留注释与未知键(preset.yml 模板含大量注释);空值键删除('' / [],
|
|
9450
9647
|
* 回落模板/引擎默认;0 与 false 照常写入——语义与函数内注释、docs §3 一致)。
|
|
9451
9648
|
*/
|
|
9452
|
-
function
|
|
9453
|
-
const file = join(
|
|
9649
|
+
function saveModuleParams(moduleRoot, templateName, params, promptConfigs, variables, variablesEnabled) {
|
|
9650
|
+
const file = join(assertModuleDirectory(moduleRoot, templateName), MODULE_DEFINITION_FILE);
|
|
9454
9651
|
if (!existsSync(file)) throw new Error(`preset ${templateName} 无 preset.yml`);
|
|
9455
9652
|
const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
9456
9653
|
readPresetLayerSettings(doc.toJS());
|
|
9654
|
+
const previousLegacy = readLegacyPromptParams(doc.toJS());
|
|
9655
|
+
const legacyOverrides = {};
|
|
9457
9656
|
if (params !== void 0) for (const [key, value] of Object.entries(params)) {
|
|
9458
9657
|
if (value === void 0 || value === null || key.trim().length === 0) continue;
|
|
9459
9658
|
const isEmpty = value === "" || Array.isArray(value) && value.length === 0;
|
|
9460
|
-
|
|
9659
|
+
if (isLegacyPromptParam(key)) legacyOverrides[key] = value;
|
|
9660
|
+
const path = isLegacyPromptParam(key) ? legacyPromptParamPath(key) : engineParamPath(key);
|
|
9461
9661
|
if (isEmpty) deleteYamlPath(doc, path);
|
|
9462
9662
|
else doc.setIn(path, value);
|
|
9463
9663
|
}
|
|
9464
|
-
|
|
9465
|
-
|
|
9664
|
+
const source = doc.toJS();
|
|
9665
|
+
const legacy = resolveLegacyPromptConfigs(source, {
|
|
9666
|
+
moduleDir: join(moduleRoot, templateName),
|
|
9667
|
+
overrides: legacyOverrides,
|
|
9668
|
+
partial: Object.keys(previousLegacy).length === 0
|
|
9669
|
+
});
|
|
9670
|
+
const legacyWrite = Object.keys(legacyOverrides).length > 0 && legacy.consumedKeys.length > 0;
|
|
9671
|
+
if (promptConfigs !== void 0 || legacyWrite) {
|
|
9672
|
+
const cleaned = (promptConfigs === void 0 ? legacy.configs : Object.keys(legacyOverrides).length === 0 ? promptConfigs : resolveLegacyPromptConfigs({
|
|
9673
|
+
...source,
|
|
9674
|
+
promptConfigs
|
|
9675
|
+
}, {
|
|
9676
|
+
moduleDir: join(moduleRoot, templateName),
|
|
9677
|
+
overrides: legacyOverrides,
|
|
9678
|
+
partial: Object.keys(previousLegacy).length === 0
|
|
9679
|
+
}).configs).map((config) => {
|
|
9466
9680
|
if (config === null || typeof config !== "object" || Array.isArray(config)) return config;
|
|
9467
9681
|
const vars = config.variables;
|
|
9468
9682
|
if (vars === null || typeof vars !== "object" || Array.isArray(vars)) return config;
|
|
@@ -9473,7 +9687,16 @@ function savePresetParams(presetRoot, templateName, params, promptConfigs, varia
|
|
|
9473
9687
|
return next;
|
|
9474
9688
|
});
|
|
9475
9689
|
doc.setIn(["promptConfigs"], cleaned);
|
|
9476
|
-
|
|
9690
|
+
const accepted = resolveLegacyPromptConfigs({
|
|
9691
|
+
...source,
|
|
9692
|
+
promptConfigs: cleaned
|
|
9693
|
+
}, {
|
|
9694
|
+
moduleDir: join(moduleRoot, templateName),
|
|
9695
|
+
overrides: legacyOverrides
|
|
9696
|
+
});
|
|
9697
|
+
for (const key of accepted.consumedKeys) deleteYamlPath(doc, legacyPromptParamPath(key));
|
|
9698
|
+
for (const warning of accepted.warnings) console.warn(`prompt-tool: ${warning}`);
|
|
9699
|
+
} else for (const warning of legacy.warnings) console.warn(`prompt-tool: ${warning}`);
|
|
9477
9700
|
if (variables !== void 0) {
|
|
9478
9701
|
const kept = Object.fromEntries(Object.entries(variables).filter(([key, value]) => key.trim().length > 0 && typeof value === "string"));
|
|
9479
9702
|
if (Object.keys(kept).length > 0) doc.setIn(["variables"], kept);
|
|
@@ -9484,34 +9707,34 @@ function savePresetParams(presetRoot, templateName, params, promptConfigs, varia
|
|
|
9484
9707
|
else doc.setIn(["variablesEnabled"], false);
|
|
9485
9708
|
}
|
|
9486
9709
|
atomicWriteTextFile(file, doc.toString());
|
|
9487
|
-
|
|
9710
|
+
invalidateModuleSpec(join(moduleRoot, templateName));
|
|
9488
9711
|
}
|
|
9489
9712
|
/**
|
|
9490
|
-
*
|
|
9713
|
+
* 保存激活模块的顶层 persona 段(官方 `@deepseek-ai/dsh-persona` 行 config 同构)。
|
|
9491
9714
|
* null = 删除该段(回落宿主部署人设);默认值不落键(见 personaRowConfig)。
|
|
9492
9715
|
*/
|
|
9493
|
-
function savePresetPersona(
|
|
9494
|
-
const file = join(
|
|
9716
|
+
function savePresetPersona(moduleRoot, templateName, persona) {
|
|
9717
|
+
const file = join(assertModuleDirectory(moduleRoot, templateName), MODULE_DEFINITION_FILE);
|
|
9495
9718
|
if (!existsSync(file)) throw new Error(`preset ${templateName} 无 preset.yml`);
|
|
9496
9719
|
const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
9497
9720
|
readPresetLayerSettings(doc.toJS());
|
|
9498
9721
|
if (persona === null) doc.deleteIn(["persona"]);
|
|
9499
9722
|
else doc.setIn(["persona"], personaRowConfig(persona));
|
|
9500
9723
|
atomicWriteTextFile(file, doc.toString());
|
|
9501
|
-
|
|
9724
|
+
invalidateModuleSpec(join(moduleRoot, templateName));
|
|
9502
9725
|
}
|
|
9503
|
-
/**
|
|
9726
|
+
/** 模块文件读-改-写(parseDocument 保留注释与未知键;mutate 内 setIn/deleteIn)。
|
|
9504
9727
|
* 角色卡库(characters)与世界书工具(world-book-tools)共用此入口,避免
|
|
9505
9728
|
* 各自实现 parseDocument 往返。写盘走原子替换,失败保留旧文件。 */
|
|
9506
|
-
function withPresetDoc(
|
|
9507
|
-
const file = join(
|
|
9508
|
-
if (!existsSync(file)) throw new Error(`${
|
|
9729
|
+
function withPresetDoc(moduleDir, mutate) {
|
|
9730
|
+
const file = join(moduleDir, MODULE_DEFINITION_FILE);
|
|
9731
|
+
if (!existsSync(file)) throw new Error(`${moduleDir} 无 preset.yml`);
|
|
9509
9732
|
const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
9510
9733
|
readPresetLayerSettings(doc.toJS());
|
|
9511
9734
|
mutate(doc);
|
|
9512
9735
|
readPresetLayerSettings(doc.toJS());
|
|
9513
9736
|
atomicWriteTextFile(file, doc.toString());
|
|
9514
|
-
|
|
9737
|
+
invalidateModuleSpec(moduleDir);
|
|
9515
9738
|
}
|
|
9516
9739
|
/** 向 preset.yml 的 modules 追加功能模块;空数组保持按需装配语义。 */
|
|
9517
9740
|
function appendPresetModules(doc, additions) {
|
|
@@ -9525,18 +9748,18 @@ function appendPresetModules(doc, additions) {
|
|
|
9525
9748
|
}
|
|
9526
9749
|
doc.set("modules", modules);
|
|
9527
9750
|
}
|
|
9528
|
-
/**
|
|
9529
|
-
*
|
|
9530
|
-
*
|
|
9531
|
-
function removeUserPreset(id,
|
|
9751
|
+
/** 删除具有合法身份的模块目录(模块根/<id>);隐藏备份不经公共接口删除。
|
|
9752
|
+
* 仅作用于插件自有模块根,包内置模板不受影响;路径越界与非法 id 拒绝。
|
|
9753
|
+
* 模块不在宿主预设注册表里,删除后无需撤销官方登记;调用方按需重建当前模块。 */
|
|
9754
|
+
function removeUserPreset(id, moduleRoot = userModulesDir()) {
|
|
9532
9755
|
try {
|
|
9533
|
-
const target =
|
|
9756
|
+
const target = assertModuleDirectory(moduleRoot, id);
|
|
9534
9757
|
assertPresetTree(target);
|
|
9535
9758
|
rmSync(target, {
|
|
9536
9759
|
recursive: true,
|
|
9537
9760
|
force: true
|
|
9538
9761
|
});
|
|
9539
|
-
|
|
9762
|
+
invalidateModuleSpec(target);
|
|
9540
9763
|
return { ok: true };
|
|
9541
9764
|
} catch (error) {
|
|
9542
9765
|
return {
|
|
@@ -9611,7 +9834,9 @@ function buildModuleConfigsFromParams(params, options = {}) {
|
|
|
9611
9834
|
/**
|
|
9612
9835
|
* 组合模块目录分工:
|
|
9613
9836
|
* - source/local:本项目自有模块的唯一源文件;
|
|
9614
|
-
* - library
|
|
9837
|
+
* - library:随包分发的官方切块与官方预设变体快照(版本化产物,不手工编辑);
|
|
9838
|
+
* 生成它的 `rebuild-composition` 脚本随内置模块目录 `modules/` 一并退场——输入源已不存在,
|
|
9839
|
+
* 快照本体保留为既成事实,不再重建。
|
|
9615
9840
|
* 同名文件禁止同时存在,避免 source 与产物漂移。
|
|
9616
9841
|
*/
|
|
9617
9842
|
function compositionModuleDirs() {
|
|
@@ -9647,7 +9872,7 @@ function assembleModules(spec) {
|
|
|
9647
9872
|
* 行级 config 合并:仅支持 map 型 config 浅合并;
|
|
9648
9873
|
* 数组型 config(如 delegation 组)按子行 id 嵌套合并;未声明模块原样保留。
|
|
9649
9874
|
* 未声明 configs 时返回原文(零开销);parseDocument 往返保留注释。
|
|
9650
|
-
* 本函数是参数桥产物落位组合行的唯一机制(含 delegation 组子行嵌套)
|
|
9875
|
+
* 本函数是参数桥产物落位组合行的唯一机制(含 delegation 组子行嵌套),不是模块覆盖通道。
|
|
9651
9876
|
*/
|
|
9652
9877
|
function applyModuleConfigs(raw, configs) {
|
|
9653
9878
|
if (configs === void 0 || Object.keys(configs).length === 0) return raw;
|
|
@@ -9692,9 +9917,9 @@ function applyModuleConfigs(raw, configs) {
|
|
|
9692
9917
|
return changed ? doc.toString() : raw;
|
|
9693
9918
|
}
|
|
9694
9919
|
/**
|
|
9695
|
-
*
|
|
9920
|
+
* 加载模块声明的组合(原始 token 文本,未渲染)。
|
|
9696
9921
|
* - `modules:` 清单 → 引擎模块库按序装配;
|
|
9697
|
-
* - `composition: ./xxx.yml` →
|
|
9922
|
+
* - `composition: ./xxx.yml` → 模块模板目录内组合文件(官方预设直用);
|
|
9698
9923
|
* - `composition:` 内联文本或组合清单名。
|
|
9699
9924
|
*/
|
|
9700
9925
|
function loadCompositionText(spec, templateDir, runtime = {}) {
|
|
@@ -9744,10 +9969,10 @@ function loadCompositionText(spec, templateDir, runtime = {}) {
|
|
|
9744
9969
|
return raw;
|
|
9745
9970
|
}
|
|
9746
9971
|
/**
|
|
9747
|
-
*
|
|
9972
|
+
* 解析模块的模块事实,供 UI 卡片存在性和受控创建共用。
|
|
9748
9973
|
* 显式模块及当前参数/策略所需的装配是可编辑插件能力;官方组合 row 仅保留为运行事实。
|
|
9749
9974
|
*/
|
|
9750
|
-
function
|
|
9975
|
+
function resolveModuleFacts(spec, templateDir, editable = false) {
|
|
9751
9976
|
const declaredModules = Array.isArray(spec.modules) ? [...new Set(spec.modules.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim()))] : null;
|
|
9752
9977
|
const sourceMode = Array.isArray(spec.modules) ? "explicit" : typeof spec.composition === "string" && spec.composition.trim().length > 0 ? "composition" : templateDir !== void 0 && existsSync(join(templateDir, "agent.cordis.yml")) ? "official" : "unknown";
|
|
9753
9978
|
const effectiveModules = sourceMode === "explicit" ? [...declaredModules ?? []] : null;
|
|
@@ -9825,13 +10050,14 @@ function resolvePresetModuleFacts(spec, templateDir, editable = false) {
|
|
|
9825
10050
|
rowIds,
|
|
9826
10051
|
sourceMode,
|
|
9827
10052
|
editable: editable && sourceMode === "explicit",
|
|
9828
|
-
effectiveConfigs
|
|
10053
|
+
effectiveConfigs,
|
|
10054
|
+
subagentToolPolicyEnabled: spec.subagentToolPolicy !== void 0 && spec.subagentToolPolicy !== null
|
|
9829
10055
|
};
|
|
9830
10056
|
}
|
|
9831
|
-
/** 在内存候选文档中展开能力/recipe,校验成功后一次原子替换
|
|
9832
|
-
function createEngineCapabilityInPreset(
|
|
9833
|
-
const file = join(
|
|
9834
|
-
if (!existsSync(file)) throw new Error(
|
|
10057
|
+
/** 在内存候选文档中展开能力/recipe,校验成功后一次原子替换 module.yml。 */
|
|
10058
|
+
function createEngineCapabilityInPreset(moduleDir, request) {
|
|
10059
|
+
const file = join(moduleDir, MODULE_DEFINITION_FILE);
|
|
10060
|
+
if (!existsSync(file)) throw new Error(`模块目录缺少 preset.yml:${moduleDir}`);
|
|
9835
10061
|
const original = readFileSync(file, "utf8");
|
|
9836
10062
|
const doc = parseDocument(original, { logLevel: "silent" });
|
|
9837
10063
|
const source = doc.toJS();
|
|
@@ -9839,7 +10065,7 @@ function createEngineCapabilityInPreset(presetDir, request) {
|
|
|
9839
10065
|
if (!Array.isArray(source.modules)) throw new Error("当前预设没有可编辑的 modules 数组;请先复制为插件用户预设");
|
|
9840
10066
|
const capabilityIds = request.action === "create" ? [request.capabilityId] : engineRecipe(request.recipeId)?.capabilities ? [...engineRecipe(request.recipeId).capabilities] : [];
|
|
9841
10067
|
if (capabilityIds.length === 0 || capabilityIds.some((id) => engineCapability(id) === void 0)) throw new Error(`未知引擎能力或 recipe:${request.action === "create" ? request.capabilityId : request.recipeId}`);
|
|
9842
|
-
const currentFacts =
|
|
10068
|
+
const currentFacts = resolveModuleFacts(source, moduleDir, true);
|
|
9843
10069
|
const declaredFacts = {
|
|
9844
10070
|
...currentFacts,
|
|
9845
10071
|
effectiveModules: currentFacts.declaredModules
|
|
@@ -9864,7 +10090,7 @@ function createEngineCapabilityInPreset(presetDir, request) {
|
|
|
9864
10090
|
sectionWritten = true;
|
|
9865
10091
|
}
|
|
9866
10092
|
const candidate = doc.toJS();
|
|
9867
|
-
const rows = assertCompositionArray(renderComposition(candidate, {},
|
|
10093
|
+
const rows = assertCompositionArray(renderComposition(candidate, {}, moduleDir), candidate);
|
|
9868
10094
|
const rowPaths = /* @__PURE__ */ new Map();
|
|
9869
10095
|
const visit = (value, path) => {
|
|
9870
10096
|
if (Array.isArray(value)) {
|
|
@@ -9893,7 +10119,7 @@ function createEngineCapabilityInPreset(presetDir, request) {
|
|
|
9893
10119
|
capabilityIds
|
|
9894
10120
|
};
|
|
9895
10121
|
atomicWriteTextFile(file, doc.toString());
|
|
9896
|
-
|
|
10122
|
+
invalidateModuleSpec(moduleDir);
|
|
9897
10123
|
return {
|
|
9898
10124
|
changed: true,
|
|
9899
10125
|
addedModules,
|
|
@@ -9903,9 +10129,9 @@ function createEngineCapabilityInPreset(presetDir, request) {
|
|
|
9903
10129
|
/** 删除能力:模块声明、顶层数据段,以及该能力的**显式参数与行配置**一起移除。
|
|
9904
10130
|
* 参数在 ⇒ 装配在(见 impliedModulesFromParams):留下参数会让移除立刻被隐含装配拉回来,
|
|
9905
10131
|
* 所以"移除能力"必须是完整移除。未登记参数、其他能力的数据与未知字段一律不动。 */
|
|
9906
|
-
function removeEngineCapabilityFromPreset(
|
|
9907
|
-
const file = join(
|
|
9908
|
-
if (!existsSync(file)) throw new Error(
|
|
10132
|
+
function removeEngineCapabilityFromPreset(moduleDir, capabilityId) {
|
|
10133
|
+
const file = join(moduleDir, MODULE_DEFINITION_FILE);
|
|
10134
|
+
if (!existsSync(file)) throw new Error(`模块目录缺少 preset.yml:${moduleDir}`);
|
|
9909
10135
|
const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
9910
10136
|
const source = doc.toJS();
|
|
9911
10137
|
const params = readPresetLayerSettings(source);
|
|
@@ -9929,9 +10155,9 @@ function removeEngineCapabilityFromPreset(presetDir, capabilityId) {
|
|
|
9929
10155
|
for (const key of paramKeys) doc.deleteIn(engineParamPath(key));
|
|
9930
10156
|
for (const rowId of configRows) doc.deleteIn(["moduleConfigs", rowId]);
|
|
9931
10157
|
const candidate = doc.toJS();
|
|
9932
|
-
assertCompositionArray(renderComposition(candidate, {},
|
|
10158
|
+
assertCompositionArray(renderComposition(candidate, {}, moduleDir), candidate);
|
|
9933
10159
|
atomicWriteTextFile(file, doc.toString());
|
|
9934
|
-
|
|
10160
|
+
invalidateModuleSpec(moduleDir);
|
|
9935
10161
|
return {
|
|
9936
10162
|
changed: true,
|
|
9937
10163
|
removedModules,
|
|
@@ -9939,7 +10165,7 @@ function removeEngineCapabilityFromPreset(presetDir, capabilityId) {
|
|
|
9939
10165
|
};
|
|
9940
10166
|
}
|
|
9941
10167
|
/**
|
|
9942
|
-
*
|
|
10168
|
+
* 模块组合渲染完整链路:模块装配 → 参数桥(moduleConfigs + params)行级合并。
|
|
9943
10169
|
* 合并优先级:参数桥(params/UI 基础层) > moduleConfigs(模板/ST 行级直写) > 行默认。
|
|
9944
10170
|
* moduleConfigs 仅补充参数桥未覆盖的行级 config(如 ST 导入的 tool-web.fetch),
|
|
9945
10171
|
* 不再锁定覆盖 UI 可管理参数(旧作者锁定语义已移除)。
|
|
@@ -9979,107 +10205,259 @@ function assertCompositionArray(raw, spec) {
|
|
|
9979
10205
|
if (!Array.isArray(parsed)) throw new Error(`generated agent.cordis.yml is not a YAML array (preset ${spec.id})`);
|
|
9980
10206
|
return parsed;
|
|
9981
10207
|
}
|
|
10208
|
+
/** 存储根配置文件名。 */
|
|
10209
|
+
const STORAGE_CONFIG_FILE = "config.yml";
|
|
10210
|
+
/**
|
|
10211
|
+
* 启用表文件位置。
|
|
10212
|
+
*
|
|
10213
|
+
* 以**模块根**为入口、上溯一级取存储根:模块级常量在 import 时按 `DSH_HOME` 冻结,
|
|
10214
|
+
* 而测试会在动态 import 之后才设 `DSH_HOME`,所以这里必须按传入的根现算
|
|
10215
|
+
* (与 `charactersDir(moduleRoot)` 同一条约定)。
|
|
10216
|
+
*/
|
|
10217
|
+
function enableTablePath(moduleRoot) {
|
|
10218
|
+
return join(dirname(moduleRoot), STORAGE_CONFIG_FILE);
|
|
10219
|
+
}
|
|
10220
|
+
function readDocument(file) {
|
|
10221
|
+
if (!existsSync(file)) return void 0;
|
|
10222
|
+
const doc = parseDocument(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
10223
|
+
if (doc.errors.length > 0 || !isMap(doc.contents)) return void 0;
|
|
10224
|
+
return doc;
|
|
10225
|
+
}
|
|
10226
|
+
/**
|
|
10227
|
+
* 读启用表:返回启用的模块 id(按声明顺序,重复项去掉)。
|
|
10228
|
+
*
|
|
10229
|
+
* 文件缺失、解析失败、schemaVersion 低于 3、`enabled` 不是字符串数组——一律返回空表。
|
|
10230
|
+
* 空表意味着「没有模块参与装配」,这是明确状态,不是错误:旧版本的索引文件里
|
|
10231
|
+
* `enabled` 还没有启用语义,当成启用会让历史键意外参与装配。
|
|
10232
|
+
*/
|
|
10233
|
+
function enabledModuleIds(moduleRoot) {
|
|
10234
|
+
const doc = readDocument(enableTablePath(moduleRoot));
|
|
10235
|
+
if (doc === void 0) return [];
|
|
10236
|
+
if (Number(doc.get("schemaVersion")) < 3) return [];
|
|
10237
|
+
const list = doc.toJS()?.enabled;
|
|
10238
|
+
if (!Array.isArray(list)) return [];
|
|
10239
|
+
return [...new Set(list.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => id.trim()))];
|
|
10240
|
+
}
|
|
10241
|
+
/** 写启用表:保留既有注释与未知字段,只改 `schemaVersion` 与 `enabled`。 */
|
|
10242
|
+
function writeEnabled(moduleRoot, ids) {
|
|
10243
|
+
const file = enableTablePath(moduleRoot);
|
|
10244
|
+
const doc = readDocument(file);
|
|
10245
|
+
if (doc === void 0) {
|
|
10246
|
+
atomicWriteTextFile(file, [
|
|
10247
|
+
"# Prompt Tool 模块启用表:只记录哪些模块参与运行时装配。",
|
|
10248
|
+
"# 参数、提示词与素材在各模块自己的目录里(modules/<id>/);这里没有版本指针,也没有配装段。",
|
|
10249
|
+
"",
|
|
10250
|
+
`schemaVersion: 3`,
|
|
10251
|
+
...ids.length === 0 ? ["enabled: []"] : ["enabled:", ...ids.map((id) => ` - ${id}`)],
|
|
10252
|
+
""
|
|
10253
|
+
].join("\n"));
|
|
10254
|
+
return;
|
|
10255
|
+
}
|
|
10256
|
+
doc.set("schemaVersion", 3);
|
|
10257
|
+
doc.set("enabled", [...ids]);
|
|
10258
|
+
atomicWriteTextFile(file, doc.toString());
|
|
10259
|
+
}
|
|
10260
|
+
/**
|
|
10261
|
+
* 解析**编辑目标**模块目录:请求声明了目标就按它定位,没声明则回退启用表首项。
|
|
10262
|
+
*
|
|
10263
|
+
* 这是「按请求定位编辑目标」的唯一入口——编辑器不必依赖某个全局单选:写请求带上
|
|
10264
|
+
* 模块 id 就写那个模块,不带则落到启用表第一项(= 装配顺序首项)。目标不存在时返回
|
|
10265
|
+
* 空串,由调用方按「无目标」拒绝,不静默改写到别的模块。
|
|
10266
|
+
*/
|
|
10267
|
+
function resolveEditDir(moduleRoot, moduleId) {
|
|
10268
|
+
const target = moduleId !== void 0 && moduleId.trim().length > 0 ? moduleId.trim() : enabledModuleIds(moduleRoot)[0];
|
|
10269
|
+
if (target === void 0) return "";
|
|
10270
|
+
return moduleDirExists(moduleRoot, target) ? join(moduleRoot, target) : "";
|
|
10271
|
+
}
|
|
10272
|
+
/**
|
|
10273
|
+
* 启用或停用一个模块。幂等:已在表里再启用不产生第二条,不在表里再停用不报错。
|
|
10274
|
+
* 顺序即装配顺序,新模块追加在末尾(同键参数的覆盖顺序因此可预测)。
|
|
10275
|
+
*/
|
|
10276
|
+
function setModuleEnabled(moduleRoot, id, enabled) {
|
|
10277
|
+
const current = enabledModuleIds(moduleRoot);
|
|
10278
|
+
const next = enabled ? current.includes(id) ? current : [...current, id] : current.filter((existing) => existing !== id);
|
|
10279
|
+
if (next.length === current.length && next.every((value, index) => value === current[index])) return;
|
|
10280
|
+
writeEnabled(moduleRoot, next);
|
|
10281
|
+
}
|
|
9982
10282
|
//#endregion
|
|
9983
|
-
//#region src/shared/
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
}
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
path: "params.useCustom",
|
|
10036
|
-
sourceParam: "guideCustom",
|
|
10037
|
-
derived: true
|
|
10038
|
-
},
|
|
10039
|
-
{
|
|
10040
|
-
path: "params.text",
|
|
10041
|
-
sourceParam: "guideText"
|
|
10042
|
-
},
|
|
10043
|
-
{
|
|
10044
|
-
path: "params.complexPattern",
|
|
10045
|
-
sourceParam: "complexPattern"
|
|
10046
|
-
},
|
|
10047
|
-
{
|
|
10048
|
-
path: "params.guideWeak",
|
|
10049
|
-
sourceParam: "guideWeak"
|
|
10050
|
-
},
|
|
10051
|
-
{
|
|
10052
|
-
path: "params.guideDeep",
|
|
10053
|
-
sourceParam: "guideDeep"
|
|
10054
|
-
}
|
|
10055
|
-
]
|
|
10056
|
-
}];
|
|
10057
|
-
/** 严格投影白名单;未知路径、参数键和附加属性都不进入客户端。 */
|
|
10058
|
-
function readConfigFieldSources(configId, raw) {
|
|
10059
|
-
const spec = MANAGED_CONFIG_FIELDS.find((entry) => entry.configId === configId);
|
|
10060
|
-
if (spec === void 0 || raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
10061
|
-
const record = raw;
|
|
10062
|
-
if (record.configId !== configId || !Array.isArray(record.fields)) return void 0;
|
|
10283
|
+
//#region src/shared/module-config-order.ts
|
|
10284
|
+
const configIdentityKey = (entry) => JSON.stringify([entry.moduleId, entry.configId]);
|
|
10285
|
+
/** 不以启用表次序打破同号平局;排序不改变配置卡身份。 */
|
|
10286
|
+
function compareModuleConfigOrder(a, b) {
|
|
10287
|
+
const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
10288
|
+
return a.sequence - b.sequence || compare(a.moduleId, b.moduleId) || compare(a.configId, b.configId);
|
|
10289
|
+
}
|
|
10290
|
+
//#endregion
|
|
10291
|
+
//#region src/host/module-config-order.ts
|
|
10292
|
+
/** 已启用配置卡的排序:轻量序号归各自 module.yml,文件是可重建投影。 */
|
|
10293
|
+
var ModuleConfigOrderError = class extends Error {
|
|
10294
|
+
status;
|
|
10295
|
+
code;
|
|
10296
|
+
constructor(message, conflict = false) {
|
|
10297
|
+
super(message);
|
|
10298
|
+
this.status = conflict ? 409 : 400;
|
|
10299
|
+
this.code = conflict ? "module-config-order-conflict" : "module-config-order-invalid";
|
|
10300
|
+
}
|
|
10301
|
+
};
|
|
10302
|
+
function readConfigOrder(value) {
|
|
10303
|
+
if (value === void 0) return Object.create(null);
|
|
10304
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("configOrder 必须是配置卡序号对象");
|
|
10305
|
+
for (const [id, sequence] of Object.entries(value)) {
|
|
10306
|
+
assertSafeConfigId(id);
|
|
10307
|
+
if (typeof sequence !== "number" || !Number.isSafeInteger(sequence) || sequence < 0) throw new Error(`配置卡 ${id} 的序号必须是非负安全整数`);
|
|
10308
|
+
}
|
|
10309
|
+
return Object.assign(Object.create(null), value);
|
|
10310
|
+
}
|
|
10311
|
+
function readInput(root, moduleId) {
|
|
10312
|
+
const dir = assertModuleDirectory(root, moduleId);
|
|
10313
|
+
const raw = readFileSync(join(dir, MODULE_DEFINITION_FILE), "utf8");
|
|
10314
|
+
const doc = parseDocument(raw, { logLevel: "silent" });
|
|
10315
|
+
const saved = readConfigOrder(doc.toJS()?.configOrder);
|
|
10316
|
+
const spec = loadModuleSpec(dir);
|
|
10317
|
+
const directory = join(dir, MODULE_CONFIGS_DIR);
|
|
10318
|
+
const files = /* @__PURE__ */ new Map();
|
|
10319
|
+
if (existsSync(directory)) {
|
|
10320
|
+
assertPresetTree(directory);
|
|
10321
|
+
for (const [index, file] of promptConfigFileNames(readdirSync(directory, { withFileTypes: true })).entries()) {
|
|
10322
|
+
const source = readFileSync(join(directory, file), "utf8");
|
|
10323
|
+
const config = /\.json$/i.test(file) ? JSON.parse(source) : parsePromptConfigYaml(source);
|
|
10324
|
+
assertSafeConfigId(config.id);
|
|
10325
|
+
if (files.has(config.id)) throw new Error(`模块 ${moduleId} 存在重复配置卡:${config.id}`);
|
|
10326
|
+
const prefix = /^(\d+)-/.exec(file);
|
|
10327
|
+
files.set(config.id, {
|
|
10328
|
+
config,
|
|
10329
|
+
sequence: prefix === null ? index * 10 : Number(prefix[1])
|
|
10330
|
+
});
|
|
10331
|
+
}
|
|
10332
|
+
}
|
|
10333
|
+
const cards = Array.isArray(spec.promptConfigs) ? mergePromptConfigs(modelRequestConfigs(resolvePresetParams(spec, {})), spec.promptConfigs) : [...files.values()].map((entry) => entry.config);
|
|
10334
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10063
10335
|
return {
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10336
|
+
moduleId,
|
|
10337
|
+
dir,
|
|
10338
|
+
raw,
|
|
10339
|
+
doc,
|
|
10340
|
+
saved,
|
|
10341
|
+
entries: cards.map((card, index) => {
|
|
10342
|
+
assertSafeConfigId(card.id);
|
|
10343
|
+
if (seen.has(card.id)) throw new Error(`模块 ${moduleId} 存在重复配置卡:${card.id}`);
|
|
10344
|
+
seen.add(card.id);
|
|
10345
|
+
const sequence = saved[card.id] ?? files.get(card.id)?.sequence ?? index * 10;
|
|
10346
|
+
if (!Number.isSafeInteger(sequence) || sequence < 0) throw new Error(`配置卡 ${card.id} 的文件序号无效`);
|
|
10347
|
+
return {
|
|
10348
|
+
moduleId,
|
|
10349
|
+
configId: card.id,
|
|
10350
|
+
name: card.name ?? card.id,
|
|
10351
|
+
layer: card.layer ?? "pre-step",
|
|
10352
|
+
position: card.position ?? "after-user",
|
|
10353
|
+
sequence,
|
|
10354
|
+
enabled: card.enabled !== false,
|
|
10355
|
+
strategy: card.strategy ?? "static",
|
|
10356
|
+
...card.audience === void 0 ? {} : { audience: card.audience },
|
|
10357
|
+
...card.layer === "system-section" || card.layer === "runtime-context" ? { order: card.order ?? 0 } : {}
|
|
10358
|
+
};
|
|
10071
10359
|
})
|
|
10072
10360
|
};
|
|
10073
10361
|
}
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10362
|
+
function readInputs(root, ids = enabledModuleIds(root)) {
|
|
10363
|
+
return [...new Set(ids)].sort().map((id) => readInput(root, id));
|
|
10364
|
+
}
|
|
10365
|
+
function snapshotOf(inputs) {
|
|
10366
|
+
return {
|
|
10367
|
+
revision: createHash("sha256").update(JSON.stringify(inputs.map((input) => [
|
|
10368
|
+
input.moduleId,
|
|
10369
|
+
input.raw,
|
|
10370
|
+
input.entries
|
|
10371
|
+
]))).digest("hex"),
|
|
10372
|
+
entries: inputs.flatMap((input) => input.entries).sort(compareModuleConfigOrder)
|
|
10373
|
+
};
|
|
10374
|
+
}
|
|
10375
|
+
function readModuleConfigOrder(root, moduleId) {
|
|
10376
|
+
return snapshotOf(readInputs(root, moduleId === void 0 ? void 0 : [moduleId]));
|
|
10377
|
+
}
|
|
10378
|
+
function writeOrders(root, inputs, orders) {
|
|
10379
|
+
const changes = inputs.flatMap((input) => {
|
|
10380
|
+
const next = orders.get(input.moduleId);
|
|
10381
|
+
if (next === void 0 || Object.entries(next).every(([id, value]) => input.saved[id] === value)) return [];
|
|
10382
|
+
for (const [id, sequence] of Object.entries(next)) input.doc.setIn(["configOrder", id], sequence);
|
|
10383
|
+
return [{
|
|
10384
|
+
...input,
|
|
10385
|
+
next: input.doc.toString()
|
|
10386
|
+
}];
|
|
10387
|
+
});
|
|
10388
|
+
const verify = (change) => {
|
|
10389
|
+
const dir = assertModuleDirectory(root, change.moduleId);
|
|
10390
|
+
if (dir !== change.dir || readFileSync(join(dir, "module.yml"), "utf8") !== change.raw) throw new ModuleConfigOrderError("模块版本已变化,请重新读取排序", true);
|
|
10391
|
+
};
|
|
10392
|
+
for (const change of changes) verify(change);
|
|
10393
|
+
const written = [];
|
|
10394
|
+
try {
|
|
10395
|
+
for (const change of changes) {
|
|
10396
|
+
atomicWriteTextFile(join(change.dir, MODULE_DEFINITION_FILE), change.next, { beforeReplace: () => verify(change) });
|
|
10397
|
+
invalidateModuleSpec(change.dir);
|
|
10398
|
+
written.push(change);
|
|
10399
|
+
}
|
|
10400
|
+
} catch (error) {
|
|
10401
|
+
for (const change of written.reverse()) {
|
|
10402
|
+
const file = join(change.dir, MODULE_DEFINITION_FILE);
|
|
10403
|
+
if (readFileSync(file, "utf8") === change.next) {
|
|
10404
|
+
atomicWriteTextFile(file, change.raw, { beforeReplace: () => {
|
|
10405
|
+
assertModuleDirectory(root, change.moduleId);
|
|
10406
|
+
if (readFileSync(file, "utf8") !== change.next) throw new Error("排序恢复时模块再次变化");
|
|
10407
|
+
} });
|
|
10408
|
+
invalidateModuleSpec(change.dir);
|
|
10409
|
+
}
|
|
10410
|
+
}
|
|
10411
|
+
throw error;
|
|
10412
|
+
}
|
|
10413
|
+
return changes.map((change) => change.moduleId);
|
|
10414
|
+
}
|
|
10415
|
+
/** 新卡或与其他启用模块碰号的卡接在尾部;已保存且无冲突的顺序保持。 */
|
|
10416
|
+
function appendModuleConfigOrder(root, moduleId) {
|
|
10417
|
+
const others = readInputs(root, enabledModuleIds(root).filter((id) => id !== moduleId));
|
|
10418
|
+
const input = readInput(root, moduleId);
|
|
10419
|
+
const used = new Set(others.flatMap((item) => item.entries.map((entry) => entry.sequence)));
|
|
10420
|
+
let next = -10;
|
|
10421
|
+
for (const value of used) next = Math.max(next, value);
|
|
10422
|
+
for (const value of Object.values(input.saved)) next = Math.max(next, value);
|
|
10423
|
+
next += 10;
|
|
10424
|
+
const assigned = Object.create(null);
|
|
10425
|
+
const collision = input.entries.some((entry) => input.saved[entry.configId] !== void 0 && used.has(input.saved[entry.configId]));
|
|
10426
|
+
for (const entry of [...input.entries].sort(compareModuleConfigOrder)) {
|
|
10427
|
+
let sequence = input.saved[entry.configId];
|
|
10428
|
+
if (collision || sequence === void 0 || used.has(sequence)) {
|
|
10429
|
+
if (!Number.isSafeInteger(next)) throw new Error("配置卡排序序号超出安全整数范围");
|
|
10430
|
+
sequence = next;
|
|
10431
|
+
next += 10;
|
|
10432
|
+
}
|
|
10433
|
+
used.add(sequence);
|
|
10434
|
+
assigned[entry.configId] = sequence;
|
|
10435
|
+
}
|
|
10436
|
+
return writeOrders(root, [input], /* @__PURE__ */ new Map([[moduleId, assigned]]));
|
|
10437
|
+
}
|
|
10438
|
+
/** 默认重排启用集合;指定模块只交换其现有槽位,不接受客户端序号、正文或路径。 */
|
|
10439
|
+
function saveModuleConfigOrder(root, expectedRevision, entries, moduleId) {
|
|
10440
|
+
const inputs = readInputs(root, moduleId === void 0 ? void 0 : [moduleId]);
|
|
10441
|
+
const current = snapshotOf(inputs);
|
|
10442
|
+
if (expectedRevision !== current.revision) throw new ModuleConfigOrderError("模块版本已变化,请重新读取排序", true);
|
|
10443
|
+
if (!Array.isArray(entries) || entries.length !== current.entries.length) throw new ModuleConfigOrderError("排序配置卡集合已变化", true);
|
|
10444
|
+
const known = new Set(current.entries.map(configIdentityKey));
|
|
10445
|
+
const orders = /* @__PURE__ */ new Map();
|
|
10446
|
+
for (const [index, entry] of entries.entries()) {
|
|
10447
|
+
if (entry === null || typeof entry !== "object" || typeof entry.moduleId !== "string" || typeof entry.configId !== "string" || Object.keys(entry).some((key) => key !== "moduleId" && key !== "configId") || !known.delete(configIdentityKey(entry))) throw new ModuleConfigOrderError("排序包含未知或重复配置卡");
|
|
10448
|
+
let values = orders.get(entry.moduleId);
|
|
10449
|
+
if (values === void 0) {
|
|
10450
|
+
values = Object.create(null);
|
|
10451
|
+
orders.set(entry.moduleId, values);
|
|
10452
|
+
}
|
|
10453
|
+
values[entry.configId] = moduleId === void 0 ? index * 10 : current.entries[index].sequence;
|
|
10454
|
+
}
|
|
10455
|
+
return writeOrders(root, inputs, orders);
|
|
10078
10456
|
}
|
|
10079
10457
|
//#endregion
|
|
10080
|
-
//#region src/host/
|
|
10458
|
+
//#region src/host/module-triggers.ts
|
|
10081
10459
|
/** 触发器只归属 preset.yml;内容版本覆盖完整定义,写盘复用现有 YAML Document 通道。 */
|
|
10082
|
-
/** 声明数据以实际 triggers.yml 为基准;允许根与注册层注入的
|
|
10460
|
+
/** 声明数据以实际 triggers.yml 为基准;允许根与注册层注入的 moduleRoot 一致。 */
|
|
10083
10461
|
function triggerPromptConfigOptions(directory, strategy) {
|
|
10084
10462
|
const templatePresetRoot = pathToFileURL(dirname(directory) + "/");
|
|
10085
10463
|
const templateBaseUrl = pathToFileURL(join(directory, "triggers.yml"));
|
|
@@ -10089,9 +10467,9 @@ function triggerPromptConfigOptions(directory, strategy) {
|
|
|
10089
10467
|
strategyDir: typeof strategy !== "string" || strategy.length === 0 ? void 0 : isAbsolute(strategy) ? pathToFileURL(strategy).href : new URL(strategy, templateBaseUrl).href
|
|
10090
10468
|
};
|
|
10091
10469
|
}
|
|
10092
|
-
function
|
|
10093
|
-
const dir =
|
|
10094
|
-
const source = readFileSync(join(dir,
|
|
10470
|
+
function readModuleTriggers(directory) {
|
|
10471
|
+
const dir = assertModuleDirectory(dirname(directory), basename(directory));
|
|
10472
|
+
const source = readFileSync(join(dir, MODULE_DEFINITION_FILE), "utf8");
|
|
10095
10473
|
const triggers = parseDocument(source, { logLevel: "silent" }).toJS().triggers ?? [];
|
|
10096
10474
|
if (!Array.isArray(triggers)) throw new TypeError("preset.yml 的 triggers 必须是数组");
|
|
10097
10475
|
return {
|
|
@@ -10100,8 +10478,8 @@ function readPresetTriggers(directory) {
|
|
|
10100
10478
|
};
|
|
10101
10479
|
}
|
|
10102
10480
|
/** 同步读改写之间不让出事件循环;版本不符不调用写盘,也不丢弃外部字段和注释。 */
|
|
10103
|
-
function
|
|
10104
|
-
if (
|
|
10481
|
+
function saveModuleTriggers(directory, triggers, expectedRevision) {
|
|
10482
|
+
if (readModuleTriggers(directory).revision !== expectedRevision) return false;
|
|
10105
10483
|
withPresetDoc(directory, (doc) => {
|
|
10106
10484
|
if (triggers.length === 0) doc.delete("triggers");
|
|
10107
10485
|
else doc.set("triggers", triggers);
|
|
@@ -10197,16 +10575,16 @@ function buildWorldBookEntry(input) {
|
|
|
10197
10575
|
};
|
|
10198
10576
|
}
|
|
10199
10577
|
const isWorldBook = (config) => config !== null && typeof config === "object" && !Array.isArray(config) && config.strategy === "world-book";
|
|
10200
|
-
/**
|
|
10201
|
-
function listWorldBookEntries(
|
|
10202
|
-
const spec =
|
|
10578
|
+
/** 当前模块全部世界书条目(保持文件顺序)。 */
|
|
10579
|
+
function listWorldBookEntries(moduleDir) {
|
|
10580
|
+
const spec = loadModuleSpec(moduleDir);
|
|
10203
10581
|
return Array.isArray(spec.promptConfigs) ? spec.promptConfigs.filter(isWorldBook) : [];
|
|
10204
10582
|
}
|
|
10205
10583
|
/** 新增或更新一条世界书条目(按 id 定位;不存在则追加)。返回写入后条目总数。 */
|
|
10206
|
-
function upsertWorldBookEntry(
|
|
10584
|
+
function upsertWorldBookEntry(moduleDir, entry) {
|
|
10207
10585
|
if (entry === null || typeof entry !== "object" || typeof entry.id !== "string" || entry.id.length === 0) throw new TypeError("世界书条目必须含非空字符串 id");
|
|
10208
10586
|
let count = 0;
|
|
10209
|
-
withPresetDoc(
|
|
10587
|
+
withPresetDoc(moduleDir, (doc) => {
|
|
10210
10588
|
const current = doc.toJS();
|
|
10211
10589
|
const configs = Array.isArray(current.promptConfigs) ? current.promptConfigs : [];
|
|
10212
10590
|
const existing = configs.findIndex((config) => String(config.id ?? "") === entry.id);
|
|
@@ -10218,10 +10596,10 @@ function upsertWorldBookEntry(presetDir, entry) {
|
|
|
10218
10596
|
return count;
|
|
10219
10597
|
}
|
|
10220
10598
|
/** 删除一条世界书条目;不存在抛错。返回删除后条目总数。 */
|
|
10221
|
-
function deleteWorldBookEntry(
|
|
10599
|
+
function deleteWorldBookEntry(moduleDir, id) {
|
|
10222
10600
|
let count = 0;
|
|
10223
10601
|
let removed = false;
|
|
10224
|
-
withPresetDoc(
|
|
10602
|
+
withPresetDoc(moduleDir, (doc) => {
|
|
10225
10603
|
const current = doc.toJS();
|
|
10226
10604
|
const configs = Array.isArray(current.promptConfigs) ? current.promptConfigs : [];
|
|
10227
10605
|
const kept = configs.filter((config) => !(isWorldBook(config) && String(config.id ?? "") === id));
|
|
@@ -10429,7 +10807,7 @@ function stOrderSelectionState(card, options = {}) {
|
|
|
10429
10807
|
};
|
|
10430
10808
|
return { needsSelection: false };
|
|
10431
10809
|
}
|
|
10432
|
-
/** SillyTavern JSON 预设卡片 → 本项目
|
|
10810
|
+
/** SillyTavern JSON 预设卡片 → 本项目 ModuleSpec(导入端点直接消费)。 */
|
|
10433
10811
|
function convertStToPreset(card, baseName, options = {}) {
|
|
10434
10812
|
return convertStToPresetWithReport(card, baseName, options).spec;
|
|
10435
10813
|
}
|
|
@@ -11413,7 +11791,7 @@ function classify(raw, target) {
|
|
|
11413
11791
|
function prepareImport(input, target, choices = {}) {
|
|
11414
11792
|
const files = normalizeAssetFiles(input);
|
|
11415
11793
|
const sourceDigest = assetSourceDigest(files);
|
|
11416
|
-
const named = files.filter((file) => /^(preset|converted)\.ya?ml$/i.test(file.path));
|
|
11794
|
+
const named = files.filter((file) => /^(?:module|preset|converted)\.ya?ml$/i.test(file.path));
|
|
11417
11795
|
if (named.length > 1) throw new Error(`存在多个定义候选:${named.map((file) => file.path).join("、")}`);
|
|
11418
11796
|
const topFiles = files.filter((file) => !file.path.includes("/"));
|
|
11419
11797
|
const structured = topFiles.filter((file) => /\.(json|ya?ml)$/i.test(file.path));
|
|
@@ -11454,7 +11832,7 @@ function prepareImport(input, target, choices = {}) {
|
|
|
11454
11832
|
validateCharacterSpec(raw);
|
|
11455
11833
|
selfContained = true;
|
|
11456
11834
|
} catch {}
|
|
11457
|
-
if (selfContained && fragment && !/^preset\.ya?ml$/i.test(file.path) && !/^converted\.ya?ml$/i.test(file.path) && choices.sourceKind === void 0) return {
|
|
11835
|
+
if (selfContained && fragment && !/^(?:module|preset)\.ya?ml$/i.test(file.path) && !/^converted\.ya?ml$/i.test(file.path) && choices.sourceKind === void 0) return {
|
|
11458
11836
|
state: "needs-kind-selection",
|
|
11459
11837
|
kinds: ["native-preset", "native-character"],
|
|
11460
11838
|
sourceName: file.path
|
|
@@ -11531,7 +11909,7 @@ function prepareImport(input, target, choices = {}) {
|
|
|
11531
11909
|
const avatar = first.avatar ?? (avatarFile === void 0 ? void 0 : decodeAssetFile(avatarFile));
|
|
11532
11910
|
if (avatar !== void 0 && !isPngBuffer(avatar)) throw new Error("avatar.png 必须提供显式编码的 PNG 原始字节");
|
|
11533
11911
|
const preparedFiles = [{
|
|
11534
|
-
path:
|
|
11912
|
+
path: MODULE_DEFINITION_FILE,
|
|
11535
11913
|
content: yaml,
|
|
11536
11914
|
encoding: "utf8"
|
|
11537
11915
|
}, ...resources];
|
|
@@ -11555,9 +11933,11 @@ function prepareImport(input, target, choices = {}) {
|
|
|
11555
11933
|
}
|
|
11556
11934
|
//#endregion
|
|
11557
11935
|
//#region src/host/characters.ts
|
|
11558
|
-
/**
|
|
11559
|
-
*
|
|
11560
|
-
*
|
|
11936
|
+
/** 角色卡库:独立于模块根的素材+参数存储(**存储根**下点前缀目录,官方 discovery 跳过)。
|
|
11937
|
+
* 注意区分两种语义:`moduleRoot` 形参指的是**模块根**(`<存储根>/modules`),角色卡库
|
|
11938
|
+
* 在它的上一级(`<存储根>/.characters`)——两者是兄弟,不是父子。
|
|
11939
|
+
* 角色卡参数(converted.yml = convertStToPreset 产物)不直接生成模块,而是
|
|
11940
|
+
* 由用户按需「导入到当前模块」合并进激活模块 module.yml(promptConfigs 带
|
|
11561
11941
|
* chara-<cardId>- 前缀防冲突,params 合并,meta.importedCharacters 记录来源),
|
|
11562
11942
|
* 并可一键移除。 */
|
|
11563
11943
|
/** 合并写盘时按九层顺序排序(数组序 = 引擎序):层序只由共享契约提供,这里不再维护第二份。 */
|
|
@@ -11603,15 +11983,24 @@ function recordMemory(doc, cardId, config) {
|
|
|
11603
11983
|
contentHash: contentHash(config)
|
|
11604
11984
|
});
|
|
11605
11985
|
}
|
|
11986
|
+
/**
|
|
11987
|
+
* 并入条目的前缀集合:**写入统一用 `module-<id>-`**,读取端同时接受历史的 `chara-<id>-`。
|
|
11988
|
+
*
|
|
11989
|
+
* 不迁移老数据:用户已并入的条目 id 就长成 `chara-…`,改写它们等于重写用户的 module.yml;
|
|
11990
|
+
* 而「按前缀差集撤销」的移除逻辑必须认得两种前缀,否则老条目会撤不掉、新条目会重复并入。
|
|
11991
|
+
*/
|
|
11992
|
+
function importPrefixes(id) {
|
|
11993
|
+
return [`module-${id}-`, `chara-${id}-`];
|
|
11994
|
+
}
|
|
11606
11995
|
function availableMemoryId(configs, cardId) {
|
|
11607
|
-
const base = `
|
|
11996
|
+
const base = `module-${cardId}-memory`;
|
|
11608
11997
|
const used = new Set(configs.map((config) => config.id));
|
|
11609
11998
|
let id = base;
|
|
11610
11999
|
for (let suffix = 2; used.has(id); suffix += 1) id = `${base}-${suffix}`;
|
|
11611
12000
|
return id;
|
|
11612
12001
|
}
|
|
11613
12002
|
/** 分享副本:只排除本机生成证明仍匹配的记忆;旧项与已编辑项须明确选择。 */
|
|
11614
|
-
function projectCharacterMemories(source, choices = {},
|
|
12003
|
+
function projectCharacterMemories(source, choices = {}, moduleRoot) {
|
|
11615
12004
|
const doc = source.clone();
|
|
11616
12005
|
const spec = doc.toJS();
|
|
11617
12006
|
const records = memoryRecords(spec.meta);
|
|
@@ -11626,12 +12015,12 @@ function projectCharacterMemories(source, choices = {}, presetRoot) {
|
|
|
11626
12015
|
excluded.add(index);
|
|
11627
12016
|
continue;
|
|
11628
12017
|
}
|
|
11629
|
-
const cardId = imported.find((card) => id ===
|
|
12018
|
+
const cardId = imported.find((card) => importPrefixes(card).some((base) => id === `${base}memory` || id.startsWith(`${base}memory-`))) ?? proof?.characterId;
|
|
11630
12019
|
if (cardId === void 0) continue;
|
|
11631
|
-
if (
|
|
11632
|
-
const original =
|
|
11633
|
-
if (original?.promptConfigs?.some((entry) => isRecord$1(entry) &&
|
|
11634
|
-
const memory = original === void 0 ? void 0 : buildCharacterMemoryEntry(original, readCharacterMemory(
|
|
12020
|
+
if (moduleRoot !== void 0 && validCardId(cardId)) {
|
|
12021
|
+
const original = loadImportSource(moduleRoot, cardId);
|
|
12022
|
+
if (original?.promptConfigs?.some((entry) => isRecord$1(entry) && importPrefixes(cardId).some((base) => `${base}${String(entry.id)}` === id))) continue;
|
|
12023
|
+
const memory = original === void 0 ? void 0 : buildCharacterMemoryEntry(original, readCharacterMemory(moduleRoot, cardId));
|
|
11635
12024
|
if (memory !== void 0 && contentHash({
|
|
11636
12025
|
...memory,
|
|
11637
12026
|
id
|
|
@@ -11694,13 +12083,16 @@ function characterModuleStillNeeded(module, context) {
|
|
|
11694
12083
|
case "world-book-tools": return context.configs.some((config) => config.strategy === "world-book");
|
|
11695
12084
|
case "session-var-tools": return context.configs.some((config) => isRecord$1(config.params) && config.params.stMacros === true);
|
|
11696
12085
|
case "tool-config-engine": return context.customTools;
|
|
11697
|
-
case "character-tools": return context.importedCharacters.length > 0 || context.configs.some((config) =>
|
|
12086
|
+
case "character-tools": return context.importedCharacters.length > 0 || context.configs.some((config) => {
|
|
12087
|
+
const id = String(config.id ?? "");
|
|
12088
|
+
return id.startsWith("module-") || id.startsWith("chara-");
|
|
12089
|
+
});
|
|
11698
12090
|
default: return true;
|
|
11699
12091
|
}
|
|
11700
12092
|
}
|
|
11701
12093
|
/** 其他已导入卡「引入过」或「声明过」的模块:移除本卡时不得夺走别人要用的模块。
|
|
11702
12094
|
* 声明这一路必须读卡库的 converted.yml——第二张卡声明同一模块时差集为空、不会留下引入记录。 */
|
|
11703
|
-
function modulesClaimedByOtherCards(
|
|
12095
|
+
function modulesClaimedByOtherCards(moduleRoot, importedCards, meta, cardId) {
|
|
11704
12096
|
const claimed = /* @__PURE__ */ new Set();
|
|
11705
12097
|
for (const [id, modules] of Object.entries(recordedCharacterModules(meta))) {
|
|
11706
12098
|
if (id === cardId || !importedCards.includes(id)) continue;
|
|
@@ -11708,7 +12100,7 @@ function modulesClaimedByOtherCards(presetRoot, importedCards, meta, cardId) {
|
|
|
11708
12100
|
}
|
|
11709
12101
|
for (const id of importedCards) {
|
|
11710
12102
|
if (id === cardId || !validCardId(id)) continue;
|
|
11711
|
-
const spec =
|
|
12103
|
+
const spec = loadImportSource(moduleRoot, id);
|
|
11712
12104
|
if (spec === void 0) continue;
|
|
11713
12105
|
for (const module of declaredCharacterModules(spec)) claimed.add(module);
|
|
11714
12106
|
}
|
|
@@ -11719,22 +12111,22 @@ function readPresetModules(doc) {
|
|
|
11719
12111
|
const source = doc.toJS();
|
|
11720
12112
|
return Array.isArray(source.modules) ? source.modules.filter((item) => typeof item === "string") : [];
|
|
11721
12113
|
}
|
|
11722
|
-
/**
|
|
11723
|
-
* (world_book note
|
|
11724
|
-
function syncImportedCharacterMemory(
|
|
12114
|
+
/** 角色卡记忆变更后同步已导入模块的 chara-<id>-memory 注入条目
|
|
12115
|
+
* (world_book note 写入卡记忆后调用;未导入当前模块的卡返回 synced=false)。 */
|
|
12116
|
+
function syncImportedCharacterMemory(moduleRoot, templateName, cardId) {
|
|
11725
12117
|
if (!validCardId(cardId)) return {
|
|
11726
12118
|
ok: false,
|
|
11727
12119
|
message: `非法角色卡 id:${cardId}`
|
|
11728
12120
|
};
|
|
11729
12121
|
let synced = false;
|
|
11730
12122
|
try {
|
|
11731
|
-
const spec =
|
|
12123
|
+
const spec = loadImportSource(moduleRoot, cardId);
|
|
11732
12124
|
if (spec === void 0) return {
|
|
11733
12125
|
ok: false,
|
|
11734
12126
|
message: `角色卡 ${cardId} 不存在或参数损坏`
|
|
11735
12127
|
};
|
|
11736
|
-
const memory = readCharacterMemory(
|
|
11737
|
-
withPresetDoc(join(
|
|
12128
|
+
const memory = readCharacterMemory(moduleRoot, cardId);
|
|
12129
|
+
withPresetDoc(join(moduleRoot, templateName), (doc) => {
|
|
11738
12130
|
const current = doc.toJS();
|
|
11739
12131
|
if (!(Array.isArray(current.meta?.importedCharacters) ? current.meta.importedCharacters.map(String) : []).includes(cardId)) return;
|
|
11740
12132
|
const configs = Array.isArray(current.promptConfigs) ? current.promptConfigs : [];
|
|
@@ -11774,25 +12166,55 @@ function syncImportedCharacterMemory(presetRoot, templateName, cardId) {
|
|
|
11774
12166
|
};
|
|
11775
12167
|
}
|
|
11776
12168
|
}
|
|
11777
|
-
/**
|
|
11778
|
-
|
|
11779
|
-
|
|
12169
|
+
/**
|
|
12170
|
+
* 角色卡库根:**存储根**下的点前缀目录(`<存储根>/.characters`)。
|
|
12171
|
+
*
|
|
12172
|
+
* 本模块形参 `moduleRoot` 的语义是**模块根**(`<存储根>/modules`),角色卡库与模块目录
|
|
12173
|
+
* 是兄弟:从模块根上溯一级即存储根。**必须由参数推导、不读模块级常量**——常量在
|
|
12174
|
+
* import 时按 `DSH_HOME` 冻结,测试内改 `DSH_HOME` 对它无效,会直接写进真实
|
|
12175
|
+
* `DSH_HOME`(2026-10-02 实测踩过:5 个用例把夹具角色卡写进了活的数据目录)。
|
|
12176
|
+
* 官方 roster 不扫描它(点前缀,与 `.engine` 同机制)。
|
|
12177
|
+
*/
|
|
12178
|
+
function charactersDir(moduleRoot) {
|
|
12179
|
+
return join(dirname(moduleRoot), ".characters");
|
|
11780
12180
|
}
|
|
11781
|
-
function cardDir(
|
|
11782
|
-
return join(charactersDir(
|
|
12181
|
+
function cardDir(moduleRoot, id) {
|
|
12182
|
+
return join(charactersDir(moduleRoot), id);
|
|
11783
12183
|
}
|
|
11784
|
-
|
|
11785
|
-
|
|
12184
|
+
/** 读一个 ModuleSpec 形状的定义文件(`converted.yml` 与 `module.yml` 同构)。 */
|
|
12185
|
+
function loadSpecFile(file) {
|
|
11786
12186
|
assertNoLinks(file);
|
|
11787
12187
|
if (!presetPathExists(file)) return void 0;
|
|
11788
12188
|
const parsed = parse(readFileSync(file, "utf8"), { logLevel: "silent" });
|
|
11789
12189
|
if (!isRecord$1(parsed)) throw new Error(`角色定义不是对象:${file}`);
|
|
11790
|
-
|
|
12190
|
+
const spec = parsed;
|
|
12191
|
+
const legacy = resolveLegacyPromptConfigs(spec, { moduleDir: dirname(file) });
|
|
12192
|
+
return legacy.active ? {
|
|
12193
|
+
...spec,
|
|
12194
|
+
promptConfigs: legacy.configs,
|
|
12195
|
+
legacyParamWarnings: legacy.warnings
|
|
12196
|
+
} : spec;
|
|
12197
|
+
}
|
|
12198
|
+
function loadConverted(dir) {
|
|
12199
|
+
return loadSpecFile(join(dir, "converted.yml"));
|
|
11791
12200
|
}
|
|
11792
|
-
/**
|
|
11793
|
-
|
|
12201
|
+
/**
|
|
12202
|
+
* 并入与移除的**源**:模块优先(`<模块根>/<id>/module.yml`),回退角色卡库
|
|
12203
|
+
* (`.characters/<id>/converted.yml`)。
|
|
12204
|
+
*
|
|
12205
|
+
* 模块是统一身份——并入因此成为**模块间**能力,而不是只为角色卡存在的一套;
|
|
12206
|
+
* 角色卡库保留为素材来源与历史形态,所以仍作回退,已有卡片不迁移。
|
|
12207
|
+
* 一个入口、一套前缀(`module-<id>-`),不另立第二套机制。
|
|
12208
|
+
*/
|
|
12209
|
+
function loadImportSource(moduleRoot, id) {
|
|
12210
|
+
const moduleFile = join(moduleRoot, id, MODULE_DEFINITION_FILE);
|
|
12211
|
+
if (presetPathExists(moduleFile)) return loadSpecFile(moduleFile);
|
|
12212
|
+
return loadConverted(cardDir(moduleRoot, id));
|
|
12213
|
+
}
|
|
12214
|
+
/** 追加角色卡本地记忆(.characters/<id>/memory.md,跟随角色卡跨模块)。 */
|
|
12215
|
+
function appendCharacterMemory(moduleRoot, cardId, note) {
|
|
11794
12216
|
assertPresetId(cardId);
|
|
11795
|
-
appendMemoryFile(join(cardDir(
|
|
12217
|
+
appendMemoryFile(join(cardDir(moduleRoot, cardId), "memory.md"), note, "# 角色记忆");
|
|
11796
12218
|
}
|
|
11797
12219
|
/** 检查现有祖先和最终文件,不能通过角色目录 junction 或单文件链接越界。 */
|
|
11798
12220
|
function assertNoLinks(path) {
|
|
@@ -11812,9 +12234,9 @@ function appendMemoryFile(file, note, header = "# 本地记忆") {
|
|
|
11812
12234
|
else writeFileSync(file, `${header}\n${line}`, "utf8");
|
|
11813
12235
|
}
|
|
11814
12236
|
/** 读角色卡本地记忆文本(无记忆返回空串)。 */
|
|
11815
|
-
function readCharacterMemory(
|
|
12237
|
+
function readCharacterMemory(moduleRoot, cardId) {
|
|
11816
12238
|
assertPresetId(cardId);
|
|
11817
|
-
const file = join(cardDir(
|
|
12239
|
+
const file = join(cardDir(moduleRoot, cardId), "memory.md");
|
|
11818
12240
|
assertNoLinks(file);
|
|
11819
12241
|
return presetPathExists(file) ? readFileSync(file, "utf8").trim() : "";
|
|
11820
12242
|
}
|
|
@@ -11858,19 +12280,19 @@ function previewCharacterCard(files, options = {}) {
|
|
|
11858
12280
|
};
|
|
11859
12281
|
}
|
|
11860
12282
|
}
|
|
11861
|
-
function persistCharacterCard(
|
|
12283
|
+
function persistCharacterCard(moduleRoot, converted, sourceText, avatar, convertedYaml = stringify(converted, { lineWidth: 0 })) {
|
|
11862
12284
|
if (!validCardId(converted.id)) return {
|
|
11863
12285
|
ok: false,
|
|
11864
12286
|
message: `非法角色卡 id:${converted.id}`
|
|
11865
12287
|
};
|
|
11866
|
-
const parent = charactersDir(
|
|
11867
|
-
const dir = cardDir(
|
|
12288
|
+
const parent = charactersDir(moduleRoot);
|
|
12289
|
+
const dir = cardDir(moduleRoot, converted.id);
|
|
11868
12290
|
let tmp;
|
|
11869
12291
|
let installed = false;
|
|
11870
12292
|
try {
|
|
11871
12293
|
assertNoLinks(dir);
|
|
11872
12294
|
const before = characterDirectoryDigest(dir);
|
|
11873
|
-
if (presetPathExists(join(dir, "memory.md"))) readCharacterMemory(
|
|
12295
|
+
if (presetPathExists(join(dir, "memory.md"))) readCharacterMemory(moduleRoot, converted.id);
|
|
11874
12296
|
mkdirSync(parent, { recursive: true });
|
|
11875
12297
|
tmp = mkdtempSync(join(parent, `.${converted.id}.tmp-`));
|
|
11876
12298
|
if (before !== void 0) {
|
|
@@ -11950,14 +12372,14 @@ function characterDirectoryDigest(dir) {
|
|
|
11950
12372
|
return hash.digest("hex");
|
|
11951
12373
|
}
|
|
11952
12374
|
/** 角色卡入库:PNG 原图(可选)+ 角色卡 JSON → 转换参数存 converted.yml。 */
|
|
11953
|
-
function importCharacterCard(
|
|
12375
|
+
function importCharacterCard(moduleRoot, files, options = {}) {
|
|
11954
12376
|
try {
|
|
11955
12377
|
const prepared = prepareImport(files, "character", importChoices(options));
|
|
11956
12378
|
if (prepared.state !== "ready") return {
|
|
11957
12379
|
ok: false,
|
|
11958
12380
|
message: "请先选择角色内容类型或提示顺序组"
|
|
11959
12381
|
};
|
|
11960
|
-
return persistCharacterCard(
|
|
12382
|
+
return persistCharacterCard(moduleRoot, prepared.spec, prepared.sourceText ?? prepared.yaml, prepared.avatar, prepared.yaml);
|
|
11961
12383
|
} catch (error) {
|
|
11962
12384
|
return {
|
|
11963
12385
|
ok: false,
|
|
@@ -11966,10 +12388,10 @@ function importCharacterCard(presetRoot, files, options = {}) {
|
|
|
11966
12388
|
}
|
|
11967
12389
|
}
|
|
11968
12390
|
/** 原始文件流导入:识别 PNG 魔数后在 host 侧提取角色卡,避免客户端 base64 膨胀 JSON。 */
|
|
11969
|
-
function importCharacterCardFile(
|
|
12391
|
+
function importCharacterCardFile(moduleRoot, filePath, fileName = basename(filePath), options = {}) {
|
|
11970
12392
|
try {
|
|
11971
12393
|
const buffer = readFileSync(filePath);
|
|
11972
|
-
return importCharacterCard(
|
|
12394
|
+
return importCharacterCard(moduleRoot, [{
|
|
11973
12395
|
path: basename(fileName),
|
|
11974
12396
|
content: buffer.toString("base64"),
|
|
11975
12397
|
encoding: "base64"
|
|
@@ -11981,16 +12403,16 @@ function importCharacterCardFile(presetRoot, filePath, fileName = basename(fileP
|
|
|
11981
12403
|
};
|
|
11982
12404
|
}
|
|
11983
12405
|
}
|
|
11984
|
-
/** 角色卡库清单 +
|
|
11985
|
-
function listCharacterCards(
|
|
11986
|
-
const root = charactersDir(
|
|
12406
|
+
/** 角色卡库清单 + 各卡在当前模块的导入状态。 */
|
|
12407
|
+
function listCharacterCards(moduleRoot, activeTemplate) {
|
|
12408
|
+
const root = charactersDir(moduleRoot);
|
|
11987
12409
|
let importedIds;
|
|
11988
12410
|
try {
|
|
11989
|
-
const
|
|
11990
|
-
const list = (existsSync(
|
|
12411
|
+
const moduleFile = activeTemplate !== void 0 && activeTemplate.length > 0 ? join(moduleRoot, activeTemplate, MODULE_DEFINITION_FILE) : "";
|
|
12412
|
+
const list = (existsSync(moduleFile) ? parseDocument(readFileSync(moduleFile, "utf8"), { logLevel: "silent" }).toJS().meta : void 0)?.importedCharacters;
|
|
11991
12413
|
importedIds = new Set(Array.isArray(list) ? list.map(String) : []);
|
|
11992
12414
|
} catch (error) {
|
|
11993
|
-
throw new Error(
|
|
12415
|
+
throw new Error(`读取当前模块角色状态失败:${String(error)}`);
|
|
11994
12416
|
}
|
|
11995
12417
|
if (!presetPathExists(root)) return [];
|
|
11996
12418
|
assertNoLinks(root);
|
|
@@ -12007,12 +12429,12 @@ function listCharacterCards(presetRoot, activeTemplate) {
|
|
|
12007
12429
|
}).sort((a, b) => a.id.localeCompare(b.id));
|
|
12008
12430
|
}
|
|
12009
12431
|
/** 删除角色卡库条目。 */
|
|
12010
|
-
function deleteCharacterCard(
|
|
12432
|
+
function deleteCharacterCard(moduleRoot, id) {
|
|
12011
12433
|
if (!validCardId(id)) return {
|
|
12012
12434
|
ok: false,
|
|
12013
12435
|
message: `非法角色卡 id:${id}`
|
|
12014
12436
|
};
|
|
12015
|
-
const dir = cardDir(
|
|
12437
|
+
const dir = cardDir(moduleRoot, id);
|
|
12016
12438
|
if (!existsSync(dir)) return {
|
|
12017
12439
|
ok: false,
|
|
12018
12440
|
message: `角色卡 ${id} 不存在`
|
|
@@ -12031,17 +12453,17 @@ function deleteCharacterCard(presetRoot, id) {
|
|
|
12031
12453
|
};
|
|
12032
12454
|
}
|
|
12033
12455
|
}
|
|
12034
|
-
/**
|
|
12456
|
+
/** 角色卡参数导入当前模块:promptConfigs 合并(chara-<id>- 前缀防冲突)、
|
|
12035
12457
|
* params 合并(角色卡覆盖同键)、meta.importedCharacters 记录来源。 */
|
|
12036
|
-
function applyCharacterToPreset(
|
|
12458
|
+
function applyCharacterToPreset(moduleRoot, templateName, cardId) {
|
|
12037
12459
|
if (!validCardId(cardId)) return {
|
|
12038
12460
|
ok: false,
|
|
12039
12461
|
message: `非法角色卡 id:${cardId}`
|
|
12040
12462
|
};
|
|
12041
|
-
const prefix = `
|
|
12463
|
+
const prefix = `module-${cardId}-`;
|
|
12042
12464
|
let personaOpened = false;
|
|
12043
12465
|
try {
|
|
12044
|
-
const spec =
|
|
12466
|
+
const spec = loadImportSource(moduleRoot, cardId);
|
|
12045
12467
|
if (spec === void 0) return {
|
|
12046
12468
|
ok: false,
|
|
12047
12469
|
message: `角色卡 ${cardId} 不存在或参数损坏`
|
|
@@ -12049,7 +12471,7 @@ function applyCharacterToPreset(presetRoot, templateName, cardId) {
|
|
|
12049
12471
|
validateCharacterSpec(spec);
|
|
12050
12472
|
const hasSystemSections = (spec.promptConfigs ?? []).some((config) => isRecord$1(config) && config.layer === "system-section");
|
|
12051
12473
|
let count = 0;
|
|
12052
|
-
withPresetDoc(join(
|
|
12474
|
+
withPresetDoc(join(moduleRoot, templateName), (doc) => {
|
|
12053
12475
|
const current = doc.toJS();
|
|
12054
12476
|
if (hasSystemSections) {
|
|
12055
12477
|
const persona = current.persona;
|
|
@@ -12074,7 +12496,7 @@ function applyCharacterToPreset(presetRoot, templateName, cardId) {
|
|
|
12074
12496
|
}];
|
|
12075
12497
|
});
|
|
12076
12498
|
for (const [key, value] of Object.entries(readPresetLayerSettings(spec))) doc.setIn(engineParamPath(key), value);
|
|
12077
|
-
const memory = readCharacterMemory(
|
|
12499
|
+
const memory = readCharacterMemory(moduleRoot, cardId);
|
|
12078
12500
|
const memoryEntry = buildCharacterMemoryEntry(spec, memory);
|
|
12079
12501
|
const memoryConfig = memoryEntry === void 0 ? void 0 : {
|
|
12080
12502
|
...memoryEntry,
|
|
@@ -12128,21 +12550,21 @@ function applyCharacterToPreset(presetRoot, templateName, cardId) {
|
|
|
12128
12550
|
};
|
|
12129
12551
|
}
|
|
12130
12552
|
}
|
|
12131
|
-
/**
|
|
12553
|
+
/** 从当前模块移除角色卡参数:删前缀 promptConfigs、删该卡声明的 params 键、
|
|
12132
12554
|
* meta.importedCharacters 除名。 */
|
|
12133
|
-
function removeCharacterFromPreset(
|
|
12555
|
+
function removeCharacterFromPreset(moduleRoot, templateName, cardId) {
|
|
12134
12556
|
if (!validCardId(cardId)) return {
|
|
12135
12557
|
ok: false,
|
|
12136
12558
|
message: `非法角色卡 id:${cardId}`
|
|
12137
12559
|
};
|
|
12138
|
-
const
|
|
12560
|
+
const prefixes = importPrefixes(cardId);
|
|
12139
12561
|
try {
|
|
12140
|
-
const spec =
|
|
12562
|
+
const spec = loadImportSource(moduleRoot, cardId);
|
|
12141
12563
|
let removed = 0;
|
|
12142
|
-
withPresetDoc(join(
|
|
12564
|
+
withPresetDoc(join(moduleRoot, templateName), (doc) => {
|
|
12143
12565
|
const current = doc.toJS();
|
|
12144
12566
|
const kept = (Array.isArray(current.promptConfigs) ? current.promptConfigs : []).filter((config) => {
|
|
12145
|
-
const isCard = config !== null && typeof config === "object" && String(config.id ?? "").startsWith(
|
|
12567
|
+
const isCard = config !== null && typeof config === "object" && prefixes.some((base) => String(config.id ?? "").startsWith(base));
|
|
12146
12568
|
if (isCard) removed += 1;
|
|
12147
12569
|
return !isCard;
|
|
12148
12570
|
});
|
|
@@ -12165,7 +12587,7 @@ function removeCharacterFromPreset(presetRoot, templateName, cardId) {
|
|
|
12165
12587
|
const records = recordedCharacterModules(current.meta);
|
|
12166
12588
|
const recorded = Object.values(records).flat();
|
|
12167
12589
|
if (recorded.length > 0) {
|
|
12168
|
-
const others = modulesClaimedByOtherCards(
|
|
12590
|
+
const others = modulesClaimedByOtherCards(moduleRoot, remainingCards, current.meta, cardId);
|
|
12169
12591
|
const after = doc.toJS();
|
|
12170
12592
|
const context = {
|
|
12171
12593
|
configs: kept.filter(isRecord$1),
|
|
@@ -13466,27 +13888,25 @@ function officialChannelText(text, label, registry, warnOnce) {
|
|
|
13466
13888
|
}
|
|
13467
13889
|
return result.text;
|
|
13468
13890
|
}
|
|
13469
|
-
/**
|
|
13891
|
+
/** 只合并排序后同来源、同官方档位和位置的连续 merged 卡,不跨过穿插段。 */
|
|
13470
13892
|
function textLayerGroups(configs) {
|
|
13471
|
-
const sorted = [...configs].sort(
|
|
13893
|
+
const sorted = [...configs].sort(compareTextPlacement);
|
|
13472
13894
|
const groups = [];
|
|
13473
|
-
const index = /* @__PURE__ */ new Map();
|
|
13474
13895
|
for (const config of sorted) {
|
|
13475
|
-
const
|
|
13476
|
-
|
|
13477
|
-
|
|
13478
|
-
|
|
13479
|
-
}
|
|
13480
|
-
let at = index.get(key);
|
|
13481
|
-
if (at === void 0) {
|
|
13482
|
-
at = groups.length;
|
|
13483
|
-
index.set(key, at);
|
|
13484
|
-
groups.push([]);
|
|
13485
|
-
}
|
|
13486
|
-
groups[at].push(config);
|
|
13896
|
+
const previous = groups.at(-1);
|
|
13897
|
+
const base = previous?.[0];
|
|
13898
|
+
if (config.mergeMode === "merged" && base?.mergeMode === "merged" && config.sourceModuleId === base.sourceModuleId && config.position === base.position && config.order === base.order) previous.push(config);
|
|
13899
|
+
else groups.push([config]);
|
|
13487
13900
|
}
|
|
13488
13901
|
return groups;
|
|
13489
13902
|
}
|
|
13903
|
+
/** 官方同 order 按 name 排序:默认名编码安全整数序号,显式名仍由作者拥有。 */
|
|
13904
|
+
function textRegistrationName(config, field) {
|
|
13905
|
+
const explicit = config.params?.[field];
|
|
13906
|
+
if (typeof explicit === "string" && explicit.length > 0) return explicit;
|
|
13907
|
+
if (config.sourceModuleId === void 0) return config.id;
|
|
13908
|
+
return `prompt-tool:${String(config.sequence).padStart(16, "0")}:${config.sourceModuleId}:${config.id}`;
|
|
13909
|
+
}
|
|
13490
13910
|
/** system-section:注册静态 system prompt 段(支持官方 {{variable}} 渲染与 merged 拼接)。 */
|
|
13491
13911
|
function wireSystemSections(ctx, configs, registry, warnOnce) {
|
|
13492
13912
|
const disposers = [];
|
|
@@ -13503,7 +13923,7 @@ function wireSystemSections(ctx, configs, registry, warnOnce) {
|
|
|
13503
13923
|
if (!dynamic && groupText.length === 0) continue;
|
|
13504
13924
|
const text = dynamic ? (context) => group.filter((config) => matchesAgentScope(config, context?.agent)).map((config) => configText(config, registry, warnOnce, context)).filter((item) => item.length > 0).join("\n\n") : groupText;
|
|
13505
13925
|
keepDisposer(ctx, systemPrompt.section({
|
|
13506
|
-
name:
|
|
13926
|
+
name: textRegistrationName(base, "sectionName"),
|
|
13507
13927
|
order: base.order,
|
|
13508
13928
|
text,
|
|
13509
13929
|
...base.params?.complete === true ? { complete: true } : {}
|
|
@@ -13533,7 +13953,7 @@ function wireRuntimeContexts(ctx, configs, registry, warnOnce) {
|
|
|
13533
13953
|
const text = dynamic ? render : render();
|
|
13534
13954
|
if (!dynamic && text.length === 0) continue;
|
|
13535
13955
|
keepDisposer(ctx, systemPrompt.context({
|
|
13536
|
-
name:
|
|
13956
|
+
name: textRegistrationName(base, "contextName"),
|
|
13537
13957
|
order: base.order,
|
|
13538
13958
|
text
|
|
13539
13959
|
}), `${name$1}: context ${base.id}`);
|
|
@@ -13541,7 +13961,7 @@ function wireRuntimeContexts(ctx, configs, registry, warnOnce) {
|
|
|
13541
13961
|
warnOnce(`${name$1}: runtime-context config ${base.id} failed: ${String(error?.message ?? error)}`);
|
|
13542
13962
|
}
|
|
13543
13963
|
}
|
|
13544
|
-
const placeholders = configs.filter(needsResolver).sort(
|
|
13964
|
+
const placeholders = configs.filter(needsResolver).sort(compareTextPlacement);
|
|
13545
13965
|
if (placeholders.length === 0) return disposers;
|
|
13546
13966
|
const slotVariable = `pt_runtime_${newMessageId("context").replace(/[^a-z0-9_]/gi, "_").toLowerCase()}`;
|
|
13547
13967
|
const slotText = `{{${slotVariable}}}`;
|
|
@@ -13552,7 +13972,7 @@ function wireRuntimeContexts(ctx, configs, registry, warnOnce) {
|
|
|
13552
13972
|
}, `${name$1}: runtime-context lifecycle`);
|
|
13553
13973
|
keepDisposer(ctx, systemPrompt.variable(slotVariable, () => ""), `${name$1}: runtime-context placeholder`);
|
|
13554
13974
|
for (const config of placeholders) try {
|
|
13555
|
-
const contextName =
|
|
13975
|
+
const contextName = textRegistrationName(config, "contextName");
|
|
13556
13976
|
keepDisposer(ctx, systemPrompt.context({
|
|
13557
13977
|
name: contextName,
|
|
13558
13978
|
order: config.order,
|
|
@@ -13631,18 +14051,17 @@ function applyAgentRequestParams(params, base) {
|
|
|
13631
14051
|
}
|
|
13632
14052
|
/** agent-request:对冻结的 LlmCallConfig 做浅合并 / 整体替换 / 按值条件删键。 */
|
|
13633
14053
|
function wireAgentRequests(ctx, configs, warnOnce) {
|
|
13634
|
-
|
|
13635
|
-
|
|
13636
|
-
|
|
13637
|
-
|
|
13638
|
-
|
|
13639
|
-
|
|
14054
|
+
if (configs.length === 0) return [];
|
|
14055
|
+
const ordered = configs.some((config) => config.sequence !== void 0) ? configs : [...configs].reverse();
|
|
14056
|
+
return [ctx.on("agent/request", async (payload, next) => {
|
|
14057
|
+
let result = await next();
|
|
14058
|
+
for (const config of ordered) try {
|
|
14059
|
+
if (matchesAgentScope(config, payload?.agent)) result = applyAgentRequestParams(config.params, result);
|
|
13640
14060
|
} catch (error) {
|
|
13641
14061
|
warnOnce(`${name$1}: agent-request config ${config.id} failed: ${String(error?.message ?? error)}`);
|
|
13642
|
-
return base;
|
|
13643
14062
|
}
|
|
13644
|
-
|
|
13645
|
-
|
|
14063
|
+
return result;
|
|
14064
|
+
})];
|
|
13646
14065
|
}
|
|
13647
14066
|
/** 把流替换为提示词配置文本的最小合法 chunk 序列。 */
|
|
13648
14067
|
async function* replacedStream(text) {
|
|
@@ -13815,23 +14234,28 @@ function createTurnStopBudget() {
|
|
|
13815
14234
|
function wireTurnStops(ctx, configs, warnOnce) {
|
|
13816
14235
|
const disposers = [];
|
|
13817
14236
|
if (configs.length === 0) return disposers;
|
|
13818
|
-
const
|
|
13819
|
-
for (const config of configs)
|
|
13820
|
-
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
13824
|
-
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
-
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
14237
|
+
const budgets = /* @__PURE__ */ new Map();
|
|
14238
|
+
for (const config of configs) {
|
|
14239
|
+
const source = config.sourceModuleId ?? "";
|
|
14240
|
+
if (!budgets.has(source)) budgets.set(source, createTurnStopBudget());
|
|
14241
|
+
const budget = budgets.get(source);
|
|
14242
|
+
disposers.push(ctx.on("agent/turn-stopping", ({ agent, turn } = {}) => {
|
|
14243
|
+
try {
|
|
14244
|
+
const session = agent?.session;
|
|
14245
|
+
if (session?.id === void 0 || typeof agent.steer !== "function") return;
|
|
14246
|
+
if (!matchesAgentScope(config, agent)) return;
|
|
14247
|
+
const entry = budget.entry(session.id, turn);
|
|
14248
|
+
if (!budget.available(entry, turn)) return;
|
|
14249
|
+
if (!conditionHit(config, { assistantText: lastAssistantText(session) })) return;
|
|
14250
|
+
const text = layerText(config, agent, warnOnce);
|
|
14251
|
+
if (text.length === 0) return;
|
|
14252
|
+
budget.claim(entry, turn);
|
|
14253
|
+
agent.steer(pluginMessage(`turn-stop-${config.id}`, text, `turn-stop ${config.id}`));
|
|
14254
|
+
} catch (error) {
|
|
14255
|
+
warnOnce(`${name$1}: turn-stop config ${config.id} failed: ${String(error?.message ?? error)}`);
|
|
14256
|
+
}
|
|
14257
|
+
}));
|
|
14258
|
+
}
|
|
13835
14259
|
return disposers;
|
|
13836
14260
|
}
|
|
13837
14261
|
/** 沿官方持久 session 血缘找根会话,不根据当前 UI 会话或预设名称猜测。 */
|
|
@@ -13934,6 +14358,7 @@ function wireSubagentEvents(ctx, configs, warnOnce) {
|
|
|
13934
14358
|
* 变量注册走 keepDisposer(随 ctx fiber 释放),不在本函数的回收面内。
|
|
13935
14359
|
*/
|
|
13936
14360
|
function wireLayers(ctx, configs, warnOnce) {
|
|
14361
|
+
configs = [...configs].sort(compareConfigSequence);
|
|
13937
14362
|
const registry = registerOfficialVariables(ctx, configs.filter((config) => config.layer === "system-section" || config.layer === "runtime-context"), warnOnce);
|
|
13938
14363
|
const registered = [
|
|
13939
14364
|
wireSystemSections(ctx, configs.filter((config) => config.layer === "system-section"), registry, warnOnce),
|
|
@@ -14482,22 +14907,18 @@ async function runPreStepBatch(options) {
|
|
|
14482
14907
|
} catch (error) {
|
|
14483
14908
|
warnOnce(`${name}: config ${String(config?.id ?? "<unknown>")} failed, skipping: ${String(error && error.message || error)}`);
|
|
14484
14909
|
}
|
|
14485
|
-
due.sort((a, b) => a.config
|
|
14910
|
+
due.sort((a, b) => compareConfigSequence(a.config, b.config));
|
|
14486
14911
|
const orderedGroups = [];
|
|
14487
|
-
const
|
|
14912
|
+
const lastAtPosition = /* @__PURE__ */ new Map();
|
|
14488
14913
|
for (const entry of due) {
|
|
14489
|
-
const
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
|
|
14493
|
-
|
|
14494
|
-
|
|
14495
|
-
|
|
14496
|
-
index = orderedGroups.length;
|
|
14497
|
-
groupIndex.set(group, index);
|
|
14498
|
-
orderedGroups.push([]);
|
|
14914
|
+
const previous = lastAtPosition.get(entry.config.position);
|
|
14915
|
+
const base = previous?.[0].config;
|
|
14916
|
+
if (entry.config.mergeMode === "merged" && base?.mergeMode === "merged" && entry.config.sourceModuleId === base.sourceModuleId) previous.push(entry);
|
|
14917
|
+
else {
|
|
14918
|
+
const group = [entry];
|
|
14919
|
+
orderedGroups.push(group);
|
|
14920
|
+
lastAtPosition.set(entry.config.position, group);
|
|
14499
14921
|
}
|
|
14500
|
-
orderedGroups[index].push(entry);
|
|
14501
14922
|
}
|
|
14502
14923
|
const planned = [];
|
|
14503
14924
|
for (const group of orderedGroups) {
|
|
@@ -14569,11 +14990,10 @@ async function runPreStepBatch(options) {
|
|
|
14569
14990
|
* @param options.officialInstructions 本 mount 的组合是否仍挂着官方指令加载行
|
|
14570
14991
|
* (负责人冲突事实;协调器据此拒绝同时注入文件正文)。
|
|
14571
14992
|
*/
|
|
14572
|
-
function
|
|
14993
|
+
function effectivePromptConfigs(configs) {
|
|
14573
14994
|
const list = configs.filter((config) => config !== void 0 && config !== null);
|
|
14574
|
-
const sourceId = typeof options.sourceId === "string" && options.sourceId.length > 0 ? options.sourceId : `preset:${String(list[0]?.id ?? "unknown")}`;
|
|
14575
14995
|
const claimedGroups = /* @__PURE__ */ new Set();
|
|
14576
|
-
|
|
14996
|
+
return list.filter((config) => {
|
|
14577
14997
|
if (config.enabled === false) return false;
|
|
14578
14998
|
if (config.group !== void 0 && config.exclusive === true) {
|
|
14579
14999
|
if (claimedGroups.has(config.group)) return false;
|
|
@@ -14581,6 +15001,28 @@ function applyPromptConfigs(ctx, configs, options = {}) {
|
|
|
14581
15001
|
}
|
|
14582
15002
|
return true;
|
|
14583
15003
|
});
|
|
15004
|
+
}
|
|
15005
|
+
/** 多模块仍各自拥有来源/互斥组;只有非 pre-step 层汇总接线,避免模块块状执行。 */
|
|
15006
|
+
function applyPromptConfigSources(ctx, sources, options = {}) {
|
|
15007
|
+
const selected = sources.map((source) => ({
|
|
15008
|
+
...source,
|
|
15009
|
+
configs: effectivePromptConfigs(source.configs)
|
|
15010
|
+
}));
|
|
15011
|
+
const releases = selected.map((source) => applyPromptConfigs(ctx, source.configs, {
|
|
15012
|
+
...options,
|
|
15013
|
+
...source,
|
|
15014
|
+
layers: false
|
|
15015
|
+
}));
|
|
15016
|
+
const releaseLayers = wireLayers(ctx, selected.flatMap((source) => source.configs).filter((config) => config.layer !== "pre-step"), createWarnOnce(ctx, name));
|
|
15017
|
+
return () => {
|
|
15018
|
+
for (const release of releases) release();
|
|
15019
|
+
releaseLayers();
|
|
15020
|
+
};
|
|
15021
|
+
}
|
|
15022
|
+
function applyPromptConfigs(ctx, configs, options = {}) {
|
|
15023
|
+
const list = configs.filter((config) => config !== void 0 && config !== null);
|
|
15024
|
+
const sourceId = typeof options.sourceId === "string" && options.sourceId.length > 0 ? options.sourceId : `preset:${String(list[0]?.id ?? "unknown")}`;
|
|
15025
|
+
const effectiveList = effectivePromptConfigs(list);
|
|
14584
15026
|
const injectedMemo = /* @__PURE__ */ new Map();
|
|
14585
15027
|
const main = createEpochPromotion(PROMOTE_EVENTS.either, { includeSubagents: false });
|
|
14586
15028
|
const withSubagents = createEpochPromotion(PROMOTE_EVENTS.either, { includeSubagents: true });
|
|
@@ -14617,7 +15059,7 @@ function applyPromptConfigs(ctx, configs, options = {}) {
|
|
|
14617
15059
|
injectedMemo.clear();
|
|
14618
15060
|
};
|
|
14619
15061
|
keepDisposer(ctx, release);
|
|
14620
|
-
const releaseLayers = wireLayers(ctx, effectiveList.filter((config) => config.layer !== "pre-step"), warnOnce);
|
|
15062
|
+
const releaseLayers = options.layers === false ? () => {} : wireLayers(ctx, effectiveList.filter((config) => config.layer !== "pre-step"), warnOnce);
|
|
14621
15063
|
let registeredCoordinator = coordinatorOf(ctx);
|
|
14622
15064
|
if (registeredCoordinator !== void 0) registration = registeredCoordinator.registerPreset(ctx, sourceId, source) ?? null;
|
|
14623
15065
|
const preStepDisposer = ctx.on("agent/pre-step", async (payload, next) => {
|
|
@@ -16031,17 +16473,17 @@ function compileDeclarations(specs, context = {}) {
|
|
|
16031
16473
|
//#endregion
|
|
16032
16474
|
//#region src/host/write-preset.ts
|
|
16033
16475
|
/**
|
|
16034
|
-
* write-preset — 单一参数 YAML
|
|
16476
|
+
* write-preset — 单一参数 YAML 驱动的模块目录物化器。
|
|
16035
16477
|
*
|
|
16036
|
-
* 用户只需编写
|
|
16478
|
+
* 用户只需编写 modules/<template>/module.yml(纯参数):
|
|
16037
16479
|
* meta + content + modules(模块清单)+ params(直读参数)+ 可选 promptConfigs 覆盖。
|
|
16038
16480
|
* 默认提示词配置与组合 token 都由引擎按 params 生成,参数文件不含任何模板语法。
|
|
16039
|
-
* 输出 = 官方对齐布局:
|
|
16481
|
+
* 输出 = 官方对齐布局:moduleDir/<template>/(模块目录,agent.cordis.yml 组合本体
|
|
16040
16482
|
* 直接可挂载)。共享引擎自阶段 2 起由插件包提供(组合行引用包内说明符),不再物化。
|
|
16041
16483
|
*/
|
|
16042
16484
|
const ENGINE_DIR = packageEngineDir();
|
|
16043
16485
|
const RENDER_STAMP = `# prompt-tool:render v5`;
|
|
16044
|
-
/**
|
|
16486
|
+
/** 剥离文本中的模块级变量引用({{key}} → 空串);内置变量({{DSH_HOME}} 等)保留。
|
|
16045
16487
|
* 模板变量插值停用时由 writePreset 调用,避免 {{key}} 残留导致官方渲染 unknown variable。 */
|
|
16046
16488
|
function stripVariableRefs(text, keys) {
|
|
16047
16489
|
return text.replace(/\{\{([A-Za-z0-9_.\u4e00-\u9fff-]+)\}\}/g, (whole, key) => keys.has(key) ? "" : whole);
|
|
@@ -16050,6 +16492,28 @@ function stripVariableRefs(text, keys) {
|
|
|
16050
16492
|
* ST 导入的设置 dump(SPresetSettings 等)动辄数百 KB 且 enabled=false——
|
|
16051
16493
|
* 注入与展示都不需要正文,全量落盘只拖慢 rebuild 与引擎启动扫描。 */
|
|
16052
16494
|
const DISABLED_TEXT_SLIM_THRESHOLD = 32768;
|
|
16495
|
+
/** 按模块身份重建:定义与正文均读取该模块,不叠加工作台当前编辑态。 */
|
|
16496
|
+
function materializeModule(id, options) {
|
|
16497
|
+
if (enabledModuleIds(options.moduleDir).includes(id)) appendModuleConfigOrder(options.moduleDir, id);
|
|
16498
|
+
const dir = resolveModuleDir(id, options.moduleDir);
|
|
16499
|
+
const spec = loadModuleSpec(dir);
|
|
16500
|
+
const content = (file, fallback) => {
|
|
16501
|
+
try {
|
|
16502
|
+
return readFileSync(join(dir, file), "utf8") || fallback;
|
|
16503
|
+
} catch {
|
|
16504
|
+
return fallback;
|
|
16505
|
+
}
|
|
16506
|
+
};
|
|
16507
|
+
return writePreset(content("preset.md", asString(spec.content?.presetText)), {
|
|
16508
|
+
moduleDir: options.moduleDir,
|
|
16509
|
+
presetOrder: options.presetOrder,
|
|
16510
|
+
presetTemplate: id,
|
|
16511
|
+
outputId: id,
|
|
16512
|
+
promptConfigs: [],
|
|
16513
|
+
agentsInstructionText: content("agents.md", asString(spec.content?.agentsText)),
|
|
16514
|
+
warn: options.warn
|
|
16515
|
+
});
|
|
16516
|
+
}
|
|
16053
16517
|
/** Windows 瞬时文件锁(杀软/宿主短读)下的重试:rename/写文件失败最多重试 3 次、间隔 400ms。 */
|
|
16054
16518
|
function withLockRetry(action, retries = 3) {
|
|
16055
16519
|
for (let attempt = 0;; attempt++) try {
|
|
@@ -16092,19 +16556,16 @@ function syncDirInPlace(srcDir, destDir) {
|
|
|
16092
16556
|
/**
|
|
16093
16557
|
* 写盘用的 runtime 参数对象。
|
|
16094
16558
|
*
|
|
16095
|
-
*
|
|
16096
|
-
*
|
|
16097
|
-
* 需要,且与 `index.ts` 的 `reloadPresetParams` **不同**(例如 `firstTurnAnchor` 在这里
|
|
16098
|
-
* 保留调用方显式的 `false`,读回时却布尔化成 `params.x === true`)。所以它必须被逐键钉住,
|
|
16099
|
-
* 不能被「按 kind 统一驱动」取代。
|
|
16559
|
+
* 保留外部调用方的显式覆盖语义;普通模块重建不再回传定义中的参数副本。
|
|
16560
|
+
* 未提供与显式 false / 0 / 空字符串不同,不能用编辑器默认值补齐。
|
|
16100
16561
|
*/
|
|
16101
16562
|
function runtimeOf(options, prompt) {
|
|
16102
16563
|
/** 未提供的参数保持 undefined:resolvePresetParams 跳过 undefined 键,
|
|
16103
|
-
*
|
|
16564
|
+
* 模块 module.yml 的 params/model 段才是缺省值来源。写成 false/''/true 会把
|
|
16104
16565
|
* 「调用方没给」冒充成「调用方要求」,导入与离线物化时覆盖作者定义。 */
|
|
16105
16566
|
const providedBoolean = (value) => typeof value === "boolean" ? value : void 0;
|
|
16106
16567
|
return {
|
|
16107
|
-
...Object.fromEntries(
|
|
16568
|
+
...Object.fromEntries(WRITER_PARAM_KEYS.map((key) => [key, options[key]])),
|
|
16108
16569
|
promptText: prompt,
|
|
16109
16570
|
firstTurnAnchor: providedBoolean(options.firstTurnAnchor),
|
|
16110
16571
|
firstTurnCustom: providedBoolean(options.firstTurnCustom),
|
|
@@ -16127,7 +16588,7 @@ function runtimeOf(options, prompt) {
|
|
|
16127
16588
|
firstTurnWord: typeof options.firstTurnWord === "string" && options.firstTurnWord.length > 0 ? options.firstTurnWord : void 0
|
|
16128
16589
|
};
|
|
16129
16590
|
}
|
|
16130
|
-
/**
|
|
16591
|
+
/** 手写/导入模块恢复路径:与保存方完整编译同源,但坏定义仍逐条告警跳过。 */
|
|
16131
16592
|
function materializeCustomTool(tool, warn) {
|
|
16132
16593
|
try {
|
|
16133
16594
|
return compileCustomTool(tool);
|
|
@@ -16136,18 +16597,18 @@ function materializeCustomTool(tool, warn) {
|
|
|
16136
16597
|
return;
|
|
16137
16598
|
}
|
|
16138
16599
|
}
|
|
16139
|
-
/**
|
|
16600
|
+
/** 把任意单一参数模块模板物化到生成目录;全部失败 fail loud。 */
|
|
16140
16601
|
function writePreset(prompt, options) {
|
|
16141
|
-
const
|
|
16142
|
-
const templateName = typeof options.presetTemplate === "string" && options.presetTemplate.trim().length > 0 ? options.presetTemplate.trim() :
|
|
16602
|
+
const moduleDir = options.moduleDir.trim().length > 0 ? options.moduleDir : MODULES_DIR;
|
|
16603
|
+
const templateName = typeof options.presetTemplate === "string" && options.presetTemplate.trim().length > 0 ? options.presetTemplate.trim() : DEFAULT_MODULE_ID;
|
|
16143
16604
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(templateName)) throw new Error(`invalid presetTemplate ${JSON.stringify(templateName)}: must match official agent-presets id /^[a-z0-9][a-z0-9-]*$/`);
|
|
16144
16605
|
const outputId = typeof options.outputId === "string" && options.outputId.trim().length > 0 ? options.outputId.trim() : templateName;
|
|
16145
16606
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(outputId)) throw new Error(`invalid outputId ${JSON.stringify(outputId)}: must match official agent-presets id /^[a-z0-9][a-z0-9-]*$/`);
|
|
16146
16607
|
assertPresetId(templateName);
|
|
16147
16608
|
assertPresetId(outputId);
|
|
16148
|
-
const templateDir = options.sourceDir ??
|
|
16609
|
+
const templateDir = options.sourceDir ?? resolveModuleDir(templateName, moduleDir);
|
|
16149
16610
|
assertPresetTree(templateDir);
|
|
16150
|
-
|
|
16611
|
+
const spec = loadModuleSpec(templateDir, { legacyParams: false });
|
|
16151
16612
|
if (Array.isArray(spec.meta?.stWarnings)) {
|
|
16152
16613
|
for (const warning of spec.meta.stWarnings) if (typeof warning === "string") options.warn?.(`prompt-tool: ST 导入兼容提示:${warning}`);
|
|
16153
16614
|
}
|
|
@@ -16165,26 +16626,32 @@ function writePreset(prompt, options) {
|
|
|
16165
16626
|
}
|
|
16166
16627
|
const runtime = runtimeOf(options, prompt);
|
|
16167
16628
|
const params = resolvePresetParams(spec, runtime);
|
|
16168
|
-
const
|
|
16169
|
-
|
|
16170
|
-
|
|
16629
|
+
const legacy = resolveLegacyPromptConfigs(spec, {
|
|
16630
|
+
moduleDir: templateDir,
|
|
16631
|
+
prompt,
|
|
16632
|
+
overrides: runtime,
|
|
16633
|
+
promptConfigs: options.promptConfigs
|
|
16634
|
+
});
|
|
16635
|
+
for (const warning of legacy.warnings) (options.warn ?? console.warn)(`prompt-tool: ${warning}`);
|
|
16636
|
+
const targetDir = assertModuleDirectory(moduleDir, outputId, true);
|
|
16637
|
+
mkdirSync(moduleDir, { recursive: true });
|
|
16638
|
+
const tmpDir = mkdtempSync(join(moduleDir, `.${outputId}.tmp-`));
|
|
16171
16639
|
const outDir = tmpDir;
|
|
16172
16640
|
try {
|
|
16173
|
-
const composition = renderComposition(
|
|
16641
|
+
const composition = renderComposition({
|
|
16174
16642
|
...spec,
|
|
16175
|
-
promptConfigs:
|
|
16176
|
-
}
|
|
16643
|
+
promptConfigs: legacy.configs
|
|
16644
|
+
}, runtime, templateDir);
|
|
16177
16645
|
assertCompositionArray(composition, spec);
|
|
16178
16646
|
const subComposition = rewritePresetEngineReferences(composition, outputId, engineModuleFileNames(ENGINE_DIR));
|
|
16179
16647
|
writeFileSync(join(outDir, "agent.cordis.yml"), `${RENDER_STAMP}\n${subComposition}`, "utf8");
|
|
16180
16648
|
const meta = spec.meta !== null && typeof spec.meta === "object" ? spec.meta : {};
|
|
16181
|
-
const sourceYamlPath = options.sourceDir !== void 0 || !existsSync(join(targetDir, "
|
|
16649
|
+
const sourceYamlPath = options.sourceDir !== void 0 || !existsSync(join(targetDir, "module.yml")) ? join(templateDir, MODULE_DEFINITION_FILE) : join(targetDir, MODULE_DEFINITION_FILE);
|
|
16182
16650
|
const existingPresetYaml = existsSync(sourceYamlPath) ? readFileSync(sourceYamlPath, "utf8") : void 0;
|
|
16183
16651
|
if (existingPresetYaml !== void 0 && existingPresetYaml.trim().length > 0) {
|
|
16184
16652
|
const doc = parseDocument(existingPresetYaml, { logLevel: "silent" });
|
|
16185
16653
|
if (doc.errors.length > 0) throw new Error(`invalid preset.yml: ${doc.errors[0].message}`);
|
|
16186
16654
|
doc.set("id", outputId);
|
|
16187
|
-
doc.setIn(["order"], options.presetOrder);
|
|
16188
16655
|
const ensureMetaKey = (key, value) => {
|
|
16189
16656
|
if (value === void 0 || value === null) return;
|
|
16190
16657
|
const current = doc.get(key);
|
|
@@ -16193,18 +16660,15 @@ function writePreset(prompt, options) {
|
|
|
16193
16660
|
ensureMetaKey("name", typeof spec.name === "string" && spec.name.length > 0 ? spec.name : void 0);
|
|
16194
16661
|
ensureMetaKey("description", typeof spec.description === "string" && spec.description.length > 0 ? spec.description : void 0);
|
|
16195
16662
|
ensureMetaKey("meta", Object.keys(meta).length > 0 ? meta : void 0);
|
|
16196
|
-
writeFileSync(join(outDir,
|
|
16197
|
-
} else writeFileSync(join(outDir,
|
|
16198
|
-
...meta,
|
|
16199
|
-
order: options.presetOrder
|
|
16200
|
-
}) + "\n", "utf8");
|
|
16663
|
+
writeFileSync(join(outDir, MODULE_DEFINITION_FILE), doc.toString(), "utf8");
|
|
16664
|
+
} else writeFileSync(join(outDir, MODULE_DEFINITION_FILE), stringify(meta) + "\n", "utf8");
|
|
16201
16665
|
if (prompt.trim().length > 0) writeFileSync(join(outDir, "preset.md"), prompt, "utf8");
|
|
16202
16666
|
else if (existsSync(join(templateDir, "preset.md"))) cpSync(join(templateDir, "preset.md"), join(outDir, "preset.md"));
|
|
16203
16667
|
if (typeof options.agentsInstructionText === "string" && options.agentsInstructionText.trim().length > 0) writeFileSync(join(outDir, "agents.md"), options.agentsInstructionText, "utf8");
|
|
16204
16668
|
else if (existsSync(join(templateDir, "agents.md"))) cpSync(join(templateDir, "agents.md"), join(outDir, "agents.md"));
|
|
16205
16669
|
const templateEntries = readdirSync(templateDir, { withFileTypes: true });
|
|
16206
16670
|
for (const entry of templateEntries) {
|
|
16207
|
-
if (entry.name === "
|
|
16671
|
+
if (entry.name === "module.yml" || entry.name === "agent.cordis.yml" || entry.name === "preset.md" || entry.name === "agents.md") continue;
|
|
16208
16672
|
const source = join(templateDir, entry.name);
|
|
16209
16673
|
const target = join(outDir, entry.name);
|
|
16210
16674
|
if (entry.isDirectory()) cpSync(source, target, {
|
|
@@ -16213,84 +16677,24 @@ function writePreset(prompt, options) {
|
|
|
16213
16677
|
});
|
|
16214
16678
|
else cpSync(source, target, { force: true });
|
|
16215
16679
|
}
|
|
16216
|
-
const promptConfigsDir = join(outDir,
|
|
16680
|
+
const promptConfigsDir = join(outDir, MODULE_CONFIGS_DIR);
|
|
16217
16681
|
rmSync(promptConfigsDir, {
|
|
16218
16682
|
recursive: true,
|
|
16219
16683
|
force: true
|
|
16220
16684
|
});
|
|
16221
16685
|
mkdirSync(promptConfigsDir, { recursive: true });
|
|
16222
|
-
const
|
|
16223
|
-
let templateDefaults;
|
|
16224
|
-
if (templateConfigs.length > 0) templateDefaults = templateConfigs.map((config) => {
|
|
16225
|
-
const next = {
|
|
16226
|
-
...config,
|
|
16227
|
-
params: { ...config.params }
|
|
16228
|
-
};
|
|
16229
|
-
if (next.id === "near-anchor") {
|
|
16230
|
-
next.enabled = params.firstTurnAnchor === true;
|
|
16231
|
-
next.params = {
|
|
16232
|
-
...next.params,
|
|
16233
|
-
useCustom: params.firstTurnCustom === true,
|
|
16234
|
-
text: asString(params.firstTurnText),
|
|
16235
|
-
buildPattern: asString(params.buildPattern),
|
|
16236
|
-
complexPattern: asString(params.complexPattern),
|
|
16237
|
-
firstTurnBuild: asString(params.firstTurnBuild),
|
|
16238
|
-
firstTurnInspect: asString(params.firstTurnInspect),
|
|
16239
|
-
firstTurnDeep: asString(params.firstTurnDeep)
|
|
16240
|
-
};
|
|
16241
|
-
} else if (next.id === "router-guide") {
|
|
16242
|
-
const guideEnabled = params.guideEnabled === true;
|
|
16243
|
-
next.enabled = guideEnabled;
|
|
16244
|
-
const useCustom = guideEnabled && params.guideCustom === true;
|
|
16245
|
-
next.modelScope = useCustom ? "all" : "flash";
|
|
16246
|
-
next.params = {
|
|
16247
|
-
...next.params,
|
|
16248
|
-
useCustom,
|
|
16249
|
-
text: asString(params.guideText),
|
|
16250
|
-
complexPattern: asString(params.complexPattern),
|
|
16251
|
-
guideWeak: asString(params.guideWeak),
|
|
16252
|
-
guideDeep: asString(params.guideDeep)
|
|
16253
|
-
};
|
|
16254
|
-
}
|
|
16255
|
-
return next;
|
|
16256
|
-
});
|
|
16257
|
-
else templateDefaults = [];
|
|
16258
|
-
const merged = mergePromptConfigs(modelRequestConfigs(params), templateDefaults, options.promptConfigs);
|
|
16686
|
+
const merged = mergePromptConfigs(modelRequestConfigs(params), legacy.configs);
|
|
16259
16687
|
const presetVariables = {};
|
|
16260
16688
|
for (const [key, value] of Object.entries(spec.variables ?? {})) if (typeof value === "string") presetVariables[key] = value;
|
|
16261
16689
|
const variablesEnabled = spec.variablesEnabled !== false;
|
|
16262
16690
|
const presetVariableKeys = new Set(Object.keys(presetVariables));
|
|
16263
16691
|
if (variablesEnabled && presetVariableKeys.size > 0) writeFileSync(join(promptConfigsDir, "variables.yml"), stringify(presetVariables), "utf8");
|
|
16692
|
+
const configOrder = readConfigOrder(spec.configOrder);
|
|
16264
16693
|
for (const [index, source] of merged.entries()) {
|
|
16265
16694
|
const config = {
|
|
16266
16695
|
...source,
|
|
16267
16696
|
params: source.params !== void 0 && source.params !== null ? { ...source.params } : source.params
|
|
16268
16697
|
};
|
|
16269
|
-
if (config.id === "prompt-injector") {
|
|
16270
|
-
delete config.text;
|
|
16271
|
-
config.texts = [];
|
|
16272
|
-
config.enabled = params.injectPrompt !== false && prompt.trim().length > 0;
|
|
16273
|
-
const explicitWord = asString(config.params?.firstTurnWord ?? params.firstTurnWord, "");
|
|
16274
|
-
const signalWord = (text) => {
|
|
16275
|
-
const sentence = /the exact sentence:\s*([A-Za-z]+)/i.exec(text);
|
|
16276
|
-
if (sentence !== null && sentence[1] !== void 0) return sentence[1].toLowerCase();
|
|
16277
|
-
const first = /^\P{L}*([\p{L}]+)/u.exec(text.trim());
|
|
16278
|
-
return first !== null && first[1] !== void 0 ? first[1].toLowerCase() : void 0;
|
|
16279
|
-
};
|
|
16280
|
-
const derivedWords = [...new Set([
|
|
16281
|
-
signalWord(asString(params.firstTurnText)),
|
|
16282
|
-
signalWord(asString(params.firstTurnBuild)),
|
|
16283
|
-
signalWord(asString(params.firstTurnInspect)),
|
|
16284
|
-
signalWord(asString(params.firstTurnDeep))
|
|
16285
|
-
].filter((word) => word !== void 0))];
|
|
16286
|
-
const anchorWords = explicitWord.length > 0 ? [explicitWord] : derivedWords;
|
|
16287
|
-
config.params = {
|
|
16288
|
-
...config.params,
|
|
16289
|
-
text: prompt,
|
|
16290
|
-
firstTurnWord: explicitWord,
|
|
16291
|
-
anchorWords
|
|
16292
|
-
};
|
|
16293
|
-
}
|
|
16294
16698
|
if (!variablesEnabled && presetVariableKeys.size > 0) {
|
|
16295
16699
|
const strip = (item) => stripVariableRefs(item, presetVariableKeys);
|
|
16296
16700
|
config.texts = (config.texts ?? []).map(strip);
|
|
@@ -16305,16 +16709,8 @@ function writePreset(prompt, options) {
|
|
|
16305
16709
|
delete config.text;
|
|
16306
16710
|
config.texts = [];
|
|
16307
16711
|
}
|
|
16308
|
-
const
|
|
16309
|
-
|
|
16310
|
-
configId: config.id,
|
|
16311
|
-
fields: binding.fields.map(({ path }) => ({
|
|
16312
|
-
path,
|
|
16313
|
-
source: templateDefaults.includes(source) ? "preset-param" : "prompt-config"
|
|
16314
|
-
}))
|
|
16315
|
-
};
|
|
16316
|
-
const provenance = fieldSources === void 0 ? "" : "\n" + stringify({ fieldSources });
|
|
16317
|
-
writeFileSync(join(promptConfigsDir, configFileName(index * 10, config.id)), renderPromptConfigYaml(config) + provenance, "utf8");
|
|
16712
|
+
const sequence = configOrder[config.id] ?? index * 10;
|
|
16713
|
+
writeFileSync(join(promptConfigsDir, configFileName(sequence, config.id)), renderPromptConfigYaml(config), "utf8");
|
|
16318
16714
|
}
|
|
16319
16715
|
const customToolsDir = join(outDir, "custom-tools");
|
|
16320
16716
|
rmSync(customToolsDir, {
|
|
@@ -16344,7 +16740,7 @@ function writePreset(prompt, options) {
|
|
|
16344
16740
|
rmSync(triggersFile, { force: true });
|
|
16345
16741
|
if (Array.isArray(spec.triggers) && spec.triggers.length > 0) writeFileSync(triggersFile, stringify(spec.triggers, { lineWidth: 0 }), "utf8");
|
|
16346
16742
|
if (options.materializeOnly) return outDir;
|
|
16347
|
-
const backupDir = join(
|
|
16743
|
+
const backupDir = join(moduleDir, `.${templateName}.bak-${Date.now().toString(36)}`);
|
|
16348
16744
|
let oldMoved = false;
|
|
16349
16745
|
let inPlace = false;
|
|
16350
16746
|
if (existsSync(targetDir)) try {
|
|
@@ -16384,10 +16780,17 @@ function writePreset(prompt, options) {
|
|
|
16384
16780
|
}
|
|
16385
16781
|
}
|
|
16386
16782
|
//#endregion
|
|
16387
|
-
//#region src/host/
|
|
16388
|
-
/**
|
|
16783
|
+
//#region src/host/module-package.ts
|
|
16784
|
+
/** 模块交换:有界资源、完整候选与可恢复提交,Web 和 CLI 共用。 */
|
|
16389
16785
|
const MANIFEST = "prompt-tool-package.json";
|
|
16390
16786
|
const PACKAGE_VERSION = 1;
|
|
16787
|
+
/**
|
|
16788
|
+
* 包内定义文件名:写入用新名,**读取接受历史名**。
|
|
16789
|
+
*
|
|
16790
|
+
* 包是可分享的外部产物,用户手里可能有旧版导出的包(清单 `definition: preset.yml`);
|
|
16791
|
+
* 导入是信任边界,拒绝它等于把用户已有的包判为损坏。
|
|
16792
|
+
*/
|
|
16793
|
+
const DEFINITION_NAMES = [MODULE_DEFINITION_FILE, "preset.yml"];
|
|
16391
16794
|
const sha = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
16392
16795
|
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16393
16796
|
var AssetTransferError = class extends Error {
|
|
@@ -16407,7 +16810,7 @@ function assertTransferId(id) {
|
|
|
16407
16810
|
function ownedFile(path) {
|
|
16408
16811
|
const root = path.split("/")[0];
|
|
16409
16812
|
if (root.startsWith(".") || [
|
|
16410
|
-
"
|
|
16813
|
+
"configs",
|
|
16411
16814
|
"custom-tools",
|
|
16412
16815
|
"subagent-tools",
|
|
16413
16816
|
"skills"
|
|
@@ -16442,7 +16845,7 @@ async function expandPresetSource(input) {
|
|
|
16442
16845
|
const manifest = files.find((file) => file.path === MANIFEST);
|
|
16443
16846
|
if (manifest !== void 0) {
|
|
16444
16847
|
const value = JSON.parse(decodeAssetFile(manifest).toString("utf8"));
|
|
16445
|
-
if (!isRecord(value) || value.version !== PACKAGE_VERSION || value.definition !== "
|
|
16848
|
+
if (!isRecord(value) || value.version !== PACKAGE_VERSION || typeof value.definition !== "string" || !DEFINITION_NAMES.includes(value.definition) || !isRecord(value.files)) throw new Error("不支持或损坏的预设包清单");
|
|
16446
16849
|
if (isRecord(value.requires) && typeof value.requires.renderVersion === "number" && value.requires.renderVersion > 5) throw new Error("预设包需要较新的 Prompt Tool");
|
|
16447
16850
|
const entries = files.filter((file) => file !== manifest);
|
|
16448
16851
|
if (Object.keys(value.files).length !== entries.length) throw new Error("包清单与文件集合不一致");
|
|
@@ -16450,7 +16853,7 @@ async function expandPresetSource(input) {
|
|
|
16450
16853
|
files = entries;
|
|
16451
16854
|
}
|
|
16452
16855
|
const yaml = files.filter((file) => !file.path.includes("/") && /\.ya?ml$/i.test(file.path) && file.path !== "agent.cordis.yml");
|
|
16453
|
-
const definition = yaml.find((file) => /^preset\.ya?ml$/i.test(file.path)) ?? (yaml.length === 1 ? yaml[0] : void 0);
|
|
16856
|
+
const definition = yaml.find((file) => /^(?:module|preset)\.ya?ml$/i.test(file.path)) ?? (yaml.length === 1 ? yaml[0] : void 0);
|
|
16454
16857
|
if (definition !== void 0) {
|
|
16455
16858
|
const doc = parseDocument(decodeAssetFile(definition).toString("utf8"), { logLevel: "silent" });
|
|
16456
16859
|
const value = doc.errors.length === 0 ? doc.toJS({ maxAliasCount: 100 }) : void 0;
|
|
@@ -16483,7 +16886,7 @@ function presetImportPreview(root, files, request) {
|
|
|
16483
16886
|
});
|
|
16484
16887
|
if (prepared.state !== "ready") return prepared;
|
|
16485
16888
|
const id = targetId(root, request.targetId ?? prepared.spec.id, request);
|
|
16486
|
-
const version = directoryVersionOf(
|
|
16889
|
+
const version = directoryVersionOf(assertModuleDirectory(root, id, true));
|
|
16487
16890
|
const sourceDigest = assetSourceDigest(files);
|
|
16488
16891
|
return {
|
|
16489
16892
|
prepared,
|
|
@@ -16572,7 +16975,7 @@ async function installPresetPackage(root, files, request) {
|
|
|
16572
16975
|
let moved = false;
|
|
16573
16976
|
const backup = join(stage, "previous");
|
|
16574
16977
|
try {
|
|
16575
|
-
for (const file of preview.prepared.files.filter((file) =>
|
|
16978
|
+
for (const file of preview.prepared.files.filter((file) => file.path !== MANIFEST && !DEFINITION_NAMES.includes(file.path))) {
|
|
16576
16979
|
const path = join(source, safeAssetPath(file.path));
|
|
16577
16980
|
mkdirSync(dirname(path), { recursive: true });
|
|
16578
16981
|
writeFileSync(path, decodeAssetFile(file), { flag: "wx" });
|
|
@@ -16581,10 +16984,10 @@ async function installPresetPackage(root, files, request) {
|
|
|
16581
16984
|
setPresetDefinitionId(doc, preview.summary.targetId);
|
|
16582
16985
|
doc.set("name", preview.summary.targetName);
|
|
16583
16986
|
const spec = doc.toJS();
|
|
16584
|
-
writeFileSync(join(source,
|
|
16987
|
+
writeFileSync(join(source, MODULE_DEFINITION_FILE), doc.toString(), "utf8");
|
|
16585
16988
|
checkCandidate(spec, source, root, preview.summary.targetId);
|
|
16586
16989
|
generated = writePreset(existsSync(join(source, "preset.md")) ? readFileSync(join(source, "preset.md"), "utf8") : "", {
|
|
16587
|
-
|
|
16990
|
+
moduleDir: root,
|
|
16588
16991
|
presetTemplate: preview.summary.targetId,
|
|
16589
16992
|
outputId: preview.summary.targetId,
|
|
16590
16993
|
sourceDir: source,
|
|
@@ -16610,7 +17013,7 @@ async function installPresetPackage(root, files, request) {
|
|
|
16610
17013
|
}
|
|
16611
17014
|
throw error;
|
|
16612
17015
|
}
|
|
16613
|
-
|
|
17016
|
+
invalidateModuleSpec(target);
|
|
16614
17017
|
try {
|
|
16615
17018
|
rmSync(stage, {
|
|
16616
17019
|
recursive: true,
|
|
@@ -16687,7 +17090,7 @@ function dependencyErrors(files, spec) {
|
|
|
16687
17090
|
if (typeof config.templateFile === "string") {
|
|
16688
17091
|
const prefix = `../${spec.id}/`;
|
|
16689
17092
|
if (!config.templateFile.startsWith(prefix)) errors.add(`外部模板:${config.templateFile}`);
|
|
16690
|
-
else check("./" + config.templateFile.slice(prefix.length),
|
|
17093
|
+
else check("./" + config.templateFile.slice(prefix.length), MODULE_DEFINITION_FILE);
|
|
16691
17094
|
}
|
|
16692
17095
|
}
|
|
16693
17096
|
for (const file of files) {
|
|
@@ -16715,11 +17118,11 @@ function dependencyErrors(files, spec) {
|
|
|
16715
17118
|
}
|
|
16716
17119
|
async function exportPresetPackage(root, request) {
|
|
16717
17120
|
assertTransferId(request.id);
|
|
16718
|
-
const dir =
|
|
17121
|
+
const dir = resolveModuleDir(request.id, root);
|
|
16719
17122
|
const before = directoryVersionOf(dir);
|
|
16720
17123
|
if (before === null) throw new Error("预设不存在");
|
|
16721
17124
|
const files = collectFiles(dir);
|
|
16722
|
-
const definition = files.find((file) => file.path ===
|
|
17125
|
+
const definition = files.find((file) => file.path === MODULE_DEFINITION_FILE);
|
|
16723
17126
|
if (!definition) throw new Error("缺少预设定义");
|
|
16724
17127
|
const original = parseDocument(definition.bytes.toString("utf8"));
|
|
16725
17128
|
if (original.errors.length > 0) throw new Error(original.errors[0].message);
|
|
@@ -16742,7 +17145,7 @@ async function exportPresetPackage(root, request) {
|
|
|
16742
17145
|
name: spec.name || request.id,
|
|
16743
17146
|
content: "",
|
|
16744
17147
|
revision,
|
|
16745
|
-
filename: `${request.id}.${mode === "zip" ? "zip" :
|
|
17148
|
+
filename: `${request.id}.${mode === "zip" ? "zip" : MODULE_DEFINITION_FILE}`,
|
|
16746
17149
|
files: files.map((file) => ({
|
|
16747
17150
|
path: file.path,
|
|
16748
17151
|
bytes: file.bytes.length
|
|
@@ -16765,7 +17168,7 @@ async function exportPresetPackage(root, request) {
|
|
|
16765
17168
|
const plugin = JSON.parse(readFileSync(join(dirname(packageEngineDir()), "package.json"), "utf8"));
|
|
16766
17169
|
const manifest = Buffer.from(JSON.stringify({
|
|
16767
17170
|
version: PACKAGE_VERSION,
|
|
16768
|
-
definition:
|
|
17171
|
+
definition: MODULE_DEFINITION_FILE,
|
|
16769
17172
|
requires: {
|
|
16770
17173
|
promptTool: plugin.version,
|
|
16771
17174
|
renderVersion: 5
|
|
@@ -16789,4 +17192,4 @@ async function exportPresetPackage(root, request) {
|
|
|
16789
17192
|
};
|
|
16790
17193
|
}
|
|
16791
17194
|
//#endregion
|
|
16792
|
-
export {
|
|
17195
|
+
export { setModuleEnabled as $, moduleParamFallbacks as $t, requiredCharacterModules as A, createPromptConfigs as At, listWorldBookEntries as B, presetPathExists as Bt, importCharacterCard as C, userModulesDir as Ct, readCharacterMemory as D, configFileName as Dt, previewCharacterCard as E, assertSafeConfigId as Et, mergeStPresets as F, createWarnOnce as Ft, triggerPromptConfigOptions as G, ENGINE_RECIPES as Gt, MAX_ASSET_BYTES as H, readPersonaSpec as Ht, processStText as I, PresetLayerSettingsError as It, readConfigOrder as J, isEngineCapabilityPresent as Jt, ModuleConfigOrderError as K, engineCapability as Kt, stPresetId as L, assertModuleDirectory as Lt, decodeAssetFile as M, compareConfigSequence as Mt, prepareImport as N, createEpochPromotion as Nt, recordedCharacterModules as O, listPromptConfigSpecs as Ot, convertStToPreset as P, PROMOTE_EVENTS as Pt, resolveEditDir as Q, WRITER_PARAM_KEYS as Qt, buildWorldBookEntry as R, assertPresetId as Rt, deleteCharacterCard as S, savePresetPersona as St, listCharacterCards as T, atomicWriteTextFile as Tt, readModuleTriggers as U, ENGINE_CAPABILITIES as Ut, upsertWorldBookEntry as V, DEFAULT_MODULE_ID as Vt, saveModuleTriggers as W, ENGINE_EDITOR_GROUP_MAP as Wt, saveModuleConfigOrder as X, ENGINE_PARAM_DEFINITIONS as Xt, readModuleConfigOrder as Y, validateCustomToolIdentities as Yt, enabledModuleIds as Z, ENGINE_PARAM_KEYS as Zt, appendMemoryFile as _, renderComposition as _t, materializeModule as a, MODULE_DEFINITION_FILE as an, duplicateUserModule as at, charactersDir as b, resolvePresetParams as bt, confirmDelivered as c, listBuiltinTemplates as ct, validateCustomTools as d, moduleDirExists as dt, validateEngineParamValues as en, appendPresetModules as et, safeAssetPath as f, openModuleLocation as ft, appendCharacterMemory as g, removeUserPreset as gt, CHARACTER_MODULES_KEY as h, removePresetModule as ht, presetImportPreview as i, MODULE_CONFIGS_DIR as in, createEngineCapabilityInPreset as it, syncImportedCharacterMemory as j, loadPromptConfigFiles as jt, removeCharacterFromPreset as k, renderPromptConfigYaml as kt, runPreStepBatch as l, listModules as lt, directoryVersionOf as m, removeEngineCapabilityFromPreset as mt, exportPresetPackage as n, DSH_HOME as nn, buildModuleConfigsFromParams as nt, writePreset as o, USER_SKILLS_DIR as on, ensurePresetSeed as ot, computePreviewRevision as p, packageEngineDir as pt, appendModuleConfigOrder as q, engineRecipe as qt, installPresetPackage as r, MODULES_DIR as rn, cloneBuiltinPreset as rt, applyPromptConfigSources as s, invalidateModuleSpec as st, expandPresetSource as t, LEGACY_PROMPT_PARAM_KEYS as tn, applyModuleConfigs as tt, lastWorldBookDiagnostics as u, loadModuleSpec as ut, applyCharacterToPreset as v, resolveModuleDir as vt, importCharacterCardFile as w, withPresetDoc as wt, declaredCharacterModules as x, saveModuleParams as xt, characterModuleStillNeeded as y, resolveModuleFacts as yt, deleteWorldBookEntry as z, canonicalPresetRoot as zt };
|