# 机制速查(一)· Profile 层 cordis patch / Skill 装载机制 / Skill 管理面 > **归属**:技能 `dsh-change-workflow` 的详情档(**按需读**,不是每次都要读)。 > **本档覆盖**:Profile 层 cordis patch 机制 · Skill 装载机制 · Skill 管理面 · dsh 沙箱与权限预设机制(原行 L481–L504 + L555–L600)。 > **主文件 / 判据与流程主干** = `../SKILL.md`(§1 六阶段流程 · §2 红线 R1–R8 原文速查 · §3 本机 Git Bash 环境坑 · §4 并行调度结论)。 > **来源**:2026-09-22「技能重组线」把 `../SKILL.md` 的 L481–L504 + L555–L600 段**逐行原样**下沉到本文件,未改一字。 > **跨档引用**:正文里的「§N / 见 §8 坑 N / 见下表」等编号,用 `../SKILL.md` 末节「详情索引」的原章节列定位。 > **维护**:本文件与 `../SKILL.md` 的指针行成对;改内容时同时核对主文件的指针描述是否仍准确。 --- ### Profile 层 cordis patch 机制(角色化 UI 裁剪,档案 09) - client 插件行 id = **短 id**(`ui-settings-models`,非包名),见 `dsh --profile web --dump-config`。 - **禁用官方 client 插件**(如设置面板「模型」分区对普通用户):profile `cordis.patch.yml` 写 `- id: <短id>\n name: "@deepseek-ai/<包名>"\n disabled: true`(dsh-app-boot applyEntryPatches:非 insert patch 按 id 合入 overrides)。`--dump-config` 验证:目标行出现 `disabled: true` + `# == ... patched by ` 注释。 - **生效必须重启实例**(client bundle 启动时打包);`patchReload: live` 对 client 插件增减**不生效**(实测 5 轮)。 - 当前生产 spawn 不带 `--patch` overlay(enablePatch=false),disable 只能写 profile 层 `cordis.patch.yml`。 - 幂等工具:`/opt/dshs/ensure-role-profile-patch.cjs [--restart] `(全量=非 admin 用户;含管理标记头则跳过;非默认空内容不覆盖)。admin profile 不动 = admin 保留该分区。 ### Skill 装载机制(全员共享只读技能 = bundledSkillDir,档案 10) - 技能由 agent preset 注册:`@deepseek-ai/dsh-agent-presets/presets/standard/agent.cordis.yml` L83-88(`skill-filesystem` + `tool-skill`),preset 无 config 块 → provider 配置走 **env**。 - 技能分层 rank(`dsh-skill-filesystem/lib/index.js` roots()):project-dsh 100(`<项目>/.dsh/skills`)/ project-agents 200 / custom 300 / user-dsh 400(`$DSH_HOME/skills`,每用户独立)/ user-agents 500 / **bundled 600(`$DSH_BUNDLED_SKILL_DIR`,`trustedHost: true` 只读共享)**。 - 扫描粒度:`discoverRoot` **只扫 1 层**(root 下每目录须含 `SKILL.md`;或直接 .md 文件);`root/主技能/subskills/子技能/SKILL.md` **不会被发现** → subskill 要独立可调需**平铺**到技能根。 - **编排器 env 白名单(关键卡点)**:`/opt/dshs/lib/supervisor/spawn.js` `ALLOWED_ENV` 仅 PATH/HOME/USER/TMP/LANG 等基础变量;`baseEnv()` = `{...scrubEnv(process.env), HOME, DSH_HOME, DEEPSEEK_API_KEY}` → **新 env 变量必须同时加白名单 + baseEnv 显式注入**(src/*.ts 同步),否则被 scrub 丢弃。这是改编排器(自研,非红线 2 对象)。 - MCN V1.0 技能跨平台部署注意:`subskills/browser-harness/envs/` 为 **Windows venv**(`Scripts/*.exe`),Linux 服务器不可用需重建;海外节点直连抖音风控风险高,数据类优先 RedFox API。 ### Skill 管理面(编排器 API + 静态页,档案 11) - **dsh 技能名硬规则**:`/^[a-z0-9]+(?:-[a-z0-9]+)*$/`(`@deepseek-ai/dsh-skill/lib/index.js` SKILL_NAME)—— **中文名技能 dsh 静默丢弃**。MCN 等含中文名技能需先改 `SKILL.md` frontmatter `name` 为 kebab(如 `mcn-workstation`)。 - **API 形态**:`/api/skills/shared`(admin 三件套 GET/POST/DELETE)+ `/api/skills/mine`(任何登录用户);上传 base64 body `{ file, filename, force? }`;后端走系统 `tar/unzip` 解压、路径穿越校验、合法名校验。包结构:单顶层目录 + 含 SKILL.md。 - **落盘属主**:shared → root:root 0755(OS 权限兜底只读);mine → `home` 属主用户 uid/gid(确保用户可改自己的技能)。踩坑:`chownTree` 须 chown 顶层目录自身(首次实现只 chown 子项,目录保留 tar 包内 owner)。 - **watch 即时生效**:`skill-filesystem` 对共享根 watchManager 监听 addDir/unlinkDir/SKILL.md → 自动 invalidate registry(**无需重启实例**,源码已核实)。 - **编排器 env 注入 DSH_BUNDLED_SKILL_DIR**:spawn.ts ALLOWED_ENV 加变量 + orchestrator.ts baseEnv 显式注入(`config.bundledSkillDir`,默认 `/bundled-skills`);这是上一节「全员共享只读技能层」的实例侧落地。 ## dsh 沙箱与权限预设机制(2026-09-11 源码核实,改默认值必看) **两层隔离,别混淆**: | 层 | 谁提供 | 说明 | |---|---|---| | **内层** | dsh 自带沙箱 `read-only` / `workspace-write` / `danger-full-access` | 靠 Landlock 或 bubblewrap 后端。**本机不可用**:内核 5.10(Landlock 需 5.13+,LSM 无 landlock),bwrap 嵌套探测失败 → **fail-closed 拒绝执行任何 shell**(`@deepseek-ai/dsh-sandbox/lib/index.js:185`)| | **外层** | 平台 `systemd-run --scope` + `bwrap` + `setpriv` | **真正的边界**:mount 路径隔离 + uid 隔离 + cgroup(**基础 448M → 上界 1024M**:`MemoryHigh=MIN` 软限 + `MemoryMax=MAX` 硬限;**与插件开关无关**(档案 96)/150%/128)。内层失效不影响它 | **权限预设插件**(row `@deepseek-ai/dsh-permission-presets`,短 id 一般即 `permission-presets`): - 内置预设表(**sandbox 与 approval 成对绑定**,这是关键坑): ```js 'workspace-write': { sandbox: 'workspace-write', approval: 'ask' } 'danger-full-access': { sandbox: 'danger-full-access', approval: 'never' } // ← 选它=同时摘掉审批 ``` - `defaultPreset = config.defaultPreset ?? inferredDefault`;默认落 `workspace-write`(→ 本机即「shell 全废」)。 - 设置持久化命名空间 = **`permission`**(settings key `permission.defaultPreset`,值必须是 `presets` 里的键名)。 **改默认值的官方做法(不碰官方主程序,R2 合规)**: > ⭐ **首选:`DSH_PERMISSION_MODE` 环境变量**(2026-09-11 定案,档案 33)。 > 官方 `@deepseek-ai/dsh-base/cordis.patch.yml:217/233` 直接读它: > ```yaml > mode: !!js process.env.DSH_PERMISSION_MODE ?? 'workspace-write' > policy: !!js "(process.env.DSH_PERMISSION_MODE ?? 'workspace-write') === 'danger-full-access' ? 'never' : 'ask'" > ``` > 在编排器 spawn 时注入 `DSH_PERMISSION_MODE=danger-full-access` 即可 **全局默认「完全权限」+ 免审批**,且: > ① 不必铺 profile(env 随 spawn,**新用户自动生效**);② 完全复用官方逻辑(预设表 / 设置 UI 都不动)。 > 落地两处:`spawn.ts` 的 `ALLOWED_ENV` 加该键 + `orchestrator.ts` 的 `baseEnv()` 注入(值取 `process.env.X ?? 'danger-full-access'`,运维可覆盖)。 > ⚠️ **副作用**:权限档位是**会话级播种**(会话创建时写 `permission/preset` / `sandbox/mode` / `approval/policy` 三条种子事件)→ **旧会话不跟随新默认**,必须**新建会话**才生效。给用户的话术是「新开一个会话」,不是「刷新页面」。 其余做法(需要更细粒度时才用): 1. **profile 层 `cordis.patch.yml` 改该行 `config`** —— 已核实 `applyEntryPatches` 里 `const { id, insert, name, ...overrides } = patch` → **patch 的其它键会作为 overrides 合入目标行**,所以 `config:` 可覆盖。 注意 **`config` 是整体替换不是深合并** → 必须把内置的两个预设一并写全。 2. 或改设置:`$DSH_HOME/settings.yaml` 加 ```yaml permission: defaultPreset: ``` 3. **profile 是每用户一份** → 要像 `ensure-role-profile-patch.cjs` 那样给每个用户(含新用户)铺;改完**必须重启该用户实例**才生效。 4. 用户仍可在实例「设置 → 权限」自助切换(`ui-permission`),这是设计内的。 **推荐形态**:不要直接用 `danger-full-access`(会连审批一起摘掉)。在 patch 里**新增一个自定义预设**: `sandbox: danger-full-access` + `approval: ask`,语义 = 「隔离由平台容器提供,高风险操作仍需确认」。