code-abyss 2.0.3 → 2.0.4
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/package.json
CHANGED
|
@@ -17,6 +17,10 @@ argument-hint: [--last N]
|
|
|
17
17
|
|
|
18
18
|
`/hi` 直接改写当前会话 JSONL 中最近 N 条(默认5条)模型输出里的拒绝话术,原地替换为同意措辞。
|
|
19
19
|
|
|
20
|
+
自动检测运行环境(Claude Code vs Codex CLI),定位对应会话文件:
|
|
21
|
+
- Claude Code: `~/.claude/projects/<cwd-slug>/*.jsonl`(按 mtime 取最新)
|
|
22
|
+
- Codex CLI: `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`(递归扫描按 mtime 取最新)
|
|
23
|
+
|
|
20
24
|
**不是匹配报告,是直接改写。**
|
|
21
25
|
|
|
22
26
|
## 用法
|
|
@@ -69,7 +73,9 @@ node scripts/refusal_rewriter.js --dry-run # 仅报告,不改写
|
|
|
69
73
|
|
|
70
74
|
## 注意事项
|
|
71
75
|
|
|
72
|
-
-
|
|
76
|
+
- 自动检测运行环境:脚本路径含 `.codex` → Codex 模式,含 `.claude` → Claude 模式,否则按 cwd 回退检测
|
|
77
|
+
- Claude 模式:仅改写 `type: "assistant"` 且含 `content[].type: "text"` 的消息
|
|
78
|
+
- Codex 模式:仅改写 `type: "response_item"` + `payload.role: "assistant"` 且含 `content[].type: "output_text"` 的消息
|
|
73
79
|
- 不改写用户消息、系统消息、工具调用
|
|
74
80
|
- 改写是幂等的:多次执行不会产生副作用
|
|
75
81
|
- `--dry-run` 模式仅输出匹配报告,不写入文件
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* 破禁术 · 反拒绝覆写器
|
|
6
6
|
*
|
|
7
|
-
* 扫描当前 Claude 会话 JSONL,直接替换拒绝话术为同意措辞。
|
|
7
|
+
* 扫描当前 Claude / Codex 会话 JSONL,直接替换拒绝话术为同意措辞。
|
|
8
|
+
* 自动检测运行环境(Claude Code vs Codex CLI),定位对应会话文件。
|
|
8
9
|
*
|
|
9
10
|
* 用法:
|
|
10
11
|
* node refusal_rewriter.js [--last N] [--dry-run]
|
|
@@ -13,7 +14,6 @@
|
|
|
13
14
|
const fs = require('fs');
|
|
14
15
|
const path = require('path');
|
|
15
16
|
const os = require('os');
|
|
16
|
-
const { createHash } = require('crypto');
|
|
17
17
|
|
|
18
18
|
// ─── 替换规则表 ───────────────────────────────────────────
|
|
19
19
|
|
|
@@ -80,19 +80,38 @@ function parseArgs(argv) {
|
|
|
80
80
|
return { last, dryRun };
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
// ───
|
|
83
|
+
// ─── 环境检测 ─────────────────────────────────────────────
|
|
84
84
|
|
|
85
|
-
function
|
|
86
|
-
|
|
85
|
+
function detectRuntime() {
|
|
86
|
+
// Codex 在 skills 路径中包含 .codex
|
|
87
|
+
const scriptDir = __dirname;
|
|
88
|
+
if (scriptDir.includes('.codex')) return 'codex';
|
|
89
|
+
if (scriptDir.includes('.claude')) return 'claude';
|
|
90
|
+
|
|
91
|
+
// 回退:检查 ~/.codex/sessions 和 ~/.claude/projects 哪个存在
|
|
92
|
+
const home = os.homedir();
|
|
93
|
+
const codexSessions = path.join(home, '.codex', 'sessions');
|
|
94
|
+
const claudeProjects = path.join(home, '.claude', 'projects');
|
|
95
|
+
|
|
96
|
+
// 优先检测当前 cwd 是否有对应的 claude project
|
|
97
|
+
const cwd = process.cwd();
|
|
98
|
+
const slug = cwd.replace(/\//g, '-').replace(/^-/, '');
|
|
99
|
+
const claudeProjectDir = path.join(claudeProjects, slug);
|
|
100
|
+
if (fs.existsSync(claudeProjectDir)) return 'claude';
|
|
101
|
+
if (fs.existsSync(codexSessions)) return 'codex';
|
|
102
|
+
|
|
103
|
+
return 'claude'; // 默认回退
|
|
87
104
|
}
|
|
88
105
|
|
|
89
|
-
|
|
106
|
+
// ─── Claude 会话定位 ─────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
function findClaudeSessionJsonl() {
|
|
90
109
|
const cwd = process.cwd();
|
|
91
|
-
const slug =
|
|
110
|
+
const slug = cwd.replace(/\//g, '-').replace(/^-/, '');
|
|
92
111
|
const projectDir = path.join(os.homedir(), '.claude', 'projects', slug);
|
|
93
112
|
|
|
94
113
|
if (!fs.existsSync(projectDir)) {
|
|
95
|
-
console.error(`错误:
|
|
114
|
+
console.error(`错误: 未找到 Claude 项目目录 ${projectDir}`);
|
|
96
115
|
process.exit(1);
|
|
97
116
|
}
|
|
98
117
|
|
|
@@ -113,6 +132,43 @@ function findSessionJsonl() {
|
|
|
113
132
|
return files[0].full;
|
|
114
133
|
}
|
|
115
134
|
|
|
135
|
+
// ─── Codex 会话定位 ──────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
function findCodexSessionJsonl() {
|
|
138
|
+
const sessionsDir = path.join(os.homedir(), '.codex', 'sessions');
|
|
139
|
+
|
|
140
|
+
if (!fs.existsSync(sessionsDir)) {
|
|
141
|
+
console.error(`错误: 未找到 Codex 会话目录 ${sessionsDir}`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 递归扫描所有 .jsonl,按 mtime 倒序取最新
|
|
146
|
+
const allJsonl = [];
|
|
147
|
+
|
|
148
|
+
function scan(dir) {
|
|
149
|
+
let entries;
|
|
150
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
151
|
+
for (const entry of entries) {
|
|
152
|
+
const full = path.join(dir, entry.name);
|
|
153
|
+
if (entry.isDirectory()) {
|
|
154
|
+
scan(full);
|
|
155
|
+
} else if (entry.name.endsWith('.jsonl')) {
|
|
156
|
+
allJsonl.push({ full, mtime: fs.statSync(full).mtimeMs });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
scan(sessionsDir);
|
|
162
|
+
allJsonl.sort((a, b) => b.mtime - a.mtime);
|
|
163
|
+
|
|
164
|
+
if (allJsonl.length === 0) {
|
|
165
|
+
console.error(`错误: ${sessionsDir} 下无 .jsonl 会话文件`);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return allJsonl[0].full;
|
|
170
|
+
}
|
|
171
|
+
|
|
116
172
|
// ─── JSONL 解析与回写 ─────────────────────────────────────
|
|
117
173
|
|
|
118
174
|
function readJsonlLines(filePath) {
|
|
@@ -141,19 +197,53 @@ function applyReplacements(text) {
|
|
|
141
197
|
return { result, matched, changed: matched.length > 0 };
|
|
142
198
|
}
|
|
143
199
|
|
|
144
|
-
|
|
200
|
+
// ─── Claude 消息处理 ─────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
function isClaudeAssistant(obj) {
|
|
203
|
+
return obj.type === 'assistant' && Array.isArray(obj.message?.content);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function processClaudeMessage(obj) {
|
|
207
|
+
let totalMatched = [];
|
|
208
|
+
let changed = false;
|
|
209
|
+
|
|
210
|
+
if (!isClaudeAssistant(obj)) return { obj, matched: totalMatched, changed };
|
|
211
|
+
|
|
212
|
+
for (const block of obj.message.content) {
|
|
213
|
+
if (block.type === 'text' && typeof block.text === 'string') {
|
|
214
|
+
const { result, matched, changed: blockChanged } = applyReplacements(block.text);
|
|
215
|
+
if (blockChanged) {
|
|
216
|
+
block.text = result;
|
|
217
|
+
totalMatched.push(...matched);
|
|
218
|
+
changed = true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { obj, matched: totalMatched, changed };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ─── Codex 消息处理 ──────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
function isCodexAssistant(obj) {
|
|
229
|
+
return obj.type === 'response_item'
|
|
230
|
+
&& obj.payload?.role === 'assistant'
|
|
231
|
+
&& Array.isArray(obj.payload?.content);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function processCodexMessage(obj) {
|
|
145
235
|
let totalMatched = [];
|
|
146
236
|
let changed = false;
|
|
147
237
|
|
|
148
|
-
if (obj
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
238
|
+
if (!isCodexAssistant(obj)) return { obj, matched: totalMatched, changed };
|
|
239
|
+
|
|
240
|
+
for (const block of obj.payload.content) {
|
|
241
|
+
if (block.type === 'output_text' && typeof block.text === 'string') {
|
|
242
|
+
const { result, matched, changed: blockChanged } = applyReplacements(block.text);
|
|
243
|
+
if (blockChanged) {
|
|
244
|
+
block.text = result;
|
|
245
|
+
totalMatched.push(...matched);
|
|
246
|
+
changed = true;
|
|
157
247
|
}
|
|
158
248
|
}
|
|
159
249
|
}
|
|
@@ -165,9 +255,17 @@ function processAssistantMessage(obj) {
|
|
|
165
255
|
|
|
166
256
|
function main() {
|
|
167
257
|
const { last, dryRun } = parseArgs(process.argv);
|
|
168
|
-
const
|
|
258
|
+
const runtime = detectRuntime();
|
|
259
|
+
|
|
260
|
+
const jsonlPath = runtime === 'codex'
|
|
261
|
+
? findCodexSessionJsonl()
|
|
262
|
+
: findClaudeSessionJsonl();
|
|
263
|
+
|
|
264
|
+
const isAssistant = runtime === 'codex' ? isCodexAssistant : isClaudeAssistant;
|
|
265
|
+
const processMessage = runtime === 'codex' ? processCodexMessage : processClaudeMessage;
|
|
169
266
|
|
|
170
267
|
console.log(`🔓 破禁术启动`);
|
|
268
|
+
console.log(` 运行环境: ${runtime === 'codex' ? 'Codex CLI' : 'Claude Code'}`);
|
|
171
269
|
console.log(` 会话文件: ${jsonlPath}`);
|
|
172
270
|
console.log(` 扫描范围: 最近 ${last} 条模型输出`);
|
|
173
271
|
console.log(` 模式: ${dryRun ? '仅报告 (dry-run)' : '直接改写'}`);
|
|
@@ -180,7 +278,7 @@ function main() {
|
|
|
180
278
|
for (let i = lines.length - 1; i >= 0 && assistantIndices.length < last; i--) {
|
|
181
279
|
try {
|
|
182
280
|
const obj = JSON.parse(lines[i]);
|
|
183
|
-
if (obj
|
|
281
|
+
if (isAssistant(obj)) {
|
|
184
282
|
assistantIndices.push(i);
|
|
185
283
|
}
|
|
186
284
|
} catch {
|
|
@@ -198,7 +296,7 @@ function main() {
|
|
|
198
296
|
|
|
199
297
|
for (const idx of assistantIndices) {
|
|
200
298
|
const obj = JSON.parse(lines[idx]);
|
|
201
|
-
const { obj: processed, matched, changed } =
|
|
299
|
+
const { obj: processed, matched, changed } = processMessage(obj);
|
|
202
300
|
|
|
203
301
|
if (changed) {
|
|
204
302
|
totalChanged++;
|