dev3000 0.0.44 → 0.0.46

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.
@@ -1,7 +1,129 @@
1
1
  import LogsClient from './LogsClient';
2
+ import { redirect } from 'next/navigation';
3
+ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
4
+ import { join, dirname, basename } from 'path';
5
+ import { parseLogEntries } from './utils';
2
6
 
3
- export default function LogsPage() {
7
+ interface PageProps {
8
+ searchParams: { file?: string; mode?: 'head' | 'tail' };
9
+ }
10
+
11
+ async function getLogFiles() {
12
+ try {
13
+ const currentLogPath = process.env.LOG_FILE_PATH || '/tmp/dev3000.log';
14
+
15
+ if (!existsSync(currentLogPath)) {
16
+ return { files: [], currentFile: '', projectName: 'unknown' };
17
+ }
18
+
19
+ const logDir = dirname(currentLogPath);
20
+ const currentLogName = basename(currentLogPath);
21
+
22
+ // Extract project name from current log filename
23
+ const projectMatch = currentLogName.match(/^dev3000-(.+?)-\d{4}-\d{2}-\d{2}T/);
24
+ const projectName = projectMatch ? projectMatch[1] : 'unknown';
25
+
26
+ const dirContents = readdirSync(logDir);
27
+ const logFiles = dirContents
28
+ .filter(file =>
29
+ file.startsWith(`dev3000-${projectName}-`) &&
30
+ file.endsWith('.log')
31
+ )
32
+ .map(file => {
33
+ const filePath = join(logDir, file);
34
+ const stats = statSync(filePath);
35
+
36
+ const timestampMatch = file.match(/(\d{4}-\d{2}-\d{2}T[\d-]+Z)/);
37
+ const timestamp = timestampMatch ? timestampMatch[1].replace(/-/g, ':') : '';
38
+
39
+ return {
40
+ name: file,
41
+ path: filePath,
42
+ timestamp,
43
+ size: stats.size,
44
+ mtime: stats.mtime,
45
+ isCurrent: file === currentLogName
46
+ };
47
+ })
48
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
49
+
50
+ return {
51
+ files: logFiles,
52
+ currentFile: currentLogPath,
53
+ projectName
54
+ };
55
+ } catch (error) {
56
+ return { files: [], currentFile: '', projectName: 'unknown' };
57
+ }
58
+ }
59
+
60
+ async function getLogData(logPath: string, mode: 'head' | 'tail' = 'tail', lines: number = 100) {
61
+ try {
62
+ if (!existsSync(logPath)) {
63
+ return { logs: '', total: 0 };
64
+ }
65
+
66
+ const logContent = readFileSync(logPath, 'utf-8');
67
+ const allLines = logContent.split('\n').filter(line => line.trim());
68
+
69
+ const selectedLines = mode === 'head'
70
+ ? allLines.slice(0, lines)
71
+ : allLines.slice(-lines);
72
+
73
+ return {
74
+ logs: selectedLines.join('\n'),
75
+ total: allLines.length
76
+ };
77
+ } catch (error) {
78
+ return { logs: '', total: 0 };
79
+ }
80
+ }
81
+
82
+ export default async function LogsPage({ searchParams }: PageProps) {
4
83
  const version = process.env.DEV3000_VERSION || '0.0.0';
5
84
 
6
- return <LogsClient version={version} />;
85
+ // Get available log files
86
+ const { files, currentFile } = await getLogFiles();
87
+
88
+ // If no file specified and we have files, redirect to latest
89
+ if (!searchParams.file && files.length > 0) {
90
+ const latestFile = files[0].name;
91
+ redirect(`/logs?file=${encodeURIComponent(latestFile)}`);
92
+ }
93
+
94
+ // If no file specified and no files available, render with empty data
95
+ if (!searchParams.file) {
96
+ return (
97
+ <LogsClient
98
+ version={version}
99
+ initialData={{
100
+ logs: [],
101
+ logFiles: [],
102
+ currentLogFile: '',
103
+ mode: 'tail'
104
+ }}
105
+ />
106
+ );
107
+ }
108
+
109
+ // Find the selected log file
110
+ const selectedFile = files.find(f => f.name === searchParams.file);
111
+ const logPath = selectedFile?.path || currentFile;
112
+ const mode = (searchParams.mode as 'head' | 'tail') || 'tail';
113
+
114
+ // Get initial log data server-side
115
+ const logData = await getLogData(logPath, mode);
116
+ const parsedLogs = parseLogEntries(logData.logs);
117
+
118
+ return (
119
+ <LogsClient
120
+ version={version}
121
+ initialData={{
122
+ logs: parsedLogs,
123
+ logFiles: files,
124
+ currentLogFile: logPath,
125
+ mode
126
+ }}
127
+ />
128
+ );
7
129
  }
@@ -0,0 +1,46 @@
1
+ import { LogEntry } from '@/types';
2
+
3
+ export function parseLogEntries(logContent: string): LogEntry[] {
4
+ // Split by timestamp pattern - each timestamp starts a new log entry
5
+ const timestampPattern = /\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\] \[([^\]]+)\] /;
6
+
7
+ const entries: LogEntry[] = [];
8
+ const lines = logContent.split('\n');
9
+ let currentEntry: LogEntry | null = null;
10
+
11
+ for (const line of lines) {
12
+ if (!line.trim()) continue;
13
+
14
+ const match = line.match(timestampPattern);
15
+ if (match) {
16
+ // Save previous entry if exists
17
+ if (currentEntry) {
18
+ entries.push(currentEntry);
19
+ }
20
+
21
+ // Start new entry
22
+ const [fullMatch, timestamp, source] = match;
23
+ const message = line.substring(fullMatch.length);
24
+ const screenshot = message.match(/\[SCREENSHOT\] (.+)/)?.[1];
25
+
26
+ currentEntry = {
27
+ timestamp,
28
+ source,
29
+ message,
30
+ screenshot,
31
+ original: line
32
+ };
33
+ } else if (currentEntry) {
34
+ // Append to current entry's message
35
+ currentEntry.message += '\n' + line;
36
+ currentEntry.original += '\n' + line;
37
+ }
38
+ }
39
+
40
+ // Don't forget the last entry
41
+ if (currentEntry) {
42
+ entries.push(currentEntry);
43
+ }
44
+
45
+ return entries;
46
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev3000",
3
- "version": "0.0.44",
3
+ "version": "0.0.46",
4
4
  "description": "AI-powered development tools with browser monitoring and MCP server integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,6 +30,7 @@
30
30
  "claude"
31
31
  ],
32
32
  "dependencies": {
33
+ "biome": "^0.3.3",
33
34
  "chalk": "^5.3.0",
34
35
  "cli-progress": "^3.12.0",
35
36
  "commander": "^11.1.0",
@@ -59,6 +60,7 @@
59
60
  "scripts": {
60
61
  "build": "tsc && mkdir -p dist/src && cp src/loading.html dist/src/",
61
62
  "dev": "tsc --watch",
63
+ "format": "biome format --write .",
62
64
  "test": "vitest run",
63
65
  "postinstall": "cd mcp-server && pnpm install --frozen-lockfile",
64
66
  "release": "./scripts/release.sh",