@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/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 html_generator_1 = require("./html-generator");
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
- // Scenario 1: No args -> launch node with interceptor and absolute path to claude
324
- async function runClaudeWithInterception(claudeArgs = [], includeAllRequests = false, openInBrowser = false, customClaudePath, logBaseName, enableTrace = false) {
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
- const spawnArgs = ["--require", loaderPath, claudePath, ...claudeArgs];
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, claudeArgs, {
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
- const htmlGenerator = new html_generator_1.HTMLGenerator();
519
- const finalOutputFile = await htmlGenerator.generateHTMLFromJSONL(inputFile, outputFile, includeAllRequests);
520
- if (openInBrowser) {
521
- (0, child_process_1.spawn)("open", [finalOutputFile], { detached: true, stdio: "ignore" }).unref();
522
- log(`正在浏览器中打开 ${finalOutputFile}`, "green");
523
- }
524
- process.exit(0);
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
- const err = error;
528
- log(`错误: ${err.message}`, "red");
529
- process.exit(1);
564
+ // 静默处理错误,不影响主流程
565
+ console.log(`处理CC日志时出错: ${error}`);
530
566
  }
531
567
  }
532
- // Scenario 5: --serve, --log, -l
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("--trace");
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");
@@ -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":"AAQA,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;IAiDtB,OAAO,CAAC,mBAAmB;IA+C3B,OAAO,CAAC,aAAa;IAoFrB,OAAO,CAAC,6BAA6B;IAuB9B,QAAQ;;;;;;CAQf;AAQD,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,mBAAmB,CA8BrF;AAED,wBAAgB,SAAS,IAAI,mBAAmB,GAAG,IAAI,CAEtD"}
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"}
@@ -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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myskyline_ai/ccdebug",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "跟踪记录CC请求,可针对特定请求反复调试验证,以更直观的时间线形式展示CC日志",
5
5
  "publishConfig": {
6
6
  "access": "public"