@myerscarpenter/quest-dev 1.0.8 → 1.1.0
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/build/commands/logcat.d.ts +24 -0
- package/build/commands/logcat.d.ts.map +1 -0
- package/build/commands/logcat.js +261 -0
- package/build/commands/logcat.js.map +1 -0
- package/build/index.js +35 -0
- package/build/index.js.map +1 -1
- package/build/utils/adb.d.ts.map +1 -1
- package/build/utils/adb.js +0 -1
- package/build/utils/adb.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/logcat.ts +288 -0
- package/src/index.ts +42 -0
- package/src/utils/adb.ts +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quest logcat command
|
|
3
|
+
* Captures Android logcat to files for Quest debugging
|
|
4
|
+
*
|
|
5
|
+
* CRITICAL: Quest's ring buffer fills in seconds under VR load.
|
|
6
|
+
* Always capture to a file BEFORE testing to avoid losing crash logs.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Start logcat capture
|
|
10
|
+
*/
|
|
11
|
+
export declare function startCommand(filter?: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Stop logcat capture
|
|
14
|
+
*/
|
|
15
|
+
export declare function stopCommand(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Show capture status
|
|
18
|
+
*/
|
|
19
|
+
export declare function statusCommand(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Tail current capture
|
|
22
|
+
*/
|
|
23
|
+
export declare function tailCommand(): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=logcat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logcat.d.ts","sourceRoot":"","sources":["../../src/commands/logcat.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8GH;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEjE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA+BjD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAgCnD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBjD"}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quest logcat command
|
|
3
|
+
* Captures Android logcat to files for Quest debugging
|
|
4
|
+
*
|
|
5
|
+
* CRITICAL: Quest's ring buffer fills in seconds under VR load.
|
|
6
|
+
* Always capture to a file BEFORE testing to avoid losing crash logs.
|
|
7
|
+
*/
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync, symlinkSync, statSync, readlinkSync, openSync } from 'fs';
|
|
10
|
+
import { spawn } from 'child_process';
|
|
11
|
+
import { checkADBPath, checkADBDevices } from '../utils/adb.js';
|
|
12
|
+
import { execCommand, execCommandFull } from '../utils/exec.js';
|
|
13
|
+
const LOG_DIR = process.env.LOG_DIR || 'logs/logcat';
|
|
14
|
+
const PID_FILE = join(LOG_DIR, '.logcat_pid');
|
|
15
|
+
const LOGFILE_LINK = join(LOG_DIR, 'latest.txt');
|
|
16
|
+
/**
|
|
17
|
+
* Ensure log directory exists
|
|
18
|
+
*/
|
|
19
|
+
function ensureLogDir() {
|
|
20
|
+
if (!existsSync(LOG_DIR)) {
|
|
21
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if a process is running by PID
|
|
26
|
+
*/
|
|
27
|
+
function isProcessRunning(pid) {
|
|
28
|
+
try {
|
|
29
|
+
// Sending signal 0 checks if process exists without killing it
|
|
30
|
+
process.kill(pid, 0);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read PID from PID file
|
|
39
|
+
*/
|
|
40
|
+
function readPidFile() {
|
|
41
|
+
if (!existsSync(PID_FILE)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const pidStr = readFileSync(PID_FILE, 'utf-8').trim();
|
|
46
|
+
return parseInt(pidStr, 10);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Write PID to PID file
|
|
54
|
+
*/
|
|
55
|
+
function writePidFile(pid) {
|
|
56
|
+
writeFileSync(PID_FILE, pid.toString(), 'utf-8');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Delete PID file
|
|
60
|
+
*/
|
|
61
|
+
function deletePidFile() {
|
|
62
|
+
if (existsSync(PID_FILE)) {
|
|
63
|
+
unlinkSync(PID_FILE);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the latest log file path
|
|
68
|
+
*/
|
|
69
|
+
function getLatestLogFile() {
|
|
70
|
+
if (!existsSync(LOGFILE_LINK)) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const target = readlinkSync(LOGFILE_LINK);
|
|
75
|
+
const fullPath = join(LOG_DIR, target);
|
|
76
|
+
if (existsSync(fullPath)) {
|
|
77
|
+
return fullPath;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Symlink might be broken
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get file size and line count
|
|
87
|
+
*/
|
|
88
|
+
function getFileStats(filePath) {
|
|
89
|
+
try {
|
|
90
|
+
const stats = statSync(filePath);
|
|
91
|
+
const sizeInBytes = stats.size;
|
|
92
|
+
let sizeStr;
|
|
93
|
+
if (sizeInBytes < 1024) {
|
|
94
|
+
sizeStr = `${sizeInBytes}B`;
|
|
95
|
+
}
|
|
96
|
+
else if (sizeInBytes < 1024 * 1024) {
|
|
97
|
+
sizeStr = `${(sizeInBytes / 1024).toFixed(1)}K`;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
sizeStr = `${(sizeInBytes / (1024 * 1024)).toFixed(1)}M`;
|
|
101
|
+
}
|
|
102
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
103
|
+
const lines = content.split('\n').length;
|
|
104
|
+
return { size: sizeStr, lines };
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Start logcat capture
|
|
112
|
+
*/
|
|
113
|
+
export async function startCommand(filter) {
|
|
114
|
+
// Check for existing capture
|
|
115
|
+
const existingPid = readPidFile();
|
|
116
|
+
if (existingPid && isProcessRunning(existingPid)) {
|
|
117
|
+
console.error('Already capturing. Use "quest-dev logcat stop" first.');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
// Check prerequisites
|
|
121
|
+
checkADBPath();
|
|
122
|
+
await checkADBDevices();
|
|
123
|
+
ensureLogDir();
|
|
124
|
+
// Generate log filename
|
|
125
|
+
const timestamp = new Date().toISOString()
|
|
126
|
+
.replace(/[-:]/g, '')
|
|
127
|
+
.replace(/\..+/, '')
|
|
128
|
+
.replace('T', '_')
|
|
129
|
+
.slice(0, 15); // YYYYMMDD_HHMMSS
|
|
130
|
+
const logFile = join(LOG_DIR, `logcat_${timestamp}.txt`);
|
|
131
|
+
console.log(`Starting capture to: ${logFile}`);
|
|
132
|
+
// Clear the buffer first - critical for Quest
|
|
133
|
+
try {
|
|
134
|
+
await execCommand('adb', ['logcat', '-c']);
|
|
135
|
+
console.log('Ring buffer cleared.');
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.error('Failed to clear ring buffer:', error.message);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
if (filter) {
|
|
142
|
+
console.log(`Filter: ${filter}`);
|
|
143
|
+
}
|
|
144
|
+
// Start background logcat process
|
|
145
|
+
const args = ['logcat', '-v', 'threadtime'];
|
|
146
|
+
if (filter) {
|
|
147
|
+
args.push(filter);
|
|
148
|
+
}
|
|
149
|
+
// Open file for writing
|
|
150
|
+
const fd = openSync(logFile, 'w');
|
|
151
|
+
const proc = spawn('adb', args, {
|
|
152
|
+
stdio: ['ignore', fd, fd],
|
|
153
|
+
detached: true
|
|
154
|
+
});
|
|
155
|
+
// Unref so parent can exit immediately
|
|
156
|
+
proc.unref();
|
|
157
|
+
// Save PID
|
|
158
|
+
writePidFile(proc.pid);
|
|
159
|
+
// Update symlink
|
|
160
|
+
try {
|
|
161
|
+
if (existsSync(LOGFILE_LINK)) {
|
|
162
|
+
unlinkSync(LOGFILE_LINK);
|
|
163
|
+
}
|
|
164
|
+
symlinkSync(`logcat_${timestamp}.txt`, LOGFILE_LINK);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
console.warn('Warning: Failed to create symlink:', error.message);
|
|
168
|
+
}
|
|
169
|
+
console.log(`Capturing (PID: ${proc.pid})`);
|
|
170
|
+
console.log('');
|
|
171
|
+
console.log('Now run your test. When done: quest-dev logcat stop');
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Stop logcat capture
|
|
175
|
+
*/
|
|
176
|
+
export async function stopCommand() {
|
|
177
|
+
const pid = readPidFile();
|
|
178
|
+
if (!pid) {
|
|
179
|
+
console.log('No capture in progress');
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (isProcessRunning(pid)) {
|
|
183
|
+
try {
|
|
184
|
+
process.kill(pid, 'SIGTERM');
|
|
185
|
+
console.log(`Capture stopped (PID: ${pid})`);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.log(`Capture process already ended (PID: ${pid})`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.log('Capture process already ended');
|
|
193
|
+
}
|
|
194
|
+
deletePidFile();
|
|
195
|
+
// Show file info
|
|
196
|
+
const latestFile = getLatestLogFile();
|
|
197
|
+
if (latestFile) {
|
|
198
|
+
const stats = getFileStats(latestFile);
|
|
199
|
+
if (stats) {
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log(`Log file: ${latestFile}`);
|
|
202
|
+
console.log(`Size: ${stats.size} (${stats.lines} lines)`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Show capture status
|
|
208
|
+
*/
|
|
209
|
+
export async function statusCommand() {
|
|
210
|
+
const pid = readPidFile();
|
|
211
|
+
if (pid && isProcessRunning(pid)) {
|
|
212
|
+
console.log(`Capturing (PID: ${pid})`);
|
|
213
|
+
const latestFile = getLatestLogFile();
|
|
214
|
+
if (latestFile) {
|
|
215
|
+
const stats = getFileStats(latestFile);
|
|
216
|
+
if (stats) {
|
|
217
|
+
console.log(`File: ${latestFile}`);
|
|
218
|
+
console.log(`Size: ${stats.size} (${stats.lines} lines)`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
console.log('Not capturing');
|
|
224
|
+
// Show recent logs
|
|
225
|
+
if (existsSync(LOG_DIR)) {
|
|
226
|
+
console.log('');
|
|
227
|
+
console.log('Recent logs:');
|
|
228
|
+
try {
|
|
229
|
+
const result = await execCommandFull('ls', ['-lht', join(LOG_DIR, '*.txt')]);
|
|
230
|
+
if (result.code === 0) {
|
|
231
|
+
const lines = result.stdout.trim().split('\n').slice(0, 5);
|
|
232
|
+
lines.forEach(line => console.log(' ' + line));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Ignore
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Tail current capture
|
|
243
|
+
*/
|
|
244
|
+
export async function tailCommand() {
|
|
245
|
+
const latestFile = getLatestLogFile();
|
|
246
|
+
if (!latestFile) {
|
|
247
|
+
console.error('No active log file');
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
console.log(`Tailing: ${latestFile}`);
|
|
251
|
+
console.log('Press Ctrl+C to stop\n');
|
|
252
|
+
// Use tail -f
|
|
253
|
+
const tailProc = spawn('tail', ['-f', latestFile], {
|
|
254
|
+
stdio: 'inherit'
|
|
255
|
+
});
|
|
256
|
+
tailProc.on('error', (error) => {
|
|
257
|
+
console.error('Failed to tail log:', error.message);
|
|
258
|
+
process.exit(1);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=logcat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logcat.js","sourceRoot":"","sources":["../../src/commands/logcat.ts"],"names":[],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { fileURLToPath } from 'url';
|
|
|
10
10
|
import { dirname, join } from 'path';
|
|
11
11
|
import { screenshotCommand } from './commands/screenshot.js';
|
|
12
12
|
import { openCommand } from './commands/open.js';
|
|
13
|
+
import { startCommand, stopCommand, statusCommand, tailCommand } from './commands/logcat.js';
|
|
13
14
|
// Read version from package.json
|
|
14
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
@@ -60,6 +61,40 @@ cli.command('open <url>', 'Open URL in Quest browser (sets up CDP debugging port
|
|
|
60
61
|
}, async (argv) => {
|
|
61
62
|
await openCommand(argv.url, argv.closeOthers);
|
|
62
63
|
});
|
|
64
|
+
// Logcat command
|
|
65
|
+
cli.command('logcat <action>', 'Capture Android logcat to files (CRITICAL: always start before testing to avoid losing crash logs)', (yargs) => {
|
|
66
|
+
return yargs
|
|
67
|
+
.positional('action', {
|
|
68
|
+
describe: 'Action to perform',
|
|
69
|
+
type: 'string',
|
|
70
|
+
choices: ['start', 'stop', 'status', 'tail'],
|
|
71
|
+
demandOption: true
|
|
72
|
+
})
|
|
73
|
+
.option('filter', {
|
|
74
|
+
describe: 'Logcat filter expression (e.g., "*:W" for warnings+, "chromium:V *:S" for chromium only)',
|
|
75
|
+
type: 'string'
|
|
76
|
+
});
|
|
77
|
+
}, async (argv) => {
|
|
78
|
+
const action = argv.action;
|
|
79
|
+
const filter = argv.filter;
|
|
80
|
+
switch (action) {
|
|
81
|
+
case 'start':
|
|
82
|
+
await startCommand(filter);
|
|
83
|
+
break;
|
|
84
|
+
case 'stop':
|
|
85
|
+
await stopCommand();
|
|
86
|
+
break;
|
|
87
|
+
case 'status':
|
|
88
|
+
await statusCommand();
|
|
89
|
+
break;
|
|
90
|
+
case 'tail':
|
|
91
|
+
await tailCommand();
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
console.error(`Unknown action: ${action}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
63
98
|
// Parse and execute
|
|
64
99
|
cli.parse();
|
|
65
100
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/build/utils/adb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../../src/utils/adb.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../../src/utils/adb.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAiBrC;AAoBD;;GAEG;AACH,wBAAsB,eAAe,CAAC,UAAU,SAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAqCtE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiB9D;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CtE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAOzD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAmBjE;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CA8BzD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAe1D;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAUrD"}
|
package/build/utils/adb.js
CHANGED
package/build/utils/adb.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adb.js","sourceRoot":"","sources":["../../src/utils/adb.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEzD,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,wDAAwD;AAE/E;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,
|
|
1
|
+
{"version":3,"file":"adb.js","sourceRoot":"","sources":["../../src/utils/adb.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quest logcat command
|
|
3
|
+
* Captures Android logcat to files for Quest debugging
|
|
4
|
+
*
|
|
5
|
+
* CRITICAL: Quest's ring buffer fills in seconds under VR load.
|
|
6
|
+
* Always capture to a file BEFORE testing to avoid losing crash logs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { resolve, join } from 'path';
|
|
10
|
+
import { readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync, symlinkSync, statSync, readlinkSync, openSync } from 'fs';
|
|
11
|
+
import { spawn } from 'child_process';
|
|
12
|
+
import { checkADBPath, checkADBDevices } from '../utils/adb.js';
|
|
13
|
+
import { execCommand, execCommandFull } from '../utils/exec.js';
|
|
14
|
+
|
|
15
|
+
const LOG_DIR = process.env.LOG_DIR || 'logs/logcat';
|
|
16
|
+
const PID_FILE = join(LOG_DIR, '.logcat_pid');
|
|
17
|
+
const LOGFILE_LINK = join(LOG_DIR, 'latest.txt');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Ensure log directory exists
|
|
21
|
+
*/
|
|
22
|
+
function ensureLogDir(): void {
|
|
23
|
+
if (!existsSync(LOG_DIR)) {
|
|
24
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if a process is running by PID
|
|
30
|
+
*/
|
|
31
|
+
function isProcessRunning(pid: number): boolean {
|
|
32
|
+
try {
|
|
33
|
+
// Sending signal 0 checks if process exists without killing it
|
|
34
|
+
process.kill(pid, 0);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read PID from PID file
|
|
43
|
+
*/
|
|
44
|
+
function readPidFile(): number | null {
|
|
45
|
+
if (!existsSync(PID_FILE)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const pidStr = readFileSync(PID_FILE, 'utf-8').trim();
|
|
50
|
+
return parseInt(pidStr, 10);
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Write PID to PID file
|
|
58
|
+
*/
|
|
59
|
+
function writePidFile(pid: number): void {
|
|
60
|
+
writeFileSync(PID_FILE, pid.toString(), 'utf-8');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Delete PID file
|
|
65
|
+
*/
|
|
66
|
+
function deletePidFile(): void {
|
|
67
|
+
if (existsSync(PID_FILE)) {
|
|
68
|
+
unlinkSync(PID_FILE);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the latest log file path
|
|
74
|
+
*/
|
|
75
|
+
function getLatestLogFile(): string | null {
|
|
76
|
+
if (!existsSync(LOGFILE_LINK)) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const target = readlinkSync(LOGFILE_LINK);
|
|
81
|
+
const fullPath = join(LOG_DIR, target);
|
|
82
|
+
if (existsSync(fullPath)) {
|
|
83
|
+
return fullPath;
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// Symlink might be broken
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get file size and line count
|
|
93
|
+
*/
|
|
94
|
+
function getFileStats(filePath: string): { size: string; lines: number } | null {
|
|
95
|
+
try {
|
|
96
|
+
const stats = statSync(filePath);
|
|
97
|
+
const sizeInBytes = stats.size;
|
|
98
|
+
let sizeStr: string;
|
|
99
|
+
|
|
100
|
+
if (sizeInBytes < 1024) {
|
|
101
|
+
sizeStr = `${sizeInBytes}B`;
|
|
102
|
+
} else if (sizeInBytes < 1024 * 1024) {
|
|
103
|
+
sizeStr = `${(sizeInBytes / 1024).toFixed(1)}K`;
|
|
104
|
+
} else {
|
|
105
|
+
sizeStr = `${(sizeInBytes / (1024 * 1024)).toFixed(1)}M`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
109
|
+
const lines = content.split('\n').length;
|
|
110
|
+
|
|
111
|
+
return { size: sizeStr, lines };
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Start logcat capture
|
|
119
|
+
*/
|
|
120
|
+
export async function startCommand(filter?: string): Promise<void> {
|
|
121
|
+
// Check for existing capture
|
|
122
|
+
const existingPid = readPidFile();
|
|
123
|
+
if (existingPid && isProcessRunning(existingPid)) {
|
|
124
|
+
console.error('Already capturing. Use "quest-dev logcat stop" first.');
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Check prerequisites
|
|
129
|
+
checkADBPath();
|
|
130
|
+
await checkADBDevices();
|
|
131
|
+
|
|
132
|
+
ensureLogDir();
|
|
133
|
+
|
|
134
|
+
// Generate log filename
|
|
135
|
+
const timestamp = new Date().toISOString()
|
|
136
|
+
.replace(/[-:]/g, '')
|
|
137
|
+
.replace(/\..+/, '')
|
|
138
|
+
.replace('T', '_')
|
|
139
|
+
.slice(0, 15); // YYYYMMDD_HHMMSS
|
|
140
|
+
const logFile = join(LOG_DIR, `logcat_${timestamp}.txt`);
|
|
141
|
+
|
|
142
|
+
console.log(`Starting capture to: ${logFile}`);
|
|
143
|
+
|
|
144
|
+
// Clear the buffer first - critical for Quest
|
|
145
|
+
try {
|
|
146
|
+
await execCommand('adb', ['logcat', '-c']);
|
|
147
|
+
console.log('Ring buffer cleared.');
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error('Failed to clear ring buffer:', (error as Error).message);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (filter) {
|
|
154
|
+
console.log(`Filter: ${filter}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Start background logcat process
|
|
158
|
+
const args = ['logcat', '-v', 'threadtime'];
|
|
159
|
+
if (filter) {
|
|
160
|
+
args.push(filter);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Open file for writing
|
|
164
|
+
const fd = openSync(logFile, 'w');
|
|
165
|
+
|
|
166
|
+
const proc = spawn('adb', args, {
|
|
167
|
+
stdio: ['ignore', fd, fd],
|
|
168
|
+
detached: true
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Unref so parent can exit immediately
|
|
172
|
+
proc.unref();
|
|
173
|
+
|
|
174
|
+
// Save PID
|
|
175
|
+
writePidFile(proc.pid!);
|
|
176
|
+
|
|
177
|
+
// Update symlink
|
|
178
|
+
try {
|
|
179
|
+
if (existsSync(LOGFILE_LINK)) {
|
|
180
|
+
unlinkSync(LOGFILE_LINK);
|
|
181
|
+
}
|
|
182
|
+
symlinkSync(`logcat_${timestamp}.txt`, LOGFILE_LINK);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.warn('Warning: Failed to create symlink:', (error as Error).message);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
console.log(`Capturing (PID: ${proc.pid})`);
|
|
188
|
+
console.log('');
|
|
189
|
+
console.log('Now run your test. When done: quest-dev logcat stop');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Stop logcat capture
|
|
194
|
+
*/
|
|
195
|
+
export async function stopCommand(): Promise<void> {
|
|
196
|
+
const pid = readPidFile();
|
|
197
|
+
|
|
198
|
+
if (!pid) {
|
|
199
|
+
console.log('No capture in progress');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (isProcessRunning(pid)) {
|
|
204
|
+
try {
|
|
205
|
+
process.kill(pid, 'SIGTERM');
|
|
206
|
+
console.log(`Capture stopped (PID: ${pid})`);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.log(`Capture process already ended (PID: ${pid})`);
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
console.log('Capture process already ended');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
deletePidFile();
|
|
215
|
+
|
|
216
|
+
// Show file info
|
|
217
|
+
const latestFile = getLatestLogFile();
|
|
218
|
+
if (latestFile) {
|
|
219
|
+
const stats = getFileStats(latestFile);
|
|
220
|
+
if (stats) {
|
|
221
|
+
console.log('');
|
|
222
|
+
console.log(`Log file: ${latestFile}`);
|
|
223
|
+
console.log(`Size: ${stats.size} (${stats.lines} lines)`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Show capture status
|
|
230
|
+
*/
|
|
231
|
+
export async function statusCommand(): Promise<void> {
|
|
232
|
+
const pid = readPidFile();
|
|
233
|
+
|
|
234
|
+
if (pid && isProcessRunning(pid)) {
|
|
235
|
+
console.log(`Capturing (PID: ${pid})`);
|
|
236
|
+
|
|
237
|
+
const latestFile = getLatestLogFile();
|
|
238
|
+
if (latestFile) {
|
|
239
|
+
const stats = getFileStats(latestFile);
|
|
240
|
+
if (stats) {
|
|
241
|
+
console.log(`File: ${latestFile}`);
|
|
242
|
+
console.log(`Size: ${stats.size} (${stats.lines} lines)`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
console.log('Not capturing');
|
|
247
|
+
|
|
248
|
+
// Show recent logs
|
|
249
|
+
if (existsSync(LOG_DIR)) {
|
|
250
|
+
console.log('');
|
|
251
|
+
console.log('Recent logs:');
|
|
252
|
+
try {
|
|
253
|
+
const result = await execCommandFull('ls', ['-lht', join(LOG_DIR, '*.txt')]);
|
|
254
|
+
if (result.code === 0) {
|
|
255
|
+
const lines = result.stdout.trim().split('\n').slice(0, 5);
|
|
256
|
+
lines.forEach(line => console.log(' ' + line));
|
|
257
|
+
}
|
|
258
|
+
} catch {
|
|
259
|
+
// Ignore
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Tail current capture
|
|
267
|
+
*/
|
|
268
|
+
export async function tailCommand(): Promise<void> {
|
|
269
|
+
const latestFile = getLatestLogFile();
|
|
270
|
+
|
|
271
|
+
if (!latestFile) {
|
|
272
|
+
console.error('No active log file');
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log(`Tailing: ${latestFile}`);
|
|
277
|
+
console.log('Press Ctrl+C to stop\n');
|
|
278
|
+
|
|
279
|
+
// Use tail -f
|
|
280
|
+
const tailProc = spawn('tail', ['-f', latestFile], {
|
|
281
|
+
stdio: 'inherit'
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
tailProc.on('error', (error) => {
|
|
285
|
+
console.error('Failed to tail log:', error.message);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
});
|
|
288
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { fileURLToPath } from 'url';
|
|
|
12
12
|
import { dirname, join } from 'path';
|
|
13
13
|
import { screenshotCommand } from './commands/screenshot.js';
|
|
14
14
|
import { openCommand } from './commands/open.js';
|
|
15
|
+
import { startCommand, stopCommand, statusCommand, tailCommand } from './commands/logcat.js';
|
|
15
16
|
|
|
16
17
|
// Read version from package.json
|
|
17
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -79,5 +80,46 @@ cli.command(
|
|
|
79
80
|
}
|
|
80
81
|
);
|
|
81
82
|
|
|
83
|
+
// Logcat command
|
|
84
|
+
cli.command(
|
|
85
|
+
'logcat <action>',
|
|
86
|
+
'Capture Android logcat to files (CRITICAL: always start before testing to avoid losing crash logs)',
|
|
87
|
+
(yargs) => {
|
|
88
|
+
return yargs
|
|
89
|
+
.positional('action', {
|
|
90
|
+
describe: 'Action to perform',
|
|
91
|
+
type: 'string',
|
|
92
|
+
choices: ['start', 'stop', 'status', 'tail'],
|
|
93
|
+
demandOption: true
|
|
94
|
+
})
|
|
95
|
+
.option('filter', {
|
|
96
|
+
describe: 'Logcat filter expression (e.g., "*:W" for warnings+, "chromium:V *:S" for chromium only)',
|
|
97
|
+
type: 'string'
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
async (argv) => {
|
|
101
|
+
const action = argv.action as string;
|
|
102
|
+
const filter = argv.filter as string | undefined;
|
|
103
|
+
|
|
104
|
+
switch (action) {
|
|
105
|
+
case 'start':
|
|
106
|
+
await startCommand(filter);
|
|
107
|
+
break;
|
|
108
|
+
case 'stop':
|
|
109
|
+
await stopCommand();
|
|
110
|
+
break;
|
|
111
|
+
case 'status':
|
|
112
|
+
await statusCommand();
|
|
113
|
+
break;
|
|
114
|
+
case 'tail':
|
|
115
|
+
await tailCommand();
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
console.error(`Unknown action: ${action}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
|
|
82
124
|
// Parse and execute
|
|
83
125
|
cli.parse();
|
package/src/utils/adb.ts
CHANGED
|
@@ -14,7 +14,6 @@ const CDP_PORT = 9223; // Chrome DevTools Protocol port (Quest browser default)
|
|
|
14
14
|
export function checkADBPath(): string {
|
|
15
15
|
try {
|
|
16
16
|
const adbPath = which.sync('adb');
|
|
17
|
-
console.log(`Found ADB at: ${adbPath}`);
|
|
18
17
|
return adbPath;
|
|
19
18
|
} catch (error) {
|
|
20
19
|
console.error('Error: ADB not found in PATH');
|