agent-purge 0.2.6 → 0.2.8
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 +5 -22
- package/lib/dsh.mjs +19 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,32 +80,14 @@ agent-purge pi --persona <id> # 只这一轮用别的
|
|
|
80
80
|
|
|
81
81
|
闭源桌面端(提示词在服务端或二进制里)只做原样启动,不做任何注入——`agent-purge <h>` 会明确打印原因,不会假装生效。
|
|
82
82
|
|
|
83
|
-
###
|
|
84
|
-
|
|
85
|
-
desktop / web / headless 三个形态共用一套插件:往每个 profile 的 `node_modules/agent-purge/` 放一个 cordis 插件,在 `cordis.patch.yml` 里用 `- insert` 挂上。**不改 `app.asar`,不碰安装目录,不依赖任何第三方插件。**
|
|
83
|
+
### 常驻安装(install)
|
|
86
84
|
|
|
87
85
|
```bash
|
|
88
|
-
agent-purge install
|
|
89
|
-
agent-purge
|
|
90
|
-
agent-purge uninstall dsh # 删插件 + 剥离 patch 段,字节还原
|
|
86
|
+
agent-purge install <h> # 写进该 harness 自己的托管位置,别人启动也生效
|
|
87
|
+
agent-purge uninstall <h> # 摘掉,按字节还原
|
|
91
88
|
```
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
三个形态可以**分别开关**:Web 面板的 dsh 卡片下方有 desktop / web / headless 三个小勾,对应 `state.json` 的 `dshProfiles`。比如只在桌面版用,就把 web 和 headless 关掉;勾选只写状态文件,插件每轮现读,**不用重启**。
|
|
96
|
-
|
|
97
|
-
只想临时试一下、不碰常驻 profile:`agent-purge dsh --ask "…"`(一次性会话,进程退出即清理)。
|
|
98
|
-
|
|
99
|
-
#### 为什么不做「文件夹级人设」
|
|
100
|
-
|
|
101
|
-
很容易想到的下一步是:同一个 dsh 里,A 项目一套人设、B 项目另一套。技术上做得到——插件在 `assemble` 时能拿到 cwd,按目录前缀匹配 persona 即可。但这是**破坏性**的设计,所以刻意不做:
|
|
102
|
-
|
|
103
|
-
- **唯一真相被打破**。现在所有人设共用一个 `state.json` 指针,换一次、所有入口跟着变。按目录分叉等于把「配置」升级成「规则引擎」,之后每次改人设都要先回答「改的是哪一层」。
|
|
104
|
-
- **规则会自我繁殖**。前缀匹配必然要处理嵌套目录、glob、优先级、冲突兜底。今天一个前缀,明天就要 `.agent-purge/` 文件,后天要继承与覆盖——最后没人说得清当前生效的是哪一条。
|
|
105
|
-
- **跨入口语义分裂**。codex / claude / opencode 这些入口没有「项目」概念(有的是 PATH shim,有的是配置文件)。dsh 里按目录切、别处全局,用户无法解释「为什么这里是人设 A,那里是 B」。
|
|
106
|
-
- **卸载不再是字节还原**。一旦引入目录规则,卸载就要判断哪些规则是用户写的、哪些是工具写的,回滚成本陡增。
|
|
107
|
-
|
|
108
|
-
一句话:**agent-purge 卖的是「一份人设,所有入口,一起切换」**。做文件夹级隔离,赚的是灵活性,赔的是这句话还成不成立。真有这个需求,给每个项目放不同的人设文件、用 `agent-purge use` 手动切,比在插件里塞一套匹配引擎干净得多。
|
|
90
|
+
dsh 是唯一有多个形态的:desktop / web / headless 共用一套 cordis 插件,装在各自 profile 的 `node_modules/agent-purge/`,并在 `cordis.patch.yml` 里 `- insert` 挂上,不改 `app.asar`。装完重启一次,之后热生效;三个形态还能在 Web 面板分别勾选。
|
|
109
91
|
|
|
110
92
|
## magpie 网关插件(可选)
|
|
111
93
|
|
|
@@ -151,6 +133,7 @@ matrix.json 能力矩阵
|
|
|
151
133
|
|
|
152
134
|
- 只改提示词相关的位置,不碰二进制、不破解、不绕过任何授权。
|
|
153
135
|
- 所有改动可逆:`agent-purge uninstall <h>` / `disarm` / `--mode off`。
|
|
136
|
+
- 不做文件夹级人设隔离:技术上做得到,但会打破「一份人设、所有入口、一起切换」,卸载也不再是字节还原。
|
|
154
137
|
- 状态只存本机 `state.json`,不上传任何数据。
|
|
155
138
|
|
|
156
139
|
## 免责与合规声明
|
package/lib/dsh.mjs
CHANGED
|
@@ -21,6 +21,16 @@ export const profileDir = (p) => join(dshHome(), "profiles", p);
|
|
|
21
21
|
export const pluginDir = (p, name = RESIDENT) => join(profileDir(p), "node_modules", name);
|
|
22
22
|
const patchFile = (p) => join(profileDir(p), "cordis.patch.yml");
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* dsh 自己建过的 profile(带 package.json)。
|
|
26
|
+
* 不在这个名单里的目录绝不碰:PROFILE_TEMPLATES 里没有 desktop,我们写个
|
|
27
|
+
* 只有 patch + node_modules 的空目录,dsh 启动时会判定 "profile desktop does not exist" 直接拒绝启动。
|
|
28
|
+
* web / headless 虽然会自动 initProfile,但那是 dsh 的活,不是我们的。
|
|
29
|
+
*/
|
|
30
|
+
export function liveProfiles() {
|
|
31
|
+
return DSH_PROFILES.filter((p) => existsSync(join(profileDir(p), "package.json")));
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
const mark = (name) => [`# >>> agent-purge:${name}`, `# <<< agent-purge:${name}`];
|
|
25
35
|
|
|
26
36
|
/** 这个 package.json 是 agent-purge 写的吗(别碰同名外来目录)。 */
|
|
@@ -260,14 +270,17 @@ export function addPlugin(name, source) {
|
|
|
260
270
|
const block = `\n${begin}\n- insert:\n - id: ${name}\n name: ${name}\n${end}\n`;
|
|
261
271
|
const files = [];
|
|
262
272
|
const touched = []; // { dir, patch, before }:中途抛错时按相反顺序回滚
|
|
273
|
+
// 只装进 dsh 已经建好的 profile;一个都没有说明 dsh 还没跑过
|
|
274
|
+
const targets = liveProfiles();
|
|
275
|
+
if (targets.length === 0) throw new Error("找不到任何 dsh profile(~/.dsh/profiles/*/package.json)—— 先启动一次 dsh,让它把 profile 建好再来装");
|
|
263
276
|
// 预检:同名目录要么不存在,要么是我们的(package.json 带 agentPurge 标记),否则不碰
|
|
264
|
-
for (const p of
|
|
277
|
+
for (const p of targets) {
|
|
265
278
|
const dir = pluginDir(p, name);
|
|
266
279
|
const meta = join(dir, "package.json");
|
|
267
280
|
if (existsSync(dir) && !isOurs(meta)) throw new Error(`${dir} 已存在且不是 agent-purge 装的,拒绝覆盖`);
|
|
268
281
|
}
|
|
269
282
|
try {
|
|
270
|
-
for (const p of
|
|
283
|
+
for (const p of targets) {
|
|
271
284
|
const dir = pluginDir(p, name);
|
|
272
285
|
const patch = patchFile(p);
|
|
273
286
|
const before = existsSync(patch) ? readFileSync(patch, "utf8") : null;
|
|
@@ -299,7 +312,7 @@ export function addPlugin(name, source) {
|
|
|
299
312
|
}
|
|
300
313
|
throw e;
|
|
301
314
|
}
|
|
302
|
-
return { dirs: files, profiles:
|
|
315
|
+
return { dirs: files, profiles: targets };
|
|
303
316
|
}
|
|
304
317
|
|
|
305
318
|
/** 只撤自己那一段:目录 + patch marker,别的配置原样。 */
|
|
@@ -339,7 +352,9 @@ export function removePlugin(name) {
|
|
|
339
352
|
/** patch 里还挂着这个名字吗(status 用;不许在有其他 insert 时谎报干净)。 */
|
|
340
353
|
export function hasPlugin(name) {
|
|
341
354
|
const [begin] = mark(name);
|
|
342
|
-
|
|
355
|
+
const targets = liveProfiles();
|
|
356
|
+
if (targets.length === 0) return false;
|
|
357
|
+
return targets.every((p) => {
|
|
343
358
|
const patch = patchFile(p);
|
|
344
359
|
return existsSync(join(pluginDir(p, name), "index.js")) && existsSync(patch) && readFileSync(patch, "utf8").includes(begin);
|
|
345
360
|
});
|
package/package.json
CHANGED