fix: 停用/卸载时清掉 selectedDefault=redteam(#108)

This commit is contained in:
叫我小杨同学 committed 2026-10-24 14:42:42 +08:00
1 parent ac58b3e355
commit 90fa8fe894
6 files changed
+77 -17

No files matched your search

+1 -1
View File
@@ -338,7 +338,7 @@ Success: `profiles/<web|desktop>/node_modules/dsh-purge/package.json` exists. If
### Uninstall
**dsh-purge** beside the session title → **Clean** → **Uninstall**. Confirm the dialog: it reverts any applied patches, restores the original Harness, and restarts the current host.
**dsh-purge** beside the session title → **Clean** → **Uninstall**. Confirm the dialog: it reverts any applied patches, restores the original Harness, and restarts the current host. Disable or uninstall also resets an agent-preset default that still points at `redteam` back to `standard`, so the host can create sessions after the preset is gone.
```sh
# or from a terminal / chat
+1 -1
View File
@@ -340,7 +340,7 @@ pnpm install
### 卸载
会话标题旁的 **dsh-purge** →「清洗」→「卸载」。弹窗确认:会先还原已应用的补丁,还原回原版 Harness,再重启当前宿主。
会话标题旁的 **dsh-purge** →「清洗」→「卸载」。弹窗确认:会先还原已应用的补丁,还原回原版 Harness,再重启当前宿主。停用或卸载时会把「智能体预设」里指向红队模式的默认值改回 `standard`,避免宿主因找不到 `redteam` 而无法新建会话。
```sh
# 也可以用命令行 / 聊天
+14
View File
@@ -27,6 +27,12 @@ import * as continueRetry from "./continue-retry.js";
import * as uninstaller from "./uninstall.js";
import { readDrillGrant, writeDrillGrant } from "./drill-grant.js";
import { installRegionGate, readAllowText, writeAllowText } from "./net-scope.js";
import {
cancelClearOwnedSelectedDefault,
clearOwnedSelectedDefault,
removeStaleAgentPresetDir,
scheduleClearOwnedSelectedDefault,
} from "./preset-default.js";
import {
applyShouldRestart,
isInactiveContextError,
@@ -1455,8 +1461,16 @@ export function apply(ctx, config) {
const cfg = { ...DEFAULTS, ...(config || {}) };
if (!cfg.enabled) {
purgeLog("boot", { enabled: false });
try { clearOwnedSelectedDefault({ ctx, dshHome: core.findDshHome() }); } catch { /* 停用不被清默认挡住 */ }
try { removeStaleAgentPresetDir(core.findDshHome(), "redteam"); } catch { /* ignore */ }
return;
}
cancelClearOwnedSelectedDefault();
try {
ctx.effect(() => () => {
try { scheduleClearOwnedSelectedDefault({ ctx, dshHome: core.findDshHome() }); } catch { /* 停用不被清默认挡住 */ }
});
} catch { /* 无 effect 时靠 uninstall / enabled:false 清 */ }
purgeLog("boot", {
enabled: true,
surface: hostctl.currentSurface(),
+26 -2
View File
@@ -38,8 +38,10 @@ function listProfilePatchFiles(dshHome) {
}
for (const name of names) {
if (name === "node_modules") continue;
const fp = path.join(root, name, "cordis.patch.yml");
if (fs.existsSync(fp)) out.push(fp);
for (const file of ["cordis.patch.yml", "cordis.yml"]) {
const fp = path.join(root, name, file);
if (fs.existsSync(fp)) out.push(fp);
}
}
return out;
}
@@ -167,6 +169,28 @@ export function clearOwnedSelectedDefault({ ctx = null, dshHome = "" } = {}) {
return report;
}
let pendingClear = null;
/** 热重载会先 dispose 再 apply:延迟写盘,apply 进来就取消,避免把用户的红队默认冲掉。 */
export function scheduleClearOwnedSelectedDefault(opts = {}) {
if (pendingClear) {
clearTimeout(pendingClear);
pendingClear = null;
}
pendingClear = setTimeout(() => {
pendingClear = null;
try { clearOwnedSelectedDefault(opts); } catch { /* 停用不被清默认挡住 */ }
try { removeStaleAgentPresetDir(opts.dshHome, "redteam"); } catch { /* ignore */ }
}, 800);
if (typeof pendingClear.unref === "function") pendingClear.unref();
}
export function cancelClearOwnedSelectedDefault() {
if (!pendingClear) return;
clearTimeout(pendingClear);
pendingClear = null;
}
export function removeStaleAgentPresetDir(dshHome, id = "redteam") {
const home = dshHome || findDshHome();
if (!home || !id) return false;
+2
View File
@@ -11,6 +11,7 @@ import { homedir } from 'node:os'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { readPresetDefinition } from './preset-composition.js'
import { scheduleClearOwnedSelectedDefault } from '../preset-default.js'
/** Cordis 插件名。 */
export const name = 'dsh-purge-redteam'
@@ -605,6 +606,7 @@ export function apply(ctx) {
closed = true
if (timer) clearInterval(timer)
if (stop) clearTimeout(stop)
try { scheduleClearOwnedSelectedDefault({ ctx }) } catch { /* 清默认失败不挡停用 */ }
void undeclare()
}
}, 'dsh-purge-redteam: declare agent preset')
+33 -13
View File
@@ -6,6 +6,7 @@ import * as hostctl from "./host.js";
import * as rules from "./rules.js";
import * as updater from "./update.js";
import { clearDrillGrant } from "./drill-grant.js";
import { clearOwnedSelectedDefault, removeStaleAgentPresetDir } from "./preset-default.js";
export function wasApplied(state) {
if (!state) return false;
@@ -34,6 +35,18 @@ export function shouldDeletePluginTree(dir, dshHome = core.findDshHome()) {
return n.startsWith(`${homeN}/profiles/`);
}
function isPurgeBundleItem(item) {
if (typeof item === "string") return item === "dsh-purge" || item.startsWith("dsh-purge@");
const id = item && (item.id || item.name);
return id === "dsh-purge";
}
function stripBundleList(list) {
if (!Array.isArray(list)) return { list, changed: false };
const next = list.filter((item) => !isPurgeBundleItem(item));
return { list: next, changed: next.length !== list.length };
}
export function stripPluginFromPackage(pkg) {
if (!pkg || typeof pkg !== "object") return false;
let changed = false;
@@ -44,14 +57,15 @@ export function stripPluginFromPackage(pkg) {
}
}
for (const key of ["bundles", "plugins"]) {
if (!Array.isArray(pkg[key])) continue;
const next = pkg[key].filter((item) => {
if (typeof item === "string") return item !== "dsh-purge" && !item.startsWith("dsh-purge@");
const id = item && (item.id || item.name);
return id !== "dsh-purge";
});
if (next.length !== pkg[key].length) {
pkg[key] = next;
const top = stripBundleList(pkg[key]);
if (top.changed) {
pkg[key] = top.list;
changed = true;
}
const nested = pkg.dsh && pkg.dsh.profile ? pkg.dsh.profile[key] : null;
const nest = stripBundleList(nested);
if (nest.changed) {
pkg.dsh.profile[key] = nest.list;
changed = true;
}
}
@@ -74,11 +88,9 @@ function pkgMentionsPurge(pkg) {
if (!pkg) return false;
if (pkg.dependencies?.["dsh-purge"] || pkg.devDependencies?.["dsh-purge"]) return true;
for (const key of ["bundles", "plugins"]) {
if (!Array.isArray(pkg[key])) continue;
if (pkg[key].some((item) => {
if (typeof item === "string") return item === "dsh-purge" || item.startsWith("dsh-purge@");
return item && (item.id === "dsh-purge" || item.name === "dsh-purge");
})) return true;
if (Array.isArray(pkg[key]) && pkg[key].some(isPurgeBundleItem)) return true;
const nested = pkg.dsh && pkg.dsh.profile ? pkg.dsh.profile[key] : null;
if (Array.isArray(nested) && nested.some(isPurgeBundleItem)) return true;
}
return false;
}
@@ -254,6 +266,14 @@ export async function uninstallPurge() {
report.errors.push(`reset rules: ${e.message || e}`);
}
try {
const preset = clearOwnedSelectedDefault({ dshHome });
report.presetDefault = { live: preset.live, files: preset.files };
report.stalePresetDir = removeStaleAgentPresetDir(dshHome, "redteam");
} catch (e) {
report.errors.push(`clear selectedDefault: ${e.message || e}`);
}
const profiles = listInstalledProfiles(dshHome);
report.profiles = profiles.map((p) => p.name);
const unreg = unregisterProfiles(profiles);