dsh-paoding 0.3.9 → 0.4.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.
@@ -12,8 +12,9 @@
12
12
  * directory(安全侧:声明行误落 0.1.6 会炸 profile 启动,目录轨误落
13
13
  * 0.1.7 只是预设不显示);
14
14
  * 2) 声明行构造 buildPresetRowText:把生成的 agent.cordis.yml 全文内联成
15
- * 一条声明行的 config.plugins(restrict 行 name 换成 restrict.mjs 的绝对
16
- * file: URL——声明轨没有「相对 preset 目录」语义,必须绝对化);
15
+ * 一条声明行的 config.plugins(restrict 行与 delegation-labels 行的本地
16
+ * name 换成对应 .mjs 的绝对 file: URL——声明轨没有「相对 preset 目录」
17
+ * 语义,必须绝对化);
17
18
  * 3) home patch 托管块手术 upsertHomePatchRows / stripHomePatchRows /
18
19
  * readHomePatchRows:对 $DSH_HOME/cordis.patch.yml 的 MANAGED 标记块做
19
20
  * 行级增删查,块外用户内容一字节不动;托管块损坏(缺结束标记)时拒绝
@@ -155,11 +156,15 @@ function dshVersionText() {
155
156
  * 缩进基准:4 空格起排(`- insert:` 列表项层级由 upsertHomePatchRows 拼),
156
157
  * plugins: 落 8 空格、插件条目行落 10 空格。name/description 用 JSON.stringify
157
158
  * 落值——JSON 字符串即合法 YAML 双引号标量,换行/引号自动转义。agentYmlText
158
- * 先把 restrict 行的 `name: ./restrict.mjs` 替换为 restrictFileUrl(绝对 file:
159
- * URL),再整体 +10 缩进;空行保持空行(块标量相对缩进不变),`!!js` 表达式
160
- * 行原样保留(宿主 patch schema 支持,求值仍由 cordis 装载时完成)。
159
+ * 先把 restrict 行的 `name: ./restrict.mjs` 与 delegation-labels 行的
160
+ * `name: ./delegation-labels.mjs` 替换为对应绝对 file: URL(两个本地插件文件
161
+ * 均无「相对 preset 目录」语义),再整体 +10 缩进;空行保持空行(块标量相对
162
+ * 缩进不变),`!!js` 表达式行原样保留(宿主 patch schema 支持,求值仍由
163
+ * cordis 装载时完成)。文本里出现 delegation-labels 行而未提供
164
+ * delegationLabelsFileUrl 时硬错误——漏传会让相对名字静默进 patch,宿主
165
+ * 解析不了、preset 直接挂掉。
161
166
  */
162
- export function buildPresetRowText({ presetId, name = null, description = null, order = null, agentYmlText, restrictFileUrl }) {
167
+ export function buildPresetRowText({ presetId, name = null, description = null, order = null, agentYmlText, restrictFileUrl, delegationLabelsFileUrl = null }) {
163
168
  if (typeof presetId !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(presetId)) {
164
169
  throw new Error(`buildPresetRowText: 非法 presetId: ${presetId}`)
165
170
  }
@@ -169,6 +174,12 @@ export function buildPresetRowText({ presetId, name = null, description = null,
169
174
  if (typeof restrictFileUrl !== 'string' || restrictFileUrl === '') {
170
175
  throw new Error('buildPresetRowText: restrictFileUrl 不能为空')
171
176
  }
177
+ const referencesLabelsRow = String(agentYmlText)
178
+ .split(/\r?\n/)
179
+ .some((line) => line.trim() === 'name: ./delegation-labels.mjs')
180
+ if (referencesLabelsRow && (typeof delegationLabelsFileUrl !== 'string' || delegationLabelsFileUrl === '')) {
181
+ throw new Error('buildPresetRowText: agentYmlText 引用了 ./delegation-labels.mjs,但未提供 delegationLabelsFileUrl')
182
+ }
172
183
  const lines = [
173
184
  ` - id: ${presetRowId(presetId)}`,
174
185
  ` name: '${PRESET_ROW_NAME}'`,
@@ -184,10 +195,13 @@ export function buildPresetRowText({ presetId, name = null, description = null,
184
195
  lines.push('')
185
196
  continue
186
197
  }
187
- // restrict 行换绝对 file: URL(按行精确内容匹配,缩进原样保留)
188
- const replaced = line.trim() === 'name: ./restrict.mjs'
189
- ? `${line.match(/^[ \t]*/)[0]}name: ${restrictFileUrl}`
190
- : line
198
+ // 本地插件行换绝对 file: URL(按行精确内容匹配,缩进原样保留)
199
+ const trimmed = line.trim()
200
+ const fileUrl =
201
+ trimmed === 'name: ./restrict.mjs' ? restrictFileUrl
202
+ : trimmed === 'name: ./delegation-labels.mjs' ? delegationLabelsFileUrl
203
+ : null
204
+ const replaced = fileUrl !== null ? `${line.match(/^[ \t]*/)[0]}name: ${fileUrl}` : line
191
205
  lines.push(` ${replaced}`)
192
206
  }
193
207
  while (lines.length > 0 && lines[lines.length - 1] === '') lines.pop() // 去尾部空行,段拼接才紧凑
@@ -503,7 +517,8 @@ export function readPresetDirMeta(dir) {
503
517
  /**
504
518
  * 把磁盘上已有的编排类 preset 目录补写进 home patch 托管块(缺行才补,已有行
505
519
  * 原样不动);行在、目录没了的撤行——目录与行同生同灭。产物不全(缺
506
- * agent.cordis.yml / restrict.mjs)的目录跳过并 warn,不拖垮其余补写。
520
+ * agent.cordis.yml / restrict.mjs / delegation-labels.mjs)的目录跳过并 warn,
521
+ * 不拖垮其余补写(整盘重生成可修复)。
507
522
  * 返回实际补写成功的 presetId 列表(既没补也没撤时为空数组)。
508
523
  */
509
524
  export function backfillHomePatchRows(dshHome, presetIds) {
@@ -518,6 +533,8 @@ export function backfillHomePatchRows(dshHome, presetIds) {
518
533
  const agentYmlText = readFileSync(path.join(dir, 'agent.cordis.yml'), 'utf8')
519
534
  const restrictFile = path.join(dir, 'restrict.mjs')
520
535
  if (!existsSync(restrictFile)) throw new Error('restrict.mjs 缺失')
536
+ const labelsFile = path.join(dir, 'delegation-labels.mjs')
537
+ if (!existsSync(labelsFile)) throw new Error('delegation-labels.mjs 缺失')
521
538
  const meta = readPresetDirMeta(dir)
522
539
  rows.push(
523
540
  buildPresetRowText({
@@ -527,6 +544,7 @@ export function backfillHomePatchRows(dshHome, presetIds) {
527
544
  order: meta.order,
528
545
  agentYmlText,
529
546
  restrictFileUrl: pathToFileURL(restrictFile).href,
547
+ delegationLabelsFileUrl: pathToFileURL(labelsFile).href,
530
548
  }),
531
549
  )
532
550
  backfilled.push(presetId)
@@ -58,6 +58,25 @@ import {
58
58
  upsertHomePatchRows,
59
59
  } from './preset-system.mjs'
60
60
 
61
+ /**
62
+ * 预设源文件清单(generateAndInstall 原样复制件;agent.cordis.yml 走重写,
63
+ * 不在此列)。collectState 的存在性检查与物化复制共用同一份,加源文件只改
64
+ * 这一处。delegation-labels.mjs 与 restrict.mjs 同生命周期:都是本地 cordis
65
+ * 插件,声明行轨内联时按绝对 file: URL 引用。
66
+ */
67
+ export const PRESET_ASIS_FILES = ['preset.yml', 'restrict.mjs', 'delegation-labels.mjs']
68
+
69
+ /**
70
+ * 预设源文件存在性检查(collectState 第一步):全部源文件缺任一件即硬错误,
71
+ * 报错点名缺的文件——delegation-labels.mjs 与 restrict.mjs 同口径。
72
+ */
73
+ export function assertPresetSourceFiles(srcDir = SRC_DIR) {
74
+ for (const name of ['agent.cordis.yml', ...PRESET_ASIS_FILES]) {
75
+ const file = path.join(srcDir, name)
76
+ if (!existsSync(file)) throw new Error(`missing preset source file: ${file}`)
77
+ }
78
+ }
79
+
61
80
  /**
62
81
  * Reusable state collection for the CLI and the visual config UI.
63
82
  * Runs the whole detection pipeline (patch scan, MCP handshakes, plugin and
@@ -78,10 +97,7 @@ export async function collectState({ dshHome, configFile, profile = null, patche
78
97
  const dstAgentFile = path.join(dstDir, 'agent.cordis.yml')
79
98
 
80
99
  // Source preset files must exist before anything else.
81
- const sourceFiles = ['agent.cordis.yml', 'preset.yml', 'restrict.mjs'].map((f) => path.join(SRC_DIR, f))
82
- for (const file of sourceFiles) {
83
- if (!existsSync(file)) throw new Error(`missing preset source file: ${file}`)
84
- }
100
+ assertPresetSourceFiles()
85
101
 
86
102
  const yamlMod = loadYaml(dshHome)
87
103
  const srcText = readFileSync(path.join(SRC_DIR, 'agent.cordis.yml'), 'utf8')
@@ -437,7 +453,7 @@ export function generateAndInstall(state, assignments, { dryRun = false, saveCon
437
453
  mkdirSync(path.join(dshHome, '.agent-presets'), { recursive: true })
438
454
  rmSync(dstDir, { recursive: true, force: true })
439
455
  mkdirSync(dstDir, { recursive: true })
440
- for (const name of ['preset.yml', 'restrict.mjs']) {
456
+ for (const name of PRESET_ASIS_FILES) {
441
457
  const src = path.join(SRC_DIR, name)
442
458
  const dst = path.join(dstDir, name)
443
459
  if (name === 'preset.yml' && displayName !== null) {
@@ -517,7 +533,8 @@ export function generateAndInstall(state, assignments, { dryRun = false, saveCon
517
533
  // declarative(0.1.7+):目录扫描机制已删,必须把本 preset 以一条 `- insert:`
518
534
  // 声明行写进 home patch 托管块才算安装完成(preset 元信息与写 preset.yml 同
519
535
  // 源:显示名取 displayName ?? SRC preset.yml 的 name,description / order 取
520
- // SRC;restrict.mjs 用绝对 file: URL——声明行没有「相对 preset 目录」语义)。
536
+ // SRC;restrict.mjs / delegation-labels.mjs 用绝对 file: URL——声明行没有
537
+ // 「相对 preset 目录」语义)。
521
538
  // directory(≤0.1.6):上面落盘即完成;home patch 里若有残留托管声明块
522
539
  //(宿主刚从 0.1.7 降级等场景)整块撤下自愈——残留声明行会让 profile 启动失败。
523
540
  if (wrote) {
@@ -531,6 +548,7 @@ export function generateAndInstall(state, assignments, { dryRun = false, saveCon
531
548
  order: meta.order,
532
549
  agentYmlText: generatedText,
533
550
  restrictFileUrl: pathToFileURL(path.join(dstDir, 'restrict.mjs')).href,
551
+ delegationLabelsFileUrl: pathToFileURL(path.join(dstDir, 'delegation-labels.mjs')).href,
534
552
  }),
535
553
  ])
536
554
  } else if (readHomePatchRows(dshHome).length > 0) {