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>
189 lines
3.7 KiB
C
189 lines
3.7 KiB
C
#include <assert.h>
|
|
#include <bare.h>
|
|
#include <js.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <url.h>
|
|
#include <utf.h>
|
|
#include <utf/string.h>
|
|
|
|
static js_type_tag_t bare_url__tag = {0x6f1fd92f476698b0, 0x93b59c349143ee50};
|
|
|
|
static js_value_t *
|
|
bare_url_parse (js_env_t *env, js_callback_info_t *info) {
|
|
int err;
|
|
|
|
size_t argc = 4;
|
|
js_value_t *argv[4];
|
|
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
assert(err == 0);
|
|
|
|
assert(argc == 4);
|
|
|
|
bool should_throw;
|
|
err = js_get_value_bool(env, argv[3], &should_throw);
|
|
assert(err == 0);
|
|
|
|
bool has_base;
|
|
err = js_is_string(env, argv[1], &has_base);
|
|
assert(err == 0);
|
|
|
|
url_t base;
|
|
url_init(&base);
|
|
|
|
if (has_base) {
|
|
size_t len;
|
|
err = js_get_value_string_utf8(env, argv[1], NULL, 0, &len);
|
|
assert(err == 0);
|
|
|
|
utf8_t *input = malloc(len);
|
|
err = js_get_value_string_utf8(env, argv[1], input, len, NULL);
|
|
assert(err == 0);
|
|
|
|
err = url_parse(&base, input, len, NULL);
|
|
|
|
free(input);
|
|
|
|
if (err < 0) {
|
|
url_destroy(&base);
|
|
|
|
if (should_throw) js_throw_error(env, NULL, "Invalid base URL");
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
size_t len;
|
|
err = js_get_value_string_utf8(env, argv[0], NULL, 0, &len);
|
|
assert(err == 0);
|
|
|
|
utf8_t *input = malloc(len);
|
|
err = js_get_value_string_utf8(env, argv[0], input, len, NULL);
|
|
assert(err == 0);
|
|
|
|
js_value_t *handle;
|
|
|
|
url_t url;
|
|
url_init(&url);
|
|
|
|
err = url_parse(&url, input, len, has_base ? &base : NULL);
|
|
|
|
free(input);
|
|
|
|
if (err < 0) {
|
|
url_destroy(&base);
|
|
url_destroy(&url);
|
|
|
|
if (should_throw) js_throw_error(env, NULL, "Invalid URL");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
js_value_t *href;
|
|
err = js_create_string_utf8(env, url.href.data, url.href.len, &href);
|
|
assert(err == 0);
|
|
|
|
uint32_t *components;
|
|
err = js_get_typedarray_info(env, argv[2], NULL, (void **) &components, NULL, NULL, NULL);
|
|
assert(err == 0);
|
|
|
|
memcpy(components, &url.components, sizeof(url.components));
|
|
|
|
url_destroy(&base);
|
|
url_destroy(&url);
|
|
|
|
return href;
|
|
}
|
|
|
|
static js_value_t *
|
|
bare_url_can_parse (js_env_t *env, js_callback_info_t *info) {
|
|
int err;
|
|
|
|
size_t argc = 2;
|
|
js_value_t *argv[2];
|
|
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
assert(err == 0);
|
|
|
|
assert(argc == 2);
|
|
|
|
bool has_base;
|
|
err = js_is_string(env, argv[1], &has_base);
|
|
assert(err == 0);
|
|
|
|
url_t base;
|
|
url_init(&base);
|
|
|
|
if (has_base) {
|
|
size_t len;
|
|
err = js_get_value_string_utf8(env, argv[1], NULL, 0, &len);
|
|
assert(err == 0);
|
|
|
|
utf8_t *input = malloc(len);
|
|
err = js_get_value_string_utf8(env, argv[1], input, len, NULL);
|
|
assert(err == 0);
|
|
|
|
err = url_parse(&base, input, len, NULL);
|
|
|
|
free(input);
|
|
|
|
if (err < 0) {
|
|
url_destroy(&base);
|
|
|
|
js_value_t *result;
|
|
err = js_get_boolean(env, false, &result);
|
|
assert(err == 0);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
size_t len;
|
|
err = js_get_value_string_utf8(env, argv[0], NULL, 0, &len);
|
|
assert(err == 0);
|
|
|
|
utf8_t *input = malloc(len);
|
|
err = js_get_value_string_utf8(env, argv[0], input, len, NULL);
|
|
assert(err == 0);
|
|
|
|
url_t url;
|
|
url_init(&url);
|
|
|
|
err = url_parse(&url, input, len, has_base ? &base : NULL);
|
|
|
|
free(input);
|
|
|
|
url_destroy(&base);
|
|
url_destroy(&url);
|
|
|
|
js_value_t *result;
|
|
err = js_get_boolean(env, err == 0, &result);
|
|
assert(err == 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
static js_value_t *
|
|
bare_url_exports (js_env_t *env, js_value_t *exports) {
|
|
int err;
|
|
|
|
#define V(name, fn) \
|
|
{ \
|
|
js_value_t *val; \
|
|
err = js_create_function(env, name, -1, fn, NULL, &val); \
|
|
assert(err == 0); \
|
|
err = js_set_named_property(env, exports, name, val); \
|
|
assert(err == 0); \
|
|
}
|
|
|
|
V("parse", bare_url_parse)
|
|
V("canParse", bare_url_can_parse)
|
|
#undef V
|
|
|
|
return exports;
|
|
}
|
|
|
|
BARE_MODULE(bare_url, bare_url_exports)
|