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>
69 lines
1.8 KiB
JavaScript
69 lines
1.8 KiB
JavaScript
import { parseRegexp } from '@vitest/utils';
|
|
|
|
const REGEXP_WRAP_PREFIX = "$$vitest:";
|
|
const processSend = process.send?.bind(process);
|
|
const processOn = process.on?.bind(process);
|
|
const processOff = process.off?.bind(process);
|
|
const dispose = [];
|
|
function createThreadsRpcOptions({
|
|
port
|
|
}) {
|
|
return {
|
|
post: (v) => {
|
|
port.postMessage(v);
|
|
},
|
|
on: (fn) => {
|
|
port.addListener("message", fn);
|
|
}
|
|
};
|
|
}
|
|
function disposeInternalListeners() {
|
|
for (const fn of dispose) {
|
|
try {
|
|
fn();
|
|
} catch {
|
|
}
|
|
}
|
|
dispose.length = 0;
|
|
}
|
|
function createForksRpcOptions(nodeV8) {
|
|
return {
|
|
serialize: nodeV8.serialize,
|
|
deserialize: (v) => nodeV8.deserialize(Buffer.from(v)),
|
|
post(v) {
|
|
processSend(v);
|
|
},
|
|
on(fn) {
|
|
const handler = (message, ...extras) => {
|
|
if (message?.__tinypool_worker_message__) {
|
|
return;
|
|
}
|
|
return fn(message, ...extras);
|
|
};
|
|
processOn("message", handler);
|
|
dispose.push(() => processOff("message", handler));
|
|
}
|
|
};
|
|
}
|
|
function unwrapSerializableConfig(config) {
|
|
if (config.testNamePattern && typeof config.testNamePattern === "string") {
|
|
const testNamePattern = config.testNamePattern;
|
|
if (testNamePattern.startsWith(REGEXP_WRAP_PREFIX)) {
|
|
config.testNamePattern = parseRegexp(
|
|
testNamePattern.slice(REGEXP_WRAP_PREFIX.length)
|
|
);
|
|
}
|
|
}
|
|
if (config.defines && Array.isArray(config.defines.keys) && config.defines.original) {
|
|
const { keys, original } = config.defines;
|
|
const defines = {};
|
|
for (const key of keys) {
|
|
defines[key] = original[key];
|
|
}
|
|
config.defines = defines;
|
|
}
|
|
return config;
|
|
}
|
|
|
|
export { createThreadsRpcOptions as a, createForksRpcOptions as c, disposeInternalListeners as d, unwrapSerializableConfig as u };
|