fix: 停用/卸载时清掉 selectedDefault=redteam(#108)
This commit is contained in:
1 parent
ac58b3e355
commit
90fa8fe894
6 files changed
+77
-17
No files matched your search
@@ -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
@@ -340,7 +340,7 @@ pnpm install
|
||||
|
||||
### 卸载
|
||||
|
||||
会话标题旁的 **dsh-purge** →「清洗」→「卸载」。弹窗确认:会先还原已应用的补丁,还原回原版 Harness,再重启当前宿主。
|
||||
会话标题旁的 **dsh-purge** →「清洗」→「卸载」。弹窗确认:会先还原已应用的补丁,还原回原版 Harness,再重启当前宿主。停用或卸载时会把「智能体预设」里指向红队模式的默认值改回 `standard`,避免宿主因找不到 `redteam` 而无法新建会话。
|
||||
|
||||
```sh
|
||||
# 也可以用命令行 / 聊天
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user