@h2loop/uav 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/USAGE.md CHANGED
@@ -15,8 +15,9 @@ covers getting it running on **macOS, Linux, and Windows**, validating it works,
15
15
  `mcp[cli]>=1.0.0`). The pack does **not** auto-provision a venv; you install the deps
16
16
  yourself (see §6). Plain `.bin` log analysis needs no Python.
17
17
  - *Optional:* `export EXA_API_KEY=…` for web grounding of bounds.
18
- - macOS/Linux are native; on **Windows** use **WSL2** for the bridge's Python (the bridge is
19
- POSIX-oriented).
18
+ - **macOS, Linux, and Windows are all native** — no WSL required. The pack resolves the Python
19
+ interpreter per-platform (`python3` / `python` / the `py -3` launcher); override it with
20
+ `UAV_PYTHON=<full path to python>` if you keep one in a venv.
20
21
 
21
22
  ---
22
23
 
@@ -41,14 +42,27 @@ hydron plugin add /path/to/uav-pack # add --project to scope it to
41
42
  Native. Nothing extra needed to use a published pack. Python ≥ 3.10 is only required for the
42
43
  optional MAVLink bridge (§6).
43
44
 
44
- ### Windows — use WSL2 for the bridge
45
- The `hydron plugin add` command itself is cross-platform. The MAVLink bridge, however, is
46
- POSIX-oriented Python, so run it (and ideally Hydron) inside **WSL2** (Ubuntu):
45
+ ### Windows
46
+ Native — run the same `hydron plugin add` in PowerShell. `.bin` log analysis, the checks, param
47
+ audit, tuning, plots and compare need no Python at all.
48
+
49
+ The Python-backed features (`uav_python`, `.tlog`/`.ulog` decode, the MAVLink bridge) need a
50
+ real Python ≥ 3.10 — install it from **python.org** and tick **“Add python.exe to PATH”**. The
51
+ pack finds it via the `py -3` launcher or `python`; there is no `python3` on Windows.
52
+
53
+ > Windows pre-installs *stub* `python.exe` / `python3.exe` aliases that only open the Microsoft
54
+ > Store. The pack ignores them (it probes by running code, not by looking on PATH), but if
55
+ > `python --version` opens the Store rather than printing a version, turn the stubs off under
56
+ > **Settings → Apps → Advanced app settings → App execution aliases**.
57
+
58
+ Pinning a specific interpreter (e.g. a venv) works everywhere:
47
59
  ```powershell
48
- wsl --install -d Ubuntu # PowerShell (admin), one-time; then open the Ubuntu shell
60
+ $env:UAV_PYTHON = "C:\path\to\.venv\Scripts\python.exe"
49
61
  ```
50
- Use the **VS Code "WSL" remote** (or Hydron's CLI inside WSL) so Hydron and the bridge share
51
- the same environment.
62
+
63
+ WSL2 also works and stays fully supported — but if you use it, install the pack **and** run
64
+ Hydron inside WSL (VS Code's **WSL** remote). A Windows-side Hydron cannot see a WSL-side
65
+ install; that mismatch is the usual cause of "the UAV tools didn't appear".
52
66
 
53
67
  ### Uninstall (any OS)
54
68
  ```bash
@@ -121,7 +135,10 @@ The pack does **not** auto-provision a venv. The bridge needs Python ≥ 3.10 wi
121
135
  ```bash
122
136
  pip install -r bridge/requirements.txt # ideally into a venv
123
137
  ```
124
- Then point the `mavlink` command's `python3` at that interpreter.
138
+ Then point the `mavlink` command's first element at that interpreter. **This is the one place
139
+ the interpreter is not auto-detected** — `mcp.json` is static config that Hydron spawns
140
+ directly, so it ships the POSIX default `python3`. On Windows change it to `python` (or the
141
+ venv's full `…\Scripts\python.exe`); everywhere else an absolute venv path is the safest value.
125
142
 
126
143
  ### Enable and configure (per-project in opencode.jsonc)
127
144
  The `mavlink` server ships in `mcp.json` **disabled by default**. Enable it per-project:
@@ -135,8 +152,9 @@ The `mavlink` server ships in `mcp.json` **disabled by default**. Enable it per-
135
152
  }
136
153
  ```
137
154
  Connect-target forms: `udp:HOST:PORT` (default `udp:127.0.0.1:14550` = SITL/MAVProxy) |
138
- `tcp:HOST:PORT` | `serial:/dev/ttyUSB0:BAUD` | `/path/log.tlog` (offline replay). Can also be
139
- set via the `MAVLINK_CONNECT` env on the server.
155
+ `tcp:HOST:PORT` | `serial:/dev/ttyUSB0:BAUD` (on Windows a COM port, e.g. `COM3` or
156
+ `COM3:57600`) | `/path/log.tlog` (offline replay). Can also be set via the `MAVLINK_CONNECT`
157
+ env on the server.
140
158
 
141
159
  > **"Connected" ≠ a live vehicle.** The panel shows `mavlink Connected` once the bridge *process*
142
160
  > starts. The tools only return data while something is **actually publishing MAVLink** at the
@@ -159,11 +177,17 @@ set via the `MAVLINK_CONNECT` env on the server.
159
177
  ## 7. Troubleshooting
160
178
 
161
179
  - **UAV tools don't appear** → you didn't *fully* restart Hydron (a window reload isn't enough); the
162
- plugin loads once per process. Confirm it's installed with `hydron plugin list`. On Windows make
163
- sure Hydron is running in the **same WSL env** you're using.
164
- - **`mavlink` shows Disabled** → the `python3` in the `mavlink` command lacks `pymavlink`+`mcp`, or
165
- it isn't Python ≥ 3.10. Install the deps (`pip install -r bridge/requirements.txt`, ideally into a
166
- venv) and point the command at that interpreter.
180
+ plugin loads once per process. Confirm it's installed with `hydron plugin list`. If you install
181
+ under WSL, Hydron must also **run** under WSL — a Windows-side Hydron cannot see a WSL-side
182
+ install (check for the green `WSL: Ubuntu` badge in VS Code's bottom-left corner).
183
+ - **`uav_python` / `.ulog` decode → "no working Python 3 interpreter found"** → no runnable Python
184
+ on PATH. On Windows, install from python.org with **“Add python.exe to PATH”** ticked — the
185
+ pre-installed Store *stubs* don't count. Then reopen the terminal, or set `UAV_PYTHON` to the
186
+ interpreter's full path.
187
+ - **`mavlink` shows Disabled** → the interpreter in the `mavlink` command lacks `pymavlink`+`mcp`,
188
+ or it isn't Python ≥ 3.10. Install the deps (`pip install -r bridge/requirements.txt`, ideally
189
+ into a venv) and point the command at that interpreter. Note `mcp.json` ships `python3`, which
190
+ does not exist on Windows — change it to `python` or an absolute path.
167
191
  - **Every mavlink tool → `MCP error -32001: Request timed out`** → the bridge is Connected but **no
168
192
  MAVLink source is live** at the connect target. Start SITL / point `--connect` at a live link.
169
193
  - **`MCP error -32000: Connection closed`** → the bridge crashed at startup: usually the wrong Python
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h2loop/uav",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "UAV domain pack for Hydron — ArduPilot/PX4 flight-log intelligence (analyze, triage, compare, tune)",
5
5
  "type": "module",
6
6
  "private": false,
package/plugins/index.js CHANGED
@@ -1,46 +1,46 @@
1
1
  // @bun
2
- import{readdir as t$,readFile as e$,writeFile as M6,mkdir as S6}from"fs/promises";import{join as g}from"path";import{tool as R}from"@kilocode/plugin";import{readdir as y0,readFile as k0}from"fs/promises";import{join as eZ}from"path";import{open as W0}from"fs/promises";function uZ(){let Z=process.env.UAV_DECODE_MAX_CPU;if(!Z)return;let $=Number(Z);return Number.isFinite($)&&$>0&&$<1?$:void 0}var G0=(Z)=>new Promise(($)=>setTimeout($,Z));async function xZ(Z,$){if(!$||Z<=0)return;let Q=Math.min(Z*(1/$-1),250);if(Q>=1)await G0(Q)}var mZ={a:{kind:"str",width:64},b:{kind:"i8",width:1},B:{kind:"u8",width:1},h:{kind:"i16",width:2},H:{kind:"u16",width:2},i:{kind:"i32",width:4},I:{kind:"u32",width:4},f:{kind:"f32",width:4},d:{kind:"f64",width:8},n:{kind:"str",width:4},N:{kind:"str",width:16},Z:{kind:"str",width:64},c:{kind:"i16",width:2,scale:0.01},C:{kind:"u16",width:2,scale:0.01},e:{kind:"i32",width:4,scale:0.01},E:{kind:"u32",width:4,scale:0.01},L:{kind:"i32",width:4,scale:0.0000001},M:{kind:"u8",width:1},q:{kind:"i64",width:8},Q:{kind:"u64",width:8}};var z0=163,B0=149,V0=128,dZ=86;function U0(Z){return($,Q)=>{let J="",X=Q+Z;for(let Y=Q;Y<X;Y++){let K=$.getUint8(Y);if(K===0)break;J+=String.fromCharCode(K)}return J}}function H0(Z){let $=Z.scale;switch(Z.kind){case"i8":return $===void 0?(Q,J)=>Q.getInt8(J):(Q,J)=>Q.getInt8(J)*$;case"u8":return $===void 0?(Q,J)=>Q.getUint8(J):(Q,J)=>Q.getUint8(J)*$;case"i16":return $===void 0?(Q,J)=>Q.getInt16(J,!0):(Q,J)=>Q.getInt16(J,!0)*$;case"u16":return $===void 0?(Q,J)=>Q.getUint16(J,!0):(Q,J)=>Q.getUint16(J,!0)*$;case"i32":return $===void 0?(Q,J)=>Q.getInt32(J,!0):(Q,J)=>Q.getInt32(J,!0)*$;case"u32":return $===void 0?(Q,J)=>Q.getUint32(J,!0):(Q,J)=>Q.getUint32(J,!0)*$;case"f32":return(Q,J)=>Q.getFloat32(J,!0);case"f64":return(Q,J)=>Q.getFloat64(J,!0);case"i64":return(Q,J)=>Q.getBigInt64(J,!0);case"u64":return(Q,J)=>Q.getBigUint64(J,!0);case"str":return U0(Z.width)}}function L0(Z,$){let Q=[],J=[];for(let Y of Z){let K=mZ[Y];if(!K)return{readers:[],widths:[],ok:!1,timeIdx:-1};Q.push(H0(K)),J.push(K.width)}let X=$.indexOf("TimeUS");return{readers:Q,widths:J,ok:!0,timeIdx:X}}async function pZ(Z,$,Q){let J=Q??uZ(),X=new Map,Y={},K=!1,G=null,W=null,z=4194304,B=await W0(Z,"r");try{let V=Buffer.allocUnsafe(z),U=new Uint8Array(0),L=0,A=!1;while(!A){let{bytesRead:F}=await B.read(V,0,z,L);L+=F,A=F===0;let H;if(F===0)H=U;else if(U.length===0)H=V.subarray(0,F);else H=new Uint8Array(U.length+F),H.set(U,0),H.set(V.subarray(0,F),U.length);let O=new DataView(H.buffer,H.byteOffset,H.byteLength),E=H.length,N=A,_=performance.now(),T=0;while(T+3<=E){if(H[T]!==z0||H[T+1]!==B0){T+=1;continue}let M=H[T+2],S=T+3;if(M===V0){if(S+dZ>E){if(N)K=!0;break}let k=H[S],gZ=H[S+1],r=qZ(H,S+2,4),$0=qZ(H,S+6,16),OZ=qZ(H,S+22,64).split(",").filter((K0)=>K0.length>0),{readers:J0,widths:Q0,ok:X0,timeIdx:Y0}=L0($0,OZ);X.set(k,{name:r,length:gZ,columns:OZ,ok:X0,payloadLen:gZ-3,readers:J0,widths:Q0,timeIdx:Y0}),$.onFmt(r,OZ),Y[r]=(Y[r]??0)+1,T+=3+dZ;continue}let I=X.get(M);if(I===void 0){T+=1;continue}let UZ=I.payloadLen;if(S+UZ>E){if(N)K=!0;break}if(Y[I.name]=(Y[I.name]??0)+1,!I.ok){T+=3+UZ;continue}let{readers:HZ,widths:Z0}=I,LZ=Array(HZ.length),fZ=S;for(let k=0;k<HZ.length;k++)LZ[k]=HZ[k](O,fZ),fZ+=Z0[k];if($.onRow(I.name,LZ),I.timeIdx>=0){let k=LZ[I.timeIdx];if(typeof k==="bigint")W=k}T+=3+UZ}if(U=H.slice(T),await xZ(performance.now()-_,J),A)break}}finally{await B.close()}if(K)G=W;return{partial:K,decodedUntil:G,messageCounts:Y}}function lZ(Z,$,Q){let J=Z[$];if(!J)Z[$]={columns:Q,rows:[]};else J.columns=Q}async function n(Z){let $={},Q=await pZ(Z,{onFmt:(J,X)=>lZ($,J,X),onRow:(J,X)=>{$[J].rows.push(X)}});return{partial:Q.partial,decodedUntil:Q.decodedUntil,tables:$,messageCounts:Q.messageCounts}}async function nZ(Z,$,Q={}){let J=Q.retain??new Set,X=5000,Y={},K=new Map,G=await pZ(Z,{onFmt:(W,z)=>{if(lZ(Y,W,z),$.table(W,z),!K.has(W))K.set(W,[])},onRow:(W,z)=>{let B=K.get(W);if(B){if(B.push(z),B.length>=5000)$.rows(W,B),B.length=0}if(J.has(W))Y[W].rows.push(z)}});for(let[W,z]of K)if(z.length)$.rows(W,z);return{partial:G.partial,decodedUntil:G.decodedUntil,tables:Y,messageCounts:G.messageCounts}}function qZ(Z,$,Q){let J="",X=$+Q;for(let Y=$;Y<X;Y++){let K=Z[Y];if(K===0)break;J+=String.fromCharCode(K)}return J}var O0=new URL("./mavdump.py",import.meta.url).pathname,q0=new Set(["TimeUS","TimeMS"]);function cZ(Z){return typeof Z==="object"&&Z!==null&&"_summary"in Z}function A0(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z))return BigInt(Z);return 0n}async function u(Z){if(await Bun.spawn(["python3","-c","import pymavlink"],{stdout:"ignore",stderr:"ignore"}).exited!==0)throw Error("pymavlink not found \u2014 pip install pymavlink");let Q=Bun.spawn(["python3",O0,Z],{stdout:"pipe",stderr:"pipe"}),J=new Map,X={},Y=!1,K=null,G=null,W=!1;function z(H){let O=J.get(H);if(!O)O={columns:[],colIndex:new Map,rows:[]},J.set(H,O);return O}function B(H){let O=H.t,E=z(O),N=H.f;for(let T of Object.keys(N))if(!E.colIndex.has(T))E.colIndex.set(T,E.columns.length),E.columns.push(T);let _=Array(E.columns.length).fill(null);for(let T of Object.keys(N)){let M=E.colIndex.get(T),S=N[T],I=q0.has(T)?A0(S):S;if(_[M]=I,T==="TimeUS"&&typeof I==="bigint")G=I}E.rows.push(_)}let V=new TextDecoder,U="";for await(let H of Q.stdout){U+=V.decode(H,{stream:!0});let O;while((O=U.indexOf(`
3
- `))!==-1){let E=U.slice(0,O);if(U=U.slice(O+1),E.length===0)continue;let N;try{N=JSON.parse(E)}catch{Y=!0;continue}if(cZ(N)){W=!0;for(let[_,T]of Object.entries(N._summary.counts))X[_]=T;if(N._summary.partial)Y=!0}else B(N)}}U+=V.decode();let L=U.trim();if(L.length>0)try{let H=JSON.parse(L);if(cZ(H)){W=!0;for(let[O,E]of Object.entries(H._summary.counts))X[O]=E;if(H._summary.partial)Y=!0}else B(H)}catch{Y=!0}let A=await Q.exited;if(A!==0){if(!W){let H=(await new Response(Q.stderr).text()).trim();throw Error(`pymavlink decode failed (exit ${A})${H?`: ${H}`:""}`)}Y=!0}if(!W)Y=!0;if(Y)K=G;let F={};for(let[H,O]of J)F[H]={columns:O.columns,rows:O.rows};for(let[H,O]of J)if(X[H]===void 0)X[H]=O.rows.length;return{partial:Y,decodedUntil:K,tables:F,messageCounts:X}}var E0=new URL("./ulog.py",import.meta.url).pathname,T0=new Set(["timestamp","timestamp_sample"]);function F0(Z){return typeof Z==="object"&&Z!==null&&"_summary"in Z}function N0(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z))return BigInt(Z);return 0n}async function _0(){let Z=()=>Bun.spawn(["python3","-c","import pyulog"],{stdout:"ignore",stderr:"ignore"}).exited;if(await Z()===0)return;if(await Bun.spawn(["python3","-m","pip","install","--quiet","pyulog"],{stdout:"ignore",stderr:"ignore"}).exited,await Z()!==0)throw Error("pyulog not found \u2014 pip install pyulog")}async function a(Z){await _0();let $=Bun.spawn(["python3",E0,Z],{stdout:"pipe",stderr:"pipe"}),Q=new Map,J={},X=!1,Y=null,K=null,G=!1;function W(H){let O=Q.get(H);if(!O)O={columns:[],colIndex:new Map,rows:[]},Q.set(H,O);return O}function z(H){let O=H.t,E=W(O),N=H.f;for(let T of Object.keys(N))if(!E.colIndex.has(T))E.colIndex.set(T,E.columns.length),E.columns.push(T);let _=Array(E.columns.length).fill(null);for(let T of Object.keys(N)){let M=E.colIndex.get(T),S=N[T],I=T0.has(T)?N0(S):S;if(_[M]=I,T==="timestamp"&&typeof I==="bigint")K=I}E.rows.push(_)}function B(H){if(H.length===0)return;let O;try{O=JSON.parse(H)}catch{X=!0;return}if(F0(O)){G=!0;for(let[E,N]of Object.entries(O._summary.counts))J[E]=N;if(O._summary.partial)X=!0}else z(O)}let V=new TextDecoder,U="";for await(let H of $.stdout){U+=V.decode(H,{stream:!0});let O;while((O=U.indexOf(`
4
- `))!==-1)B(U.slice(0,O)),U=U.slice(O+1)}U+=V.decode();let L=U.trim();if(L.length>0)B(L);let A=await $.exited;if(A!==0){if(!G){let H=(await new Response($.stderr).text()).trim();throw Error(`ulog decode failed (exit ${A})${H?`: ${H}`:""}`)}X=!0}if(!G)X=!0;if(X)Y=K;let F={};for(let[H,O]of Q)F[H]={columns:O.columns,rows:O.rows};for(let[H,O]of Q)if(J[H]===void 0)J[H]=O.rows.length;return{partial:X,decodedUntil:Y,tables:F,messageCounts:J}}import{existsSync as iZ,readFileSync as R0}from"fs";import{mkdtemp as P0,rm as D0}from"fs/promises";import{tmpdir as j0}from"os";import{basename as AZ,join as EZ}from"path";function s(Z){if(!Z)return[];let $=EZ(Z,".hydron","uav-format-converters.json");if(!iZ($))return[];try{let Q=JSON.parse(R0($,"utf8"));return Array.isArray(Q.converters)?Q.converters.filter((J)=>J&&J.match&&J.command):[]}catch{return[]}}function t(Z,$){for(let Q of $)try{if(new RegExp(Q.match,"i").test(Z))return Q}catch{}return null}async function e(Z,$){let Q=await P0(EZ(j0(),"uav-convert-")),J=AZ(Z).replace(/\.[^.]+$/,""),X=EZ(Q,J+($.produces??".bin")),Y=$.command.replaceAll("{input}",Z).replaceAll("{output}",X),K=()=>D0(Q,{recursive:!0,force:!0});try{let G=Bun.spawn(["bash","-lc",Y],{stdout:"pipe",stderr:"pipe"}),W=await G.exited;if(W!==0){let z=(await new Response(G.stderr).text()).trim().slice(0,300);throw Error(`format converter failed (exit ${W}) for ${AZ(Z)}: ${z}`)}if(!iZ(X))throw Error(`format converter produced no output for ${AZ(Z)}`);return{out:X,cleanup:K}}catch(G){throw await K(),G}}import{Database as I0}from"bun:sqlite";import{tmpdir as C0}from"os";import{join as M0}from"path";import{existsSync as oZ,unlinkSync as rZ}from"fs";var S0=0;function sZ(Z){let $=M0(C0(),`uav-${Z}-${process.pid}-${S0++}.sqlite`);try{if(oZ($))rZ($)}catch{}let Q=new I0($);return Q.exec("PRAGMA page_size = 8192"),Q.query("PRAGMA journal_mode = MEMORY").get(),Q.exec("PRAGMA synchronous = OFF"),Q.exec("PRAGMA temp_store = FILE"),Q.exec("PRAGMA cache_size = -65536"),{db:Q,cleanup(){try{Q.close()}catch{}try{if(oZ($))rZ($)}catch{}}}}var TZ=(Z)=>`"${Z.replace(/"/g,'""')}"`,aZ=5000;function FZ(Z){let $=new Map,Q=new Map;return{table(J,X){if($.has(J)||X.length===0)return;Z.run(`CREATE TABLE ${TZ(J)} (${X.map(TZ).join(", ")})`);let Y=X.map(()=>"?").join(", ");$.set(J,Z.query(`INSERT INTO ${TZ(J)} VALUES (${Y})`)),Q.set(J,X.length)},rows(J,X){let Y=$.get(J),K=Q.get(J);if(!Y||K===void 0||X.length===0)return;let G=Z.transaction((W)=>{for(let z of W){let B=Array(K);for(let V=0;V<K;V++){let U=z[V];B[V]=U===void 0?null:U}Y.run(...B)}});for(let W=0;W<X.length;W+=aZ)G(X.slice(W,W+aZ))}}}function tZ(Z,$){let Q=FZ(Z);for(let[J,X]of Object.entries($.tables)){if(X.columns.length===0)continue;if(Q.table(J,X.columns),X.rows.length)Q.rows(J,X.rows)}}var w0=new Set(["PARM","MSG","GPS","ERR","MODE"]);async function c(Z,$="flight",Q){let J=t(Z,s(Q?.configDir));if(J){let{out:K,cleanup:G}=await e(Z,J);try{return await c(K,$)}finally{await G()}}let{db:X,cleanup:Y}=sZ($);try{let K=Z.toLowerCase();if(K.endsWith(".bin"))return{log:await nZ(Z,FZ(X),{retain:w0}),db:X,cleanup:Y};let G;if(K.endsWith(".ulog"))G=await a(Z);else if(K.endsWith(".tlog")||K.endsWith(".log"))G=await u(Z);else try{G=await n(Z)}catch{G=await u(Z)}return tZ(X,G),{log:G,db:X,cleanup:Y}}catch(K){throw Y(),K}}var ZZ=1e4;async function v0(Z,$,Q){let J=eZ(Z,".hydron","observations",$),X;try{X=await y0(J)}catch{throw Error(`No observation store at ${J}. Run uav_analyze first.`)}for(let Y of X){let K=eZ(J,Y,`${Q}.json`);try{let W=JSON.parse(await k0(K,"utf8"))?.episode?.attrs?.source_path;if(typeof W==="string"&&W)return W;throw Error(`Episode ${Q} has no source_path; re-run uav_analyze to record it.`)}catch(G){if(G.code==="ENOENT")continue;throw G}}throw Error(`flight_id ${Q} not found in the observation store. Run uav_analyze first.`)}function h0(Z){let $=Z.trim();$=$.replace(/^```(?:sql)?\s*/i,"").replace(/\s*```$/,"").trim();for(;;){let X=$;if($=$.replace(/^--[^\n]*\n?/,"").replace(/^\/\*[\s\S]*?\*\//,"").trimStart(),$===X)break}if($=$.replace(/;\s*$/,""),$.includes(";"))throw Error("Only a single statement is allowed (no ';').");let Q=$.replace(/^\(+\s*/,"");if(!(/^\s*(select|with|values)\b/i.test(Q)||/^\s*explain\b/i.test($)||/^\s*pragma\s+(table_info|table_list|table_xinfo|index_list|index_info|index_xinfo|database_list|collation_list|function_list|pragma_list)\b/i.test($)))throw Error("uav_query is read-only: use SELECT/WITH/VALUES (comments & ```fences ok), EXPLAIN, or a read PRAGMA (table_info/table_list/index_*). For heavier analysis use Python via bash, not SQL.");if(/\b(insert|update|delete|drop|create|alter|attach|detach|replace|reindex|vacuum)\b/i.test($))throw Error("Write/DDL keyword detected \u2014 uav_query is read-only.");if(!/\blimit\b/i.test($))$+=` LIMIT ${ZZ}`;return $}async function Z$(Z){let $=h0(Z.query),Q=await v0(Z.projectDir,Z.workspace,Z.flight_id),{db:J,cleanup:X}=await c(Q,Z.flight_id);try{J.exec?.("PRAGMA query_only = ON");let K=J.query($).all(),G=K.length?Object.keys(K[0]):[],z=K.slice(0,ZZ).map((B)=>G.map((V)=>B[V]));return{columns:G,rows:z,truncated:K.length>ZZ}}finally{X()}}function $$(Z){if(!Z.columns.length)return"(no rows)";let $=Z.columns.join(" | "),Q=Z.rows.slice(0,50).map((X)=>X.map((Y)=>typeof Y==="bigint"?Y.toString():String(Y)).join(" | ")).join(`
2
+ import{readdir as $0,readFile as J0,writeFile as g6,mkdir as u6}from"fs/promises";import{join as u}from"path";import{tool as R}from"@kilocode/plugin";import{readdir as m0,readFile as d0}from"fs/promises";import{join as J$}from"path";import{open as V0}from"fs/promises";function mZ(){let Z=process.env.UAV_DECODE_MAX_CPU;if(!Z)return;let $=Number(Z);return Number.isFinite($)&&$>0&&$<1?$:void 0}var B0=(Z)=>new Promise(($)=>setTimeout($,Z));async function dZ(Z,$){if(!$||Z<=0)return;let Q=Math.min(Z*(1/$-1),250);if(Q>=1)await B0(Q)}var pZ={a:{kind:"str",width:64},b:{kind:"i8",width:1},B:{kind:"u8",width:1},h:{kind:"i16",width:2},H:{kind:"u16",width:2},i:{kind:"i32",width:4},I:{kind:"u32",width:4},f:{kind:"f32",width:4},d:{kind:"f64",width:8},n:{kind:"str",width:4},N:{kind:"str",width:16},Z:{kind:"str",width:64},c:{kind:"i16",width:2,scale:0.01},C:{kind:"u16",width:2,scale:0.01},e:{kind:"i32",width:4,scale:0.01},E:{kind:"u32",width:4,scale:0.01},L:{kind:"i32",width:4,scale:0.0000001},M:{kind:"u8",width:1},q:{kind:"i64",width:8},Q:{kind:"u64",width:8}};var U0=163,H0=149,L0=128,lZ=86;function O0(Z){return($,Q)=>{let J="",X=Q+Z;for(let Y=Q;Y<X;Y++){let K=$.getUint8(Y);if(K===0)break;J+=String.fromCharCode(K)}return J}}function A0(Z){let $=Z.scale;switch(Z.kind){case"i8":return $===void 0?(Q,J)=>Q.getInt8(J):(Q,J)=>Q.getInt8(J)*$;case"u8":return $===void 0?(Q,J)=>Q.getUint8(J):(Q,J)=>Q.getUint8(J)*$;case"i16":return $===void 0?(Q,J)=>Q.getInt16(J,!0):(Q,J)=>Q.getInt16(J,!0)*$;case"u16":return $===void 0?(Q,J)=>Q.getUint16(J,!0):(Q,J)=>Q.getUint16(J,!0)*$;case"i32":return $===void 0?(Q,J)=>Q.getInt32(J,!0):(Q,J)=>Q.getInt32(J,!0)*$;case"u32":return $===void 0?(Q,J)=>Q.getUint32(J,!0):(Q,J)=>Q.getUint32(J,!0)*$;case"f32":return(Q,J)=>Q.getFloat32(J,!0);case"f64":return(Q,J)=>Q.getFloat64(J,!0);case"i64":return(Q,J)=>Q.getBigInt64(J,!0);case"u64":return(Q,J)=>Q.getBigUint64(J,!0);case"str":return O0(Z.width)}}function q0(Z,$){let Q=[],J=[];for(let Y of Z){let K=pZ[Y];if(!K)return{readers:[],widths:[],ok:!1,timeIdx:-1};Q.push(A0(K)),J.push(K.width)}let X=$.indexOf("TimeUS");return{readers:Q,widths:J,ok:!0,timeIdx:X}}async function nZ(Z,$,Q){let J=Q??mZ(),X=new Map,Y={},K=!1,G=null,W=null,z=4194304,B=await V0(Z,"r");try{let V=Buffer.allocUnsafe(z),U=new Uint8Array(0),H=0,E=!1;while(!E){let{bytesRead:T}=await B.read(V,0,z,H);H+=T,E=T===0;let L;if(T===0)L=U;else if(U.length===0)L=V.subarray(0,T);else L=new Uint8Array(U.length+T),L.set(U,0),L.set(V.subarray(0,T),U.length);let A=new DataView(L.buffer,L.byteOffset,L.byteLength),O=L.length,F=E,N=performance.now(),_=0;while(_+3<=O){if(L[_]!==U0||L[_+1]!==H0){_+=1;continue}let D=L[_+2],w=_+3;if(D===L0){if(w+lZ>O){if(F)K=!0;break}let v=L[w],xZ=L[w+1],s=qZ(L,w+2,4),X0=qZ(L,w+6,16),AZ=qZ(L,w+22,64).split(",").filter((z0)=>z0.length>0),{readers:Y0,widths:K0,ok:G0,timeIdx:W0}=q0(X0,AZ);X.set(v,{name:s,length:xZ,columns:AZ,ok:G0,payloadLen:xZ-3,readers:Y0,widths:K0,timeIdx:W0}),$.onFmt(s,AZ),Y[s]=(Y[s]??0)+1,_+=3+lZ;continue}let M=X.get(D);if(M===void 0){_+=1;continue}let y=M.payloadLen;if(w+y>O){if(F)K=!0;break}if(Y[M.name]=(Y[M.name]??0)+1,!M.ok){_+=3+y;continue}let{readers:LZ,widths:Q0}=M,OZ=Array(LZ.length),uZ=w;for(let v=0;v<LZ.length;v++)OZ[v]=LZ[v](A,uZ),uZ+=Q0[v];if($.onRow(M.name,OZ),M.timeIdx>=0){let v=OZ[M.timeIdx];if(typeof v==="bigint")W=v}_+=3+y}if(U=L.slice(_),await dZ(performance.now()-N,J),E)break}}finally{await B.close()}if(K)G=W;return{partial:K,decodedUntil:G,messageCounts:Y}}function cZ(Z,$,Q){let J=Z[$];if(!J)Z[$]={columns:Q,rows:[]};else J.columns=Q}async function i(Z){let $={},Q=await nZ(Z,{onFmt:(J,X)=>cZ($,J,X),onRow:(J,X)=>{$[J].rows.push(X)}});return{partial:Q.partial,decodedUntil:Q.decodedUntil,tables:$,messageCounts:Q.messageCounts}}async function iZ(Z,$,Q={}){let J=Q.retain??new Set,X=5000,Y={},K=new Map,G=await nZ(Z,{onFmt:(W,z)=>{if(cZ(Y,W,z),$.table(W,z),!K.has(W))K.set(W,[])},onRow:(W,z)=>{let B=K.get(W);if(B){if(B.push(z),B.length>=5000)$.rows(W,B),B.length=0}if(J.has(W))Y[W].rows.push(z)}});for(let[W,z]of K)if(z.length)$.rows(W,z);return{partial:G.partial,decodedUntil:G.decodedUntil,tables:Y,messageCounts:G.messageCounts}}function qZ(Z,$,Q){let J="",X=$+Q;for(let Y=$;Y<X;Y++){let K=Z[Y];if(K===0)break;J+=String.fromCharCode(K)}return J}import{fileURLToPath as _0}from"url";var E0="no working Python 3 interpreter found. Install Python >= 3.8 and make sure `python3` "+"(macOS/Linux) or `python` (Windows \u2014 tick \u201CAdd python.exe to PATH\u201D in the installer) runs "+"from a terminal, or set UAV_PYTHON to the interpreter's full path.",EZ=null,oZ=!1;function F0(){let Z=process.env.UAV_PYTHON?.trim();if(Z)return[[Z]];return process.platform==="win32"?[["py","-3"],["python"],["python3"]]:[["python3"],["python"]]}async function T0(Z){try{return await Bun.spawn([...Z,"-c","import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)"],{stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited===0}catch{return!1}}async function N0(){if(oZ)return EZ;for(let Z of F0())if(await T0(Z)){EZ=Z;break}return oZ=!0,EZ}async function x(){let Z=await N0();if(!Z)throw Error(E0);return Z}var R0=_0(new URL("./mavdump.py",import.meta.url)),D0=new Set(["TimeUS","TimeMS"]);function rZ(Z){return typeof Z==="object"&&Z!==null&&"_summary"in Z}function j0(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z))return BigInt(Z);return 0n}async function m(Z){let $=await x();if(await Bun.spawn([...$,"-c","import pymavlink"],{stdout:"ignore",stderr:"ignore"}).exited!==0)throw Error(`pymavlink not found \u2014 ${$.join(" ")} -m pip install pymavlink`);let J=Bun.spawn([...$,R0,Z],{stdout:"pipe",stderr:"pipe"}),X=new Map,Y={},K=!1,G=null,W=null,z=!1;function B(A){let O=X.get(A);if(!O)O={columns:[],colIndex:new Map,rows:[]},X.set(A,O);return O}function V(A){let O=A.t,F=B(O),N=A.f;for(let D of Object.keys(N))if(!F.colIndex.has(D))F.colIndex.set(D,F.columns.length),F.columns.push(D);let _=Array(F.columns.length).fill(null);for(let D of Object.keys(N)){let w=F.colIndex.get(D),M=N[D],y=D0.has(D)?j0(M):M;if(_[w]=y,D==="TimeUS"&&typeof y==="bigint")W=y}F.rows.push(_)}let U=new TextDecoder,H="";for await(let A of J.stdout){H+=U.decode(A,{stream:!0});let O;while((O=H.indexOf(`
3
+ `))!==-1){let F=H.slice(0,O);if(H=H.slice(O+1),F.length===0)continue;let N;try{N=JSON.parse(F)}catch{K=!0;continue}if(rZ(N)){z=!0;for(let[_,D]of Object.entries(N._summary.counts))Y[_]=D;if(N._summary.partial)K=!0}else V(N)}}H+=U.decode();let E=H.trim();if(E.length>0)try{let A=JSON.parse(E);if(rZ(A)){z=!0;for(let[O,F]of Object.entries(A._summary.counts))Y[O]=F;if(A._summary.partial)K=!0}else V(A)}catch{K=!0}let T=await J.exited;if(T!==0){if(!z){let A=(await new Response(J.stderr).text()).trim();throw Error(`pymavlink decode failed (exit ${T})${A?`: ${A}`:""}`)}K=!0}if(!z)K=!0;if(K)G=W;let L={};for(let[A,O]of X)L[A]={columns:O.columns,rows:O.rows};for(let[A,O]of X)if(Y[A]===void 0)Y[A]=O.rows.length;return{partial:K,decodedUntil:G,tables:L,messageCounts:Y}}import{fileURLToPath as P0}from"url";var I0=P0(new URL("./ulog.py",import.meta.url)),C0=new Set(["timestamp","timestamp_sample"]);function M0(Z){return typeof Z==="object"&&Z!==null&&"_summary"in Z}function S0(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z))return BigInt(Z);return 0n}async function w0(Z){let $=()=>Bun.spawn([...Z,"-c","import pyulog"],{stdout:"ignore",stderr:"ignore"}).exited;if(await $()===0)return;if(await Bun.spawn([...Z,"-m","pip","install","--quiet","pyulog"],{stdout:"ignore",stderr:"ignore"}).exited,await $()!==0)throw Error(`pyulog not found \u2014 ${Z.join(" ")} -m pip install pyulog`)}async function t(Z){let $=await x();await w0($);let Q=Bun.spawn([...$,I0,Z],{stdout:"pipe",stderr:"pipe"}),J=new Map,X={},Y=!1,K=null,G=null,W=!1;function z(A){let O=J.get(A);if(!O)O={columns:[],colIndex:new Map,rows:[]},J.set(A,O);return O}function B(A){let O=A.t,F=z(O),N=A.f;for(let D of Object.keys(N))if(!F.colIndex.has(D))F.colIndex.set(D,F.columns.length),F.columns.push(D);let _=Array(F.columns.length).fill(null);for(let D of Object.keys(N)){let w=F.colIndex.get(D),M=N[D],y=C0.has(D)?S0(M):M;if(_[w]=y,D==="timestamp"&&typeof y==="bigint")G=y}F.rows.push(_)}function V(A){if(A.length===0)return;let O;try{O=JSON.parse(A)}catch{Y=!0;return}if(M0(O)){W=!0;for(let[F,N]of Object.entries(O._summary.counts))X[F]=N;if(O._summary.partial)Y=!0}else B(O)}let U=new TextDecoder,H="";for await(let A of Q.stdout){H+=U.decode(A,{stream:!0});let O;while((O=H.indexOf(`
4
+ `))!==-1)V(H.slice(0,O)),H=H.slice(O+1)}H+=U.decode();let E=H.trim();if(E.length>0)V(E);let T=await Q.exited;if(T!==0){if(!W){let A=(await new Response(Q.stderr).text()).trim();throw Error(`ulog decode failed (exit ${T})${A?`: ${A}`:""}`)}Y=!0}if(!W)Y=!0;if(Y)K=G;let L={};for(let[A,O]of J)L[A]={columns:O.columns,rows:O.rows};for(let[A,O]of J)if(X[A]===void 0)X[A]=O.rows.length;return{partial:Y,decodedUntil:K,tables:L,messageCounts:X}}import{existsSync as aZ,readFileSync as y0}from"fs";import{mkdtemp as k0,rm as v0}from"fs/promises";import{tmpdir as h0}from"os";import{basename as FZ,join as TZ}from"path";function e(Z){if(!Z)return[];let $=TZ(Z,".hydron","uav-format-converters.json");if(!aZ($))return[];try{let Q=JSON.parse(y0($,"utf8"));return Array.isArray(Q.converters)?Q.converters.filter((J)=>J&&J.match&&J.command):[]}catch{return[]}}function ZZ(Z,$){for(let Q of $)try{if(new RegExp(Q.match,"i").test(Z))return Q}catch{}return null}async function $Z(Z,$){let Q=await k0(TZ(h0(),"uav-convert-")),J=FZ(Z).replace(/\.[^.]+$/,""),X=TZ(Q,J+($.produces??".bin")),Y=$.command.replaceAll("{input}",Z).replaceAll("{output}",X),K=()=>v0(Q,{recursive:!0,force:!0});try{let G=process.platform==="win32"?["cmd.exe","/d","/s","/c",Y]:["bash","-lc",Y],W=Bun.spawn(G,{stdout:"pipe",stderr:"pipe"}),z=await W.exited;if(z!==0){let B=(await new Response(W.stderr).text()).trim().slice(0,300);throw Error(`format converter failed (exit ${z}) for ${FZ(Z)}: ${B}`)}if(!aZ(X))throw Error(`format converter produced no output for ${FZ(Z)}`);return{out:X,cleanup:K}}catch(G){throw await K(),G}}import{Database as b0}from"bun:sqlite";import{tmpdir as f0}from"os";import{join as g0}from"path";import{existsSync as sZ,unlinkSync as tZ}from"fs";var u0=0;function Z$(Z){let $=g0(f0(),`uav-${Z}-${process.pid}-${u0++}.sqlite`);try{if(sZ($))tZ($)}catch{}let Q=new b0($);return Q.exec("PRAGMA page_size = 8192"),Q.query("PRAGMA journal_mode = MEMORY").get(),Q.exec("PRAGMA synchronous = OFF"),Q.exec("PRAGMA temp_store = FILE"),Q.exec("PRAGMA cache_size = -65536"),{db:Q,cleanup(){try{Q.close()}catch{}try{if(sZ($))tZ($)}catch{}}}}var NZ=(Z)=>`"${Z.replace(/"/g,'""')}"`,eZ=5000;function _Z(Z){let $=new Map,Q=new Map;return{table(J,X){if($.has(J)||X.length===0)return;Z.run(`CREATE TABLE ${NZ(J)} (${X.map(NZ).join(", ")})`);let Y=X.map(()=>"?").join(", ");$.set(J,Z.query(`INSERT INTO ${NZ(J)} VALUES (${Y})`)),Q.set(J,X.length)},rows(J,X){let Y=$.get(J),K=Q.get(J);if(!Y||K===void 0||X.length===0)return;let G=Z.transaction((W)=>{for(let z of W){let B=Array(K);for(let V=0;V<K;V++){let U=z[V];B[V]=U===void 0?null:U}Y.run(...B)}});for(let W=0;W<X.length;W+=eZ)G(X.slice(W,W+eZ))}}}function $$(Z,$){let Q=_Z(Z);for(let[J,X]of Object.entries($.tables)){if(X.columns.length===0)continue;if(Q.table(J,X.columns),X.rows.length)Q.rows(J,X.rows)}}var x0=new Set(["PARM","MSG","GPS","ERR","MODE"]);async function o(Z,$="flight",Q){let J=ZZ(Z,e(Q?.configDir));if(J){let{out:K,cleanup:G}=await $Z(Z,J);try{return await o(K,$)}finally{await G()}}let{db:X,cleanup:Y}=Z$($);try{let K=Z.toLowerCase();if(K.endsWith(".bin"))return{log:await iZ(Z,_Z(X),{retain:x0}),db:X,cleanup:Y};let G;if(K.endsWith(".ulog"))G=await t(Z);else if(K.endsWith(".tlog")||K.endsWith(".log"))G=await m(Z);else try{G=await i(Z)}catch{G=await m(Z)}return $$(X,G),{log:G,db:X,cleanup:Y}}catch(K){throw Y(),K}}var JZ=1e4;async function p0(Z,$,Q){let J=J$(Z,".hydron","observations",$),X;try{X=await m0(J)}catch{throw Error(`No observation store at ${J}. Run uav_analyze first.`)}for(let Y of X){let K=J$(J,Y,`${Q}.json`);try{let W=JSON.parse(await d0(K,"utf8"))?.episode?.attrs?.source_path;if(typeof W==="string"&&W)return W;throw Error(`Episode ${Q} has no source_path; re-run uav_analyze to record it.`)}catch(G){if(G.code==="ENOENT")continue;throw G}}throw Error(`flight_id ${Q} not found in the observation store. Run uav_analyze first.`)}function l0(Z){let $=Z.trim();$=$.replace(/^```(?:sql)?\s*/i,"").replace(/\s*```$/,"").trim();for(;;){let X=$;if($=$.replace(/^--[^\n]*\n?/,"").replace(/^\/\*[\s\S]*?\*\//,"").trimStart(),$===X)break}if($=$.replace(/;\s*$/,""),$.includes(";"))throw Error("Only a single statement is allowed (no ';').");let Q=$.replace(/^\(+\s*/,"");if(!(/^\s*(select|with|values)\b/i.test(Q)||/^\s*explain\b/i.test($)||/^\s*pragma\s+(table_info|table_list|table_xinfo|index_list|index_info|index_xinfo|database_list|collation_list|function_list|pragma_list)\b/i.test($)))throw Error("uav_query is read-only: use SELECT/WITH/VALUES (comments & ```fences ok), EXPLAIN, or a read PRAGMA (table_info/table_list/index_*). For heavier analysis use Python via bash, not SQL.");if(/\b(insert|update|delete|drop|create|alter|attach|detach|replace|reindex|vacuum)\b/i.test($))throw Error("Write/DDL keyword detected \u2014 uav_query is read-only.");if(!/\blimit\b/i.test($))$+=` LIMIT ${JZ}`;return $}async function Q$(Z){let $=l0(Z.query),Q=await p0(Z.projectDir,Z.workspace,Z.flight_id),{db:J,cleanup:X}=await o(Q,Z.flight_id);try{J.exec?.("PRAGMA query_only = ON");let K=J.query($).all(),G=K.length?Object.keys(K[0]):[],z=K.slice(0,JZ).map((B)=>G.map((V)=>B[V]));return{columns:G,rows:z,truncated:K.length>JZ}}finally{X()}}function X$(Z){if(!Z.columns.length)return"(no rows)";let $=Z.columns.join(" | "),Q=Z.rows.slice(0,50).map((X)=>X.map((Y)=>typeof Y==="bigint"?Y.toString():String(Y)).join(" | ")).join(`
5
5
  `),J=Z.rows.length>50?`
6
6
  \u2026 ${Z.rows.length-50} more rows`:Z.truncated?`
7
- (capped at ${ZZ} rows)`:"";return`${$}
8
- ${Q}${J}`}import{createHash as e8}from"crypto";import{open as h$,stat as Z6}from"fs/promises";import{resolve as $6}from"path";function NZ(Z,$){let Q=Z.tables.PARM;if(!Q)return null;let J=Q.columns.findIndex((K)=>K.toLowerCase()==="name"),X=Q.columns.findIndex((K)=>K.toLowerCase()==="value");if(J<0||X<0)return null;let Y=$.toLowerCase();for(let K of Q.rows)if(String(K[J]).toLowerCase()===Y){let G=Number(K[X]);return Number.isFinite(G)?G:null}return null}function b0(Z){let $=Z.tables.MSG;if(!$)return"";let Q=$.columns.findIndex((J)=>/message|msg/i.test(J));if(Q<0)return"";for(let J of $.rows){let X=String(J[Q]??"");if(/Ardu(Copter|Plane|Sub|Rover)|PX4/i.test(X))return X}return""}function J$(Z){let $=NZ(Z,"Q_ENABLE");if($!==null&&$>=1||Z.tables.QUIK||Z.tables.AETR){if(Z.tables.MOTB||NZ(Z,"FRAME_CLASS")!==null||Z.tables.ARSP)return"vtol"}if($!==null&&$>=1)return"vtol";let Q=b0(Z);if(/ArduPlane/i.test(Q))return"fixedwing";if(/ArduCopter/i.test(Q))return"multirotor";if(NZ(Z,"FRAME_CLASS")!==null)return"multirotor";if(Z.tables.ARSP&&!Z.tables.MOTB)return"fixedwing";if(Z.tables.MOTB||Z.tables.RATE)return"multirotor";return"unknown"}function $Z(Z){return Z===void 0||Z==="multirotor"||Z==="vtol"||Z==="unknown"}function Q$(Z){return Z==="fixedwing"||Z==="vtol"}function w(Z){return`"${Z.replace(/"/g,'""')}"`}function P(Z,$){let Q=Z.db.query($).get();if(!Q)return null;let J=Object.values(Q)[0];if(J===null||J===void 0)return null;return Number(J)}function y(Z,$){try{let Q=Z.db.query(`PRAGMA table_info(${w($)})`).all();return new Set(Q.map((J)=>J.name))}catch{return new Set}}function C(Z,$,Q){return y(Z,$).has(Q)}function Y$(Z,$){for(let Q of $)if(Z.hasTable(Q))return Q;return null}function q(Z,$,Q){return{check:Z,severity:"OK",value:null,threshold_used:null,ruleset_version:$.ruleset_version,verdict:"SKIP",tier:"system",...Q?{detail:Q}:{}}}function D(Z){return{check:Z.check,severity:Z.failed?Z.failSeverity:"OK",value:Z.value,threshold_used:Z.threshold_used,ruleset_version:Z.thresholds.ruleset_version,verdict:Z.failed?"FAIL":"PASS",tier:"system",...Z.source?{source:Z.source}:{},...Z.detail?{detail:Z.detail}:{}}}var f0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("ERR"))return q("battery",Z.thresholds);if(!C(Z,"ERR","Subsys")||!C(Z,"ERR","ECode"))return q("battery",Z.thresholds,"ERR present but Subsys/ECode columns absent");let Q=$?.err_subsys??6,J=$?.err_ecode??1,X=P(Z,`SELECT COUNT(*) FROM "ERR" WHERE "Subsys"=${Q} AND "ECode"=${J}`)??0;return D({check:"battery",thresholds:Z.thresholds,failed:X>0,failSeverity:"CRITICAL",value:X,threshold_used:0,source:$?.source,detail:X>0?`${X} battery failsafe ERR event(s) (Subsys=${Q} ECode=${J})`:void 0})},g0=(Z)=>{let $=Z.thresholds.attitude;if(!Z.hasTable("ATT"))return q("attitude",Z.thresholds);for(let K of["DesRoll","Roll","DesPitch","Pitch"])if(!C(Z,"ATT",K))return q("attitude",Z.thresholds,`ATT missing column ${K}`);let Q=$?.max_deg??5,J=P(Z,'SELECT MAX(ABS("DesRoll"-"Roll")) FROM "ATT"'),X=P(Z,'SELECT MAX(ABS("DesPitch"-"Pitch")) FROM "ATT"'),Y=Math.max(J??0,X??0);return D({check:"attitude",thresholds:Z.thresholds,failed:Y>Q,failSeverity:"HIGH",value:Y,threshold_used:Q,source:$?.source,detail:`peak roll err ${(J??0).toFixed(2)}\xB0, pitch err ${(X??0).toFixed(2)}\xB0`})},u0=(Z)=>{let $=Z.thresholds.altitude;if(!Z.hasTable("CTUN"))return q("altitude",Z.thresholds);if(!C(Z,"CTUN","DAlt")||!C(Z,"CTUN","Alt"))return q("altitude",Z.thresholds,"CTUN missing DAlt/Alt columns");let Q=$?.max_m??2,J=P(Z,'SELECT MAX(ABS("DAlt"-"Alt")) FROM "CTUN"');return D({check:"altitude",thresholds:Z.thresholds,failed:(J??0)>Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source})},x0=(Z)=>{let $=Z.thresholds.hover;if(!$Z(Z.vehicleType))return q("hover",Z.thresholds,`n/a for ${Z.vehicleType} (no hover throttle)`);if(!Z.hasTable("CTUN"))return q("hover",Z.thresholds);if(!C(Z,"CTUN","ThH"))return q("hover",Z.thresholds,"CTUN missing ThH column");let Q=$?.band??[0.25,0.75],[J,X]=Q,Y=P(Z,'SELECT AVG("ThH") FROM "CTUN"'),K=Y===null?!1:Y<J||Y>X;return D({check:"hover",thresholds:Z.thresholds,failed:K,failSeverity:"HIGH",value:Y,threshold_used:X,source:$?.source,detail:`mean hover throttle ${(Y??0).toFixed(3)} vs band [${J}, ${X}]`})},X$=2000,m0=(Z)=>{if(!$Z(Z.vehicleType))return q("rcou",Z.thresholds,`n/a for ${Z.vehicleType} (control surfaces, not motor mix)`);if(!Z.hasTable("RCOU"))return q("rcou",Z.thresholds);let $=y(Z,"RCOU"),Q=[];for(let Y=1;Y<=8;Y++)if($.has(`C${Y}`))Q.push(`C${Y}`);if(Q.length===0)return q("rcou",Z.thresholds,"RCOU has no C1..C8 channels");let J=-1/0,X=[];for(let Y of Q){let K=P(Z,`SELECT MAX(${w(Y)}) FROM "RCOU"`);if(K===null)continue;if(K>J)J=K;if(K>=X$)X.push(`${Y}=${K}`)}if(J===-1/0)J=0;return D({check:"rcou",thresholds:Z.thresholds,failed:X.length>0,failSeverity:"HIGH",value:J,threshold_used:X$,detail:X.length>0?`motor output(s) pegged at max: ${X.join(", ")}`:void 0})},_Z=1000,RZ=2000,d0=(Z)=>{if(!Z.hasTable("RCIN"))return q("rcin",Z.thresholds);if(!C(Z,"RCIN","C3"))return q("rcin",Z.thresholds,"RCIN missing C3 column");let $=P(Z,'SELECT MIN("C3") FROM "RCIN"'),Q=P(Z,'SELECT MAX("C3") FROM "RCIN"'),J=$!==null&&$<_Z||Q!==null&&Q>RZ,X=Q;if($!==null&&$<_Z)X=$;return D({check:"rcin",thresholds:Z.thresholds,failed:J,failSeverity:"HIGH",value:X,threshold_used:RZ,detail:`RCIN C3 range [${$??"\u2014"}, ${Q??"\u2014"}] vs [${_Z}, ${RZ}]`})},p0=(Z)=>{let $=Z.thresholds.vibration;if(!Z.hasTable("VIBE"))return q("vibration",Z.thresholds);let Q=y(Z,"VIBE"),J=["VibeX","VibeY","VibeZ"].filter((V)=>Q.has(V));if(J.length===0)return q("vibration",Z.thresholds,"VIBE has no VibeX/Y/Z columns");let X=$?.max_ms2??30,Y={},K=-1/0;for(let V of J){let L=P(Z,`SELECT MAX(${w(V)}) FROM "VIBE"`)??0;if(Y[V]=L,L>K)K=L}if(K===-1/0)K=0;let G=["Clip","Clip0","Clip1","Clip2"].filter((V)=>Q.has(V)),W=0;for(let V of G){let U=P(Z,`SELECT MAX(${w(V)}) FROM "VIBE"`);if(U!==null&&U>W)W=U}let z=G.length>0?` | clipping MAX=${W}${W>0?" (CLIP EVENTS)":""}`:"",B=l0(Y,K,W);return D({check:"vibration",thresholds:Z.thresholds,failed:K>=X,failSeverity:"HIGH",value:K,threshold_used:X,source:$?.source,detail:`peak vibe ${K} m/s\xB2 (axes ${J.join("/")})${z}${B}`})};function l0(Z,$,Q){let J=Object.values(Z);if($<=0||J.length===0)return"";let X=[...J].sort((z,B)=>B-z),Y=X[0],K=X.length>1?X[1]:0;if(X.length>=2&&K>0&&Y>=2*K)return" | cause: likely sensor/mounting alignment (one axis dominates)";let W=X.length>=2&&Y<=1.5*(X[X.length-1]||Y);if(Q>0||W)return" | cause: likely mechanical degradation (props/bearings/frame)";return""}var n0=(Z)=>{let $=Z.thresholds.mag;if(!Z.hasTable("MAG"))return q("mag",Z.thresholds);if(!C(Z,"MAG","Health"))return q("mag",Z.thresholds,"MAG missing Health column");let Q=$?.min??1,J=P(Z,'SELECT MIN("Health") FROM "MAG"');return D({check:"mag",thresholds:Z.thresholds,failed:J!==null&&J<Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source})},c0=(Z)=>{let $=Z.thresholds.ekf,Q=Y$(Z,["XKF3","NKF3"]);if(!Q)return q("ekf",Z.thresholds);let J=y(Z,Q),X=["IVN","IVE","IVD","IVT","IPN","IPE","IPD"].filter((G)=>J.has(G));if(X.length===0)return q("ekf",Z.thresholds,`${Q} has no IVN/IVE/IVD/IVT/IPN/IPE/IPD columns`);let Y=$?.max_ratio??0.5,K=-1/0;for(let G of X){let W=P(Z,`SELECT MAX(ABS(${w(G)})) FROM ${w(Q)}`);if(W!==null&&W>K)K=W}if(K===-1/0)K=0;return D({check:"ekf",thresholds:Z.thresholds,failed:K>Y,failSeverity:"HIGH",value:K,threshold_used:Y,source:$?.source,detail:`peak innovation ${K} (${Q}: ${X.join("/")})`})},i0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return q("min_cell_voltage",Z.thresholds);if(!C(Z,"BAT","Volt"))return q("min_cell_voltage",Z.thresholds,"BAT missing Volt column");let Q=$?.min_cell_v??3.3,J=P(Z,'SELECT MAX("Volt") FROM "BAT"'),X=P(Z,'SELECT MIN("Volt") FROM "BAT"');if(J===null||X===null||J<=0)return q("min_cell_voltage",Z.thresholds,"BAT.Volt has no usable values");let Y=Math.max(1,Math.round(J/4.2)),K=X/Y;return D({check:"min_cell_voltage",thresholds:Z.thresholds,failed:K<Q,failSeverity:"CRITICAL",value:K,threshold_used:Q,source:$?.source,detail:`${Y}S pack (MAX pack ${J.toFixed(2)}V), min cell ${K.toFixed(3)}V vs floor ${Q}V`})},o0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return q("voltage_sag",Z.thresholds);if(!C(Z,"BAT","VoltR")||!C(Z,"BAT","Volt"))return q("voltage_sag",Z.thresholds,"BAT missing VoltR/Volt columns");let Q=$?.max_sag_v??2,J=P(Z,'SELECT MAX("VoltR"-"Volt") FROM "BAT"');return D({check:"voltage_sag",thresholds:Z.thresholds,failed:J!==null&&J>Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source,detail:`peak sag ${(J??0).toFixed(2)}V (VoltR-Volt) vs ${Q}V`})},r0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return q("over_current",Z.thresholds);if(!C(Z,"BAT","Curr"))return q("over_current",Z.thresholds,"BAT missing Curr column");let Q=$?.max_current_a??200,J=P(Z,'SELECT MAX("Curr") FROM "BAT"');return D({check:"over_current",thresholds:Z.thresholds,failed:J!==null&&J>Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source,detail:`peak current ${(J??0).toFixed(1)}A vs ${Q}A`})},a0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return q("internal_resistance",Z.thresholds);if(!C(Z,"BAT","Res"))return q("internal_resistance",Z.thresholds,"BAT missing Res column");let Q=P(Z,'SELECT MAX("Res") FROM "BAT"');if(Q===null||Q===0)return q("internal_resistance",Z.thresholds,"BAT.Res all zero/absent (not reported)");let J=$?.res_min_current_a??5,Y=C(Z,"BAT","Curr")?P(Z,`SELECT MAX("Res") FROM "BAT" WHERE "Curr" >= ${J}`):Q;if(Y===null)return q("internal_resistance",Z.thresholds,`no BAT samples at Curr \u2265 ${J} A \u2014 Res only valid under load (max raw ${Q.toFixed(3)}\u03A9 is a low-current artifact)`);let K=$?.max_res_ohm??0.03;return D({check:"internal_resistance",thresholds:Z.thresholds,failed:Y>K,failSeverity:"HIGH",value:Y,threshold_used:K,source:$?.source,detail:`peak internal resistance ${Y.toFixed(4)}\u03A9 (at Curr \u2265 ${J} A) vs ${K}\u03A9`})},s0=(Z)=>{let $=Z.thresholds.power;if(!Z.hasTable("POWR"))return q("board_brownout",Z.thresholds);if(!C(Z,"POWR","Vcc"))return q("board_brownout",Z.thresholds,"POWR missing Vcc column");let Q=$?.min_vcc_v??4.5,J=P(Z,'SELECT MIN("Vcc") FROM "POWR"');return D({check:"board_brownout",thresholds:Z.thresholds,failed:J!==null&&J<Q,failSeverity:"CRITICAL",value:J,threshold_used:Q,source:$?.source,detail:`min board Vcc ${(J??0).toFixed(3)}V vs floor ${Q}V`})},t0=(Z)=>{let $=Z.thresholds.rate;if(!Z.hasTable("RATE"))return q("rate_tracking",Z.thresholds);let Q=y(Z,"RATE"),X=[["RDes","R"],["PDes","P"],["YDes","Y"]].filter(([W,z])=>Q.has(W)&&Q.has(z));if(X.length===0)return q("rate_tracking",Z.thresholds,"RATE has no RDes/R, PDes/P, YDes/Y pairs");let Y=$?.max_err_dps??50,K=-1/0,G="";for(let[W,z]of X){let B=P(Z,`SELECT MAX(ABS(${w(W)}-${w(z)})) FROM "RATE"`);if(B!==null&&B>K)K=B,G=W.replace("Des","")}if(K===-1/0)K=0;return D({check:"rate_tracking",thresholds:Z.thresholds,failed:K>Y,failSeverity:"HIGH",value:K,threshold_used:Y,source:$?.source,detail:`peak rate-tracking error ${K.toFixed(2)} deg/s (axis ${G||"?"}) vs ${Y} deg/s`})},e0=(Z)=>{let $=Z.thresholds.gps;if(!Z.hasTable("GPA"))return q("gps_accuracy",Z.thresholds);if(!C(Z,"GPA","HAcc"))return q("gps_accuracy",Z.thresholds,"GPA missing HAcc column");let Q=$?.hacc_max_m??5,J=P(Z,'SELECT MAX("HAcc") FROM "GPA"');return D({check:"gps_accuracy",thresholds:Z.thresholds,failed:J!==null&&J>Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source,detail:`peak horizontal accuracy ${(J??0).toFixed(2)}m vs ${Q}m`})},Z8=(Z)=>{let $=Z.thresholds.ekf,Q=Y$(Z,["XKF4","NKF4"]);if(!Q)return q("ekf_variance",Z.thresholds);let J=y(Z,Q),X=["SV","SP","SH","SM"].filter((B)=>J.has(B));if(X.length===0)return q("ekf_variance",Z.thresholds,`${Q} has no SV/SP/SH/SM columns`);let Y=$?.max_variance??1,K=-1/0,G="";for(let B of X){let V=P(Z,`SELECT MAX(${w(B)}) FROM ${w(Q)}`);if(V!==null&&V>K)K=V,G=B}if(K===-1/0)K=0;let W=J.has("FS")?P(Z,`SELECT MAX("FS") FROM ${w(Q)}`):null,z=W!==null&&W>0?` | EKF failsafe set (FS MAX=${W})`:"";return D({check:"ekf_variance",thresholds:Z.thresholds,failed:K>Y,failSeverity:"HIGH",value:K,threshold_used:Y,source:$?.source,detail:`peak EKF variance ${K.toFixed(3)} (${Q}: ${G||X.join("/")}) vs ${Y}${z}`})},$8=(Z)=>{let $=Z.thresholds.mag;if(!Z.hasTable("MAG"))return q("mag_offsets",Z.thresholds);let Q=y(Z,"MAG"),J=["OfsX","OfsY","OfsZ"].filter((G)=>Q.has(G));if(J.length===0)return q("mag_offsets",Z.thresholds,"MAG has no OfsX/OfsY/OfsZ columns");let X=$?.max_offset??600,Y=-1/0,K="";for(let G of J){let W=P(Z,`SELECT MAX(ABS(${w(G)})) FROM "MAG"`);if(W!==null&&W>Y)Y=W,K=G}if(Y===-1/0)Y=0;return D({check:"mag_offsets",thresholds:Z.thresholds,failed:Y>X,failSeverity:"HIGH",value:Y,threshold_used:X,source:$?.source,detail:`peak mag offset ${Y.toFixed(0)} (${K||J.join("/")}) vs ${X}`})},J8=(Z)=>{let $=Z.thresholds.esc;if(!Z.hasTable("ESC"))return q("esc_health",Z.thresholds);let Q=y(Z,"ESC"),J=["Temp","MotTemp"].filter((V)=>Q.has(V));if(J.length===0&&!Q.has("Err"))return q("esc_health",Z.thresholds,"ESC has no Temp/MotTemp/Err columns");let X=$?.max_temp_c??90,Y=-1/0,K="";for(let V of J){let U=P(Z,`SELECT MAX(${w(V)}) FROM "ESC"`);if(U!==null&&U>Y)Y=U,K=V}if(Y===-1/0)Y=0;let G=Q.has("Err")?P(Z,'SELECT MAX("Err") FROM "ESC"')??0:0,W=J.length>0&&Y>X,z=G>0,B=z?` | ESC Err MAX=${G}`:"";return D({check:"esc_health",thresholds:Z.thresholds,failed:W||z,failSeverity:"HIGH",value:Y,threshold_used:X,source:$?.source,detail:`peak ESC temp ${Y.toFixed(1)}\xB0C (${K||"n/a"}) vs ${X}\xB0C${B}`})},Q8=(Z)=>{if(!$Z(Z.vehicleType))return q("thrust_margin",Z.thresholds,`n/a for ${Z.vehicleType} (lift/T:W is a rotorcraft metric)`);if(!Z.hasTable("MOTB"))return q("thrust_margin",Z.thresholds);let $=y(Z,"MOTB");if(!$.has("FailFlags")&&!($.has("ThrOut")&&$.has("LiftMax")))return q("thrust_margin",Z.thresholds,"MOTB missing FailFlags and ThrOut/LiftMax columns");let Q=$.has("FailFlags")?P(Z,'SELECT MAX("FailFlags") FROM "MOTB"')??0:0,J=null;if($.has("ThrOut")&&$.has("LiftMax")){let G=P(Z,'SELECT AVG("ThrOut") FROM "MOTB"'),W=P(Z,'SELECT AVG("LiftMax") FROM "MOTB"');if(G!==null&&W!==null&&W>0)J=G/W}let X=J!==null&&J>0.5,Y=Q>0,K=`MOTB FailFlags MAX=${Q}`+(J!==null?`, mean ThrOut/LiftMax ${J.toFixed(2)} (low-margin > 0.5)`:"")+(Y?" \u2014 THRUST-LOSS flagged":X?" \u2014 low thrust margin":"");return D({check:"thrust_margin",thresholds:Z.thresholds,failed:Y||X,failSeverity:"HIGH",value:J,threshold_used:0.5,detail:K})},X8=(Z)=>{let $=Z.thresholds.imu;if(!Z.hasTable("IMU"))return q("imu_health",Z.thresholds);let Q=y(Z,"IMU"),J=["EG","EA"].filter((G)=>Q.has(G));if(J.length===0)return q("imu_health",Z.thresholds,"IMU has no EG/EA error-count columns");let X=$?.max_err??0,Y=-1/0,K="";for(let G of J){let W=P(Z,`SELECT MAX(${w(G)}) FROM "IMU"`);if(W!==null&&W>Y)Y=W,K=G}if(Y===-1/0)Y=0;return D({check:"imu_health",thresholds:Z.thresholds,failed:Y>X,failSeverity:"HIGH",value:Y,threshold_used:X,source:$?.source,detail:`max IMU error count ${Y} (${K||J.join("/")}) vs ${X}`})},Y8=(Z)=>{let $=Z.thresholds.imu;if(!Z.hasTable("IMU"))return q("imu_consistency",Z.thresholds);let Q=y(Z,"IMU");if(!Q.has("I"))return q("imu_consistency",Z.thresholds,"IMU has no instance column I");let J=["AccX","AccY","AccZ"].filter((_)=>Q.has(_)),X=["GyrX","GyrY","GyrZ"].filter((_)=>Q.has(_));if(J.length===0&&X.length===0)return q("imu_consistency",Z.thresholds,"IMU has no Acc*/Gyr* columns");let Y=[...J,...X].map((_)=>`AVG(${w(_)}) ${w(_)}`).join(", "),K=Z.db.query(`SELECT "I" AS "I", ${Y} FROM "IMU" GROUP BY "I"`).all();if(K.length<2)return q("imu_consistency",Z.thresholds,`only ${K.length} IMU instance \u2014 nothing to cross-check`);let G=(_)=>{let T=K.map((M)=>Number(M[_])).filter((M)=>Number.isFinite(M));return T.length>=2?Math.max(...T)-Math.min(...T):0},W=0,z="";for(let _ of J){let T=G(_);if(T>W)W=T,z=_}let B=0,V="";for(let _ of X){let T=G(_);if(T>B)B=T,V=_}let U=$?.accel_consistency_max??0.7,L=$?.gyro_consistency_max??0.3,A=W>U,F=B>L,H=A||F,O=W/U>=B/L?W:B,E=W/U>=B/L?U:L,N=H?`INCONSISTENT \u2014 ${A?`accel ${z} spread ${W.toFixed(3)} m/s\xB2 > ${U}`:""}${A&&F?"; ":""}${F?`gyro ${V} spread ${B.toFixed(3)} rad/s > ${L}`:""}`:`IMUs consistent \u2014 max inter-instance bias spread: accel ${W.toFixed(3)} m/s\xB2 (<${U}), gyro ${B.toFixed(3)} rad/s (<${L}); relative differences between instances are within healthy tolerance`;return D({check:"imu_consistency",thresholds:Z.thresholds,failed:H,failSeverity:"HIGH",value:O,threshold_used:E,source:{kind:"rule-of-thumb",ref:"IMU inconsistency tolerance (~ArduPilot arming limits)"},detail:N})},K8=(Z)=>{if(!Z.hasTable("ERR"))return q("err_events",Z.thresholds);if(!C(Z,"ERR","Subsys")||!C(Z,"ERR","ECode"))return q("err_events",Z.thresholds,"ERR present but Subsys/ECode columns absent");let $=Z.thresholds.battery?.err_subsys??6,Q=Z.db.query(`SELECT "Subsys" AS subsys, "ECode" AS ecode, COUNT(*) AS n FROM "ERR" WHERE "ECode" <> 0 AND "Subsys" <> ${$} GROUP BY "Subsys", "ECode" ORDER BY "Subsys", "ECode"`).all(),J=0,X=[];for(let Y of Q)J+=Number(Y.n),X.push(`Subsys=${Y.subsys} ECode=${Y.ecode} \xD7${Y.n}`);return D({check:"err_events",thresholds:Z.thresholds,failed:J>0,failSeverity:"HIGH",value:J,threshold_used:0,detail:J>0?`${J} non-battery ERR event(s): ${X.join(", ")}`:"no non-battery ERR events"})},G8=new Set([6,9]),W8=(Z)=>{if(!Z.hasTable("MODE"))return q("mode_changes",Z.thresholds);if(!C(Z,"MODE","ModeNum"))return q("mode_changes",Z.thresholds,"MODE missing ModeNum column");let $=Z.db.query('SELECT "ModeNum" AS modenum FROM "MODE"').all(),Q=$.length,J=Q>0?Q-1:0,X=new Map,Y=!1;for(let G of $){let W=Number(G.modenum);if(X.set(W,(X.get(W)??0)+1),G8.has(W))Y=!0}let K=[...X.entries()].sort((G,W)=>G[0]-W[0]).map(([G,W])=>`Mode${G}\xD7${W}`).join(", ");return D({check:"mode_changes",thresholds:Z.thresholds,failed:Y,failSeverity:"HIGH",value:J,threshold_used:0,detail:`${J} mode change(s); timeline: ${K||"\u2014"}`+(Y?" \u2014 emergency mode (RTL/LAND) entered":"")})},z8=(Z)=>{let $=Z.thresholds.airspeed;if(!Q$(Z.vehicleType))return q("airspeed",Z.thresholds,`n/a for ${Z.vehicleType} (no fixed-wing airspeed)`);if(!Z.hasTable("ARSP"))return q("airspeed",Z.thresholds,"no ARSP (airspeed) data");if(!C(Z,"ARSP","Airspeed"))return q("airspeed",Z.thresholds,"ARSP missing Airspeed column");let Q=$?.stall_min_ms??8,J={kind:"rule-of-thumb",ref:"default stall floor"};if(Z.hasTable("PARM")){let Y=P(Z,`SELECT "Value" FROM "PARM" WHERE "Name"='ARSPD_FBW_MIN' LIMIT 1`);if(Y!==null&&Y>0)Q=Y,J={kind:"datasheet",ref:"ARSPD_FBW_MIN"}}let X=P(Z,'SELECT MIN("Airspeed") FROM "ARSP" WHERE "Airspeed" > 2');if(X===null)return q("airspeed",Z.thresholds,"no airborne airspeed samples");return D({check:"airspeed",thresholds:Z.thresholds,failed:X<Q,failSeverity:"HIGH",value:X,threshold_used:Q,source:J,detail:`min in-flight airspeed ${X.toFixed(1)} m/s vs stall floor ${Q} m/s`})},B8=[{id:"battery",run:f0},{id:"min_cell_voltage",run:i0},{id:"voltage_sag",run:o0},{id:"over_current",run:r0},{id:"internal_resistance",run:a0},{id:"board_brownout",run:s0},{id:"attitude",run:g0},{id:"rate_tracking",run:t0},{id:"altitude",run:u0},{id:"hover",run:x0},{id:"rcou",run:m0},{id:"rcin",run:d0},{id:"thrust_margin",run:Q8},{id:"vibration",run:p0},{id:"imu_health",run:X8},{id:"imu_consistency",run:Y8},{id:"mag",run:n0},{id:"mag_offsets",run:$8},{id:"gps_accuracy",run:e0},{id:"ekf",run:c0},{id:"ekf_variance",run:Z8},{id:"esc_health",run:J8},{id:"airspeed",run:z8},{id:"err_events",run:K8},{id:"mode_changes",run:W8}];function K$(Z,$,Q){let J=new Set;try{let K=Z.query("SELECT name FROM sqlite_master WHERE type='table'").all();for(let G of K)J.add(G.name)}catch{}let X={db:Z,thresholds:$,hasTable:(K)=>J.has(K),vehicleType:Q?.vehicleType},Y=[];for(let{id:K,run:G}of B8)try{Y.push(G(X))}catch(W){let z=W instanceof Error?W.message:String(W);Y.push(q(K,$,`check error: ${z}`))}return Y}import{existsSync as q8,readFileSync as A8}from"fs";import{join as E8}from"path";import{existsSync as V8,readFileSync as U8}from"fs";import{dirname as H8,join as PZ}from"path";import{fileURLToPath as L8}from"url";var G$=H8(L8(import.meta.url)),O8=[PZ(G$,"..","data","apm-params.json"),PZ(G$,"..","..","data","apm-params.json")];function z$(Z){if(Z){let $=PZ(Z,".hydron","apm-params.json"),Q=W$($);if(Q&&Q.size>0)return Q}for(let $ of O8){let Q=W$($);if(Q)return Q}return new Map}function B$(Z,$){return $.get(Z.toUpperCase())}function W$(Z){if(!V8(Z))return;try{let $=U8(Z,"utf8"),Q=JSON.parse($);if(!Q||typeof Q!=="object")return;let J=new Map;for(let[X,Y]of Object.entries(Q)){if(!X||X.startsWith("_")||typeof Y!=="object"||Y===null)continue;let K={name:X};if(typeof Y.min==="number"&&Number.isFinite(Y.min))K.min=Y.min;if(typeof Y.max==="number"&&Number.isFinite(Y.max))K.max=Y.max;if(typeof Y.units==="string"&&Y.units)K.units=Y.units;J.set(X.toUpperCase(),K)}return J}catch{return}}function U$(){return{ruleset_version:"v1",safety:{ARMING_CHECK:{min:1},FS_THR_ENABLE:{min:1},FENCE_ENABLE:{min:1}},range:{INS_GYRO_FILTER:{min:5,max:80,severity:"HIGH"},ATC_RAT_RLL_P:{min:0,max:0.5,severity:"HIGH"},ATC_RAT_PIT_P:{min:0,max:0.5,severity:"HIGH"},ATC_RAT_YAW_P:{min:0,max:1,severity:"HIGH"},MOT_SPIN_ARM:{min:0,max:0.2,severity:"HIGH"},MOT_SPIN_MIN:{min:0,max:0.3,severity:"HIGH"},RTL_ALT:{min:200,max:1e4,severity:"HIGH"},WPNAV_SPEED:{min:50,max:2000,severity:"HIGH"}}}}function T8(Z){let $=U$(),Q=E8(Z,".hydron","param-rules.yaml");if(!q8(Q))return $;try{let J=A8(Q,"utf8");return F8($,J)}catch{return $}}function F8(Z,$){let Q={ruleset_version:Z.ruleset_version,safety:{...Z.safety},range:{...Z.range}},J=null;for(let X of $.split(/\r?\n/)){let K=X.replace(/#.*$/,"").trim();if(!K||K.startsWith("-"))continue;let G=/^ruleset_version\s*:\s*(.+)$/.exec(K);if(G){Q.ruleset_version=G[1].trim().replace(/^["']|["']$/g,""),J=null;continue}if(/^safety\s*:\s*$/.test(K)){J="safety";continue}if(/^range\s*:\s*$/.test(K)){J="range";continue}let W=/^([A-Za-z0-9_]+)\s*:\s*\{(.+)\}\s*$/.exec(K);if(W&&J){let z=W[1],B=N8(W[2]);if(J==="safety"){let V=B.min;if(typeof V==="number")Q.safety[z]={min:V}}else{let{min:V,max:U,severity:L}=B;if(typeof V==="number"&&typeof U==="number")Q.range[z]={min:V,max:U,severity:L==="CRITICAL"?"CRITICAL":"HIGH"}}}}return Q}function N8(Z){let $={};for(let Q of Z.split(",")){let J=/^\s*([A-Za-z0-9_]+)\s*:\s*(.+?)\s*$/.exec(Q);if(!J)continue;let X=J[1],Y=J[2].replace(/^["']|["']$/g,""),K=Number(Y);$[X]=Number.isFinite(K)&&Y!==""?K:Y}return $}function _8(Z){let $=new Map;for(let Q of Z)$.set(Q.name.toUpperCase(),Q.value);return $}function h(Z){return{check:Z.check,severity:Z.severity,value:Z.value,threshold_used:Z.threshold_used,ruleset_version:Z.ruleset_version,verdict:Z.verdict,tier:"system",source:Z.source,detail:Z.detail}}function JZ(Z,$={}){let Q=$.configDir?T8($.configDir):U$(),J=$.ruleset_version??Q.ruleset_version,X=_8(Z),Y=z$($.configDir);return[...R8(X,Q,J),...P8(X,J),...j8(X,Q,J,Y),...I8(Z,null)]}function R8(Z,$,Q){let J=[],X={kind:"rule-of-thumb"},Y=Z.get("ARMING_CHECK");if(Y===void 0)J.push(f("param.safety.arming",Q,"ARMING_CHECK absent"));else{let B=Y!==0;J.push(h({check:"param.safety.arming",severity:B?"OK":"CRITICAL",value:Y,threshold_used:$.safety.ARMING_CHECK?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:Q,detail:B?"arming checks enabled":"ARMING_CHECK=0 \u2014 ALL pre-arm safety checks are disabled",source:X}))}let K=Z.get("BATT_LOW_VOLT"),G=Z.get("BATT_CRT_VOLT");if(K===void 0&&G===void 0)J.push(f("param.safety.battery_failsafe",Q,"BATT_LOW_VOLT/BATT_CRT_VOLT absent"));else{let B=(K??0)===0&&(G??0)===0;J.push(h({check:"param.safety.battery_failsafe",severity:B?"CRITICAL":"OK",value:B?0:G??K??0,threshold_used:0,verdict:B?"FAIL":"PASS",ruleset_version:Q,detail:B?"battery failsafe not configured (BATT_LOW_VOLT and BATT_CRT_VOLT both 0)":"battery failsafe configured",source:X}))}let W=Z.get("FS_THR_ENABLE");if(W===void 0)J.push(f("param.safety.radio_failsafe",Q,"FS_THR_ENABLE absent"));else{let B=W!==0;J.push(h({check:"param.safety.radio_failsafe",severity:B?"OK":"HIGH",value:W,threshold_used:$.safety.FS_THR_ENABLE?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:Q,detail:B?"radio failsafe enabled":"FS_THR_ENABLE=0 \u2014 radio/throttle failsafe is OFF",source:X}))}let z=Z.get("FENCE_ENABLE");if(z===void 0)J.push(f("param.safety.fence",Q,"FENCE_ENABLE absent"));else{let B=z!==0;J.push(h({check:"param.safety.fence",severity:B?"OK":"HIGH",value:z,threshold_used:$.safety.FENCE_ENABLE?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:Q,detail:B?"geofence enabled":"FENCE_ENABLE=0 \u2014 geofence disabled (advisory)",source:X}))}return J}function P8(Z,$){let Q=[],J={kind:"rule-of-thumb"},X=Z.get("BATT_CRT_VOLT"),Y=D8(Z);if(X===void 0||X===0||Y===null||Y<=0)Q.push(f("param.consistency.battery_cells",$,"BATT_CRT_VOLT and a cell-count source required"));else{let z=X/Y,B=z>=3&&z<=3.7;Q.push(h({check:"param.consistency.battery_cells",severity:B?"OK":"HIGH",value:DZ(z,3),threshold_used:3,verdict:B?"PASS":"FAIL",ruleset_version:$,detail:B?`per-cell critical voltage ${DZ(z,2)}V across ${Y}S looks sane`:`per-cell critical voltage ${DZ(z,2)}V (${Y}S) outside [3.0,3.7] \u2014 cell count vs BATT_CRT_VOLT inconsistent`,source:J}))}let K=Z.get("FRAME_CLASS"),G=Z.get("FRAME_TYPE");if(K===void 0&&G===void 0)Q.push(f("param.consistency.frame",$,"FRAME_CLASS/FRAME_TYPE absent"));else{let z=(K??0)!==0&&(G??0)!==0;Q.push(h({check:"param.consistency.frame",severity:z?"OK":"HIGH",value:K??null,threshold_used:1,verdict:z?"PASS":"FAIL",ruleset_version:$,detail:z?`frame configured (CLASS=${K}, TYPE=${G})`:`frame not fully configured (CLASS=${K??"?"}, TYPE=${G??"?"}) \u2014 must be non-zero for a copter`,source:J}))}let W=Z.get("INS_HNTCH_ENABLE");if(W===void 0)Q.push(f("param.consistency.notch_filter",$,"INS_HNTCH_ENABLE absent"));else Q.push(h({check:"param.consistency.notch_filter",severity:"OK",value:W,threshold_used:null,verdict:"PASS",ruleset_version:$,detail:W===0?"harmonic notch filter disabled (INS_HNTCH_ENABLE=0) \u2014 advisory; tune if vibration is high":"harmonic notch filter enabled",source:J}));return Q}function D8(Z){for(let Q of["BATT_CELLS","BATT_N_CELLS","BATT_CELL_COUNT"]){let J=Z.get(Q);if(J!==void 0&&J>0)return Math.round(J)}let $=Z.get("BATT_ARM_VOLT");if($!==void 0&&$>0)return Math.round($/3.6);return null}function j8(Z,$,Q,J){let X=[];for(let[Y,K]of Object.entries($.range)){let G=Z.get(Y.toUpperCase()),W=`param.range.${Y}`;if(G===void 0){X.push(f(W,Q,`${Y} absent`));continue}let z=B$(Y,J),B=z!==void 0&&(typeof z.min==="number"||typeof z.max==="number"),V=B&&typeof z.min==="number"?Math.max(K.min,z.min):K.min,U=B&&typeof z.max==="number"?Math.min(K.max,z.max):K.max,L=B&&typeof z.min==="number"?z.min:K.min,A=B&&typeof z.max==="number"?z.max:K.max,F=B?{kind:"datasheet",ref:"apm.pdef.xml"}:{kind:"tuned"},H=G>=V&&G<=U;X.push(h({check:W,severity:H?"OK":K.severity,value:G,threshold_used:H?A:G<V?L:A,verdict:H?"PASS":"FAIL",ruleset_version:Q,detail:H?`${Y}=${G} within [${L},${A}]${B?" (apm.pdef.xml)":""}`:`${Y}=${G} outside [${L},${A}]${B?" (apm.pdef.xml)":""}`,source:F}))}return X}function I8(Z,$){return[]}function f(Z,$,Q){return h({check:Z,severity:"OK",value:null,threshold_used:null,verdict:"SKIP",ruleset_version:$,detail:Q})}function DZ(Z,$){let Q=10**$;return Math.round(Z*Q)/Q}function H$(Z){if(Z.length===0)return"Parameter audit: no findings.";let $=(W)=>{if(W.verdict==="SKIP")return"\u2013";if(W.verdict==="PASS")return"\u2713";return W.severity==="CRITICAL"?"\u2717 CRIT":"\u2717 HIGH"},Q=Z.filter((W)=>W.verdict==="FAIL"),J=Z.filter((W)=>W.verdict==="PASS"),X=Z.filter((W)=>W.verdict==="SKIP"),Y=Q.filter((W)=>W.severity==="CRITICAL").length,K=[];K.push(`Parameter audit \u2014 ${Z.length} checks: ${J.length} pass, ${Q.length} fail (${Y} critical), ${X.length} skipped`);let G=[...Z].sort((W,z)=>V$(W)-V$(z));for(let W of G){let z=W.detail?` \u2014 ${W.detail}`:"";K.push(` ${$(W).padEnd(7)} ${W.check}${z}`)}return K.join(`
9
- `)}function V$(Z){if(Z.verdict==="FAIL")return Z.severity==="CRITICAL"?0:1;if(Z.verdict==="PASS")return 2;return 3}import{existsSync as C8,readFileSync as M8}from"fs";import{join as S8}from"path";function w8(){return{ruleset_version:"v1",vibration:{field:"VibeX|VibeY|VibeZ",max_ms2:30,source:{kind:"rule-of-thumb"}},clipping:{field:"Clip",max:0},battery:{err_subsys:6,err_ecode:1,min_cell_v:3.3,max_sag_v:2,max_current_a:200,max_res_ohm:0.03,res_min_current_a:5,source:{kind:"rule-of-thumb"}},attitude:{fields:"DesRoll-Roll,DesPitch-Pitch",max_deg:5,window_samples:10},altitude:{field:"DAlt-Alt",max_m:2},hover:{field:"ThH",band:[0.25,0.75]},ekf:{fields:"IVN,IVE,IVD",max_ratio:0.5,max_variance:1},mag:{field:"Health",min:1,late_align_s:30,max_offset:600},gps:{field:"NSats",min:6,hdop_max:2,hacc_max_m:5},power:{min_vcc_v:4.5,source:{kind:"rule-of-thumb"}},rate:{max_err_dps:50,source:{kind:"rule-of-thumb"}},esc:{max_temp_c:90,source:{kind:"rule-of-thumb"}},imu:{max_err:0,accel_consistency_max:0.7,gyro_consistency_max:0.3,source:{kind:"rule-of-thumb"}},airspeed:{stall_min_ms:8,source:{kind:"rule-of-thumb"}}}}function O$(Z){let $=w8(),Q=S8(Z,".hydron","uav-thresholds.yaml");if(!C8(Q))return $;try{let J=y8(M8(Q,"utf8"));return q$($,J)}catch{return $}}function q$(Z,$){let Q={...Z};for(let[J,X]of Object.entries($)){let Y=Q[J];if(X&&typeof X==="object"&&!Array.isArray(X)&&Y&&typeof Y==="object"&&!Array.isArray(Y))Q[J]=q$(Y,X);else Q[J]=X}return Q}function L$(Z){let $=!1,Q=!1;for(let J=0;J<Z.length;J++){let X=Z[J];if(X==="'"&&!Q)$=!$;else if(X==='"'&&!$)Q=!Q;else if(X==="#"&&!$&&!Q)return Z.slice(0,J)}return Z}function A$(Z,$){let Q=[],J=0,X=!1,Y=!1,K=0;for(let G=0;G<Z.length;G++){let W=Z[G];if(W==="'"&&!Y)X=!X;else if(W==='"'&&!X)Y=!Y;else if(!X&&!Y){if(W==="["||W==="{")J++;else if(W==="]"||W==="}")J--;else if(W===$&&J===0)Q.push(Z.slice(K,G)),K=G+1}}return Q.push(Z.slice(K)),Q}function QZ(Z){let $=Z.trim();if($==="")return"";if(/^-?\d+(\.\d+)?$/.test($))return Number($);if($==="true")return!0;if($==="false")return!1;if($.startsWith("[")&&$.endsWith("]")){let Q=$.slice(1,-1).trim();return Q?A$(Q,",").map(QZ):[]}if($.startsWith("{")&&$.endsWith("}"))return E$($);return $.replace(/^["']|["']$/g,"")}function E$(Z){let $=Z.trim().replace(/^\{/,"").replace(/\}$/,""),Q={};for(let J of A$($,",")){let X=J.indexOf(":");if(X<0)continue;let Y=J.slice(0,X).trim().replace(/^["']|["']$/g,"");if(Y)Q[Y]=QZ(J.slice(X+1))}return Q}function y8(Z){let $={},Q=Z.split(/\r?\n/),J=0;while(J<Q.length){let X=L$(Q[J]);if(J++,!X.trim()||/^\s/.test(X))continue;let Y=/^([A-Za-z0-9_]+):\s*(.*)$/.exec(X);if(!Y)continue;let K=Y[1],G=Y[2].trim();if(G.startsWith("{"))$[K]=E$(G);else if(G!=="")$[K]=QZ(G);else{let W={};while(J<Q.length){let z=L$(Q[J]);if(!z.trim()){J++;continue}if(!/^\s/.test(z))break;J++;let B=/^\s+([A-Za-z0-9_]+):\s*(.*)$/.exec(z);if(B&&B[2].trim()!=="")W[B[1]]=QZ(B[2])}$[K]=W}}return $}function j(Z,$){let Q=$.toLowerCase();return Z.columns.findIndex((J)=>J.toLowerCase()===Q)}function v(Z){if(typeof Z==="number")return Number.isFinite(Z)?Z:null;if(typeof Z==="bigint")return Number(Z);if(typeof Z==="string"){let $=Number(Z);return Number.isFinite($)?$:null}return null}function k8(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z.trim()))try{return BigInt(Z.trim())}catch{return null}return null}function x(Z){if(Z===null||Z===void 0)return null;if(typeof Z==="string")return Z;return String(Z)}function XZ(Z){return`"${Z.replace(/"/g,'""')}"`}function MZ(Z,$){try{return!!Z.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get($)}catch{return!1}}function SZ(Z,$){try{let Q=Z.query(`PRAGMA table_info(${XZ($)})`).all();return new Set(Q.map((J)=>J.name))}catch{return new Set}}function YZ(Z,$){try{let Q=Z.query($).get();if(!Q)return null;let J=Object.values(Q)[0];if(J===null||J===void 0)return null;let X=typeof J==="bigint"?Number(J):Number(J);return Number.isFinite(X)?X:null}catch{return null}}function v8(Z){for(let $ of["ATT","GPS","VIBE","CTUN","IMU"])if(MZ(Z,$)&&SZ(Z,$).has("TimeUS")){let Q=YZ(Z,`SELECT COUNT(*) AS c FROM ${XZ($)}`);if(Q===null||Q<=0)continue;let J=YZ(Z,`SELECT (MAX("TimeUS") - MIN("TimeUS")) AS d FROM ${XZ($)}`);if(J!==null)return J/1e6}return null}function h8(Z){if(!MZ(Z,"VIBE"))return null;let $=SZ(Z,"VIBE"),Q=["VibeX","VibeY","VibeZ"].filter((X)=>$.has(X));if(Q.length===0)return null;let J=Q.map((X)=>`SELECT MAX(${XZ(X)}) AS m FROM "VIBE"`).join(" UNION ALL ");return YZ(Z,`SELECT MAX(m) AS m FROM (${J})`)}function b8(Z){if(!MZ(Z,"CTUN")||!SZ(Z,"CTUN").has("ThH"))return null;return YZ(Z,'SELECT AVG("ThH") AS m FROM "CTUN"')}function jZ(Z,$){let Q=Z.tables.PARM;if(!Q)return null;let J=j(Q,"Name"),X=j(Q,"Value");if(J<0||X<0)return null;let Y=$.toLowerCase();for(let K of Q.rows)if(x(K[J])?.toLowerCase()===Y)return v(K[X]);return null}var f8={0:"Undefined",1:"Quad",2:"Hexa",3:"Octa",4:"OctaQuad",5:"Y6",6:"Heli",7:"Tri",8:"SingleCopter",9:"CoaxCopter",10:"BiCopter",11:"Heli_Dual",12:"DodecaHexa",13:"HeliQuad",14:"Deca",15:"Scripting"};function CZ(Z,$){let Q=Z.tables.MSG;if(!Q)return null;let J=j(Q,"Message")>=0?j(Q,"Message"):j(Q,"Msg");if(J<0)return null;for(let X of Q.rows){let Y=x(X[J]);if(Y&&$.test(Y))return Y}return null}var g8=/^([A-Za-z][A-Za-z0-9_+\-]*)\s+([0-9A-Fa-f]{8})\s+([0-9A-Fa-f]{8})\s+([0-9A-Fa-f]{8})\b/;function u8(Z){let $=Z.tables.MSG;if(!$)return null;let Q=j($,"Message")>=0?j($,"Message"):j($,"Msg");if(Q<0)return null;for(let J of $.rows){let X=x(J[Q]),Y=X&&g8.exec(X.trim());if(Y){let[,K,G,W,z]=Y;return{id:`${G}${W}${z}`.toUpperCase(),name:`${K}-${G.toUpperCase()}`}}}return null}function x8(Z){let $=u8(Z),Q=jZ(Z,"SYSID_THISMAV"),J,X;if($)J=$.id,X=$.name;else if(Q!==null)J=`sysid-${Q}`,X=CZ(Z,/ArduCopter|ArduPlane|ArduPilot|board/i)??J;else{let G=CZ(Z,/ArduCopter|ArduPlane|ArduPilot|ChibiOS|board/i);J=G??"unknown",X=G??J}let Y,K=jZ(Z,"Q_ENABLE");if(K!==null&&K>=1)Y="QuadPlane";else{let G=jZ(Z,"FRAME_CLASS");if(G!==null)Y=f8[G]??`class_${G}`}return{kind:"airframe",external_id:J,attrs:{name:X,frame:Y}}}function m8(Z){let $=Z.tables.GPS;if(!$)return null;let Q=j($,"GWk"),J=j($,"GMS");if(Q<0||J<0)return null;let X=315964800000,Y=18000,K=null;for(let W of $.rows){let z=v(W[Q]),B=v(W[J]);if(z===null||B===null||z<=0)continue;let V=X+z*7*24*3600*1000+B-Y;if(K===null||V<K)K=V}if(K===null)return null;let G=new Date(K);return Number.isNaN(G.getTime())?null:G.toISOString()}function d8(Z,$,Q){let J=CZ(Z,/ArduCopter|ArduPlane|ArduPilot/);return{kind:"analysis",source_hash:Q,logged_at:m8(Z),attrs:{firmware:J??null,duration_s:v8($),pilot_id:null,rc_id:null}}}function p8(Z){let $=Z.tables.PARM;if(!$)return[];let Q=j($,"Name"),J=j($,"Value");if(Q<0||J<0)return[];let X=j($,"Default"),Y=[];for(let K of $.rows){let G=x(K[Q]);if(!G)continue;let W=v(K[J]);if(W===null)continue;let z={name:G,value:W};if(X>=0){let B=v(K[X]);z.default=B}Y.push(z)}return Y}function IZ(Z,$){let Q=j(Z,"TimeUS");if(Q<0)return 0n;return k8($[Q])??0n}function l8(Z){let $=[],Q=Z.tables.ERR;if(Q){let Y=j(Q,"Subsys"),K=j(Q,"ECode");for(let G of Q.rows)$.push({at:IZ(Q,G),type:"ERR",payload:{Subsys:Y>=0?v(G[Y]):null,ECode:K>=0?v(G[K]):null}})}let J=Z.tables.MODE;if(J){let Y=j(J,"TimeUS");for(let K of J.rows){let G={};J.columns.forEach((W,z)=>{if(z===Y)return;G[W]=v(K[z])??x(K[z])}),$.push({at:IZ(J,K),type:"MODE",payload:G})}}let X=Z.tables.MSG;if(X){let Y=j(X,"Message")>=0?j(X,"Message"):j(X,"Msg");for(let K of X.rows)$.push({at:IZ(X,K),type:"MSG",payload:{text:Y>=0?x(K[Y]):null}})}return $}function n8(Z,$){let Q=[],J=h8($);if(J!==null)Q.push({name:"vibe_peak",value:J,unit:"m/s/s"});let X=Z.tables.ERR;if(X){let K=j(X,"Subsys"),G=j(X,"ECode");if(K>=0&&G>=0){let W=0;for(let z of X.rows)if(v(z[K])===6&&v(z[G])===1)W++;Q.push({name:"failsafe_count",value:W})}}let Y=b8($);if(Y!==null)Q.push({name:"hover_thr",value:Y});return Q}function T$(Z,$,Q){return{entity:x8(Z),episode:d8(Z,$,Q.sourceHash),params:p8(Z),events:l8(Z),measurements:n8(Z,$)}}import{mkdir as c8,readFile as wZ,writeFile as KZ,readdir as N$,stat as i8}from"fs/promises";import{dirname as _$,join as m}from"path";function R$(Z,$,Q){return m(Z,".hydron","observations",$,GZ(Q))}function GZ(Z){return Z.replace(/[^A-Za-z0-9._-]/g,"_")||"unknown"}function yZ(Z,$){return typeof $==="bigint"?$.toString():$}function P$(Z){if(Array.isArray(Z.events))Z.events=Z.events.map(($)=>{let Q=$.at,J;if(typeof Q==="bigint")J=Q;else if(typeof Q==="string"&&/^-?\d+$/.test(Q))J=BigInt(Q);else if(typeof Q==="number")J=BigInt(Math.trunc(Q));else J=0n;return{...$,at:J}});return Z}var F$={OK:0,HIGH:1,CRITICAL:2};function o8(Z){let $=Z.findings??[],Q=$.filter((K)=>K.verdict==="FAIL").length,J=$.filter((K)=>K.severity==="CRITICAL"&&K.verdict==="FAIL").length,X="OK";for(let K of $)if(F$[K.severity]>F$[X])X=K.severity;let Y=(K)=>Z.measurements?.find((G)=>G.name===K)?.value??null;return{source_hash:Z.episode.source_hash,logged_at:Z.episode.logged_at,ruleset_version:Z.ruleset_version,anomalies:Q,critical:J,vibe_peak:Y("vibe_peak"),failsafe_count:Y("failsafe_count"),worst_severity:X,...Z.tags&&Z.tags.length>0?{tags:Z.tags}:{}}}async function r8(Z){let $;try{$=await wZ(Z,"utf8")}catch{return[]}let Q=[];for(let J of $.split(`
10
- `)){let X=J.trim();if(!X)continue;try{Q.push(JSON.parse(X))}catch{}}return Q}async function kZ(Z,$){let Q=m(Z,"index.jsonl"),J=await r8(Q),X=o8($),Y=J.filter((K)=>K.source_hash!==X.source_hash);Y.push(X),await KZ(Q,Y.map((K)=>JSON.stringify(K)).join(`
7
+ (capped at ${JZ} rows)`:"";return`${$}
8
+ ${Q}${J}`}import{createHash as W6}from"crypto";import{open as g$,stat as z6}from"fs/promises";import{resolve as B6}from"path";function RZ(Z,$){let Q=Z.tables.PARM;if(!Q)return null;let J=Q.columns.findIndex((K)=>K.toLowerCase()==="name"),X=Q.columns.findIndex((K)=>K.toLowerCase()==="value");if(J<0||X<0)return null;let Y=$.toLowerCase();for(let K of Q.rows)if(String(K[J]).toLowerCase()===Y){let G=Number(K[X]);return Number.isFinite(G)?G:null}return null}function n0(Z){let $=Z.tables.MSG;if(!$)return"";let Q=$.columns.findIndex((J)=>/message|msg/i.test(J));if(Q<0)return"";for(let J of $.rows){let X=String(J[Q]??"");if(/Ardu(Copter|Plane|Sub|Rover)|PX4/i.test(X))return X}return""}function Y$(Z){let $=RZ(Z,"Q_ENABLE");if($!==null&&$>=1||Z.tables.QUIK||Z.tables.AETR){if(Z.tables.MOTB||RZ(Z,"FRAME_CLASS")!==null||Z.tables.ARSP)return"vtol"}if($!==null&&$>=1)return"vtol";let Q=n0(Z);if(/ArduPlane/i.test(Q))return"fixedwing";if(/ArduCopter/i.test(Q))return"multirotor";if(RZ(Z,"FRAME_CLASS")!==null)return"multirotor";if(Z.tables.ARSP&&!Z.tables.MOTB)return"fixedwing";if(Z.tables.MOTB||Z.tables.RATE)return"multirotor";return"unknown"}function QZ(Z){return Z===void 0||Z==="multirotor"||Z==="vtol"||Z==="unknown"}function K$(Z){return Z==="fixedwing"||Z==="vtol"}function S(Z){return`"${Z.replace(/"/g,'""')}"`}function j(Z,$){let Q=Z.db.query($).get();if(!Q)return null;let J=Object.values(Q)[0];if(J===null||J===void 0)return null;return Number(J)}function k(Z,$){try{let Q=Z.db.query(`PRAGMA table_info(${S($)})`).all();return new Set(Q.map((J)=>J.name))}catch{return new Set}}function C(Z,$,Q){return k(Z,$).has(Q)}function W$(Z,$){for(let Q of $)if(Z.hasTable(Q))return Q;return null}function q(Z,$,Q){return{check:Z,severity:"OK",value:null,threshold_used:null,ruleset_version:$.ruleset_version,verdict:"SKIP",tier:"system",...Q?{detail:Q}:{}}}function P(Z){return{check:Z.check,severity:Z.failed?Z.failSeverity:"OK",value:Z.value,threshold_used:Z.threshold_used,ruleset_version:Z.thresholds.ruleset_version,verdict:Z.failed?"FAIL":"PASS",tier:"system",...Z.source?{source:Z.source}:{},...Z.detail?{detail:Z.detail}:{}}}var c0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("ERR"))return q("battery",Z.thresholds);if(!C(Z,"ERR","Subsys")||!C(Z,"ERR","ECode"))return q("battery",Z.thresholds,"ERR present but Subsys/ECode columns absent");let Q=$?.err_subsys??6,J=$?.err_ecode??1,X=j(Z,`SELECT COUNT(*) FROM "ERR" WHERE "Subsys"=${Q} AND "ECode"=${J}`)??0;return P({check:"battery",thresholds:Z.thresholds,failed:X>0,failSeverity:"CRITICAL",value:X,threshold_used:0,source:$?.source,detail:X>0?`${X} battery failsafe ERR event(s) (Subsys=${Q} ECode=${J})`:void 0})},i0=(Z)=>{let $=Z.thresholds.attitude;if(!Z.hasTable("ATT"))return q("attitude",Z.thresholds);for(let K of["DesRoll","Roll","DesPitch","Pitch"])if(!C(Z,"ATT",K))return q("attitude",Z.thresholds,`ATT missing column ${K}`);let Q=$?.max_deg??5,J=j(Z,'SELECT MAX(ABS("DesRoll"-"Roll")) FROM "ATT"'),X=j(Z,'SELECT MAX(ABS("DesPitch"-"Pitch")) FROM "ATT"'),Y=Math.max(J??0,X??0);return P({check:"attitude",thresholds:Z.thresholds,failed:Y>Q,failSeverity:"HIGH",value:Y,threshold_used:Q,source:$?.source,detail:`peak roll err ${(J??0).toFixed(2)}\xB0, pitch err ${(X??0).toFixed(2)}\xB0`})},o0=(Z)=>{let $=Z.thresholds.altitude;if(!Z.hasTable("CTUN"))return q("altitude",Z.thresholds);if(!C(Z,"CTUN","DAlt")||!C(Z,"CTUN","Alt"))return q("altitude",Z.thresholds,"CTUN missing DAlt/Alt columns");let Q=$?.max_m??2,J=j(Z,'SELECT MAX(ABS("DAlt"-"Alt")) FROM "CTUN"');return P({check:"altitude",thresholds:Z.thresholds,failed:(J??0)>Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source})},r0=(Z)=>{let $=Z.thresholds.hover;if(!QZ(Z.vehicleType))return q("hover",Z.thresholds,`n/a for ${Z.vehicleType} (no hover throttle)`);if(!Z.hasTable("CTUN"))return q("hover",Z.thresholds);if(!C(Z,"CTUN","ThH"))return q("hover",Z.thresholds,"CTUN missing ThH column");let Q=$?.band??[0.25,0.75],[J,X]=Q,Y=j(Z,'SELECT AVG("ThH") FROM "CTUN"'),K=Y===null?!1:Y<J||Y>X;return P({check:"hover",thresholds:Z.thresholds,failed:K,failSeverity:"HIGH",value:Y,threshold_used:X,source:$?.source,detail:`mean hover throttle ${(Y??0).toFixed(3)} vs band [${J}, ${X}]`})},G$=2000,a0=(Z)=>{if(!QZ(Z.vehicleType))return q("rcou",Z.thresholds,`n/a for ${Z.vehicleType} (control surfaces, not motor mix)`);if(!Z.hasTable("RCOU"))return q("rcou",Z.thresholds);let $=k(Z,"RCOU"),Q=[];for(let Y=1;Y<=8;Y++)if($.has(`C${Y}`))Q.push(`C${Y}`);if(Q.length===0)return q("rcou",Z.thresholds,"RCOU has no C1..C8 channels");let J=-1/0,X=[];for(let Y of Q){let K=j(Z,`SELECT MAX(${S(Y)}) FROM "RCOU"`);if(K===null)continue;if(K>J)J=K;if(K>=G$)X.push(`${Y}=${K}`)}if(J===-1/0)J=0;return P({check:"rcou",thresholds:Z.thresholds,failed:X.length>0,failSeverity:"HIGH",value:J,threshold_used:G$,detail:X.length>0?`motor output(s) pegged at max: ${X.join(", ")}`:void 0})},DZ=1000,jZ=2000,s0=(Z)=>{if(!Z.hasTable("RCIN"))return q("rcin",Z.thresholds);if(!C(Z,"RCIN","C3"))return q("rcin",Z.thresholds,"RCIN missing C3 column");let $=j(Z,'SELECT MIN("C3") FROM "RCIN"'),Q=j(Z,'SELECT MAX("C3") FROM "RCIN"'),J=$!==null&&$<DZ||Q!==null&&Q>jZ,X=Q;if($!==null&&$<DZ)X=$;return P({check:"rcin",thresholds:Z.thresholds,failed:J,failSeverity:"HIGH",value:X,threshold_used:jZ,detail:`RCIN C3 range [${$??"\u2014"}, ${Q??"\u2014"}] vs [${DZ}, ${jZ}]`})},t0=(Z)=>{let $=Z.thresholds.vibration;if(!Z.hasTable("VIBE"))return q("vibration",Z.thresholds);let Q=k(Z,"VIBE"),J=["VibeX","VibeY","VibeZ"].filter((V)=>Q.has(V));if(J.length===0)return q("vibration",Z.thresholds,"VIBE has no VibeX/Y/Z columns");let X=$?.max_ms2??30,Y={},K=-1/0;for(let V of J){let H=j(Z,`SELECT MAX(${S(V)}) FROM "VIBE"`)??0;if(Y[V]=H,H>K)K=H}if(K===-1/0)K=0;let G=["Clip","Clip0","Clip1","Clip2"].filter((V)=>Q.has(V)),W=0;for(let V of G){let U=j(Z,`SELECT MAX(${S(V)}) FROM "VIBE"`);if(U!==null&&U>W)W=U}let z=G.length>0?` | clipping MAX=${W}${W>0?" (CLIP EVENTS)":""}`:"",B=e0(Y,K,W);return P({check:"vibration",thresholds:Z.thresholds,failed:K>=X,failSeverity:"HIGH",value:K,threshold_used:X,source:$?.source,detail:`peak vibe ${K} m/s\xB2 (axes ${J.join("/")})${z}${B}`})};function e0(Z,$,Q){let J=Object.values(Z);if($<=0||J.length===0)return"";let X=[...J].sort((z,B)=>B-z),Y=X[0],K=X.length>1?X[1]:0;if(X.length>=2&&K>0&&Y>=2*K)return" | cause: likely sensor/mounting alignment (one axis dominates)";let W=X.length>=2&&Y<=1.5*(X[X.length-1]||Y);if(Q>0||W)return" | cause: likely mechanical degradation (props/bearings/frame)";return""}var Z8=(Z)=>{let $=Z.thresholds.mag;if(!Z.hasTable("MAG"))return q("mag",Z.thresholds);if(!C(Z,"MAG","Health"))return q("mag",Z.thresholds,"MAG missing Health column");let Q=$?.min??1,J=j(Z,'SELECT MIN("Health") FROM "MAG"');return P({check:"mag",thresholds:Z.thresholds,failed:J!==null&&J<Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source})},$8=(Z)=>{let $=Z.thresholds.ekf,Q=W$(Z,["XKF3","NKF3"]);if(!Q)return q("ekf",Z.thresholds);let J=k(Z,Q),X=["IVN","IVE","IVD","IVT","IPN","IPE","IPD"].filter((G)=>J.has(G));if(X.length===0)return q("ekf",Z.thresholds,`${Q} has no IVN/IVE/IVD/IVT/IPN/IPE/IPD columns`);let Y=$?.max_ratio??0.5,K=-1/0;for(let G of X){let W=j(Z,`SELECT MAX(ABS(${S(G)})) FROM ${S(Q)}`);if(W!==null&&W>K)K=W}if(K===-1/0)K=0;return P({check:"ekf",thresholds:Z.thresholds,failed:K>Y,failSeverity:"HIGH",value:K,threshold_used:Y,source:$?.source,detail:`peak innovation ${K} (${Q}: ${X.join("/")})`})},J8=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return q("min_cell_voltage",Z.thresholds);if(!C(Z,"BAT","Volt"))return q("min_cell_voltage",Z.thresholds,"BAT missing Volt column");let Q=$?.min_cell_v??3.3,J=j(Z,'SELECT MAX("Volt") FROM "BAT"'),X=j(Z,'SELECT MIN("Volt") FROM "BAT"');if(J===null||X===null||J<=0)return q("min_cell_voltage",Z.thresholds,"BAT.Volt has no usable values");let Y=Math.max(1,Math.round(J/4.2)),K=X/Y;return P({check:"min_cell_voltage",thresholds:Z.thresholds,failed:K<Q,failSeverity:"CRITICAL",value:K,threshold_used:Q,source:$?.source,detail:`${Y}S pack (MAX pack ${J.toFixed(2)}V), min cell ${K.toFixed(3)}V vs floor ${Q}V`})},Q8=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return q("voltage_sag",Z.thresholds);if(!C(Z,"BAT","VoltR")||!C(Z,"BAT","Volt"))return q("voltage_sag",Z.thresholds,"BAT missing VoltR/Volt columns");let Q=$?.max_sag_v??2,J=j(Z,'SELECT MAX("VoltR"-"Volt") FROM "BAT"');return P({check:"voltage_sag",thresholds:Z.thresholds,failed:J!==null&&J>Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source,detail:`peak sag ${(J??0).toFixed(2)}V (VoltR-Volt) vs ${Q}V`})},X8=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return q("over_current",Z.thresholds);if(!C(Z,"BAT","Curr"))return q("over_current",Z.thresholds,"BAT missing Curr column");let Q=$?.max_current_a??200,J=j(Z,'SELECT MAX("Curr") FROM "BAT"');return P({check:"over_current",thresholds:Z.thresholds,failed:J!==null&&J>Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source,detail:`peak current ${(J??0).toFixed(1)}A vs ${Q}A`})},Y8=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return q("internal_resistance",Z.thresholds);if(!C(Z,"BAT","Res"))return q("internal_resistance",Z.thresholds,"BAT missing Res column");let Q=j(Z,'SELECT MAX("Res") FROM "BAT"');if(Q===null||Q===0)return q("internal_resistance",Z.thresholds,"BAT.Res all zero/absent (not reported)");let J=$?.res_min_current_a??5,Y=C(Z,"BAT","Curr")?j(Z,`SELECT MAX("Res") FROM "BAT" WHERE "Curr" >= ${J}`):Q;if(Y===null)return q("internal_resistance",Z.thresholds,`no BAT samples at Curr \u2265 ${J} A \u2014 Res only valid under load (max raw ${Q.toFixed(3)}\u03A9 is a low-current artifact)`);let K=$?.max_res_ohm??0.03;return P({check:"internal_resistance",thresholds:Z.thresholds,failed:Y>K,failSeverity:"HIGH",value:Y,threshold_used:K,source:$?.source,detail:`peak internal resistance ${Y.toFixed(4)}\u03A9 (at Curr \u2265 ${J} A) vs ${K}\u03A9`})},K8=(Z)=>{let $=Z.thresholds.power;if(!Z.hasTable("POWR"))return q("board_brownout",Z.thresholds);if(!C(Z,"POWR","Vcc"))return q("board_brownout",Z.thresholds,"POWR missing Vcc column");let Q=$?.min_vcc_v??4.5,J=j(Z,'SELECT MIN("Vcc") FROM "POWR"');return P({check:"board_brownout",thresholds:Z.thresholds,failed:J!==null&&J<Q,failSeverity:"CRITICAL",value:J,threshold_used:Q,source:$?.source,detail:`min board Vcc ${(J??0).toFixed(3)}V vs floor ${Q}V`})},G8=(Z)=>{let $=Z.thresholds.rate;if(!Z.hasTable("RATE"))return q("rate_tracking",Z.thresholds);let Q=k(Z,"RATE"),X=[["RDes","R"],["PDes","P"],["YDes","Y"]].filter(([W,z])=>Q.has(W)&&Q.has(z));if(X.length===0)return q("rate_tracking",Z.thresholds,"RATE has no RDes/R, PDes/P, YDes/Y pairs");let Y=$?.max_err_dps??50,K=-1/0,G="";for(let[W,z]of X){let B=j(Z,`SELECT MAX(ABS(${S(W)}-${S(z)})) FROM "RATE"`);if(B!==null&&B>K)K=B,G=W.replace("Des","")}if(K===-1/0)K=0;return P({check:"rate_tracking",thresholds:Z.thresholds,failed:K>Y,failSeverity:"HIGH",value:K,threshold_used:Y,source:$?.source,detail:`peak rate-tracking error ${K.toFixed(2)} deg/s (axis ${G||"?"}) vs ${Y} deg/s`})},W8=(Z)=>{let $=Z.thresholds.gps;if(!Z.hasTable("GPA"))return q("gps_accuracy",Z.thresholds);if(!C(Z,"GPA","HAcc"))return q("gps_accuracy",Z.thresholds,"GPA missing HAcc column");let Q=$?.hacc_max_m??5,J=j(Z,'SELECT MAX("HAcc") FROM "GPA"');return P({check:"gps_accuracy",thresholds:Z.thresholds,failed:J!==null&&J>Q,failSeverity:"HIGH",value:J,threshold_used:Q,source:$?.source,detail:`peak horizontal accuracy ${(J??0).toFixed(2)}m vs ${Q}m`})},z8=(Z)=>{let $=Z.thresholds.ekf,Q=W$(Z,["XKF4","NKF4"]);if(!Q)return q("ekf_variance",Z.thresholds);let J=k(Z,Q),X=["SV","SP","SH","SM"].filter((B)=>J.has(B));if(X.length===0)return q("ekf_variance",Z.thresholds,`${Q} has no SV/SP/SH/SM columns`);let Y=$?.max_variance??1,K=-1/0,G="";for(let B of X){let V=j(Z,`SELECT MAX(${S(B)}) FROM ${S(Q)}`);if(V!==null&&V>K)K=V,G=B}if(K===-1/0)K=0;let W=J.has("FS")?j(Z,`SELECT MAX("FS") FROM ${S(Q)}`):null,z=W!==null&&W>0?` | EKF failsafe set (FS MAX=${W})`:"";return P({check:"ekf_variance",thresholds:Z.thresholds,failed:K>Y,failSeverity:"HIGH",value:K,threshold_used:Y,source:$?.source,detail:`peak EKF variance ${K.toFixed(3)} (${Q}: ${G||X.join("/")}) vs ${Y}${z}`})},B8=(Z)=>{let $=Z.thresholds.mag;if(!Z.hasTable("MAG"))return q("mag_offsets",Z.thresholds);let Q=k(Z,"MAG"),J=["OfsX","OfsY","OfsZ"].filter((G)=>Q.has(G));if(J.length===0)return q("mag_offsets",Z.thresholds,"MAG has no OfsX/OfsY/OfsZ columns");let X=$?.max_offset??600,Y=-1/0,K="";for(let G of J){let W=j(Z,`SELECT MAX(ABS(${S(G)})) FROM "MAG"`);if(W!==null&&W>Y)Y=W,K=G}if(Y===-1/0)Y=0;return P({check:"mag_offsets",thresholds:Z.thresholds,failed:Y>X,failSeverity:"HIGH",value:Y,threshold_used:X,source:$?.source,detail:`peak mag offset ${Y.toFixed(0)} (${K||J.join("/")}) vs ${X}`})},V8=(Z)=>{let $=Z.thresholds.esc;if(!Z.hasTable("ESC"))return q("esc_health",Z.thresholds);let Q=k(Z,"ESC"),J=["Temp","MotTemp"].filter((V)=>Q.has(V));if(J.length===0&&!Q.has("Err"))return q("esc_health",Z.thresholds,"ESC has no Temp/MotTemp/Err columns");let X=$?.max_temp_c??90,Y=-1/0,K="";for(let V of J){let U=j(Z,`SELECT MAX(${S(V)}) FROM "ESC"`);if(U!==null&&U>Y)Y=U,K=V}if(Y===-1/0)Y=0;let G=Q.has("Err")?j(Z,'SELECT MAX("Err") FROM "ESC"')??0:0,W=J.length>0&&Y>X,z=G>0,B=z?` | ESC Err MAX=${G}`:"";return P({check:"esc_health",thresholds:Z.thresholds,failed:W||z,failSeverity:"HIGH",value:Y,threshold_used:X,source:$?.source,detail:`peak ESC temp ${Y.toFixed(1)}\xB0C (${K||"n/a"}) vs ${X}\xB0C${B}`})},U8=(Z)=>{if(!QZ(Z.vehicleType))return q("thrust_margin",Z.thresholds,`n/a for ${Z.vehicleType} (lift/T:W is a rotorcraft metric)`);if(!Z.hasTable("MOTB"))return q("thrust_margin",Z.thresholds);let $=k(Z,"MOTB");if(!$.has("FailFlags")&&!($.has("ThrOut")&&$.has("LiftMax")))return q("thrust_margin",Z.thresholds,"MOTB missing FailFlags and ThrOut/LiftMax columns");let Q=$.has("FailFlags")?j(Z,'SELECT MAX("FailFlags") FROM "MOTB"')??0:0,J=null;if($.has("ThrOut")&&$.has("LiftMax")){let G=j(Z,'SELECT AVG("ThrOut") FROM "MOTB"'),W=j(Z,'SELECT AVG("LiftMax") FROM "MOTB"');if(G!==null&&W!==null&&W>0)J=G/W}let X=J!==null&&J>0.5,Y=Q>0,K=`MOTB FailFlags MAX=${Q}`+(J!==null?`, mean ThrOut/LiftMax ${J.toFixed(2)} (low-margin > 0.5)`:"")+(Y?" \u2014 THRUST-LOSS flagged":X?" \u2014 low thrust margin":"");return P({check:"thrust_margin",thresholds:Z.thresholds,failed:Y||X,failSeverity:"HIGH",value:J,threshold_used:0.5,detail:K})},H8=(Z)=>{let $=Z.thresholds.imu;if(!Z.hasTable("IMU"))return q("imu_health",Z.thresholds);let Q=k(Z,"IMU"),J=["EG","EA"].filter((G)=>Q.has(G));if(J.length===0)return q("imu_health",Z.thresholds,"IMU has no EG/EA error-count columns");let X=$?.max_err??0,Y=-1/0,K="";for(let G of J){let W=j(Z,`SELECT MAX(${S(G)}) FROM "IMU"`);if(W!==null&&W>Y)Y=W,K=G}if(Y===-1/0)Y=0;return P({check:"imu_health",thresholds:Z.thresholds,failed:Y>X,failSeverity:"HIGH",value:Y,threshold_used:X,source:$?.source,detail:`max IMU error count ${Y} (${K||J.join("/")}) vs ${X}`})},L8=(Z)=>{let $=Z.thresholds.imu;if(!Z.hasTable("IMU"))return q("imu_consistency",Z.thresholds);let Q=k(Z,"IMU");if(!Q.has("I"))return q("imu_consistency",Z.thresholds,"IMU has no instance column I");let J=["AccX","AccY","AccZ"].filter((N)=>Q.has(N)),X=["GyrX","GyrY","GyrZ"].filter((N)=>Q.has(N));if(J.length===0&&X.length===0)return q("imu_consistency",Z.thresholds,"IMU has no Acc*/Gyr* columns");let Y=[...J,...X].map((N)=>`AVG(${S(N)}) ${S(N)}`).join(", "),K=Z.db.query(`SELECT "I" AS "I", ${Y} FROM "IMU" GROUP BY "I"`).all();if(K.length<2)return q("imu_consistency",Z.thresholds,`only ${K.length} IMU instance \u2014 nothing to cross-check`);let G=(N)=>{let _=K.map((D)=>Number(D[N])).filter((D)=>Number.isFinite(D));return _.length>=2?Math.max(..._)-Math.min(..._):0},W=0,z="";for(let N of J){let _=G(N);if(_>W)W=_,z=N}let B=0,V="";for(let N of X){let _=G(N);if(_>B)B=_,V=N}let U=$?.accel_consistency_max??0.7,H=$?.gyro_consistency_max??0.3,E=W>U,T=B>H,L=E||T,A=W/U>=B/H?W:B,O=W/U>=B/H?U:H,F=L?`INCONSISTENT \u2014 ${E?`accel ${z} spread ${W.toFixed(3)} m/s\xB2 > ${U}`:""}${E&&T?"; ":""}${T?`gyro ${V} spread ${B.toFixed(3)} rad/s > ${H}`:""}`:`IMUs consistent \u2014 max inter-instance bias spread: accel ${W.toFixed(3)} m/s\xB2 (<${U}), gyro ${B.toFixed(3)} rad/s (<${H}); relative differences between instances are within healthy tolerance`;return P({check:"imu_consistency",thresholds:Z.thresholds,failed:L,failSeverity:"HIGH",value:A,threshold_used:O,source:{kind:"rule-of-thumb",ref:"IMU inconsistency tolerance (~ArduPilot arming limits)"},detail:F})},O8=(Z)=>{if(!Z.hasTable("ERR"))return q("err_events",Z.thresholds);if(!C(Z,"ERR","Subsys")||!C(Z,"ERR","ECode"))return q("err_events",Z.thresholds,"ERR present but Subsys/ECode columns absent");let $=Z.thresholds.battery?.err_subsys??6,Q=Z.db.query(`SELECT "Subsys" AS subsys, "ECode" AS ecode, COUNT(*) AS n FROM "ERR" WHERE "ECode" <> 0 AND "Subsys" <> ${$} GROUP BY "Subsys", "ECode" ORDER BY "Subsys", "ECode"`).all(),J=0,X=[];for(let Y of Q)J+=Number(Y.n),X.push(`Subsys=${Y.subsys} ECode=${Y.ecode} \xD7${Y.n}`);return P({check:"err_events",thresholds:Z.thresholds,failed:J>0,failSeverity:"HIGH",value:J,threshold_used:0,detail:J>0?`${J} non-battery ERR event(s): ${X.join(", ")}`:"no non-battery ERR events"})},A8=new Set([6,9]),q8=(Z)=>{if(!Z.hasTable("MODE"))return q("mode_changes",Z.thresholds);if(!C(Z,"MODE","ModeNum"))return q("mode_changes",Z.thresholds,"MODE missing ModeNum column");let $=Z.db.query('SELECT "ModeNum" AS modenum FROM "MODE"').all(),Q=$.length,J=Q>0?Q-1:0,X=new Map,Y=!1;for(let G of $){let W=Number(G.modenum);if(X.set(W,(X.get(W)??0)+1),A8.has(W))Y=!0}let K=[...X.entries()].sort((G,W)=>G[0]-W[0]).map(([G,W])=>`Mode${G}\xD7${W}`).join(", ");return P({check:"mode_changes",thresholds:Z.thresholds,failed:Y,failSeverity:"HIGH",value:J,threshold_used:0,detail:`${J} mode change(s); timeline: ${K||"\u2014"}`+(Y?" \u2014 emergency mode (RTL/LAND) entered":"")})},E8=(Z)=>{let $=Z.thresholds.airspeed;if(!K$(Z.vehicleType))return q("airspeed",Z.thresholds,`n/a for ${Z.vehicleType} (no fixed-wing airspeed)`);if(!Z.hasTable("ARSP"))return q("airspeed",Z.thresholds,"no ARSP (airspeed) data");if(!C(Z,"ARSP","Airspeed"))return q("airspeed",Z.thresholds,"ARSP missing Airspeed column");let Q=$?.stall_min_ms??8,J={kind:"rule-of-thumb",ref:"default stall floor"};if(Z.hasTable("PARM")){let Y=j(Z,`SELECT "Value" FROM "PARM" WHERE "Name"='ARSPD_FBW_MIN' LIMIT 1`);if(Y!==null&&Y>0)Q=Y,J={kind:"datasheet",ref:"ARSPD_FBW_MIN"}}let X=j(Z,'SELECT MIN("Airspeed") FROM "ARSP" WHERE "Airspeed" > 2');if(X===null)return q("airspeed",Z.thresholds,"no airborne airspeed samples");return P({check:"airspeed",thresholds:Z.thresholds,failed:X<Q,failSeverity:"HIGH",value:X,threshold_used:Q,source:J,detail:`min in-flight airspeed ${X.toFixed(1)} m/s vs stall floor ${Q} m/s`})},F8=[{id:"battery",run:c0},{id:"min_cell_voltage",run:J8},{id:"voltage_sag",run:Q8},{id:"over_current",run:X8},{id:"internal_resistance",run:Y8},{id:"board_brownout",run:K8},{id:"attitude",run:i0},{id:"rate_tracking",run:G8},{id:"altitude",run:o0},{id:"hover",run:r0},{id:"rcou",run:a0},{id:"rcin",run:s0},{id:"thrust_margin",run:U8},{id:"vibration",run:t0},{id:"imu_health",run:H8},{id:"imu_consistency",run:L8},{id:"mag",run:Z8},{id:"mag_offsets",run:B8},{id:"gps_accuracy",run:W8},{id:"ekf",run:$8},{id:"ekf_variance",run:z8},{id:"esc_health",run:V8},{id:"airspeed",run:E8},{id:"err_events",run:O8},{id:"mode_changes",run:q8}];function z$(Z,$,Q){let J=new Set;try{let K=Z.query("SELECT name FROM sqlite_master WHERE type='table'").all();for(let G of K)J.add(G.name)}catch{}let X={db:Z,thresholds:$,hasTable:(K)=>J.has(K),vehicleType:Q?.vehicleType},Y=[];for(let{id:K,run:G}of F8)try{Y.push(G(X))}catch(W){let z=W instanceof Error?W.message:String(W);Y.push(q(K,$,`check error: ${z}`))}return Y}import{existsSync as j8,readFileSync as P8}from"fs";import{join as I8}from"path";import{existsSync as T8,readFileSync as N8}from"fs";import{dirname as _8,join as PZ}from"path";import{fileURLToPath as R8}from"url";var B$=_8(R8(import.meta.url)),D8=[PZ(B$,"..","data","apm-params.json"),PZ(B$,"..","..","data","apm-params.json")];function U$(Z){if(Z){let $=PZ(Z,".hydron","apm-params.json"),Q=V$($);if(Q&&Q.size>0)return Q}for(let $ of D8){let Q=V$($);if(Q)return Q}return new Map}function H$(Z,$){return $.get(Z.toUpperCase())}function V$(Z){if(!T8(Z))return;try{let $=N8(Z,"utf8"),Q=JSON.parse($);if(!Q||typeof Q!=="object")return;let J=new Map;for(let[X,Y]of Object.entries(Q)){if(!X||X.startsWith("_")||typeof Y!=="object"||Y===null)continue;let K={name:X};if(typeof Y.min==="number"&&Number.isFinite(Y.min))K.min=Y.min;if(typeof Y.max==="number"&&Number.isFinite(Y.max))K.max=Y.max;if(typeof Y.units==="string"&&Y.units)K.units=Y.units;J.set(X.toUpperCase(),K)}return J}catch{return}}function O$(){return{ruleset_version:"v1",safety:{ARMING_CHECK:{min:1},FS_THR_ENABLE:{min:1},FENCE_ENABLE:{min:1}},range:{INS_GYRO_FILTER:{min:5,max:80,severity:"HIGH"},ATC_RAT_RLL_P:{min:0,max:0.5,severity:"HIGH"},ATC_RAT_PIT_P:{min:0,max:0.5,severity:"HIGH"},ATC_RAT_YAW_P:{min:0,max:1,severity:"HIGH"},MOT_SPIN_ARM:{min:0,max:0.2,severity:"HIGH"},MOT_SPIN_MIN:{min:0,max:0.3,severity:"HIGH"},RTL_ALT:{min:200,max:1e4,severity:"HIGH"},WPNAV_SPEED:{min:50,max:2000,severity:"HIGH"}}}}function C8(Z){let $=O$(),Q=I8(Z,".hydron","param-rules.yaml");if(!j8(Q))return $;try{let J=P8(Q,"utf8");return M8($,J)}catch{return $}}function M8(Z,$){let Q={ruleset_version:Z.ruleset_version,safety:{...Z.safety},range:{...Z.range}},J=null;for(let X of $.split(/\r?\n/)){let K=X.replace(/#.*$/,"").trim();if(!K||K.startsWith("-"))continue;let G=/^ruleset_version\s*:\s*(.+)$/.exec(K);if(G){Q.ruleset_version=G[1].trim().replace(/^["']|["']$/g,""),J=null;continue}if(/^safety\s*:\s*$/.test(K)){J="safety";continue}if(/^range\s*:\s*$/.test(K)){J="range";continue}let W=/^([A-Za-z0-9_]+)\s*:\s*\{(.+)\}\s*$/.exec(K);if(W&&J){let z=W[1],B=S8(W[2]);if(J==="safety"){let V=B.min;if(typeof V==="number")Q.safety[z]={min:V}}else{let{min:V,max:U,severity:H}=B;if(typeof V==="number"&&typeof U==="number")Q.range[z]={min:V,max:U,severity:H==="CRITICAL"?"CRITICAL":"HIGH"}}}}return Q}function S8(Z){let $={};for(let Q of Z.split(",")){let J=/^\s*([A-Za-z0-9_]+)\s*:\s*(.+?)\s*$/.exec(Q);if(!J)continue;let X=J[1],Y=J[2].replace(/^["']|["']$/g,""),K=Number(Y);$[X]=Number.isFinite(K)&&Y!==""?K:Y}return $}function w8(Z){let $=new Map;for(let Q of Z)$.set(Q.name.toUpperCase(),Q.value);return $}function b(Z){return{check:Z.check,severity:Z.severity,value:Z.value,threshold_used:Z.threshold_used,ruleset_version:Z.ruleset_version,verdict:Z.verdict,tier:"system",source:Z.source,detail:Z.detail}}function XZ(Z,$={}){let Q=$.configDir?C8($.configDir):O$(),J=$.ruleset_version??Q.ruleset_version,X=w8(Z),Y=U$($.configDir);return[...y8(X,Q,J),...k8(X,J),...h8(X,Q,J,Y),...b8(Z,null)]}function y8(Z,$,Q){let J=[],X={kind:"rule-of-thumb"},Y=Z.get("ARMING_CHECK");if(Y===void 0)J.push(g("param.safety.arming",Q,"ARMING_CHECK absent"));else{let B=Y!==0;J.push(b({check:"param.safety.arming",severity:B?"OK":"CRITICAL",value:Y,threshold_used:$.safety.ARMING_CHECK?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:Q,detail:B?"arming checks enabled":"ARMING_CHECK=0 \u2014 ALL pre-arm safety checks are disabled",source:X}))}let K=Z.get("BATT_LOW_VOLT"),G=Z.get("BATT_CRT_VOLT");if(K===void 0&&G===void 0)J.push(g("param.safety.battery_failsafe",Q,"BATT_LOW_VOLT/BATT_CRT_VOLT absent"));else{let B=(K??0)===0&&(G??0)===0;J.push(b({check:"param.safety.battery_failsafe",severity:B?"CRITICAL":"OK",value:B?0:G??K??0,threshold_used:0,verdict:B?"FAIL":"PASS",ruleset_version:Q,detail:B?"battery failsafe not configured (BATT_LOW_VOLT and BATT_CRT_VOLT both 0)":"battery failsafe configured",source:X}))}let W=Z.get("FS_THR_ENABLE");if(W===void 0)J.push(g("param.safety.radio_failsafe",Q,"FS_THR_ENABLE absent"));else{let B=W!==0;J.push(b({check:"param.safety.radio_failsafe",severity:B?"OK":"HIGH",value:W,threshold_used:$.safety.FS_THR_ENABLE?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:Q,detail:B?"radio failsafe enabled":"FS_THR_ENABLE=0 \u2014 radio/throttle failsafe is OFF",source:X}))}let z=Z.get("FENCE_ENABLE");if(z===void 0)J.push(g("param.safety.fence",Q,"FENCE_ENABLE absent"));else{let B=z!==0;J.push(b({check:"param.safety.fence",severity:B?"OK":"HIGH",value:z,threshold_used:$.safety.FENCE_ENABLE?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:Q,detail:B?"geofence enabled":"FENCE_ENABLE=0 \u2014 geofence disabled (advisory)",source:X}))}return J}function k8(Z,$){let Q=[],J={kind:"rule-of-thumb"},X=Z.get("BATT_CRT_VOLT"),Y=v8(Z);if(X===void 0||X===0||Y===null||Y<=0)Q.push(g("param.consistency.battery_cells",$,"BATT_CRT_VOLT and a cell-count source required"));else{let z=X/Y,B=z>=3&&z<=3.7;Q.push(b({check:"param.consistency.battery_cells",severity:B?"OK":"HIGH",value:IZ(z,3),threshold_used:3,verdict:B?"PASS":"FAIL",ruleset_version:$,detail:B?`per-cell critical voltage ${IZ(z,2)}V across ${Y}S looks sane`:`per-cell critical voltage ${IZ(z,2)}V (${Y}S) outside [3.0,3.7] \u2014 cell count vs BATT_CRT_VOLT inconsistent`,source:J}))}let K=Z.get("FRAME_CLASS"),G=Z.get("FRAME_TYPE");if(K===void 0&&G===void 0)Q.push(g("param.consistency.frame",$,"FRAME_CLASS/FRAME_TYPE absent"));else{let z=(K??0)!==0&&(G??0)!==0;Q.push(b({check:"param.consistency.frame",severity:z?"OK":"HIGH",value:K??null,threshold_used:1,verdict:z?"PASS":"FAIL",ruleset_version:$,detail:z?`frame configured (CLASS=${K}, TYPE=${G})`:`frame not fully configured (CLASS=${K??"?"}, TYPE=${G??"?"}) \u2014 must be non-zero for a copter`,source:J}))}let W=Z.get("INS_HNTCH_ENABLE");if(W===void 0)Q.push(g("param.consistency.notch_filter",$,"INS_HNTCH_ENABLE absent"));else Q.push(b({check:"param.consistency.notch_filter",severity:"OK",value:W,threshold_used:null,verdict:"PASS",ruleset_version:$,detail:W===0?"harmonic notch filter disabled (INS_HNTCH_ENABLE=0) \u2014 advisory; tune if vibration is high":"harmonic notch filter enabled",source:J}));return Q}function v8(Z){for(let Q of["BATT_CELLS","BATT_N_CELLS","BATT_CELL_COUNT"]){let J=Z.get(Q);if(J!==void 0&&J>0)return Math.round(J)}let $=Z.get("BATT_ARM_VOLT");if($!==void 0&&$>0)return Math.round($/3.6);return null}function h8(Z,$,Q,J){let X=[];for(let[Y,K]of Object.entries($.range)){let G=Z.get(Y.toUpperCase()),W=`param.range.${Y}`;if(G===void 0){X.push(g(W,Q,`${Y} absent`));continue}let z=H$(Y,J),B=z!==void 0&&(typeof z.min==="number"||typeof z.max==="number"),V=B&&typeof z.min==="number"?Math.max(K.min,z.min):K.min,U=B&&typeof z.max==="number"?Math.min(K.max,z.max):K.max,H=B&&typeof z.min==="number"?z.min:K.min,E=B&&typeof z.max==="number"?z.max:K.max,T=B?{kind:"datasheet",ref:"apm.pdef.xml"}:{kind:"tuned"},L=G>=V&&G<=U;X.push(b({check:W,severity:L?"OK":K.severity,value:G,threshold_used:L?E:G<V?H:E,verdict:L?"PASS":"FAIL",ruleset_version:Q,detail:L?`${Y}=${G} within [${H},${E}]${B?" (apm.pdef.xml)":""}`:`${Y}=${G} outside [${H},${E}]${B?" (apm.pdef.xml)":""}`,source:T}))}return X}function b8(Z,$){return[]}function g(Z,$,Q){return b({check:Z,severity:"OK",value:null,threshold_used:null,verdict:"SKIP",ruleset_version:$,detail:Q})}function IZ(Z,$){let Q=10**$;return Math.round(Z*Q)/Q}function A$(Z){if(Z.length===0)return"Parameter audit: no findings.";let $=(W)=>{if(W.verdict==="SKIP")return"\u2013";if(W.verdict==="PASS")return"\u2713";return W.severity==="CRITICAL"?"\u2717 CRIT":"\u2717 HIGH"},Q=Z.filter((W)=>W.verdict==="FAIL"),J=Z.filter((W)=>W.verdict==="PASS"),X=Z.filter((W)=>W.verdict==="SKIP"),Y=Q.filter((W)=>W.severity==="CRITICAL").length,K=[];K.push(`Parameter audit \u2014 ${Z.length} checks: ${J.length} pass, ${Q.length} fail (${Y} critical), ${X.length} skipped`);let G=[...Z].sort((W,z)=>L$(W)-L$(z));for(let W of G){let z=W.detail?` \u2014 ${W.detail}`:"";K.push(` ${$(W).padEnd(7)} ${W.check}${z}`)}return K.join(`
9
+ `)}function L$(Z){if(Z.verdict==="FAIL")return Z.severity==="CRITICAL"?0:1;if(Z.verdict==="PASS")return 2;return 3}import{existsSync as f8,readFileSync as g8}from"fs";import{join as u8}from"path";function x8(){return{ruleset_version:"v1",vibration:{field:"VibeX|VibeY|VibeZ",max_ms2:30,source:{kind:"rule-of-thumb"}},clipping:{field:"Clip",max:0},battery:{err_subsys:6,err_ecode:1,min_cell_v:3.3,max_sag_v:2,max_current_a:200,max_res_ohm:0.03,res_min_current_a:5,source:{kind:"rule-of-thumb"}},attitude:{fields:"DesRoll-Roll,DesPitch-Pitch",max_deg:5,window_samples:10},altitude:{field:"DAlt-Alt",max_m:2},hover:{field:"ThH",band:[0.25,0.75]},ekf:{fields:"IVN,IVE,IVD",max_ratio:0.5,max_variance:1},mag:{field:"Health",min:1,late_align_s:30,max_offset:600},gps:{field:"NSats",min:6,hdop_max:2,hacc_max_m:5},power:{min_vcc_v:4.5,source:{kind:"rule-of-thumb"}},rate:{max_err_dps:50,source:{kind:"rule-of-thumb"}},esc:{max_temp_c:90,source:{kind:"rule-of-thumb"}},imu:{max_err:0,accel_consistency_max:0.7,gyro_consistency_max:0.3,source:{kind:"rule-of-thumb"}},airspeed:{stall_min_ms:8,source:{kind:"rule-of-thumb"}}}}function E$(Z){let $=x8(),Q=u8(Z,".hydron","uav-thresholds.yaml");if(!f8(Q))return $;try{let J=m8(g8(Q,"utf8"));return F$($,J)}catch{return $}}function F$(Z,$){let Q={...Z};for(let[J,X]of Object.entries($)){let Y=Q[J];if(X&&typeof X==="object"&&!Array.isArray(X)&&Y&&typeof Y==="object"&&!Array.isArray(Y))Q[J]=F$(Y,X);else Q[J]=X}return Q}function q$(Z){let $=!1,Q=!1;for(let J=0;J<Z.length;J++){let X=Z[J];if(X==="'"&&!Q)$=!$;else if(X==='"'&&!$)Q=!Q;else if(X==="#"&&!$&&!Q)return Z.slice(0,J)}return Z}function T$(Z,$){let Q=[],J=0,X=!1,Y=!1,K=0;for(let G=0;G<Z.length;G++){let W=Z[G];if(W==="'"&&!Y)X=!X;else if(W==='"'&&!X)Y=!Y;else if(!X&&!Y){if(W==="["||W==="{")J++;else if(W==="]"||W==="}")J--;else if(W===$&&J===0)Q.push(Z.slice(K,G)),K=G+1}}return Q.push(Z.slice(K)),Q}function YZ(Z){let $=Z.trim();if($==="")return"";if(/^-?\d+(\.\d+)?$/.test($))return Number($);if($==="true")return!0;if($==="false")return!1;if($.startsWith("[")&&$.endsWith("]")){let Q=$.slice(1,-1).trim();return Q?T$(Q,",").map(YZ):[]}if($.startsWith("{")&&$.endsWith("}"))return N$($);return $.replace(/^["']|["']$/g,"")}function N$(Z){let $=Z.trim().replace(/^\{/,"").replace(/\}$/,""),Q={};for(let J of T$($,",")){let X=J.indexOf(":");if(X<0)continue;let Y=J.slice(0,X).trim().replace(/^["']|["']$/g,"");if(Y)Q[Y]=YZ(J.slice(X+1))}return Q}function m8(Z){let $={},Q=Z.split(/\r?\n/),J=0;while(J<Q.length){let X=q$(Q[J]);if(J++,!X.trim()||/^\s/.test(X))continue;let Y=/^([A-Za-z0-9_]+):\s*(.*)$/.exec(X);if(!Y)continue;let K=Y[1],G=Y[2].trim();if(G.startsWith("{"))$[K]=N$(G);else if(G!=="")$[K]=YZ(G);else{let W={};while(J<Q.length){let z=q$(Q[J]);if(!z.trim()){J++;continue}if(!/^\s/.test(z))break;J++;let B=/^\s+([A-Za-z0-9_]+):\s*(.*)$/.exec(z);if(B&&B[2].trim()!=="")W[B[1]]=YZ(B[2])}$[K]=W}}return $}function I(Z,$){let Q=$.toLowerCase();return Z.columns.findIndex((J)=>J.toLowerCase()===Q)}function h(Z){if(typeof Z==="number")return Number.isFinite(Z)?Z:null;if(typeof Z==="bigint")return Number(Z);if(typeof Z==="string"){let $=Number(Z);return Number.isFinite($)?$:null}return null}function d8(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z.trim()))try{return BigInt(Z.trim())}catch{return null}return null}function d(Z){if(Z===null||Z===void 0)return null;if(typeof Z==="string")return Z;return String(Z)}function KZ(Z){return`"${Z.replace(/"/g,'""')}"`}function wZ(Z,$){try{return!!Z.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get($)}catch{return!1}}function yZ(Z,$){try{let Q=Z.query(`PRAGMA table_info(${KZ($)})`).all();return new Set(Q.map((J)=>J.name))}catch{return new Set}}function GZ(Z,$){try{let Q=Z.query($).get();if(!Q)return null;let J=Object.values(Q)[0];if(J===null||J===void 0)return null;let X=typeof J==="bigint"?Number(J):Number(J);return Number.isFinite(X)?X:null}catch{return null}}function p8(Z){for(let $ of["ATT","GPS","VIBE","CTUN","IMU"])if(wZ(Z,$)&&yZ(Z,$).has("TimeUS")){let Q=GZ(Z,`SELECT COUNT(*) AS c FROM ${KZ($)}`);if(Q===null||Q<=0)continue;let J=GZ(Z,`SELECT (MAX("TimeUS") - MIN("TimeUS")) AS d FROM ${KZ($)}`);if(J!==null)return J/1e6}return null}function l8(Z){if(!wZ(Z,"VIBE"))return null;let $=yZ(Z,"VIBE"),Q=["VibeX","VibeY","VibeZ"].filter((X)=>$.has(X));if(Q.length===0)return null;let J=Q.map((X)=>`SELECT MAX(${KZ(X)}) AS m FROM "VIBE"`).join(" UNION ALL ");return GZ(Z,`SELECT MAX(m) AS m FROM (${J})`)}function n8(Z){if(!wZ(Z,"CTUN")||!yZ(Z,"CTUN").has("ThH"))return null;return GZ(Z,'SELECT AVG("ThH") AS m FROM "CTUN"')}function CZ(Z,$){let Q=Z.tables.PARM;if(!Q)return null;let J=I(Q,"Name"),X=I(Q,"Value");if(J<0||X<0)return null;let Y=$.toLowerCase();for(let K of Q.rows)if(d(K[J])?.toLowerCase()===Y)return h(K[X]);return null}var c8={0:"Undefined",1:"Quad",2:"Hexa",3:"Octa",4:"OctaQuad",5:"Y6",6:"Heli",7:"Tri",8:"SingleCopter",9:"CoaxCopter",10:"BiCopter",11:"Heli_Dual",12:"DodecaHexa",13:"HeliQuad",14:"Deca",15:"Scripting"};function SZ(Z,$){let Q=Z.tables.MSG;if(!Q)return null;let J=I(Q,"Message")>=0?I(Q,"Message"):I(Q,"Msg");if(J<0)return null;for(let X of Q.rows){let Y=d(X[J]);if(Y&&$.test(Y))return Y}return null}var i8=/^([A-Za-z][A-Za-z0-9_+\-]*)\s+([0-9A-Fa-f]{8})\s+([0-9A-Fa-f]{8})\s+([0-9A-Fa-f]{8})\b/;function o8(Z){let $=Z.tables.MSG;if(!$)return null;let Q=I($,"Message")>=0?I($,"Message"):I($,"Msg");if(Q<0)return null;for(let J of $.rows){let X=d(J[Q]),Y=X&&i8.exec(X.trim());if(Y){let[,K,G,W,z]=Y;return{id:`${G}${W}${z}`.toUpperCase(),name:`${K}-${G.toUpperCase()}`}}}return null}function r8(Z){let $=o8(Z),Q=CZ(Z,"SYSID_THISMAV"),J,X;if($)J=$.id,X=$.name;else if(Q!==null)J=`sysid-${Q}`,X=SZ(Z,/ArduCopter|ArduPlane|ArduPilot|board/i)??J;else{let G=SZ(Z,/ArduCopter|ArduPlane|ArduPilot|ChibiOS|board/i);J=G??"unknown",X=G??J}let Y,K=CZ(Z,"Q_ENABLE");if(K!==null&&K>=1)Y="QuadPlane";else{let G=CZ(Z,"FRAME_CLASS");if(G!==null)Y=c8[G]??`class_${G}`}return{kind:"airframe",external_id:J,attrs:{name:X,frame:Y}}}function a8(Z){let $=Z.tables.GPS;if(!$)return null;let Q=I($,"GWk"),J=I($,"GMS");if(Q<0||J<0)return null;let X=315964800000,Y=18000,K=null;for(let W of $.rows){let z=h(W[Q]),B=h(W[J]);if(z===null||B===null||z<=0)continue;let V=X+z*7*24*3600*1000+B-Y;if(K===null||V<K)K=V}if(K===null)return null;let G=new Date(K);return Number.isNaN(G.getTime())?null:G.toISOString()}function s8(Z,$,Q){let J=SZ(Z,/ArduCopter|ArduPlane|ArduPilot/);return{kind:"analysis",source_hash:Q,logged_at:a8(Z),attrs:{firmware:J??null,duration_s:p8($),pilot_id:null,rc_id:null}}}function t8(Z){let $=Z.tables.PARM;if(!$)return[];let Q=I($,"Name"),J=I($,"Value");if(Q<0||J<0)return[];let X=I($,"Default"),Y=[];for(let K of $.rows){let G=d(K[Q]);if(!G)continue;let W=h(K[J]);if(W===null)continue;let z={name:G,value:W};if(X>=0){let B=h(K[X]);z.default=B}Y.push(z)}return Y}function MZ(Z,$){let Q=I(Z,"TimeUS");if(Q<0)return 0n;return d8($[Q])??0n}function e8(Z){let $=[],Q=Z.tables.ERR;if(Q){let Y=I(Q,"Subsys"),K=I(Q,"ECode");for(let G of Q.rows)$.push({at:MZ(Q,G),type:"ERR",payload:{Subsys:Y>=0?h(G[Y]):null,ECode:K>=0?h(G[K]):null}})}let J=Z.tables.MODE;if(J){let Y=I(J,"TimeUS");for(let K of J.rows){let G={};J.columns.forEach((W,z)=>{if(z===Y)return;G[W]=h(K[z])??d(K[z])}),$.push({at:MZ(J,K),type:"MODE",payload:G})}}let X=Z.tables.MSG;if(X){let Y=I(X,"Message")>=0?I(X,"Message"):I(X,"Msg");for(let K of X.rows)$.push({at:MZ(X,K),type:"MSG",payload:{text:Y>=0?d(K[Y]):null}})}return $}function Z6(Z,$){let Q=[],J=l8($);if(J!==null)Q.push({name:"vibe_peak",value:J,unit:"m/s/s"});let X=Z.tables.ERR;if(X){let K=I(X,"Subsys"),G=I(X,"ECode");if(K>=0&&G>=0){let W=0;for(let z of X.rows)if(h(z[K])===6&&h(z[G])===1)W++;Q.push({name:"failsafe_count",value:W})}}let Y=n8($);if(Y!==null)Q.push({name:"hover_thr",value:Y});return Q}function _$(Z,$,Q){return{entity:r8(Z),episode:s8(Z,$,Q.sourceHash),params:t8(Z),events:e8(Z),measurements:Z6(Z,$)}}import{mkdir as $6,readFile as kZ,writeFile as WZ,readdir as D$,stat as J6}from"fs/promises";import{dirname as j$,join as p}from"path";function P$(Z,$,Q){return p(Z,".hydron","observations",$,zZ(Q))}function zZ(Z){return Z.replace(/[^A-Za-z0-9._-]/g,"_")||"unknown"}function vZ(Z,$){return typeof $==="bigint"?$.toString():$}function I$(Z){if(Array.isArray(Z.events))Z.events=Z.events.map(($)=>{let Q=$.at,J;if(typeof Q==="bigint")J=Q;else if(typeof Q==="string"&&/^-?\d+$/.test(Q))J=BigInt(Q);else if(typeof Q==="number")J=BigInt(Math.trunc(Q));else J=0n;return{...$,at:J}});return Z}var R$={OK:0,HIGH:1,CRITICAL:2};function Q6(Z){let $=Z.findings??[],Q=$.filter((K)=>K.verdict==="FAIL").length,J=$.filter((K)=>K.severity==="CRITICAL"&&K.verdict==="FAIL").length,X="OK";for(let K of $)if(R$[K.severity]>R$[X])X=K.severity;let Y=(K)=>Z.measurements?.find((G)=>G.name===K)?.value??null;return{source_hash:Z.episode.source_hash,logged_at:Z.episode.logged_at,ruleset_version:Z.ruleset_version,anomalies:Q,critical:J,vibe_peak:Y("vibe_peak"),failsafe_count:Y("failsafe_count"),worst_severity:X,...Z.tags&&Z.tags.length>0?{tags:Z.tags}:{}}}async function X6(Z){let $;try{$=await kZ(Z,"utf8")}catch{return[]}let Q=[];for(let J of $.split(`
10
+ `)){let X=J.trim();if(!X)continue;try{Q.push(JSON.parse(X))}catch{}}return Q}async function hZ(Z,$){let Q=p(Z,"index.jsonl"),J=await X6(Q),X=Q6($),Y=J.filter((K)=>K.source_hash!==X.source_hash);Y.push(X),await WZ(Q,Y.map((K)=>JSON.stringify(K)).join(`
11
11
  `)+`
12
- `,"utf8")}async function D$(Z,$){let Q=R$($.projectDir,$.workspace,Z.entity.external_id);await c8(Q,{recursive:!0});let J=m(Q,`${GZ(Z.episode.source_hash)}.json`);return await KZ(J,JSON.stringify(Z,yZ,2),"utf8"),await kZ(Q,Z),J}async function j$(Z){let $=R$(Z.projectDir,Z.workspace,Z.sysid),Q;try{Q=await N$($)}catch{return null}let J=`${GZ(Z.excludeSourceHash)}.json`,X=Q.filter((K)=>K.endsWith(".json")&&K!==J),Y=null;for(let K of X){let G=m($,K),W;try{let U=await wZ(G,"utf8");W=P$(JSON.parse(U))}catch{continue}let z=0;try{z=(await i8(G)).mtimeMs}catch{}let B=W.episode?.logged_at?Date.parse(W.episode.logged_at):NaN,V=Number.isNaN(B)?-1/0:B;if(Y===null||V>Y.loggedAt||V===Y.loggedAt&&z>Y.mtime)Y={doc:W,loggedAt:V,mtime:z}}return Y?.doc??null}async function i(Z,$,Q){let J=m(Z,".hydron","observations",$),X=`${GZ(Q)}.json`,Y;try{Y=await N$(J)}catch{return null}for(let K of Y){let G=m(J,K,X),W;try{let z=await wZ(G,"utf8");W=P$(JSON.parse(z))}catch{continue}return{path:G,doc:W}}return null}async function I$(Z){return(await i(Z.projectDir,Z.workspace,Z.flight_id))?.doc??null}async function C$(Z){let $=await i(Z.projectDir,Z.workspace,Z.flight_id);if(!$)throw Error(`flight not found: ${Z.flight_id} (workspace ${Z.workspace})`);let Q=[...new Set([...$.doc.tags??[],...Z.tags])];return $.doc.tags=Q,await KZ($.path,JSON.stringify($.doc,yZ,2),"utf8"),await kZ(_$($.path),$.doc),Q}async function M$(Z){let $=await i(Z.projectDir,Z.workspace,Z.flight_id);if(!$)throw Error(`flight not found: ${Z.flight_id} (workspace ${Z.workspace})`);return $.doc.findings??[]}async function S$(Z){let $=await i(Z.projectDir,Z.workspace,Z.flight_id);if(!$)return[];return($.doc.findings??[]).filter((Q)=>Q.origin==="agent")}async function w$(Z){let $=await i(Z.projectDir,Z.workspace,Z.flight_id);if(!$)throw Error(`flight not found: ${Z.flight_id} (workspace ${Z.workspace})`);let Q=$.doc.findings??[],J={...Z.finding,origin:Z.finding.origin??"agent",ruleset_version:Z.finding.ruleset_version||$.doc.ruleset_version};if(J.id&&Q.some((X)=>X.id===J.id))return{added:!1,id:J.id,total:Q.length,reason:"id already present"};if(Z.supersedesId){let X=Q.findIndex((Y)=>Y.id===Z.supersedesId);if(X>=0)Q.splice(X,1)}return Q.push(J),$.doc.findings=Q,await KZ($.path,JSON.stringify($.doc,yZ,2),"utf8"),await kZ(_$($.path),$.doc),{added:!0,id:J.id??"",total:Q.length}}function a8(Z,$){let Q=new Map;for(let X of Z)Q.set(X.name,X.value);let J=[];for(let X of $){let Y=Q.get(X.name);if(Y===void 0)continue;if(Y!==X.value)J.push({name:X.name,old:Y,new:X.value})}return J}function s8(Z,$){return a8(Z.params??[],$.params??[])}function t8(Z,$){let Q=new Map;for(let X of Z.findings??[])if(X.tier==="system")Q.set(X.check,X);let J=[];for(let X of $.findings??[]){if(X.tier!=="system")continue;let Y=Q.get(X.check);if(!Y)continue;if(Y.severity!==X.severity)J.push({check:X.check,old:Y.severity,new:X.severity})}return J}function y$(Z,$){return{param_changes:s8(Z,$),finding_changes:t8(Z,$)}}function k$(Z){let $=Z.map((V)=>V.episode?.source_hash??"?"),Q=Z.map((V)=>{let U=new Map;for(let L of V.params??[])U.set(L.name,L.value);return U}),J=[],X=new Set;for(let V of Q)for(let U of V.keys())X.add(U);for(let V of X){let U=Q.map((F)=>F.has(V)?F.get(V):null),L=U.filter((F)=>F!==null),A=new Set(L).size>1||U.some((F)=>F===null);if(L.length>0&&A)J.push({name:V,values:U})}J.sort((V,U)=>V.name.localeCompare(U.name));let Y=Z.map((V)=>{let U=new Map;for(let L of V.findings??[])if(L.tier==="system")U.set(L.check,{severity:L.severity,verdict:L.verdict});return U}),K=[],G=new Set;for(let V of Y)for(let U of V.keys())G.add(U);for(let V of G){let U=Y.map((A)=>A.get(V)?.severity??"OK"),L=Y.map((A)=>A.get(V)?.verdict??"SKIP");if(new Set(U).size>1)K.push({check:V,severities:U,verdicts:L})}K.sort((V,U)=>V.check.localeCompare(U.check));let W=Z.map((V)=>{let U=new Map;for(let L of V.measurements??[])U.set(L.name,L.value);return U}),z=[],B=new Set;for(let V of W)for(let U of V.keys())B.add(U);for(let V of B)z.push({name:V,values:W.map((U)=>U.has(V)?U.get(V):null)});return z.sort((V,U)=>V.name.localeCompare(U.name)),{flights:$,param_drift:J,finding_trend:K,metric_trend:z}}var v$=[163,149];async function J6(Z){let $=await h$(Z,"r");try{let Q=Buffer.alloc(2);return await $.read(Q,0,2,0),Q[0]===v$[0]&&Q[1]===v$[1]}finally{await $.close()}}async function Q6(Z){let $=await h$(Z,"r");try{let Q=e8("sha256"),J=Buffer.alloc(1048576),X=0;for(;;){let{bytesRead:Y}=await $.read(J,0,J.length,X);if(Y===0)break;Q.update(J.subarray(0,Y)),X+=Y}return Q.digest("hex").slice(0,16)}finally{await $.close()}}function X6(Z){let $=Z.filter((X)=>X.verdict==="FAIL").length,Q=Z.filter((X)=>X.severity==="CRITICAL"&&X.verdict==="FAIL").length,J=Z.filter((X)=>X.verdict==="PASS").length;return{total:Z.length,passed:J,anomalies:$,critical:Q}}async function b$(Z,$){if(await Z6(Z),!await J6(Z))throw Error(`Not an ArduPilot DataFlash .bin (missing 0xA3 0x95 header): ${Z}`);let Q=await Q6(Z),J=O$($.configDir??$.projectDir),{log:X,db:Y,cleanup:K}=await c(Z,Q,{configDir:$.configDir??$.projectDir});try{let G=T$(X,Y,{sourceHash:Q});G.episode.attrs.source_path=$6(Z);let W=J$(X);G.episode.attrs.vehicle_type=W;let z=K$(Y,J,{vehicleType:W});z=[...z,...JZ(G.params,{configDir:$.configDir??$.projectDir,ruleset_version:J.ruleset_version})];let B=await S$({projectDir:$.projectDir,workspace:$.workspace,flight_id:Q});z=[...z,...B];let V={schema_version:1,entity:G.entity,episode:G.episode,params:G.params,findings:z,events:G.events,measurements:G.measurements,ruleset_version:J.ruleset_version},U=await j$({projectDir:$.projectDir,workspace:$.workspace,sysid:G.entity.external_id,excludeSourceHash:Q}),L=await D$(V,{projectDir:$.projectDir,workspace:$.workspace}),A={flight_id:Q,vehicle_id:G.entity.external_id,metadata:{airframe:G.entity.attrs.name??G.entity.external_id,firmware:G.episode.attrs.firmware??null,duration_s:G.episode.attrs.duration_s??null},partial_decode:X.partial,decoded_until:X.decodedUntil,checks:z,summary:X6(z),ruleset_version:J.ruleset_version};if(U){let{param_changes:F,finding_changes:H}=y$(U,V);A.comparison={prior_flight_id:U.episode.source_hash,param_changes:F,finding_changes:H,narration:{claims:[]}}}return{report:A,doc:V,episodePath:L}}finally{K()}}var Y6=new URL(import.meta.url).pathname.endsWith(".js"),K6=Y6?new URL("./worker/worker-entry.js",import.meta.url).href:new URL("./worker-entry.ts",import.meta.url).href;function f$(Z){return new Promise(($,Q)=>{let J;try{J=new Worker(K6,{type:"module"})}catch(Y){Q(Y);return}let X=(Y)=>{try{J.terminate()}catch{}Y()};J.onmessage=(Y)=>{let{ok:K,result:G,error:W}=Y.data;X(()=>K?$(G):Q(Error(W??"worker failed")))},J.onerror=(Y)=>X(()=>Q(Error(Y.message??"worker error"))),J.postMessage(Z)})}async function g$(Z,$){try{return await f$({op:"analyze",binPath:Z,opts:$})}catch{return b$(Z,$)}}async function u$(Z){try{return await f$({op:"query",opts:Z})}catch{return Z$(Z)}}async function m$(Z){let $=[],Q=[];for(let X of Z.flight_ids){let Y=await I$({projectDir:Z.projectDir,workspace:Z.workspace,flight_id:X});if(Y)$.push(Y);else Q.push(X)}if($.length<2)throw Error(`need \u22652 analyzed flights to compare (found ${$.length}). Run uav_analyze on each first.`+(Q.length?` Not in store: ${Q.join(", ")}.`:""));let J=k$($);return{multi:J,markdown:G6(J,$,Q),missing:Q}}var x$=(Z)=>Z===null?"\u2014":Number.isInteger(Z)?String(Z):Z.toFixed(3);function G6(Z,$,Q){let J=$.map((G,W)=>{return`${G.entity?.attrs?.name??G.entity?.external_id??`flight${W+1}`} (${(G.episode?.source_hash??"?").slice(0,8)})`}),X=`| metric | ${J.join(" | ")} |`,Y=`| --- | ${J.map(()=>"---").join(" | ")} |`,K=[`# Flight comparison (${$.length} flights)`];if(Q.length)K.push(`> not found (skipped): ${Q.join(", ")}`);if(K.push(`
12
+ `,"utf8")}async function C$(Z,$){let Q=P$($.projectDir,$.workspace,Z.entity.external_id);await $6(Q,{recursive:!0});let J=p(Q,`${zZ(Z.episode.source_hash)}.json`);return await WZ(J,JSON.stringify(Z,vZ,2),"utf8"),await hZ(Q,Z),J}async function M$(Z){let $=P$(Z.projectDir,Z.workspace,Z.sysid),Q;try{Q=await D$($)}catch{return null}let J=`${zZ(Z.excludeSourceHash)}.json`,X=Q.filter((K)=>K.endsWith(".json")&&K!==J),Y=null;for(let K of X){let G=p($,K),W;try{let U=await kZ(G,"utf8");W=I$(JSON.parse(U))}catch{continue}let z=0;try{z=(await J6(G)).mtimeMs}catch{}let B=W.episode?.logged_at?Date.parse(W.episode.logged_at):NaN,V=Number.isNaN(B)?-1/0:B;if(Y===null||V>Y.loggedAt||V===Y.loggedAt&&z>Y.mtime)Y={doc:W,loggedAt:V,mtime:z}}return Y?.doc??null}async function r(Z,$,Q){let J=p(Z,".hydron","observations",$),X=`${zZ(Q)}.json`,Y;try{Y=await D$(J)}catch{return null}for(let K of Y){let G=p(J,K,X),W;try{let z=await kZ(G,"utf8");W=I$(JSON.parse(z))}catch{continue}return{path:G,doc:W}}return null}async function S$(Z){return(await r(Z.projectDir,Z.workspace,Z.flight_id))?.doc??null}async function w$(Z){let $=await r(Z.projectDir,Z.workspace,Z.flight_id);if(!$)throw Error(`flight not found: ${Z.flight_id} (workspace ${Z.workspace})`);let Q=[...new Set([...$.doc.tags??[],...Z.tags])];return $.doc.tags=Q,await WZ($.path,JSON.stringify($.doc,vZ,2),"utf8"),await hZ(j$($.path),$.doc),Q}async function y$(Z){let $=await r(Z.projectDir,Z.workspace,Z.flight_id);if(!$)throw Error(`flight not found: ${Z.flight_id} (workspace ${Z.workspace})`);return $.doc.findings??[]}async function k$(Z){let $=await r(Z.projectDir,Z.workspace,Z.flight_id);if(!$)return[];return($.doc.findings??[]).filter((Q)=>Q.origin==="agent")}async function v$(Z){let $=await r(Z.projectDir,Z.workspace,Z.flight_id);if(!$)throw Error(`flight not found: ${Z.flight_id} (workspace ${Z.workspace})`);let Q=$.doc.findings??[],J={...Z.finding,origin:Z.finding.origin??"agent",ruleset_version:Z.finding.ruleset_version||$.doc.ruleset_version};if(J.id&&Q.some((X)=>X.id===J.id))return{added:!1,id:J.id,total:Q.length,reason:"id already present"};if(Z.supersedesId){let X=Q.findIndex((Y)=>Y.id===Z.supersedesId);if(X>=0)Q.splice(X,1)}return Q.push(J),$.doc.findings=Q,await WZ($.path,JSON.stringify($.doc,vZ,2),"utf8"),await hZ(j$($.path),$.doc),{added:!0,id:J.id??"",total:Q.length}}function Y6(Z,$){let Q=new Map;for(let X of Z)Q.set(X.name,X.value);let J=[];for(let X of $){let Y=Q.get(X.name);if(Y===void 0)continue;if(Y!==X.value)J.push({name:X.name,old:Y,new:X.value})}return J}function K6(Z,$){return Y6(Z.params??[],$.params??[])}function G6(Z,$){let Q=new Map;for(let X of Z.findings??[])if(X.tier==="system")Q.set(X.check,X);let J=[];for(let X of $.findings??[]){if(X.tier!=="system")continue;let Y=Q.get(X.check);if(!Y)continue;if(Y.severity!==X.severity)J.push({check:X.check,old:Y.severity,new:X.severity})}return J}function h$(Z,$){return{param_changes:K6(Z,$),finding_changes:G6(Z,$)}}function b$(Z){let $=Z.map((V)=>V.episode?.source_hash??"?"),Q=Z.map((V)=>{let U=new Map;for(let H of V.params??[])U.set(H.name,H.value);return U}),J=[],X=new Set;for(let V of Q)for(let U of V.keys())X.add(U);for(let V of X){let U=Q.map((T)=>T.has(V)?T.get(V):null),H=U.filter((T)=>T!==null),E=new Set(H).size>1||U.some((T)=>T===null);if(H.length>0&&E)J.push({name:V,values:U})}J.sort((V,U)=>V.name.localeCompare(U.name));let Y=Z.map((V)=>{let U=new Map;for(let H of V.findings??[])if(H.tier==="system")U.set(H.check,{severity:H.severity,verdict:H.verdict});return U}),K=[],G=new Set;for(let V of Y)for(let U of V.keys())G.add(U);for(let V of G){let U=Y.map((E)=>E.get(V)?.severity??"OK"),H=Y.map((E)=>E.get(V)?.verdict??"SKIP");if(new Set(U).size>1)K.push({check:V,severities:U,verdicts:H})}K.sort((V,U)=>V.check.localeCompare(U.check));let W=Z.map((V)=>{let U=new Map;for(let H of V.measurements??[])U.set(H.name,H.value);return U}),z=[],B=new Set;for(let V of W)for(let U of V.keys())B.add(U);for(let V of B)z.push({name:V,values:W.map((U)=>U.has(V)?U.get(V):null)});return z.sort((V,U)=>V.name.localeCompare(U.name)),{flights:$,param_drift:J,finding_trend:K,metric_trend:z}}var f$=[163,149];async function V6(Z){let $=await g$(Z,"r");try{let Q=Buffer.alloc(2);return await $.read(Q,0,2,0),Q[0]===f$[0]&&Q[1]===f$[1]}finally{await $.close()}}async function U6(Z){let $=await g$(Z,"r");try{let Q=W6("sha256"),J=Buffer.alloc(1048576),X=0;for(;;){let{bytesRead:Y}=await $.read(J,0,J.length,X);if(Y===0)break;Q.update(J.subarray(0,Y)),X+=Y}return Q.digest("hex").slice(0,16)}finally{await $.close()}}function H6(Z){let $=Z.filter((X)=>X.verdict==="FAIL").length,Q=Z.filter((X)=>X.severity==="CRITICAL"&&X.verdict==="FAIL").length,J=Z.filter((X)=>X.verdict==="PASS").length;return{total:Z.length,passed:J,anomalies:$,critical:Q}}async function u$(Z,$){if(await z6(Z),!await V6(Z))throw Error(`Not an ArduPilot DataFlash .bin (missing 0xA3 0x95 header): ${Z}`);let Q=await U6(Z),J=E$($.configDir??$.projectDir),{log:X,db:Y,cleanup:K}=await o(Z,Q,{configDir:$.configDir??$.projectDir});try{let G=_$(X,Y,{sourceHash:Q});G.episode.attrs.source_path=B6(Z);let W=Y$(X);G.episode.attrs.vehicle_type=W;let z=z$(Y,J,{vehicleType:W});z=[...z,...XZ(G.params,{configDir:$.configDir??$.projectDir,ruleset_version:J.ruleset_version})];let B=await k$({projectDir:$.projectDir,workspace:$.workspace,flight_id:Q});z=[...z,...B];let V={schema_version:1,entity:G.entity,episode:G.episode,params:G.params,findings:z,events:G.events,measurements:G.measurements,ruleset_version:J.ruleset_version},U=await M$({projectDir:$.projectDir,workspace:$.workspace,sysid:G.entity.external_id,excludeSourceHash:Q}),H=await C$(V,{projectDir:$.projectDir,workspace:$.workspace}),E={flight_id:Q,vehicle_id:G.entity.external_id,metadata:{airframe:G.entity.attrs.name??G.entity.external_id,firmware:G.episode.attrs.firmware??null,duration_s:G.episode.attrs.duration_s??null},partial_decode:X.partial,decoded_until:X.decodedUntil,checks:z,summary:H6(z),ruleset_version:J.ruleset_version};if(U){let{param_changes:T,finding_changes:L}=h$(U,V);E.comparison={prior_flight_id:U.episode.source_hash,param_changes:T,finding_changes:L,narration:{claims:[]}}}return{report:E,doc:V,episodePath:H}}finally{K()}}var L6=new URL(import.meta.url).pathname.endsWith(".js"),O6=L6?new URL("./worker/worker-entry.js",import.meta.url).href:new URL("./worker-entry.ts",import.meta.url).href;function x$(Z){return new Promise(($,Q)=>{let J;try{J=new Worker(O6,{type:"module"})}catch(Y){Q(Y);return}let X=(Y)=>{try{J.terminate()}catch{}Y()};J.onmessage=(Y)=>{let{ok:K,result:G,error:W}=Y.data;X(()=>K?$(G):Q(Error(W??"worker failed")))},J.onerror=(Y)=>X(()=>Q(Error(Y.message??"worker error"))),J.postMessage(Z)})}async function m$(Z,$){try{return await x$({op:"analyze",binPath:Z,opts:$})}catch{return u$(Z,$)}}async function d$(Z){try{return await x$({op:"query",opts:Z})}catch{return Q$(Z)}}async function l$(Z){let $=[],Q=[];for(let X of Z.flight_ids){let Y=await S$({projectDir:Z.projectDir,workspace:Z.workspace,flight_id:X});if(Y)$.push(Y);else Q.push(X)}if($.length<2)throw Error(`need \u22652 analyzed flights to compare (found ${$.length}). Run uav_analyze on each first.`+(Q.length?` Not in store: ${Q.join(", ")}.`:""));let J=b$($);return{multi:J,markdown:A6(J,$,Q),missing:Q}}var p$=(Z)=>Z===null?"\u2014":Number.isInteger(Z)?String(Z):Z.toFixed(3);function A6(Z,$,Q){let J=$.map((G,W)=>{return`${G.entity?.attrs?.name??G.entity?.external_id??`flight${W+1}`} (${(G.episode?.source_hash??"?").slice(0,8)})`}),X=`| metric | ${J.join(" | ")} |`,Y=`| --- | ${J.map(()=>"---").join(" | ")} |`,K=[`# Flight comparison (${$.length} flights)`];if(Q.length)K.push(`> not found (skipped): ${Q.join(", ")}`);if(K.push(`
13
13
  ## Finding-severity trend (only checks that differ)`),Z.finding_trend.length===0)K.push("_no system check changed severity across these flights_");else{K.push(X,Y);for(let G of Z.finding_trend)K.push(`| ${G.check} | ${G.severities.join(" | ")} |`)}if(K.push(`
14
- ## Key-metric trend`),Z.metric_trend.length===0)K.push("_no measurements recorded_");else{K.push(X,Y);for(let G of Z.metric_trend)K.push(`| ${G.name} | ${G.values.map(x$).join(" | ")} |`)}if(K.push(`
15
- ## Parameter drift (only params that changed) \u2014 ${Z.param_drift.length} changed`),Z.param_drift.length>0){K.push(X,Y);for(let G of Z.param_drift.slice(0,50))K.push(`| ${G.name} | ${G.values.map(x$).join(" | ")} |`);if(Z.param_drift.length>50)K.push(`
14
+ ## Key-metric trend`),Z.metric_trend.length===0)K.push("_no measurements recorded_");else{K.push(X,Y);for(let G of Z.metric_trend)K.push(`| ${G.name} | ${G.values.map(p$).join(" | ")} |`)}if(K.push(`
15
+ ## Parameter drift (only params that changed) \u2014 ${Z.param_drift.length} changed`),Z.param_drift.length>0){K.push(X,Y);for(let G of Z.param_drift.slice(0,50))K.push(`| ${G.name} | ${G.values.map(p$).join(" | ")} |`);if(Z.param_drift.length>50)K.push(`
16
16
  \u2026 ${Z.param_drift.length-50} more changed params`)}return K.join(`
17
- `)}function W6(Z,$){let Q=Z.length;if(Q<=1)return;for(let J=1,X=0;J<Q;J++){let Y=Q>>1;for(;X&Y;Y>>=1)X^=Y;if(X^=Y,J<X)[Z[J],Z[X]]=[Z[X],Z[J]],[$[J],$[X]]=[$[X],$[J]]}for(let J=2;J<=Q;J<<=1){let X=-2*Math.PI/J,Y=Math.cos(X),K=Math.sin(X);for(let G=0;G<Q;G+=J){let W=1,z=0;for(let B=0;B<J>>1;B++){let V=Z[G+B],U=$[G+B],L=Z[G+B+(J>>1)],A=$[G+B+(J>>1)],F=L*W-A*z,H=L*z+A*W;Z[G+B]=V+F,$[G+B]=U+H,Z[G+B+(J>>1)]=V-F,$[G+B+(J>>1)]=U-H;let O=W*Y-z*K;z=W*K+z*Y,W=O}}}}function z6(Z){let $=1;while($<Z)$<<=1;return $}function B6(Z,$){let Q=Z.length;if(Q<4||!($>0))return{freqs:[],mags:[]};let J=Z.reduce((B,V)=>B+V,0)/Q,X=z6(Q),Y=Array(X).fill(0),K=Array(X).fill(0);for(let B=0;B<Q;B++){let V=0.5*(1-Math.cos(2*Math.PI*B/(Q-1)));Y[B]=(Z[B]-J)*V}W6(Y,K);let G=X>>1,W=Array(G),z=Array(G);for(let B=0;B<G;B++)W[B]=B*$/X,z[B]=Math.hypot(Y[B],K[B]);return{freqs:W,mags:z}}function V6(Z,$){return Z.columns.indexOf($)}function p(Z,$){let Q=V6(Z,$);if(Q<0)return[];let J=[];for(let X of Z.rows){let Y=X[Q],K=typeof Y==="bigint"?Number(Y):typeof Y==="number"?Y:Number(Y);if(Number.isFinite(K))J.push(K)}return J}function d$(Z){if(Z.length===0)return 0;let $=Z.reduce((J,X)=>J+X,0)/Z.length,Q=0;for(let J of Z){let X=J-$;Q+=X*X}return Q}function p$(Z,$){if($){let X=p(Z,$).filter((Y)=>Y>0);if(X.length>0){let Y=X.reduce((K,G)=>K+G,0)/X.length;if(Y>0)return{hz:Y,src:`${$} column (${Y.toFixed(0)} Hz)`}}}let Q=p(Z,"TimeUS");if(Q.length>=2){let J=[];for(let X=1;X<Q.length;X++){let Y=Q[X]-Q[X-1];if(Y>0)J.push(Y)}if(J.length>0){J.sort((Y,K)=>Y-K);let X=J[J.length>>1];if(X>0){let Y=1e6/X;return{hz:Y,src:`TimeUS deltas (median dt ${X.toFixed(0)} \xB5s \u2192 ${Y.toFixed(0)} Hz)`}}}}return{hz:0,src:"no sample rate available"}}function U6(Z){let $=Z.tables.IMU;if($&&$.rows.length>=4){let J=[{col:"GyrX",label:"GyrX"},{col:"GyrY",label:"GyrY"},{col:"GyrZ",label:"GyrZ"}],X=null;for(let Y of J){let K=p($,Y.col);if(K.length<4)continue;let G=d$(K);if(!X||G>X.energy)X={samples:K,axis:Y.label,energy:G}}if(X&&X.energy>0){let{hz:Y,src:K}=p$($,"GHz");if(Y>0)return{samples:X.samples,sampleRateHz:Y,axis:X.axis,rateSource:K}}}let Q=Z.tables.VIBE;if(Q&&Q.rows.length>=4){let J=[{col:"VibeX",label:"VibeX"},{col:"VibeY",label:"VibeY"},{col:"VibeZ",label:"VibeZ"}],X=null;for(let Y of J){let K=p(Q,Y.col);if(K.length<4)continue;let G=d$(K);if(!X||G>X.energy)X={samples:K,axis:Y.label,energy:G}}if(X&&X.energy>0){let{hz:Y,src:K}=p$(Q);if(Y>0)return{samples:X.samples,sampleRateHz:Y,axis:X.axis,rateSource:K}}}return null}function H6(Z,$,Q,J){let[X,Y]=J,K=[];for(let G=1;G<$.length-1;G++){let W=Z[G];if(W<X||W>Y)continue;if($[G]>=$[G-1]&&$[G]>=$[G+1])K.push({freq_hz:W,magnitude:$[G],axis:Q})}if(K.length===0){let G=-1;for(let W=0;W<$.length;W++){let z=Z[W];if(z<X||z>Y)continue;if(G<0||$[W]>$[G])G=W}if(G>=0)K.push({freq_hz:Z[G],magnitude:$[G],axis:Q})}return K.sort((G,W)=>W.magnitude-G.magnitude),K}function d(Z,$,Q){return Math.max($,Math.min(Q,Z))}var WZ={dominant_peak_hz:null,peaks:[],params:[],rationale:"insufficient gyro/vibe data"};function L6(Z){let $=Z.tables.ESC;if($&&p($,"RPM").some((Q)=>Q>0))return{mode:3,label:"ESC telemetry"};if(Z.tables.RPM)return{mode:2,label:"RPM sensor"};return{mode:1,label:"throttle-based"}}function l$(Z,$){try{let Q=$?.band_hz??[20,200],J=U6(Z);if(!J)return WZ;let{freqs:X,mags:Y}=B6(J.samples,J.sampleRateHz);if(X.length===0)return WZ;let K=H6(X,Y,J.axis,Q);if(K.length===0)return WZ;let G=K[0].freq_hz,W=K.slice(0,3),z={kind:"rule-of-thumb",ref:"ArduPilot harmonic notch tuning"},B=L6(Z),V=1,U;if(B.mode===1){let N=Z.tables.CTUN,_=N?p(N,"ThH").filter((T)=>T>=0&&T<=1):[];if(_.length>0)V=_.reduce((T,M)=>T+M,0)/_.length,U=`throttle-based (MODE 1): INS_HNTCH_REF = avg(CTUN.ThH) = ${V.toFixed(3)} (hover throttle)`;else U="throttle-based (MODE 1) but no CTUN.ThH \u2014 INS_HNTCH_REF defaulted to 1.0 (set it to your hover throttle)"}else U=`${B.label} (MODE ${B.mode}): the notch tracks measured RPM directly \u2014 INS_HNTCH_REF = 1.0 and INS_HNTCH_FREQ is the low-RPM floor. Preferred over throttle mode (holds the notch on the peak across the throttle range)`;let L=Math.round(G),A=Math.round(G/2),F=[{name:"INS_HNTCH_ENABLE",value:1,range:[0,1],source:z},{name:"INS_HNTCH_MODE",value:B.mode,range:[0,5],source:z},{name:"INS_HNTCH_FREQ",value:d(L,10,495),range:[10,495],source:z},{name:"INS_HNTCH_BW",value:d(A,5,250),range:[5,250],source:z},{name:"INS_HNTCH_REF",value:d(V,0,1),range:[0,1],source:z},{name:"INS_HNTCH_HMNCS",value:3,range:[0,255],source:z}],H="",O=W[1];if(O&&Math.abs(O.freq_hz-G)>15&&O.magnitude>=0.5*K[0].magnitude){let N=Math.round(O.freq_hz),_=Math.round(O.freq_hz/2);F.push({name:"INS_HNTC2_ENABLE",value:1,range:[0,1],source:z},{name:"INS_HNTC2_MODE",value:B.mode,range:[0,5],source:z},{name:"INS_HNTC2_FREQ",value:d(N,10,495),range:[10,495],source:z},{name:"INS_HNTC2_BW",value:d(_,5,250),range:[5,250],source:z},{name:"INS_HNTC2_REF",value:d(V,0,1),range:[0,1],source:z},{name:"INS_HNTC2_HMNCS",value:3,range:[0,255],source:z}),H=` A distinct second peak at ${O.freq_hz.toFixed(1)} Hz warrants the SECOND notch (INS_HNTC2_*) centred there.`}let E=`Dominant vibration peak at ${G.toFixed(1)} Hz on ${J.axis} (within the ${Q[0]}\u2013${Q[1]} Hz search band), found by FFT over ${J.samples.length} samples at a sample rate from ${J.rateSource}. Recommend a harmonic notch centred at ${L} Hz with bandwidth ${A} Hz `+"(\u2248 half the centre, a standard starting width) and the 1st+2nd harmonics "+`(HMNCS=3), tracked ${U}.${H} Review before applying; these are starting values to refine against a post-change FFT.`;return{dominant_peak_hz:G,peaks:W,params:F,rationale:E}}catch{return WZ}}import{mkdir as O6,writeFile as q6}from"fs/promises";import{join as A6}from"path";function E6(Z){if(Z===null||Z===void 0)return"";if(typeof Z==="bigint")return Z.toString();let $=String(Z);return/[",\n]/.test($)?`"${$.replace(/"/g,'""')}"`:$}async function vZ(Z,$){await O6($,{recursive:!0});let Q=[];for(let[J,X]of Object.entries(Z.tables)){if(!X.rows.length)continue;let Y=X.columns.join(","),K=X.rows.map((W)=>W.map(E6).join(",")).join(`
18
- `),G=A6($,`${J}.csv`);await q6(G,`${Y}
17
+ `)}function q6(Z,$){let Q=Z.length;if(Q<=1)return;for(let J=1,X=0;J<Q;J++){let Y=Q>>1;for(;X&Y;Y>>=1)X^=Y;if(X^=Y,J<X)[Z[J],Z[X]]=[Z[X],Z[J]],[$[J],$[X]]=[$[X],$[J]]}for(let J=2;J<=Q;J<<=1){let X=-2*Math.PI/J,Y=Math.cos(X),K=Math.sin(X);for(let G=0;G<Q;G+=J){let W=1,z=0;for(let B=0;B<J>>1;B++){let V=Z[G+B],U=$[G+B],H=Z[G+B+(J>>1)],E=$[G+B+(J>>1)],T=H*W-E*z,L=H*z+E*W;Z[G+B]=V+T,$[G+B]=U+L,Z[G+B+(J>>1)]=V-T,$[G+B+(J>>1)]=U-L;let A=W*Y-z*K;z=W*K+z*Y,W=A}}}}function E6(Z){let $=1;while($<Z)$<<=1;return $}function F6(Z,$){let Q=Z.length;if(Q<4||!($>0))return{freqs:[],mags:[]};let J=Z.reduce((B,V)=>B+V,0)/Q,X=E6(Q),Y=Array(X).fill(0),K=Array(X).fill(0);for(let B=0;B<Q;B++){let V=0.5*(1-Math.cos(2*Math.PI*B/(Q-1)));Y[B]=(Z[B]-J)*V}q6(Y,K);let G=X>>1,W=Array(G),z=Array(G);for(let B=0;B<G;B++)W[B]=B*$/X,z[B]=Math.hypot(Y[B],K[B]);return{freqs:W,mags:z}}function T6(Z,$){return Z.columns.indexOf($)}function n(Z,$){let Q=T6(Z,$);if(Q<0)return[];let J=[];for(let X of Z.rows){let Y=X[Q],K=typeof Y==="bigint"?Number(Y):typeof Y==="number"?Y:Number(Y);if(Number.isFinite(K))J.push(K)}return J}function n$(Z){if(Z.length===0)return 0;let $=Z.reduce((J,X)=>J+X,0)/Z.length,Q=0;for(let J of Z){let X=J-$;Q+=X*X}return Q}function c$(Z,$){if($){let X=n(Z,$).filter((Y)=>Y>0);if(X.length>0){let Y=X.reduce((K,G)=>K+G,0)/X.length;if(Y>0)return{hz:Y,src:`${$} column (${Y.toFixed(0)} Hz)`}}}let Q=n(Z,"TimeUS");if(Q.length>=2){let J=[];for(let X=1;X<Q.length;X++){let Y=Q[X]-Q[X-1];if(Y>0)J.push(Y)}if(J.length>0){J.sort((Y,K)=>Y-K);let X=J[J.length>>1];if(X>0){let Y=1e6/X;return{hz:Y,src:`TimeUS deltas (median dt ${X.toFixed(0)} \xB5s \u2192 ${Y.toFixed(0)} Hz)`}}}}return{hz:0,src:"no sample rate available"}}function N6(Z){let $=Z.tables.IMU;if($&&$.rows.length>=4){let J=[{col:"GyrX",label:"GyrX"},{col:"GyrY",label:"GyrY"},{col:"GyrZ",label:"GyrZ"}],X=null;for(let Y of J){let K=n($,Y.col);if(K.length<4)continue;let G=n$(K);if(!X||G>X.energy)X={samples:K,axis:Y.label,energy:G}}if(X&&X.energy>0){let{hz:Y,src:K}=c$($,"GHz");if(Y>0)return{samples:X.samples,sampleRateHz:Y,axis:X.axis,rateSource:K}}}let Q=Z.tables.VIBE;if(Q&&Q.rows.length>=4){let J=[{col:"VibeX",label:"VibeX"},{col:"VibeY",label:"VibeY"},{col:"VibeZ",label:"VibeZ"}],X=null;for(let Y of J){let K=n(Q,Y.col);if(K.length<4)continue;let G=n$(K);if(!X||G>X.energy)X={samples:K,axis:Y.label,energy:G}}if(X&&X.energy>0){let{hz:Y,src:K}=c$(Q);if(Y>0)return{samples:X.samples,sampleRateHz:Y,axis:X.axis,rateSource:K}}}return null}function _6(Z,$,Q,J){let[X,Y]=J,K=[];for(let G=1;G<$.length-1;G++){let W=Z[G];if(W<X||W>Y)continue;if($[G]>=$[G-1]&&$[G]>=$[G+1])K.push({freq_hz:W,magnitude:$[G],axis:Q})}if(K.length===0){let G=-1;for(let W=0;W<$.length;W++){let z=Z[W];if(z<X||z>Y)continue;if(G<0||$[W]>$[G])G=W}if(G>=0)K.push({freq_hz:Z[G],magnitude:$[G],axis:Q})}return K.sort((G,W)=>W.magnitude-G.magnitude),K}function l(Z,$,Q){return Math.max($,Math.min(Q,Z))}var BZ={dominant_peak_hz:null,peaks:[],params:[],rationale:"insufficient gyro/vibe data"};function R6(Z){let $=Z.tables.ESC;if($&&n($,"RPM").some((Q)=>Q>0))return{mode:3,label:"ESC telemetry"};if(Z.tables.RPM)return{mode:2,label:"RPM sensor"};return{mode:1,label:"throttle-based"}}function i$(Z,$){try{let Q=$?.band_hz??[20,200],J=N6(Z);if(!J)return BZ;let{freqs:X,mags:Y}=F6(J.samples,J.sampleRateHz);if(X.length===0)return BZ;let K=_6(X,Y,J.axis,Q);if(K.length===0)return BZ;let G=K[0].freq_hz,W=K.slice(0,3),z={kind:"rule-of-thumb",ref:"ArduPilot harmonic notch tuning"},B=R6(Z),V=1,U;if(B.mode===1){let F=Z.tables.CTUN,N=F?n(F,"ThH").filter((_)=>_>=0&&_<=1):[];if(N.length>0)V=N.reduce((_,D)=>_+D,0)/N.length,U=`throttle-based (MODE 1): INS_HNTCH_REF = avg(CTUN.ThH) = ${V.toFixed(3)} (hover throttle)`;else U="throttle-based (MODE 1) but no CTUN.ThH \u2014 INS_HNTCH_REF defaulted to 1.0 (set it to your hover throttle)"}else U=`${B.label} (MODE ${B.mode}): the notch tracks measured RPM directly \u2014 INS_HNTCH_REF = 1.0 and INS_HNTCH_FREQ is the low-RPM floor. Preferred over throttle mode (holds the notch on the peak across the throttle range)`;let H=Math.round(G),E=Math.round(G/2),T=[{name:"INS_HNTCH_ENABLE",value:1,range:[0,1],source:z},{name:"INS_HNTCH_MODE",value:B.mode,range:[0,5],source:z},{name:"INS_HNTCH_FREQ",value:l(H,10,495),range:[10,495],source:z},{name:"INS_HNTCH_BW",value:l(E,5,250),range:[5,250],source:z},{name:"INS_HNTCH_REF",value:l(V,0,1),range:[0,1],source:z},{name:"INS_HNTCH_HMNCS",value:3,range:[0,255],source:z}],L="",A=W[1];if(A&&Math.abs(A.freq_hz-G)>15&&A.magnitude>=0.5*K[0].magnitude){let F=Math.round(A.freq_hz),N=Math.round(A.freq_hz/2);T.push({name:"INS_HNTC2_ENABLE",value:1,range:[0,1],source:z},{name:"INS_HNTC2_MODE",value:B.mode,range:[0,5],source:z},{name:"INS_HNTC2_FREQ",value:l(F,10,495),range:[10,495],source:z},{name:"INS_HNTC2_BW",value:l(N,5,250),range:[5,250],source:z},{name:"INS_HNTC2_REF",value:l(V,0,1),range:[0,1],source:z},{name:"INS_HNTC2_HMNCS",value:3,range:[0,255],source:z}),L=` A distinct second peak at ${A.freq_hz.toFixed(1)} Hz warrants the SECOND notch (INS_HNTC2_*) centred there.`}let O=`Dominant vibration peak at ${G.toFixed(1)} Hz on ${J.axis} (within the ${Q[0]}\u2013${Q[1]} Hz search band), found by FFT over ${J.samples.length} samples at a sample rate from ${J.rateSource}. Recommend a harmonic notch centred at ${H} Hz with bandwidth ${E} Hz `+"(\u2248 half the centre, a standard starting width) and the 1st+2nd harmonics "+`(HMNCS=3), tracked ${U}.${L} Review before applying; these are starting values to refine against a post-change FFT.`;return{dominant_peak_hz:G,peaks:W,params:T,rationale:O}}catch{return BZ}}import{mkdir as D6,writeFile as j6}from"fs/promises";import{join as P6}from"path";function I6(Z){if(Z===null||Z===void 0)return"";if(typeof Z==="bigint")return Z.toString();let $=String(Z);return/[",\n]/.test($)?`"${$.replace(/"/g,'""')}"`:$}async function bZ(Z,$){await D6($,{recursive:!0});let Q=[];for(let[J,X]of Object.entries(Z.tables)){if(!X.rows.length)continue;let Y=X.columns.join(","),K=X.rows.map((W)=>W.map(I6).join(",")).join(`
18
+ `),G=P6($,`${J}.csv`);await j6(G,`${Y}
19
19
  ${K}
20
- `),Q.push(G)}return Q}function hZ(Z,$){let Q=$.toLowerCase();return Z.findIndex((J)=>J.toLowerCase()===Q)}function b(Z){if(typeof Z==="number")return Number.isFinite(Z)?Z:null;if(typeof Z==="bigint")return Number(Z);if(typeof Z==="string"){let $=Number(Z);return Number.isFinite($)?$:null}return null}function o(Z,$){let Q,J;if($.includes("."))[Q,J]=$.split(".",2);else J=$,Q=Object.keys(Z.tables).find((K)=>hZ(Z.tables[K].columns,J)>=0);if(!Q)return null;let X=Z.tables[Q];if(!X)return null;let Y=hZ(X.columns,J);if(Y<0)return null;return{table:X,tableName:Q,ci:Y,ti:hZ(X.columns,"TimeUS")}}function i$(Z,$){if(Z.length<=$)return Z;let Q=Z.length/$,J=[];for(let X=0;X<$;X++){let Y=Math.floor(X*Q),K=Math.min(Z.length,Math.floor((X+1)*Q)),G=Z[Y];for(let W=Y;W<K;W++)if(Math.abs(Z[W][1])>Math.abs(G[1]))G=Z[W];J.push(G)}return J}function zZ(Z,$,Q=1200){let J=o(Z,$);if(!J)return null;let X=[],Y=null;for(let K of J.table.rows){let G=b(K[J.ci]);if(G===null)continue;let W=J.ti>=0?b(K[J.ti]):X.length,z=W===null?X.length:W;if(Y===null)Y=z;X.push([J.ti>=0?(z-Y)/1e6:z,G])}if(!X.length)return null;return{name:$,points:i$(X,Q)}}function BZ(Z,$,Q){let J=o(Z,$);if(!J||J.ti<0)return zZ(Z,$)?T6(zZ(Z,$),Q):null;let X=new Map,Y=null;for(let K of J.table.rows){let G=b(K[J.ci]),W=b(K[J.ti]);if(G===null||W===null)continue;if(Y===null)Y=W;let z=Math.floor((W-Y)/1e6/Q),B=X.get(z)??{sum:0,n:0};B.sum+=G,B.n++,X.set(z,B)}if(!X.size)return null;return new Map([...X].map(([K,G])=>[K,G.sum/G.n]))}function T6(Z,$){let Q=new Map;for(let[J,X]of Z.points){let Y=Math.floor(J/$),K=Q.get(Y)??{sum:0,n:0};K.sum+=X,K.n++,Q.set(Y,K)}return new Map([...Q].map(([J,X])=>[J,X.sum/X.n]))}var F6=/^\s*(.+?)\s*([-+*/])\s*(.+?)\s*$/;function bZ(Z,$,Q){return $==="+"?Z+Q:$==="-"?Z-Q:$==="*"?Z*Q:Q===0?NaN:Z/Q}function N6(Z,$,Q=1200){let J=F6.exec($);if(!J)return zZ(Z,$.trim(),Q);let[,X,Y,K]=J,G=Number(K),W=o(Z,X);if(!W)return null;if(Number.isFinite(G)&&!K.includes(".")){let L=zZ(Z,X,Q);if(!L)return null;return{name:$.trim(),points:L.points.map(([A,F])=>[A,bZ(F,Y,G)])}}let z=o(Z,K);if(!z)return null;if(W.tableName===z.tableName&&W.ti>=0){let L=[],A=null;for(let F of W.table.rows){let H=b(F[W.ci]),O=b(F[z.ci]),E=b(F[W.ti]);if(H===null||O===null||E===null)continue;if(A===null)A=E;L.push([(E-A)/1e6,bZ(H,Y,O)])}return L.length?{name:$.trim(),points:i$(L,Q)}:null}let B=BZ(Z,X,0.5),V=BZ(Z,K,0.5);if(!B||!V)return null;let U=[];for(let[L,A]of B){let F=V.get(L);if(F!==void 0)U.push([L*0.5,bZ(A,Y,F)])}return U.length?{name:$.trim(),points:U.sort((L,A)=>L[0]-A[0])}:null}function _6(Z,$,Q,J=0.5){let X=BZ(Z,$,J),Y=BZ(Z,Q,J);if(!X||!Y)return null;let K=[];for(let[G,W]of X){let z=Y.get(G);if(z!==void 0)K.push([W,z])}if(!K.length)return null;return{name:`${Q} vs ${$}`,points:K.sort((G,W)=>G[0]-W[0]),kind:"scatter"}}function R6(Z,$){let Q=Z.length;for(let J=1,X=0;J<Q;J++){let Y=Q>>1;for(;X&Y;Y>>=1)X^=Y;if(X^=Y,J<X)[Z[J],Z[X]]=[Z[X],Z[J]],[$[J],$[X]]=[$[X],$[J]]}for(let J=2;J<=Q;J<<=1){let X=-2*Math.PI/J,Y=Math.cos(X),K=Math.sin(X);for(let G=0;G<Q;G+=J){let W=1,z=0;for(let B=0;B<J/2;B++){let V=Z[G+B],U=$[G+B],L=Z[G+B+J/2]*W-$[G+B+J/2]*z,A=Z[G+B+J/2]*z+$[G+B+J/2]*W;Z[G+B]=V+L,$[G+B]=U+A,Z[G+B+J/2]=V-L,$[G+B+J/2]=U-A;let F=W*Y-z*K;z=W*K+z*Y,W=F}}}}function P6(Z,$,Q={}){let J=o(Z,$);if(!J||J.ti<0)return null;let X=[],Y=[],K=-1/0;for(let H of J.table.rows){let O=b(H[J.ci]),E=b(H[J.ti]);if(O===null||E===null||E<=K)continue;X.push(O),Y.push(E),K=E}if(X.length<16)return null;let G=[];for(let H=1;H<Y.length;H++)G.push(Y[H]-Y[H-1]);G.sort((H,O)=>H-O);let W=G[G.length>>1]/1e6;if(!(W>0))return null;let z=1/W,B=X.reduce((H,O)=>H+O,0)/X.length,V=1;while(V<X.length)V<<=1;let U=Array(V).fill(0),L=Array(V).fill(0);for(let H=0;H<X.length;H++)U[H]=X[H]-B;R6(U,L);let A=Q.fMax??Math.min(z/2,250),F=[];for(let H=1;H<V/2;H++){let O=H*z/V;if(O>A)break;F.push([O,Math.hypot(U[H],L[H])/X.length])}return F.length?{name:`FFT ${$}`,points:F}:null}var n$=["#2563eb","#dc2626","#16a34a","#d97706","#9333ea","#0891b2"];function c$(Z,$={}){let Q=$.width??900,J=$.height??380,X={l:60,r:150,t:34,b:40},Y=Q-X.l-X.r,K=J-X.t-X.b,G=(N)=>N.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),W=Z.flatMap((N)=>N.points);if(!W.length)return`<svg xmlns="http://www.w3.org/2000/svg" width="${Q}" height="${J}"><text x="20" y="30">no data</text></svg>`;let z=W.map((N)=>N[0]),B=W.map((N)=>N[1]),V=Math.min(...z),U=Math.max(...z),L=Math.min(...B),A=Math.max(...B);if(U===V)U=V+1;if(A===L)A=L+1,L-=1;let F=(A-L)*0.06;L-=F,A+=F;let H=(N)=>X.l+(N-V)/(U-V)*Y,O=(N)=>X.t+(1-(N-L)/(A-L))*K,E=[];if(E.push(`<svg xmlns="http://www.w3.org/2000/svg" width="${Q}" height="${J}" font-family="ui-sans-serif,system-ui,sans-serif" font-size="12">`),E.push(`<rect width="${Q}" height="${J}" fill="#ffffff"/>`),$.title)E.push(`<text x="${X.l}" y="20" font-weight="600">${G($.title)}</text>`);E.push(`<rect x="${X.l}" y="${X.t}" width="${Y}" height="${K}" fill="none" stroke="#e5e7eb"/>`);for(let N=0;N<=5;N++){let _=L+N/5*(A-L),T=O(_);E.push(`<line x1="${X.l}" y1="${T}" x2="${X.l+Y}" y2="${T}" stroke="#f1f5f9"/>`),E.push(`<text x="${X.l-6}" y="${T+4}" text-anchor="end" fill="#64748b">${_.toFixed(2)}</text>`)}for(let N of[0,0.5,1]){let _=V+N*(U-V);E.push(`<text x="${H(_)}" y="${X.t+K+22}" text-anchor="middle" fill="#64748b">${_.toFixed(U-V<10?2:0)}</text>`)}return E.push(`<text x="${X.l+Y/2}" y="${J-6}" text-anchor="middle" fill="#475569">${G($.xLabel??"s")}</text>`),Z.forEach((N,_)=>{let T=n$[_%n$.length];if(N.kind==="scatter")for(let M of N.points)E.push(`<circle cx="${H(M[0]).toFixed(1)}" cy="${O(M[1]).toFixed(1)}" r="1.8" fill="${T}" fill-opacity="0.6"/>`);else{let M=N.points.map((S,I)=>`${I===0?"M":"L"}${H(S[0]).toFixed(1)},${O(S[1]).toFixed(1)}`).join(" ");E.push(`<path d="${M}" fill="none" stroke="${T}" stroke-width="1.2"/>`)}E.push(`<rect x="${X.l+Y+14}" y="${X.t+4+_*18}" width="10" height="10" fill="${T}"/>`),E.push(`<text x="${X.l+Y+28}" y="${X.t+13+_*18}" fill="#334155">${G(N.name)}</text>`)}),E.push("</svg>"),E.join("")}function o$(Z,$){let Q=$.mode??"timeseries",J=[],X=[],Y="s";if(Q==="xy"){if(!$.x||!$.y)return{svg:c$([]),plotted:[],missing:["x and y required for xy mode"],xLabel:""};let G=_6(Z,$.x,$.y);if(G)J.push(G);else X.push(`${$.y} vs ${$.x}`);Y=$.x}else if(Q==="fft"){for(let G of $.signals??[]){let W=P6(Z,G);if(W)J.push(W);else X.push(G)}Y="Hz"}else{for(let G of $.signals??[]){let W=N6(Z,G,$.points??1200);if(W&&W.points.length)J.push(W);else X.push(G)}Y="s"}let K=$.title??(J.map((G)=>G.name).join(", ")||Q);return{svg:c$(J,{title:K,xLabel:Y}),plotted:J.map((G)=>G.name),missing:X,xLabel:Y}}var D6=[{match:/\bVIBE\b|\bVibe[XYZ]\b/i,note:"VIBE = whole-airframe vibration at the IMU (per IMU instance) \u2014 NOT per-motor. To blame a motor, correlate the FFT peak Hz with that motor's RPM."},{match:/CTUN\.ThH\b|\bThH\b/i,note:"CTUN.ThH = slow hover-throttle estimate (~constant), NOT instantaneous throttle \u2014 use RCOU (motor PWM) or CTUN.ThO."},{match:/\bRCOU\b|RCOU\.C\d/i,note:"RCOU.Cn = per-motor/servo PWM output (\xB5s)."},{match:/XKF3|NKF3|\bI[VP][NEDT]\b/i,note:"XKF3/NKF3 innovations: velocity (IVN/IVE/IVD) reflect vibration stress; position (IPN/IPE/IPD) reflect GPS."}];function r$(Z){let $=Z.join(" "),Q=[];for(let{match:J,note:X}of D6)if(J.test($)&&!Q.includes(X))Q.push(X);return Q}import{readdir as j6,writeFile as I6}from"fs/promises";import{join as C6}from"path";async function a$(Z,$,Q={}){let J=(await j6(Z).catch(()=>[])).filter((U)=>U.endsWith(".csv")).map((U)=>U.slice(0,-4)).sort(),X=["import os, sys, json","import pandas as pd, numpy as np",`CSV_DIR = ${JSON.stringify(Z)}`,"_cache = {}","def load(name):"," if name not in _cache:"," _cache[name] = pd.read_csv(os.path.join(CSV_DIR, name + '.csv'))"," return _cache[name]",`# Message tables available via load('NAME'): ${J.join(", ")}`,"# TimeUS is microseconds since boot; seconds = (TimeUS - TimeUS.iloc[0]) / 1e6",""].join(`
21
- `),Y=C6(Z,"_uav_snippet.py");await I6(Y,X+`
20
+ `),Q.push(G)}return Q}function fZ(Z,$){let Q=$.toLowerCase();return Z.findIndex((J)=>J.toLowerCase()===Q)}function f(Z){if(typeof Z==="number")return Number.isFinite(Z)?Z:null;if(typeof Z==="bigint")return Number(Z);if(typeof Z==="string"){let $=Number(Z);return Number.isFinite($)?$:null}return null}function a(Z,$){let Q,J;if($.includes("."))[Q,J]=$.split(".",2);else J=$,Q=Object.keys(Z.tables).find((K)=>fZ(Z.tables[K].columns,J)>=0);if(!Q)return null;let X=Z.tables[Q];if(!X)return null;let Y=fZ(X.columns,J);if(Y<0)return null;return{table:X,tableName:Q,ci:Y,ti:fZ(X.columns,"TimeUS")}}function a$(Z,$){if(Z.length<=$)return Z;let Q=Z.length/$,J=[];for(let X=0;X<$;X++){let Y=Math.floor(X*Q),K=Math.min(Z.length,Math.floor((X+1)*Q)),G=Z[Y];for(let W=Y;W<K;W++)if(Math.abs(Z[W][1])>Math.abs(G[1]))G=Z[W];J.push(G)}return J}function VZ(Z,$,Q=1200){let J=a(Z,$);if(!J)return null;let X=[],Y=null;for(let K of J.table.rows){let G=f(K[J.ci]);if(G===null)continue;let W=J.ti>=0?f(K[J.ti]):X.length,z=W===null?X.length:W;if(Y===null)Y=z;X.push([J.ti>=0?(z-Y)/1e6:z,G])}if(!X.length)return null;return{name:$,points:a$(X,Q)}}function UZ(Z,$,Q){let J=a(Z,$);if(!J||J.ti<0)return VZ(Z,$)?C6(VZ(Z,$),Q):null;let X=new Map,Y=null;for(let K of J.table.rows){let G=f(K[J.ci]),W=f(K[J.ti]);if(G===null||W===null)continue;if(Y===null)Y=W;let z=Math.floor((W-Y)/1e6/Q),B=X.get(z)??{sum:0,n:0};B.sum+=G,B.n++,X.set(z,B)}if(!X.size)return null;return new Map([...X].map(([K,G])=>[K,G.sum/G.n]))}function C6(Z,$){let Q=new Map;for(let[J,X]of Z.points){let Y=Math.floor(J/$),K=Q.get(Y)??{sum:0,n:0};K.sum+=X,K.n++,Q.set(Y,K)}return new Map([...Q].map(([J,X])=>[J,X.sum/X.n]))}var M6=/^\s*(.+?)\s*([-+*/])\s*(.+?)\s*$/;function gZ(Z,$,Q){return $==="+"?Z+Q:$==="-"?Z-Q:$==="*"?Z*Q:Q===0?NaN:Z/Q}function S6(Z,$,Q=1200){let J=M6.exec($);if(!J)return VZ(Z,$.trim(),Q);let[,X,Y,K]=J,G=Number(K),W=a(Z,X);if(!W)return null;if(Number.isFinite(G)&&!K.includes(".")){let H=VZ(Z,X,Q);if(!H)return null;return{name:$.trim(),points:H.points.map(([E,T])=>[E,gZ(T,Y,G)])}}let z=a(Z,K);if(!z)return null;if(W.tableName===z.tableName&&W.ti>=0){let H=[],E=null;for(let T of W.table.rows){let L=f(T[W.ci]),A=f(T[z.ci]),O=f(T[W.ti]);if(L===null||A===null||O===null)continue;if(E===null)E=O;H.push([(O-E)/1e6,gZ(L,Y,A)])}return H.length?{name:$.trim(),points:a$(H,Q)}:null}let B=UZ(Z,X,0.5),V=UZ(Z,K,0.5);if(!B||!V)return null;let U=[];for(let[H,E]of B){let T=V.get(H);if(T!==void 0)U.push([H*0.5,gZ(E,Y,T)])}return U.length?{name:$.trim(),points:U.sort((H,E)=>H[0]-E[0])}:null}function w6(Z,$,Q,J=0.5){let X=UZ(Z,$,J),Y=UZ(Z,Q,J);if(!X||!Y)return null;let K=[];for(let[G,W]of X){let z=Y.get(G);if(z!==void 0)K.push([W,z])}if(!K.length)return null;return{name:`${Q} vs ${$}`,points:K.sort((G,W)=>G[0]-W[0]),kind:"scatter"}}function y6(Z,$){let Q=Z.length;for(let J=1,X=0;J<Q;J++){let Y=Q>>1;for(;X&Y;Y>>=1)X^=Y;if(X^=Y,J<X)[Z[J],Z[X]]=[Z[X],Z[J]],[$[J],$[X]]=[$[X],$[J]]}for(let J=2;J<=Q;J<<=1){let X=-2*Math.PI/J,Y=Math.cos(X),K=Math.sin(X);for(let G=0;G<Q;G+=J){let W=1,z=0;for(let B=0;B<J/2;B++){let V=Z[G+B],U=$[G+B],H=Z[G+B+J/2]*W-$[G+B+J/2]*z,E=Z[G+B+J/2]*z+$[G+B+J/2]*W;Z[G+B]=V+H,$[G+B]=U+E,Z[G+B+J/2]=V-H,$[G+B+J/2]=U-E;let T=W*Y-z*K;z=W*K+z*Y,W=T}}}}function k6(Z,$,Q={}){let J=a(Z,$);if(!J||J.ti<0)return null;let X=[],Y=[],K=-1/0;for(let L of J.table.rows){let A=f(L[J.ci]),O=f(L[J.ti]);if(A===null||O===null||O<=K)continue;X.push(A),Y.push(O),K=O}if(X.length<16)return null;let G=[];for(let L=1;L<Y.length;L++)G.push(Y[L]-Y[L-1]);G.sort((L,A)=>L-A);let W=G[G.length>>1]/1e6;if(!(W>0))return null;let z=1/W,B=X.reduce((L,A)=>L+A,0)/X.length,V=1;while(V<X.length)V<<=1;let U=Array(V).fill(0),H=Array(V).fill(0);for(let L=0;L<X.length;L++)U[L]=X[L]-B;y6(U,H);let E=Q.fMax??Math.min(z/2,250),T=[];for(let L=1;L<V/2;L++){let A=L*z/V;if(A>E)break;T.push([A,Math.hypot(U[L],H[L])/X.length])}return T.length?{name:`FFT ${$}`,points:T}:null}var o$=["#2563eb","#dc2626","#16a34a","#d97706","#9333ea","#0891b2"];function r$(Z,$={}){let Q=$.width??900,J=$.height??380,X={l:60,r:150,t:34,b:40},Y=Q-X.l-X.r,K=J-X.t-X.b,G=(F)=>F.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),W=Z.flatMap((F)=>F.points);if(!W.length)return`<svg xmlns="http://www.w3.org/2000/svg" width="${Q}" height="${J}"><text x="20" y="30">no data</text></svg>`;let z=W.map((F)=>F[0]),B=W.map((F)=>F[1]),V=Math.min(...z),U=Math.max(...z),H=Math.min(...B),E=Math.max(...B);if(U===V)U=V+1;if(E===H)E=H+1,H-=1;let T=(E-H)*0.06;H-=T,E+=T;let L=(F)=>X.l+(F-V)/(U-V)*Y,A=(F)=>X.t+(1-(F-H)/(E-H))*K,O=[];if(O.push(`<svg xmlns="http://www.w3.org/2000/svg" width="${Q}" height="${J}" font-family="ui-sans-serif,system-ui,sans-serif" font-size="12">`),O.push(`<rect width="${Q}" height="${J}" fill="#ffffff"/>`),$.title)O.push(`<text x="${X.l}" y="20" font-weight="600">${G($.title)}</text>`);O.push(`<rect x="${X.l}" y="${X.t}" width="${Y}" height="${K}" fill="none" stroke="#e5e7eb"/>`);for(let F=0;F<=5;F++){let N=H+F/5*(E-H),_=A(N);O.push(`<line x1="${X.l}" y1="${_}" x2="${X.l+Y}" y2="${_}" stroke="#f1f5f9"/>`),O.push(`<text x="${X.l-6}" y="${_+4}" text-anchor="end" fill="#64748b">${N.toFixed(2)}</text>`)}for(let F of[0,0.5,1]){let N=V+F*(U-V);O.push(`<text x="${L(N)}" y="${X.t+K+22}" text-anchor="middle" fill="#64748b">${N.toFixed(U-V<10?2:0)}</text>`)}return O.push(`<text x="${X.l+Y/2}" y="${J-6}" text-anchor="middle" fill="#475569">${G($.xLabel??"s")}</text>`),Z.forEach((F,N)=>{let _=o$[N%o$.length];if(F.kind==="scatter")for(let D of F.points)O.push(`<circle cx="${L(D[0]).toFixed(1)}" cy="${A(D[1]).toFixed(1)}" r="1.8" fill="${_}" fill-opacity="0.6"/>`);else{let D=F.points.map((w,M)=>`${M===0?"M":"L"}${L(w[0]).toFixed(1)},${A(w[1]).toFixed(1)}`).join(" ");O.push(`<path d="${D}" fill="none" stroke="${_}" stroke-width="1.2"/>`)}O.push(`<rect x="${X.l+Y+14}" y="${X.t+4+N*18}" width="10" height="10" fill="${_}"/>`),O.push(`<text x="${X.l+Y+28}" y="${X.t+13+N*18}" fill="#334155">${G(F.name)}</text>`)}),O.push("</svg>"),O.join("")}function s$(Z,$){let Q=$.mode??"timeseries",J=[],X=[],Y="s";if(Q==="xy"){if(!$.x||!$.y)return{svg:r$([]),plotted:[],missing:["x and y required for xy mode"],xLabel:""};let G=w6(Z,$.x,$.y);if(G)J.push(G);else X.push(`${$.y} vs ${$.x}`);Y=$.x}else if(Q==="fft"){for(let G of $.signals??[]){let W=k6(Z,G);if(W)J.push(W);else X.push(G)}Y="Hz"}else{for(let G of $.signals??[]){let W=S6(Z,G,$.points??1200);if(W&&W.points.length)J.push(W);else X.push(G)}Y="s"}let K=$.title??(J.map((G)=>G.name).join(", ")||Q);return{svg:r$(J,{title:K,xLabel:Y}),plotted:J.map((G)=>G.name),missing:X,xLabel:Y}}var v6=[{match:/\bVIBE\b|\bVibe[XYZ]\b/i,note:"VIBE = whole-airframe vibration at the IMU (per IMU instance) \u2014 NOT per-motor. To blame a motor, correlate the FFT peak Hz with that motor's RPM."},{match:/CTUN\.ThH\b|\bThH\b/i,note:"CTUN.ThH = slow hover-throttle estimate (~constant), NOT instantaneous throttle \u2014 use RCOU (motor PWM) or CTUN.ThO."},{match:/\bRCOU\b|RCOU\.C\d/i,note:"RCOU.Cn = per-motor/servo PWM output (\xB5s)."},{match:/XKF3|NKF3|\bI[VP][NEDT]\b/i,note:"XKF3/NKF3 innovations: velocity (IVN/IVE/IVD) reflect vibration stress; position (IPN/IPE/IPD) reflect GPS."}];function t$(Z){let $=Z.join(" "),Q=[];for(let{match:J,note:X}of v6)if(J.test($)&&!Q.includes(X))Q.push(X);return Q}import{readdir as h6,writeFile as b6}from"fs/promises";import{join as f6}from"path";async function e$(Z,$,Q={}){let J=(await h6(Z).catch(()=>[])).filter((H)=>H.endsWith(".csv")).map((H)=>H.slice(0,-4)).sort(),X=["import os, sys, json","import pandas as pd, numpy as np",`CSV_DIR = ${JSON.stringify(Z)}`,"_cache = {}","def load(name):"," if name not in _cache:"," _cache[name] = pd.read_csv(os.path.join(CSV_DIR, name + '.csv'))"," return _cache[name]",`# Message tables available via load('NAME'): ${J.join(", ")}`,"# TimeUS is microseconds since boot; seconds = (TimeUS - TimeUS.iloc[0]) / 1e6",""].join(`
21
+ `),Y=f6(Z,"_uav_snippet.py");await b6(Y,X+`
22
22
  `+$+`
23
- `);let K=Bun.spawn(["python3",Y],{stdout:"pipe",stderr:"pipe"}),G=setTimeout(()=>K.kill(9),Q.timeoutMs??30000),[W,z,B]=await Promise.all([new Response(K.stdout).text(),new Response(K.stderr).text(),K.exited]);return clearTimeout(G),{stdout:(W+(z.trim()?`
23
+ `);let K=await x(),G=Bun.spawn([...K,Y],{stdout:"pipe",stderr:"pipe"}),W=setTimeout(()=>G.kill(9),Q.timeoutMs??30000),[z,B,V]=await Promise.all([new Response(G.stdout).text(),new Response(G.stderr).text(),G.exited]);return clearTimeout(W),{stdout:(z+(B.trim()?`
24
24
  --- stderr ---
25
- ${z.trim()}`:"")).trim().slice(0,12000),ok:B===0,tables:J}}import{existsSync as w6}from"fs";async function l(Z,$){let Q=t(Z,s($?.configDir));if(Q){let{out:X,cleanup:Y}=await e(Z,Q);try{return await l(X)}finally{await Y()}}let J=Z.toLowerCase();if(J.endsWith(".bin"))return n(Z);if(J.endsWith(".ulog"))return a(Z);if(J.endsWith(".tlog")||J.endsWith(".log"))return u(Z);return n(Z).catch(()=>u(Z))}function y6(Z){let $=[];if(Z.partial_decode)$.push(`\u26A0\uFE0F PARTIAL DECODE \u2014 log truncated at TimeUS=${Z.decoded_until}. Post-cut checks were SKIPPED.`);$.push(`Flight ${Z.flight_id} \xB7 ${Z.metadata.airframe}`+(Z.metadata.firmware?` \xB7 ${Z.metadata.firmware}`:"")+(Z.metadata.duration_s?` \xB7 ${Math.round(Z.metadata.duration_s/60)}min`:"")),$.push(`Checks: ${Z.summary.total} run \xB7 ${Z.summary.passed} clean \xB7 ${Z.summary.anomalies} anomalies (${Z.summary.critical} critical) \xB7 ruleset ${Z.ruleset_version}`);let Q=Z.checks.filter((X)=>X.verdict==="FAIL").sort((X,Y)=>s$(Y.severity)-s$(X.severity));for(let X of Q){let Y=X.source?` [${X.source.kind}${X.source.ref?`: ${X.source.ref}`:""}]`:"";$.push(` ${X.severity.padEnd(8)} ${X.check}: value=${X.value??"\u2014"} vs threshold=${X.threshold_used??"\u2014"}${Y}`)}let J=Z.checks.filter((X)=>X.verdict==="SKIP").map((X)=>X.check);if(J.length)$.push(` (skipped \u2014 data absent: ${J.join(", ")})`);if(Z.comparison){$.push(`
25
+ ${B.trim()}`:"")).trim().slice(0,12000),ok:V===0,tables:J}}import{existsSync as x6}from"fs";async function c(Z,$){let Q=ZZ(Z,e($?.configDir));if(Q){let{out:X,cleanup:Y}=await $Z(Z,Q);try{return await c(X)}finally{await Y()}}let J=Z.toLowerCase();if(J.endsWith(".bin"))return i(Z);if(J.endsWith(".ulog"))return t(Z);if(J.endsWith(".tlog")||J.endsWith(".log"))return m(Z);return i(Z).catch(()=>m(Z))}function m6(Z){let $=[];if(Z.partial_decode)$.push(`\u26A0\uFE0F PARTIAL DECODE \u2014 log truncated at TimeUS=${Z.decoded_until}. Post-cut checks were SKIPPED.`);$.push(`Flight ${Z.flight_id} \xB7 ${Z.metadata.airframe}`+(Z.metadata.firmware?` \xB7 ${Z.metadata.firmware}`:"")+(Z.metadata.duration_s?` \xB7 ${Math.round(Z.metadata.duration_s/60)}min`:"")),$.push(`Checks: ${Z.summary.total} run \xB7 ${Z.summary.passed} clean \xB7 ${Z.summary.anomalies} anomalies (${Z.summary.critical} critical) \xB7 ruleset ${Z.ruleset_version}`);let Q=Z.checks.filter((X)=>X.verdict==="FAIL").sort((X,Y)=>Z0(Y.severity)-Z0(X.severity));for(let X of Q){let Y=X.source?` [${X.source.kind}${X.source.ref?`: ${X.source.ref}`:""}]`:"";$.push(` ${X.severity.padEnd(8)} ${X.check}: value=${X.value??"\u2014"} vs threshold=${X.threshold_used??"\u2014"}${Y}`)}let J=Z.checks.filter((X)=>X.verdict==="SKIP").map((X)=>X.check);if(J.length)$.push(` (skipped \u2014 data absent: ${J.join(", ")})`);if(Z.comparison){$.push(`
26
26
  vs prior flight ${Z.comparison.prior_flight_id}:`);for(let X of Z.comparison.param_changes)$.push(` param ${X.name}: ${X.old} \u2192 ${X.new}`);for(let X of Z.comparison.finding_changes)$.push(` finding ${X.check}: ${X.old} \u2192 ${X.new}`);if(!Z.comparison.param_changes.length&&!Z.comparison.finding_changes.length)$.push(" (no changes)")}else $.push(`
27
27
  (first flight for this airframe \u2014 no prior to compare)`);return $.join(`
28
- `)}var s$=(Z)=>Z==="CRITICAL"?2:Z==="HIGH"?1:0;function k6(Z){if(!Z.length)return"(no findings stored for this flight yet)";let $=Z.filter((Y)=>Y.origin!=="agent"),Q=Z.filter((Y)=>Y.origin==="agent"),J=(Y)=>` ${Y.origin==="agent"?`[agent ${Y.id}]`:"[deterministic]"} ${Y.severity}/${Y.verdict} ${Y.check}${Y.detail?` \u2014 ${Y.detail}`:""}`,X=[`${Z.length} finding(s) stored:`];return X.push(`Deterministic (${$.length}):`,...$.map(J)),X.push(`Agent-authored (${Q.length}):`,...Q.length?Q.map(J):[" (none)"]),X.push("","Before adding, judge whether your finding is ALREADY represented above (same root cause, even if phrased differently). Add only if genuinely new; pass supersedes_id to replace a stale one."),X.join(`
29
- `)}async function v6(Z,$,Q){let J=g(Z,".hydron","observations",$),X=await t$(J).catch(()=>{throw Error(`No observation store at ${J}. Run uav_analyze first.`)});for(let Y of X){let K=g(J,Y,`${Q}.json`);try{let G=JSON.parse(await e$(K,"utf8"));if(Array.isArray(G?.params))return G.params}catch(G){if(G.code==="ENOENT")continue;throw G}}throw Error(`flight_id ${Q} not found in the observation store. Run uav_analyze first.`)}async function VZ(Z,$,Q){let J=g(Z,".hydron","observations",$),X=await t$(J).catch(()=>{throw Error(`No observation store at ${J}. Run uav_analyze first.`)});for(let Y of X)try{let G=JSON.parse(await e$(g(J,Y,`${Q}.json`),"utf8"))?.episode?.attrs?.source_path;if(typeof G==="string"&&G)return G}catch(K){if(K.code==="ENOENT")continue;throw K}throw Error(`flight_id ${Q} not found (or no source_path). Run uav_analyze first.`)}function h6(Z){if(Z.dominant_peak_hz==null)return`No notch recommendation: ${Z.rationale}`;let $=[`Dominant vibration peak: ${Z.dominant_peak_hz.toFixed(1)} Hz`,Z.rationale,"","Recommended harmonic-notch params:"];for(let Q of Z.params){let J=Q.range?` (range ${Q.range[0]}\u2013${Q.range[1]})`:"",X=Q.source?` [${Q.source.kind}${Q.source.ref?`: ${Q.source.ref}`:""}]`:"";$.push(` ${Q.name} = ${Q.value}${J}${X}`)}return $.push("","Review before applying. Push via the gated MAVLink param link, then tag the retest."),$.join(`
30
- `)}var b6=["# UAV domain pack active","","This project uses the Hydron UAV pack for ArduPilot/PX4 flight-log intelligence. Handle UAV","work (flight logs, parameters, tuning, telemetry) in whatever mode you are already in \u2014 do","NOT switch to a dedicated agent. The `uav_*` tools are available now: `uav_analyze` (decode +","run the system checks on a .bin/.log/.ulog/.tlog), `uav_query` (read-only SQL over a decoded","flight), `uav_python` (pandas/numpy over its tables \u2014 the tool for any non-trivial","computation), `uav_plot` (charts), `uav_param_audit`, `uav_tune`, `uav_tag`, `uav_finding`,","`uav_export`.","","For the domain rules \u2014 grounding discipline (the guardrails judge, you narrate; cite every","bound's source; never invent a threshold), signal semantics, per-check interpretation, the","tuning playbook, and when to fan work out \u2014 **load the `uav` skill**. Load it as soon as you","start UAV work, not only when narrating results.","","Specialist loops (spawn only for a genuine multi-step drill-down): `uav-investigate`","(open-ended 'why' on one flight), `uav-compare` (cross-flight regression), `uav-verify`","(adversarially pressure-test a proposed anomaly)."].join(`
31
- `),f6=async(Z)=>{let $=Z.directory,Q=Z.project?.id!=null?String(Z.project.id):"local";return{"experimental.chat.system.transform":async(J,X)=>{X.system.push(b6)},tool:{uav_analyze:R({description:"Analyze one ArduPilot/PX4 flight log (.bin): decode, run the UAV system checks, persist the observation locally, and (if a prior flight for the same airframe exists) compute the param/finding diff. Returns a ranked anomaly report. Verdicts come from deterministic guardrails, not the model.",args:{path:R.schema.string().describe("Absolute path to the .bin DataFlash log")},async execute(J){let{report:X}=await g$(J.path,{projectDir:$,workspace:Q});return y6(X)}}),uav_query:R({description:"Ask an ad-hoc SQL question over a single decoded flight's message tables (one table per message type, e.g. VIBE, ATT, BAT, ERR, CTUN, RCOU). Read-only: SELECT/WITH only, single statement, capped at 10k rows. Use the table/column names from the ArduPilot log. Findings produced this way are exploratory (tier:user), not part of the regression record.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),query:R.schema.string().describe("read-only SQL (SELECT/WITH) over the flight's message tables")},async execute(J){try{let X=await u$({projectDir:$,workspace:Q,flight_id:J.flight_id,query:J.query});return $$(X)}catch(X){return`uav_query error: ${X instanceof Error?X.message:String(X)}`}}}),uav_compare:R({description:"Compare 2+ ALREADY-ANALYZED flights at once: finding-severity trend (only checks that "+"differ), key-metric trend (vibe_peak, hover_thr, \u2026), and parameter drift across the runs. "+"Analyze each flight first with uav_analyze. Memory-light \u2014 compares the stored per-flight "+"summaries, NOT the raw logs, so four 1 GB flights compare cheaply. Returns a markdown report.",args:{flight_ids:R.schema.array(R.schema.string()).describe("2+ flight_ids from prior uav_analyze runs, in the order to display")},async execute(J){try{let{markdown:X}=await m$({projectDir:$,workspace:Q,flight_ids:J.flight_ids});return X}catch(X){return`uav_compare error: ${X instanceof Error?X.message:String(X)}`}}}),uav_param_audit:R({description:"Audit a flight's ArduPilot parameters for safety-critical settings (arming checks, battery/radio failsafes), out-of-range tuning values, and consistency (cell-count vs failsafe voltages, frame/notch). Deterministic; bounds come from param-rules.yaml + built-in defaults. (uav_analyze already includes this; use this for a standalone param re-audit of a stored flight.)",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze")},async execute(J){try{let X=await v6($,Q,J.flight_id);return H$(JZ(X,{configDir:$}))}catch(X){return`uav_param_audit error: ${X instanceof Error?X.message:String(X)}`}}}),uav_tune:R({description:"Compute an FFT over a flight's gyro/vibration data, find the dominant vibration peak, and recommend ArduPilot harmonic-notch params (INS_HNTCH_ENABLE/FREQ/BW/REF/HMNCS) with their verified ranges. "+"Recommendation only \u2014 review before applying; pushing params is a separate gated step.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze")},async execute(J){try{let X=await l(await VZ($,Q,J.flight_id),{configDir:$});return h6(l$(X))}catch(X){return`uav_tune error: ${X instanceof Error?X.message:String(X)}`}}}),uav_tag:R({description:"Tag a stored flight with test-profile / campaign labels (e.g. 'notch-v2', 'windy', 'post-prop-balance') for the param-history / run-tracking record. Returns the flight's full tag set.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),tags:R.schema.array(R.schema.string()).describe("one or more tags to add")},async execute(J){try{let X=await C$({projectDir:$,workspace:Q,flight_id:J.flight_id,tags:J.tags});return`tagged ${J.flight_id}: [${X.join(", ")}]`}catch(X){return`uav_tag error: ${X instanceof Error?X.message:String(X)}`}}}),uav_finding:R({description:`Store or list a flight's findings. Findings from uav_analyze are deterministic; THIS tool records the conclusions YOU reach during an investigation so they attach to the flight.
28
+ `)}var Z0=(Z)=>Z==="CRITICAL"?2:Z==="HIGH"?1:0;function d6(Z){if(!Z.length)return"(no findings stored for this flight yet)";let $=Z.filter((Y)=>Y.origin!=="agent"),Q=Z.filter((Y)=>Y.origin==="agent"),J=(Y)=>` ${Y.origin==="agent"?`[agent ${Y.id}]`:"[deterministic]"} ${Y.severity}/${Y.verdict} ${Y.check}${Y.detail?` \u2014 ${Y.detail}`:""}`,X=[`${Z.length} finding(s) stored:`];return X.push(`Deterministic (${$.length}):`,...$.map(J)),X.push(`Agent-authored (${Q.length}):`,...Q.length?Q.map(J):[" (none)"]),X.push("","Before adding, judge whether your finding is ALREADY represented above (same root cause, even if phrased differently). Add only if genuinely new; pass supersedes_id to replace a stale one."),X.join(`
29
+ `)}async function p6(Z,$,Q){let J=u(Z,".hydron","observations",$),X=await $0(J).catch(()=>{throw Error(`No observation store at ${J}. Run uav_analyze first.`)});for(let Y of X){let K=u(J,Y,`${Q}.json`);try{let G=JSON.parse(await J0(K,"utf8"));if(Array.isArray(G?.params))return G.params}catch(G){if(G.code==="ENOENT")continue;throw G}}throw Error(`flight_id ${Q} not found in the observation store. Run uav_analyze first.`)}async function HZ(Z,$,Q){let J=u(Z,".hydron","observations",$),X=await $0(J).catch(()=>{throw Error(`No observation store at ${J}. Run uav_analyze first.`)});for(let Y of X)try{let G=JSON.parse(await J0(u(J,Y,`${Q}.json`),"utf8"))?.episode?.attrs?.source_path;if(typeof G==="string"&&G)return G}catch(K){if(K.code==="ENOENT")continue;throw K}throw Error(`flight_id ${Q} not found (or no source_path). Run uav_analyze first.`)}function l6(Z){if(Z.dominant_peak_hz==null)return`No notch recommendation: ${Z.rationale}`;let $=[`Dominant vibration peak: ${Z.dominant_peak_hz.toFixed(1)} Hz`,Z.rationale,"","Recommended harmonic-notch params:"];for(let Q of Z.params){let J=Q.range?` (range ${Q.range[0]}\u2013${Q.range[1]})`:"",X=Q.source?` [${Q.source.kind}${Q.source.ref?`: ${Q.source.ref}`:""}]`:"";$.push(` ${Q.name} = ${Q.value}${J}${X}`)}return $.push("","Review before applying. Push via the gated MAVLink param link, then tag the retest."),$.join(`
30
+ `)}var n6=["# UAV domain pack active","","This project uses the Hydron UAV pack for ArduPilot/PX4 flight-log intelligence. Handle UAV","work (flight logs, parameters, tuning, telemetry) in whatever mode you are already in \u2014 do","NOT switch to a dedicated agent. The `uav_*` tools are available now: `uav_analyze` (decode +","run the system checks on a .bin/.log/.ulog/.tlog), `uav_query` (read-only SQL over a decoded","flight), `uav_python` (pandas/numpy over its tables \u2014 the tool for any non-trivial","computation), `uav_plot` (charts), `uav_param_audit`, `uav_tune`, `uav_tag`, `uav_finding`,","`uav_export`.","","For the domain rules \u2014 grounding discipline (the guardrails judge, you narrate; cite every","bound's source; never invent a threshold), signal semantics, per-check interpretation, the","tuning playbook, and when to fan work out \u2014 **load the `uav` skill**. Load it as soon as you","start UAV work, not only when narrating results.","","Specialist loops (spawn only for a genuine multi-step drill-down): `uav-investigate`","(open-ended 'why' on one flight), `uav-compare` (cross-flight regression), `uav-verify`","(adversarially pressure-test a proposed anomaly)."].join(`
31
+ `),c6=async(Z)=>{let $=Z.directory,Q=Z.project?.id!=null?String(Z.project.id):"local";return{"experimental.chat.system.transform":async(J,X)=>{X.system.push(n6)},tool:{uav_analyze:R({description:"Analyze one ArduPilot/PX4 flight log (.bin): decode, run the UAV system checks, persist the observation locally, and (if a prior flight for the same airframe exists) compute the param/finding diff. Returns a ranked anomaly report. Verdicts come from deterministic guardrails, not the model.",args:{path:R.schema.string().describe("Absolute path to the .bin DataFlash log")},async execute(J){let{report:X}=await m$(J.path,{projectDir:$,workspace:Q});return m6(X)}}),uav_query:R({description:"Ask an ad-hoc SQL question over a single decoded flight's message tables (one table per message type, e.g. VIBE, ATT, BAT, ERR, CTUN, RCOU). Read-only: SELECT/WITH only, single statement, capped at 10k rows. Use the table/column names from the ArduPilot log. Findings produced this way are exploratory (tier:user), not part of the regression record.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),query:R.schema.string().describe("read-only SQL (SELECT/WITH) over the flight's message tables")},async execute(J){try{let X=await d$({projectDir:$,workspace:Q,flight_id:J.flight_id,query:J.query});return X$(X)}catch(X){return`uav_query error: ${X instanceof Error?X.message:String(X)}`}}}),uav_compare:R({description:"Compare 2+ ALREADY-ANALYZED flights at once: finding-severity trend (only checks that "+"differ), key-metric trend (vibe_peak, hover_thr, \u2026), and parameter drift across the runs. "+"Analyze each flight first with uav_analyze. Memory-light \u2014 compares the stored per-flight "+"summaries, NOT the raw logs, so four 1 GB flights compare cheaply. Returns a markdown report.",args:{flight_ids:R.schema.array(R.schema.string()).describe("2+ flight_ids from prior uav_analyze runs, in the order to display")},async execute(J){try{let{markdown:X}=await l$({projectDir:$,workspace:Q,flight_ids:J.flight_ids});return X}catch(X){return`uav_compare error: ${X instanceof Error?X.message:String(X)}`}}}),uav_param_audit:R({description:"Audit a flight's ArduPilot parameters for safety-critical settings (arming checks, battery/radio failsafes), out-of-range tuning values, and consistency (cell-count vs failsafe voltages, frame/notch). Deterministic; bounds come from param-rules.yaml + built-in defaults. (uav_analyze already includes this; use this for a standalone param re-audit of a stored flight.)",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze")},async execute(J){try{let X=await p6($,Q,J.flight_id);return A$(XZ(X,{configDir:$}))}catch(X){return`uav_param_audit error: ${X instanceof Error?X.message:String(X)}`}}}),uav_tune:R({description:"Compute an FFT over a flight's gyro/vibration data, find the dominant vibration peak, and recommend ArduPilot harmonic-notch params (INS_HNTCH_ENABLE/FREQ/BW/REF/HMNCS) with their verified ranges. "+"Recommendation only \u2014 review before applying; pushing params is a separate gated step.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze")},async execute(J){try{let X=await c(await HZ($,Q,J.flight_id),{configDir:$});return l6(i$(X))}catch(X){return`uav_tune error: ${X instanceof Error?X.message:String(X)}`}}}),uav_tag:R({description:"Tag a stored flight with test-profile / campaign labels (e.g. 'notch-v2', 'windy', 'post-prop-balance') for the param-history / run-tracking record. Returns the flight's full tag set.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),tags:R.schema.array(R.schema.string()).describe("one or more tags to add")},async execute(J){try{let X=await w$({projectDir:$,workspace:Q,flight_id:J.flight_id,tags:J.tags});return`tagged ${J.flight_id}: [${X.join(", ")}]`}catch(X){return`uav_tag error: ${X instanceof Error?X.message:String(X)}`}}}),uav_finding:R({description:`Store or list a flight's findings. Findings from uav_analyze are deterministic; THIS tool records the conclusions YOU reach during an investigation so they attach to the flight.
32
32
  `+`\u2022 op:"list" \u2014 show every stored finding (deterministic + agent). ALWAYS call this first.
33
- `+'\u2022 op:"add" \u2014 append a NEW agent finding. Only add after listing and judging it is not already '+"represented (same root cause, even if worded differently). To replace one you now consider stale, "+"pass supersedes_id. A finding must cite the ABSOLUTE bound it breached (in detail) \u2014 not 'N\xD7 the "+"other instances'. The store refuses an exact-id rewrite but does NOT dedup semantically; that is your call.",args:{op:R.schema.enum(["list","add"]).describe("list existing findings, or add a new one"),flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),title:R.schema.string().optional().describe("add: short label, e.g. 'IMU2 accel-Y divergence'"),severity:R.schema.enum(["OK","HIGH","CRITICAL"]).optional().describe("add: magnitude (default HIGH)"),detail:R.schema.string().optional().describe("add: the grounded evidence + the ABSOLUTE bound it breached and that bound's source"),value:R.schema.number().optional().describe("add: measured metric, if any"),threshold_used:R.schema.number().optional().describe("add: the absolute bound it was judged against"),supersedes_id:R.schema.string().optional().describe("add: id of an existing finding this replaces")},async execute(J){try{if(J.op==="list")return k6(await M$({projectDir:$,workspace:Q,flight_id:J.flight_id}));if(!J.title)return"uav_finding error: title is required to add a finding";let X=J.severity??"HIGH",Y={check:J.title,severity:X,verdict:X==="OK"?"PASS":"FAIL",tier:"user",origin:"agent",value:J.value??null,threshold_used:J.threshold_used??null,ruleset_version:"",detail:J.detail,id:`a-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,created_at:new Date().toISOString()},K=await w$({projectDir:$,workspace:Q,flight_id:J.flight_id,finding:Y,supersedesId:J.supersedes_id});return K.added?`stored agent finding ${K.id} on ${J.flight_id} (${K.total} findings total)`+(J.supersedes_id?` \u2014 superseded ${J.supersedes_id}`:""):`not stored (${K.reason}); ${K.total} findings already on ${J.flight_id}`}catch(X){return`uav_finding error: ${X instanceof Error?X.message:String(X)}`}}}),uav_python:R({description:"Run a Python snippet (pandas + numpy) over a flight's decoded data \u2014 THE tool for any non-trivial "+"analysis: binning, correlation, scoring/ranking windows, regression, FFT, custom stats. The message "+"tables are available as `load('VIBE')`, `load('ATT')`, `load('BAT')`, \u2026 (each a DataFrame; TimeUS is \xB5s "+"since boot). `print()` your results. Use this INSTEAD of multiple uav_query calls or doing arithmetic "+"in your head \u2014 the numbers must come from code you actually run.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),code:R.schema.string().describe("Python; use load('TABLE') for DataFrames, print() results")},async execute(J){try{let X=g($,".hydron","pycache",J.flight_id);if(!w6(X)){let W=await l(await VZ($,Q,J.flight_id),{configDir:$});await vZ(W,X)}let{stdout:Y,ok:K,tables:G}=await a$(X,J.code);return K?Y||"(snippet ran, no output \u2014 did you print()?)":`python exited non-zero:
33
+ `+'\u2022 op:"add" \u2014 append a NEW agent finding. Only add after listing and judging it is not already '+"represented (same root cause, even if worded differently). To replace one you now consider stale, "+"pass supersedes_id. A finding must cite the ABSOLUTE bound it breached (in detail) \u2014 not 'N\xD7 the "+"other instances'. The store refuses an exact-id rewrite but does NOT dedup semantically; that is your call.",args:{op:R.schema.enum(["list","add"]).describe("list existing findings, or add a new one"),flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),title:R.schema.string().optional().describe("add: short label, e.g. 'IMU2 accel-Y divergence'"),severity:R.schema.enum(["OK","HIGH","CRITICAL"]).optional().describe("add: magnitude (default HIGH)"),detail:R.schema.string().optional().describe("add: the grounded evidence + the ABSOLUTE bound it breached and that bound's source"),value:R.schema.number().optional().describe("add: measured metric, if any"),threshold_used:R.schema.number().optional().describe("add: the absolute bound it was judged against"),supersedes_id:R.schema.string().optional().describe("add: id of an existing finding this replaces")},async execute(J){try{if(J.op==="list")return d6(await y$({projectDir:$,workspace:Q,flight_id:J.flight_id}));if(!J.title)return"uav_finding error: title is required to add a finding";let X=J.severity??"HIGH",Y={check:J.title,severity:X,verdict:X==="OK"?"PASS":"FAIL",tier:"user",origin:"agent",value:J.value??null,threshold_used:J.threshold_used??null,ruleset_version:"",detail:J.detail,id:`a-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,created_at:new Date().toISOString()},K=await v$({projectDir:$,workspace:Q,flight_id:J.flight_id,finding:Y,supersedesId:J.supersedes_id});return K.added?`stored agent finding ${K.id} on ${J.flight_id} (${K.total} findings total)`+(J.supersedes_id?` \u2014 superseded ${J.supersedes_id}`:""):`not stored (${K.reason}); ${K.total} findings already on ${J.flight_id}`}catch(X){return`uav_finding error: ${X instanceof Error?X.message:String(X)}`}}}),uav_python:R({description:"Run a Python snippet (pandas + numpy) over a flight's decoded data \u2014 THE tool for any non-trivial "+"analysis: binning, correlation, scoring/ranking windows, regression, FFT, custom stats. The message "+"tables are available as `load('VIBE')`, `load('ATT')`, `load('BAT')`, \u2026 (each a DataFrame; TimeUS is \xB5s "+"since boot). `print()` your results. Use this INSTEAD of multiple uav_query calls or doing arithmetic "+"in your head \u2014 the numbers must come from code you actually run.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),code:R.schema.string().describe("Python; use load('TABLE') for DataFrames, print() results")},async execute(J){try{let X=u($,".hydron","pycache",J.flight_id);if(!x6(X)){let W=await c(await HZ($,Q,J.flight_id),{configDir:$});await bZ(W,X)}let{stdout:Y,ok:K,tables:G}=await e$(X,J.code);return K?Y||"(snippet ran, no output \u2014 did you print()?)":`python exited non-zero:
34
34
  ${Y}
35
35
 
36
- (tables available: ${G.join(", ")})`}catch(X){return`uav_python error: ${X instanceof Error?X.message:String(X)}`}}}),uav_export:R({description:"Export a decoded flight to one CSV per message type (PlotJuggler-importable) into a directory, so the lab's existing PlotJuggler validation flow isn't disrupted. Returns the output directory + file count.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),out_dir:R.schema.string().describe("absolute output directory for the CSVs")},async execute(J){try{let X=await l(await VZ($,Q,J.flight_id),{configDir:$});return`exported ${(await vZ(X,J.out_dir)).length} CSV file(s) to ${J.out_dir} (PlotJuggler-importable)`}catch(X){return`uav_export error: ${X instanceof Error?X.message:String(X)}`}}}),uav_plot:R({description:"Chart flight signals as an SVG that renders in the editor. Use for ANY plot/chart/graph/visualise "+`request \u2014 never hand-write SQL via uav_query for visualisation. Three modes:
36
+ (tables available: ${G.join(", ")})`}catch(X){return`uav_python error: ${X instanceof Error?X.message:String(X)}`}}}),uav_export:R({description:"Export a decoded flight to one CSV per message type (PlotJuggler-importable) into a directory, so the lab's existing PlotJuggler validation flow isn't disrupted. Returns the output directory + file count.",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),out_dir:R.schema.string().describe("absolute output directory for the CSVs")},async execute(J){try{let X=await c(await HZ($,Q,J.flight_id),{configDir:$});return`exported ${(await bZ(X,J.out_dir)).length} CSV file(s) to ${J.out_dir} (PlotJuggler-importable)`}catch(X){return`uav_export error: ${X instanceof Error?X.message:String(X)}`}}}),uav_plot:R({description:"Chart flight signals as an SVG that renders in the editor. Use for ANY plot/chart/graph/visualise "+`request \u2014 never hand-write SQL via uav_query for visualisation. Three modes:
37
37
  `+'\u2022 timeseries (default): signals vs time. Supports DERIVED expressions, e.g. "ATT.DesRoll - ATT.Roll" '+`(tracking error), "CTUN.ThH * 100". Multiple signals overlay.
38
38
  `+"\u2022 xy: one signal vs another (time-aligned) \u2014 correlations, e.g. x=BAT.Curr y=BAT.Volt (sag/IR), "+`x=RCOU.C1 y=VIBE.VibeZ (vibe vs throttle \u2014 motor output, NOT CTUN.ThH which is the constant hover estimate).
39
39
  `+`\u2022 fft: magnitude spectrum (Hz) of a signal \u2014 vibration peaks, e.g. signals=["IMU.GyrX"] or ["VIBE.VibeZ"].
40
- `+"Signals/exprs use TABLE.COLUMN (VIBE.VibeZ, ATT.DesRoll, BAT.Volt, RCOU.C1, XKF3.IVN, \u2026).",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),mode:R.schema.enum(["timeseries","xy","fft"]).optional().describe("default timeseries"),signals:R.schema.array(R.schema.string()).optional().describe('timeseries/fft: signals or derived exprs, e.g. ["ATT.DesRoll - ATT.Roll"] or ["IMU.GyrX"]'),x:R.schema.string().optional().describe("xy mode: x-axis signal, e.g. BAT.Curr"),y:R.schema.string().optional().describe("xy mode: y-axis signal, e.g. BAT.Volt"),title:R.schema.string().optional().describe("optional chart title")},async execute(J){try{let X=await l(await VZ($,Q,J.flight_id),{configDir:$}),{svg:Y,plotted:K,missing:G}=o$(X,{mode:J.mode,signals:J.signals,x:J.x,y:J.y,title:J.title});if(!K.length)return`uav_plot: nothing plottable (${G.join(", ")||"no signals given"}) for flight ${J.flight_id}`;let W=g($,".hydron","plots");await S6(W,{recursive:!0});let z=(J.mode??"ts")+"_"+K.join("_").replace(/[^A-Za-z0-9._-]/g,"-").slice(0,50),B=g(W,`${J.flight_id}__${z}.svg`);await M6(B,Y);let V=`data:image/svg+xml;base64,${Buffer.from(Y).toString("base64")}`,U=G.length?`
41
- _(no data for: ${G.join(", ")})_`:"",L=[...J.signals??[],J.x,J.y].filter((H)=>!!H),A=r$(L.length?L:K),F=A.length?`
40
+ `+"Signals/exprs use TABLE.COLUMN (VIBE.VibeZ, ATT.DesRoll, BAT.Volt, RCOU.C1, XKF3.IVN, \u2026).",args:{flight_id:R.schema.string().describe("flight_id from a prior uav_analyze"),mode:R.schema.enum(["timeseries","xy","fft"]).optional().describe("default timeseries"),signals:R.schema.array(R.schema.string()).optional().describe('timeseries/fft: signals or derived exprs, e.g. ["ATT.DesRoll - ATT.Roll"] or ["IMU.GyrX"]'),x:R.schema.string().optional().describe("xy mode: x-axis signal, e.g. BAT.Curr"),y:R.schema.string().optional().describe("xy mode: y-axis signal, e.g. BAT.Volt"),title:R.schema.string().optional().describe("optional chart title")},async execute(J){try{let X=await c(await HZ($,Q,J.flight_id),{configDir:$}),{svg:Y,plotted:K,missing:G}=s$(X,{mode:J.mode,signals:J.signals,x:J.x,y:J.y,title:J.title});if(!K.length)return`uav_plot: nothing plottable (${G.join(", ")||"no signals given"}) for flight ${J.flight_id}`;let W=u($,".hydron","plots");await u6(W,{recursive:!0});let z=(J.mode??"ts")+"_"+K.join("_").replace(/[^A-Za-z0-9._-]/g,"-").slice(0,50),B=u(W,`${J.flight_id}__${z}.svg`);await g6(B,Y);let V=`data:image/svg+xml;base64,${Buffer.from(Y).toString("base64")}`,U=G.length?`
41
+ _(no data for: ${G.join(", ")})_`:"",H=[...J.signals??[],J.x,J.y].filter((L)=>!!L),E=t$(H.length?H:K),T=E.length?`
42
42
 
43
- `+A.map((H)=>`> \u2139\uFE0F ${H}`).join(`
43
+ `+E.map((L)=>`> \u2139\uFE0F ${L}`).join(`
44
44
  `):"";return`![${K.join(", ")}](${V})
45
45
 
46
- Chart saved: ${B} (\u2318\u21E7V to preview)${U}${F}`}catch(X){return`uav_plot error: ${X instanceof Error?X.message:String(X)}`}}})}}},DJ=f6;export{DJ as default};
46
+ Chart saved: ${B} (\u2318\u21E7V to preview)${U}${T}`}catch(X){return`uav_plot error: ${X instanceof Error?X.message:String(X)}`}}})}}},xJ=c6;export{xJ as default};
@@ -1,11 +1,11 @@
1
1
  // @bun
2
- var g$=Object.defineProperty;var f$=(Z)=>Z;function u$(Z,J){this[Z]=f$.bind(null,J)}var jZ=(Z,J)=>{for(var $ in J)g$(Z,$,{get:J[$],enumerable:!0,configurable:!0,set:u$.bind(J,$)})};var M=(Z,J)=>()=>(Z&&(J=Z(Z=0)),J);function AZ(){let Z=process.env.UAV_DECODE_MAX_CPU;if(!Z)return;let J=Number(Z);return Number.isFinite(J)&&J>0&&J<1?J:void 0}async function FZ(Z,J){if(!J||Z<=0)return;let $=Math.min(Z*(1/J-1),250);if($>=1)await x$($)}var x$=(Z)=>new Promise((J)=>setTimeout(J,Z));var RZ;var DZ=M(()=>{RZ={a:{kind:"str",width:64},b:{kind:"i8",width:1},B:{kind:"u8",width:1},h:{kind:"i16",width:2},H:{kind:"u16",width:2},i:{kind:"i32",width:4},I:{kind:"u32",width:4},f:{kind:"f32",width:4},d:{kind:"f64",width:8},n:{kind:"str",width:4},N:{kind:"str",width:16},Z:{kind:"str",width:64},c:{kind:"i16",width:2,scale:0.01},C:{kind:"u16",width:2,scale:0.01},e:{kind:"i32",width:4,scale:0.01},E:{kind:"u32",width:4,scale:0.01},L:{kind:"i32",width:4,scale:0.0000001},M:{kind:"u8",width:1},q:{kind:"i64",width:8},Q:{kind:"u64",width:8}}});import{open as m$}from"fs/promises";function n$(Z){return(J,$)=>{let X="",Q=$+Z;for(let Y=$;Y<Q;Y++){let z=J.getUint8(Y);if(z===0)break;X+=String.fromCharCode(z)}return X}}function i$(Z){let J=Z.scale;switch(Z.kind){case"i8":return J===void 0?($,X)=>$.getInt8(X):($,X)=>$.getInt8(X)*J;case"u8":return J===void 0?($,X)=>$.getUint8(X):($,X)=>$.getUint8(X)*J;case"i16":return J===void 0?($,X)=>$.getInt16(X,!0):($,X)=>$.getInt16(X,!0)*J;case"u16":return J===void 0?($,X)=>$.getUint16(X,!0):($,X)=>$.getUint16(X,!0)*J;case"i32":return J===void 0?($,X)=>$.getInt32(X,!0):($,X)=>$.getInt32(X,!0)*J;case"u32":return J===void 0?($,X)=>$.getUint32(X,!0):($,X)=>$.getUint32(X,!0)*J;case"f32":return($,X)=>$.getFloat32(X,!0);case"f64":return($,X)=>$.getFloat64(X,!0);case"i64":return($,X)=>$.getBigInt64(X,!0);case"u64":return($,X)=>$.getBigUint64(X,!0);case"str":return n$(Z.width)}}function c$(Z,J){let $=[],X=[];for(let Y of Z){let z=RZ[Y];if(!z)return{readers:[],widths:[],ok:!1,timeIdx:-1};$.push(i$(z)),X.push(z.width)}let Q=J.indexOf("TimeUS");return{readers:$,widths:X,ok:!0,timeIdx:Q}}async function EZ(Z,J,$){let X=$??AZ(),Q=new Map,Y={},z=!1,K=null,W=null,G=4194304,B=await m$(Z,"r");try{let L=Buffer.allocUnsafe(G),V=new Uint8Array(0),A=0,D=!1;while(!D){let{bytesRead:E}=await B.read(L,0,G,A);A+=E,D=E===0;let H;if(E===0)H=V;else if(V.length===0)H=L.subarray(0,E);else H=new Uint8Array(V.length+E),H.set(V,0),H.set(L.subarray(0,E),V.length);let O=new DataView(H.buffer,H.byteOffset,H.byteLength),j=H.length,T=D,R=performance.now(),N=0;while(N+3<=j){if(H[N]!==p$||H[N+1]!==d$){N+=1;continue}let y=H[N+2],C=N+3;if(y===l$){if(C+PZ>j){if(T)z=!0;break}let k=H[C],qZ=H[C+1],m=t(H,C+2,4),w$=t(H,C+6,16),a=t(H,C+22,64).split(",").filter((b$)=>b$.length>0),{readers:y$,widths:k$,ok:h$,timeIdx:v$}=c$(w$,a);Q.set(k,{name:m,length:qZ,columns:a,ok:h$,payloadLen:qZ-3,readers:y$,widths:k$,timeIdx:v$}),J.onFmt(m,a),Y[m]=(Y[m]??0)+1,N+=3+PZ;continue}let I=Q.get(y);if(I===void 0){N+=1;continue}let r=I.payloadLen;if(C+r>j){if(T)z=!0;break}if(Y[I.name]=(Y[I.name]??0)+1,!I.ok){N+=3+r;continue}let{readers:o,widths:C$}=I,s=Array(o.length),NZ=C;for(let k=0;k<o.length;k++)s[k]=o[k](O,NZ),NZ+=C$[k];if(J.onRow(I.name,s),I.timeIdx>=0){let k=s[I.timeIdx];if(typeof k==="bigint")W=k}N+=3+r}if(V=H.slice(N),await FZ(performance.now()-R,X),D)break}}finally{await B.close()}if(z)K=W;return{partial:z,decodedUntil:K,messageCounts:Y}}function TZ(Z,J,$){let X=Z[J];if(!X)Z[J]={columns:$,rows:[]};else X.columns=$}async function _Z(Z){let J={},$=await EZ(Z,{onFmt:(X,Q)=>TZ(J,X,Q),onRow:(X,Q)=>{J[X].rows.push(Q)}});return{partial:$.partial,decodedUntil:$.decodedUntil,tables:J,messageCounts:$.messageCounts}}async function IZ(Z,J,$={}){let X=$.retain??new Set,Q=5000,Y={},z=new Map,K=await EZ(Z,{onFmt:(W,G)=>{if(TZ(Y,W,G),J.table(W,G),!z.has(W))z.set(W,[])},onRow:(W,G)=>{let B=z.get(W);if(B){if(B.push(G),B.length>=5000)J.rows(W,B),B.length=0}if(X.has(W))Y[W].rows.push(G)}});for(let[W,G]of z)if(G.length)J.rows(W,G);return{partial:K.partial,decodedUntil:K.decodedUntil,tables:Y,messageCounts:K.messageCounts}}function t(Z,J,$){let X="",Q=J+$;for(let Y=J;Y<Q;Y++){let z=Z[Y];if(z===0)break;X+=String.fromCharCode(z)}return X}var p$=163,d$=149,l$=128,PZ=86;var SZ=M(()=>{DZ()});function MZ(Z){return typeof Z==="object"&&Z!==null&&"_summary"in Z}function s$(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z))return BigInt(Z);return 0n}async function e(Z){if(await Bun.spawn(["python3","-c","import pymavlink"],{stdout:"ignore",stderr:"ignore"}).exited!==0)throw Error("pymavlink not found \u2014 pip install pymavlink");let $=Bun.spawn(["python3",r$,Z],{stdout:"pipe",stderr:"pipe"}),X=new Map,Q={},Y=!1,z=null,K=null,W=!1;function G(H){let O=X.get(H);if(!O)O={columns:[],colIndex:new Map,rows:[]},X.set(H,O);return O}function B(H){let O=H.t,j=G(O),T=H.f;for(let N of Object.keys(T))if(!j.colIndex.has(N))j.colIndex.set(N,j.columns.length),j.columns.push(N);let R=Array(j.columns.length).fill(null);for(let N of Object.keys(T)){let y=j.colIndex.get(N),C=T[N],I=o$.has(N)?s$(C):C;if(R[y]=I,N==="TimeUS"&&typeof I==="bigint")K=I}j.rows.push(R)}let L=new TextDecoder,V="";for await(let H of $.stdout){V+=L.decode(H,{stream:!0});let O;while((O=V.indexOf(`
3
- `))!==-1){let j=V.slice(0,O);if(V=V.slice(O+1),j.length===0)continue;let T;try{T=JSON.parse(j)}catch{Y=!0;continue}if(MZ(T)){W=!0;for(let[R,N]of Object.entries(T._summary.counts))Q[R]=N;if(T._summary.partial)Y=!0}else B(T)}}V+=L.decode();let A=V.trim();if(A.length>0)try{let H=JSON.parse(A);if(MZ(H)){W=!0;for(let[O,j]of Object.entries(H._summary.counts))Q[O]=j;if(H._summary.partial)Y=!0}else B(H)}catch{Y=!0}let D=await $.exited;if(D!==0){if(!W){let H=(await new Response($.stderr).text()).trim();throw Error(`pymavlink decode failed (exit ${D})${H?`: ${H}`:""}`)}Y=!0}if(!W)Y=!0;if(Y)z=K;let E={};for(let[H,O]of X)E[H]={columns:O.columns,rows:O.rows};for(let[H,O]of X)if(Q[H]===void 0)Q[H]=O.rows.length;return{partial:Y,decodedUntil:z,tables:E,messageCounts:Q}}var r$,o$;var CZ=M(()=>{r$=new URL("./mavdump.py",import.meta.url).pathname,o$=new Set(["TimeUS","TimeMS"])});function e$(Z){return typeof Z==="object"&&Z!==null&&"_summary"in Z}function Z0(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z))return BigInt(Z);return 0n}async function $0(){let Z=()=>Bun.spawn(["python3","-c","import pyulog"],{stdout:"ignore",stderr:"ignore"}).exited;if(await Z()===0)return;if(await Bun.spawn(["python3","-m","pip","install","--quiet","pyulog"],{stdout:"ignore",stderr:"ignore"}).exited,await Z()!==0)throw Error("pyulog not found \u2014 pip install pyulog")}async function wZ(Z){await $0();let J=Bun.spawn(["python3",a$,Z],{stdout:"pipe",stderr:"pipe"}),$=new Map,X={},Q=!1,Y=null,z=null,K=!1;function W(H){let O=$.get(H);if(!O)O={columns:[],colIndex:new Map,rows:[]},$.set(H,O);return O}function G(H){let O=H.t,j=W(O),T=H.f;for(let N of Object.keys(T))if(!j.colIndex.has(N))j.colIndex.set(N,j.columns.length),j.columns.push(N);let R=Array(j.columns.length).fill(null);for(let N of Object.keys(T)){let y=j.colIndex.get(N),C=T[N],I=t$.has(N)?Z0(C):C;if(R[y]=I,N==="timestamp"&&typeof I==="bigint")z=I}j.rows.push(R)}function B(H){if(H.length===0)return;let O;try{O=JSON.parse(H)}catch{Q=!0;return}if(e$(O)){K=!0;for(let[j,T]of Object.entries(O._summary.counts))X[j]=T;if(O._summary.partial)Q=!0}else G(O)}let L=new TextDecoder,V="";for await(let H of J.stdout){V+=L.decode(H,{stream:!0});let O;while((O=V.indexOf(`
4
- `))!==-1)B(V.slice(0,O)),V=V.slice(O+1)}V+=L.decode();let A=V.trim();if(A.length>0)B(A);let D=await J.exited;if(D!==0){if(!K){let H=(await new Response(J.stderr).text()).trim();throw Error(`ulog decode failed (exit ${D})${H?`: ${H}`:""}`)}Q=!0}if(!K)Q=!0;if(Q)Y=z;let E={};for(let[H,O]of $)E[H]={columns:O.columns,rows:O.rows};for(let[H,O]of $)if(X[H]===void 0)X[H]=O.rows.length;return{partial:Q,decodedUntil:Y,tables:E,messageCounts:X}}var a$,t$;var yZ=M(()=>{a$=new URL("./ulog.py",import.meta.url).pathname,t$=new Set(["timestamp","timestamp_sample"])});import{existsSync as kZ,readFileSync as J0}from"fs";import{mkdtemp as X0,rm as Q0}from"fs/promises";import{tmpdir as Y0}from"os";import{basename as ZZ,join as $Z}from"path";function hZ(Z){if(!Z)return[];let J=$Z(Z,".hydron","uav-format-converters.json");if(!kZ(J))return[];try{let $=JSON.parse(J0(J,"utf8"));return Array.isArray($.converters)?$.converters.filter((X)=>X&&X.match&&X.command):[]}catch{return[]}}function vZ(Z,J){for(let $ of J)try{if(new RegExp($.match,"i").test(Z))return $}catch{}return null}async function bZ(Z,J){let $=await X0($Z(Y0(),"uav-convert-")),X=ZZ(Z).replace(/\.[^.]+$/,""),Q=$Z($,X+(J.produces??".bin")),Y=J.command.replaceAll("{input}",Z).replaceAll("{output}",Q),z=()=>Q0($,{recursive:!0,force:!0});try{let K=Bun.spawn(["bash","-lc",Y],{stdout:"pipe",stderr:"pipe"}),W=await K.exited;if(W!==0){let G=(await new Response(K.stderr).text()).trim().slice(0,300);throw Error(`format converter failed (exit ${W}) for ${ZZ(Z)}: ${G}`)}if(!kZ(Q))throw Error(`format converter produced no output for ${ZZ(Z)}`);return{out:Q,cleanup:z}}catch(K){throw await z(),K}}var gZ=()=>{};import{Database as z0}from"bun:sqlite";import{tmpdir as K0}from"os";import{join as W0}from"path";import{existsSync as fZ,unlinkSync as uZ}from"fs";function mZ(Z){let J=W0(K0(),`uav-${Z}-${process.pid}-${G0++}.sqlite`);try{if(fZ(J))uZ(J)}catch{}let $=new z0(J);return $.exec("PRAGMA page_size = 8192"),$.query("PRAGMA journal_mode = MEMORY").get(),$.exec("PRAGMA synchronous = OFF"),$.exec("PRAGMA temp_store = FILE"),$.exec("PRAGMA cache_size = -65536"),{db:$,cleanup(){try{$.close()}catch{}try{if(fZ(J))uZ(J)}catch{}}}}function XZ(Z){let J=new Map,$=new Map;return{table(X,Q){if(J.has(X)||Q.length===0)return;Z.run(`CREATE TABLE ${JZ(X)} (${Q.map(JZ).join(", ")})`);let Y=Q.map(()=>"?").join(", ");J.set(X,Z.query(`INSERT INTO ${JZ(X)} VALUES (${Y})`)),$.set(X,Q.length)},rows(X,Q){let Y=J.get(X),z=$.get(X);if(!Y||z===void 0||Q.length===0)return;let K=Z.transaction((W)=>{for(let G of W){let B=Array(z);for(let L=0;L<z;L++){let V=G[L];B[L]=V===void 0?null:V}Y.run(...B)}});for(let W=0;W<Q.length;W+=xZ)K(Q.slice(W,W+xZ))}}}function pZ(Z,J){let $=XZ(Z);for(let[X,Q]of Object.entries(J.tables)){if(Q.columns.length===0)continue;if($.table(X,Q.columns),Q.rows.length)$.rows(X,Q.rows)}}var G0=0,JZ=(Z)=>`"${Z.replace(/"/g,'""')}"`,xZ=5000;var dZ=()=>{};async function x(Z,J="flight",$){let X=vZ(Z,hZ($?.configDir));if(X){let{out:z,cleanup:K}=await bZ(Z,X);try{return await x(z,J)}finally{await K()}}let{db:Q,cleanup:Y}=mZ(J);try{let z=Z.toLowerCase();if(z.endsWith(".bin"))return{log:await IZ(Z,XZ(Q),{retain:B0}),db:Q,cleanup:Y};let K;if(z.endsWith(".ulog"))K=await wZ(Z);else if(z.endsWith(".tlog")||z.endsWith(".log"))K=await e(Z);else try{K=await _Z(Z)}catch{K=await e(Z)}return pZ(Q,K),{log:K,db:Q,cleanup:Y}}catch(z){throw Y(),z}}var B0;var QZ=M(()=>{SZ();CZ();yZ();gZ();dZ();B0=new Set(["PARM","MSG","GPS","ERR","MODE"])});function YZ(Z,J){let $=Z.tables.PARM;if(!$)return null;let X=$.columns.findIndex((z)=>z.toLowerCase()==="name"),Q=$.columns.findIndex((z)=>z.toLowerCase()==="value");if(X<0||Q<0)return null;let Y=J.toLowerCase();for(let z of $.rows)if(String(z[X]).toLowerCase()===Y){let K=Number(z[Q]);return Number.isFinite(K)?K:null}return null}function H0(Z){let J=Z.tables.MSG;if(!J)return"";let $=J.columns.findIndex((X)=>/message|msg/i.test(X));if($<0)return"";for(let X of J.rows){let Q=String(X[$]??"");if(/Ardu(Copter|Plane|Sub|Rover)|PX4/i.test(Q))return Q}return""}function lZ(Z){let J=YZ(Z,"Q_ENABLE");if(J!==null&&J>=1||Z.tables.QUIK||Z.tables.AETR){if(Z.tables.MOTB||YZ(Z,"FRAME_CLASS")!==null||Z.tables.ARSP)return"vtol"}if(J!==null&&J>=1)return"vtol";let $=H0(Z);if(/ArduPlane/i.test($))return"fixedwing";if(/ArduCopter/i.test($))return"multirotor";if(YZ(Z,"FRAME_CLASS")!==null)return"multirotor";if(Z.tables.ARSP&&!Z.tables.MOTB)return"fixedwing";if(Z.tables.MOTB||Z.tables.RATE)return"multirotor";return"unknown"}function p(Z){return Z===void 0||Z==="multirotor"||Z==="vtol"||Z==="unknown"}function nZ(Z){return Z==="fixedwing"||Z==="vtol"}function S(Z){return`"${Z.replace(/"/g,'""')}"`}function q(Z,J){let $=Z.db.query(J).get();if(!$)return null;let X=Object.values($)[0];if(X===null||X===void 0)return null;return Number(X)}function w(Z,J){try{let $=Z.db.query(`PRAGMA table_info(${S(J)})`).all();return new Set($.map((X)=>X.name))}catch{return new Set}}function _(Z,J,$){return w(Z,J).has($)}function cZ(Z,J){for(let $ of J)if(Z.hasTable($))return $;return null}function U(Z,J,$){return{check:Z,severity:"OK",value:null,threshold_used:null,ruleset_version:J.ruleset_version,verdict:"SKIP",tier:"system",...$?{detail:$}:{}}}function F(Z){return{check:Z.check,severity:Z.failed?Z.failSeverity:"OK",value:Z.value,threshold_used:Z.threshold_used,ruleset_version:Z.thresholds.ruleset_version,verdict:Z.failed?"FAIL":"PASS",tier:"system",...Z.source?{source:Z.source}:{},...Z.detail?{detail:Z.detail}:{}}}function A0(Z,J,$){let X=Object.values(Z);if(J<=0||X.length===0)return"";let Q=[...X].sort((G,B)=>B-G),Y=Q[0],z=Q.length>1?Q[1]:0;if(Q.length>=2&&z>0&&Y>=2*z)return" | cause: likely sensor/mounting alignment (one axis dominates)";let W=Q.length>=2&&Y<=1.5*(Q[Q.length-1]||Y);if($>0||W)return" | cause: likely mechanical degradation (props/bearings/frame)";return""}function rZ(Z,J,$){let X=new Set;try{let z=Z.query("SELECT name FROM sqlite_master WHERE type='table'").all();for(let K of z)X.add(K.name)}catch{}let Q={db:Z,thresholds:J,hasTable:(z)=>X.has(z),vehicleType:$?.vehicleType},Y=[];for(let{id:z,run:K}of u0)try{Y.push(K(Q))}catch(W){let G=W instanceof Error?W.message:String(W);Y.push(U(z,J,`check error: ${G}`))}return Y}var U0=(Z)=>{let J=Z.thresholds.battery;if(!Z.hasTable("ERR"))return U("battery",Z.thresholds);if(!_(Z,"ERR","Subsys")||!_(Z,"ERR","ECode"))return U("battery",Z.thresholds,"ERR present but Subsys/ECode columns absent");let $=J?.err_subsys??6,X=J?.err_ecode??1,Q=q(Z,`SELECT COUNT(*) FROM "ERR" WHERE "Subsys"=${$} AND "ECode"=${X}`)??0;return F({check:"battery",thresholds:Z.thresholds,failed:Q>0,failSeverity:"CRITICAL",value:Q,threshold_used:0,source:J?.source,detail:Q>0?`${Q} battery failsafe ERR event(s) (Subsys=${$} ECode=${X})`:void 0})},L0=(Z)=>{let J=Z.thresholds.attitude;if(!Z.hasTable("ATT"))return U("attitude",Z.thresholds);for(let z of["DesRoll","Roll","DesPitch","Pitch"])if(!_(Z,"ATT",z))return U("attitude",Z.thresholds,`ATT missing column ${z}`);let $=J?.max_deg??5,X=q(Z,'SELECT MAX(ABS("DesRoll"-"Roll")) FROM "ATT"'),Q=q(Z,'SELECT MAX(ABS("DesPitch"-"Pitch")) FROM "ATT"'),Y=Math.max(X??0,Q??0);return F({check:"attitude",thresholds:Z.thresholds,failed:Y>$,failSeverity:"HIGH",value:Y,threshold_used:$,source:J?.source,detail:`peak roll err ${(X??0).toFixed(2)}\xB0, pitch err ${(Q??0).toFixed(2)}\xB0`})},V0=(Z)=>{let J=Z.thresholds.altitude;if(!Z.hasTable("CTUN"))return U("altitude",Z.thresholds);if(!_(Z,"CTUN","DAlt")||!_(Z,"CTUN","Alt"))return U("altitude",Z.thresholds,"CTUN missing DAlt/Alt columns");let $=J?.max_m??2,X=q(Z,'SELECT MAX(ABS("DAlt"-"Alt")) FROM "CTUN"');return F({check:"altitude",thresholds:Z.thresholds,failed:(X??0)>$,failSeverity:"HIGH",value:X,threshold_used:$,source:J?.source})},O0=(Z)=>{let J=Z.thresholds.hover;if(!p(Z.vehicleType))return U("hover",Z.thresholds,`n/a for ${Z.vehicleType} (no hover throttle)`);if(!Z.hasTable("CTUN"))return U("hover",Z.thresholds);if(!_(Z,"CTUN","ThH"))return U("hover",Z.thresholds,"CTUN missing ThH column");let $=J?.band??[0.25,0.75],[X,Q]=$,Y=q(Z,'SELECT AVG("ThH") FROM "CTUN"'),z=Y===null?!1:Y<X||Y>Q;return F({check:"hover",thresholds:Z.thresholds,failed:z,failSeverity:"HIGH",value:Y,threshold_used:Q,source:J?.source,detail:`mean hover throttle ${(Y??0).toFixed(3)} vs band [${X}, ${Q}]`})},iZ=2000,N0=(Z)=>{if(!p(Z.vehicleType))return U("rcou",Z.thresholds,`n/a for ${Z.vehicleType} (control surfaces, not motor mix)`);if(!Z.hasTable("RCOU"))return U("rcou",Z.thresholds);let J=w(Z,"RCOU"),$=[];for(let Y=1;Y<=8;Y++)if(J.has(`C${Y}`))$.push(`C${Y}`);if($.length===0)return U("rcou",Z.thresholds,"RCOU has no C1..C8 channels");let X=-1/0,Q=[];for(let Y of $){let z=q(Z,`SELECT MAX(${S(Y)}) FROM "RCOU"`);if(z===null)continue;if(z>X)X=z;if(z>=iZ)Q.push(`${Y}=${z}`)}if(X===-1/0)X=0;return F({check:"rcou",thresholds:Z.thresholds,failed:Q.length>0,failSeverity:"HIGH",value:X,threshold_used:iZ,detail:Q.length>0?`motor output(s) pegged at max: ${Q.join(", ")}`:void 0})},zZ=1000,KZ=2000,q0=(Z)=>{if(!Z.hasTable("RCIN"))return U("rcin",Z.thresholds);if(!_(Z,"RCIN","C3"))return U("rcin",Z.thresholds,"RCIN missing C3 column");let J=q(Z,'SELECT MIN("C3") FROM "RCIN"'),$=q(Z,'SELECT MAX("C3") FROM "RCIN"'),X=J!==null&&J<zZ||$!==null&&$>KZ,Q=$;if(J!==null&&J<zZ)Q=J;return F({check:"rcin",thresholds:Z.thresholds,failed:X,failSeverity:"HIGH",value:Q,threshold_used:KZ,detail:`RCIN C3 range [${J??"\u2014"}, ${$??"\u2014"}] vs [${zZ}, ${KZ}]`})},j0=(Z)=>{let J=Z.thresholds.vibration;if(!Z.hasTable("VIBE"))return U("vibration",Z.thresholds);let $=w(Z,"VIBE"),X=["VibeX","VibeY","VibeZ"].filter((L)=>$.has(L));if(X.length===0)return U("vibration",Z.thresholds,"VIBE has no VibeX/Y/Z columns");let Q=J?.max_ms2??30,Y={},z=-1/0;for(let L of X){let A=q(Z,`SELECT MAX(${S(L)}) FROM "VIBE"`)??0;if(Y[L]=A,A>z)z=A}if(z===-1/0)z=0;let K=["Clip","Clip0","Clip1","Clip2"].filter((L)=>$.has(L)),W=0;for(let L of K){let V=q(Z,`SELECT MAX(${S(L)}) FROM "VIBE"`);if(V!==null&&V>W)W=V}let G=K.length>0?` | clipping MAX=${W}${W>0?" (CLIP EVENTS)":""}`:"",B=A0(Y,z,W);return F({check:"vibration",thresholds:Z.thresholds,failed:z>=Q,failSeverity:"HIGH",value:z,threshold_used:Q,source:J?.source,detail:`peak vibe ${z} m/s\xB2 (axes ${X.join("/")})${G}${B}`})},F0=(Z)=>{let J=Z.thresholds.mag;if(!Z.hasTable("MAG"))return U("mag",Z.thresholds);if(!_(Z,"MAG","Health"))return U("mag",Z.thresholds,"MAG missing Health column");let $=J?.min??1,X=q(Z,'SELECT MIN("Health") FROM "MAG"');return F({check:"mag",thresholds:Z.thresholds,failed:X!==null&&X<$,failSeverity:"HIGH",value:X,threshold_used:$,source:J?.source})},R0=(Z)=>{let J=Z.thresholds.ekf,$=cZ(Z,["XKF3","NKF3"]);if(!$)return U("ekf",Z.thresholds);let X=w(Z,$),Q=["IVN","IVE","IVD","IVT","IPN","IPE","IPD"].filter((K)=>X.has(K));if(Q.length===0)return U("ekf",Z.thresholds,`${$} has no IVN/IVE/IVD/IVT/IPN/IPE/IPD columns`);let Y=J?.max_ratio??0.5,z=-1/0;for(let K of Q){let W=q(Z,`SELECT MAX(ABS(${S(K)})) FROM ${S($)}`);if(W!==null&&W>z)z=W}if(z===-1/0)z=0;return F({check:"ekf",thresholds:Z.thresholds,failed:z>Y,failSeverity:"HIGH",value:z,threshold_used:Y,source:J?.source,detail:`peak innovation ${z} (${$}: ${Q.join("/")})`})},D0=(Z)=>{let J=Z.thresholds.battery;if(!Z.hasTable("BAT"))return U("min_cell_voltage",Z.thresholds);if(!_(Z,"BAT","Volt"))return U("min_cell_voltage",Z.thresholds,"BAT missing Volt column");let $=J?.min_cell_v??3.3,X=q(Z,'SELECT MAX("Volt") FROM "BAT"'),Q=q(Z,'SELECT MIN("Volt") FROM "BAT"');if(X===null||Q===null||X<=0)return U("min_cell_voltage",Z.thresholds,"BAT.Volt has no usable values");let Y=Math.max(1,Math.round(X/4.2)),z=Q/Y;return F({check:"min_cell_voltage",thresholds:Z.thresholds,failed:z<$,failSeverity:"CRITICAL",value:z,threshold_used:$,source:J?.source,detail:`${Y}S pack (MAX pack ${X.toFixed(2)}V), min cell ${z.toFixed(3)}V vs floor ${$}V`})},P0=(Z)=>{let J=Z.thresholds.battery;if(!Z.hasTable("BAT"))return U("voltage_sag",Z.thresholds);if(!_(Z,"BAT","VoltR")||!_(Z,"BAT","Volt"))return U("voltage_sag",Z.thresholds,"BAT missing VoltR/Volt columns");let $=J?.max_sag_v??2,X=q(Z,'SELECT MAX("VoltR"-"Volt") FROM "BAT"');return F({check:"voltage_sag",thresholds:Z.thresholds,failed:X!==null&&X>$,failSeverity:"HIGH",value:X,threshold_used:$,source:J?.source,detail:`peak sag ${(X??0).toFixed(2)}V (VoltR-Volt) vs ${$}V`})},E0=(Z)=>{let J=Z.thresholds.battery;if(!Z.hasTable("BAT"))return U("over_current",Z.thresholds);if(!_(Z,"BAT","Curr"))return U("over_current",Z.thresholds,"BAT missing Curr column");let $=J?.max_current_a??200,X=q(Z,'SELECT MAX("Curr") FROM "BAT"');return F({check:"over_current",thresholds:Z.thresholds,failed:X!==null&&X>$,failSeverity:"HIGH",value:X,threshold_used:$,source:J?.source,detail:`peak current ${(X??0).toFixed(1)}A vs ${$}A`})},T0=(Z)=>{let J=Z.thresholds.battery;if(!Z.hasTable("BAT"))return U("internal_resistance",Z.thresholds);if(!_(Z,"BAT","Res"))return U("internal_resistance",Z.thresholds,"BAT missing Res column");let $=q(Z,'SELECT MAX("Res") FROM "BAT"');if($===null||$===0)return U("internal_resistance",Z.thresholds,"BAT.Res all zero/absent (not reported)");let X=J?.res_min_current_a??5,Y=_(Z,"BAT","Curr")?q(Z,`SELECT MAX("Res") FROM "BAT" WHERE "Curr" >= ${X}`):$;if(Y===null)return U("internal_resistance",Z.thresholds,`no BAT samples at Curr \u2265 ${X} A \u2014 Res only valid under load (max raw ${$.toFixed(3)}\u03A9 is a low-current artifact)`);let z=J?.max_res_ohm??0.03;return F({check:"internal_resistance",thresholds:Z.thresholds,failed:Y>z,failSeverity:"HIGH",value:Y,threshold_used:z,source:J?.source,detail:`peak internal resistance ${Y.toFixed(4)}\u03A9 (at Curr \u2265 ${X} A) vs ${z}\u03A9`})},_0=(Z)=>{let J=Z.thresholds.power;if(!Z.hasTable("POWR"))return U("board_brownout",Z.thresholds);if(!_(Z,"POWR","Vcc"))return U("board_brownout",Z.thresholds,"POWR missing Vcc column");let $=J?.min_vcc_v??4.5,X=q(Z,'SELECT MIN("Vcc") FROM "POWR"');return F({check:"board_brownout",thresholds:Z.thresholds,failed:X!==null&&X<$,failSeverity:"CRITICAL",value:X,threshold_used:$,source:J?.source,detail:`min board Vcc ${(X??0).toFixed(3)}V vs floor ${$}V`})},I0=(Z)=>{let J=Z.thresholds.rate;if(!Z.hasTable("RATE"))return U("rate_tracking",Z.thresholds);let $=w(Z,"RATE"),Q=[["RDes","R"],["PDes","P"],["YDes","Y"]].filter(([W,G])=>$.has(W)&&$.has(G));if(Q.length===0)return U("rate_tracking",Z.thresholds,"RATE has no RDes/R, PDes/P, YDes/Y pairs");let Y=J?.max_err_dps??50,z=-1/0,K="";for(let[W,G]of Q){let B=q(Z,`SELECT MAX(ABS(${S(W)}-${S(G)})) FROM "RATE"`);if(B!==null&&B>z)z=B,K=W.replace("Des","")}if(z===-1/0)z=0;return F({check:"rate_tracking",thresholds:Z.thresholds,failed:z>Y,failSeverity:"HIGH",value:z,threshold_used:Y,source:J?.source,detail:`peak rate-tracking error ${z.toFixed(2)} deg/s (axis ${K||"?"}) vs ${Y} deg/s`})},S0=(Z)=>{let J=Z.thresholds.gps;if(!Z.hasTable("GPA"))return U("gps_accuracy",Z.thresholds);if(!_(Z,"GPA","HAcc"))return U("gps_accuracy",Z.thresholds,"GPA missing HAcc column");let $=J?.hacc_max_m??5,X=q(Z,'SELECT MAX("HAcc") FROM "GPA"');return F({check:"gps_accuracy",thresholds:Z.thresholds,failed:X!==null&&X>$,failSeverity:"HIGH",value:X,threshold_used:$,source:J?.source,detail:`peak horizontal accuracy ${(X??0).toFixed(2)}m vs ${$}m`})},M0=(Z)=>{let J=Z.thresholds.ekf,$=cZ(Z,["XKF4","NKF4"]);if(!$)return U("ekf_variance",Z.thresholds);let X=w(Z,$),Q=["SV","SP","SH","SM"].filter((B)=>X.has(B));if(Q.length===0)return U("ekf_variance",Z.thresholds,`${$} has no SV/SP/SH/SM columns`);let Y=J?.max_variance??1,z=-1/0,K="";for(let B of Q){let L=q(Z,`SELECT MAX(${S(B)}) FROM ${S($)}`);if(L!==null&&L>z)z=L,K=B}if(z===-1/0)z=0;let W=X.has("FS")?q(Z,`SELECT MAX("FS") FROM ${S($)}`):null,G=W!==null&&W>0?` | EKF failsafe set (FS MAX=${W})`:"";return F({check:"ekf_variance",thresholds:Z.thresholds,failed:z>Y,failSeverity:"HIGH",value:z,threshold_used:Y,source:J?.source,detail:`peak EKF variance ${z.toFixed(3)} (${$}: ${K||Q.join("/")}) vs ${Y}${G}`})},C0=(Z)=>{let J=Z.thresholds.mag;if(!Z.hasTable("MAG"))return U("mag_offsets",Z.thresholds);let $=w(Z,"MAG"),X=["OfsX","OfsY","OfsZ"].filter((K)=>$.has(K));if(X.length===0)return U("mag_offsets",Z.thresholds,"MAG has no OfsX/OfsY/OfsZ columns");let Q=J?.max_offset??600,Y=-1/0,z="";for(let K of X){let W=q(Z,`SELECT MAX(ABS(${S(K)})) FROM "MAG"`);if(W!==null&&W>Y)Y=W,z=K}if(Y===-1/0)Y=0;return F({check:"mag_offsets",thresholds:Z.thresholds,failed:Y>Q,failSeverity:"HIGH",value:Y,threshold_used:Q,source:J?.source,detail:`peak mag offset ${Y.toFixed(0)} (${z||X.join("/")}) vs ${Q}`})},w0=(Z)=>{let J=Z.thresholds.esc;if(!Z.hasTable("ESC"))return U("esc_health",Z.thresholds);let $=w(Z,"ESC"),X=["Temp","MotTemp"].filter((L)=>$.has(L));if(X.length===0&&!$.has("Err"))return U("esc_health",Z.thresholds,"ESC has no Temp/MotTemp/Err columns");let Q=J?.max_temp_c??90,Y=-1/0,z="";for(let L of X){let V=q(Z,`SELECT MAX(${S(L)}) FROM "ESC"`);if(V!==null&&V>Y)Y=V,z=L}if(Y===-1/0)Y=0;let K=$.has("Err")?q(Z,'SELECT MAX("Err") FROM "ESC"')??0:0,W=X.length>0&&Y>Q,G=K>0,B=G?` | ESC Err MAX=${K}`:"";return F({check:"esc_health",thresholds:Z.thresholds,failed:W||G,failSeverity:"HIGH",value:Y,threshold_used:Q,source:J?.source,detail:`peak ESC temp ${Y.toFixed(1)}\xB0C (${z||"n/a"}) vs ${Q}\xB0C${B}`})},y0=(Z)=>{if(!p(Z.vehicleType))return U("thrust_margin",Z.thresholds,`n/a for ${Z.vehicleType} (lift/T:W is a rotorcraft metric)`);if(!Z.hasTable("MOTB"))return U("thrust_margin",Z.thresholds);let J=w(Z,"MOTB");if(!J.has("FailFlags")&&!(J.has("ThrOut")&&J.has("LiftMax")))return U("thrust_margin",Z.thresholds,"MOTB missing FailFlags and ThrOut/LiftMax columns");let $=J.has("FailFlags")?q(Z,'SELECT MAX("FailFlags") FROM "MOTB"')??0:0,X=null;if(J.has("ThrOut")&&J.has("LiftMax")){let K=q(Z,'SELECT AVG("ThrOut") FROM "MOTB"'),W=q(Z,'SELECT AVG("LiftMax") FROM "MOTB"');if(K!==null&&W!==null&&W>0)X=K/W}let Q=X!==null&&X>0.5,Y=$>0,z=`MOTB FailFlags MAX=${$}`+(X!==null?`, mean ThrOut/LiftMax ${X.toFixed(2)} (low-margin > 0.5)`:"")+(Y?" \u2014 THRUST-LOSS flagged":Q?" \u2014 low thrust margin":"");return F({check:"thrust_margin",thresholds:Z.thresholds,failed:Y||Q,failSeverity:"HIGH",value:X,threshold_used:0.5,detail:z})},k0=(Z)=>{let J=Z.thresholds.imu;if(!Z.hasTable("IMU"))return U("imu_health",Z.thresholds);let $=w(Z,"IMU"),X=["EG","EA"].filter((K)=>$.has(K));if(X.length===0)return U("imu_health",Z.thresholds,"IMU has no EG/EA error-count columns");let Q=J?.max_err??0,Y=-1/0,z="";for(let K of X){let W=q(Z,`SELECT MAX(${S(K)}) FROM "IMU"`);if(W!==null&&W>Y)Y=W,z=K}if(Y===-1/0)Y=0;return F({check:"imu_health",thresholds:Z.thresholds,failed:Y>Q,failSeverity:"HIGH",value:Y,threshold_used:Q,source:J?.source,detail:`max IMU error count ${Y} (${z||X.join("/")}) vs ${Q}`})},h0=(Z)=>{let J=Z.thresholds.imu;if(!Z.hasTable("IMU"))return U("imu_consistency",Z.thresholds);let $=w(Z,"IMU");if(!$.has("I"))return U("imu_consistency",Z.thresholds,"IMU has no instance column I");let X=["AccX","AccY","AccZ"].filter((R)=>$.has(R)),Q=["GyrX","GyrY","GyrZ"].filter((R)=>$.has(R));if(X.length===0&&Q.length===0)return U("imu_consistency",Z.thresholds,"IMU has no Acc*/Gyr* columns");let Y=[...X,...Q].map((R)=>`AVG(${S(R)}) ${S(R)}`).join(", "),z=Z.db.query(`SELECT "I" AS "I", ${Y} FROM "IMU" GROUP BY "I"`).all();if(z.length<2)return U("imu_consistency",Z.thresholds,`only ${z.length} IMU instance \u2014 nothing to cross-check`);let K=(R)=>{let N=z.map((y)=>Number(y[R])).filter((y)=>Number.isFinite(y));return N.length>=2?Math.max(...N)-Math.min(...N):0},W=0,G="";for(let R of X){let N=K(R);if(N>W)W=N,G=R}let B=0,L="";for(let R of Q){let N=K(R);if(N>B)B=N,L=R}let V=J?.accel_consistency_max??0.7,A=J?.gyro_consistency_max??0.3,D=W>V,E=B>A,H=D||E,O=W/V>=B/A?W:B,j=W/V>=B/A?V:A,T=H?`INCONSISTENT \u2014 ${D?`accel ${G} spread ${W.toFixed(3)} m/s\xB2 > ${V}`:""}${D&&E?"; ":""}${E?`gyro ${L} spread ${B.toFixed(3)} rad/s > ${A}`:""}`:`IMUs consistent \u2014 max inter-instance bias spread: accel ${W.toFixed(3)} m/s\xB2 (<${V}), gyro ${B.toFixed(3)} rad/s (<${A}); relative differences between instances are within healthy tolerance`;return F({check:"imu_consistency",thresholds:Z.thresholds,failed:H,failSeverity:"HIGH",value:O,threshold_used:j,source:{kind:"rule-of-thumb",ref:"IMU inconsistency tolerance (~ArduPilot arming limits)"},detail:T})},v0=(Z)=>{if(!Z.hasTable("ERR"))return U("err_events",Z.thresholds);if(!_(Z,"ERR","Subsys")||!_(Z,"ERR","ECode"))return U("err_events",Z.thresholds,"ERR present but Subsys/ECode columns absent");let J=Z.thresholds.battery?.err_subsys??6,$=Z.db.query(`SELECT "Subsys" AS subsys, "ECode" AS ecode, COUNT(*) AS n FROM "ERR" WHERE "ECode" <> 0 AND "Subsys" <> ${J} GROUP BY "Subsys", "ECode" ORDER BY "Subsys", "ECode"`).all(),X=0,Q=[];for(let Y of $)X+=Number(Y.n),Q.push(`Subsys=${Y.subsys} ECode=${Y.ecode} \xD7${Y.n}`);return F({check:"err_events",thresholds:Z.thresholds,failed:X>0,failSeverity:"HIGH",value:X,threshold_used:0,detail:X>0?`${X} non-battery ERR event(s): ${Q.join(", ")}`:"no non-battery ERR events"})},b0,g0=(Z)=>{if(!Z.hasTable("MODE"))return U("mode_changes",Z.thresholds);if(!_(Z,"MODE","ModeNum"))return U("mode_changes",Z.thresholds,"MODE missing ModeNum column");let J=Z.db.query('SELECT "ModeNum" AS modenum FROM "MODE"').all(),$=J.length,X=$>0?$-1:0,Q=new Map,Y=!1;for(let K of J){let W=Number(K.modenum);if(Q.set(W,(Q.get(W)??0)+1),b0.has(W))Y=!0}let z=[...Q.entries()].sort((K,W)=>K[0]-W[0]).map(([K,W])=>`Mode${K}\xD7${W}`).join(", ");return F({check:"mode_changes",thresholds:Z.thresholds,failed:Y,failSeverity:"HIGH",value:X,threshold_used:0,detail:`${X} mode change(s); timeline: ${z||"\u2014"}`+(Y?" \u2014 emergency mode (RTL/LAND) entered":"")})},f0=(Z)=>{let J=Z.thresholds.airspeed;if(!nZ(Z.vehicleType))return U("airspeed",Z.thresholds,`n/a for ${Z.vehicleType} (no fixed-wing airspeed)`);if(!Z.hasTable("ARSP"))return U("airspeed",Z.thresholds,"no ARSP (airspeed) data");if(!_(Z,"ARSP","Airspeed"))return U("airspeed",Z.thresholds,"ARSP missing Airspeed column");let $=J?.stall_min_ms??8,X={kind:"rule-of-thumb",ref:"default stall floor"};if(Z.hasTable("PARM")){let Y=q(Z,`SELECT "Value" FROM "PARM" WHERE "Name"='ARSPD_FBW_MIN' LIMIT 1`);if(Y!==null&&Y>0)$=Y,X={kind:"datasheet",ref:"ARSPD_FBW_MIN"}}let Q=q(Z,'SELECT MIN("Airspeed") FROM "ARSP" WHERE "Airspeed" > 2');if(Q===null)return U("airspeed",Z.thresholds,"no airborne airspeed samples");return F({check:"airspeed",thresholds:Z.thresholds,failed:Q<$,failSeverity:"HIGH",value:Q,threshold_used:$,source:X,detail:`min in-flight airspeed ${Q.toFixed(1)} m/s vs stall floor ${$} m/s`})},u0;var oZ=M(()=>{b0=new Set([6,9]),u0=[{id:"battery",run:U0},{id:"min_cell_voltage",run:D0},{id:"voltage_sag",run:P0},{id:"over_current",run:E0},{id:"internal_resistance",run:T0},{id:"board_brownout",run:_0},{id:"attitude",run:L0},{id:"rate_tracking",run:I0},{id:"altitude",run:V0},{id:"hover",run:O0},{id:"rcou",run:N0},{id:"rcin",run:q0},{id:"thrust_margin",run:y0},{id:"vibration",run:j0},{id:"imu_health",run:k0},{id:"imu_consistency",run:h0},{id:"mag",run:F0},{id:"mag_offsets",run:C0},{id:"gps_accuracy",run:S0},{id:"ekf",run:R0},{id:"ekf_variance",run:M0},{id:"esc_health",run:w0},{id:"airspeed",run:f0},{id:"err_events",run:v0},{id:"mode_changes",run:g0}]});import{existsSync as x0,readFileSync as m0}from"fs";import{dirname as p0,join as WZ}from"path";import{fileURLToPath as d0}from"url";function tZ(Z){if(Z){let J=WZ(Z,".hydron","apm-params.json"),$=aZ(J);if($&&$.size>0)return $}for(let J of l0){let $=aZ(J);if($)return $}return new Map}function eZ(Z,J){return J.get(Z.toUpperCase())}function aZ(Z){if(!x0(Z))return;try{let J=m0(Z,"utf8"),$=JSON.parse(J);if(!$||typeof $!=="object")return;let X=new Map;for(let[Q,Y]of Object.entries($)){if(!Q||Q.startsWith("_")||typeof Y!=="object"||Y===null)continue;let z={name:Q};if(typeof Y.min==="number"&&Number.isFinite(Y.min))z.min=Y.min;if(typeof Y.max==="number"&&Number.isFinite(Y.max))z.max=Y.max;if(typeof Y.units==="string"&&Y.units)z.units=Y.units;X.set(Q.toUpperCase(),z)}return X}catch{return}}var sZ,l0;var Z$=M(()=>{sZ=p0(d0(import.meta.url)),l0=[WZ(sZ,"..","data","apm-params.json"),WZ(sZ,"..","..","data","apm-params.json")]});import{existsSync as n0,readFileSync as i0}from"fs";import{join as c0}from"path";function $$(){return{ruleset_version:"v1",safety:{ARMING_CHECK:{min:1},FS_THR_ENABLE:{min:1},FENCE_ENABLE:{min:1}},range:{INS_GYRO_FILTER:{min:5,max:80,severity:"HIGH"},ATC_RAT_RLL_P:{min:0,max:0.5,severity:"HIGH"},ATC_RAT_PIT_P:{min:0,max:0.5,severity:"HIGH"},ATC_RAT_YAW_P:{min:0,max:1,severity:"HIGH"},MOT_SPIN_ARM:{min:0,max:0.2,severity:"HIGH"},MOT_SPIN_MIN:{min:0,max:0.3,severity:"HIGH"},RTL_ALT:{min:200,max:1e4,severity:"HIGH"},WPNAV_SPEED:{min:50,max:2000,severity:"HIGH"}}}}function r0(Z){let J=$$(),$=c0(Z,".hydron","param-rules.yaml");if(!n0($))return J;try{let X=i0($,"utf8");return o0(J,X)}catch{return J}}function o0(Z,J){let $={ruleset_version:Z.ruleset_version,safety:{...Z.safety},range:{...Z.range}},X=null;for(let Q of J.split(/\r?\n/)){let z=Q.replace(/#.*$/,"").trim();if(!z||z.startsWith("-"))continue;let K=/^ruleset_version\s*:\s*(.+)$/.exec(z);if(K){$.ruleset_version=K[1].trim().replace(/^["']|["']$/g,""),X=null;continue}if(/^safety\s*:\s*$/.test(z)){X="safety";continue}if(/^range\s*:\s*$/.test(z)){X="range";continue}let W=/^([A-Za-z0-9_]+)\s*:\s*\{(.+)\}\s*$/.exec(z);if(W&&X){let G=W[1],B=s0(W[2]);if(X==="safety"){let L=B.min;if(typeof L==="number")$.safety[G]={min:L}}else{let{min:L,max:V,severity:A}=B;if(typeof L==="number"&&typeof V==="number")$.range[G]={min:L,max:V,severity:A==="CRITICAL"?"CRITICAL":"HIGH"}}}}return $}function s0(Z){let J={};for(let $ of Z.split(",")){let X=/^\s*([A-Za-z0-9_]+)\s*:\s*(.+?)\s*$/.exec($);if(!X)continue;let Q=X[1],Y=X[2].replace(/^["']|["']$/g,""),z=Number(Y);J[Q]=Number.isFinite(z)&&Y!==""?z:Y}return J}function a0(Z){let J=new Map;for(let $ of Z)J.set($.name.toUpperCase(),$.value);return J}function v(Z){return{check:Z.check,severity:Z.severity,value:Z.value,threshold_used:Z.threshold_used,ruleset_version:Z.ruleset_version,verdict:Z.verdict,tier:"system",source:Z.source,detail:Z.detail}}function J$(Z,J={}){let $=J.configDir?r0(J.configDir):$$(),X=J.ruleset_version??$.ruleset_version,Q=a0(Z),Y=tZ(J.configDir);return[...t0(Q,$,X),...e0(Q,X),...$8(Q,$,X,Y),...J8(Z,null)]}function t0(Z,J,$){let X=[],Q={kind:"rule-of-thumb"},Y=Z.get("ARMING_CHECK");if(Y===void 0)X.push(b("param.safety.arming",$,"ARMING_CHECK absent"));else{let B=Y!==0;X.push(v({check:"param.safety.arming",severity:B?"OK":"CRITICAL",value:Y,threshold_used:J.safety.ARMING_CHECK?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:$,detail:B?"arming checks enabled":"ARMING_CHECK=0 \u2014 ALL pre-arm safety checks are disabled",source:Q}))}let z=Z.get("BATT_LOW_VOLT"),K=Z.get("BATT_CRT_VOLT");if(z===void 0&&K===void 0)X.push(b("param.safety.battery_failsafe",$,"BATT_LOW_VOLT/BATT_CRT_VOLT absent"));else{let B=(z??0)===0&&(K??0)===0;X.push(v({check:"param.safety.battery_failsafe",severity:B?"CRITICAL":"OK",value:B?0:K??z??0,threshold_used:0,verdict:B?"FAIL":"PASS",ruleset_version:$,detail:B?"battery failsafe not configured (BATT_LOW_VOLT and BATT_CRT_VOLT both 0)":"battery failsafe configured",source:Q}))}let W=Z.get("FS_THR_ENABLE");if(W===void 0)X.push(b("param.safety.radio_failsafe",$,"FS_THR_ENABLE absent"));else{let B=W!==0;X.push(v({check:"param.safety.radio_failsafe",severity:B?"OK":"HIGH",value:W,threshold_used:J.safety.FS_THR_ENABLE?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:$,detail:B?"radio failsafe enabled":"FS_THR_ENABLE=0 \u2014 radio/throttle failsafe is OFF",source:Q}))}let G=Z.get("FENCE_ENABLE");if(G===void 0)X.push(b("param.safety.fence",$,"FENCE_ENABLE absent"));else{let B=G!==0;X.push(v({check:"param.safety.fence",severity:B?"OK":"HIGH",value:G,threshold_used:J.safety.FENCE_ENABLE?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:$,detail:B?"geofence enabled":"FENCE_ENABLE=0 \u2014 geofence disabled (advisory)",source:Q}))}return X}function e0(Z,J){let $=[],X={kind:"rule-of-thumb"},Q=Z.get("BATT_CRT_VOLT"),Y=Z8(Z);if(Q===void 0||Q===0||Y===null||Y<=0)$.push(b("param.consistency.battery_cells",J,"BATT_CRT_VOLT and a cell-count source required"));else{let G=Q/Y,B=G>=3&&G<=3.7;$.push(v({check:"param.consistency.battery_cells",severity:B?"OK":"HIGH",value:GZ(G,3),threshold_used:3,verdict:B?"PASS":"FAIL",ruleset_version:J,detail:B?`per-cell critical voltage ${GZ(G,2)}V across ${Y}S looks sane`:`per-cell critical voltage ${GZ(G,2)}V (${Y}S) outside [3.0,3.7] \u2014 cell count vs BATT_CRT_VOLT inconsistent`,source:X}))}let z=Z.get("FRAME_CLASS"),K=Z.get("FRAME_TYPE");if(z===void 0&&K===void 0)$.push(b("param.consistency.frame",J,"FRAME_CLASS/FRAME_TYPE absent"));else{let G=(z??0)!==0&&(K??0)!==0;$.push(v({check:"param.consistency.frame",severity:G?"OK":"HIGH",value:z??null,threshold_used:1,verdict:G?"PASS":"FAIL",ruleset_version:J,detail:G?`frame configured (CLASS=${z}, TYPE=${K})`:`frame not fully configured (CLASS=${z??"?"}, TYPE=${K??"?"}) \u2014 must be non-zero for a copter`,source:X}))}let W=Z.get("INS_HNTCH_ENABLE");if(W===void 0)$.push(b("param.consistency.notch_filter",J,"INS_HNTCH_ENABLE absent"));else $.push(v({check:"param.consistency.notch_filter",severity:"OK",value:W,threshold_used:null,verdict:"PASS",ruleset_version:J,detail:W===0?"harmonic notch filter disabled (INS_HNTCH_ENABLE=0) \u2014 advisory; tune if vibration is high":"harmonic notch filter enabled",source:X}));return $}function Z8(Z){for(let $ of["BATT_CELLS","BATT_N_CELLS","BATT_CELL_COUNT"]){let X=Z.get($);if(X!==void 0&&X>0)return Math.round(X)}let J=Z.get("BATT_ARM_VOLT");if(J!==void 0&&J>0)return Math.round(J/3.6);return null}function $8(Z,J,$,X){let Q=[];for(let[Y,z]of Object.entries(J.range)){let K=Z.get(Y.toUpperCase()),W=`param.range.${Y}`;if(K===void 0){Q.push(b(W,$,`${Y} absent`));continue}let G=eZ(Y,X),B=G!==void 0&&(typeof G.min==="number"||typeof G.max==="number"),L=B&&typeof G.min==="number"?Math.max(z.min,G.min):z.min,V=B&&typeof G.max==="number"?Math.min(z.max,G.max):z.max,A=B&&typeof G.min==="number"?G.min:z.min,D=B&&typeof G.max==="number"?G.max:z.max,E=B?{kind:"datasheet",ref:"apm.pdef.xml"}:{kind:"tuned"},H=K>=L&&K<=V;Q.push(v({check:W,severity:H?"OK":z.severity,value:K,threshold_used:H?D:K<L?A:D,verdict:H?"PASS":"FAIL",ruleset_version:$,detail:H?`${Y}=${K} within [${A},${D}]${B?" (apm.pdef.xml)":""}`:`${Y}=${K} outside [${A},${D}]${B?" (apm.pdef.xml)":""}`,source:E}))}return Q}function J8(Z,J){return[]}function b(Z,J,$){return v({check:Z,severity:"OK",value:null,threshold_used:null,verdict:"SKIP",ruleset_version:J,detail:$})}function GZ(Z,J){let $=10**J;return Math.round(Z*$)/$}var X$=M(()=>{Z$()});import{existsSync as X8,readFileSync as Q8}from"fs";import{join as Y8}from"path";function z8(){return{ruleset_version:"v1",vibration:{field:"VibeX|VibeY|VibeZ",max_ms2:30,source:{kind:"rule-of-thumb"}},clipping:{field:"Clip",max:0},battery:{err_subsys:6,err_ecode:1,min_cell_v:3.3,max_sag_v:2,max_current_a:200,max_res_ohm:0.03,res_min_current_a:5,source:{kind:"rule-of-thumb"}},attitude:{fields:"DesRoll-Roll,DesPitch-Pitch",max_deg:5,window_samples:10},altitude:{field:"DAlt-Alt",max_m:2},hover:{field:"ThH",band:[0.25,0.75]},ekf:{fields:"IVN,IVE,IVD",max_ratio:0.5,max_variance:1},mag:{field:"Health",min:1,late_align_s:30,max_offset:600},gps:{field:"NSats",min:6,hdop_max:2,hacc_max_m:5},power:{min_vcc_v:4.5,source:{kind:"rule-of-thumb"}},rate:{max_err_dps:50,source:{kind:"rule-of-thumb"}},esc:{max_temp_c:90,source:{kind:"rule-of-thumb"}},imu:{max_err:0,accel_consistency_max:0.7,gyro_consistency_max:0.3,source:{kind:"rule-of-thumb"}},airspeed:{stall_min_ms:8,source:{kind:"rule-of-thumb"}}}}function Y$(Z){let J=z8(),$=Y8(Z,".hydron","uav-thresholds.yaml");if(!X8($))return J;try{let X=K8(Q8($,"utf8"));return z$(J,X)}catch{return J}}function z$(Z,J){let $={...Z};for(let[X,Q]of Object.entries(J)){let Y=$[X];if(Q&&typeof Q==="object"&&!Array.isArray(Q)&&Y&&typeof Y==="object"&&!Array.isArray(Y))$[X]=z$(Y,Q);else $[X]=Q}return $}function Q$(Z){let J=!1,$=!1;for(let X=0;X<Z.length;X++){let Q=Z[X];if(Q==="'"&&!$)J=!J;else if(Q==='"'&&!J)$=!$;else if(Q==="#"&&!J&&!$)return Z.slice(0,X)}return Z}function K$(Z,J){let $=[],X=0,Q=!1,Y=!1,z=0;for(let K=0;K<Z.length;K++){let W=Z[K];if(W==="'"&&!Y)Q=!Q;else if(W==='"'&&!Q)Y=!Y;else if(!Q&&!Y){if(W==="["||W==="{")X++;else if(W==="]"||W==="}")X--;else if(W===J&&X===0)$.push(Z.slice(z,K)),z=K+1}}return $.push(Z.slice(z)),$}function d(Z){let J=Z.trim();if(J==="")return"";if(/^-?\d+(\.\d+)?$/.test(J))return Number(J);if(J==="true")return!0;if(J==="false")return!1;if(J.startsWith("[")&&J.endsWith("]")){let $=J.slice(1,-1).trim();return $?K$($,",").map(d):[]}if(J.startsWith("{")&&J.endsWith("}"))return W$(J);return J.replace(/^["']|["']$/g,"")}function W$(Z){let J=Z.trim().replace(/^\{/,"").replace(/\}$/,""),$={};for(let X of K$(J,",")){let Q=X.indexOf(":");if(Q<0)continue;let Y=X.slice(0,Q).trim().replace(/^["']|["']$/g,"");if(Y)$[Y]=d(X.slice(Q+1))}return $}function K8(Z){let J={},$=Z.split(/\r?\n/),X=0;while(X<$.length){let Q=Q$($[X]);if(X++,!Q.trim()||/^\s/.test(Q))continue;let Y=/^([A-Za-z0-9_]+):\s*(.*)$/.exec(Q);if(!Y)continue;let z=Y[1],K=Y[2].trim();if(K.startsWith("{"))J[z]=W$(K);else if(K!=="")J[z]=d(K);else{let W={};while(X<$.length){let G=Q$($[X]);if(!G.trim()){X++;continue}if(!/^\s/.test(G))break;X++;let B=/^\s+([A-Za-z0-9_]+):\s*(.*)$/.exec(G);if(B&&B[2].trim()!=="")W[B[1]]=d(B[2])}J[z]=W}}return J}var G$=()=>{};function P(Z,J){let $=J.toLowerCase();return Z.columns.findIndex((X)=>X.toLowerCase()===$)}function h(Z){if(typeof Z==="number")return Number.isFinite(Z)?Z:null;if(typeof Z==="bigint")return Number(Z);if(typeof Z==="string"){let J=Number(Z);return Number.isFinite(J)?J:null}return null}function W8(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z.trim()))try{return BigInt(Z.trim())}catch{return null}return null}function g(Z){if(Z===null||Z===void 0)return null;if(typeof Z==="string")return Z;return String(Z)}function l(Z){return`"${Z.replace(/"/g,'""')}"`}function LZ(Z,J){try{return!!Z.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get(J)}catch{return!1}}function VZ(Z,J){try{let $=Z.query(`PRAGMA table_info(${l(J)})`).all();return new Set($.map((X)=>X.name))}catch{return new Set}}function n(Z,J){try{let $=Z.query(J).get();if(!$)return null;let X=Object.values($)[0];if(X===null||X===void 0)return null;let Q=typeof X==="bigint"?Number(X):Number(X);return Number.isFinite(Q)?Q:null}catch{return null}}function G8(Z){for(let J of["ATT","GPS","VIBE","CTUN","IMU"])if(LZ(Z,J)&&VZ(Z,J).has("TimeUS")){let $=n(Z,`SELECT COUNT(*) AS c FROM ${l(J)}`);if($===null||$<=0)continue;let X=n(Z,`SELECT (MAX("TimeUS") - MIN("TimeUS")) AS d FROM ${l(J)}`);if(X!==null)return X/1e6}return null}function B8(Z){if(!LZ(Z,"VIBE"))return null;let J=VZ(Z,"VIBE"),$=["VibeX","VibeY","VibeZ"].filter((Q)=>J.has(Q));if($.length===0)return null;let X=$.map((Q)=>`SELECT MAX(${l(Q)}) AS m FROM "VIBE"`).join(" UNION ALL ");return n(Z,`SELECT MAX(m) AS m FROM (${X})`)}function H8(Z){if(!LZ(Z,"CTUN")||!VZ(Z,"CTUN").has("ThH"))return null;return n(Z,'SELECT AVG("ThH") AS m FROM "CTUN"')}function BZ(Z,J){let $=Z.tables.PARM;if(!$)return null;let X=P($,"Name"),Q=P($,"Value");if(X<0||Q<0)return null;let Y=J.toLowerCase();for(let z of $.rows)if(g(z[X])?.toLowerCase()===Y)return h(z[Q]);return null}function UZ(Z,J){let $=Z.tables.MSG;if(!$)return null;let X=P($,"Message")>=0?P($,"Message"):P($,"Msg");if(X<0)return null;for(let Q of $.rows){let Y=g(Q[X]);if(Y&&J.test(Y))return Y}return null}function V8(Z){let J=Z.tables.MSG;if(!J)return null;let $=P(J,"Message")>=0?P(J,"Message"):P(J,"Msg");if($<0)return null;for(let X of J.rows){let Q=g(X[$]),Y=Q&&L8.exec(Q.trim());if(Y){let[,z,K,W,G]=Y;return{id:`${K}${W}${G}`.toUpperCase(),name:`${z}-${K.toUpperCase()}`}}}return null}function O8(Z){let J=V8(Z),$=BZ(Z,"SYSID_THISMAV"),X,Q;if(J)X=J.id,Q=J.name;else if($!==null)X=`sysid-${$}`,Q=UZ(Z,/ArduCopter|ArduPlane|ArduPilot|board/i)??X;else{let K=UZ(Z,/ArduCopter|ArduPlane|ArduPilot|ChibiOS|board/i);X=K??"unknown",Q=K??X}let Y,z=BZ(Z,"Q_ENABLE");if(z!==null&&z>=1)Y="QuadPlane";else{let K=BZ(Z,"FRAME_CLASS");if(K!==null)Y=U8[K]??`class_${K}`}return{kind:"airframe",external_id:X,attrs:{name:Q,frame:Y}}}function N8(Z){let J=Z.tables.GPS;if(!J)return null;let $=P(J,"GWk"),X=P(J,"GMS");if($<0||X<0)return null;let Q=315964800000,Y=18000,z=null;for(let W of J.rows){let G=h(W[$]),B=h(W[X]);if(G===null||B===null||G<=0)continue;let L=Q+G*7*24*3600*1000+B-Y;if(z===null||L<z)z=L}if(z===null)return null;let K=new Date(z);return Number.isNaN(K.getTime())?null:K.toISOString()}function q8(Z,J,$){let X=UZ(Z,/ArduCopter|ArduPlane|ArduPilot/);return{kind:"analysis",source_hash:$,logged_at:N8(Z),attrs:{firmware:X??null,duration_s:G8(J),pilot_id:null,rc_id:null}}}function j8(Z){let J=Z.tables.PARM;if(!J)return[];let $=P(J,"Name"),X=P(J,"Value");if($<0||X<0)return[];let Q=P(J,"Default"),Y=[];for(let z of J.rows){let K=g(z[$]);if(!K)continue;let W=h(z[X]);if(W===null)continue;let G={name:K,value:W};if(Q>=0){let B=h(z[Q]);G.default=B}Y.push(G)}return Y}function HZ(Z,J){let $=P(Z,"TimeUS");if($<0)return 0n;return W8(J[$])??0n}function A8(Z){let J=[],$=Z.tables.ERR;if($){let Y=P($,"Subsys"),z=P($,"ECode");for(let K of $.rows)J.push({at:HZ($,K),type:"ERR",payload:{Subsys:Y>=0?h(K[Y]):null,ECode:z>=0?h(K[z]):null}})}let X=Z.tables.MODE;if(X){let Y=P(X,"TimeUS");for(let z of X.rows){let K={};X.columns.forEach((W,G)=>{if(G===Y)return;K[W]=h(z[G])??g(z[G])}),J.push({at:HZ(X,z),type:"MODE",payload:K})}}let Q=Z.tables.MSG;if(Q){let Y=P(Q,"Message")>=0?P(Q,"Message"):P(Q,"Msg");for(let z of Q.rows)J.push({at:HZ(Q,z),type:"MSG",payload:{text:Y>=0?g(z[Y]):null}})}return J}function F8(Z,J){let $=[],X=B8(J);if(X!==null)$.push({name:"vibe_peak",value:X,unit:"m/s/s"});let Q=Z.tables.ERR;if(Q){let z=P(Q,"Subsys"),K=P(Q,"ECode");if(z>=0&&K>=0){let W=0;for(let G of Q.rows)if(h(G[z])===6&&h(G[K])===1)W++;$.push({name:"failsafe_count",value:W})}}let Y=H8(J);if(Y!==null)$.push({name:"hover_thr",value:Y});return $}function B$(Z,J,$){return{entity:O8(Z),episode:q8(Z,J,$.sourceHash),params:j8(Z),events:A8(Z),measurements:F8(Z,J)}}var U8,L8;var H$=M(()=>{U8={0:"Undefined",1:"Quad",2:"Hexa",3:"Octa",4:"OctaQuad",5:"Y6",6:"Heli",7:"Tri",8:"SingleCopter",9:"CoaxCopter",10:"BiCopter",11:"Heli_Dual",12:"DodecaHexa",13:"HeliQuad",14:"Deca",15:"Scripting"};L8=/^([A-Za-z][A-Za-z0-9_+\-]*)\s+([0-9A-Fa-f]{8})\s+([0-9A-Fa-f]{8})\s+([0-9A-Fa-f]{8})\b/});import{mkdir as R8,readFile as OZ,writeFile as L$,readdir as V$,stat as D8}from"fs/promises";import{dirname as kJ,join as f}from"path";function O$(Z,J,$){return f(Z,".hydron","observations",J,i($))}function i(Z){return Z.replace(/[^A-Za-z0-9._-]/g,"_")||"unknown"}function P8(Z,J){return typeof J==="bigint"?J.toString():J}function N$(Z){if(Array.isArray(Z.events))Z.events=Z.events.map((J)=>{let $=J.at,X;if(typeof $==="bigint")X=$;else if(typeof $==="string"&&/^-?\d+$/.test($))X=BigInt($);else if(typeof $==="number")X=BigInt(Math.trunc($));else X=0n;return{...J,at:X}});return Z}function E8(Z){let J=Z.findings??[],$=J.filter((z)=>z.verdict==="FAIL").length,X=J.filter((z)=>z.severity==="CRITICAL"&&z.verdict==="FAIL").length,Q="OK";for(let z of J)if(U$[z.severity]>U$[Q])Q=z.severity;let Y=(z)=>Z.measurements?.find((K)=>K.name===z)?.value??null;return{source_hash:Z.episode.source_hash,logged_at:Z.episode.logged_at,ruleset_version:Z.ruleset_version,anomalies:$,critical:X,vibe_peak:Y("vibe_peak"),failsafe_count:Y("failsafe_count"),worst_severity:Q,...Z.tags&&Z.tags.length>0?{tags:Z.tags}:{}}}async function T8(Z){let J;try{J=await OZ(Z,"utf8")}catch{return[]}let $=[];for(let X of J.split(`
5
- `)){let Q=X.trim();if(!Q)continue;try{$.push(JSON.parse(Q))}catch{}}return $}async function _8(Z,J){let $=f(Z,"index.jsonl"),X=await T8($),Q=E8(J),Y=X.filter((z)=>z.source_hash!==Q.source_hash);Y.push(Q),await L$($,Y.map((z)=>JSON.stringify(z)).join(`
2
+ var m$=Object.defineProperty;var p$=(Z)=>Z;function d$(Z,$){this[Z]=p$.bind(null,$)}var DZ=(Z,$)=>{for(var J in $)m$(Z,J,{get:$[J],enumerable:!0,configurable:!0,set:d$.bind($,J)})};var M=(Z,$)=>()=>(Z&&($=Z(Z=0)),$);function EZ(){let Z=process.env.UAV_DECODE_MAX_CPU;if(!Z)return;let $=Number(Z);return Number.isFinite($)&&$>0&&$<1?$:void 0}async function RZ(Z,$){if(!$||Z<=0)return;let J=Math.min(Z*(1/$-1),250);if(J>=1)await l$(J)}var l$=(Z)=>new Promise(($)=>setTimeout($,Z));var PZ;var TZ=M(()=>{PZ={a:{kind:"str",width:64},b:{kind:"i8",width:1},B:{kind:"u8",width:1},h:{kind:"i16",width:2},H:{kind:"u16",width:2},i:{kind:"i32",width:4},I:{kind:"u32",width:4},f:{kind:"f32",width:4},d:{kind:"f64",width:8},n:{kind:"str",width:4},N:{kind:"str",width:16},Z:{kind:"str",width:64},c:{kind:"i16",width:2,scale:0.01},C:{kind:"u16",width:2,scale:0.01},e:{kind:"i32",width:4,scale:0.01},E:{kind:"u32",width:4,scale:0.01},L:{kind:"i32",width:4,scale:0.0000001},M:{kind:"u8",width:1},q:{kind:"i64",width:8},Q:{kind:"u64",width:8}}});import{open as n$}from"fs/promises";function o$(Z){return($,J)=>{let X="",Q=J+Z;for(let z=J;z<Q;z++){let K=$.getUint8(z);if(K===0)break;X+=String.fromCharCode(K)}return X}}function s$(Z){let $=Z.scale;switch(Z.kind){case"i8":return $===void 0?(J,X)=>J.getInt8(X):(J,X)=>J.getInt8(X)*$;case"u8":return $===void 0?(J,X)=>J.getUint8(X):(J,X)=>J.getUint8(X)*$;case"i16":return $===void 0?(J,X)=>J.getInt16(X,!0):(J,X)=>J.getInt16(X,!0)*$;case"u16":return $===void 0?(J,X)=>J.getUint16(X,!0):(J,X)=>J.getUint16(X,!0)*$;case"i32":return $===void 0?(J,X)=>J.getInt32(X,!0):(J,X)=>J.getInt32(X,!0)*$;case"u32":return $===void 0?(J,X)=>J.getUint32(X,!0):(J,X)=>J.getUint32(X,!0)*$;case"f32":return(J,X)=>J.getFloat32(X,!0);case"f64":return(J,X)=>J.getFloat64(X,!0);case"i64":return(J,X)=>J.getBigInt64(X,!0);case"u64":return(J,X)=>J.getBigUint64(X,!0);case"str":return o$(Z.width)}}function a$(Z,$){let J=[],X=[];for(let z of Z){let K=PZ[z];if(!K)return{readers:[],widths:[],ok:!1,timeIdx:-1};J.push(s$(K)),X.push(K.width)}let Q=$.indexOf("TimeUS");return{readers:J,widths:X,ok:!0,timeIdx:Q}}async function IZ(Z,$,J){let X=J??EZ(),Q=new Map,z={},K=!1,W=null,Y=null,G=4194304,B=await n$(Z,"r");try{let U=Buffer.allocUnsafe(G),O=new Uint8Array(0),N=0,P=!1;while(!P){let{bytesRead:_}=await B.read(U,0,G,N);N+=_,P=_===0;let j;if(_===0)j=O;else if(O.length===0)j=U.subarray(0,_);else j=new Uint8Array(O.length+_),j.set(O,0),j.set(U.subarray(0,_),O.length);let L=new DataView(j.buffer,j.byteOffset,j.byteLength),V=j.length,D=P,q=performance.now(),F=0;while(F+3<=V){if(j[F]!==i$||j[F+1]!==c$){F+=1;continue}let E=j[F+2],w=F+3;if(E===r$){if(w+_Z>V){if(D)K=!0;break}let v=j[w],FZ=j[w+1],p=e(j,w+2,4),h$=e(j,w+6,16),t=e(j,w+22,64).split(",").filter((x$)=>x$.length>0),{readers:b$,widths:g$,ok:f$,timeIdx:u$}=a$(h$,t);Q.set(v,{name:p,length:FZ,columns:t,ok:f$,payloadLen:FZ-3,readers:b$,widths:g$,timeIdx:u$}),$.onFmt(p,t),z[p]=(z[p]??0)+1,F+=3+_Z;continue}let S=Q.get(E);if(S===void 0){F+=1;continue}let y=S.payloadLen;if(w+y>V){if(D)K=!0;break}if(z[S.name]=(z[S.name]??0)+1,!S.ok){F+=3+y;continue}let{readers:s,widths:v$}=S,a=Array(s.length),AZ=w;for(let v=0;v<s.length;v++)a[v]=s[v](L,AZ),AZ+=v$[v];if($.onRow(S.name,a),S.timeIdx>=0){let v=a[S.timeIdx];if(typeof v==="bigint")Y=v}F+=3+y}if(O=j.slice(F),await RZ(performance.now()-q,X),P)break}}finally{await B.close()}if(K)W=Y;return{partial:K,decodedUntil:W,messageCounts:z}}function SZ(Z,$,J){let X=Z[$];if(!X)Z[$]={columns:J,rows:[]};else X.columns=J}async function MZ(Z){let $={},J=await IZ(Z,{onFmt:(X,Q)=>SZ($,X,Q),onRow:(X,Q)=>{$[X].rows.push(Q)}});return{partial:J.partial,decodedUntil:J.decodedUntil,tables:$,messageCounts:J.messageCounts}}async function CZ(Z,$,J={}){let X=J.retain??new Set,Q=5000,z={},K=new Map,W=await IZ(Z,{onFmt:(Y,G)=>{if(SZ(z,Y,G),$.table(Y,G),!K.has(Y))K.set(Y,[])},onRow:(Y,G)=>{let B=K.get(Y);if(B){if(B.push(G),B.length>=5000)$.rows(Y,B),B.length=0}if(X.has(Y))z[Y].rows.push(G)}});for(let[Y,G]of K)if(G.length)$.rows(Y,G);return{partial:W.partial,decodedUntil:W.decodedUntil,tables:z,messageCounts:W.messageCounts}}function e(Z,$,J){let X="",Q=$+J;for(let z=$;z<Q;z++){let K=Z[z];if(K===0)break;X+=String.fromCharCode(K)}return X}var i$=163,c$=149,r$=128,_Z=86;var wZ=M(()=>{TZ()});function e$(){let Z=process.env.UAV_PYTHON?.trim();if(Z)return[[Z]];return process.platform==="win32"?[["py","-3"],["python"],["python3"]]:[["python3"],["python"]]}async function Z0(Z){try{return await Bun.spawn([...Z,"-c","import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)"],{stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited===0}catch{return!1}}async function $0(){if(yZ)return ZZ;for(let Z of e$())if(await Z0(Z)){ZZ=Z;break}return yZ=!0,ZZ}async function d(){let Z=await $0();if(!Z)throw Error(t$);return Z}var t$,ZZ=null,yZ=!1;var $Z=M(()=>{t$="no working Python 3 interpreter found. Install Python >= 3.8 and make sure `python3` "+"(macOS/Linux) or `python` (Windows \u2014 tick \u201CAdd python.exe to PATH\u201D in the installer) runs "+"from a terminal, or set UAV_PYTHON to the interpreter's full path."});import{fileURLToPath as J0}from"url";function kZ(Z){return typeof Z==="object"&&Z!==null&&"_summary"in Z}function z0(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z))return BigInt(Z);return 0n}async function JZ(Z){let $=await d();if(await Bun.spawn([...$,"-c","import pymavlink"],{stdout:"ignore",stderr:"ignore"}).exited!==0)throw Error(`pymavlink not found \u2014 ${$.join(" ")} -m pip install pymavlink`);let X=Bun.spawn([...$,X0,Z],{stdout:"pipe",stderr:"pipe"}),Q=new Map,z={},K=!1,W=null,Y=null,G=!1;function B(L){let V=Q.get(L);if(!V)V={columns:[],colIndex:new Map,rows:[]},Q.set(L,V);return V}function U(L){let V=L.t,D=B(V),q=L.f;for(let E of Object.keys(q))if(!D.colIndex.has(E))D.colIndex.set(E,D.columns.length),D.columns.push(E);let F=Array(D.columns.length).fill(null);for(let E of Object.keys(q)){let w=D.colIndex.get(E),S=q[E],y=Q0.has(E)?z0(S):S;if(F[w]=y,E==="TimeUS"&&typeof y==="bigint")Y=y}D.rows.push(F)}let O=new TextDecoder,N="";for await(let L of X.stdout){N+=O.decode(L,{stream:!0});let V;while((V=N.indexOf(`
3
+ `))!==-1){let D=N.slice(0,V);if(N=N.slice(V+1),D.length===0)continue;let q;try{q=JSON.parse(D)}catch{K=!0;continue}if(kZ(q)){G=!0;for(let[F,E]of Object.entries(q._summary.counts))z[F]=E;if(q._summary.partial)K=!0}else U(q)}}N+=O.decode();let P=N.trim();if(P.length>0)try{let L=JSON.parse(P);if(kZ(L)){G=!0;for(let[V,D]of Object.entries(L._summary.counts))z[V]=D;if(L._summary.partial)K=!0}else U(L)}catch{K=!0}let _=await X.exited;if(_!==0){if(!G){let L=(await new Response(X.stderr).text()).trim();throw Error(`pymavlink decode failed (exit ${_})${L?`: ${L}`:""}`)}K=!0}if(!G)K=!0;if(K)W=Y;let j={};for(let[L,V]of Q)j[L]={columns:V.columns,rows:V.rows};for(let[L,V]of Q)if(z[L]===void 0)z[L]=V.rows.length;return{partial:K,decodedUntil:W,tables:j,messageCounts:z}}var X0,Q0;var vZ=M(()=>{$Z();X0=J0(new URL("./mavdump.py",import.meta.url)),Q0=new Set(["TimeUS","TimeMS"])});import{fileURLToPath as K0}from"url";function G0(Z){return typeof Z==="object"&&Z!==null&&"_summary"in Z}function B0(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z))return BigInt(Z);return 0n}async function H0(Z){let $=()=>Bun.spawn([...Z,"-c","import pyulog"],{stdout:"ignore",stderr:"ignore"}).exited;if(await $()===0)return;if(await Bun.spawn([...Z,"-m","pip","install","--quiet","pyulog"],{stdout:"ignore",stderr:"ignore"}).exited,await $()!==0)throw Error(`pyulog not found \u2014 ${Z.join(" ")} -m pip install pyulog`)}async function hZ(Z){let $=await d();await H0($);let J=Bun.spawn([...$,W0,Z],{stdout:"pipe",stderr:"pipe"}),X=new Map,Q={},z=!1,K=null,W=null,Y=!1;function G(L){let V=X.get(L);if(!V)V={columns:[],colIndex:new Map,rows:[]},X.set(L,V);return V}function B(L){let V=L.t,D=G(V),q=L.f;for(let E of Object.keys(q))if(!D.colIndex.has(E))D.colIndex.set(E,D.columns.length),D.columns.push(E);let F=Array(D.columns.length).fill(null);for(let E of Object.keys(q)){let w=D.colIndex.get(E),S=q[E],y=Y0.has(E)?B0(S):S;if(F[w]=y,E==="timestamp"&&typeof y==="bigint")W=y}D.rows.push(F)}function U(L){if(L.length===0)return;let V;try{V=JSON.parse(L)}catch{z=!0;return}if(G0(V)){Y=!0;for(let[D,q]of Object.entries(V._summary.counts))Q[D]=q;if(V._summary.partial)z=!0}else B(V)}let O=new TextDecoder,N="";for await(let L of J.stdout){N+=O.decode(L,{stream:!0});let V;while((V=N.indexOf(`
4
+ `))!==-1)U(N.slice(0,V)),N=N.slice(V+1)}N+=O.decode();let P=N.trim();if(P.length>0)U(P);let _=await J.exited;if(_!==0){if(!Y){let L=(await new Response(J.stderr).text()).trim();throw Error(`ulog decode failed (exit ${_})${L?`: ${L}`:""}`)}z=!0}if(!Y)z=!0;if(z)K=W;let j={};for(let[L,V]of X)j[L]={columns:V.columns,rows:V.rows};for(let[L,V]of X)if(Q[L]===void 0)Q[L]=V.rows.length;return{partial:z,decodedUntil:K,tables:j,messageCounts:Q}}var W0,Y0;var bZ=M(()=>{$Z();W0=K0(new URL("./ulog.py",import.meta.url)),Y0=new Set(["timestamp","timestamp_sample"])});import{existsSync as gZ,readFileSync as U0}from"fs";import{mkdtemp as V0,rm as L0}from"fs/promises";import{tmpdir as O0}from"os";import{basename as XZ,join as QZ}from"path";function fZ(Z){if(!Z)return[];let $=QZ(Z,".hydron","uav-format-converters.json");if(!gZ($))return[];try{let J=JSON.parse(U0($,"utf8"));return Array.isArray(J.converters)?J.converters.filter((X)=>X&&X.match&&X.command):[]}catch{return[]}}function uZ(Z,$){for(let J of $)try{if(new RegExp(J.match,"i").test(Z))return J}catch{}return null}async function xZ(Z,$){let J=await V0(QZ(O0(),"uav-convert-")),X=XZ(Z).replace(/\.[^.]+$/,""),Q=QZ(J,X+($.produces??".bin")),z=$.command.replaceAll("{input}",Z).replaceAll("{output}",Q),K=()=>L0(J,{recursive:!0,force:!0});try{let W=process.platform==="win32"?["cmd.exe","/d","/s","/c",z]:["bash","-lc",z],Y=Bun.spawn(W,{stdout:"pipe",stderr:"pipe"}),G=await Y.exited;if(G!==0){let B=(await new Response(Y.stderr).text()).trim().slice(0,300);throw Error(`format converter failed (exit ${G}) for ${XZ(Z)}: ${B}`)}if(!gZ(Q))throw Error(`format converter produced no output for ${XZ(Z)}`);return{out:Q,cleanup:K}}catch(W){throw await K(),W}}var mZ=()=>{};import{Database as N0}from"bun:sqlite";import{tmpdir as j0}from"os";import{join as q0}from"path";import{existsSync as pZ,unlinkSync as dZ}from"fs";function nZ(Z){let $=q0(j0(),`uav-${Z}-${process.pid}-${A0++}.sqlite`);try{if(pZ($))dZ($)}catch{}let J=new N0($);return J.exec("PRAGMA page_size = 8192"),J.query("PRAGMA journal_mode = MEMORY").get(),J.exec("PRAGMA synchronous = OFF"),J.exec("PRAGMA temp_store = FILE"),J.exec("PRAGMA cache_size = -65536"),{db:J,cleanup(){try{J.close()}catch{}try{if(pZ($))dZ($)}catch{}}}}function KZ(Z){let $=new Map,J=new Map;return{table(X,Q){if($.has(X)||Q.length===0)return;Z.run(`CREATE TABLE ${zZ(X)} (${Q.map(zZ).join(", ")})`);let z=Q.map(()=>"?").join(", ");$.set(X,Z.query(`INSERT INTO ${zZ(X)} VALUES (${z})`)),J.set(X,Q.length)},rows(X,Q){let z=$.get(X),K=J.get(X);if(!z||K===void 0||Q.length===0)return;let W=Z.transaction((Y)=>{for(let G of Y){let B=Array(K);for(let U=0;U<K;U++){let O=G[U];B[U]=O===void 0?null:O}z.run(...B)}});for(let Y=0;Y<Q.length;Y+=lZ)W(Q.slice(Y,Y+lZ))}}}function iZ(Z,$){let J=KZ(Z);for(let[X,Q]of Object.entries($.tables)){if(Q.columns.length===0)continue;if(J.table(X,Q.columns),Q.rows.length)J.rows(X,Q.rows)}}var A0=0,zZ=(Z)=>`"${Z.replace(/"/g,'""')}"`,lZ=5000;var cZ=()=>{};async function m(Z,$="flight",J){let X=uZ(Z,fZ(J?.configDir));if(X){let{out:K,cleanup:W}=await xZ(Z,X);try{return await m(K,$)}finally{await W()}}let{db:Q,cleanup:z}=nZ($);try{let K=Z.toLowerCase();if(K.endsWith(".bin"))return{log:await CZ(Z,KZ(Q),{retain:F0}),db:Q,cleanup:z};let W;if(K.endsWith(".ulog"))W=await hZ(Z);else if(K.endsWith(".tlog")||K.endsWith(".log"))W=await JZ(Z);else try{W=await MZ(Z)}catch{W=await JZ(Z)}return iZ(Q,W),{log:W,db:Q,cleanup:z}}catch(K){throw z(),K}}var F0;var WZ=M(()=>{wZ();vZ();bZ();mZ();cZ();F0=new Set(["PARM","MSG","GPS","ERR","MODE"])});function YZ(Z,$){let J=Z.tables.PARM;if(!J)return null;let X=J.columns.findIndex((K)=>K.toLowerCase()==="name"),Q=J.columns.findIndex((K)=>K.toLowerCase()==="value");if(X<0||Q<0)return null;let z=$.toLowerCase();for(let K of J.rows)if(String(K[X]).toLowerCase()===z){let W=Number(K[Q]);return Number.isFinite(W)?W:null}return null}function D0(Z){let $=Z.tables.MSG;if(!$)return"";let J=$.columns.findIndex((X)=>/message|msg/i.test(X));if(J<0)return"";for(let X of $.rows){let Q=String(X[J]??"");if(/Ardu(Copter|Plane|Sub|Rover)|PX4/i.test(Q))return Q}return""}function rZ(Z){let $=YZ(Z,"Q_ENABLE");if($!==null&&$>=1||Z.tables.QUIK||Z.tables.AETR){if(Z.tables.MOTB||YZ(Z,"FRAME_CLASS")!==null||Z.tables.ARSP)return"vtol"}if($!==null&&$>=1)return"vtol";let J=D0(Z);if(/ArduPlane/i.test(J))return"fixedwing";if(/ArduCopter/i.test(J))return"multirotor";if(YZ(Z,"FRAME_CLASS")!==null)return"multirotor";if(Z.tables.ARSP&&!Z.tables.MOTB)return"fixedwing";if(Z.tables.MOTB||Z.tables.RATE)return"multirotor";return"unknown"}function l(Z){return Z===void 0||Z==="multirotor"||Z==="vtol"||Z==="unknown"}function oZ(Z){return Z==="fixedwing"||Z==="vtol"}function C(Z){return`"${Z.replace(/"/g,'""')}"`}function A(Z,$){let J=Z.db.query($).get();if(!J)return null;let X=Object.values(J)[0];if(X===null||X===void 0)return null;return Number(X)}function k(Z,$){try{let J=Z.db.query(`PRAGMA table_info(${C($)})`).all();return new Set(J.map((X)=>X.name))}catch{return new Set}}function I(Z,$,J){return k(Z,$).has(J)}function aZ(Z,$){for(let J of $)if(Z.hasTable(J))return J;return null}function H(Z,$,J){return{check:Z,severity:"OK",value:null,threshold_used:null,ruleset_version:$.ruleset_version,verdict:"SKIP",tier:"system",...J?{detail:J}:{}}}function R(Z){return{check:Z.check,severity:Z.failed?Z.failSeverity:"OK",value:Z.value,threshold_used:Z.threshold_used,ruleset_version:Z.thresholds.ruleset_version,verdict:Z.failed?"FAIL":"PASS",tier:"system",...Z.source?{source:Z.source}:{},...Z.detail?{detail:Z.detail}:{}}}function M0(Z,$,J){let X=Object.values(Z);if($<=0||X.length===0)return"";let Q=[...X].sort((G,B)=>B-G),z=Q[0],K=Q.length>1?Q[1]:0;if(Q.length>=2&&K>0&&z>=2*K)return" | cause: likely sensor/mounting alignment (one axis dominates)";let Y=Q.length>=2&&z<=1.5*(Q[Q.length-1]||z);if(J>0||Y)return" | cause: likely mechanical degradation (props/bearings/frame)";return""}function tZ(Z,$,J){let X=new Set;try{let K=Z.query("SELECT name FROM sqlite_master WHERE type='table'").all();for(let W of K)X.add(W.name)}catch{}let Q={db:Z,thresholds:$,hasTable:(K)=>X.has(K),vehicleType:J?.vehicleType},z=[];for(let{id:K,run:W}of o0)try{z.push(W(Q))}catch(Y){let G=Y instanceof Error?Y.message:String(Y);z.push(H(K,$,`check error: ${G}`))}return z}var E0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("ERR"))return H("battery",Z.thresholds);if(!I(Z,"ERR","Subsys")||!I(Z,"ERR","ECode"))return H("battery",Z.thresholds,"ERR present but Subsys/ECode columns absent");let J=$?.err_subsys??6,X=$?.err_ecode??1,Q=A(Z,`SELECT COUNT(*) FROM "ERR" WHERE "Subsys"=${J} AND "ECode"=${X}`)??0;return R({check:"battery",thresholds:Z.thresholds,failed:Q>0,failSeverity:"CRITICAL",value:Q,threshold_used:0,source:$?.source,detail:Q>0?`${Q} battery failsafe ERR event(s) (Subsys=${J} ECode=${X})`:void 0})},R0=(Z)=>{let $=Z.thresholds.attitude;if(!Z.hasTable("ATT"))return H("attitude",Z.thresholds);for(let K of["DesRoll","Roll","DesPitch","Pitch"])if(!I(Z,"ATT",K))return H("attitude",Z.thresholds,`ATT missing column ${K}`);let J=$?.max_deg??5,X=A(Z,'SELECT MAX(ABS("DesRoll"-"Roll")) FROM "ATT"'),Q=A(Z,'SELECT MAX(ABS("DesPitch"-"Pitch")) FROM "ATT"'),z=Math.max(X??0,Q??0);return R({check:"attitude",thresholds:Z.thresholds,failed:z>J,failSeverity:"HIGH",value:z,threshold_used:J,source:$?.source,detail:`peak roll err ${(X??0).toFixed(2)}\xB0, pitch err ${(Q??0).toFixed(2)}\xB0`})},P0=(Z)=>{let $=Z.thresholds.altitude;if(!Z.hasTable("CTUN"))return H("altitude",Z.thresholds);if(!I(Z,"CTUN","DAlt")||!I(Z,"CTUN","Alt"))return H("altitude",Z.thresholds,"CTUN missing DAlt/Alt columns");let J=$?.max_m??2,X=A(Z,'SELECT MAX(ABS("DAlt"-"Alt")) FROM "CTUN"');return R({check:"altitude",thresholds:Z.thresholds,failed:(X??0)>J,failSeverity:"HIGH",value:X,threshold_used:J,source:$?.source})},T0=(Z)=>{let $=Z.thresholds.hover;if(!l(Z.vehicleType))return H("hover",Z.thresholds,`n/a for ${Z.vehicleType} (no hover throttle)`);if(!Z.hasTable("CTUN"))return H("hover",Z.thresholds);if(!I(Z,"CTUN","ThH"))return H("hover",Z.thresholds,"CTUN missing ThH column");let J=$?.band??[0.25,0.75],[X,Q]=J,z=A(Z,'SELECT AVG("ThH") FROM "CTUN"'),K=z===null?!1:z<X||z>Q;return R({check:"hover",thresholds:Z.thresholds,failed:K,failSeverity:"HIGH",value:z,threshold_used:Q,source:$?.source,detail:`mean hover throttle ${(z??0).toFixed(3)} vs band [${X}, ${Q}]`})},sZ=2000,_0=(Z)=>{if(!l(Z.vehicleType))return H("rcou",Z.thresholds,`n/a for ${Z.vehicleType} (control surfaces, not motor mix)`);if(!Z.hasTable("RCOU"))return H("rcou",Z.thresholds);let $=k(Z,"RCOU"),J=[];for(let z=1;z<=8;z++)if($.has(`C${z}`))J.push(`C${z}`);if(J.length===0)return H("rcou",Z.thresholds,"RCOU has no C1..C8 channels");let X=-1/0,Q=[];for(let z of J){let K=A(Z,`SELECT MAX(${C(z)}) FROM "RCOU"`);if(K===null)continue;if(K>X)X=K;if(K>=sZ)Q.push(`${z}=${K}`)}if(X===-1/0)X=0;return R({check:"rcou",thresholds:Z.thresholds,failed:Q.length>0,failSeverity:"HIGH",value:X,threshold_used:sZ,detail:Q.length>0?`motor output(s) pegged at max: ${Q.join(", ")}`:void 0})},GZ=1000,BZ=2000,I0=(Z)=>{if(!Z.hasTable("RCIN"))return H("rcin",Z.thresholds);if(!I(Z,"RCIN","C3"))return H("rcin",Z.thresholds,"RCIN missing C3 column");let $=A(Z,'SELECT MIN("C3") FROM "RCIN"'),J=A(Z,'SELECT MAX("C3") FROM "RCIN"'),X=$!==null&&$<GZ||J!==null&&J>BZ,Q=J;if($!==null&&$<GZ)Q=$;return R({check:"rcin",thresholds:Z.thresholds,failed:X,failSeverity:"HIGH",value:Q,threshold_used:BZ,detail:`RCIN C3 range [${$??"\u2014"}, ${J??"\u2014"}] vs [${GZ}, ${BZ}]`})},S0=(Z)=>{let $=Z.thresholds.vibration;if(!Z.hasTable("VIBE"))return H("vibration",Z.thresholds);let J=k(Z,"VIBE"),X=["VibeX","VibeY","VibeZ"].filter((U)=>J.has(U));if(X.length===0)return H("vibration",Z.thresholds,"VIBE has no VibeX/Y/Z columns");let Q=$?.max_ms2??30,z={},K=-1/0;for(let U of X){let N=A(Z,`SELECT MAX(${C(U)}) FROM "VIBE"`)??0;if(z[U]=N,N>K)K=N}if(K===-1/0)K=0;let W=["Clip","Clip0","Clip1","Clip2"].filter((U)=>J.has(U)),Y=0;for(let U of W){let O=A(Z,`SELECT MAX(${C(U)}) FROM "VIBE"`);if(O!==null&&O>Y)Y=O}let G=W.length>0?` | clipping MAX=${Y}${Y>0?" (CLIP EVENTS)":""}`:"",B=M0(z,K,Y);return R({check:"vibration",thresholds:Z.thresholds,failed:K>=Q,failSeverity:"HIGH",value:K,threshold_used:Q,source:$?.source,detail:`peak vibe ${K} m/s\xB2 (axes ${X.join("/")})${G}${B}`})},C0=(Z)=>{let $=Z.thresholds.mag;if(!Z.hasTable("MAG"))return H("mag",Z.thresholds);if(!I(Z,"MAG","Health"))return H("mag",Z.thresholds,"MAG missing Health column");let J=$?.min??1,X=A(Z,'SELECT MIN("Health") FROM "MAG"');return R({check:"mag",thresholds:Z.thresholds,failed:X!==null&&X<J,failSeverity:"HIGH",value:X,threshold_used:J,source:$?.source})},w0=(Z)=>{let $=Z.thresholds.ekf,J=aZ(Z,["XKF3","NKF3"]);if(!J)return H("ekf",Z.thresholds);let X=k(Z,J),Q=["IVN","IVE","IVD","IVT","IPN","IPE","IPD"].filter((W)=>X.has(W));if(Q.length===0)return H("ekf",Z.thresholds,`${J} has no IVN/IVE/IVD/IVT/IPN/IPE/IPD columns`);let z=$?.max_ratio??0.5,K=-1/0;for(let W of Q){let Y=A(Z,`SELECT MAX(ABS(${C(W)})) FROM ${C(J)}`);if(Y!==null&&Y>K)K=Y}if(K===-1/0)K=0;return R({check:"ekf",thresholds:Z.thresholds,failed:K>z,failSeverity:"HIGH",value:K,threshold_used:z,source:$?.source,detail:`peak innovation ${K} (${J}: ${Q.join("/")})`})},y0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return H("min_cell_voltage",Z.thresholds);if(!I(Z,"BAT","Volt"))return H("min_cell_voltage",Z.thresholds,"BAT missing Volt column");let J=$?.min_cell_v??3.3,X=A(Z,'SELECT MAX("Volt") FROM "BAT"'),Q=A(Z,'SELECT MIN("Volt") FROM "BAT"');if(X===null||Q===null||X<=0)return H("min_cell_voltage",Z.thresholds,"BAT.Volt has no usable values");let z=Math.max(1,Math.round(X/4.2)),K=Q/z;return R({check:"min_cell_voltage",thresholds:Z.thresholds,failed:K<J,failSeverity:"CRITICAL",value:K,threshold_used:J,source:$?.source,detail:`${z}S pack (MAX pack ${X.toFixed(2)}V), min cell ${K.toFixed(3)}V vs floor ${J}V`})},k0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return H("voltage_sag",Z.thresholds);if(!I(Z,"BAT","VoltR")||!I(Z,"BAT","Volt"))return H("voltage_sag",Z.thresholds,"BAT missing VoltR/Volt columns");let J=$?.max_sag_v??2,X=A(Z,'SELECT MAX("VoltR"-"Volt") FROM "BAT"');return R({check:"voltage_sag",thresholds:Z.thresholds,failed:X!==null&&X>J,failSeverity:"HIGH",value:X,threshold_used:J,source:$?.source,detail:`peak sag ${(X??0).toFixed(2)}V (VoltR-Volt) vs ${J}V`})},v0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return H("over_current",Z.thresholds);if(!I(Z,"BAT","Curr"))return H("over_current",Z.thresholds,"BAT missing Curr column");let J=$?.max_current_a??200,X=A(Z,'SELECT MAX("Curr") FROM "BAT"');return R({check:"over_current",thresholds:Z.thresholds,failed:X!==null&&X>J,failSeverity:"HIGH",value:X,threshold_used:J,source:$?.source,detail:`peak current ${(X??0).toFixed(1)}A vs ${J}A`})},h0=(Z)=>{let $=Z.thresholds.battery;if(!Z.hasTable("BAT"))return H("internal_resistance",Z.thresholds);if(!I(Z,"BAT","Res"))return H("internal_resistance",Z.thresholds,"BAT missing Res column");let J=A(Z,'SELECT MAX("Res") FROM "BAT"');if(J===null||J===0)return H("internal_resistance",Z.thresholds,"BAT.Res all zero/absent (not reported)");let X=$?.res_min_current_a??5,z=I(Z,"BAT","Curr")?A(Z,`SELECT MAX("Res") FROM "BAT" WHERE "Curr" >= ${X}`):J;if(z===null)return H("internal_resistance",Z.thresholds,`no BAT samples at Curr \u2265 ${X} A \u2014 Res only valid under load (max raw ${J.toFixed(3)}\u03A9 is a low-current artifact)`);let K=$?.max_res_ohm??0.03;return R({check:"internal_resistance",thresholds:Z.thresholds,failed:z>K,failSeverity:"HIGH",value:z,threshold_used:K,source:$?.source,detail:`peak internal resistance ${z.toFixed(4)}\u03A9 (at Curr \u2265 ${X} A) vs ${K}\u03A9`})},b0=(Z)=>{let $=Z.thresholds.power;if(!Z.hasTable("POWR"))return H("board_brownout",Z.thresholds);if(!I(Z,"POWR","Vcc"))return H("board_brownout",Z.thresholds,"POWR missing Vcc column");let J=$?.min_vcc_v??4.5,X=A(Z,'SELECT MIN("Vcc") FROM "POWR"');return R({check:"board_brownout",thresholds:Z.thresholds,failed:X!==null&&X<J,failSeverity:"CRITICAL",value:X,threshold_used:J,source:$?.source,detail:`min board Vcc ${(X??0).toFixed(3)}V vs floor ${J}V`})},g0=(Z)=>{let $=Z.thresholds.rate;if(!Z.hasTable("RATE"))return H("rate_tracking",Z.thresholds);let J=k(Z,"RATE"),Q=[["RDes","R"],["PDes","P"],["YDes","Y"]].filter(([Y,G])=>J.has(Y)&&J.has(G));if(Q.length===0)return H("rate_tracking",Z.thresholds,"RATE has no RDes/R, PDes/P, YDes/Y pairs");let z=$?.max_err_dps??50,K=-1/0,W="";for(let[Y,G]of Q){let B=A(Z,`SELECT MAX(ABS(${C(Y)}-${C(G)})) FROM "RATE"`);if(B!==null&&B>K)K=B,W=Y.replace("Des","")}if(K===-1/0)K=0;return R({check:"rate_tracking",thresholds:Z.thresholds,failed:K>z,failSeverity:"HIGH",value:K,threshold_used:z,source:$?.source,detail:`peak rate-tracking error ${K.toFixed(2)} deg/s (axis ${W||"?"}) vs ${z} deg/s`})},f0=(Z)=>{let $=Z.thresholds.gps;if(!Z.hasTable("GPA"))return H("gps_accuracy",Z.thresholds);if(!I(Z,"GPA","HAcc"))return H("gps_accuracy",Z.thresholds,"GPA missing HAcc column");let J=$?.hacc_max_m??5,X=A(Z,'SELECT MAX("HAcc") FROM "GPA"');return R({check:"gps_accuracy",thresholds:Z.thresholds,failed:X!==null&&X>J,failSeverity:"HIGH",value:X,threshold_used:J,source:$?.source,detail:`peak horizontal accuracy ${(X??0).toFixed(2)}m vs ${J}m`})},u0=(Z)=>{let $=Z.thresholds.ekf,J=aZ(Z,["XKF4","NKF4"]);if(!J)return H("ekf_variance",Z.thresholds);let X=k(Z,J),Q=["SV","SP","SH","SM"].filter((B)=>X.has(B));if(Q.length===0)return H("ekf_variance",Z.thresholds,`${J} has no SV/SP/SH/SM columns`);let z=$?.max_variance??1,K=-1/0,W="";for(let B of Q){let U=A(Z,`SELECT MAX(${C(B)}) FROM ${C(J)}`);if(U!==null&&U>K)K=U,W=B}if(K===-1/0)K=0;let Y=X.has("FS")?A(Z,`SELECT MAX("FS") FROM ${C(J)}`):null,G=Y!==null&&Y>0?` | EKF failsafe set (FS MAX=${Y})`:"";return R({check:"ekf_variance",thresholds:Z.thresholds,failed:K>z,failSeverity:"HIGH",value:K,threshold_used:z,source:$?.source,detail:`peak EKF variance ${K.toFixed(3)} (${J}: ${W||Q.join("/")}) vs ${z}${G}`})},x0=(Z)=>{let $=Z.thresholds.mag;if(!Z.hasTable("MAG"))return H("mag_offsets",Z.thresholds);let J=k(Z,"MAG"),X=["OfsX","OfsY","OfsZ"].filter((W)=>J.has(W));if(X.length===0)return H("mag_offsets",Z.thresholds,"MAG has no OfsX/OfsY/OfsZ columns");let Q=$?.max_offset??600,z=-1/0,K="";for(let W of X){let Y=A(Z,`SELECT MAX(ABS(${C(W)})) FROM "MAG"`);if(Y!==null&&Y>z)z=Y,K=W}if(z===-1/0)z=0;return R({check:"mag_offsets",thresholds:Z.thresholds,failed:z>Q,failSeverity:"HIGH",value:z,threshold_used:Q,source:$?.source,detail:`peak mag offset ${z.toFixed(0)} (${K||X.join("/")}) vs ${Q}`})},m0=(Z)=>{let $=Z.thresholds.esc;if(!Z.hasTable("ESC"))return H("esc_health",Z.thresholds);let J=k(Z,"ESC"),X=["Temp","MotTemp"].filter((U)=>J.has(U));if(X.length===0&&!J.has("Err"))return H("esc_health",Z.thresholds,"ESC has no Temp/MotTemp/Err columns");let Q=$?.max_temp_c??90,z=-1/0,K="";for(let U of X){let O=A(Z,`SELECT MAX(${C(U)}) FROM "ESC"`);if(O!==null&&O>z)z=O,K=U}if(z===-1/0)z=0;let W=J.has("Err")?A(Z,'SELECT MAX("Err") FROM "ESC"')??0:0,Y=X.length>0&&z>Q,G=W>0,B=G?` | ESC Err MAX=${W}`:"";return R({check:"esc_health",thresholds:Z.thresholds,failed:Y||G,failSeverity:"HIGH",value:z,threshold_used:Q,source:$?.source,detail:`peak ESC temp ${z.toFixed(1)}\xB0C (${K||"n/a"}) vs ${Q}\xB0C${B}`})},p0=(Z)=>{if(!l(Z.vehicleType))return H("thrust_margin",Z.thresholds,`n/a for ${Z.vehicleType} (lift/T:W is a rotorcraft metric)`);if(!Z.hasTable("MOTB"))return H("thrust_margin",Z.thresholds);let $=k(Z,"MOTB");if(!$.has("FailFlags")&&!($.has("ThrOut")&&$.has("LiftMax")))return H("thrust_margin",Z.thresholds,"MOTB missing FailFlags and ThrOut/LiftMax columns");let J=$.has("FailFlags")?A(Z,'SELECT MAX("FailFlags") FROM "MOTB"')??0:0,X=null;if($.has("ThrOut")&&$.has("LiftMax")){let W=A(Z,'SELECT AVG("ThrOut") FROM "MOTB"'),Y=A(Z,'SELECT AVG("LiftMax") FROM "MOTB"');if(W!==null&&Y!==null&&Y>0)X=W/Y}let Q=X!==null&&X>0.5,z=J>0,K=`MOTB FailFlags MAX=${J}`+(X!==null?`, mean ThrOut/LiftMax ${X.toFixed(2)} (low-margin > 0.5)`:"")+(z?" \u2014 THRUST-LOSS flagged":Q?" \u2014 low thrust margin":"");return R({check:"thrust_margin",thresholds:Z.thresholds,failed:z||Q,failSeverity:"HIGH",value:X,threshold_used:0.5,detail:K})},d0=(Z)=>{let $=Z.thresholds.imu;if(!Z.hasTable("IMU"))return H("imu_health",Z.thresholds);let J=k(Z,"IMU"),X=["EG","EA"].filter((W)=>J.has(W));if(X.length===0)return H("imu_health",Z.thresholds,"IMU has no EG/EA error-count columns");let Q=$?.max_err??0,z=-1/0,K="";for(let W of X){let Y=A(Z,`SELECT MAX(${C(W)}) FROM "IMU"`);if(Y!==null&&Y>z)z=Y,K=W}if(z===-1/0)z=0;return R({check:"imu_health",thresholds:Z.thresholds,failed:z>Q,failSeverity:"HIGH",value:z,threshold_used:Q,source:$?.source,detail:`max IMU error count ${z} (${K||X.join("/")}) vs ${Q}`})},l0=(Z)=>{let $=Z.thresholds.imu;if(!Z.hasTable("IMU"))return H("imu_consistency",Z.thresholds);let J=k(Z,"IMU");if(!J.has("I"))return H("imu_consistency",Z.thresholds,"IMU has no instance column I");let X=["AccX","AccY","AccZ"].filter((q)=>J.has(q)),Q=["GyrX","GyrY","GyrZ"].filter((q)=>J.has(q));if(X.length===0&&Q.length===0)return H("imu_consistency",Z.thresholds,"IMU has no Acc*/Gyr* columns");let z=[...X,...Q].map((q)=>`AVG(${C(q)}) ${C(q)}`).join(", "),K=Z.db.query(`SELECT "I" AS "I", ${z} FROM "IMU" GROUP BY "I"`).all();if(K.length<2)return H("imu_consistency",Z.thresholds,`only ${K.length} IMU instance \u2014 nothing to cross-check`);let W=(q)=>{let F=K.map((E)=>Number(E[q])).filter((E)=>Number.isFinite(E));return F.length>=2?Math.max(...F)-Math.min(...F):0},Y=0,G="";for(let q of X){let F=W(q);if(F>Y)Y=F,G=q}let B=0,U="";for(let q of Q){let F=W(q);if(F>B)B=F,U=q}let O=$?.accel_consistency_max??0.7,N=$?.gyro_consistency_max??0.3,P=Y>O,_=B>N,j=P||_,L=Y/O>=B/N?Y:B,V=Y/O>=B/N?O:N,D=j?`INCONSISTENT \u2014 ${P?`accel ${G} spread ${Y.toFixed(3)} m/s\xB2 > ${O}`:""}${P&&_?"; ":""}${_?`gyro ${U} spread ${B.toFixed(3)} rad/s > ${N}`:""}`:`IMUs consistent \u2014 max inter-instance bias spread: accel ${Y.toFixed(3)} m/s\xB2 (<${O}), gyro ${B.toFixed(3)} rad/s (<${N}); relative differences between instances are within healthy tolerance`;return R({check:"imu_consistency",thresholds:Z.thresholds,failed:j,failSeverity:"HIGH",value:L,threshold_used:V,source:{kind:"rule-of-thumb",ref:"IMU inconsistency tolerance (~ArduPilot arming limits)"},detail:D})},n0=(Z)=>{if(!Z.hasTable("ERR"))return H("err_events",Z.thresholds);if(!I(Z,"ERR","Subsys")||!I(Z,"ERR","ECode"))return H("err_events",Z.thresholds,"ERR present but Subsys/ECode columns absent");let $=Z.thresholds.battery?.err_subsys??6,J=Z.db.query(`SELECT "Subsys" AS subsys, "ECode" AS ecode, COUNT(*) AS n FROM "ERR" WHERE "ECode" <> 0 AND "Subsys" <> ${$} GROUP BY "Subsys", "ECode" ORDER BY "Subsys", "ECode"`).all(),X=0,Q=[];for(let z of J)X+=Number(z.n),Q.push(`Subsys=${z.subsys} ECode=${z.ecode} \xD7${z.n}`);return R({check:"err_events",thresholds:Z.thresholds,failed:X>0,failSeverity:"HIGH",value:X,threshold_used:0,detail:X>0?`${X} non-battery ERR event(s): ${Q.join(", ")}`:"no non-battery ERR events"})},i0,c0=(Z)=>{if(!Z.hasTable("MODE"))return H("mode_changes",Z.thresholds);if(!I(Z,"MODE","ModeNum"))return H("mode_changes",Z.thresholds,"MODE missing ModeNum column");let $=Z.db.query('SELECT "ModeNum" AS modenum FROM "MODE"').all(),J=$.length,X=J>0?J-1:0,Q=new Map,z=!1;for(let W of $){let Y=Number(W.modenum);if(Q.set(Y,(Q.get(Y)??0)+1),i0.has(Y))z=!0}let K=[...Q.entries()].sort((W,Y)=>W[0]-Y[0]).map(([W,Y])=>`Mode${W}\xD7${Y}`).join(", ");return R({check:"mode_changes",thresholds:Z.thresholds,failed:z,failSeverity:"HIGH",value:X,threshold_used:0,detail:`${X} mode change(s); timeline: ${K||"\u2014"}`+(z?" \u2014 emergency mode (RTL/LAND) entered":"")})},r0=(Z)=>{let $=Z.thresholds.airspeed;if(!oZ(Z.vehicleType))return H("airspeed",Z.thresholds,`n/a for ${Z.vehicleType} (no fixed-wing airspeed)`);if(!Z.hasTable("ARSP"))return H("airspeed",Z.thresholds,"no ARSP (airspeed) data");if(!I(Z,"ARSP","Airspeed"))return H("airspeed",Z.thresholds,"ARSP missing Airspeed column");let J=$?.stall_min_ms??8,X={kind:"rule-of-thumb",ref:"default stall floor"};if(Z.hasTable("PARM")){let z=A(Z,`SELECT "Value" FROM "PARM" WHERE "Name"='ARSPD_FBW_MIN' LIMIT 1`);if(z!==null&&z>0)J=z,X={kind:"datasheet",ref:"ARSPD_FBW_MIN"}}let Q=A(Z,'SELECT MIN("Airspeed") FROM "ARSP" WHERE "Airspeed" > 2');if(Q===null)return H("airspeed",Z.thresholds,"no airborne airspeed samples");return R({check:"airspeed",thresholds:Z.thresholds,failed:Q<J,failSeverity:"HIGH",value:Q,threshold_used:J,source:X,detail:`min in-flight airspeed ${Q.toFixed(1)} m/s vs stall floor ${J} m/s`})},o0;var eZ=M(()=>{i0=new Set([6,9]),o0=[{id:"battery",run:E0},{id:"min_cell_voltage",run:y0},{id:"voltage_sag",run:k0},{id:"over_current",run:v0},{id:"internal_resistance",run:h0},{id:"board_brownout",run:b0},{id:"attitude",run:R0},{id:"rate_tracking",run:g0},{id:"altitude",run:P0},{id:"hover",run:T0},{id:"rcou",run:_0},{id:"rcin",run:I0},{id:"thrust_margin",run:p0},{id:"vibration",run:S0},{id:"imu_health",run:d0},{id:"imu_consistency",run:l0},{id:"mag",run:C0},{id:"mag_offsets",run:x0},{id:"gps_accuracy",run:f0},{id:"ekf",run:w0},{id:"ekf_variance",run:u0},{id:"esc_health",run:m0},{id:"airspeed",run:r0},{id:"err_events",run:n0},{id:"mode_changes",run:c0}]});import{existsSync as s0,readFileSync as a0}from"fs";import{dirname as t0,join as HZ}from"path";import{fileURLToPath as e0}from"url";function J$(Z){if(Z){let $=HZ(Z,".hydron","apm-params.json"),J=$$($);if(J&&J.size>0)return J}for(let $ of Z8){let J=$$($);if(J)return J}return new Map}function X$(Z,$){return $.get(Z.toUpperCase())}function $$(Z){if(!s0(Z))return;try{let $=a0(Z,"utf8"),J=JSON.parse($);if(!J||typeof J!=="object")return;let X=new Map;for(let[Q,z]of Object.entries(J)){if(!Q||Q.startsWith("_")||typeof z!=="object"||z===null)continue;let K={name:Q};if(typeof z.min==="number"&&Number.isFinite(z.min))K.min=z.min;if(typeof z.max==="number"&&Number.isFinite(z.max))K.max=z.max;if(typeof z.units==="string"&&z.units)K.units=z.units;X.set(Q.toUpperCase(),K)}return X}catch{return}}var Z$,Z8;var Q$=M(()=>{Z$=t0(e0(import.meta.url)),Z8=[HZ(Z$,"..","data","apm-params.json"),HZ(Z$,"..","..","data","apm-params.json")]});import{existsSync as $8,readFileSync as J8}from"fs";import{join as X8}from"path";function z$(){return{ruleset_version:"v1",safety:{ARMING_CHECK:{min:1},FS_THR_ENABLE:{min:1},FENCE_ENABLE:{min:1}},range:{INS_GYRO_FILTER:{min:5,max:80,severity:"HIGH"},ATC_RAT_RLL_P:{min:0,max:0.5,severity:"HIGH"},ATC_RAT_PIT_P:{min:0,max:0.5,severity:"HIGH"},ATC_RAT_YAW_P:{min:0,max:1,severity:"HIGH"},MOT_SPIN_ARM:{min:0,max:0.2,severity:"HIGH"},MOT_SPIN_MIN:{min:0,max:0.3,severity:"HIGH"},RTL_ALT:{min:200,max:1e4,severity:"HIGH"},WPNAV_SPEED:{min:50,max:2000,severity:"HIGH"}}}}function Q8(Z){let $=z$(),J=X8(Z,".hydron","param-rules.yaml");if(!$8(J))return $;try{let X=J8(J,"utf8");return z8($,X)}catch{return $}}function z8(Z,$){let J={ruleset_version:Z.ruleset_version,safety:{...Z.safety},range:{...Z.range}},X=null;for(let Q of $.split(/\r?\n/)){let K=Q.replace(/#.*$/,"").trim();if(!K||K.startsWith("-"))continue;let W=/^ruleset_version\s*:\s*(.+)$/.exec(K);if(W){J.ruleset_version=W[1].trim().replace(/^["']|["']$/g,""),X=null;continue}if(/^safety\s*:\s*$/.test(K)){X="safety";continue}if(/^range\s*:\s*$/.test(K)){X="range";continue}let Y=/^([A-Za-z0-9_]+)\s*:\s*\{(.+)\}\s*$/.exec(K);if(Y&&X){let G=Y[1],B=K8(Y[2]);if(X==="safety"){let U=B.min;if(typeof U==="number")J.safety[G]={min:U}}else{let{min:U,max:O,severity:N}=B;if(typeof U==="number"&&typeof O==="number")J.range[G]={min:U,max:O,severity:N==="CRITICAL"?"CRITICAL":"HIGH"}}}}return J}function K8(Z){let $={};for(let J of Z.split(",")){let X=/^\s*([A-Za-z0-9_]+)\s*:\s*(.+?)\s*$/.exec(J);if(!X)continue;let Q=X[1],z=X[2].replace(/^["']|["']$/g,""),K=Number(z);$[Q]=Number.isFinite(K)&&z!==""?K:z}return $}function W8(Z){let $=new Map;for(let J of Z)$.set(J.name.toUpperCase(),J.value);return $}function b(Z){return{check:Z.check,severity:Z.severity,value:Z.value,threshold_used:Z.threshold_used,ruleset_version:Z.ruleset_version,verdict:Z.verdict,tier:"system",source:Z.source,detail:Z.detail}}function K$(Z,$={}){let J=$.configDir?Q8($.configDir):z$(),X=$.ruleset_version??J.ruleset_version,Q=W8(Z),z=J$($.configDir);return[...Y8(Q,J,X),...G8(Q,X),...H8(Q,J,X,z),...U8(Z,null)]}function Y8(Z,$,J){let X=[],Q={kind:"rule-of-thumb"},z=Z.get("ARMING_CHECK");if(z===void 0)X.push(g("param.safety.arming",J,"ARMING_CHECK absent"));else{let B=z!==0;X.push(b({check:"param.safety.arming",severity:B?"OK":"CRITICAL",value:z,threshold_used:$.safety.ARMING_CHECK?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:J,detail:B?"arming checks enabled":"ARMING_CHECK=0 \u2014 ALL pre-arm safety checks are disabled",source:Q}))}let K=Z.get("BATT_LOW_VOLT"),W=Z.get("BATT_CRT_VOLT");if(K===void 0&&W===void 0)X.push(g("param.safety.battery_failsafe",J,"BATT_LOW_VOLT/BATT_CRT_VOLT absent"));else{let B=(K??0)===0&&(W??0)===0;X.push(b({check:"param.safety.battery_failsafe",severity:B?"CRITICAL":"OK",value:B?0:W??K??0,threshold_used:0,verdict:B?"FAIL":"PASS",ruleset_version:J,detail:B?"battery failsafe not configured (BATT_LOW_VOLT and BATT_CRT_VOLT both 0)":"battery failsafe configured",source:Q}))}let Y=Z.get("FS_THR_ENABLE");if(Y===void 0)X.push(g("param.safety.radio_failsafe",J,"FS_THR_ENABLE absent"));else{let B=Y!==0;X.push(b({check:"param.safety.radio_failsafe",severity:B?"OK":"HIGH",value:Y,threshold_used:$.safety.FS_THR_ENABLE?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:J,detail:B?"radio failsafe enabled":"FS_THR_ENABLE=0 \u2014 radio/throttle failsafe is OFF",source:Q}))}let G=Z.get("FENCE_ENABLE");if(G===void 0)X.push(g("param.safety.fence",J,"FENCE_ENABLE absent"));else{let B=G!==0;X.push(b({check:"param.safety.fence",severity:B?"OK":"HIGH",value:G,threshold_used:$.safety.FENCE_ENABLE?.min??1,verdict:B?"PASS":"FAIL",ruleset_version:J,detail:B?"geofence enabled":"FENCE_ENABLE=0 \u2014 geofence disabled (advisory)",source:Q}))}return X}function G8(Z,$){let J=[],X={kind:"rule-of-thumb"},Q=Z.get("BATT_CRT_VOLT"),z=B8(Z);if(Q===void 0||Q===0||z===null||z<=0)J.push(g("param.consistency.battery_cells",$,"BATT_CRT_VOLT and a cell-count source required"));else{let G=Q/z,B=G>=3&&G<=3.7;J.push(b({check:"param.consistency.battery_cells",severity:B?"OK":"HIGH",value:UZ(G,3),threshold_used:3,verdict:B?"PASS":"FAIL",ruleset_version:$,detail:B?`per-cell critical voltage ${UZ(G,2)}V across ${z}S looks sane`:`per-cell critical voltage ${UZ(G,2)}V (${z}S) outside [3.0,3.7] \u2014 cell count vs BATT_CRT_VOLT inconsistent`,source:X}))}let K=Z.get("FRAME_CLASS"),W=Z.get("FRAME_TYPE");if(K===void 0&&W===void 0)J.push(g("param.consistency.frame",$,"FRAME_CLASS/FRAME_TYPE absent"));else{let G=(K??0)!==0&&(W??0)!==0;J.push(b({check:"param.consistency.frame",severity:G?"OK":"HIGH",value:K??null,threshold_used:1,verdict:G?"PASS":"FAIL",ruleset_version:$,detail:G?`frame configured (CLASS=${K}, TYPE=${W})`:`frame not fully configured (CLASS=${K??"?"}, TYPE=${W??"?"}) \u2014 must be non-zero for a copter`,source:X}))}let Y=Z.get("INS_HNTCH_ENABLE");if(Y===void 0)J.push(g("param.consistency.notch_filter",$,"INS_HNTCH_ENABLE absent"));else J.push(b({check:"param.consistency.notch_filter",severity:"OK",value:Y,threshold_used:null,verdict:"PASS",ruleset_version:$,detail:Y===0?"harmonic notch filter disabled (INS_HNTCH_ENABLE=0) \u2014 advisory; tune if vibration is high":"harmonic notch filter enabled",source:X}));return J}function B8(Z){for(let J of["BATT_CELLS","BATT_N_CELLS","BATT_CELL_COUNT"]){let X=Z.get(J);if(X!==void 0&&X>0)return Math.round(X)}let $=Z.get("BATT_ARM_VOLT");if($!==void 0&&$>0)return Math.round($/3.6);return null}function H8(Z,$,J,X){let Q=[];for(let[z,K]of Object.entries($.range)){let W=Z.get(z.toUpperCase()),Y=`param.range.${z}`;if(W===void 0){Q.push(g(Y,J,`${z} absent`));continue}let G=X$(z,X),B=G!==void 0&&(typeof G.min==="number"||typeof G.max==="number"),U=B&&typeof G.min==="number"?Math.max(K.min,G.min):K.min,O=B&&typeof G.max==="number"?Math.min(K.max,G.max):K.max,N=B&&typeof G.min==="number"?G.min:K.min,P=B&&typeof G.max==="number"?G.max:K.max,_=B?{kind:"datasheet",ref:"apm.pdef.xml"}:{kind:"tuned"},j=W>=U&&W<=O;Q.push(b({check:Y,severity:j?"OK":K.severity,value:W,threshold_used:j?P:W<U?N:P,verdict:j?"PASS":"FAIL",ruleset_version:J,detail:j?`${z}=${W} within [${N},${P}]${B?" (apm.pdef.xml)":""}`:`${z}=${W} outside [${N},${P}]${B?" (apm.pdef.xml)":""}`,source:_}))}return Q}function U8(Z,$){return[]}function g(Z,$,J){return b({check:Z,severity:"OK",value:null,threshold_used:null,verdict:"SKIP",ruleset_version:$,detail:J})}function UZ(Z,$){let J=10**$;return Math.round(Z*J)/J}var W$=M(()=>{Q$()});import{existsSync as V8,readFileSync as L8}from"fs";import{join as O8}from"path";function N8(){return{ruleset_version:"v1",vibration:{field:"VibeX|VibeY|VibeZ",max_ms2:30,source:{kind:"rule-of-thumb"}},clipping:{field:"Clip",max:0},battery:{err_subsys:6,err_ecode:1,min_cell_v:3.3,max_sag_v:2,max_current_a:200,max_res_ohm:0.03,res_min_current_a:5,source:{kind:"rule-of-thumb"}},attitude:{fields:"DesRoll-Roll,DesPitch-Pitch",max_deg:5,window_samples:10},altitude:{field:"DAlt-Alt",max_m:2},hover:{field:"ThH",band:[0.25,0.75]},ekf:{fields:"IVN,IVE,IVD",max_ratio:0.5,max_variance:1},mag:{field:"Health",min:1,late_align_s:30,max_offset:600},gps:{field:"NSats",min:6,hdop_max:2,hacc_max_m:5},power:{min_vcc_v:4.5,source:{kind:"rule-of-thumb"}},rate:{max_err_dps:50,source:{kind:"rule-of-thumb"}},esc:{max_temp_c:90,source:{kind:"rule-of-thumb"}},imu:{max_err:0,accel_consistency_max:0.7,gyro_consistency_max:0.3,source:{kind:"rule-of-thumb"}},airspeed:{stall_min_ms:8,source:{kind:"rule-of-thumb"}}}}function G$(Z){let $=N8(),J=O8(Z,".hydron","uav-thresholds.yaml");if(!V8(J))return $;try{let X=j8(L8(J,"utf8"));return B$($,X)}catch{return $}}function B$(Z,$){let J={...Z};for(let[X,Q]of Object.entries($)){let z=J[X];if(Q&&typeof Q==="object"&&!Array.isArray(Q)&&z&&typeof z==="object"&&!Array.isArray(z))J[X]=B$(z,Q);else J[X]=Q}return J}function Y$(Z){let $=!1,J=!1;for(let X=0;X<Z.length;X++){let Q=Z[X];if(Q==="'"&&!J)$=!$;else if(Q==='"'&&!$)J=!J;else if(Q==="#"&&!$&&!J)return Z.slice(0,X)}return Z}function H$(Z,$){let J=[],X=0,Q=!1,z=!1,K=0;for(let W=0;W<Z.length;W++){let Y=Z[W];if(Y==="'"&&!z)Q=!Q;else if(Y==='"'&&!Q)z=!z;else if(!Q&&!z){if(Y==="["||Y==="{")X++;else if(Y==="]"||Y==="}")X--;else if(Y===$&&X===0)J.push(Z.slice(K,W)),K=W+1}}return J.push(Z.slice(K)),J}function n(Z){let $=Z.trim();if($==="")return"";if(/^-?\d+(\.\d+)?$/.test($))return Number($);if($==="true")return!0;if($==="false")return!1;if($.startsWith("[")&&$.endsWith("]")){let J=$.slice(1,-1).trim();return J?H$(J,",").map(n):[]}if($.startsWith("{")&&$.endsWith("}"))return U$($);return $.replace(/^["']|["']$/g,"")}function U$(Z){let $=Z.trim().replace(/^\{/,"").replace(/\}$/,""),J={};for(let X of H$($,",")){let Q=X.indexOf(":");if(Q<0)continue;let z=X.slice(0,Q).trim().replace(/^["']|["']$/g,"");if(z)J[z]=n(X.slice(Q+1))}return J}function j8(Z){let $={},J=Z.split(/\r?\n/),X=0;while(X<J.length){let Q=Y$(J[X]);if(X++,!Q.trim()||/^\s/.test(Q))continue;let z=/^([A-Za-z0-9_]+):\s*(.*)$/.exec(Q);if(!z)continue;let K=z[1],W=z[2].trim();if(W.startsWith("{"))$[K]=U$(W);else if(W!=="")$[K]=n(W);else{let Y={};while(X<J.length){let G=Y$(J[X]);if(!G.trim()){X++;continue}if(!/^\s/.test(G))break;X++;let B=/^\s+([A-Za-z0-9_]+):\s*(.*)$/.exec(G);if(B&&B[2].trim()!=="")Y[B[1]]=n(B[2])}$[K]=Y}}return $}var V$=()=>{};function T(Z,$){let J=$.toLowerCase();return Z.columns.findIndex((X)=>X.toLowerCase()===J)}function h(Z){if(typeof Z==="number")return Number.isFinite(Z)?Z:null;if(typeof Z==="bigint")return Number(Z);if(typeof Z==="string"){let $=Number(Z);return Number.isFinite($)?$:null}return null}function q8(Z){if(typeof Z==="bigint")return Z;if(typeof Z==="number"&&Number.isFinite(Z))return BigInt(Math.trunc(Z));if(typeof Z==="string"&&/^-?\d+$/.test(Z.trim()))try{return BigInt(Z.trim())}catch{return null}return null}function f(Z){if(Z===null||Z===void 0)return null;if(typeof Z==="string")return Z;return String(Z)}function i(Z){return`"${Z.replace(/"/g,'""')}"`}function NZ(Z,$){try{return!!Z.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get($)}catch{return!1}}function jZ(Z,$){try{let J=Z.query(`PRAGMA table_info(${i($)})`).all();return new Set(J.map((X)=>X.name))}catch{return new Set}}function c(Z,$){try{let J=Z.query($).get();if(!J)return null;let X=Object.values(J)[0];if(X===null||X===void 0)return null;let Q=typeof X==="bigint"?Number(X):Number(X);return Number.isFinite(Q)?Q:null}catch{return null}}function A8(Z){for(let $ of["ATT","GPS","VIBE","CTUN","IMU"])if(NZ(Z,$)&&jZ(Z,$).has("TimeUS")){let J=c(Z,`SELECT COUNT(*) AS c FROM ${i($)}`);if(J===null||J<=0)continue;let X=c(Z,`SELECT (MAX("TimeUS") - MIN("TimeUS")) AS d FROM ${i($)}`);if(X!==null)return X/1e6}return null}function F8(Z){if(!NZ(Z,"VIBE"))return null;let $=jZ(Z,"VIBE"),J=["VibeX","VibeY","VibeZ"].filter((Q)=>$.has(Q));if(J.length===0)return null;let X=J.map((Q)=>`SELECT MAX(${i(Q)}) AS m FROM "VIBE"`).join(" UNION ALL ");return c(Z,`SELECT MAX(m) AS m FROM (${X})`)}function D8(Z){if(!NZ(Z,"CTUN")||!jZ(Z,"CTUN").has("ThH"))return null;return c(Z,'SELECT AVG("ThH") AS m FROM "CTUN"')}function VZ(Z,$){let J=Z.tables.PARM;if(!J)return null;let X=T(J,"Name"),Q=T(J,"Value");if(X<0||Q<0)return null;let z=$.toLowerCase();for(let K of J.rows)if(f(K[X])?.toLowerCase()===z)return h(K[Q]);return null}function OZ(Z,$){let J=Z.tables.MSG;if(!J)return null;let X=T(J,"Message")>=0?T(J,"Message"):T(J,"Msg");if(X<0)return null;for(let Q of J.rows){let z=f(Q[X]);if(z&&$.test(z))return z}return null}function P8(Z){let $=Z.tables.MSG;if(!$)return null;let J=T($,"Message")>=0?T($,"Message"):T($,"Msg");if(J<0)return null;for(let X of $.rows){let Q=f(X[J]),z=Q&&R8.exec(Q.trim());if(z){let[,K,W,Y,G]=z;return{id:`${W}${Y}${G}`.toUpperCase(),name:`${K}-${W.toUpperCase()}`}}}return null}function T8(Z){let $=P8(Z),J=VZ(Z,"SYSID_THISMAV"),X,Q;if($)X=$.id,Q=$.name;else if(J!==null)X=`sysid-${J}`,Q=OZ(Z,/ArduCopter|ArduPlane|ArduPilot|board/i)??X;else{let W=OZ(Z,/ArduCopter|ArduPlane|ArduPilot|ChibiOS|board/i);X=W??"unknown",Q=W??X}let z,K=VZ(Z,"Q_ENABLE");if(K!==null&&K>=1)z="QuadPlane";else{let W=VZ(Z,"FRAME_CLASS");if(W!==null)z=E8[W]??`class_${W}`}return{kind:"airframe",external_id:X,attrs:{name:Q,frame:z}}}function _8(Z){let $=Z.tables.GPS;if(!$)return null;let J=T($,"GWk"),X=T($,"GMS");if(J<0||X<0)return null;let Q=315964800000,z=18000,K=null;for(let Y of $.rows){let G=h(Y[J]),B=h(Y[X]);if(G===null||B===null||G<=0)continue;let U=Q+G*7*24*3600*1000+B-z;if(K===null||U<K)K=U}if(K===null)return null;let W=new Date(K);return Number.isNaN(W.getTime())?null:W.toISOString()}function I8(Z,$,J){let X=OZ(Z,/ArduCopter|ArduPlane|ArduPilot/);return{kind:"analysis",source_hash:J,logged_at:_8(Z),attrs:{firmware:X??null,duration_s:A8($),pilot_id:null,rc_id:null}}}function S8(Z){let $=Z.tables.PARM;if(!$)return[];let J=T($,"Name"),X=T($,"Value");if(J<0||X<0)return[];let Q=T($,"Default"),z=[];for(let K of $.rows){let W=f(K[J]);if(!W)continue;let Y=h(K[X]);if(Y===null)continue;let G={name:W,value:Y};if(Q>=0){let B=h(K[Q]);G.default=B}z.push(G)}return z}function LZ(Z,$){let J=T(Z,"TimeUS");if(J<0)return 0n;return q8($[J])??0n}function M8(Z){let $=[],J=Z.tables.ERR;if(J){let z=T(J,"Subsys"),K=T(J,"ECode");for(let W of J.rows)$.push({at:LZ(J,W),type:"ERR",payload:{Subsys:z>=0?h(W[z]):null,ECode:K>=0?h(W[K]):null}})}let X=Z.tables.MODE;if(X){let z=T(X,"TimeUS");for(let K of X.rows){let W={};X.columns.forEach((Y,G)=>{if(G===z)return;W[Y]=h(K[G])??f(K[G])}),$.push({at:LZ(X,K),type:"MODE",payload:W})}}let Q=Z.tables.MSG;if(Q){let z=T(Q,"Message")>=0?T(Q,"Message"):T(Q,"Msg");for(let K of Q.rows)$.push({at:LZ(Q,K),type:"MSG",payload:{text:z>=0?f(K[z]):null}})}return $}function C8(Z,$){let J=[],X=F8($);if(X!==null)J.push({name:"vibe_peak",value:X,unit:"m/s/s"});let Q=Z.tables.ERR;if(Q){let K=T(Q,"Subsys"),W=T(Q,"ECode");if(K>=0&&W>=0){let Y=0;for(let G of Q.rows)if(h(G[K])===6&&h(G[W])===1)Y++;J.push({name:"failsafe_count",value:Y})}}let z=D8($);if(z!==null)J.push({name:"hover_thr",value:z});return J}function L$(Z,$,J){return{entity:T8(Z),episode:I8(Z,$,J.sourceHash),params:S8(Z),events:M8(Z),measurements:C8(Z,$)}}var E8,R8;var O$=M(()=>{E8={0:"Undefined",1:"Quad",2:"Hexa",3:"Octa",4:"OctaQuad",5:"Y6",6:"Heli",7:"Tri",8:"SingleCopter",9:"CoaxCopter",10:"BiCopter",11:"Heli_Dual",12:"DodecaHexa",13:"HeliQuad",14:"Deca",15:"Scripting"};R8=/^([A-Za-z][A-Za-z0-9_+\-]*)\s+([0-9A-Fa-f]{8})\s+([0-9A-Fa-f]{8})\s+([0-9A-Fa-f]{8})\b/});import{mkdir as w8,readFile as qZ,writeFile as j$,readdir as q$,stat as y8}from"fs/promises";import{dirname as rJ,join as u}from"path";function A$(Z,$,J){return u(Z,".hydron","observations",$,r(J))}function r(Z){return Z.replace(/[^A-Za-z0-9._-]/g,"_")||"unknown"}function k8(Z,$){return typeof $==="bigint"?$.toString():$}function F$(Z){if(Array.isArray(Z.events))Z.events=Z.events.map(($)=>{let J=$.at,X;if(typeof J==="bigint")X=J;else if(typeof J==="string"&&/^-?\d+$/.test(J))X=BigInt(J);else if(typeof J==="number")X=BigInt(Math.trunc(J));else X=0n;return{...$,at:X}});return Z}function v8(Z){let $=Z.findings??[],J=$.filter((K)=>K.verdict==="FAIL").length,X=$.filter((K)=>K.severity==="CRITICAL"&&K.verdict==="FAIL").length,Q="OK";for(let K of $)if(N$[K.severity]>N$[Q])Q=K.severity;let z=(K)=>Z.measurements?.find((W)=>W.name===K)?.value??null;return{source_hash:Z.episode.source_hash,logged_at:Z.episode.logged_at,ruleset_version:Z.ruleset_version,anomalies:J,critical:X,vibe_peak:z("vibe_peak"),failsafe_count:z("failsafe_count"),worst_severity:Q,...Z.tags&&Z.tags.length>0?{tags:Z.tags}:{}}}async function h8(Z){let $;try{$=await qZ(Z,"utf8")}catch{return[]}let J=[];for(let X of $.split(`
5
+ `)){let Q=X.trim();if(!Q)continue;try{J.push(JSON.parse(Q))}catch{}}return J}async function b8(Z,$){let J=u(Z,"index.jsonl"),X=await h8(J),Q=v8($),z=X.filter((K)=>K.source_hash!==Q.source_hash);z.push(Q),await j$(J,z.map((K)=>JSON.stringify(K)).join(`
6
6
  `)+`
7
- `,"utf8")}async function q$(Z,J){let $=O$(J.projectDir,J.workspace,Z.entity.external_id);await R8($,{recursive:!0});let X=f($,`${i(Z.episode.source_hash)}.json`);return await L$(X,JSON.stringify(Z,P8,2),"utf8"),await _8($,Z),X}async function j$(Z){let J=O$(Z.projectDir,Z.workspace,Z.sysid),$;try{$=await V$(J)}catch{return null}let X=`${i(Z.excludeSourceHash)}.json`,Q=$.filter((z)=>z.endsWith(".json")&&z!==X),Y=null;for(let z of Q){let K=f(J,z),W;try{let V=await OZ(K,"utf8");W=N$(JSON.parse(V))}catch{continue}let G=0;try{G=(await D8(K)).mtimeMs}catch{}let B=W.episode?.logged_at?Date.parse(W.episode.logged_at):NaN,L=Number.isNaN(B)?-1/0:B;if(Y===null||L>Y.loggedAt||L===Y.loggedAt&&G>Y.mtime)Y={doc:W,loggedAt:L,mtime:G}}return Y?.doc??null}async function I8(Z,J,$){let X=f(Z,".hydron","observations",J),Q=`${i($)}.json`,Y;try{Y=await V$(X)}catch{return null}for(let z of Y){let K=f(X,z,Q),W;try{let G=await OZ(K,"utf8");W=N$(JSON.parse(G))}catch{continue}return{path:K,doc:W}}return null}async function A$(Z){let J=await I8(Z.projectDir,Z.workspace,Z.flight_id);if(!J)return[];return(J.doc.findings??[]).filter(($)=>$.origin==="agent")}var U$;var F$=M(()=>{U$={OK:0,HIGH:1,CRITICAL:2}});function S8(Z,J){let $=new Map;for(let Q of Z)$.set(Q.name,Q.value);let X=[];for(let Q of J){let Y=$.get(Q.name);if(Y===void 0)continue;if(Y!==Q.value)X.push({name:Q.name,old:Y,new:Q.value})}return X}function M8(Z,J){return S8(Z.params??[],J.params??[])}function C8(Z,J){let $=new Map;for(let Q of Z.findings??[])if(Q.tier==="system")$.set(Q.check,Q);let X=[];for(let Q of J.findings??[]){if(Q.tier!=="system")continue;let Y=$.get(Q.check);if(!Y)continue;if(Y.severity!==Q.severity)X.push({check:Q.check,old:Y.severity,new:Q.severity})}return X}function R$(Z,J){return{param_changes:M8(Z,J),finding_changes:C8(Z,J)}}var E$={};jZ(E$,{analyzeFlight:()=>g8});import{createHash as w8}from"crypto";import{open as P$,stat as y8}from"fs/promises";import{resolve as k8}from"path";async function h8(Z){let J=await P$(Z,"r");try{let $=Buffer.alloc(2);return await J.read($,0,2,0),$[0]===D$[0]&&$[1]===D$[1]}finally{await J.close()}}async function v8(Z){let J=await P$(Z,"r");try{let $=w8("sha256"),X=Buffer.alloc(1048576),Q=0;for(;;){let{bytesRead:Y}=await J.read(X,0,X.length,Q);if(Y===0)break;$.update(X.subarray(0,Y)),Q+=Y}return $.digest("hex").slice(0,16)}finally{await J.close()}}function b8(Z){let J=Z.filter((Q)=>Q.verdict==="FAIL").length,$=Z.filter((Q)=>Q.severity==="CRITICAL"&&Q.verdict==="FAIL").length,X=Z.filter((Q)=>Q.verdict==="PASS").length;return{total:Z.length,passed:X,anomalies:J,critical:$}}async function g8(Z,J){if(await y8(Z),!await h8(Z))throw Error(`Not an ArduPilot DataFlash .bin (missing 0xA3 0x95 header): ${Z}`);let $=await v8(Z),X=Y$(J.configDir??J.projectDir),{log:Q,db:Y,cleanup:z}=await x(Z,$,{configDir:J.configDir??J.projectDir});try{let K=B$(Q,Y,{sourceHash:$});K.episode.attrs.source_path=k8(Z);let W=lZ(Q);K.episode.attrs.vehicle_type=W;let G=rZ(Y,X,{vehicleType:W});G=[...G,...J$(K.params,{configDir:J.configDir??J.projectDir,ruleset_version:X.ruleset_version})];let B=await A$({projectDir:J.projectDir,workspace:J.workspace,flight_id:$});G=[...G,...B];let L={schema_version:1,entity:K.entity,episode:K.episode,params:K.params,findings:G,events:K.events,measurements:K.measurements,ruleset_version:X.ruleset_version},V=await j$({projectDir:J.projectDir,workspace:J.workspace,sysid:K.entity.external_id,excludeSourceHash:$}),A=await q$(L,{projectDir:J.projectDir,workspace:J.workspace}),D={flight_id:$,vehicle_id:K.entity.external_id,metadata:{airframe:K.entity.attrs.name??K.entity.external_id,firmware:K.episode.attrs.firmware??null,duration_s:K.episode.attrs.duration_s??null},partial_decode:Q.partial,decoded_until:Q.decodedUntil,checks:G,summary:b8(G),ruleset_version:X.ruleset_version};if(V){let{param_changes:E,finding_changes:H}=R$(V,L);D.comparison={prior_flight_id:V.episode.source_hash,param_changes:E,finding_changes:H,narration:{claims:[]}}}return{report:D,doc:L,episodePath:A}}finally{z()}}var D$;var T$=M(()=>{QZ();oZ();X$();G$();H$();F$();D$=[163,149]});var S$={};jZ(S$,{sanitizeQuery:()=>I$,runUavQuery:()=>m8,renderQueryResult:()=>p8});import{readdir as f8,readFile as u8}from"fs/promises";import{join as _$}from"path";async function x8(Z,J,$){let X=_$(Z,".hydron","observations",J),Q;try{Q=await f8(X)}catch{throw Error(`No observation store at ${X}. Run uav_analyze first.`)}for(let Y of Q){let z=_$(X,Y,`${$}.json`);try{let W=JSON.parse(await u8(z,"utf8"))?.episode?.attrs?.source_path;if(typeof W==="string"&&W)return W;throw Error(`Episode ${$} has no source_path; re-run uav_analyze to record it.`)}catch(K){if(K.code==="ENOENT")continue;throw K}}throw Error(`flight_id ${$} not found in the observation store. Run uav_analyze first.`)}function I$(Z){let J=Z.trim();J=J.replace(/^```(?:sql)?\s*/i,"").replace(/\s*```$/,"").trim();for(;;){let Q=J;if(J=J.replace(/^--[^\n]*\n?/,"").replace(/^\/\*[\s\S]*?\*\//,"").trimStart(),J===Q)break}if(J=J.replace(/;\s*$/,""),J.includes(";"))throw Error("Only a single statement is allowed (no ';').");let $=J.replace(/^\(+\s*/,"");if(!(/^\s*(select|with|values)\b/i.test($)||/^\s*explain\b/i.test(J)||/^\s*pragma\s+(table_info|table_list|table_xinfo|index_list|index_info|index_xinfo|database_list|collation_list|function_list|pragma_list)\b/i.test(J)))throw Error("uav_query is read-only: use SELECT/WITH/VALUES (comments & ```fences ok), EXPLAIN, or a read PRAGMA (table_info/table_list/index_*). For heavier analysis use Python via bash, not SQL.");if(/\b(insert|update|delete|drop|create|alter|attach|detach|replace|reindex|vacuum)\b/i.test(J))throw Error("Write/DDL keyword detected \u2014 uav_query is read-only.");if(!/\blimit\b/i.test(J))J+=` LIMIT ${c}`;return J}async function m8(Z){let J=I$(Z.query),$=await x8(Z.projectDir,Z.workspace,Z.flight_id),{db:X,cleanup:Q}=await x($,Z.flight_id);try{X.exec?.("PRAGMA query_only = ON");let z=X.query(J).all(),K=z.length?Object.keys(z[0]):[],G=z.slice(0,c).map((B)=>K.map((L)=>B[L]));return{columns:K,rows:G,truncated:z.length>c}}finally{Q()}}function p8(Z){if(!Z.columns.length)return"(no rows)";let J=Z.columns.join(" | "),$=Z.rows.slice(0,50).map((Q)=>Q.map((Y)=>typeof Y==="bigint"?Y.toString():String(Y)).join(" | ")).join(`
7
+ `,"utf8")}async function D$(Z,$){let J=A$($.projectDir,$.workspace,Z.entity.external_id);await w8(J,{recursive:!0});let X=u(J,`${r(Z.episode.source_hash)}.json`);return await j$(X,JSON.stringify(Z,k8,2),"utf8"),await b8(J,Z),X}async function E$(Z){let $=A$(Z.projectDir,Z.workspace,Z.sysid),J;try{J=await q$($)}catch{return null}let X=`${r(Z.excludeSourceHash)}.json`,Q=J.filter((K)=>K.endsWith(".json")&&K!==X),z=null;for(let K of Q){let W=u($,K),Y;try{let O=await qZ(W,"utf8");Y=F$(JSON.parse(O))}catch{continue}let G=0;try{G=(await y8(W)).mtimeMs}catch{}let B=Y.episode?.logged_at?Date.parse(Y.episode.logged_at):NaN,U=Number.isNaN(B)?-1/0:B;if(z===null||U>z.loggedAt||U===z.loggedAt&&G>z.mtime)z={doc:Y,loggedAt:U,mtime:G}}return z?.doc??null}async function g8(Z,$,J){let X=u(Z,".hydron","observations",$),Q=`${r(J)}.json`,z;try{z=await q$(X)}catch{return null}for(let K of z){let W=u(X,K,Q),Y;try{let G=await qZ(W,"utf8");Y=F$(JSON.parse(G))}catch{continue}return{path:W,doc:Y}}return null}async function R$(Z){let $=await g8(Z.projectDir,Z.workspace,Z.flight_id);if(!$)return[];return($.doc.findings??[]).filter((J)=>J.origin==="agent")}var N$;var P$=M(()=>{N$={OK:0,HIGH:1,CRITICAL:2}});function f8(Z,$){let J=new Map;for(let Q of Z)J.set(Q.name,Q.value);let X=[];for(let Q of $){let z=J.get(Q.name);if(z===void 0)continue;if(z!==Q.value)X.push({name:Q.name,old:z,new:Q.value})}return X}function u8(Z,$){return f8(Z.params??[],$.params??[])}function x8(Z,$){let J=new Map;for(let Q of Z.findings??[])if(Q.tier==="system")J.set(Q.check,Q);let X=[];for(let Q of $.findings??[]){if(Q.tier!=="system")continue;let z=J.get(Q.check);if(!z)continue;if(z.severity!==Q.severity)X.push({check:Q.check,old:z.severity,new:Q.severity})}return X}function T$(Z,$){return{param_changes:u8(Z,$),finding_changes:x8(Z,$)}}var S$={};DZ(S$,{analyzeFlight:()=>c8});import{createHash as m8}from"crypto";import{open as I$,stat as p8}from"fs/promises";import{resolve as d8}from"path";async function l8(Z){let $=await I$(Z,"r");try{let J=Buffer.alloc(2);return await $.read(J,0,2,0),J[0]===_$[0]&&J[1]===_$[1]}finally{await $.close()}}async function n8(Z){let $=await I$(Z,"r");try{let J=m8("sha256"),X=Buffer.alloc(1048576),Q=0;for(;;){let{bytesRead:z}=await $.read(X,0,X.length,Q);if(z===0)break;J.update(X.subarray(0,z)),Q+=z}return J.digest("hex").slice(0,16)}finally{await $.close()}}function i8(Z){let $=Z.filter((Q)=>Q.verdict==="FAIL").length,J=Z.filter((Q)=>Q.severity==="CRITICAL"&&Q.verdict==="FAIL").length,X=Z.filter((Q)=>Q.verdict==="PASS").length;return{total:Z.length,passed:X,anomalies:$,critical:J}}async function c8(Z,$){if(await p8(Z),!await l8(Z))throw Error(`Not an ArduPilot DataFlash .bin (missing 0xA3 0x95 header): ${Z}`);let J=await n8(Z),X=G$($.configDir??$.projectDir),{log:Q,db:z,cleanup:K}=await m(Z,J,{configDir:$.configDir??$.projectDir});try{let W=L$(Q,z,{sourceHash:J});W.episode.attrs.source_path=d8(Z);let Y=rZ(Q);W.episode.attrs.vehicle_type=Y;let G=tZ(z,X,{vehicleType:Y});G=[...G,...K$(W.params,{configDir:$.configDir??$.projectDir,ruleset_version:X.ruleset_version})];let B=await R$({projectDir:$.projectDir,workspace:$.workspace,flight_id:J});G=[...G,...B];let U={schema_version:1,entity:W.entity,episode:W.episode,params:W.params,findings:G,events:W.events,measurements:W.measurements,ruleset_version:X.ruleset_version},O=await E$({projectDir:$.projectDir,workspace:$.workspace,sysid:W.entity.external_id,excludeSourceHash:J}),N=await D$(U,{projectDir:$.projectDir,workspace:$.workspace}),P={flight_id:J,vehicle_id:W.entity.external_id,metadata:{airframe:W.entity.attrs.name??W.entity.external_id,firmware:W.episode.attrs.firmware??null,duration_s:W.episode.attrs.duration_s??null},partial_decode:Q.partial,decoded_until:Q.decodedUntil,checks:G,summary:i8(G),ruleset_version:X.ruleset_version};if(O){let{param_changes:_,finding_changes:j}=T$(O,U);P.comparison={prior_flight_id:O.episode.source_hash,param_changes:_,finding_changes:j,narration:{claims:[]}}}return{report:P,doc:U,episodePath:N}}finally{K()}}var _$;var M$=M(()=>{WZ();eZ();W$();V$();O$();P$();_$=[163,149]});var y$={};DZ(y$,{sanitizeQuery:()=>w$,runUavQuery:()=>a8,renderQueryResult:()=>t8});import{readdir as r8,readFile as o8}from"fs/promises";import{join as C$}from"path";async function s8(Z,$,J){let X=C$(Z,".hydron","observations",$),Q;try{Q=await r8(X)}catch{throw Error(`No observation store at ${X}. Run uav_analyze first.`)}for(let z of Q){let K=C$(X,z,`${J}.json`);try{let Y=JSON.parse(await o8(K,"utf8"))?.episode?.attrs?.source_path;if(typeof Y==="string"&&Y)return Y;throw Error(`Episode ${J} has no source_path; re-run uav_analyze to record it.`)}catch(W){if(W.code==="ENOENT")continue;throw W}}throw Error(`flight_id ${J} not found in the observation store. Run uav_analyze first.`)}function w$(Z){let $=Z.trim();$=$.replace(/^```(?:sql)?\s*/i,"").replace(/\s*```$/,"").trim();for(;;){let Q=$;if($=$.replace(/^--[^\n]*\n?/,"").replace(/^\/\*[\s\S]*?\*\//,"").trimStart(),$===Q)break}if($=$.replace(/;\s*$/,""),$.includes(";"))throw Error("Only a single statement is allowed (no ';').");let J=$.replace(/^\(+\s*/,"");if(!(/^\s*(select|with|values)\b/i.test(J)||/^\s*explain\b/i.test($)||/^\s*pragma\s+(table_info|table_list|table_xinfo|index_list|index_info|index_xinfo|database_list|collation_list|function_list|pragma_list)\b/i.test($)))throw Error("uav_query is read-only: use SELECT/WITH/VALUES (comments & ```fences ok), EXPLAIN, or a read PRAGMA (table_info/table_list/index_*). For heavier analysis use Python via bash, not SQL.");if(/\b(insert|update|delete|drop|create|alter|attach|detach|replace|reindex|vacuum)\b/i.test($))throw Error("Write/DDL keyword detected \u2014 uav_query is read-only.");if(!/\blimit\b/i.test($))$+=` LIMIT ${o}`;return $}async function a8(Z){let $=w$(Z.query),J=await s8(Z.projectDir,Z.workspace,Z.flight_id),{db:X,cleanup:Q}=await m(J,Z.flight_id);try{X.exec?.("PRAGMA query_only = ON");let K=X.query($).all(),W=K.length?Object.keys(K[0]):[],G=K.slice(0,o).map((B)=>W.map((U)=>B[U]));return{columns:W,rows:G,truncated:K.length>o}}finally{Q()}}function t8(Z){if(!Z.columns.length)return"(no rows)";let $=Z.columns.join(" | "),J=Z.rows.slice(0,50).map((Q)=>Q.map((z)=>typeof z==="bigint"?z.toString():String(z)).join(" | ")).join(`
8
8
  `),X=Z.rows.length>50?`
9
9
  \u2026 ${Z.rows.length-50} more rows`:Z.truncated?`
10
- (capped at ${c} rows)`:"";return`${J}
11
- ${$}${X}`}var c=1e4;var M$=M(()=>{QZ()});self.onmessage=async(Z)=>{let J=Z.data;try{let $;if(J.op==="analyze"){let{analyzeFlight:X}=await Promise.resolve().then(() => (T$(),E$));$=await X(J.binPath,J.opts)}else if(J.op==="query"){let{runUavQuery:X}=await Promise.resolve().then(() => (M$(),S$));$=await X(J.opts)}else throw Error("unknown worker op");self.postMessage({ok:!0,result:$})}catch($){self.postMessage({ok:!1,error:$ instanceof Error?$.message:String($)})}};
10
+ (capped at ${o} rows)`:"";return`${$}
11
+ ${J}${X}`}var o=1e4;var k$=M(()=>{WZ()});self.onmessage=async(Z)=>{let $=Z.data;try{let J;if($.op==="analyze"){let{analyzeFlight:X}=await Promise.resolve().then(() => (M$(),S$));J=await X($.binPath,$.opts)}else if($.op==="query"){let{runUavQuery:X}=await Promise.resolve().then(() => (k$(),y$));J=await X($.opts)}else throw Error("unknown worker op");self.postMessage({ok:!0,result:J})}catch(J){self.postMessage({ok:!1,error:J instanceof Error?J.message:String(J)})}};