@qqq123456789/codex-doctor 0.7.0 → 0.8.0
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 +1 -1
- package/tool/cli.mjs +31 -2
- package/tool/sessions.mjs +28 -0
package/package.json
CHANGED
package/tool/cli.mjs
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
import { collectChecks, summarize, renderHuman } from './checks.mjs';
|
|
4
4
|
import { cleanTarget } from './clean.mjs';
|
|
5
5
|
import { backupConfig, restoreBackup, resetAuth, listVersions, listArchives, deleteArchive, checkUpdate } from './ops.mjs';
|
|
6
|
-
import { listSessions, searchSessions } from './sessions.mjs';
|
|
6
|
+
import { listSessions, searchSessions, readTranscript } from './sessions.mjs';
|
|
7
7
|
import { CODEX_DIR } from './util.mjs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
|
|
10
|
-
const VERSION = '0.
|
|
10
|
+
const VERSION = '0.8.0';
|
|
11
11
|
|
|
12
12
|
const HELP = `codex-doctor v${VERSION} — Codex CLI 维护与排障工具(零依赖)
|
|
13
13
|
|
|
@@ -29,6 +29,7 @@ const HELP = `codex-doctor v${VERSION} — Codex CLI 维护与排障工具(零
|
|
|
29
29
|
versions [-n N] 查看 openai/codex 最近 N 个版本(默认 10)
|
|
30
30
|
sessions [-n N] [--dir 关键字] 浏览历史会话:时间、目录、来源、首条提问预览
|
|
31
31
|
[--search 关键词] [--deep] 按关键词搜索会话(--deep 全文扫描)
|
|
32
|
+
--show [--search 关键词] [--pick N] [--full] 查看会话完整对话
|
|
32
33
|
update 查询 npm 最新版本与更新方式
|
|
33
34
|
help 显示本帮助
|
|
34
35
|
|
|
@@ -48,6 +49,9 @@ function parseFlags(args) {
|
|
|
48
49
|
else if (a === '--dir') flags.dir = args[++i];
|
|
49
50
|
else if (a === '--search') flags.search = args[++i];
|
|
50
51
|
else if (a === '--deep') flags.deep = true;
|
|
52
|
+
else if (a === '--show') flags.show = true;
|
|
53
|
+
else if (a === '--pick') flags.pick = Number(args[++i]);
|
|
54
|
+
else if (a === '--full') flags.full = true;
|
|
51
55
|
else if (a === '--out') flags.out = args[++i];
|
|
52
56
|
else rest.push(a);
|
|
53
57
|
}
|
|
@@ -134,6 +138,31 @@ async function main() {
|
|
|
134
138
|
break;
|
|
135
139
|
}
|
|
136
140
|
case 'sessions': {
|
|
141
|
+
if (flags.show) {
|
|
142
|
+
// 展示会话完整对话:默认最近一次;--search 按关键词定位;--pick N 选第 N 个命中
|
|
143
|
+
const items = flags.search
|
|
144
|
+
? searchSessions({ keyword: flags.search, limit: 20, deep: true })
|
|
145
|
+
: listSessions({ limit: 20 });
|
|
146
|
+
if (items.length === 0) {
|
|
147
|
+
console.log(flags.search ? `没有找到包含「${flags.search}」的会话` : '~/.codex/sessions 里没有会话记录');
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
const pick = Math.min(Math.max(Number(flags.pick) || 1, 1), items.length);
|
|
151
|
+
const item = items[pick - 1];
|
|
152
|
+
console.log(`== 会话 ${item.date} @ ${item.dirName} ==`);
|
|
153
|
+
console.log(`文件: ${item.file}\n`);
|
|
154
|
+
const transcript = readTranscript(item.file, { maxLen: flags.full ? Infinity : 400 });
|
|
155
|
+
if (!transcript || transcript.length === 0) {
|
|
156
|
+
console.log('(该会话没有可展示的对话消息)');
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
for (const msg of transcript) {
|
|
160
|
+
const who = msg.role === 'user' ? '[用户]' : '[Codex]';
|
|
161
|
+
console.log(`${who} ${msg.text}\n`);
|
|
162
|
+
}
|
|
163
|
+
console.log(`(共 ${transcript.length} 条消息${flags.full ? '' : ',单条超 400 字已截断,--full 查看全文'})`);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
137
166
|
if (flags.search) {
|
|
138
167
|
const items = searchSessions({
|
|
139
168
|
keyword: flags.search,
|
package/tool/sessions.mjs
CHANGED
|
@@ -61,6 +61,7 @@ export function listSessions({ limit = 10, cwdFilter = null } = {}) {
|
|
|
61
61
|
const { meta, preview } = extract(readHead(f));
|
|
62
62
|
if (!meta) continue;
|
|
63
63
|
items.push({
|
|
64
|
+
file: f,
|
|
64
65
|
date: meta.date || st.mtime.toISOString().slice(0, 16).replace('T', ' '),
|
|
65
66
|
dirName: meta.cwd ? path.basename(meta.cwd) : '?',
|
|
66
67
|
cwd: meta.cwd,
|
|
@@ -117,6 +118,7 @@ export function searchSessions({ keyword, limit = 10, deep = false } = {}) {
|
|
|
117
118
|
|
|
118
119
|
const { meta } = extract(content);
|
|
119
120
|
results.push({
|
|
121
|
+
file: f,
|
|
120
122
|
date: (meta?.date) || new Date(mtimeMs).toISOString().slice(0, 16).replace('T', ' '),
|
|
121
123
|
dirName: meta?.cwd ? path.basename(meta.cwd) : '?',
|
|
122
124
|
snippet: snippet || '(匹配在元数据中)',
|
|
@@ -126,3 +128,29 @@ export function searchSessions({ keyword, limit = 10, deep = false } = {}) {
|
|
|
126
128
|
}
|
|
127
129
|
return results;
|
|
128
130
|
}
|
|
131
|
+
|
|
132
|
+
// 读取单个会话的完整对话(user / assistant 消息,跳过环境包装与工具输出)
|
|
133
|
+
export function readTranscript(file, { maxLen = 400 } = {}) {
|
|
134
|
+
if (!exists(file)) return null;
|
|
135
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
136
|
+
const out = [];
|
|
137
|
+
for (const line of content.split('\n')) {
|
|
138
|
+
if (!line.trim()) continue;
|
|
139
|
+
let j;
|
|
140
|
+
try {
|
|
141
|
+
j = JSON.parse(line);
|
|
142
|
+
} catch {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (j.type !== 'response_item' || j.payload?.type !== 'message') continue;
|
|
146
|
+
const role = j.payload.role;
|
|
147
|
+
if (role !== 'user' && role !== 'assistant') continue;
|
|
148
|
+
let text = (j.payload.content || []).map((c) => c.text || '').filter(Boolean).join('\n');
|
|
149
|
+
if (!text) continue;
|
|
150
|
+
if (role === 'user' && (text.startsWith('<environment_context>') || text.startsWith('<user_instructions>'))) continue;
|
|
151
|
+
if (role === 'user' && text.startsWith('<permissions')) continue;
|
|
152
|
+
if (text.length > maxLen) text = text.slice(0, maxLen) + '…';
|
|
153
|
+
out.push({ role, text });
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|