From eb75b3af1ee8c0da492e25ddfc6fba0807208e31 Mon Sep 17 00:00:00 2001 From: MasterPtato Date: Wed, 12 Nov 2025 17:41:28 -0800 Subject: [PATCH] chore: add debug scripts --- scripts/debug/debug_mermaid.ts | 100 ++++ scripts/{ => debug}/decode_audit_entry.js | 0 scripts/debug/logfmt_to_gantt.ts | 560 ++++++++++++++++++++++ scripts/debug/package.json | 16 + scripts/debug/tsconfig.json | 4 + scripts/debug/turbo.json | 4 + 6 files changed, 684 insertions(+) create mode 100644 scripts/debug/debug_mermaid.ts rename scripts/{ => debug}/decode_audit_entry.js (100%) create mode 100644 scripts/debug/logfmt_to_gantt.ts create mode 100644 scripts/debug/package.json create mode 100644 scripts/debug/tsconfig.json create mode 100644 scripts/debug/turbo.json diff --git a/scripts/debug/debug_mermaid.ts b/scripts/debug/debug_mermaid.ts new file mode 100644 index 0000000000..9c17403c4c --- /dev/null +++ b/scripts/debug/debug_mermaid.ts @@ -0,0 +1,100 @@ +#!/usr/bin/env tsx + +/** + * Binary search to find problematic Mermaid gantt entries + * Usage: tsx scripts/debug/debug_mermaid.ts + */ + +import { readFileSync, writeFileSync } from 'fs'; + +function main() { + const inputFile = '/tmp/rivet-engine-gantt.md'; + const content = readFileSync(inputFile, 'utf-8'); + const lines = content.split('\n'); + + // Find the start and end of the mermaid block + const startIdx = lines.findIndex(line => line.trim() === '```mermaid'); + const endIdx = lines.findIndex((line, idx) => idx > startIdx && line.trim() === '```'); + + if (startIdx === -1 || endIdx === -1) { + console.error('Could not find mermaid block'); + process.exit(1); + } + + const mermaidLines = lines.slice(startIdx + 1, endIdx); + + // Find where sections start + const sectionStarts: number[] = []; + mermaidLines.forEach((line, idx) => { + if (line.trim().startsWith('section ')) { + sectionStarts.push(idx); + } + }); + + console.log(`Found ${sectionStarts.length} sections`); + + // Try with just the header + console.log('\nTesting with just header...'); + let testLines = mermaidLines.slice(0, Math.max(...sectionStarts.filter(s => s < 10))); + writeTestFile(testLines, '/tmp/test-gantt.md'); + console.log('Created /tmp/test-gantt.md with just headers'); + + // Try with first section only + console.log('\nTesting with first section only...'); + const firstSectionEnd = sectionStarts[1] || mermaidLines.length; + testLines = mermaidLines.slice(0, firstSectionEnd); + writeTestFile(testLines, '/tmp/test-gantt-section1.md'); + console.log('Created /tmp/test-gantt-section1.md with first section'); + + // Create files with incremental sections + for (let i = 0; i < sectionStarts.length; i++) { + const endLine = i + 1 < sectionStarts.length ? sectionStarts[i + 1] : mermaidLines.length; + testLines = mermaidLines.slice(0, endLine); + writeTestFile(testLines, `/tmp/test-gantt-${i + 1}sections.md`); + console.log(`Created /tmp/test-gantt-${i + 1}sections.md`); + } + + // Also create a minimal test + console.log('\nCreating minimal test...'); + const minimal = [ + 'gantt', + ' title Test', + ' dateFormat YYYY-MM-DDTHH:mm:ss', + ' axisFormat %H:%M:%S', + ' section Test Section', + ' task1 :a1, 2025-11-12T22:30:21, 2025-11-12T22:30:22', + ' task2 :crit, a2, 2025-11-12T22:30:22, 2025-11-12T22:30:23', + ' task3 :active, a3, 2025-11-12T22:30:23, 2025-11-12T22:30:24', + ' task4 :milestone, a4, 2025-11-12T22:30:24, 2025-11-12T22:30:24', + ]; + writeTestFile(minimal, '/tmp/test-gantt-minimal.md'); + console.log('Created /tmp/test-gantt-minimal.md'); + + // Print first few task lines for inspection + console.log('\nFirst 10 task lines:'); + let taskCount = 0; + for (const line of mermaidLines) { + if (line.trim() && !line.trim().startsWith('gantt') && !line.trim().startsWith('title') && + !line.trim().startsWith('dateFormat') && !line.trim().startsWith('axisFormat') && + !line.trim().startsWith('section')) { + console.log(` ${line}`); + taskCount++; + if (taskCount >= 10) break; + } + } +} + +function writeTestFile(mermaidLines: string[], outputPath: string) { + const output = [ + '# Test Mermaid Gantt', + '', + '```mermaid', + ...mermaidLines, + '```' + ].join('\n'); + writeFileSync(outputPath, output); +} + +if (require.main === module) { + main(); +} diff --git a/scripts/decode_audit_entry.js b/scripts/debug/decode_audit_entry.js similarity index 100% rename from scripts/decode_audit_entry.js rename to scripts/debug/decode_audit_entry.js diff --git a/scripts/debug/logfmt_to_gantt.ts b/scripts/debug/logfmt_to_gantt.ts new file mode 100644 index 0000000000..7640d08798 --- /dev/null +++ b/scripts/debug/logfmt_to_gantt.ts @@ -0,0 +1,560 @@ +#!/usr/bin/env tsx + +/** + * Parses a logfmt log file and generates a Mermaid Gantt chart + * Usage: tsx scripts/debug/logfmt_to_gantt.ts [input_file] [output_file] + */ + +import { readFileSync, writeFileSync } from 'fs'; + +interface LogEntry { + ts: Date; + tsFracSec: number; // Fractional seconds for sub-millisecond precision + level: string; + message: string; + workflow_id?: string; + workflow_name?: string; + activity_name?: string; + signal_name?: string; + service?: string; + operation_name?: string; + ray_id?: string; + iteration?: number; + location?: string; + [key: string]: any; +} + +interface Task { + id: string; + name: string; + start: Date; + startFracSec: number; + end?: Date; + endFracSec?: number; + section: string; + type: 'workflow' | 'activity' | 'operation' | 'service' | 'signal'; + metadata: Record; +} + +// ANSI color code regex +const ANSI_REGEX = /\x1b\[[0-9;]*m/g; + +function stripAnsi(str: string): string { + return str.replace(ANSI_REGEX, ''); +} + +function parseLogfmt(line: string): LogEntry | null { + const cleaned = stripAnsi(line); + const entry: Partial = {}; + + // Match key=value pairs, handling quoted values + const regex = /(\w+)=(?:"([^"]*)"|(\S+))/g; + let match; + + while ((match = regex.exec(cleaned)) !== null) { + const key = match[1]; + const value = match[2] !== undefined ? match[2] : match[3]; + + if (key === 'ts') { + entry.ts = new Date(value); + const fracMatch = value.match(/\.(\d+)/); + if (fracMatch) { + const fracStr = fracMatch[1]; + if (fracStr.length > 3) { + entry.tsFracSec = parseFloat('0.000' + fracStr.substring(3)); + } else { + entry.tsFracSec = 0; + } + } else { + entry.tsFracSec = 0; + } + } else if (key === 'level') { + entry.level = value; + } else if (key === 'message') { + entry.message = value; + } else { + entry[key] = value; + } + } + + if (!entry.ts || !entry.message) { + return null; + } + + return entry as LogEntry; +} + +function sanitizeForMermaid(str: string): string { + // Remove special characters that might break Mermaid syntax + return str + .replace(/[:#,]/g, '_') + .replace(/\s+/g, '_') + .replace(/[{}[\]()]/g, '') + .replace(/_+/g, '_') // Replace multiple underscores with single + .replace(/^_|_$/g, '') // Remove leading/trailing underscores + .substring(0, 50); // Limit length +} + +function formatDuration(ms: number): string { + if (ms < 1) return `${(ms * 1000).toFixed(0)}μs`; + if (ms < 1000) return `${ms.toFixed(2)}ms`; + return `${(ms / 1000).toFixed(2)}s`; +} + +function main() { + const args = process.argv.slice(2); + + // Check for --split-by-workflow flag + const splitByWorkflow = args.includes('--split-by-workflow'); + + // Check for time filter arguments + const startTimeIdx = args.findIndex(arg => arg === '--start-time'); + const endTimeIdx = args.findIndex(arg => arg === '--end-time'); + + let startTimeFilter: Date | null = null; + let endTimeFilter: Date | null = null; + + if (startTimeIdx !== -1 && args[startTimeIdx + 1]) { + const timeStr = args[startTimeIdx + 1]; + // Parse time in format HH:mm:ss or full ISO timestamp + if (timeStr.includes('T')) { + startTimeFilter = new Date(timeStr); + } else { + // Assume it's a time today, use a placeholder date that we'll replace + startTimeFilter = new Date(`2000-01-01T${timeStr}Z`); + } + } + + if (endTimeIdx !== -1 && args[endTimeIdx + 1]) { + const timeStr = args[endTimeIdx + 1]; + if (timeStr.includes('T')) { + endTimeFilter = new Date(timeStr); + } else { + endTimeFilter = new Date(`2000-01-01T${timeStr}Z`); + } + } + + const filteredArgs = args.filter(arg => + arg !== '--split-by-workflow' && + arg !== '--start-time' && + arg !== '--end-time' && + !arg.match(/^\d{2}:\d{2}:\d{2}/) && + !arg.match(/^\d{4}-\d{2}-\d{2}T/) + ); + + const inputFile = filteredArgs[0] || '/tmp/rivet-engine.log'; + const outputFile = filteredArgs[1] || '/tmp/rivet-engine-gantt.md'; + + console.log(`Reading log file: ${inputFile}`); + if (splitByWorkflow) { + console.log('Mode: Split by workflow (entries without workflow_id will be excluded)'); + } else { + console.log('Mode: Single section'); + } + if (startTimeFilter || endTimeFilter) { + console.log(`Time filter: ${startTimeFilter ? startTimeFilter.toISOString().substring(11, 19) : 'start'} to ${endTimeFilter ? endTimeFilter.toISOString().substring(11, 19) : 'end'}`); + } + const content = readFileSync(inputFile, 'utf-8'); + const lines = content.split('\n'); + + const entries: LogEntry[] = []; + for (const line of lines) { + if (!line.trim()) continue; + const entry = parseLogfmt(line); + if (entry) { + entries.push(entry); + } + } + + console.log(`Parsed ${entries.length} log entries`); + + if (entries.length === 0) { + console.error('No valid log entries found'); + process.exit(1); + } + + // Apply time filters + if (startTimeFilter || endTimeFilter) { + const originalCount = entries.length; + + // If using HH:mm:ss format, we need to match against the time portion + const useTimeOfDay = startTimeFilter && startTimeFilter.getFullYear() === 2000; + + if (useTimeOfDay) { + // Extract time of day from the placeholder dates + const startTimeOfDay = startTimeFilter ? startTimeFilter.getUTCHours() * 3600 + startTimeFilter.getUTCMinutes() * 60 + startTimeFilter.getUTCSeconds() : 0; + const endTimeOfDay = endTimeFilter ? endTimeFilter.getUTCHours() * 3600 + endTimeFilter.getUTCMinutes() * 60 + endTimeFilter.getUTCSeconds() : 86400; + + entries.splice(0, entries.length, ...entries.filter(entry => { + const entryTimeOfDay = entry.ts.getUTCHours() * 3600 + entry.ts.getUTCMinutes() * 60 + entry.ts.getUTCSeconds(); + return entryTimeOfDay >= startTimeOfDay && entryTimeOfDay <= endTimeOfDay; + })); + } else { + // Use full timestamp comparison + entries.splice(0, entries.length, ...entries.filter(entry => { + const entryTime = entry.ts.getTime(); + const afterStart = !startTimeFilter || entryTime >= startTimeFilter.getTime(); + const beforeEnd = !endTimeFilter || entryTime <= endTimeFilter.getTime(); + return afterStart && beforeEnd; + })); + } + + console.log(`Filtered to ${entries.length} entries (removed ${originalCount - entries.length})`); + + if (entries.length === 0) { + console.error('No entries match the time filter'); + process.exit(1); + } + } + + // Track tasks by workflow_id + const workflowExecutionTasks: Map = new Map(); + const activityTasks: Map = new Map(); + const operationTasks: Map = new Map(); + const signalTasks: Map = new Map(); + + // Track workflow metadata for section naming + const workflowMetadata: Map = new Map(); + + // Track active workflow executions (to pair running/sleeping) + const activeWorkflowExecutions: Map = new Map(); + + // Track signal send times (for calculating recv duration) + const signalSendTimes: Map = new Map(); + + const startTime = entries[0].ts; + + for (const entry of entries) { + // Track workflow metadata and execution segments + if (entry.workflow_id && entry.workflow_name) { + if (!workflowMetadata.has(entry.workflow_id)) { + workflowMetadata.set(entry.workflow_id, { + name: entry.workflow_name, + id: entry.workflow_id + }); + } + + // Track workflow execution segments (running -> sleeping/completed) + if (entry.message === 'running workflow') { + const wfKey = entry.workflow_id; + // Track how many times this workflow has run + const execInfo = activeWorkflowExecutions.get(wfKey); + const execCount = execInfo ? execInfo.count + 1 : 0; + + activeWorkflowExecutions.set(wfKey, { + start: entry.ts, + startFracSec: entry.tsFracSec, + count: execCount + }); + } else if (entry.message === 'workflow sleeping' || entry.message === 'workflow completed' || entry.message === 'workflow error') { + const wfKey = entry.workflow_id; + const execInfo = activeWorkflowExecutions.get(wfKey); + + if (execInfo) { + const taskKey = `${wfKey}_exec_${execInfo.count}`; + workflowExecutionTasks.set(taskKey, { + id: sanitizeForMermaid(`wf_exec_${taskKey}`), + name: `${entry.workflow_name} exec`, + start: execInfo.start, + startFracSec: execInfo.startFracSec, + end: entry.ts, + endFracSec: entry.tsFracSec, + section: `${entry.workflow_name} (${entry.workflow_id})`, + type: 'workflow', + metadata: { workflow_id: entry.workflow_id, ray_id: entry.ray_id, exec_count: execInfo.count } + }); + } + } + } + + // Track activity execution + if (entry.activity_name && entry.workflow_id) { + const locationStr = entry.location ? `_${sanitizeForMermaid(entry.location)}` : ''; + const iterStr = entry.iteration !== undefined ? `_i${entry.iteration}` : ''; + const key = `${entry.workflow_id}_${entry.activity_name}${locationStr}${iterStr}`; + + if (entry.message === 'running activity') { + const wfMeta = workflowMetadata.get(entry.workflow_id); + const sectionName = wfMeta ? `${wfMeta.name} (${wfMeta.id})` : `unknown (${entry.workflow_id})`; + + activityTasks.set(key, { + id: sanitizeForMermaid(`act_${key}`), + name: `${entry.activity_name}${entry.iteration !== undefined ? ` [i${entry.iteration}]` : ''}`, + start: entry.ts, + startFracSec: entry.tsFracSec, + section: sectionName, + type: 'activity', + metadata: { + activity_name: entry.activity_name, + workflow_id: entry.workflow_id, + location: entry.location, + iteration: entry.iteration + } + }); + } else if (entry.message === 'activity success' && activityTasks.has(key)) { + const task = activityTasks.get(key)!; + task.end = entry.ts; + task.endFracSec = entry.tsFracSec; + } + } + + // Track operation calls (without workflow_id, group separately) + if (entry.operation_name && !entry.workflow_id) { + const key = `${entry.operation_name}_${entry.ts.getTime()}`; + + if (entry.message === 'operation call') { + operationTasks.set(key, { + id: sanitizeForMermaid(`op_${key}`), + name: entry.operation_name, + start: entry.ts, + startFracSec: entry.tsFracSec, + section: 'Operations', + type: 'operation', + metadata: { operation_name: entry.operation_name } + }); + } else if (entry.message === 'operation response') { + // Find the most recent matching operation + for (const [k, task] of operationTasks.entries()) { + if (k.startsWith(entry.operation_name) && !task.end) { + task.end = entry.ts; + task.endFracSec = entry.tsFracSec; + break; + } + } + } + } + + // Track signal dispatching and receiving + if (entry.signal_name && entry.workflow_id) { + if (entry.message === 'publishing signal' && entry.signal_id) { + const wfMeta = workflowMetadata.get(entry.workflow_id); + const sectionName = wfMeta ? `${wfMeta.name} (${wfMeta.id})` : `unknown (${entry.workflow_id})`; + + // Create send span in sender's workflow section + const sendKey = `${entry.signal_id}_send`; + signalTasks.set(sendKey, { + id: sanitizeForMermaid(`sig_send_${entry.signal_id}`), + name: `${entry.signal_name} send`, + start: entry.ts, + startFracSec: entry.tsFracSec, + end: entry.ts, + endFracSec: entry.tsFracSec, + section: sectionName, + type: 'signal', + metadata: { signal_name: entry.signal_name, workflow_id: entry.workflow_id, signal_id: entry.signal_id, is_send: true } + }); + + // Store send time for recv span duration calculation + signalSendTimes.set(entry.signal_id, { + ts: entry.ts, + tsFracSec: entry.tsFracSec, + signal_name: entry.signal_name + }); + } else if (entry.message === 'signal received' && entry.signal_id) { + const wfMeta = workflowMetadata.get(entry.workflow_id); + const sectionName = wfMeta ? `${wfMeta.name} (${wfMeta.id})` : `unknown (${entry.workflow_id})`; + + // Create recv span in receiver's workflow section + const sendTime = signalSendTimes.get(entry.signal_id); + const recvKey = `${entry.signal_id}_recv`; + signalTasks.set(recvKey, { + id: sanitizeForMermaid(`sig_recv_${entry.signal_id}`), + name: `${entry.signal_name} recv`, + start: entry.ts, + startFracSec: entry.tsFracSec, + end: entry.ts, + endFracSec: entry.tsFracSec, + section: sectionName, + type: 'signal', + metadata: { + signal_name: entry.signal_name, + workflow_id: entry.workflow_id, + signal_id: entry.signal_id, + is_recv: true, + send_ts: sendTime?.ts, + send_frac: sendTime?.tsFracSec + } + }); + } + } + } + + // Combine all tasks + const allTasks: Task[] = [ + ...workflowExecutionTasks.values(), + ...activityTasks.values(), + ...operationTasks.values(), + ...signalTasks.values() + ]; + + // Close any open tasks with the last timestamp + const lastTime = entries[entries.length - 1].ts; + for (const task of allTasks) { + if (!task.end) { + task.end = lastTime; + } + } + + // Sort tasks by start time + allTasks.sort((a, b) => a.start.getTime() - b.start.getTime()); + + // Generate Mermaid Gantt chart + const lines_out: string[] = []; + lines_out.push('# Rivet Engine Execution Timeline\n'); + lines_out.push('```mermaid'); + lines_out.push('gantt'); + lines_out.push(' title Rivet Engine Log Timeline'); + lines_out.push(' dateFormat YYYY-MM-DDTHH:mm:ss.SSS'); + lines_out.push(' axisFormat %H:%M:%S'); + lines_out.push(''); + + if (splitByWorkflow) { + // Filter out tasks without workflow_id + const workflowFilteredTasks = allTasks.filter(task => { + if (task.type === 'workflow' || task.type === 'activity' || task.type === 'signal') { + return task.metadata.workflow_id; + } + return false; + }); + + // Group tasks by workflow + const workflowGroups = new Map(); + for (const task of workflowFilteredTasks) { + const wfId = task.metadata.workflow_id; + if (!workflowGroups.has(wfId)) { + workflowGroups.set(wfId, []); + } + workflowGroups.get(wfId)!.push(task); + } + + // Output each workflow as a section + for (const [wfId, tasks] of workflowGroups.entries()) { + const wfMeta = workflowMetadata.get(wfId); + const sectionName = wfMeta ? `${wfMeta.name} (${wfId.substring(0, 8)})` : `unknown (${wfId.substring(0, 8)})`; + lines_out.push(` section ${sectionName}`); + + for (const task of tasks) { + const startStr = task.start.toISOString().replace('Z', '').substring(0, 23); + const endStr = task.end!.toISOString().replace('Z', '').substring(0, 23); + + let duration = 0; + // For signal receive spans, calculate duration from send time + if (task.type === 'signal' && task.metadata.is_recv && task.metadata.send_ts) { + const sendTime = task.metadata.send_ts as Date; + const sendFrac = task.metadata.send_frac as number; + const durationMs = task.start.getTime() - sendTime.getTime(); + const durationFracSec = task.startFracSec - sendFrac; + duration = durationMs + durationFracSec * 1000; + } else { + const durationMs = task.end!.getTime() - task.start.getTime(); + const durationFracSec = (task.endFracSec || 0) - task.startFracSec; + duration = durationMs + durationFracSec * 1000; + } + + let taskName = ''; + if (task.type === 'workflow') { + taskName = `wf(${task.name})`; + } else if (task.type === 'activity') { + taskName = `act(${task.name})`; + } else if (task.type === 'signal') { + taskName = `sig(${task.name})`; + } + + if (duration > 0.01) { + taskName = `${taskName} ${formatDuration(duration)}`; + } + + taskName = taskName.replace(/[,]/g, ''); + + let status = ''; + if (task.type === 'activity') { + status = 'active, '; + } + + lines_out.push(` ${taskName} :${status}${task.id}, ${startStr}, ${endStr}`); + } + lines_out.push(''); + } + } else { + // Output all tasks in single section + lines_out.push(' section Execution'); + + for (const task of allTasks) { + // Format dates with milliseconds + const startStr = task.start.toISOString().replace('Z', '').substring(0, 23); + const endStr = task.end!.toISOString().replace('Z', '').substring(0, 23); + + // Calculate duration with sub-millisecond precision + let duration = 0; + // For signal receive spans, calculate duration from send time + if (task.type === 'signal' && task.metadata.is_recv && task.metadata.send_ts) { + const sendTime = task.metadata.send_ts as Date; + const sendFrac = task.metadata.send_frac as number; + const durationMs = task.start.getTime() - sendTime.getTime(); + const durationFracSec = task.startFracSec - sendFrac; + duration = durationMs + durationFracSec * 1000; + } else { + const durationMs = task.end!.getTime() - task.start.getTime(); + const durationFracSec = (task.endFracSec || 0) - task.startFracSec; + duration = durationMs + durationFracSec * 1000; + } + + // Create shortened task name based on type + let taskName = ''; + if (task.type === 'workflow') { + const wfId = task.metadata.workflow_id.substring(0, 8); + taskName = `wf(${task.name} ${wfId})`; + } else if (task.type === 'activity') { + const wfId = task.metadata.workflow_id.substring(0, 8); + taskName = `act(${task.name} ${wfId})`; + } else if (task.type === 'operation') { + taskName = `op(${task.name})`; + } else if (task.type === 'signal') { + const wfId = task.metadata.workflow_id?.substring(0, 8) || 'unknown'; + taskName = `sig(${task.name} ${wfId})`; + } + + // Add duration to task name if it's meaningful + if (duration > 0.01) { // Only show duration if > 10μs + taskName = `${taskName} ${formatDuration(duration)}`; + } + + // Remove commas that might break syntax + taskName = taskName.replace(/[,]/g, ''); + + // Determine task status based on type + let status = ''; + if (task.type === 'activity') { + status = 'active, '; + } else if (task.type === 'operation') { + status = 'crit, '; + } + + lines_out.push(` ${taskName} :${status}${task.id}, ${startStr}, ${endStr}`); + } + lines_out.push(''); + } + + lines_out.push('```'); + lines_out.push(''); + lines_out.push('## Statistics\n'); + lines_out.push(`- Total log entries: ${entries.length}`); + lines_out.push(`- Total tasks tracked: ${allTasks.length}`); + lines_out.push(`- Time range: ${startTime.toISOString()} to ${lastTime.toISOString()}`); + lines_out.push(`- Duration: ${formatDuration(lastTime.getTime() - startTime.getTime())}`); + lines_out.push(''); + lines_out.push('### Task Breakdown\n'); + lines_out.push(`- Workflow Executions: ${workflowExecutionTasks.size}`); + lines_out.push(`- Activities: ${activityTasks.size}`); + lines_out.push(`- Operations: ${operationTasks.size}`); + lines_out.push(`- Signals: ${signalTasks.size}`); + + writeFileSync(outputFile, lines_out.join('\n')); + console.log(`\nGenerated Gantt chart at: ${outputFile}`); + console.log(`Total tasks: ${allTasks.length}`); + console.log(`Time range: ${formatDuration(lastTime.getTime() - startTime.getTime())}`); +} + +if (require.main === module) { + main(); +} diff --git a/scripts/debug/package.json b/scripts/debug/package.json new file mode 100644 index 0000000000..051ae86863 --- /dev/null +++ b/scripts/debug/package.json @@ -0,0 +1,16 @@ +{ + "name": "debug", + "version": "2.0.21", + "description": "", + "main": "index.js", + "scripts": { + "check-types": "tsc --noEmit" + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@10.13.1", + "devDependencies": { + "@types/node": "^24.3.1" + } +} \ No newline at end of file diff --git a/scripts/debug/tsconfig.json b/scripts/debug/tsconfig.json new file mode 100644 index 0000000000..7e3793dfc6 --- /dev/null +++ b/scripts/debug/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["**/*.ts"] +} diff --git a/scripts/debug/turbo.json b/scripts/debug/turbo.json new file mode 100644 index 0000000000..29d4cb2625 --- /dev/null +++ b/scripts/debug/turbo.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"] +}