basant307/AI_Governance_Project
045
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import fs from 'node:fs/promises';8import path from 'node:path';9import {10 createDebugLogger,11 resolveOpenAILogDir,12 Storage,13} from '@qwen-code/qwen-code-core';14import type { CommandContext } from '../ui/commands/types.js';15 16const debugLogger = createDebugLogger('SESSION_PATHS');17const OPENAI_LOG_SCAN_LIMIT = 100;18 19export interface SessionPathEntry {20 label: string;21 value: string;22}23 24export interface SessionPathSection {25 title: string;26 entries: SessionPathEntry[];27}28 29export interface SessionPathInfo {30 sections: SessionPathSection[];31}32 33export async function collectSessionPathInfo(34 context: CommandContext,35): Promise<SessionPathInfo> {36 const config = context.services.config;37 const sessionId =38 config?.getSessionId() || context.session.stats.sessionId || 'unknown';39 const contentGeneratorConfig = config?.getContentGeneratorConfig();40 const workingDir = config?.getWorkingDir() || process.cwd();41 const openAILogDir = resolveOpenAILogDir(42 contentGeneratorConfig?.openAILoggingDir,43 workingDir,44 );45 const openAILoggingEnabled =46 contentGeneratorConfig?.enableOpenAILogging === true;47 const latestOpenAILog =48 openAILoggingEnabled && sessionId !== 'unknown'49 ? await findLatestOpenAILogForSession(openAILogDir, sessionId)50 : undefined;51 const transcriptPath = config?.getTranscriptPath() || '';52 const debugLogPath =53 config?.getDebugMode() && sessionId !== 'unknown'54 ? Storage.getDebugLogPath(sessionId)55 : '';56 const planFilePath =57 config?.getPlanFilePath() ||58 (sessionId === 'unknown' ? '' : Storage.getPlanFilePath(sessionId));59 const planFileExists = planFilePath ? await pathExists(planFilePath) : false;60 61 const sections: SessionPathSection[] = [62 {63 title: 'Session files',64 entries: [65 { label: 'Session ID', value: sessionId },66 ...(transcriptPath67 ? [{ label: 'Transcript', value: transcriptPath }]68 : []),69 ...(debugLogPath ? [{ label: 'Debug log', value: debugLogPath }] : []),70 ...(planFileExists71 ? [{ label: 'Plan file', value: planFilePath }]72 : []),73 ],74 },75 ];76 77 if (openAILoggingEnabled) {78 sections.push({79 title: 'OpenAI logs',80 entries: [81 { label: 'Directory', value: openAILogDir },82 { label: 'Latest for session', value: latestOpenAILog ?? 'none yet' },83 ],84 });85 }86 87 return {88 sections,89 };90}91 92export function formatSessionPathInfo(info: SessionPathInfo): string {93 const lines: string[] = [];94 for (const [index, section] of info.sections.entries()) {95 if (index > 0) {96 lines.push('');97 }98 lines.push(`${section.title}:`);99 for (const entry of section.entries) {100 lines.push(` ${entry.label}: ${entry.value}`);101 }102 }103 return lines.join('\n');104}105 106async function findLatestOpenAILogForSession(107 logDir: string,108 sessionId: string,109): Promise<string | undefined> {110 const files = await listLogFiles(logDir, (name) =>111 /^openai-.*\.json$/.test(name),112 );113 for (const file of files) {114 try {115 const raw = await fs.readFile(file, 'utf-8');116 const parsed: unknown = JSON.parse(raw);117 if (hasContextSessionId(parsed, sessionId)) {118 return file;119 }120 } catch (error) {121 if (122 error instanceof SyntaxError ||123 (error as NodeJS.ErrnoException).code === 'ENOENT'124 ) {125 continue;126 }127 debugLogger.warn('Error reading OpenAI log file', file, error);128 }129 }130 return undefined;131}132 133async function listLogFiles(134 dir: string,135 predicate: (name: string) => boolean,136): Promise<string[]> {137 try {138 const entries = await fs.readdir(dir, { withFileTypes: true });139 const files: string[] = [];140 for (const entry of entries) {141 if (!entry.isFile() || !predicate(entry.name)) {142 continue;143 }144 insertRecentFile(files, path.join(dir, entry.name));145 }146 return files;147 } catch (error) {148 if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {149 debugLogger.warn('Unable to list OpenAI log directory', dir, error);150 }151 return [];152 }153}154 155function insertRecentFile(files: string[], file: string): void {156 const insertAt = files.findIndex((existing) => file > existing);157 if (insertAt === -1) {158 if (files.length < OPENAI_LOG_SCAN_LIMIT) {159 files.push(file);160 }161 return;162 }163 164 files.splice(insertAt, 0, file);165 if (files.length > OPENAI_LOG_SCAN_LIMIT) {166 files.pop();167 }168}169 170async function pathExists(filePath: string): Promise<boolean> {171 try {172 await fs.access(filePath);173 return true;174 } catch {175 return false;176 }177}178 179function hasContextSessionId(value: unknown, sessionId: string): boolean {180 if (!value || typeof value !== 'object') {181 return false;182 }183 const context = (value as { context?: unknown }).context;184 if (!context || typeof context !== 'object') {185 return false;186 }187 const ctx = context as { sessionId?: unknown; promptId?: unknown };188 return ctx.sessionId === sessionId || ctx.promptId === sessionId;189}190 