36 lines
1.6 KiB
JavaScript
36 lines
1.6 KiB
JavaScript
// Detached Windows lifetime owner: no services, registry changes or test profile.
|
|
import { spawn } from 'node:child_process';
|
|
import { writeFileSync } from 'node:fs';
|
|
import { redact } from './core.mjs';
|
|
const [repoRoot, resultFile] = process.argv.slice(2);
|
|
if (!repoRoot || !resultFile || process.platform !== 'win32') process.exit(2);
|
|
const child = spawn(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', 'npm run dev'], {
|
|
cwd: repoRoot, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'],
|
|
env: { ...process.env, YCODE_SKIP_KERNEL_DATA_RESET: '1' }
|
|
});
|
|
// Line buffering avoids leaking credentials split across stream chunks. Oversized
|
|
// lines are omitted, rather than persisting partial unredacted secrets.
|
|
for (const stream of [child.stdout, child.stderr]) {
|
|
stream.setEncoding('utf8');
|
|
let pending = '', dropping = false;
|
|
stream.on('data', chunk => {
|
|
for (const part of chunk.match(/[^\n]*\n|[^\n]+$/g) || []) {
|
|
if (!dropping) pending += part;
|
|
if (pending.length > 65536) { pending = ''; dropping = true; }
|
|
if (part.endsWith('\n')) {
|
|
process.stdout.write(dropping ? '[oversized log line omitted]\n' : redact(pending));
|
|
pending = ''; dropping = false;
|
|
}
|
|
}
|
|
});
|
|
stream.on('end', () => { if (pending && !dropping) process.stdout.write(redact(pending)); });
|
|
}
|
|
child.on('error', error => {
|
|
writeFileSync(resultFile, JSON.stringify({ code: 'SPAWN_FAILED', message: redact(error.message) }));
|
|
process.exitCode = 1;
|
|
});
|
|
child.on('exit', (code, signal) => {
|
|
writeFileSync(resultFile, JSON.stringify({ code: 'DEV_EXITED', exitCode: code, signal }));
|
|
process.exitCode = code ?? 1;
|
|
});
|