tftsr-devops_investigation/node_modules/execa/lib/verbose/output.js
Shaun Arman 8839075805 feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.

Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)

Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)

DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload

Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:36:25 -05:00

61 lines
2.7 KiB
JavaScript

import {BINARY_ENCODINGS} from '../arguments/encoding-option.js';
import {TRANSFORM_TYPES} from '../stdio/type.js';
import {verboseLog, serializeVerboseMessage} from './log.js';
import {isFullVerbose} from './values.js';
// `ignore` opts-out of `verbose` for a specific stream.
// `ipc` cannot use piping.
// `inherit` would result in double printing.
// They can also lead to double printing when passing file descriptor integers or `process.std*`.
// This only leaves with `pipe` and `overlapped`.
export const shouldLogOutput = ({stdioItems, encoding, verboseInfo, fdNumber}) => fdNumber !== 'all'
&& isFullVerbose(verboseInfo, fdNumber)
&& !BINARY_ENCODINGS.has(encoding)
&& fdUsesVerbose(fdNumber)
&& (stdioItems.some(({type, value}) => type === 'native' && PIPED_STDIO_VALUES.has(value))
|| stdioItems.every(({type}) => TRANSFORM_TYPES.has(type)));
// Printing input streams would be confusing.
// Files and streams can produce big outputs, which we don't want to print.
// We could print `stdio[3+]` but it often is redirected to files and streams, with the same issue.
// So we only print stdout and stderr.
const fdUsesVerbose = fdNumber => fdNumber === 1 || fdNumber === 2;
const PIPED_STDIO_VALUES = new Set(['pipe', 'overlapped']);
// `verbose: 'full'` printing logic with async methods
export const logLines = async (linesIterable, stream, fdNumber, verboseInfo) => {
for await (const line of linesIterable) {
if (!isPipingStream(stream)) {
logLine(line, fdNumber, verboseInfo);
}
}
};
// `verbose: 'full'` printing logic with sync methods
export const logLinesSync = (linesArray, fdNumber, verboseInfo) => {
for (const line of linesArray) {
logLine(line, fdNumber, verboseInfo);
}
};
// When `subprocess.stdout|stderr.pipe()` is called, `verbose` becomes a noop.
// This prevents the following problems:
// - `.pipe()` achieves the same result as using `stdout: 'inherit'`, `stdout: stream`, etc. which also make `verbose` a noop.
// For example, `subprocess.stdout.pipe(process.stdin)` would print each line twice.
// - When chaining subprocesses with `subprocess.pipe(otherSubprocess)`, only the last one should print its output.
// Detecting whether `.pipe()` is impossible without monkey-patching it, so we use the following undocumented property.
// This is not a critical behavior since changes of the following property would only make `verbose` more verbose.
const isPipingStream = stream => stream._readableState.pipes.length > 0;
// When `verbose` is `full`, print stdout|stderr
const logLine = (line, fdNumber, verboseInfo) => {
const verboseMessage = serializeVerboseMessage(line);
verboseLog({
type: 'output',
verboseMessage,
fdNumber,
verboseInfo,
});
};