@myerscarpenter/quest-dev 1.0.7 → 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/README.md +1 -1
- 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/commands/open.d.ts +1 -1
- package/build/commands/open.d.ts.map +1 -1
- package/build/commands/open.js +53 -1
- package/build/commands/open.js.map +1 -1
- package/build/index.js +43 -2
- 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/commands/open.ts +58 -1
- package/src/index.ts +54 -6
- package/src/utils/adb.ts +0 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ Port forwarding is idempotent - safe to run multiple times without issues.
|
|
|
57
57
|
|
|
58
58
|
## How It Works
|
|
59
59
|
|
|
60
|
-
- **screenshot**: Triggers `com.oculus.metacam/.capture.CaptureService` via ADB, waits for the
|
|
60
|
+
- **screenshot**: Triggers `com.oculus.metacam/.capture.CaptureService` via ADB, waits for the JPEG to be fully written (by checking for the EOI marker), pulls the file, then deletes it from the Quest
|
|
61
61
|
|
|
62
62
|
- **open**: Uses ADB for port forwarding and browser launching. If `cdp-cli` is installed, it uses CDP to intelligently reuse existing tabs instead of opening new ones.
|
|
63
63
|
|
|
@@ -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/commands/open.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/commands/open.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/commands/open.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0IH;;GAEG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAkE1F"}
|
package/build/commands/open.js
CHANGED
|
@@ -4,6 +4,51 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { checkADBPath, checkADBDevices, ensurePortForwarding, ensureCDPForwarding, isBrowserRunning, launchBrowser, getCDPPort } from '../utils/adb.js';
|
|
6
6
|
import { execCommandFull } from '../utils/exec.js';
|
|
7
|
+
/**
|
|
8
|
+
* Close all tabs except the one with the target URL
|
|
9
|
+
*/
|
|
10
|
+
async function closeOtherTabs(targetUrl) {
|
|
11
|
+
const cdpPort = getCDPPort();
|
|
12
|
+
try {
|
|
13
|
+
// Get list of tabs
|
|
14
|
+
const result = await execCommandFull('cdp-cli', ['--cdp-url', `http://localhost:${cdpPort}`, 'tabs']);
|
|
15
|
+
if (result.code !== 0) {
|
|
16
|
+
console.log('Could not list tabs to close others');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Parse tabs
|
|
20
|
+
const lines = result.stdout.trim().split('\n').filter(line => line.trim());
|
|
21
|
+
const tabs = [];
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
try {
|
|
24
|
+
const tab = JSON.parse(line);
|
|
25
|
+
if (tab.id && tab.url !== undefined) {
|
|
26
|
+
tabs.push(tab);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Skip non-JSON lines
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Normalize URLs for comparison (handle trailing slash differences)
|
|
34
|
+
const normalizeUrl = (url) => url.replace(/\/$/, '');
|
|
35
|
+
const normalizedTarget = normalizeUrl(targetUrl);
|
|
36
|
+
// Close all tabs that don't have the target URL, keeping the first match
|
|
37
|
+
let keptOne = false;
|
|
38
|
+
for (const tab of tabs) {
|
|
39
|
+
const isTargetTab = normalizeUrl(tab.url) === normalizedTarget;
|
|
40
|
+
if (isTargetTab && !keptOne) {
|
|
41
|
+
keptOne = true;
|
|
42
|
+
continue; // Keep this tab
|
|
43
|
+
}
|
|
44
|
+
console.log(`Closing tab: ${tab.url || '(blank)'}`);
|
|
45
|
+
await execCommandFull('cdp-cli', ['--cdp-url', `http://localhost:${cdpPort}`, 'close', tab.id]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.log('Failed to close other tabs:', error.message);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
7
52
|
/**
|
|
8
53
|
* Try to navigate or reload existing tab via cdp-cli
|
|
9
54
|
*/
|
|
@@ -70,7 +115,7 @@ async function tryNavigateExistingTab(targetUrl) {
|
|
|
70
115
|
/**
|
|
71
116
|
* Main open command handler
|
|
72
117
|
*/
|
|
73
|
-
export async function openCommand(url) {
|
|
118
|
+
export async function openCommand(url, closeOthers = false) {
|
|
74
119
|
// Parse URL to determine if we need reverse port forwarding
|
|
75
120
|
let parsedUrl;
|
|
76
121
|
try {
|
|
@@ -121,6 +166,13 @@ export async function openCommand(url) {
|
|
|
121
166
|
await launchBrowser(url);
|
|
122
167
|
}
|
|
123
168
|
}
|
|
169
|
+
// Close other tabs if requested
|
|
170
|
+
if (closeOthers) {
|
|
171
|
+
// Wait for browser to stabilize after launch/navigation
|
|
172
|
+
console.log('Waiting for browser to stabilize...');
|
|
173
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
174
|
+
await closeOtherTabs(url);
|
|
175
|
+
}
|
|
124
176
|
console.log('\nDone!\n');
|
|
125
177
|
}
|
|
126
178
|
//# sourceMappingURL=open.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/commands/open.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/commands/open.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'));
|
|
@@ -46,13 +47,53 @@ cli.command('screenshot <output>', 'Take a screenshot from Quest and save to loc
|
|
|
46
47
|
});
|
|
47
48
|
// Open command
|
|
48
49
|
cli.command('open <url>', 'Open URL in Quest browser (sets up CDP debugging port forwarding)', (yargs) => {
|
|
49
|
-
return yargs
|
|
50
|
+
return yargs
|
|
51
|
+
.positional('url', {
|
|
50
52
|
describe: 'URL to open (localhost URLs get reverse forwarding for dev server access)',
|
|
51
53
|
type: 'string',
|
|
52
54
|
demandOption: true
|
|
55
|
+
})
|
|
56
|
+
.option('close-others', {
|
|
57
|
+
describe: 'Close all other tabs before opening',
|
|
58
|
+
type: 'boolean',
|
|
59
|
+
default: false
|
|
53
60
|
});
|
|
54
61
|
}, async (argv) => {
|
|
55
|
-
await openCommand(argv.url);
|
|
62
|
+
await openCommand(argv.url, argv.closeOthers);
|
|
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
|
+
}
|
|
56
97
|
});
|
|
57
98
|
// Parse and execute
|
|
58
99
|
cli.parse();
|
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/commands/open.ts
CHANGED
|
@@ -14,6 +14,55 @@ import {
|
|
|
14
14
|
} from '../utils/adb.js';
|
|
15
15
|
import { execCommand, execCommandFull } from '../utils/exec.js';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Close all tabs except the one with the target URL
|
|
19
|
+
*/
|
|
20
|
+
async function closeOtherTabs(targetUrl: string): Promise<void> {
|
|
21
|
+
const cdpPort = getCDPPort();
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// Get list of tabs
|
|
25
|
+
const result = await execCommandFull('cdp-cli', ['--cdp-url', `http://localhost:${cdpPort}`, 'tabs']);
|
|
26
|
+
if (result.code !== 0) {
|
|
27
|
+
console.log('Could not list tabs to close others');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Parse tabs
|
|
32
|
+
const lines = result.stdout.trim().split('\n').filter(line => line.trim());
|
|
33
|
+
const tabs: Array<{ id: string; url: string }> = [];
|
|
34
|
+
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
try {
|
|
37
|
+
const tab = JSON.parse(line);
|
|
38
|
+
if (tab.id && tab.url !== undefined) {
|
|
39
|
+
tabs.push(tab);
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
// Skip non-JSON lines
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Normalize URLs for comparison (handle trailing slash differences)
|
|
47
|
+
const normalizeUrl = (url: string) => url.replace(/\/$/, '');
|
|
48
|
+
const normalizedTarget = normalizeUrl(targetUrl);
|
|
49
|
+
|
|
50
|
+
// Close all tabs that don't have the target URL, keeping the first match
|
|
51
|
+
let keptOne = false;
|
|
52
|
+
for (const tab of tabs) {
|
|
53
|
+
const isTargetTab = normalizeUrl(tab.url) === normalizedTarget;
|
|
54
|
+
if (isTargetTab && !keptOne) {
|
|
55
|
+
keptOne = true;
|
|
56
|
+
continue; // Keep this tab
|
|
57
|
+
}
|
|
58
|
+
console.log(`Closing tab: ${tab.url || '(blank)'}`);
|
|
59
|
+
await execCommandFull('cdp-cli', ['--cdp-url', `http://localhost:${cdpPort}`, 'close', tab.id]);
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.log('Failed to close other tabs:', (error as Error).message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
17
66
|
/**
|
|
18
67
|
* Try to navigate or reload existing tab via cdp-cli
|
|
19
68
|
*/
|
|
@@ -93,7 +142,7 @@ async function tryNavigateExistingTab(targetUrl: string): Promise<boolean> {
|
|
|
93
142
|
/**
|
|
94
143
|
* Main open command handler
|
|
95
144
|
*/
|
|
96
|
-
export async function openCommand(url: string): Promise<void> {
|
|
145
|
+
export async function openCommand(url: string, closeOthers: boolean = false): Promise<void> {
|
|
97
146
|
// Parse URL to determine if we need reverse port forwarding
|
|
98
147
|
let parsedUrl: URL;
|
|
99
148
|
try {
|
|
@@ -150,5 +199,13 @@ export async function openCommand(url: string): Promise<void> {
|
|
|
150
199
|
}
|
|
151
200
|
}
|
|
152
201
|
|
|
202
|
+
// Close other tabs if requested
|
|
203
|
+
if (closeOthers) {
|
|
204
|
+
// Wait for browser to stabilize after launch/navigation
|
|
205
|
+
console.log('Waiting for browser to stabilize...');
|
|
206
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
207
|
+
await closeOtherTabs(url);
|
|
208
|
+
}
|
|
209
|
+
|
|
153
210
|
console.log('\nDone!\n');
|
|
154
211
|
}
|
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));
|
|
@@ -62,14 +63,61 @@ cli.command(
|
|
|
62
63
|
'open <url>',
|
|
63
64
|
'Open URL in Quest browser (sets up CDP debugging port forwarding)',
|
|
64
65
|
(yargs) => {
|
|
65
|
-
return yargs
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
return yargs
|
|
67
|
+
.positional('url', {
|
|
68
|
+
describe: 'URL to open (localhost URLs get reverse forwarding for dev server access)',
|
|
69
|
+
type: 'string',
|
|
70
|
+
demandOption: true
|
|
71
|
+
})
|
|
72
|
+
.option('close-others', {
|
|
73
|
+
describe: 'Close all other tabs before opening',
|
|
74
|
+
type: 'boolean',
|
|
75
|
+
default: false
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
async (argv) => {
|
|
79
|
+
await openCommand(argv.url as string, argv.closeOthers as boolean);
|
|
80
|
+
}
|
|
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
|
+
});
|
|
70
99
|
},
|
|
71
100
|
async (argv) => {
|
|
72
|
-
|
|
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
|
+
}
|
|
73
121
|
}
|
|
74
122
|
);
|
|
75
123
|
|
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');
|