dsh-syn-config 0.8.0 → 0.8.2
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 +4 -0
- package/assets/syn-dsh.md +17 -0
- package/lib/core/commands.js +10 -1
- package/lib/index.js +29 -3
- package/package.json +1 -1
- package/scripts/verify.mjs +16 -0
package/README.md
CHANGED
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
> 点它打开面板:顶部是体检结论(阻塞/警告数),下面四个动作——**体检 / 拉取计划 / 推送计划 / 密钥扫描**——都是对只读路由的 `GET`,面板只能看、不能写;每个结果旁边有「复制命令」,真正的写操作仍要 `/syn-dsh … now`。
|
|
19
19
|
> `syn-dsh` 技能由插件自带(`assets/syn-dsh.md`,rank 600),不再放在配置仓库里。
|
|
20
20
|
>
|
|
21
|
+
> **升级插件后要 push 清单。** 配置仓库 `profiles/<profile>/package.json` 是 `apply.sh` 的来源;只改本机不改仓库,下次 `apply.sh --yes` 会把本机清单覆盖回仓库版本(曾因此把 `dsh-syn-config` 回滚成旧的 `dsh-config-sync`,状态灯随之变红)。`verify.mjs` 现在会比对这两份清单,不一致直接失败并给出修复路径。想彻底不漂移:`/syn-dsh push now`(或 `apply.sh` 之后手工提交该文件)。
|
|
22
|
+
>
|
|
23
|
+
> **密钥只由人注入。** 工具与斜杠命令都拒绝写 `inject`/`refresh`:`/syn-dsh secrets` 只放 `scan`/`fingerprint`,`dsh_sync_secrets` 在 `dry_run:false` 时直接拒绝,并提示自己执行 `bash ~/dsh-config/scripts/apply.sh --yes`。确需让工具代写,本机在插件行加 `allowSecretInject: true`(默认关闭)。
|
|
24
|
+
>
|
|
21
25
|
> 设计理由与后续阶段见主仓库的调研文档:`../research/syn-dsh-plugin/PLAN.md`。
|
|
22
26
|
|
|
23
27
|
## 为什么是插件,而不是又一层 shell
|
package/assets/syn-dsh.md
CHANGED
|
@@ -221,3 +221,20 @@ cd ~/dsh-config && bash scripts/cross-platform-selftest.sh
|
|
|
221
221
|
2. **变化**:改动的技能名(超过 10 个只列前 10 + 共几个);是否动了配置/插件
|
|
222
222
|
3. **生效**:新开会话(技能)/ 重启客户端(插件)
|
|
223
223
|
4. **待办**:缺密钥、冲突未合、推送失败等
|
|
224
|
+
|
|
225
|
+
## 密钥注入只由人执行
|
|
226
|
+
|
|
227
|
+
`/syn-dsh secrets` 只支持 `scan` / `fingerprint`(只读)。`inject` / `refresh` 会写真实密钥进已部署配置,**只能由人执行**:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
bash ~/dsh-config/scripts/apply.sh --yes # 从 ~/.dsh/secrets.env 注入
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
要让模型工具代写,需在本机插件行显式加 `allowSecretInject: true`(默认关闭)。
|
|
234
|
+
|
|
235
|
+
## 升级插件后必须同步清单
|
|
236
|
+
|
|
237
|
+
配置仓库里的 `profiles/<profile>/package.json` 与 `cordis.patch.yml` 是 `apply.sh` 的**来源**。只在本机改插件、不推仓库,下一次 `apply.sh --yes` 会把本机清单覆盖回仓库版本(真发生过:`dsh-syn-config` 被回滚成旧的 `dsh-config-sync`,状态灯变红)。
|
|
238
|
+
|
|
239
|
+
- 升级后:`/syn-dsh push now`(或手动提交该文件)
|
|
240
|
+
- 自检:`node scripts/verify.mjs` 会比对「本机清单」与「仓库清单」,不一致直接失败
|
package/lib/core/commands.js
CHANGED
|
@@ -172,7 +172,16 @@ export function registerCommands(ctx, settings) {
|
|
|
172
172
|
|
|
173
173
|
if (subcommand === 'secrets') {
|
|
174
174
|
const action = (tokens[1] ?? 'scan').toLowerCase()
|
|
175
|
-
|
|
175
|
+
// The slash command cannot inject: writing a real value into the live
|
|
176
|
+
// configuration stays a human action (`apply.sh`), so a command that
|
|
177
|
+
// is easy to type cannot be the thing that leaks a key.
|
|
178
|
+
if (action !== 'scan' && action !== 'fingerprint') {
|
|
179
|
+
return {
|
|
180
|
+
kind: 'error',
|
|
181
|
+
text: `secrets ${action}: 只读命令只支持 scan / fingerprint;注入真值请自己执行 \`bash ~/dsh-config/scripts/apply.sh --yes\``,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const readOnly = true
|
|
176
185
|
const result = await runSecretsAction({
|
|
177
186
|
action,
|
|
178
187
|
repoPath,
|
package/lib/index.js
CHANGED
|
@@ -172,7 +172,11 @@ export function resolveSettings(config = {}) {
|
|
|
172
172
|
const dshHome = typeof config.dshHome === 'string' && config.dshHome.trim() !== ''
|
|
173
173
|
? config.dshHome.trim()
|
|
174
174
|
: undefined
|
|
175
|
-
|
|
175
|
+
// Injecting a real secret into the live configuration is a human action.
|
|
176
|
+
// The model-callable tool refuses it unless this machine explicitly opts in,
|
|
177
|
+
// so an agent can never turn "sync my config" into "write my API key".
|
|
178
|
+
const allowSecretInject = config.allowSecretInject === true
|
|
179
|
+
return { repoPath, gitTimeoutMs, fetchOnStatus: config.fetchOnStatus === true, profile, dshHome, allowSecretInject }
|
|
176
180
|
}
|
|
177
181
|
|
|
178
182
|
/** Result shape of the health-check tool. */
|
|
@@ -741,13 +745,35 @@ function apply(ctx, config = {}) {
|
|
|
741
745
|
async execute(args, exec) {
|
|
742
746
|
const requested = typeof args?.repo === 'string' && args.repo.trim() !== '' ? args.repo.trim() : settings.repoPath
|
|
743
747
|
const profile = typeof args?.profile === 'string' && args.profile.trim() !== '' ? args.profile.trim() : settings.profile
|
|
748
|
+
const action = String(args?.action ?? '')
|
|
749
|
+
const dryRun = typeof args?.dry_run === 'boolean' ? args.dry_run : true
|
|
750
|
+
// Human-only gate: `inject` and `refresh` write the real value into the
|
|
751
|
+
// deployed configuration. Refuse them from the tool unless the machine
|
|
752
|
+
// opted in, and say where the human path is.
|
|
753
|
+
if ((action === 'inject' || action === 'refresh') && dryRun === false && settings.allowSecretInject !== true) {
|
|
754
|
+
return {
|
|
755
|
+
action,
|
|
756
|
+
ok: false,
|
|
757
|
+
dryRun: false,
|
|
758
|
+
changed: [],
|
|
759
|
+
leftover: [],
|
|
760
|
+
hits: [],
|
|
761
|
+
fingerprints: [],
|
|
762
|
+
historyHits: [],
|
|
763
|
+
notes: [
|
|
764
|
+
'refused: secrets are injected by the human, not by the agent',
|
|
765
|
+
'run it yourself: `bash ~/dsh-config/scripts/apply.sh --yes`',
|
|
766
|
+
'or set `allowSecretInject: true` in this plugin row if you want the tool to do it',
|
|
767
|
+
],
|
|
768
|
+
}
|
|
769
|
+
}
|
|
744
770
|
try {
|
|
745
771
|
const result = await runSecretsAction({
|
|
746
|
-
action
|
|
772
|
+
action,
|
|
747
773
|
repoPath: requested,
|
|
748
774
|
dshHome: settings.dshHome,
|
|
749
775
|
profile,
|
|
750
|
-
dryRun
|
|
776
|
+
dryRun,
|
|
751
777
|
deep: args?.deep === true,
|
|
752
778
|
})
|
|
753
779
|
return { ...result, historyHits: result.historyHits ?? [] }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-syn-config",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Cross-platform (macOS + Windows) DeepSeek Harness plugin that syncs DSH config — skills, profile settings, AGENTS.md — with a private dsh-config repository.",
|
|
6
6
|
"type": "module",
|
package/scripts/verify.mjs
CHANGED
|
@@ -136,6 +136,22 @@ async function main() {
|
|
|
136
136
|
for (const note of status.notes) console.log(` note: ${note}`)
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
// The repo copy of the profile manifest is what `apply.sh` restores from.
|
|
140
|
+
// If it still names another package, the next apply rolls this machine back
|
|
141
|
+
// to it — which is exactly how a working install "became unavailable" once.
|
|
142
|
+
const repoManifestPath = join(repoPath, 'profiles', profileName, 'package.json')
|
|
143
|
+
if (existsSync(repoManifestPath)) {
|
|
144
|
+
const repoManifest = JSON.parse(readFileSync(repoManifestPath, 'utf8'))
|
|
145
|
+
const liveSync = Object.keys(manifest.dependencies ?? {}).filter((dep) => dep.includes('syn-config') || dep.includes('config-sync'))
|
|
146
|
+
const repoSync = Object.keys(repoManifest.dependencies ?? {}).filter((dep) => dep.includes('syn-config') || dep.includes('config-sync'))
|
|
147
|
+
const agree = liveSync.length === 1 && repoSync.length === 1 && liveSync[0] === repoSync[0]
|
|
148
|
+
record(step(agree, 'repo copy agrees with this install', agree ? liveSync[0] : `live=${liveSync.join(',') || 'none'} repo=${repoSync.join(',') || 'none'}`))
|
|
149
|
+
if (!agree) {
|
|
150
|
+
console.log(' the next `apply.sh --yes` would overwrite this profile from the repo:')
|
|
151
|
+
console.log(` fix: update ${repoManifestPath} (dependencies + dsh.profile.bundles), then push`)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
139
155
|
console.log('')
|
|
140
156
|
if (failures.length > 0) {
|
|
141
157
|
console.error(`[verify] FAILED — ${failures.length} check(s) did not pass.`)
|