gemini-cli-devtools 0.1.4 → 0.1.5
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/src/bin.js +0 -21
- package/dist/src/index.js +1 -106
- package/package.json +1 -1
package/dist/src/bin.js
CHANGED
|
@@ -5,29 +5,8 @@
|
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
|
6
6
|
*/
|
|
7
7
|
import { DevTools } from './index.js';
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
import os from 'node:os';
|
|
10
|
-
import fs from 'node:fs';
|
|
11
8
|
async function main() {
|
|
12
9
|
const devTools = DevTools.getInstance();
|
|
13
|
-
// Smart Discovery:
|
|
14
|
-
// 1. User provided path
|
|
15
|
-
// 2. Local project temp dirs
|
|
16
|
-
// 3. System temp dir
|
|
17
|
-
let targetPath = process.argv[2];
|
|
18
|
-
if (!targetPath) {
|
|
19
|
-
const localPath = path.join(process.cwd(), '.gemini', 'tmp', 'logs');
|
|
20
|
-
if (fs.existsSync(localPath)) {
|
|
21
|
-
targetPath = localPath;
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
targetPath = os.tmpdir();
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
const absolutePath = path.resolve(targetPath);
|
|
28
|
-
console.log(`🔍 Watching logs in: ${absolutePath}`);
|
|
29
|
-
// Note: DevTools.setLogFile automatically scans standard roots.
|
|
30
|
-
devTools.setLogFile();
|
|
31
10
|
const url = await devTools.start();
|
|
32
11
|
if (url) {
|
|
33
12
|
console.log(`🚀 Gemini DevTools active at: ${url}`);
|
package/dist/src/index.js
CHANGED
|
@@ -22,7 +22,7 @@ const isDev = (() => {
|
|
|
22
22
|
/**
|
|
23
23
|
* DevTools Viewer
|
|
24
24
|
*
|
|
25
|
-
*
|
|
25
|
+
* Receives logs via WebSocket from CLI sessions.
|
|
26
26
|
*/
|
|
27
27
|
export class DevTools extends EventEmitter {
|
|
28
28
|
static instance;
|
|
@@ -32,7 +32,6 @@ export class DevTools extends EventEmitter {
|
|
|
32
32
|
wss = null;
|
|
33
33
|
sessions = new Map();
|
|
34
34
|
port = 25417;
|
|
35
|
-
watchedFiles = new Map(); // filePath -> lastSize
|
|
36
35
|
constructor() {
|
|
37
36
|
super();
|
|
38
37
|
}
|
|
@@ -48,110 +47,6 @@ export class DevTools extends EventEmitter {
|
|
|
48
47
|
getConsoleLogs() {
|
|
49
48
|
return this.consoleLogs;
|
|
50
49
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Main entry for log discovery.
|
|
53
|
-
* It scans both user home and system tmp for .gemini/tmp folders.
|
|
54
|
-
*/
|
|
55
|
-
setLogFile() {
|
|
56
|
-
const potentialRoots = [
|
|
57
|
-
path.join(os.homedir(), '.gemini', 'tmp'),
|
|
58
|
-
path.join(os.tmpdir(), '.gemini', 'tmp'),
|
|
59
|
-
];
|
|
60
|
-
for (const baseDir of potentialRoots) {
|
|
61
|
-
if (fs.existsSync(baseDir)) {
|
|
62
|
-
console.log(`🔍 Scanning for logs in: ${baseDir}`);
|
|
63
|
-
this.deepDiscover(baseDir);
|
|
64
|
-
this.watchRoot(baseDir);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
watchRoot(root) {
|
|
69
|
-
try {
|
|
70
|
-
fs.watch(root, { recursive: true }, (_event, filename) => {
|
|
71
|
-
if (filename &&
|
|
72
|
-
filename.includes('session-') &&
|
|
73
|
-
filename.endsWith('.jsonl')) {
|
|
74
|
-
this.deepDiscover(root);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
setInterval(() => this.deepDiscover(root), 2000);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
deepDiscover(dir) {
|
|
83
|
-
try {
|
|
84
|
-
const items = fs.readdirSync(dir);
|
|
85
|
-
for (const item of items) {
|
|
86
|
-
const fullPath = path.join(dir, item);
|
|
87
|
-
let stats;
|
|
88
|
-
try {
|
|
89
|
-
stats = fs.statSync(fullPath);
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (stats.isDirectory()) {
|
|
95
|
-
if (item === 'logs' || item.length > 20) {
|
|
96
|
-
this.deepDiscover(fullPath);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
else if (item.startsWith('session-') && item.endsWith('.jsonl')) {
|
|
100
|
-
if (!this.watchedFiles.has(fullPath)) {
|
|
101
|
-
this.watchedFiles.set(fullPath, 0);
|
|
102
|
-
this.readNewLogs(fullPath, 0);
|
|
103
|
-
}
|
|
104
|
-
else if (stats.size > this.watchedFiles.get(fullPath)) {
|
|
105
|
-
this.readNewLogs(fullPath, this.watchedFiles.get(fullPath));
|
|
106
|
-
this.watchedFiles.set(fullPath, stats.size);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
/* ignore */
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
readNewLogs(filePath, startByte) {
|
|
116
|
-
try {
|
|
117
|
-
const filename = path.basename(filePath);
|
|
118
|
-
const sessionMatch = filename.match(/session-(.*)\.jsonl/);
|
|
119
|
-
const fallbackSessionId = sessionMatch ? sessionMatch[1] : undefined;
|
|
120
|
-
const stream = fs.createReadStream(filePath, { start: startByte });
|
|
121
|
-
let buffer = '';
|
|
122
|
-
stream.on('data', (chunk) => {
|
|
123
|
-
buffer += chunk.toString();
|
|
124
|
-
const lines = buffer.split('\n');
|
|
125
|
-
buffer = lines.pop() || '';
|
|
126
|
-
for (const line of lines) {
|
|
127
|
-
if (!line.trim())
|
|
128
|
-
continue;
|
|
129
|
-
try {
|
|
130
|
-
const entry = JSON.parse(line);
|
|
131
|
-
const sid = entry.sessionId || fallbackSessionId;
|
|
132
|
-
if (entry.type === 'console') {
|
|
133
|
-
this.addInternalConsoleLog(entry.payload, sid, entry.timestamp);
|
|
134
|
-
}
|
|
135
|
-
else if (entry.type === 'network') {
|
|
136
|
-
this.addInternalNetworkLog(entry.payload, sid, entry.timestamp);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
/* ignore */
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
try {
|
|
144
|
-
this.watchedFiles.set(filePath, fs.statSync(filePath).size);
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
/* ignore */
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
/* ignore */
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
50
|
addInternalConsoleLog(payload, sessionId, timestamp) {
|
|
156
51
|
this.consoleLogs.push({
|
|
157
52
|
...payload,
|