diff --git a/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/.status.json b/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/.status.json new file mode 100644 index 0000000..7c1afa3 --- /dev/null +++ b/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/.status.json @@ -0,0 +1,10 @@ +{ + "status": "completed", + "completed": 4, + "failed": 0, + "pending": 0, + "total": 4, + "percent": 100, + "current": "completed", + "updated_at": "2026-04-18 00:33:30" +} diff --git a/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/proposal.md b/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/proposal.md new file mode 100644 index 0000000..7c56dac --- /dev/null +++ b/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/proposal.md @@ -0,0 +1,127 @@ +# 变更提案: fix-clashmeta-flow-map-export + +## 元信息 +```yaml +类型: 修复 +方案类型: implementation +优先级: P1 +状态: 已确认 +创建: 2026-04-18 +``` + +--- + +## 1. 需求 + +### 背景 +当前 Clash Meta 订阅模板本身是合法的 block style YAML,但服务端在导出订阅时会重新执行 +`Yaml::dump(...)`。由于 `inline` 深度设置过低,深层节点对象会被压成单行 flow map, +例如 TUIC 节点被输出为 `{ name: ..., alpn: [h3, h2, http/1.1], ... }`。 + +在部分 Clash Meta 客户端中,这种超长单行 flow map 会触发: + +`Flow map in block collection must be sufficiently indented and end with a }` + +### 目标 +- 修复 Clash Meta 订阅导出,避免代理节点被压成易出错的单行 flow map。 +- 保持改动最小,仅影响 `ClashMeta` 导出链路。 +- 不改动模板文件和其他协议导出器。 + +### 约束条件 +```yaml +时间约束: 当前回合内完成修复与静态验证 +性能约束: 不引入额外运行时依赖 +兼容性约束: 不改动节点字段语义,仅调整 YAML 序列化风格 +业务约束: 仅修复 Clash Meta;Clash / Stash 暂不联动修改 +``` + +### 验收标准 +- [ ] `app/Protocols/ClashMeta.php` 中 YAML 导出策略已调整,深层对象优先输出为 block style +- [ ] 不再依赖单行 flow map 来表示 `proxies` 中的 TUIC 等节点对象 +- [ ] 改动范围限制在 `ClashMeta` 导出逻辑与方案包记录 +- [ ] 完成最小静态验证并明确运行验证受限原因 + +--- + +## 2. 方案 + +### 技术方案 +将 `ClashMeta` 中的 `Yaml::dump($config, 2, 4, ...)` 提升为更高的 inline 深度阈值, +使 `proxies`、`proxy-groups` 等深层结构优先以 block style 导出,而不是压缩成单行 flow map。 + +本次仅修改: + +1. `app/Protocols/ClashMeta.php` 的 dump 参数; +2. 在代码旁增加一行说明性注释,明确修复目的; +3. 不改模板、不改节点字段构造、不改 Clash / Stash。 + +### 影响范围 +```yaml +涉及模块: + - ClashMeta 订阅导出: YAML 序列化风格调整 + - .helloagents 方案包: 记录本次修复过程 +预计变更文件: 2-4 +``` + +### 风险评估 +| 风险 | 等级 | 应对 | +|------|------|------| +| 导出 YAML 变得更“展开”,文件体积略增 | 低 | 只调整序列化风格,不改字段内容 | +| 同类问题在 `Clash` / `Stash` 中也存在,但本次未修 | 中 | 范围明确限制为 Clash Meta,后续可按需补修 | +| 本机无 PHP / vendor,无法直接做运行时生成验证 | 中 | 通过代码路径与参数语义做静态验证,并在结论中说明限制 | + +--- + +## 3. 技术设计(可选) + +> 本次为序列化参数修复,不涉及架构/API/数据模型设计。 + +### 架构设计 +N/A + +### API设计 +N/A + +### 数据模型 +N/A + +--- + +## 4. 核心场景 + +### 场景: 导出含长 TUIC 节点的 Clash Meta 订阅 +**模块**: Clash Meta 订阅导出 +**条件**: 用户订阅中包含较长节点名、`alpn` 数组、`udp-relay-mode` 等字段 +**行为**: 服务端生成 YAML 订阅文本 +**结果**: 节点对象以 block style 输出,避免客户端在解析长单行 flow map 时失败 + +--- + +## 5. 技术决策 + +### fix-clashmeta-flow-map-export#D001: 只调整 dump 参数,不重写节点构造逻辑 +**日期**: 2026-04-18 +**状态**: ✅采纳 +**背景**: 问题的根源在序列化风格,而不是 TUIC 节点字段本身的构造。 +**选项分析**: +| 选项 | 优点 | 缺点 | +|------|------|------| +| A: 提高 `Yaml::dump` 的 inline 深度阈值 | 改动最小,直接命中根因,不改变节点字段 | 输出会更展开 | +| B: 手工重写整个 YAML 渲染流程 | 可完全控制格式 | 风险高,改动大,超出当前修复范围 | +**决策**: 选择方案 A +**理由**: 当前问题是 flow style 输出兼容性差,调高 dump 阈值即可稳定转回 block style,最符合最小修复原则。 +**影响**: 仅影响 Clash Meta 订阅的最终文本格式,不影响节点字段语义 + +--- + +## 6. 成果设计 + +### 设计方向 +- N/A + +### 视觉要素 +- N/A + +### 技术约束 +- **可访问性**: N/A +- **响应式**: N/A diff --git a/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/tasks.md b/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/tasks.md new file mode 100644 index 0000000..9371758 --- /dev/null +++ b/.helloagents/archive/2026-04/202604180029_fix-clashmeta-flow-map-export/tasks.md @@ -0,0 +1,52 @@ +# 任务清单: fix-clashmeta-flow-map-export + +> **@status:** completed | 2026-04-18 00:30 + +```yaml +@feature: fix-clashmeta-flow-map-export +@created: 2026-04-18 +@status: completed +@mode: R2 +``` + +## 进度概览 + +| 完成 | 失败 | 跳过 | 总数 | +|------|------|------|------| +| 4 | 0 | 0 | 4 | + +--- + +## 任务列表 + +### 1. 问题定位与修复 + +- [√] 1.1 确认报错来自 Clash Meta 导出后的单行 flow map,而不是原始模板语法 | depends_on: [] +- [√] 1.2 调整 `ClashMeta` 的 YAML dump 参数,避免深层代理对象被压成单行 flow map | depends_on: [1.1] + +### 2. 验证与交付 + +- [√] 2.1 对修复做最小验证,确认改动路径和影响范围 | depends_on: [1.2] +- [√] 2.2 总结修复结果、残留风险和后续建议 | depends_on: [2.1] + +--- + +## 执行日志 + +| 时间 | 任务 | 状态 | 备注 | +|------|------|------|------| +| 2026-04-18 00:29:00 | 方案包创建 | completed | 已创建 `202604180029_fix-clashmeta-flow-map-export` | +| 2026-04-18 00:31:00 | 1.1 | completed | 已确认问题点在 `ClashMeta.php` 的 `Yaml::dump(..., 2, 4, ...)` | +| 2026-04-18 00:32:00 | 1.2 | completed | 已将 `ClashMeta` dump inline 深度提升到 `10` 并增加注释 | +| 2026-04-18 00:33:00 | 2.1 | completed | 已确认 `git diff` 仅涉及 `ClashMeta` 序列化参数与注释,方案包校验通过 | +| 2026-04-18 00:33:30 | 2.2 | completed | 已记录运行验证受限原因:本机无 `php` 与 `vendor` | + +--- + +## 执行备注 + +> 记录执行过程中的重要说明、决策变更、风险提示等 + +- 本次只修 `ClashMeta`,未联动 `Clash` / `Stash` +- 本机缺少 `php` 与 `vendor`,无法执行运行时订阅生成验证 +- 当前 diff 还包含换行符提示:Git 显示该文件后续可能按工作树策略从 LF 触碰为 CRLF diff --git a/.helloagents/archive/_index.md b/.helloagents/archive/_index.md index 8ecdcd3..500002a 100644 --- a/.helloagents/archive/_index.md +++ b/.helloagents/archive/_index.md @@ -7,6 +7,7 @@ | 时间戳 | 名称 | 类型 | 涉及模块 | 决策 | 结果 | |--------|------|------|---------|------|------| +| 202604180029 | fix-clashmeta-flow-map-export | - | - | - | ✅完成 | | 202604161703 | create-git-merge-preserve-local-skill | - | - | - | ✅完成 | | 202604161655 | merge-upstream-preserve-local | - | - | - | ✅完成 | | {YYYYMMDDHHMM} | {feature} | {类型} | {模块列表} | {feature}#D001,#D002 | ✅完成 | diff --git a/app/Protocols/ClashMeta.php b/app/Protocols/ClashMeta.php index feda7d0..ecfe7a0 100644 --- a/app/Protocols/ClashMeta.php +++ b/app/Protocols/ClashMeta.php @@ -143,7 +143,9 @@ class ClashMeta extends AbstractProtocol $config['proxy-groups'] = array_values($config['proxy-groups']); $config = $this->buildRules($config); - $yaml = Yaml::dump($config, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + // Keep nested proxy objects in block style to avoid long one-line flow maps + // that some Clash Meta clients fail to parse. + $yaml = Yaml::dump($config, 10, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); $yaml = str_replace('$app_name', admin_setting('app_name', 'XBoard'), $yaml); return response($yaml) ->header('content-type', 'text/yaml') @@ -705,4 +707,4 @@ class ClashMeta extends AbstractProtocol } } } -} \ No newline at end of file +}