@myskyline_ai/ccdebug 0.2.16 → 0.2.18
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.en.md +67 -57
- package/README.md +34 -20
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +149 -158
- package/dist/interceptor.d.ts +0 -3
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +0 -136
- package/package.json +1 -1
- package/web/dist/assets/{index-PAf6etkP.css → index-CHUycE0b.css} +1 -1
- package/web/dist/assets/{index-CBtN4uj6.js → index-CP7tYHG0.js} +3 -3
- package/web/dist/assets/{index-CBtN4uj6.js.map → index-CP7tYHG0.js.map} +1 -1
- package/web/dist/index.html +2 -2
package/dist/cli.js
CHANGED
|
@@ -38,7 +38,8 @@ exports.colors = void 0;
|
|
|
38
38
|
const child_process_1 = require("child_process");
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
|
-
const
|
|
41
|
+
const crypto = __importStar(require("crypto"));
|
|
42
|
+
const log_file_manager_1 = require("./log-file-manager");
|
|
42
43
|
const os = __importStar(require("os"));
|
|
43
44
|
/**
|
|
44
45
|
* 获取工具版本号
|
|
@@ -320,12 +321,132 @@ function getLoaderPath() {
|
|
|
320
321
|
}
|
|
321
322
|
return loaderPath;
|
|
322
323
|
}
|
|
323
|
-
|
|
324
|
-
|
|
324
|
+
/**
|
|
325
|
+
* 拷贝 Claude Code 日志文件到 cclog 目录
|
|
326
|
+
* @param sessionId 会话ID
|
|
327
|
+
* @param ccLogDir cclog 目录路径
|
|
328
|
+
*/
|
|
329
|
+
function copyCClogFile(sessionId, ccLogDir) {
|
|
330
|
+
// 检查是否启用了跟踪
|
|
331
|
+
if (!ccLogDir) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
// 将当前会话对应的 cc 日志文件,拷贝到 .claude-trace/cclog 目录
|
|
335
|
+
try {
|
|
336
|
+
// 创建 LogFileManager 实例
|
|
337
|
+
const logFileManager = new log_file_manager_1.LogFileManager();
|
|
338
|
+
// 获取当前工作目录作为项目路径
|
|
339
|
+
const currentProjectPath = process.cwd();
|
|
340
|
+
// 通过 LogFileManager 解析源日志目录
|
|
341
|
+
const sourceLogDir = logFileManager.resolveLogDirectory(currentProjectPath);
|
|
342
|
+
// 构建源文件路径(假设源文件名为 sessionId.jsonl)
|
|
343
|
+
const sourceFile = path.join(sourceLogDir, `${sessionId}.jsonl`);
|
|
344
|
+
// 构建目标文件路径
|
|
345
|
+
const ccLogFile = path.join(ccLogDir, `${sessionId}.jsonl`);
|
|
346
|
+
// 检查源文件是否存在
|
|
347
|
+
if (!fs.existsSync(sourceFile)) {
|
|
348
|
+
console.log(`源CC日志文件不存在: ${sourceFile}`);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// 确保目标 ccLogDir 目录存在(修复:之前未创建目录导致拷贝失败)
|
|
352
|
+
if (!fs.existsSync(ccLogDir)) {
|
|
353
|
+
fs.mkdirSync(ccLogDir, { recursive: true });
|
|
354
|
+
console.log(`已创建 cclog 目录: ${ccLogDir}`);
|
|
355
|
+
}
|
|
356
|
+
// 拷贝文件
|
|
357
|
+
fs.copyFileSync(sourceFile, ccLogFile);
|
|
358
|
+
console.log(`CC日志文件已从 ${sourceFile} 拷贝到 ${ccLogFile}`);
|
|
359
|
+
// 读取 sourceLogDir 目录下所有 agent_*.jsonl 文件,读取第一条记录的 sessionId,找到与 sessionId 变量值相同的文件,拷贝到 ccLogDir 目录
|
|
360
|
+
const files = fs.readdirSync(sourceLogDir).filter(file => file.startsWith('agent-') && file.endsWith('.jsonl'));
|
|
361
|
+
for (const file of files) {
|
|
362
|
+
const filePath = path.join(sourceLogDir, file);
|
|
363
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
364
|
+
const lines = content.split('\n').filter(line => line.trim());
|
|
365
|
+
if (lines.length > 0) {
|
|
366
|
+
try {
|
|
367
|
+
const firstRecord = JSON.parse(lines[0]);
|
|
368
|
+
const recordSessionId = firstRecord?.sessionId;
|
|
369
|
+
if (recordSessionId === sessionId) {
|
|
370
|
+
// 构建目标文件路径
|
|
371
|
+
const ccAgentLogFile = path.join(ccLogDir, file);
|
|
372
|
+
// 拷贝文件
|
|
373
|
+
fs.copyFileSync(filePath, ccAgentLogFile);
|
|
374
|
+
console.log(`SubAgent的CC日志文件已从 ${filePath} 拷贝到 ${ccAgentLogFile}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch (parseError) {
|
|
378
|
+
// 静默处理解析错误,继续下一个文件
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
// 兼容 subagents 日志的另一种存放方式:sourceLogDir/{sessionId}/subagents/ 目录
|
|
384
|
+
const subagentsDir = path.join(sourceLogDir, sessionId, 'subagents');
|
|
385
|
+
if (fs.existsSync(subagentsDir)) {
|
|
386
|
+
try {
|
|
387
|
+
const subagentFiles = fs.readdirSync(subagentsDir).filter(file => file.startsWith('agent-') && file.endsWith('.jsonl'));
|
|
388
|
+
// 只有当存在需要拷贝的文件时,才创建目标目录
|
|
389
|
+
if (subagentFiles.length > 0) {
|
|
390
|
+
// 创建目标目录结构:ccLogDir/{sessionId}/subagents/
|
|
391
|
+
const targetSubagentsDir = path.join(ccLogDir, sessionId, 'subagents');
|
|
392
|
+
if (!fs.existsSync(targetSubagentsDir)) {
|
|
393
|
+
fs.mkdirSync(targetSubagentsDir, { recursive: true });
|
|
394
|
+
}
|
|
395
|
+
for (const file of subagentFiles) {
|
|
396
|
+
const sourceFilePath = path.join(subagentsDir, file);
|
|
397
|
+
const targetFilePath = path.join(targetSubagentsDir, file);
|
|
398
|
+
// 直接拷贝文件,因为已经在正确的 sessionId 目录下
|
|
399
|
+
fs.copyFileSync(sourceFilePath, targetFilePath);
|
|
400
|
+
console.log(`SubAgent的CC日志文件已从 ${sourceFilePath} 拷贝到 ${targetFilePath}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
console.log(`处理subagents目录时出错: ${error}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
console.log(`拷贝CC日志文件时出错: ${error}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* 根据 sessionId 重命名跟踪日志文件
|
|
415
|
+
* @param sessionId 会话ID
|
|
416
|
+
* @param traceLogFile 跟踪日志文件路径
|
|
417
|
+
* @returns 新的日志文件路径
|
|
418
|
+
*/
|
|
419
|
+
function renameTraceLogFileBySessionId(sessionId, traceLogFile) {
|
|
420
|
+
// 检查是否启用了跟踪
|
|
421
|
+
if (!traceLogFile) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
const logDir = path.dirname(traceLogFile);
|
|
426
|
+
const newLogFile = path.join(logDir, `${sessionId}.jsonl`);
|
|
427
|
+
// 如果源文件不存在(如原生二进制模式下 interceptor 未运行,tracelog 未创建),直接跳过
|
|
428
|
+
if (!fs.existsSync(traceLogFile)) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
// 重命名文件
|
|
432
|
+
fs.renameSync(traceLogFile, newLogFile);
|
|
433
|
+
console.log(`Log file renamed from ${path.basename(traceLogFile)} to ${sessionId}.jsonl`);
|
|
434
|
+
return newLogFile;
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
console.log(`Error renaming log file: ${error}`);
|
|
438
|
+
return traceLogFile;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// 启动claude code
|
|
442
|
+
async function runClaudeWithInterception(claudeArgs = [], includeAllRequests = false, openInBrowser = false, customClaudePath, logBaseName, enableTrace = true) {
|
|
325
443
|
log("启动 Claude", "blue");
|
|
326
444
|
if (claudeArgs.length > 0) {
|
|
327
445
|
log(`Claude 参数: ${claudeArgs.join(" ")}`, "blue");
|
|
328
446
|
}
|
|
447
|
+
// 生成 UUID 格式的 sessionId
|
|
448
|
+
const sessionId = crypto.randomUUID();
|
|
449
|
+
log(`生成 SessionId: ${sessionId}`, "blue");
|
|
329
450
|
const claudePath = getClaudeAbsolutePath(customClaudePath);
|
|
330
451
|
log(`使用 Claude 二进制文件: ${claudePath}`, "blue");
|
|
331
452
|
let child;
|
|
@@ -338,7 +459,9 @@ async function runClaudeWithInterception(claudeArgs = [], includeAllRequests = f
|
|
|
338
459
|
log("使用 Node.js 启动 Claude(未启用跟踪)", "blue");
|
|
339
460
|
}
|
|
340
461
|
const loaderPath = getLoaderPath();
|
|
341
|
-
|
|
462
|
+
// 添加 --session-id 参数到 Claude 启动参数中
|
|
463
|
+
const claudeArgsWithSession = ["--session-id", sessionId, ...claudeArgs];
|
|
464
|
+
const spawnArgs = ["--require", loaderPath, claudePath, ...claudeArgsWithSession];
|
|
342
465
|
child = (0, child_process_1.spawn)("node", spawnArgs, {
|
|
343
466
|
env: {
|
|
344
467
|
...process.env,
|
|
@@ -362,8 +485,10 @@ async function runClaudeWithInterception(claudeArgs = [], includeAllRequests = f
|
|
|
362
485
|
console.log("");
|
|
363
486
|
// 给用户一点时间阅读提示信息
|
|
364
487
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
488
|
+
// 添加 --session-id 参数到 Claude 启动参数中
|
|
489
|
+
const claudeArgsWithSession = ["--session-id", sessionId, ...claudeArgs];
|
|
365
490
|
// 直接启动 Claude 二进制文件,不使用代理
|
|
366
|
-
child = (0, child_process_1.spawn)(claudePath,
|
|
491
|
+
child = (0, child_process_1.spawn)(claudePath, claudeArgsWithSession, {
|
|
367
492
|
env: {
|
|
368
493
|
...process.env,
|
|
369
494
|
},
|
|
@@ -418,118 +543,29 @@ async function runClaudeWithInterception(claudeArgs = [], includeAllRequests = f
|
|
|
418
543
|
log(`意外错误: ${err.message}`, "red");
|
|
419
544
|
process.exit(1);
|
|
420
545
|
}
|
|
421
|
-
|
|
422
|
-
// Scenario 2: --extract-token -> launch node with token interceptor and absolute path to claude
|
|
423
|
-
async function extractToken(customClaudePath) {
|
|
424
|
-
const claudePath = getClaudeAbsolutePath(customClaudePath);
|
|
425
|
-
// Log to stderr so it doesn't interfere with token output
|
|
426
|
-
console.error(`使用 Claude 二进制文件: ${claudePath}`);
|
|
427
|
-
// Create .claude-trace directory if it doesn't exist
|
|
428
|
-
const ccdebugDir = path.join(process.cwd(), ".claude-trace");
|
|
429
|
-
if (!fs.existsSync(ccdebugDir)) {
|
|
430
|
-
fs.mkdirSync(ccdebugDir, { recursive: true });
|
|
431
|
-
}
|
|
432
|
-
// Token file location
|
|
433
|
-
const tokenFile = path.join(ccdebugDir, "token.txt");
|
|
434
|
-
// Use the token extractor directly without copying
|
|
435
|
-
const tokenExtractorPath = path.join(__dirname, "token-extractor.js");
|
|
436
|
-
if (!fs.existsSync(tokenExtractorPath)) {
|
|
437
|
-
log(`未找到令牌提取器: ${tokenExtractorPath}`, "red");
|
|
438
|
-
process.exit(1);
|
|
439
|
-
}
|
|
440
|
-
const cleanup = () => {
|
|
441
|
-
try {
|
|
442
|
-
if (fs.existsSync(tokenFile))
|
|
443
|
-
fs.unlinkSync(tokenFile);
|
|
444
|
-
}
|
|
445
|
-
catch (e) {
|
|
446
|
-
// Ignore cleanup errors
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
// Launch node with token interceptor and absolute path to claude
|
|
450
|
-
const { ANTHROPIC_API_KEY, ...envWithoutApiKey } = process.env;
|
|
451
|
-
const child = (0, child_process_1.spawn)("node", ["--require", tokenExtractorPath, claudePath, "-p", "hello"], {
|
|
452
|
-
env: {
|
|
453
|
-
...envWithoutApiKey,
|
|
454
|
-
NODE_TLS_REJECT_UNAUTHORIZED: "0",
|
|
455
|
-
CLAUDE_TRACE_TOKEN_FILE: tokenFile,
|
|
456
|
-
},
|
|
457
|
-
stdio: "inherit", // Suppress all output from Claude
|
|
458
|
-
cwd: process.cwd(),
|
|
459
|
-
});
|
|
460
|
-
// Set a timeout to avoid hanging
|
|
461
|
-
const timeout = setTimeout(() => {
|
|
462
|
-
child.kill();
|
|
463
|
-
cleanup();
|
|
464
|
-
console.error("超时: 30 秒内未找到令牌");
|
|
465
|
-
process.exit(1);
|
|
466
|
-
}, 30000);
|
|
467
|
-
// Handle child process events
|
|
468
|
-
child.on("error", (error) => {
|
|
469
|
-
clearTimeout(timeout);
|
|
470
|
-
cleanup();
|
|
471
|
-
console.error(`启动 Claude 时出错: ${error.message}`);
|
|
472
|
-
process.exit(1);
|
|
473
|
-
});
|
|
474
|
-
child.on("exit", () => {
|
|
475
|
-
clearTimeout(timeout);
|
|
476
|
-
try {
|
|
477
|
-
if (fs.existsSync(tokenFile)) {
|
|
478
|
-
const token = fs.readFileSync(tokenFile, "utf-8").trim();
|
|
479
|
-
cleanup();
|
|
480
|
-
if (token) {
|
|
481
|
-
// Only output the token, nothing else
|
|
482
|
-
console.log(token);
|
|
483
|
-
process.exit(0);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
catch (e) {
|
|
488
|
-
// File doesn't exist or read error
|
|
489
|
-
}
|
|
490
|
-
cleanup();
|
|
491
|
-
console.error("未找到授权令牌");
|
|
492
|
-
process.exit(1);
|
|
493
|
-
});
|
|
494
|
-
// Check for token file periodically
|
|
495
|
-
const checkToken = setInterval(() => {
|
|
496
|
-
try {
|
|
497
|
-
if (fs.existsSync(tokenFile)) {
|
|
498
|
-
const token = fs.readFileSync(tokenFile, "utf-8").trim();
|
|
499
|
-
if (token) {
|
|
500
|
-
clearTimeout(timeout);
|
|
501
|
-
clearInterval(checkToken);
|
|
502
|
-
child.kill();
|
|
503
|
-
cleanup();
|
|
504
|
-
// Only output the token, nothing else
|
|
505
|
-
console.log(token);
|
|
506
|
-
process.exit(0);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
catch (e) {
|
|
511
|
-
// Ignore read errors, keep trying
|
|
512
|
-
}
|
|
513
|
-
}, 500);
|
|
514
|
-
}
|
|
515
|
-
// Scenario 3: --generate-html input.jsonl output.html
|
|
516
|
-
async function generateHTMLFromCLI(inputFile, outputFile, includeAllRequests = false, openInBrowser = false) {
|
|
546
|
+
// Claude 执行完成后,处理 CC 日志文件(不管是否启用了跟踪)
|
|
517
547
|
try {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
548
|
+
console.log("\nClaude 执行完成,处理 CC 日志文件...");
|
|
549
|
+
// 构建日志文件路径
|
|
550
|
+
const traceHomeDir = ".claude-trace";
|
|
551
|
+
const traceLogDir = path.join(traceHomeDir, 'tracelog');
|
|
552
|
+
const ccLogDir = path.join(traceHomeDir, 'cclog');
|
|
553
|
+
// 生成日志文件名
|
|
554
|
+
const fileBaseName = logBaseName || `log-${new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, -5)}`;
|
|
555
|
+
const traceLogFile = enableTrace ? path.join(traceLogDir, `${fileBaseName}.jsonl`) : null;
|
|
556
|
+
// 直接使用之前生成的 sessionId
|
|
557
|
+
console.log(`使用 SessionId: ${sessionId} 处理日志文件`);
|
|
558
|
+
// 将当前会话对应的 cc 日志文件,拷贝到 .claude-trace/cclog 目录
|
|
559
|
+
copyCClogFile(sessionId, ccLogDir);
|
|
560
|
+
// 根据 sessionId 重命名跟踪日志文件
|
|
561
|
+
renameTraceLogFileBySessionId(sessionId, traceLogFile);
|
|
525
562
|
}
|
|
526
563
|
catch (error) {
|
|
527
|
-
|
|
528
|
-
log(
|
|
529
|
-
process.exit(1);
|
|
564
|
+
// 静默处理错误,不影响主流程
|
|
565
|
+
console.log(`处理CC日志时出错: ${error}`);
|
|
530
566
|
}
|
|
531
567
|
}
|
|
532
|
-
//
|
|
568
|
+
// 启动web工具
|
|
533
569
|
async function startWebServer(port, projectDir) {
|
|
534
570
|
try {
|
|
535
571
|
// 使用 require 导入 web server 模块
|
|
@@ -561,20 +597,6 @@ async function startWebServer(port, projectDir) {
|
|
|
561
597
|
process.exit(1);
|
|
562
598
|
}
|
|
563
599
|
}
|
|
564
|
-
// Scenario 4: --index
|
|
565
|
-
async function generateIndex() {
|
|
566
|
-
try {
|
|
567
|
-
const { IndexGenerator } = await Promise.resolve().then(() => __importStar(require("./index-generator")));
|
|
568
|
-
const indexGenerator = new IndexGenerator();
|
|
569
|
-
await indexGenerator.generateIndex();
|
|
570
|
-
process.exit(0);
|
|
571
|
-
}
|
|
572
|
-
catch (error) {
|
|
573
|
-
const err = error;
|
|
574
|
-
log(`错误: ${err.message}`, "red");
|
|
575
|
-
process.exit(1);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
600
|
// Main entry point
|
|
579
601
|
async function main() {
|
|
580
602
|
const args = process.argv.slice(2);
|
|
@@ -601,7 +623,7 @@ async function main() {
|
|
|
601
623
|
process.exit(0);
|
|
602
624
|
}
|
|
603
625
|
// Check for trace flag
|
|
604
|
-
const enableTrace = claudeTraceArgs.includes("--
|
|
626
|
+
const enableTrace = !claudeTraceArgs.includes("--notrace");
|
|
605
627
|
// Check for include all requests flag
|
|
606
628
|
const includeAllRequests = claudeTraceArgs.includes("--include-all-requests");
|
|
607
629
|
// Check for no-open flag (inverted logic - open by default)
|
|
@@ -633,37 +655,6 @@ async function main() {
|
|
|
633
655
|
if (projectIndex !== -1 && claudeTraceArgs[projectIndex + 1]) {
|
|
634
656
|
serveProjectDir = claudeTraceArgs[projectIndex + 1];
|
|
635
657
|
}
|
|
636
|
-
// Scenario 2: --extract-token
|
|
637
|
-
if (claudeTraceArgs.includes("--extract-token")) {
|
|
638
|
-
await extractToken(customClaudePath);
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
// Scenario 3: --generate-html input.jsonl output.html
|
|
642
|
-
if (claudeTraceArgs.includes("--generate-html")) {
|
|
643
|
-
const flagIndex = claudeTraceArgs.indexOf("--generate-html");
|
|
644
|
-
const inputFile = claudeTraceArgs[flagIndex + 1];
|
|
645
|
-
// Find is next argument that's not a flag as the output file
|
|
646
|
-
let outputFile;
|
|
647
|
-
for (let i = flagIndex + 2; i < claudeTraceArgs.length; i++) {
|
|
648
|
-
const arg = claudeTraceArgs[i];
|
|
649
|
-
if (!arg.startsWith("--")) {
|
|
650
|
-
outputFile = arg;
|
|
651
|
-
break;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
if (!inputFile) {
|
|
655
|
-
log(`--generate-html 缺少输入文件`, "red");
|
|
656
|
-
log(`用法: ccdebug --generate-html input.jsonl [output.html]`, "yellow");
|
|
657
|
-
process.exit(1);
|
|
658
|
-
}
|
|
659
|
-
await generateHTMLFromCLI(inputFile, outputFile, includeAllRequests, openInBrowser);
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
// Scenario 4: --index
|
|
663
|
-
if (claudeTraceArgs.includes("--index")) {
|
|
664
|
-
await generateIndex();
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
658
|
// Scenario 5: --serve, --log, -l
|
|
668
659
|
const hasServeFlag = claudeTraceArgs.includes("--serve");
|
|
669
660
|
const hasLogFlag = claudeTraceArgs.includes("--log") || claudeTraceArgs.includes("-l");
|
package/dist/interceptor.d.ts
CHANGED
|
@@ -31,9 +31,6 @@ export declare class ClaudeTrafficLogger {
|
|
|
31
31
|
private writePairToLog;
|
|
32
32
|
private generateHTML;
|
|
33
33
|
cleanup(): void;
|
|
34
|
-
private getSessionIdFromLog;
|
|
35
|
-
private copyCClogFile;
|
|
36
|
-
private renameTraceLogFileBySessionId;
|
|
37
34
|
getStats(): {
|
|
38
35
|
totalPairs: number;
|
|
39
36
|
pendingRequests: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../src/interceptor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../src/interceptor.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CAC/C;AAED,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,MAAM,GAAE,iBAAsB;IAmE1C,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,sBAAsB;YAgChB,aAAa;YAKb,gBAAgB;YAsBhB,iBAAiB;IAwBxB,aAAa,IAAI,IAAI;IAKrB,eAAe,IAAI,IAAI;IA+FvB,kBAAkB,IAAI,IAAI;IAmDjC,OAAO,CAAC,oBAAoB;IAuE5B,OAAO,CAAC,mBAAmB;YAab,2BAA2B;YAiB3B,cAAc;YAcd,YAAY;IAmBnB,OAAO,IAAI,IAAI;IAuCf,QAAQ;;;;;;CAQf;AAQD,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,mBAAmB,CA8BrF;AAED,wBAAgB,SAAS,IAAI,mBAAmB,GAAG,IAAI,CAEtD"}
|
package/dist/interceptor.js
CHANGED
|
@@ -9,7 +9,6 @@ exports.getLogger = getLogger;
|
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
11
|
const html_generator_1 = require("./html-generator");
|
|
12
|
-
const log_file_manager_1 = require("./log-file-manager");
|
|
13
12
|
class ClaudeTrafficLogger {
|
|
14
13
|
constructor(config = {}) {
|
|
15
14
|
this.pendingRequests = new Map();
|
|
@@ -433,14 +432,6 @@ class ClaudeTrafficLogger {
|
|
|
433
432
|
}
|
|
434
433
|
this.pendingRequests.clear();
|
|
435
434
|
console.log(`Cleanup complete. Logged ${this.pairs.length} pairs`);
|
|
436
|
-
//获取cc会话的sessionid
|
|
437
|
-
let sessionId = this.getSessionIdFromLog();
|
|
438
|
-
if (sessionId != '') {
|
|
439
|
-
//将当前会话对应的cc日志文件,拷贝到.claude-trace/cclog目录
|
|
440
|
-
this.copyCClogFile(sessionId);
|
|
441
|
-
// Rename log file based on sessionid from first record
|
|
442
|
-
this.renameTraceLogFileBySessionId(sessionId);
|
|
443
|
-
}
|
|
444
435
|
// Open browser if requested
|
|
445
436
|
// const shouldOpenBrowser = process.env.CLAUDE_TRACE_OPEN_BROWSER === "true";
|
|
446
437
|
// if (shouldOpenBrowser && fs.existsSync(this.htmlFile)) {
|
|
@@ -452,133 +443,6 @@ class ClaudeTrafficLogger {
|
|
|
452
443
|
// }
|
|
453
444
|
// }
|
|
454
445
|
}
|
|
455
|
-
getSessionIdFromLog() {
|
|
456
|
-
// 检查是否启用了跟踪
|
|
457
|
-
if (!this.traceLogFile) {
|
|
458
|
-
return '';
|
|
459
|
-
}
|
|
460
|
-
// Check if log file exists
|
|
461
|
-
if (!fs_1.default.existsSync(this.traceLogFile)) {
|
|
462
|
-
console.log("获取sessionId错误:Log file does not exist");
|
|
463
|
-
return '';
|
|
464
|
-
}
|
|
465
|
-
// Read the first line of the JSONL file
|
|
466
|
-
const fileContent = fs_1.default.readFileSync(this.traceLogFile, 'utf-8');
|
|
467
|
-
const lines = fileContent.split('\n').filter(line => line.trim());
|
|
468
|
-
if (lines.length === 0) {
|
|
469
|
-
console.log("获取sessionId错误:Log file is empty");
|
|
470
|
-
return '';
|
|
471
|
-
}
|
|
472
|
-
// 循环读取日志,直到找到user_id为止
|
|
473
|
-
let userId = null;
|
|
474
|
-
for (const line of lines) {
|
|
475
|
-
const record = JSON.parse(line);
|
|
476
|
-
userId = record?.request?.body?.metadata?.user_id;
|
|
477
|
-
if (userId) {
|
|
478
|
-
break;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
if (!userId) {
|
|
482
|
-
console.log("获取sessionId错误:No user_id found in any record");
|
|
483
|
-
return '';
|
|
484
|
-
}
|
|
485
|
-
// Extract sessionid from user_id (format: xxxx_session_{sessionid})
|
|
486
|
-
const sessionMatch = userId.match(/_session_([^_]+)$/);
|
|
487
|
-
if (!sessionMatch || !sessionMatch[1]) {
|
|
488
|
-
console.log(`获取sessionId错误:No sessionid found in user_id: ${userId}`);
|
|
489
|
-
return '';
|
|
490
|
-
}
|
|
491
|
-
return sessionMatch[1];
|
|
492
|
-
}
|
|
493
|
-
copyCClogFile(sessionId) {
|
|
494
|
-
// 检查是否启用了跟踪
|
|
495
|
-
if (!this.ccLogDir) {
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
//将当前会话对应的cc日志文件,拷贝到.claude-trace/cclog目录
|
|
499
|
-
try {
|
|
500
|
-
// 创建LogFileManager实例
|
|
501
|
-
const logFileManager = new log_file_manager_1.LogFileManager();
|
|
502
|
-
// 获取当前工作目录作为项目路径
|
|
503
|
-
const currentProjectPath = process.cwd();
|
|
504
|
-
// 通过LogFileManager解析源日志目录
|
|
505
|
-
const sourceLogDir = logFileManager.resolveLogDirectory(currentProjectPath);
|
|
506
|
-
// 构建源文件路径(假设源文件名为sessionId.jsonl)
|
|
507
|
-
const sourceFile = path_1.default.join(sourceLogDir, `${sessionId}.jsonl`);
|
|
508
|
-
// 构建目标文件路径
|
|
509
|
-
this.ccLogFile = path_1.default.join(this.ccLogDir, `${sessionId}.jsonl`);
|
|
510
|
-
// 检查源文件是否存在
|
|
511
|
-
if (!fs_1.default.existsSync(sourceFile)) {
|
|
512
|
-
console.log(`源CC日志文件不存在: ${sourceFile}`);
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
// 拷贝文件
|
|
516
|
-
fs_1.default.copyFileSync(sourceFile, this.ccLogFile);
|
|
517
|
-
console.log(`CC日志文件已从 ${sourceFile} 拷贝到 ${this.ccLogFile}`);
|
|
518
|
-
// 读取sourceLogDir目录下所有agent_*.jsonl文件,读取第一条记录的sessionId,找到与sessionId变量值相同的文件,拷贝到ccLogDir目录
|
|
519
|
-
const files = fs_1.default.readdirSync(sourceLogDir).filter(file => file.startsWith('agent-') && file.endsWith('.jsonl'));
|
|
520
|
-
for (const file of files) {
|
|
521
|
-
const filePath = path_1.default.join(sourceLogDir, file);
|
|
522
|
-
const content = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
523
|
-
const lines = content.split('\n').filter(line => line.trim());
|
|
524
|
-
if (lines.length > 0) {
|
|
525
|
-
try {
|
|
526
|
-
const firstRecord = JSON.parse(lines[0]);
|
|
527
|
-
const recordSessionId = firstRecord?.sessionId;
|
|
528
|
-
if (recordSessionId === sessionId) {
|
|
529
|
-
// 构建目标文件路径
|
|
530
|
-
const ccAgentLogFile = path_1.default.join(this.ccLogDir, file);
|
|
531
|
-
// 拷贝文件
|
|
532
|
-
fs_1.default.copyFileSync(filePath, ccAgentLogFile);
|
|
533
|
-
console.log(`SubAgent的CC日志文件已从 ${filePath} 拷贝到 ${ccAgentLogFile}`);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
catch (parseError) {
|
|
537
|
-
// 静默处理解析错误,继续下一个文件
|
|
538
|
-
continue;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
// 兼容subagents日志的另一种存放方式:sourceLogDir/{sessionId}/subagents/目录
|
|
543
|
-
const subagentsDir = path_1.default.join(sourceLogDir, sessionId, 'subagents');
|
|
544
|
-
if (fs_1.default.existsSync(subagentsDir)) {
|
|
545
|
-
try {
|
|
546
|
-
const subagentFiles = fs_1.default.readdirSync(subagentsDir).filter(file => file.startsWith('agent-') && file.endsWith('.jsonl'));
|
|
547
|
-
for (const file of subagentFiles) {
|
|
548
|
-
const sourceFilePath = path_1.default.join(subagentsDir, file);
|
|
549
|
-
const targetFilePath = path_1.default.join(this.ccLogDir, file);
|
|
550
|
-
// 直接拷贝文件,因为已经在正确的sessionId目录下
|
|
551
|
-
fs_1.default.copyFileSync(sourceFilePath, targetFilePath);
|
|
552
|
-
console.log(`SubAgent的CC日志文件已从 ${sourceFilePath} 拷贝到 ${targetFilePath}`);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
catch (error) {
|
|
556
|
-
console.log(`处理subagents目录时出错: ${error}`);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
catch (error) {
|
|
561
|
-
console.log(`拷贝CC日志文件时出错: ${error}`);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
renameTraceLogFileBySessionId(sessionId) {
|
|
565
|
-
// 检查是否启用了跟踪
|
|
566
|
-
if (!this.traceLogFile) {
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
try {
|
|
570
|
-
const logDir = path_1.default.dirname(this.traceLogFile);
|
|
571
|
-
const newLogFile = path_1.default.join(logDir, `${sessionId}.jsonl`);
|
|
572
|
-
// Rename the file
|
|
573
|
-
fs_1.default.renameSync(this.traceLogFile, newLogFile);
|
|
574
|
-
console.log(`Log file renamed from ${path_1.default.basename(this.traceLogFile)} to ${sessionId}.jsonl`);
|
|
575
|
-
// Update the logFile path for future reference
|
|
576
|
-
this.traceLogFile = newLogFile;
|
|
577
|
-
}
|
|
578
|
-
catch (error) {
|
|
579
|
-
console.log(`Error renaming log file: ${error}`);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
446
|
getStats() {
|
|
583
447
|
return {
|
|
584
448
|
totalPairs: this.pairs.length,
|