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>
78 lines
2.7 KiB
JavaScript
Executable File
78 lines
2.7 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
const chalk = require('chalk');
|
|
const debug = require('debug')('wait-port');
|
|
const program = require('commander');
|
|
const pkg = require('../package.json');
|
|
const extractTarget = require('../lib/extract-target');
|
|
const ConnectionError = require('../lib/errors/connection-error');
|
|
const TargetError = require('../lib/errors/target-error');
|
|
const ValidationError = require('../lib/errors/validation-error');
|
|
const waitPort = require('../lib/wait-port');
|
|
|
|
program
|
|
.version(pkg.version)
|
|
.description('Wait for a target to accept connections, e.g: wait-port localhost:8080')
|
|
.option('-t, --timeout [n]', 'Timeout', parseInt)
|
|
.option('-o, --output [mode]', 'Output mode (silent, dots). Default is silent.')
|
|
.option('--wait-for-dns', 'Do not fail on ENOTFOUND, meaning you can wait for DNS record creation. Default is false.')
|
|
.arguments('<target>')
|
|
.action(async (target) => {
|
|
try {
|
|
const options = program.opts();
|
|
const { protocol, host, port, path } = extractTarget(target);
|
|
const timeout = options.timeout || 0;
|
|
const output = options.output;
|
|
const waitForDns = options.waitForDns;
|
|
|
|
debug(`Timeout: ${timeout}`);
|
|
debug(`Target: ${target} => ${protocol}://${host}:${port}${path}`);
|
|
debug(`waitForDns: ${waitForDns}`);
|
|
|
|
const params = {
|
|
timeout,
|
|
protocol,
|
|
host,
|
|
port,
|
|
path,
|
|
output,
|
|
waitForDns,
|
|
};
|
|
|
|
const { open } = await waitPort(params);
|
|
process.exit(open ? 0 : 1);
|
|
} catch (err) {
|
|
// Show validation errors in red.
|
|
if (err instanceof ValidationError) {
|
|
console.error(chalk.red(`\n Validation Error: ${err.message}`));
|
|
process.exit(2);
|
|
} else if (err instanceof ConnectionError) {
|
|
console.error(chalk.red(`\n\n Connection Error Error: ${err.message}`));
|
|
process.exit(4);
|
|
} else if (err instanceof TargetError) {
|
|
console.error(chalk.red(`\n Target Error: ${err.message}`));
|
|
process.exit(4);
|
|
} else {
|
|
console.error(chalk.red(`\n Unknown error occurred waiting for ${target}: ${err}`));
|
|
process.exit(3);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Enrich the help.
|
|
program.on('--help', () => {
|
|
console.log(' Examples:');
|
|
console.log('');
|
|
console.log(' $ wait-port 3000');
|
|
console.log(' $ wait-port -t 10 :8080');
|
|
console.log(' $ wait-port google.com:443');
|
|
console.log(' $ wait-port http://localhost:5000/healthcheck');
|
|
console.log(' $ wait-port --wait-for-dns http://mynewdomain.com:80');
|
|
console.log('');
|
|
});
|
|
|
|
// Parse the arguments. Spaff an error message if none were provided.
|
|
program.parse(process.argv);
|
|
if (program.args.length === 0) {
|
|
program.help();
|
|
}
|