@routecraft/cli 0.5.0-canary.8 → 0.5.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/README.md +9 -7
- package/dist/chunk-3CAI2NPQ.js +2 -0
- package/dist/chunk-3CAI2NPQ.js.map +1 -0
- package/dist/{chunk-DPJWCR2V.js → chunk-5UHEZNN5.js} +14 -14
- package/dist/chunk-5UHEZNN5.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/tui/app.js +3 -3
- package/dist/tui/app.js.map +1 -1
- package/dist/tui/db.d.ts +3 -3
- package/dist/tui/db.js +1 -1
- package/package.json +9 -12
- package/dist/chunk-DPJWCR2V.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# @routecraft/cli
|
|
2
2
|
|
|
3
|
-
Run Routecraft capabilities from the terminal.
|
|
3
|
+
Run Routecraft capabilities from the terminal. **Bun-only runtime** (>= 1.1.0); see the [Runtime reference](https://routecraft.dev/docs/reference/runtime) for the rationale and the Node embedding alternative.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
or
|
|
8
|
+
# Bun (recommended)
|
|
9
|
+
bun add -g @routecraft/cli
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
# npm / pnpm / yarn (still requires Bun on the host at runtime)
|
|
12
|
+
npm install -g @routecraft/cli
|
|
14
13
|
pnpm add -g @routecraft/cli
|
|
14
|
+
yarn global add @routecraft/cli
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## Usage
|
|
@@ -22,7 +22,9 @@ craft run my-capability.ts
|
|
|
22
22
|
|
|
23
23
|
The CLI loads your capability file, starts all registered capabilities, and keeps the process running. It handles graceful shutdown on `SIGINT`/`SIGTERM` and automatically loads a `.env` file from the current directory if present.
|
|
24
24
|
|
|
25
|
-
TypeScript files are supported directly -- no build step required.
|
|
25
|
+
TypeScript files are supported directly -- Bun strips types natively, no build step required.
|
|
26
|
+
|
|
27
|
+
If Bun is missing, the CLI fails fast with a `[routecraft]` error pointing at the install instructions. Node users should embed `@routecraft/routecraft` programmatically rather than going through the CLI -- see [Programmatic Invocation](https://routecraft.dev/docs/advanced/programmatic-invocation).
|
|
26
28
|
|
|
27
29
|
## Options
|
|
28
30
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../package.json"],"names":["version"],"mappings":"AAEE,IAAAA,CAAAA,CAAW","file":"chunk-3CAI2NPQ.js","sourcesContent":["{\n \"name\": \"@routecraft/cli\",\n \"version\": \"0.5.0\",\n \"description\": \"CLI for running Routecraft routes\",\n \"private\": false,\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup src/index.ts src/tui/app.tsx src/tui/db.ts --format esm --dts --external react --external ink --external bun:sqlite\",\n \"test\": \"vitest\",\n \"test:coverage\": \"vitest --coverage\",\n \"prepublishOnly\": \"bun run build\"\n },\n \"dependencies\": {\n \"@opentelemetry/sdk-trace-base\": \"^2.7.1\",\n \"@routecraft/routecraft\": \"^0.5.0\",\n \"agent-browser\": \"^0.17.1\",\n \"cheerio\": \"^1.2.0\",\n \"commander\": \"^14.0.3\",\n \"croner\": \"^10.0.1\",\n \"dotenv\": \"^17.4.2\",\n \"imapflow\": \"^1.3.3\",\n \"ink\": \"^5.2.1\",\n \"jose\": \"^6.2.3\",\n \"mailparser\": \"^3.9.8\",\n \"nodemailer\": \"^6.10.1\",\n \"papaparse\": \"^5.5.3\",\n \"pino-pretty\": \"^13.1.3\",\n \"react\": \"^18.3.1\"\n },\n \"devDependencies\": {\n \"@types/mailparser\": \"^3.4.6\",\n \"@types/nodemailer\": \"^6.4.23\",\n \"@types/react\": \"^18.3.28\",\n \"ink-testing-library\": \"^4.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": \"https://github.com/routecraftjs/routecraft\",\n \"bin\": {\n \"craft\": \"./dist/index.js\"\n },\n \"engines\": {\n \"bun\": \">=1.1.0\"\n },\n \"keywords\": [\n \"routecraft\",\n \"cli\",\n \"routes\",\n \"automation\",\n \"typescript\"\n ],\n \"author\": \"routecraftjs\",\n \"license\": \"Apache-2.0\",\n \"homepage\": \"https://routecraft.dev\"\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function u(
|
|
1
|
+
function u(a){return a.replace(/[%_\\]/g,"\\$&")}var g=class a{db;stmtCache=new Map;constructor(e){this.db=e;}stmt(e,t){let n=this.stmtCache.get(e);return n||(n=this.db.prepare(t),this.stmtCache.set(e,n)),n}static async open(e){let t;try{t=(await import('bun:sqlite')).Database;}catch{throw new Error("bun:sqlite is not available. The craft CLI requires Bun >= 1.1.0.")}let n=new t(e,{readonly:true});try{n.exec("PRAGMA journal_mode = WAL");}catch{}{let r=new t(e);try{r.prepare("CREATE INDEX IF NOT EXISTS idx_exchanges_started_at ON exchanges(started_at)").run(),r.prepare("CREATE INDEX IF NOT EXISTS idx_exchanges_route_started ON exchanges(route_id, started_at)").run(),r.prepare("CREATE INDEX IF NOT EXISTS idx_exchanges_correlation_id ON exchanges(correlation_id)").run(),r.prepare("PRAGMA table_info(events)").all().some(o=>o.name==="exchange_id")||(r.prepare("ALTER TABLE events ADD COLUMN exchange_id TEXT").run(),r.prepare("ALTER TABLE events ADD COLUMN correlation_id TEXT").run()),r.prepare("CREATE INDEX IF NOT EXISTS idx_events_exchange_id ON events(exchange_id)").run(),r.prepare("CREATE INDEX IF NOT EXISTS idx_events_correlation_id ON events(correlation_id)").run(),r.prepare(`CREATE TABLE IF NOT EXISTS exchange_snapshots (
|
|
2
2
|
exchange_id TEXT NOT NULL,
|
|
3
3
|
context_id TEXT NOT NULL,
|
|
4
4
|
headers TEXT NOT NULL,
|
|
@@ -6,7 +6,7 @@ function u(s){return s.replace(/[%_\\]/g,"\\$&")}var g=class s{db;stmtCache=new
|
|
|
6
6
|
truncated INTEGER NOT NULL DEFAULT 0,
|
|
7
7
|
captured_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
8
8
|
PRIMARY KEY (exchange_id, context_id)
|
|
9
|
-
)`).run();}catch{}finally{r.close();}}return new
|
|
9
|
+
)`).run();}catch{}finally{r.close();}}return new a(n)}getRouteSummary(){return this.stmt("routeSummary",`WITH unique_routes AS (
|
|
10
10
|
SELECT
|
|
11
11
|
id,
|
|
12
12
|
MAX(registered_at) AS registered_at,
|
|
@@ -43,7 +43,7 @@ function u(s){return s.replace(/[%_\\]/g,"\\$&")}var g=class s{db;stmtCache=new
|
|
|
43
43
|
FROM unique_routes ur
|
|
44
44
|
LEFT JOIN exchange_counts ec ON ur.id = ec.route_id
|
|
45
45
|
LEFT JOIN recent_avg ra ON ur.id = ra.route_id
|
|
46
|
-
ORDER BY ur.registered_at DESC`).all()}getExchangesByRoute(t
|
|
46
|
+
ORDER BY ur.registered_at DESC`).all()}getExchangesByRoute(e,t=-1){return this.stmt("exchangesByRoute",`SELECT
|
|
47
47
|
id,
|
|
48
48
|
route_id AS routeId,
|
|
49
49
|
context_id AS contextId,
|
|
@@ -56,7 +56,7 @@ function u(s){return s.replace(/[%_\\]/g,"\\$&")}var g=class s{db;stmtCache=new
|
|
|
56
56
|
FROM exchanges
|
|
57
57
|
WHERE route_id = ?
|
|
58
58
|
ORDER BY started_at DESC
|
|
59
|
-
LIMIT ?`).all(t
|
|
59
|
+
LIMIT ?`).all(e,t)}getAllExchanges(e=-1){return this.stmt("allExchanges",`SELECT
|
|
60
60
|
id,
|
|
61
61
|
route_id AS routeId,
|
|
62
62
|
context_id AS contextId,
|
|
@@ -68,7 +68,7 @@ function u(s){return s.replace(/[%_\\]/g,"\\$&")}var g=class s{db;stmtCache=new
|
|
|
68
68
|
error
|
|
69
69
|
FROM exchanges
|
|
70
70
|
ORDER BY started_at DESC
|
|
71
|
-
LIMIT ?`).all(
|
|
71
|
+
LIMIT ?`).all(e)}getFailedExchanges(e=-1){return this.stmt("failedExchanges",`SELECT
|
|
72
72
|
id,
|
|
73
73
|
route_id AS routeId,
|
|
74
74
|
context_id AS contextId,
|
|
@@ -81,7 +81,7 @@ function u(s){return s.replace(/[%_\\]/g,"\\$&")}var g=class s{db;stmtCache=new
|
|
|
81
81
|
FROM exchanges
|
|
82
82
|
WHERE status = 'failed'
|
|
83
83
|
ORDER BY started_at DESC
|
|
84
|
-
LIMIT ?`).all(
|
|
84
|
+
LIMIT ?`).all(e)}getEventsByExchange(e,t){let n=t||e;return this.hasEventIdColumns()?this.stmt("eventsByExchangeIndexed",`SELECT
|
|
85
85
|
id,
|
|
86
86
|
timestamp,
|
|
87
87
|
context_id AS contextId,
|
|
@@ -97,7 +97,7 @@ function u(s){return s.replace(/[%_\\]/g,"\\$&")}var g=class s{db;stmtCache=new
|
|
|
97
97
|
details
|
|
98
98
|
FROM events
|
|
99
99
|
WHERE details LIKE '%' || ? || '%' ESCAPE '\\'
|
|
100
|
-
ORDER BY id ASC`).all(u(n))}hasEventIdColumns(){if(this._hasEventIdColumns!==void 0)return this._hasEventIdColumns;try{let
|
|
100
|
+
ORDER BY id ASC`).all(u(n))}hasEventIdColumns(){if(this._hasEventIdColumns!==void 0)return this._hasEventIdColumns;try{let e=this.db.prepare("PRAGMA table_info(events)").all();this._hasEventIdColumns=e.some(t=>t.name==="exchange_id");}catch{this._hasEventIdColumns=false;}return this._hasEventIdColumns}_hasEventIdColumns;getRecentEvents(e){let t=e?.limit??100,n=e?.sinceId??0,r=`
|
|
101
101
|
SELECT
|
|
102
102
|
id,
|
|
103
103
|
timestamp,
|
|
@@ -106,7 +106,7 @@ function u(s){return s.replace(/[%_\\]/g,"\\$&")}var g=class s{db;stmtCache=new
|
|
|
106
106
|
details
|
|
107
107
|
FROM events
|
|
108
108
|
WHERE id > ?
|
|
109
|
-
`,
|
|
109
|
+
`,s=[n];if(e?.eventNameFilter&&(r+=" AND event_name LIKE ? ESCAPE '\\'",s.push(`%${u(e.eventNameFilter)}%`)),e?.routeIdFilter){r+=" AND (event_name LIKE ? ESCAPE '\\' OR details LIKE ? ESCAPE '\\')";let i=u(e.routeIdFilter);s.push(`%${i}%`,`%${i}%`);}return r+=" ORDER BY id DESC LIMIT ?",s.push(t),this.db.prepare(r).all(...s)}getMetrics(){let t=this.stmt("metrics",`SELECT
|
|
110
110
|
(SELECT COUNT(DISTINCT id) FROM routes) AS totalRoutes,
|
|
111
111
|
(SELECT COUNT(*) FROM exchanges) AS totalExchanges,
|
|
112
112
|
(SELECT COUNT(*) FROM exchanges WHERE status = 'completed') AS completedExchanges,
|
|
@@ -117,24 +117,24 @@ function u(s){return s.replace(/[%_\\]/g,"\\$&")}var g=class s{db;stmtCache=new
|
|
|
117
117
|
FROM exchanges
|
|
118
118
|
WHERE duration_ms IS NOT NULL
|
|
119
119
|
AND started_at >= datetime('now', '-5 minutes')
|
|
120
|
-
ORDER BY duration_ms ASC`).all().map(
|
|
120
|
+
ORDER BY duration_ms ASC`).all().map(s=>s.duration_ms);return {...t,errorRate:t.totalExchanges>0?t.failedExchanges/t.totalExchanges:0,p90DurationMs:l(r,.9),p95DurationMs:l(r,.95),p99DurationMs:l(r,.99)}}getLiveTrafficBuckets(e=60,t=5){let n=Math.floor(Date.now()/1e3),r=Math.floor(n/t)*t,s=r-(e-1)*t,i=this.stmt("liveTraffic",`SELECT
|
|
121
121
|
CAST((CAST(strftime('%s', started_at) AS INTEGER) - ?) / ? AS INTEGER) AS bucket,
|
|
122
122
|
COUNT(*) AS cnt
|
|
123
123
|
FROM exchanges
|
|
124
124
|
WHERE CAST(strftime('%s', started_at) AS INTEGER) >= ?
|
|
125
125
|
AND CAST(strftime('%s', started_at) AS INTEGER) <= ?
|
|
126
|
-
GROUP BY bucket`).all(
|
|
126
|
+
GROUP BY bucket`).all(s,t,s,r+t),c=new Array(e).fill(0);for(let E of i)E.bucket>=0&&E.bucket<e&&(c[E.bucket]+=E.cnt);return c}getSingleRouteActivity(e,t=12,n=5){let r=Math.floor(Date.now()/1e3),o=Math.floor(r/n)*n-(t-1)*n,c=this.stmt("singleRouteActivity",`SELECT
|
|
127
127
|
CAST((CAST(strftime('%s', started_at) AS INTEGER) - ?) / ? AS INTEGER) AS bucket,
|
|
128
128
|
COUNT(*) AS cnt
|
|
129
129
|
FROM exchanges
|
|
130
130
|
WHERE route_id = ?
|
|
131
131
|
AND CAST(strftime('%s', started_at) AS INTEGER) >= ?
|
|
132
|
-
GROUP BY bucket`).all(o,n,
|
|
132
|
+
GROUP BY bucket`).all(o,n,e,o),S=this.stmt("singleRouteErrors",`SELECT COUNT(*) AS cnt
|
|
133
133
|
FROM exchanges
|
|
134
134
|
WHERE route_id = ?
|
|
135
135
|
AND status = 'failed'
|
|
136
|
-
AND CAST(strftime('%s', started_at) AS INTEGER) >= ?`).get(
|
|
136
|
+
AND CAST(strftime('%s', started_at) AS INTEGER) >= ?`).get(e,o),m=new Array(t).fill(0);for(let d of c)d.bucket>=0&&d.bucket<t&&(m[d.bucket]=d.cnt);return {throughput:m,recentErrors:S?.cnt??0}}getMaxEventId(){return this.db.prepare("SELECT COALESCE(MAX(id), 0) AS maxId FROM events").get().maxId}getExchangeSnapshot(e){try{let n=this.stmt("exchangeSnapshot",`SELECT headers, body, truncated
|
|
137
137
|
FROM exchange_snapshots
|
|
138
138
|
WHERE exchange_id = ?
|
|
139
|
-
LIMIT 1`).get(
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
139
|
+
LIMIT 1`).get(e);return n?{headers:n.headers,body:n.body,truncated:n.truncated!==0}:null}catch{return null}}close(){this.db.close();}};function l(a,e){if(a.length===0)return null;let t=e*(a.length-1),n=Math.floor(t),r=Math.ceil(t);return n===r?a[n]:a[n]+(a[r]-a[n])*(t-n)}export{g as a};//# sourceMappingURL=chunk-5UHEZNN5.js.map
|
|
140
|
+
//# sourceMappingURL=chunk-5UHEZNN5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/db.ts"],"names":["escapeLike","s","TelemetryDb","_TelemetryDb","db","key","sql","dbPath","Database","wdb","c","routeId","limit","exchangeId","correlationId","searchId","info","col","options","sinceId","params","escaped","row","durations","r","percentile","bucketCount","bucketWidthSec","nowSec","snapped","windowStart","rows","result","tRows","eRow","throughput","sorted","p","idx","lo","hi"],"mappings":"AA6CA,SAASA,EAAWC,CAAAA,CAAmB,CACrC,OAAOA,CAAAA,CAAE,QAAQ,SAAA,CAAW,MAAM,CACpC,KAEaC,CAAAA,CAAN,MAAMC,CAAY,CACf,GACA,SAAA,CAAY,IAAI,GAAA,CAEhB,WAAA,CAAYC,EAAc,CAChC,IAAA,CAAK,EAAA,CAAKA,EACZ,CAGQ,IAAA,CAAKC,CAAAA,CAAaC,CAAAA,CAAwB,CAChD,IAAIL,CAAAA,CAAI,IAAA,CAAK,SAAA,CAAU,GAAA,CAAII,CAAG,CAAA,CAC9B,OAAKJ,CAAAA,GACHA,CAAAA,CAAI,KAAK,EAAA,CAAG,OAAA,CAAQK,CAAG,CAAA,CACvB,KAAK,SAAA,CAAU,GAAA,CAAID,CAAAA,CAAKJ,CAAC,GAEpBA,CACT,CASA,aAAa,IAAA,CAAKM,EAAsC,CACtD,IAAIC,CAAAA,CACJ,GAAI,CAEFA,CAAAA,CAAAA,CADY,MAAM,OAAO,YAAY,GACtB,SACjB,CAAA,KAAQ,CACN,MAAM,IAAI,KAAA,CACR,mEACF,CACF,CAEA,IAAMJ,EAAK,IAAII,CAAAA,CAASD,CAAAA,CAAQ,CAAE,SAAU,IAAK,CAAC,CAAA,CAClD,GAAI,CACFH,CAAAA,CAAG,IAAA,CAAK,2BAA2B,EACrC,MAAQ,CAER,CAKA,CACE,IAAMK,EAAM,IAAID,CAAAA,CAASD,CAAM,CAAA,CAC/B,GAAI,CACFE,CAAAA,CACG,OAAA,CACC,8EACF,EACC,GAAA,EAAI,CACPA,CAAAA,CACG,OAAA,CACC,2FACF,CAAA,CACC,GAAA,EAAI,CACPA,CAAAA,CACG,QACC,sFACF,CAAA,CACC,GAAA,EAAI,CAEMA,EAAI,OAAA,CAAQ,2BAA2B,CAAA,CAAE,GAAA,GAG5C,IAAA,CAAMC,CAAAA,EAAMA,CAAAA,CAAE,IAAA,GAAS,aAAa,CAAA,GAC5CD,CAAAA,CAAI,OAAA,CAAQ,gDAAgD,EAAE,GAAA,EAAI,CAClEA,CAAAA,CACG,OAAA,CAAQ,mDAAmD,CAAA,CAC3D,GAAA,EAAI,CAAA,CAETA,CAAAA,CACG,QACC,0EACF,CAAA,CACC,GAAA,EAAI,CACPA,EACG,OAAA,CACC,gFACF,EACC,GAAA,EAAI,CAEPA,EACG,OAAA,CACC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAAA,CASF,CAAA,CACC,MACL,CAAA,KAAQ,CAER,CAAA,OAAE,CACAA,CAAAA,CAAI,KAAA,GACN,CACF,CAEA,OAAO,IAAIN,CAAAA,CAAYC,CAAE,CAC3B,CAOA,iBAQG,CA0CD,OAzCU,IAAA,CAAK,IAAA,CACb,cAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAAA,CAsCF,CAAA,CACS,GAAA,EASX,CAKA,mBAAA,CAAoBO,CAAAA,CAAiBC,CAAAA,CAAQ,EAAA,CAAyB,CAkBpE,OAjBU,IAAA,CAAK,IAAA,CACb,kBAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAAA,CAcF,CAAA,CACS,GAAA,CAAID,CAAAA,CAASC,CAAK,CAC7B,CAKA,eAAA,CAAgBA,CAAAA,CAAQ,EAAA,CAAyB,CAiB/C,OAhBU,IAAA,CAAK,KACb,cAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAAA,CAaF,CAAA,CACS,GAAA,CAAIA,CAAK,CACpB,CAOA,kBAAA,CAAmBA,CAAAA,CAAQ,EAAA,CAAyB,CAkBlD,OAjBU,IAAA,CAAK,IAAA,CACb,iBAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAAA,CAcF,EACS,GAAA,CAAIA,CAAK,CACpB,CAQA,mBAAA,CACEC,EACAC,CAAAA,CACkB,CAClB,IAAMC,CAAAA,CAAWD,CAAAA,EAAiBD,EAIlC,OADmB,IAAA,CAAK,mBAAkB,CAE9B,IAAA,CAAK,KACb,yBAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAA,CASF,EACS,GAAA,CAAIE,CAAAA,CAAUA,CAAQ,CAAA,CAIvB,IAAA,CAAK,KACb,0BAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAA,CASF,CAAA,CACS,GAAA,CAAIf,CAAAA,CAAWe,CAAQ,CAAC,CACnC,CAGQ,iBAAA,EAA6B,CACnC,GAAI,IAAA,CAAK,kBAAA,GAAuB,MAAA,CAAW,OAAO,IAAA,CAAK,kBAAA,CACvD,GAAI,CACF,IAAMC,CAAAA,CAAO,IAAA,CAAK,EAAA,CAAG,OAAA,CAAQ,2BAA2B,CAAA,CAAE,GAAA,EAAI,CAG9D,IAAA,CAAK,kBAAA,CAAqBA,CAAAA,CAAK,IAAA,CAAMC,CAAAA,EAAQA,CAAAA,CAAI,IAAA,GAAS,aAAa,EACzE,CAAA,KAAQ,CACN,IAAA,CAAK,kBAAA,CAAqB,MAC5B,CACA,OAAO,IAAA,CAAK,kBACd,CACQ,kBAAA,CAKR,eAAA,CAAgBC,CAAAA,CAKK,CACnB,IAAMN,CAAAA,CAAQM,CAAAA,EAAS,KAAA,EAAS,GAAA,CAC1BC,CAAAA,CAAUD,CAAAA,EAAS,OAAA,EAAW,CAAA,CAEhCZ,CAAAA,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA,CAUJc,CAAAA,CAAoB,CAACD,CAAO,CAAA,CAOlC,GALID,CAAAA,EAAS,eAAA,GACXZ,CAAAA,EAAO,oCAAA,CACPc,CAAAA,CAAO,IAAA,CAAK,CAAA,CAAA,EAAIpB,CAAAA,CAAWkB,CAAAA,CAAQ,eAAe,CAAC,CAAA,CAAA,CAAG,CAAA,CAAA,CAGpDA,CAAAA,EAAS,aAAA,CAAe,CAC1BZ,CAAAA,EACE,oEAAA,CACF,IAAMe,CAAAA,CAAUrB,CAAAA,CAAWkB,CAAAA,CAAQ,aAAa,EAChDE,CAAAA,CAAO,IAAA,CAAK,CAAA,CAAA,EAAIC,CAAO,CAAA,CAAA,CAAA,CAAK,CAAA,CAAA,EAAIA,CAAO,CAAA,CAAA,CAAG,EAC5C,CAEA,OAAAf,CAAAA,EAAO,2BAAA,CACPc,CAAAA,CAAO,IAAA,CAAKR,CAAK,CAAA,CAEJ,IAAA,CAAK,EAAA,CAAG,OAAA,CAAQN,CAAG,CAAA,CACpB,GAAA,CAAI,GAAGc,CAAM,CAC3B,CAMA,UAAA,EAA2C,CAYzC,IAAME,CAAAA,CAXI,IAAA,CAAK,KACb,SAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4EAAA,CAQF,EACc,GAAA,EAAI,CAkBZC,CAAAA,CARU,IAAA,CAAK,KACnB,aAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA,+BAAA,CAKF,CAAA,CAC2B,GAAA,EAAI,CAAqC,GAAA,CACjEC,GAAMA,CAAAA,CAAE,WACX,CAAA,CAEA,OAAO,CACL,GAAGF,CAAAA,CACH,SAAA,CACEA,EAAI,cAAA,CAAiB,CAAA,CAAIA,CAAAA,CAAI,eAAA,CAAkBA,CAAAA,CAAI,cAAA,CAAiB,CAAA,CACtE,aAAA,CAAeG,EAAWF,CAAAA,CAAW,EAAG,CAAA,CACxC,aAAA,CAAeE,CAAAA,CAAWF,CAAAA,CAAW,GAAI,CAAA,CACzC,cAAeE,CAAAA,CAAWF,CAAAA,CAAW,GAAI,CAC3C,CACF,CAOA,qBAAA,CAAsBG,CAAAA,CAAc,GAAIC,CAAAA,CAAiB,CAAA,CAAa,CACpE,IAAMC,CAAAA,CAAS,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,KAAI,CAAI,GAAI,CAAA,CACrCC,CAAAA,CAAU,IAAA,CAAK,KAAA,CAAMD,CAAAA,CAASD,CAAc,EAAIA,CAAAA,CAChDG,CAAAA,CAAcD,CAAAA,CAAAA,CAAWH,CAAAA,CAAc,CAAA,EAAKC,CAAAA,CAY5CI,CAAAA,CAVI,IAAA,CAAK,KACb,aAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAA,CAOF,CAAA,CACe,GAAA,CACbD,CAAAA,CACAH,CAAAA,CACAG,CAAAA,CACAD,CAAAA,CAAUF,CACZ,CAAA,CAEMK,CAAAA,CAAS,IAAI,KAAA,CAAMN,CAAW,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,CAC5C,IAAA,IAAWJ,CAAAA,IAAOS,CAAAA,CACZT,CAAAA,CAAI,MAAA,EAAU,CAAA,EAAKA,CAAAA,CAAI,MAAA,CAASI,CAAAA,GAClCM,CAAAA,CAAOV,CAAAA,CAAI,MAAM,GAAKA,CAAAA,CAAI,GAAA,CAAA,CAG9B,OAAOU,CACT,CAKA,sBAAA,CACErB,CAAAA,CACAe,CAAAA,CAAc,EAAA,CACdC,CAAAA,CAAiB,CAAA,CAC+B,CAChD,IAAMC,CAAAA,CAAS,IAAA,CAAK,KAAA,CAAM,KAAK,GAAA,EAAI,CAAI,GAAI,CAAA,CAErCE,CAAAA,CADU,IAAA,CAAK,KAAA,CAAMF,CAAAA,CAASD,CAAc,CAAA,CAAIA,CAAAA,CAAAA,CACvBD,CAAAA,CAAc,CAAA,EAAKC,CAAAA,CAY5CM,CAAAA,CAVQ,IAAA,CAAK,KACjB,qBAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAA,CAOF,CAAA,CACoB,GAAA,CAClBH,CAAAA,CACAH,CAAAA,CACAhB,CAAAA,CACAmB,CACF,CAAA,CAUMI,CAAAA,CARQ,IAAA,CAAK,IAAA,CACjB,mBAAA,CACA,CAAA;AAAA;AAAA;AAAA;AAAA,4DAAA,CAKF,CAAA,CACmB,GAAA,CAAIvB,CAAAA,CAASmB,CAAW,CAAA,CAErCK,CAAAA,CAAa,IAAI,KAAA,CAAMT,CAAW,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,CAChD,IAAA,IAAWJ,CAAAA,IAAOW,CAAAA,CACZX,CAAAA,CAAI,MAAA,EAAU,CAAA,EAAKA,CAAAA,CAAI,MAAA,CAASI,CAAAA,GAClCS,CAAAA,CAAWb,CAAAA,CAAI,MAAM,CAAA,CAAIA,CAAAA,CAAI,GAAA,CAAA,CAIjC,OAAO,CAAE,UAAA,CAAAa,CAAAA,CAAY,YAAA,CAAcD,CAAAA,EAAM,GAAA,EAAO,CAAE,CACpD,CAKA,aAAA,EAAwB,CAKtB,OAJa,IAAA,CAAK,EAAA,CAAG,OAAA,CACnB,kDACF,CAAA,CACiB,GAAA,EAAI,CACV,KACb,CAMA,mBAAA,CACErB,CAAAA,CAC8C,CAC9C,GAAI,CAQF,IAAMS,CAAAA,CAPI,IAAA,CAAK,IAAA,CACb,kBAAA,CACA,CAAA;AAAA;AAAA;AAAA,gBAAA,CAIF,EACc,GAAA,CAAIT,CAAU,CAAA,CAG5B,OAAKS,EACE,CACL,OAAA,CAASA,CAAAA,CAAI,OAAA,CACb,KAAMA,CAAAA,CAAI,IAAA,CACV,SAAA,CAAWA,CAAAA,CAAI,YAAc,CAC/B,CAAA,CALiB,IAMnB,CAAA,KAAQ,CAEN,OAAO,IACT,CACF,CAKA,OAAc,CACZ,IAAA,CAAK,EAAA,CAAG,KAAA,GACV,CACF,EAMA,SAASG,CAAAA,CAAWW,EAAkBC,CAAAA,CAA0B,CAC9D,GAAID,CAAAA,CAAO,SAAW,CAAA,CAAG,OAAO,IAAA,CAChC,IAAME,EAAMD,CAAAA,EAAKD,CAAAA,CAAO,MAAA,CAAS,CAAA,CAAA,CAC3BG,EAAK,IAAA,CAAK,KAAA,CAAMD,CAAG,CAAA,CACnBE,EAAK,IAAA,CAAK,IAAA,CAAKF,CAAG,CAAA,CACxB,OAAIC,CAAAA,GAAOC,CAAAA,CAAWJ,CAAAA,CAAOG,CAAE,EACxBH,CAAAA,CAAOG,CAAE,CAAA,CAAA,CAAMH,CAAAA,CAAOI,CAAE,CAAA,CAAKJ,CAAAA,CAAOG,CAAE,CAAA,GAAOD,EAAMC,CAAAA,CAC5D","file":"chunk-5UHEZNN5.js","sourcesContent":["/// <reference types=\"bun-types\" />\nimport type { TelemetryEvent } from \"@routecraft/routecraft\";\n\n/** Exchange row shape from the telemetry SQLite database. */\ninterface TelemetryExchange {\n id: string;\n routeId: string;\n contextId: string;\n correlationId: string;\n status: string;\n startedAt: string;\n completedAt: string | null;\n durationMs: number | null;\n error: string | null;\n}\n\n/**\n * Minimal type for the bun:sqlite database to avoid pulling bun-types\n * into the CLI's public type surface.\n */\ninterface Database {\n prepare(sql: string): Statement;\n query(sql: string): Statement;\n exec(sql: string): void;\n close(): void;\n}\n\ninterface Statement {\n all(...params: unknown[]): unknown[];\n get(...params: unknown[]): unknown;\n run(...params: unknown[]): unknown;\n}\n\ntype DatabaseConstructor = new (\n filename: string,\n options?: { readonly?: boolean; create?: boolean },\n) => Database;\n\n/**\n * Read-only accessor for the telemetry SQLite database.\n * Used by the TUI to query historical data without affecting the running engine.\n *\n * Use the static `open()` factory instead of the constructor directly.\n */\n/** Escape LIKE metacharacters so they match literally. */\nfunction escapeLike(s: string): string {\n return s.replace(/[%_\\\\]/g, \"\\\\$&\");\n}\n\nexport class TelemetryDb {\n private db: Database;\n private stmtCache = new Map<string, Statement>();\n\n private constructor(db: Database) {\n this.db = db;\n }\n\n /** Prepare and cache a statement by key. Avoids re-parsing on every call. */\n private stmt(key: string, sql: string): Statement {\n let s = this.stmtCache.get(key);\n if (!s) {\n s = this.db.prepare(sql);\n this.stmtCache.set(key, s);\n }\n return s;\n }\n\n /**\n * Open a telemetry database in read-only mode.\n *\n * Uses dynamic `import(\"bun:sqlite\")` so the module resolves at runtime\n * (the CLI is Bun-only; `bun:sqlite` is a Bun built-in and is not a\n * resolvable spec under Node).\n */\n static async open(dbPath: string): Promise<TelemetryDb> {\n let Database: DatabaseConstructor;\n try {\n const mod = await import(\"bun:sqlite\");\n Database = mod.Database as unknown as DatabaseConstructor;\n } catch {\n throw new Error(\n \"bun:sqlite is not available. The craft CLI requires Bun >= 1.1.0.\",\n );\n }\n\n const db = new Database(dbPath, { readonly: true });\n try {\n db.exec(\"PRAGMA journal_mode = WAL\");\n } catch {\n // Read-only connection cannot change journal mode; safe to ignore\n }\n\n // Ensure indexes exist for TUI query performance.\n // If the DB was created before indexes were added, create them now\n // using a separate writable connection.\n {\n const wdb = new Database(dbPath);\n try {\n wdb\n .prepare(\n \"CREATE INDEX IF NOT EXISTS idx_exchanges_started_at ON exchanges(started_at)\",\n )\n .run();\n wdb\n .prepare(\n \"CREATE INDEX IF NOT EXISTS idx_exchanges_route_started ON exchanges(route_id, started_at)\",\n )\n .run();\n wdb\n .prepare(\n \"CREATE INDEX IF NOT EXISTS idx_exchanges_correlation_id ON exchanges(correlation_id)\",\n )\n .run();\n // Add exchange_id/correlation_id columns to events table if missing\n const cols = wdb.prepare(\"PRAGMA table_info(events)\").all() as Array<{\n name: string;\n }>;\n if (!cols.some((c) => c.name === \"exchange_id\")) {\n wdb.prepare(\"ALTER TABLE events ADD COLUMN exchange_id TEXT\").run();\n wdb\n .prepare(\"ALTER TABLE events ADD COLUMN correlation_id TEXT\")\n .run();\n }\n wdb\n .prepare(\n \"CREATE INDEX IF NOT EXISTS idx_events_exchange_id ON events(exchange_id)\",\n )\n .run();\n wdb\n .prepare(\n \"CREATE INDEX IF NOT EXISTS idx_events_correlation_id ON events(correlation_id)\",\n )\n .run();\n // Ensure exchange_snapshots table exists for older databases\n wdb\n .prepare(\n `CREATE TABLE IF NOT EXISTS exchange_snapshots (\n exchange_id TEXT NOT NULL,\n context_id TEXT NOT NULL,\n headers TEXT NOT NULL,\n body TEXT,\n truncated INTEGER NOT NULL DEFAULT 0,\n captured_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (exchange_id, context_id)\n )`,\n )\n .run();\n } catch {\n // Best-effort; the writer will create them on next restart\n } finally {\n wdb.close();\n }\n }\n\n return new TelemetryDb(db);\n }\n\n /**\n * Get a summary of all routes with aggregated metrics.\n * Routes are grouped by ID across all context runs so that restarting\n * the same application does not create duplicate rows.\n */\n getRouteSummary(): Array<{\n id: string;\n status: string;\n totalExchanges: number;\n completedExchanges: number;\n failedExchanges: number;\n droppedExchanges: number;\n avgDurationMs: number | null;\n }> {\n const s = this.stmt(\n \"routeSummary\",\n `WITH unique_routes AS (\n SELECT\n id,\n MAX(registered_at) AS registered_at,\n (SELECT r2.status FROM routes r2\n WHERE r2.id = r.id\n ORDER BY r2.registered_at DESC LIMIT 1) AS status\n FROM routes r\n GROUP BY id\n ),\n exchange_counts AS (\n SELECT\n route_id,\n COUNT(*) AS total,\n SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS completed,\n SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) AS failed,\n SUM(CASE WHEN status = 'dropped' THEN 1 ELSE 0 END) AS dropped\n FROM exchanges\n GROUP BY route_id\n ),\n recent_avg AS (\n SELECT route_id, AVG(duration_ms) AS avgDur\n FROM exchanges\n WHERE started_at >= datetime('now', '-5 minutes')\n GROUP BY route_id\n )\n SELECT\n ur.id,\n ur.status,\n COALESCE(ec.total, 0) AS totalExchanges,\n COALESCE(ec.completed, 0) AS completedExchanges,\n COALESCE(ec.failed, 0) AS failedExchanges,\n COALESCE(ec.dropped, 0) AS droppedExchanges,\n ra.avgDur AS avgDurationMs\n FROM unique_routes ur\n LEFT JOIN exchange_counts ec ON ur.id = ec.route_id\n LEFT JOIN recent_avg ra ON ur.id = ra.route_id\n ORDER BY ur.registered_at DESC`,\n );\n return s.all() as Array<{\n id: string;\n status: string;\n totalExchanges: number;\n completedExchanges: number;\n failedExchanges: number;\n droppedExchanges: number;\n avgDurationMs: number | null;\n }>;\n }\n\n /**\n * Get exchanges for a specific route, ordered by most recent first.\n */\n getExchangesByRoute(routeId: string, limit = -1): TelemetryExchange[] {\n const s = this.stmt(\n \"exchangesByRoute\",\n `SELECT\n id,\n route_id AS routeId,\n context_id AS contextId,\n correlation_id AS correlationId,\n status,\n started_at AS startedAt,\n completed_at AS completedAt,\n duration_ms AS durationMs,\n error\n FROM exchanges\n WHERE route_id = ?\n ORDER BY started_at DESC\n LIMIT ?`,\n );\n return s.all(routeId, limit) as TelemetryExchange[];\n }\n\n /**\n * Get all exchanges across all routes, ordered by most recent first.\n */\n getAllExchanges(limit = -1): TelemetryExchange[] {\n const s = this.stmt(\n \"allExchanges\",\n `SELECT\n id,\n route_id AS routeId,\n context_id AS contextId,\n correlation_id AS correlationId,\n status,\n started_at AS startedAt,\n completed_at AS completedAt,\n duration_ms AS durationMs,\n error\n FROM exchanges\n ORDER BY started_at DESC\n LIMIT ?`,\n );\n return s.all(limit) as TelemetryExchange[];\n }\n\n /**\n * Get all failed exchanges across all routes, ordered by most recent first.\n * Shows every failed exchange including child exchanges from split/multicast,\n * without deduplication by correlation chain.\n */\n getFailedExchanges(limit = -1): TelemetryExchange[] {\n const s = this.stmt(\n \"failedExchanges\",\n `SELECT\n id,\n route_id AS routeId,\n context_id AS contextId,\n correlation_id AS correlationId,\n status,\n started_at AS startedAt,\n completed_at AS completedAt,\n duration_ms AS durationMs,\n error\n FROM exchanges\n WHERE status = 'failed'\n ORDER BY started_at DESC\n LIMIT ?`,\n );\n return s.all(limit) as TelemetryExchange[];\n }\n\n /**\n * Get events for an exchange and all related exchanges sharing the\n * same correlation ID. Uses indexed exchange_id/correlation_id columns\n * with a fallback to details LIKE for databases created before the\n * column migration.\n */\n getEventsByExchange(\n exchangeId: string,\n correlationId: string,\n ): TelemetryEvent[] {\n const searchId = correlationId || exchangeId;\n\n // Try indexed column lookup first (fast path)\n const hasColumns = this.hasEventIdColumns();\n if (hasColumns) {\n const s = this.stmt(\n \"eventsByExchangeIndexed\",\n `SELECT\n id,\n timestamp,\n context_id AS contextId,\n event_name AS eventName,\n details\n FROM events\n WHERE exchange_id = ? OR correlation_id = ?\n ORDER BY id ASC`,\n );\n return s.all(searchId, searchId) as TelemetryEvent[];\n }\n\n // Fallback: full-text LIKE scan for older databases\n const s = this.stmt(\n \"eventsByExchangeFallback\",\n `SELECT\n id,\n timestamp,\n context_id AS contextId,\n event_name AS eventName,\n details\n FROM events\n WHERE details LIKE '%' || ? || '%' ESCAPE '\\\\'\n ORDER BY id ASC`,\n );\n return s.all(escapeLike(searchId)) as TelemetryEvent[];\n }\n\n /** Check whether the events table has the exchange_id column. */\n private hasEventIdColumns(): boolean {\n if (this._hasEventIdColumns !== undefined) return this._hasEventIdColumns;\n try {\n const info = this.db.prepare(\"PRAGMA table_info(events)\").all() as Array<{\n name: string;\n }>;\n this._hasEventIdColumns = info.some((col) => col.name === \"exchange_id\");\n } catch {\n this._hasEventIdColumns = false;\n }\n return this._hasEventIdColumns;\n }\n private _hasEventIdColumns: boolean | undefined;\n\n /**\n * Get recent events, optionally filtered by event name pattern or route ID.\n */\n getRecentEvents(options?: {\n limit?: number;\n eventNameFilter?: string;\n routeIdFilter?: string;\n sinceId?: number;\n }): TelemetryEvent[] {\n const limit = options?.limit ?? 100;\n const sinceId = options?.sinceId ?? 0;\n\n let sql = `\n SELECT\n id,\n timestamp,\n context_id AS contextId,\n event_name AS eventName,\n details\n FROM events\n WHERE id > ?\n `;\n const params: unknown[] = [sinceId];\n\n if (options?.eventNameFilter) {\n sql += \" AND event_name LIKE ? ESCAPE '\\\\'\";\n params.push(`%${escapeLike(options.eventNameFilter)}%`);\n }\n\n if (options?.routeIdFilter) {\n sql +=\n \" AND (event_name LIKE ? ESCAPE '\\\\' OR details LIKE ? ESCAPE '\\\\')\";\n const escaped = escapeLike(options.routeIdFilter);\n params.push(`%${escaped}%`, `%${escaped}%`);\n }\n\n sql += \" ORDER BY id DESC LIMIT ?\";\n params.push(limit);\n\n const stmt = this.db.prepare(sql);\n return stmt.all(...params) as TelemetryEvent[];\n }\n\n /**\n * Get aggregated metrics for the dashboard, including duration percentiles.\n * Percentiles are computed over exchanges from the last 5 minutes.\n */\n getMetrics(): import(\"./types.js\").Metrics {\n const s = this.stmt(\n \"metrics\",\n `SELECT\n (SELECT COUNT(DISTINCT id) FROM routes) AS totalRoutes,\n (SELECT COUNT(*) FROM exchanges) AS totalExchanges,\n (SELECT COUNT(*) FROM exchanges WHERE status = 'completed') AS completedExchanges,\n (SELECT COUNT(*) FROM exchanges WHERE status = 'failed') AS failedExchanges,\n (SELECT COUNT(*) FROM exchanges WHERE status = 'dropped') AS droppedExchanges,\n (SELECT AVG(duration_ms) FROM exchanges\n WHERE started_at >= datetime('now', '-5 minutes')) AS avgDurationMs`,\n );\n const row = s.get() as {\n totalRoutes: number;\n totalExchanges: number;\n completedExchanges: number;\n failedExchanges: number;\n droppedExchanges: number;\n avgDurationMs: number | null;\n };\n\n // Compute percentiles from recent exchanges with non-null durations\n const pctStmt = this.stmt(\n \"percentiles\",\n `SELECT duration_ms\n FROM exchanges\n WHERE duration_ms IS NOT NULL\n AND started_at >= datetime('now', '-5 minutes')\n ORDER BY duration_ms ASC`,\n );\n const durations = (pctStmt.all() as Array<{ duration_ms: number }>).map(\n (r) => r.duration_ms,\n );\n\n return {\n ...row,\n errorRate:\n row.totalExchanges > 0 ? row.failedExchanges / row.totalExchanges : 0,\n p90DurationMs: percentile(durations, 0.9),\n p95DurationMs: percentile(durations, 0.95),\n p99DurationMs: percentile(durations, 0.99),\n };\n }\n\n /**\n * Get exchange counts for a rolling window using fine-grained buckets.\n * Default: 60 buckets of 5 seconds = 5-minute rolling window.\n * Returns values oldest-first for sparkline rendering.\n */\n getLiveTrafficBuckets(bucketCount = 60, bucketWidthSec = 5): number[] {\n const nowSec = Math.floor(Date.now() / 1000);\n const snapped = Math.floor(nowSec / bucketWidthSec) * bucketWidthSec;\n const windowStart = snapped - (bucketCount - 1) * bucketWidthSec;\n\n const s = this.stmt(\n \"liveTraffic\",\n `SELECT\n CAST((CAST(strftime('%s', started_at) AS INTEGER) - ?) / ? AS INTEGER) AS bucket,\n COUNT(*) AS cnt\n FROM exchanges\n WHERE CAST(strftime('%s', started_at) AS INTEGER) >= ?\n AND CAST(strftime('%s', started_at) AS INTEGER) <= ?\n GROUP BY bucket`,\n );\n const rows = s.all(\n windowStart,\n bucketWidthSec,\n windowStart,\n snapped + bucketWidthSec,\n ) as Array<{ bucket: number; cnt: number }>;\n\n const result = new Array(bucketCount).fill(0) as number[];\n for (const row of rows) {\n if (row.bucket >= 0 && row.bucket < bucketCount) {\n result[row.bucket] += row.cnt;\n }\n }\n return result;\n }\n\n /**\n * Get activity data for a single route: throughput sparkline + recent error count.\n */\n getSingleRouteActivity(\n routeId: string,\n bucketCount = 12,\n bucketWidthSec = 5,\n ): { throughput: number[]; recentErrors: number } {\n const nowSec = Math.floor(Date.now() / 1000);\n const snapped = Math.floor(nowSec / bucketWidthSec) * bucketWidthSec;\n const windowStart = snapped - (bucketCount - 1) * bucketWidthSec;\n\n const tStmt = this.stmt(\n \"singleRouteActivity\",\n `SELECT\n CAST((CAST(strftime('%s', started_at) AS INTEGER) - ?) / ? AS INTEGER) AS bucket,\n COUNT(*) AS cnt\n FROM exchanges\n WHERE route_id = ?\n AND CAST(strftime('%s', started_at) AS INTEGER) >= ?\n GROUP BY bucket`,\n );\n const tRows = tStmt.all(\n windowStart,\n bucketWidthSec,\n routeId,\n windowStart,\n ) as Array<{ bucket: number; cnt: number }>;\n\n const eStmt = this.stmt(\n \"singleRouteErrors\",\n `SELECT COUNT(*) AS cnt\n FROM exchanges\n WHERE route_id = ?\n AND status = 'failed'\n AND CAST(strftime('%s', started_at) AS INTEGER) >= ?`,\n );\n const eRow = eStmt.get(routeId, windowStart) as { cnt: number } | undefined;\n\n const throughput = new Array(bucketCount).fill(0) as number[];\n for (const row of tRows) {\n if (row.bucket >= 0 && row.bucket < bucketCount) {\n throughput[row.bucket] = row.cnt;\n }\n }\n\n return { throughput, recentErrors: eRow?.cnt ?? 0 };\n }\n\n /**\n * Get the maximum event ID (for tailing/polling).\n */\n getMaxEventId(): number {\n const stmt = this.db.prepare(\n \"SELECT COALESCE(MAX(id), 0) AS maxId FROM events\",\n );\n const row = stmt.get() as { maxId: number };\n return row.maxId;\n }\n\n /**\n * Get the exchange snapshot (headers and body) for a given exchange.\n * Returns null if no snapshot was captured.\n */\n getExchangeSnapshot(\n exchangeId: string,\n ): import(\"./types.js\").ExchangeSnapshot | null {\n try {\n const s = this.stmt(\n \"exchangeSnapshot\",\n `SELECT headers, body, truncated\n FROM exchange_snapshots\n WHERE exchange_id = ?\n LIMIT 1`,\n );\n const row = s.get(exchangeId) as\n | { headers: string; body: string | null; truncated: number }\n | undefined;\n if (!row) return null;\n return {\n headers: row.headers,\n body: row.body,\n truncated: row.truncated !== 0,\n };\n } catch {\n // Table may not exist in very old databases\n return null;\n }\n }\n\n /**\n * Close the database connection.\n */\n close(): void {\n this.db.close();\n }\n}\n\n/**\n * Compute a percentile from a sorted array of numbers using linear interpolation.\n * Returns null if the array is empty.\n */\nfunction percentile(sorted: number[], p: number): number | null {\n if (sorted.length === 0) return null;\n const idx = p * (sorted.length - 1);\n const lo = Math.floor(idx);\n const hi = Math.ceil(idx);\n if (lo === hi) return sorted[lo]!;\n return sorted[lo]! + (sorted[hi]! - sorted[lo]!) * (idx - lo);\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
var
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import {a}from'./chunk-3CAI2NPQ.js';var n={major:1,minor:1,patch:0},m="https://bun.com/docs/installation",v="https://routecraft.dev/docs/advanced/programmatic-invocation";function d(e=process.versions.bun??null){let o=e;if(!o)return {ok:false,message:`[routecraft] The craft CLI requires Bun. Install Bun from ${m}, or embed @routecraft/routecraft programmatically (see ${v}).`};let s=o.split(/[-+]/)[0]??"",[t,a,u]=s.split("."),r=Number(t),c=Number(a),l=Number(u??"0");return !Number.isFinite(r)||!Number.isFinite(c)||!Number.isFinite(l)?{ok:false,message:`[routecraft] Could not parse Bun version "${o}". Routecraft requires Bun ${f(n)} or later. Upgrade Bun: ${m}.`}:r>n.major||r===n.major&&(c>n.minor||c===n.minor&&l>=n.patch)?{ok:true}:{ok:false,message:`[routecraft] Bun ${o} is not supported. Routecraft requires Bun ${f(n)} or later. Upgrade Bun: ${m}.`}}function f(e){return `${e.major}.${e.minor}.${e.patch}`}var g=d();g.ok||(console.error(g.message),process.exit(1));var{Command:h}=await import('commander'),i=new h;i.name("craft").description("A modern routing framework for TypeScript").version(a).enablePositionalOptions().option("--log-level <level>","Log level (e.g. info, warn, error, silent to disable)").option("--log-file <path>","Write logs to a file instead of stdout").showSuggestionAfterError().showHelpAfterError().exitOverride(e=>{e.code==="commander.unknownCommand"&&process.exit(0);});process.argv.length<=2&&i.help({error:false});i.command("run").description("Run routes from a single TypeScript/JavaScript file").argument("<file>","Path to a file containing routes").argument("[args...]","CLI command and flags to pass through to CLI adapter routes").option("--env <path>","Load environment variables from a .env file (default: .env)").passThroughOptions().action(async(e,o,s)=>{let t=i.opts();t.logLevel!==void 0&&(process.env.LOG_LEVEL=t.logLevel,process.env.CRAFT_LOG_LEVEL=t.logLevel),t.logFile!==void 0&&(process.env.LOG_FILE=t.logFile,process.env.CRAFT_LOG_FILE=t.logFile);let{loadEnvFile:a}=await import('./util-GX4G3K5I.js');s.env!==void 0?a(s.env):a();let{runCommand:u}=await import('./run-3Y3G7OTJ.js'),r=await u(e,o);if(!r.success){r.message&&console.error(r.message);let c=r.code??1;setImmediate(()=>process.exit(c));return}});i.command("tui").description("Launch the Terminal UI to monitor Routecraft execution history").option("--db <path>","Path to the telemetry SQLite database",".routecraft/telemetry.db").action(async e=>{let{resolve:o,isAbsolute:s}=await import('path'),t=s(e.db)?e.db:o(process.cwd(),e.db),{renderTui:a}=await import('./tui/app.js');await a(t);});i.parse();//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["major","minor","hasTSFile","arg","isBun","execFileSync","createRequire","tsxEsmPath","err","Command","program","filePath","args","options","globalOpts","loadEnvFile","runCommand","result","code","resolve","isAbsolute","dbPath","renderTui"],"mappings":"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","file":"index.js","sourcesContent":["#!/usr/bin/env node\n\n/**\n * Routecraft CLI — single entry point.\n *\n * 1. Check Node version (Pino 10 needs Node 18.19+)\n * 2. Define program and parse; log options are global and applied before lazy-loading run/util (which load the logger)\n */\n\n// ── 1. Node version gate ────────────────────────────────────────────\nconst [major, minor] = process.version.slice(1).split(\".\").map(Number) as [\n number,\n number,\n];\n\nif (!(major > 18 || (major === 18 && minor >= 19))) {\n // eslint-disable-next-line no-console\n console.error(\n `[routecraft] Node.js ${process.version} is not supported. ` +\n `Routecraft requires Node.js 18.19.0 or later (e.g. 20 or 22). ` +\n `Please upgrade Node or configure your MCP client to use a newer Node.`,\n );\n process.exit(1);\n}\n\n// ── 2. Re-exec with tsx loader if a .ts file is being run ───────────────────\n// Node's native --experimental-strip-types does not handle extensionless\n// imports or .js-to-.ts resolution. tsx (via --import tsx/esm) does.\n// We set CRAFT_TSX_LOADER=1 before re-execing to avoid an infinite loop.\n// Bun has native TypeScript support, so we skip the re-exec entirely there.\nconst hasTSFile = process.argv\n .slice(2)\n .some((arg) => !arg.startsWith(\"-\") && arg.endsWith(\".ts\"));\nconst isBun = typeof process.versions[\"bun\"] === \"string\";\n\nif (hasTSFile && !isBun && !process.env[\"CRAFT_TSX_LOADER\"]) {\n const { execFileSync } = await import(\"node:child_process\");\n const { createRequire } = await import(\"node:module\");\n // Resolve tsx/esm relative to the CLI package so it works regardless of CWD\n const tsxEsmPath = createRequire(import.meta.url).resolve(\"tsx/esm\");\n\n // Let the child handle SIGINT/SIGTERM for graceful shutdown.\n // Without this the parent dies on the first signal, orphaning the child\n // mid-teardown (connections not closed, logs truncated).\n process.on(\"SIGINT\", () => {});\n process.on(\"SIGTERM\", () => {});\n\n try {\n execFileSync(\n process.execPath,\n [\n \"--import\",\n tsxEsmPath,\n ...process.execArgv,\n process.argv[1]!,\n ...process.argv.slice(2),\n ],\n { stdio: \"inherit\", env: { ...process.env, CRAFT_TSX_LOADER: \"1\" } },\n );\n process.exit(0);\n } catch (err: unknown) {\n process.exit(\n (err as NodeJS.ErrnoException & { status?: number }).status ?? 1,\n );\n }\n}\n\n// ── 3. CLI definition (only Commander; run/util are lazy-loaded so logger sees env) ─\nconst { Command } = await import(\"commander\");\nconst program = new Command();\n\nprogram\n .name(\"craft\")\n .description(\"A modern routing framework for TypeScript\")\n .version(\"0.5.0-canary.8\")\n .enablePositionalOptions()\n .option(\n \"--log-level <level>\",\n \"Log level (e.g. info, warn, error, silent to disable)\",\n )\n .option(\"--log-file <path>\", \"Write logs to a file instead of stdout\")\n .showSuggestionAfterError()\n .showHelpAfterError()\n .exitOverride((err) => {\n if (err.code === \"commander.unknownCommand\") {\n process.exit(0);\n }\n });\n\n// Show help by default if no arguments provided\nif (process.argv.length <= 2) {\n program.help({ error: false });\n}\n\n/**\n * The 'run' command executes routes from a single file.\n *\n * Example:\n * craft run ./my-routes.ts\n * craft run ./my-cli.ts greet --name World\n */\nprogram\n .command(\"run\")\n .description(\"Run routes from a single TypeScript/JavaScript file\")\n .argument(\"<file>\", \"Path to a file containing routes\")\n .argument(\n \"[args...]\",\n \"CLI command and flags to pass through to CLI adapter routes\",\n )\n .option(\n \"--env <path>\",\n \"Load environment variables from a .env file (default: .env)\",\n )\n .passThroughOptions()\n .action(async (filePath, args: string[], options) => {\n // Apply global log options to env before any import that creates the logger\n const globalOpts = program.opts();\n if (globalOpts[\"logLevel\"] !== undefined) {\n process.env[\"LOG_LEVEL\"] = globalOpts[\"logLevel\"];\n process.env[\"CRAFT_LOG_LEVEL\"] = globalOpts[\"logLevel\"];\n }\n if (globalOpts[\"logFile\"] !== undefined) {\n process.env[\"LOG_FILE\"] = globalOpts[\"logFile\"];\n process.env[\"CRAFT_LOG_FILE\"] = globalOpts[\"logFile\"];\n }\n\n const { loadEnvFile } = await import(\"./util.js\");\n if (options.env !== undefined) {\n loadEnvFile(options.env);\n } else {\n loadEnvFile();\n }\n\n const { runCommand } = await import(\"./run.js\");\n const result = await runCommand(filePath, args);\n if (!result.success) {\n if (result.message) {\n // eslint-disable-next-line no-console\n console.error(result.message);\n }\n // Defer exit so pino/sonic-boom can finish initializing and avoid \"sonic boom is not ready yet\"\n const code = result.code ?? 1;\n setImmediate(() => process.exit(code));\n return;\n }\n // Don't call process.exit() — let the event loop drain naturally.\n // process.exit() triggers C++ static destructors that race with ONNX\n // Runtime cleanup (onnxruntime#25038: \"mutex lock failed\").\n });\n\n/**\n * The 'tui' command launches the Terminal UI for monitoring Routecraft execution.\n *\n * Example:\n * craft tui\n * craft tui --db .routecraft/telemetry.db\n */\nprogram\n .command(\"tui\")\n .description(\"Launch the Terminal UI to monitor Routecraft execution history\")\n .option(\n \"--db <path>\",\n \"Path to the telemetry SQLite database\",\n \".routecraft/telemetry.db\",\n )\n .action(async (options) => {\n const { resolve, isAbsolute } = await import(\"node:path\");\n const dbPath = isAbsolute(options.db)\n ? options.db\n : resolve(process.cwd(), options.db);\n\n const { renderTui } = await import(\"./tui/app.js\");\n await renderTui(dbPath);\n });\n\n// Parse the command line arguments and execute the appropriate command\nprogram.parse();\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime-gate.ts","../src/index.ts"],"names":["MIN_BUN_VERSION","INSTALL_URL","EMBEDDING_DOC_URL","checkBunRuntime","bunVersion","version","stripped","majorStr","minorStr","patchStr","major","minor","patch","formatVersion","v","gate","Command","program","err","filePath","args","options","globalOpts","loadEnvFile","runCommand","result","code","resolve","isAbsolute","dbPath","renderTui"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Runtime gate for the craft CLI.\n *\n * The CLI is Bun-only. Users on Node embed @routecraft/routecraft\n * programmatically instead of going through the CLI.\n *\n * Exposed as a pure function so unit tests can pass arbitrary version strings\n * without spawning real bun processes.\n */\n\nconst MIN_BUN_VERSION = { major: 1, minor: 1, patch: 0 } as const;\n\nconst INSTALL_URL = \"https://bun.com/docs/installation\";\nconst EMBEDDING_DOC_URL =\n \"https://routecraft.dev/docs/advanced/programmatic-invocation\";\n\nexport type RuntimeGateResult = { ok: true } | { ok: false; message: string };\n\n// Accept `null` (not `undefined`) to express \"not running on Bun\" so tests can\n// reliably exercise the missing-bun path. JS default-parameter semantics\n// re-apply the default for explicit `undefined`, which would otherwise let\n// `process.versions.bun` leak in when the suite runs under Bun.\nexport function checkBunRuntime(\n bunVersion: string | null = process.versions[\"bun\"] ?? null,\n): RuntimeGateResult {\n const version = bunVersion;\n\n if (!version) {\n return {\n ok: false,\n message:\n `[routecraft] The craft CLI requires Bun. ` +\n `Install Bun from ${INSTALL_URL}, ` +\n `or embed @routecraft/routecraft programmatically (see ${EMBEDDING_DOC_URL}).`,\n };\n }\n\n // Strip both prerelease (`-`) and build metadata (`+`) per SemVer.\n const stripped = version.split(/[-+]/)[0] ?? \"\";\n const [majorStr, minorStr, patchStr] = stripped.split(\".\");\n const major = Number(majorStr);\n const minor = Number(minorStr);\n const patch = Number(patchStr ?? \"0\");\n\n if (\n !Number.isFinite(major) ||\n !Number.isFinite(minor) ||\n !Number.isFinite(patch)\n ) {\n return {\n ok: false,\n message:\n `[routecraft] Could not parse Bun version \"${version}\". ` +\n `Routecraft requires Bun ${formatVersion(MIN_BUN_VERSION)} or later. ` +\n `Upgrade Bun: ${INSTALL_URL}.`,\n };\n }\n\n const meetsFloor =\n major > MIN_BUN_VERSION.major ||\n (major === MIN_BUN_VERSION.major &&\n (minor > MIN_BUN_VERSION.minor ||\n (minor === MIN_BUN_VERSION.minor && patch >= MIN_BUN_VERSION.patch)));\n\n if (!meetsFloor) {\n return {\n ok: false,\n message:\n `[routecraft] Bun ${version} is not supported. ` +\n `Routecraft requires Bun ${formatVersion(MIN_BUN_VERSION)} or later. ` +\n `Upgrade Bun: ${INSTALL_URL}.`,\n };\n }\n\n return { ok: true };\n}\n\nfunction formatVersion(v: typeof MIN_BUN_VERSION): string {\n return `${v.major}.${v.minor}.${v.patch}`;\n}\n","#!/usr/bin/env bun\n\n/**\n * Routecraft CLI: single entry point.\n *\n * The CLI runs on Bun. For Node-based usage, embed @routecraft/routecraft\n * programmatically (see https://routecraft.dev/docs/advanced/programmatic-invocation).\n *\n * 1. Bun runtime gate (presence + version floor)\n * 2. Define program and parse; log options are global and applied before\n * lazy-loading run/util (which load the logger)\n */\n\nimport { checkBunRuntime } from \"./runtime-gate.js\";\nimport { version } from \"../package.json\";\n\n// ── 1. Bun runtime gate ─────────────────────────────────────────────\nconst gate = checkBunRuntime();\nif (!gate.ok) {\n // eslint-disable-next-line no-console\n console.error(gate.message);\n process.exit(1);\n}\n\n// ── 2. CLI definition (only Commander; run/util are lazy-loaded so logger sees env) ─\nconst { Command } = await import(\"commander\");\nconst program = new Command();\n\nprogram\n .name(\"craft\")\n .description(\"A modern routing framework for TypeScript\")\n .version(version)\n .enablePositionalOptions()\n .option(\n \"--log-level <level>\",\n \"Log level (e.g. info, warn, error, silent to disable)\",\n )\n .option(\"--log-file <path>\", \"Write logs to a file instead of stdout\")\n .showSuggestionAfterError()\n .showHelpAfterError()\n .exitOverride((err) => {\n if (err.code === \"commander.unknownCommand\") {\n process.exit(0);\n }\n });\n\n// Show help by default if no arguments provided\nif (process.argv.length <= 2) {\n program.help({ error: false });\n}\n\n/**\n * The 'run' command executes routes from a single file.\n *\n * Example:\n * craft run ./my-routes.ts\n * craft run ./my-cli.ts greet --name World\n */\nprogram\n .command(\"run\")\n .description(\"Run routes from a single TypeScript/JavaScript file\")\n .argument(\"<file>\", \"Path to a file containing routes\")\n .argument(\n \"[args...]\",\n \"CLI command and flags to pass through to CLI adapter routes\",\n )\n .option(\n \"--env <path>\",\n \"Load environment variables from a .env file (default: .env)\",\n )\n .passThroughOptions()\n .action(async (filePath, args: string[], options) => {\n // Apply global log options to env before any import that creates the logger\n const globalOpts = program.opts();\n if (globalOpts[\"logLevel\"] !== undefined) {\n process.env[\"LOG_LEVEL\"] = globalOpts[\"logLevel\"];\n process.env[\"CRAFT_LOG_LEVEL\"] = globalOpts[\"logLevel\"];\n }\n if (globalOpts[\"logFile\"] !== undefined) {\n process.env[\"LOG_FILE\"] = globalOpts[\"logFile\"];\n process.env[\"CRAFT_LOG_FILE\"] = globalOpts[\"logFile\"];\n }\n\n const { loadEnvFile } = await import(\"./util.js\");\n if (options.env !== undefined) {\n loadEnvFile(options.env);\n } else {\n loadEnvFile();\n }\n\n const { runCommand } = await import(\"./run.js\");\n const result = await runCommand(filePath, args);\n if (!result.success) {\n if (result.message) {\n // eslint-disable-next-line no-console\n console.error(result.message);\n }\n // Defer exit so pino/sonic-boom can finish initializing and avoid \"sonic boom is not ready yet\"\n const code = result.code ?? 1;\n setImmediate(() => process.exit(code));\n return;\n }\n // Don't call process.exit(); let the event loop drain naturally.\n // process.exit() triggers C++ static destructors that race with ONNX\n // Runtime cleanup (onnxruntime#25038: \"mutex lock failed\").\n });\n\n/**\n * The 'tui' command launches the Terminal UI for monitoring Routecraft execution.\n *\n * Example:\n * craft tui\n * craft tui --db .routecraft/telemetry.db\n */\nprogram\n .command(\"tui\")\n .description(\"Launch the Terminal UI to monitor Routecraft execution history\")\n .option(\n \"--db <path>\",\n \"Path to the telemetry SQLite database\",\n \".routecraft/telemetry.db\",\n )\n .action(async (options) => {\n const { resolve, isAbsolute } = await import(\"node:path\");\n const dbPath = isAbsolute(options.db)\n ? options.db\n : resolve(process.cwd(), options.db);\n\n const { renderTui } = await import(\"./tui/app.js\");\n await renderTui(dbPath);\n });\n\n// Parse the command line arguments and execute the appropriate command\nprogram.parse();\n"]}
|
package/dist/tui/app.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {a}from'../chunk-
|
|
1
|
+
import {a as a$1}from'../chunk-3CAI2NPQ.js';import {a}from'../chunk-5UHEZNN5.js';import {useState,useCallback,useEffect,Fragment as Fragment$1}from'react';import {render,useApp,useStdout,useInput,Box,Text}from'ink';import {jsx,jsxs,Fragment}from'react/jsx-runtime';var st=[{items:[{key:"capabilities",label:"Capabilities",shortcut:"1"}]},{items:[{key:"exchanges",label:"Exchanges",shortcut:"2"},{key:"errors",label:"Errors",shortcut:"3"},{key:"events",label:"Events",shortcut:"4"}]}],Dt=st.flatMap(e=>e.items);function ge(e,t){return t<=0?"":e.length>t?e.slice(0,t-1)+"\u2026":e}function _(e){return e==null||!Number.isFinite(e)||e<0?"-":e===0?"<1ms":e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(1)}s`}function oe(e){switch(e){case "completed":case "started":return "green";case "failed":return "red";case "stopped":case "dropped":return "yellow";default:return "white"}}function B(e,t){return t===void 0?e:t<=0?"":e.length>t?e.slice(0,t-1)+"\u2026":e.padEnd(t)}function Xt(e){try{let t=JSON.parse(e),o="exchangeId"in t?`${String(t.exchangeId).slice(0,8)}`:"",s="duration"in t?_(t.duration):"";if("operation"in t&&"routeId"in t){let r="adapter"in t?`(${t.adapter})`:"adapterId"in t?`(${t.adapterId})`:"",n="";if("metadata"in t&&typeof t.metadata=="object"&&t.metadata!==null){let u=t.metadata,l=Object.keys(u).slice(0,2);l.length>0&&(n=l.map(m=>`${m}=${u[m]}`).join(" "));}return {step:String(t.operation),adapter:r,exchange:o,duration:s,meta:n}}if("routeId"in t&&"exchangeId"in t){let r="error"in t?"(err)":"reason"in t?`(${String(t.reason).slice(0,8)})`:"";return {step:String(t.routeId),adapter:r,exchange:o,duration:s,meta:""}}if("route"in t&&typeof t.route=="object"&&t.route!==null){let r=t.route;return {step:r.routeId??r.definition?.id??"?",adapter:"",exchange:"",duration:"",meta:""}}if("pluginId"in t)return {step:`plugin=${t.pluginId}`,adapter:"",exchange:"",duration:"",meta:""};if("error"in t){let r=t.error;return {step:typeof r=="object"&&r!==null&&"message"in r?String(r.message):String(r),adapter:"",exchange:"",duration:"",meta:""}}return null}catch{return null}}function It(e,t){let o=Xt(t);return o||{step:t,adapter:"",exchange:"",duration:"",meta:""}}function q(e){return e.toLocaleString("en-US")}function at(e,t,o){return o<=0?t:e<t?e:e>=t+o?e-o+1:t}function Ce(e=0){let[t,o]=useState(e),[s,r]=useState(0),n=useCallback((m,d,g)=>{let p=Math.max(0,Math.min(m,d-1));o(p),r(f=>at(p,f,g));},[]),u=useCallback((m,d,g)=>{o(p=>{let f=Math.max(0,Math.min(p+m,d-1));return r(i=>at(f,i,g)),f});},[]),l=useCallback(()=>{o(0),r(0);},[]);return {selectedIndex:t,scrollOffset:s,moveBy:u,moveTo:n,reset:l}}function S({title:e,subtitle:t,color:o="gray",width:s,flexGrow:r,paddingX:n=1,children:u}){let l=s?s-2-n*2:20;return jsxs(Box,{flexDirection:"column",borderStyle:"round",borderColor:o,paddingX:n,width:s,flexGrow:r,children:[e&&jsxs(Fragment,{children:[jsxs(Text,{bold:true,children:[e,t&&jsxs(Fragment,{children:[" ",t]})]}),jsx(Text,{dimColor:true,children:"\u2500".repeat(Math.max(l,1))})]}),u]})}var qt=[192,36,18,9],Yt=10240,De=[{max:0,color:"gray"},{max:10,color:"green"},{max:20,color:"green"},{max:30,color:"yellow"},{max:40,color:"yellow"},{max:50,color:"red"}];function He({values:e,columns:t,steps:o=De,label:s}){let r=o.length-1,n=e.length>=t?e.slice(e.length-t):[...new Array(t-e.length).fill(0),...e],u=n.map(d=>{if(d<=0)return 0;for(let g=1;g<o.length;g++)if(d<o[g].max)return g;return r}),l=Math.ceil(r/4),m=[];for(let d=l-1;d>=0;d--){let g=d*4,p=[];for(let f=0;f<n.length;f++){let i=u[f],y=0,k="";for(let X=0;X<4;X++){let E=g+X+1;if(E>r)break;(i>=E||i===0&&E===1)&&(y|=qt[X],k=i===0?o[0].color:o[E].color);}let L=String.fromCharCode(Yt+y),K=p[p.length-1];K&&K.color===k?K.text+=L:p.push({text:L,color:k});}m.push(jsx(Text,{children:p.map((f,i)=>f.color?jsx(Text,{color:f.color,children:f.text},i):jsx(Text,{children:f.text},i))},d));}return jsxs(Fragment,{children:[m,s&&jsx(Text,{dimColor:true,children:s})]})}function Bt({route:e}){return jsxs(Fragment,{children:[jsxs(Text,{children:["Capability:"," ",jsx(Text,{bold:true,color:"cyan",children:e.id})]}),jsxs(Text,{children:["Status:"," ",jsx(Text,{bold:true,color:oe(e.status),children:e.status})," ","Exchanges: ",jsx(Text,{bold:true,children:q(e.totalExchanges)})," ","Errors:"," ",jsx(Text,{bold:true,...e.failedExchanges>0?{color:"red"}:{},children:q(e.failedExchanges)})," ","Dropped:"," ",jsx(Text,{bold:true,...e.droppedExchanges>0?{color:"yellow"}:{},children:q(e.droppedExchanges)})," ","Avg: ",jsx(Text,{bold:true,children:_(e.avgDurationMs)})]})]})}function Ee({columns:e,data:t,rowKey:o,selectedIndex:s,scrollOffset:r,visibleRows:n,gap:u=2,emptyMessage:l="No data",renderFullRow:m}){let d=" ".repeat(u),g=jsx(Box,{children:e.map((i,y)=>{let L=y===e.length-1?"":d;if(i.width==="flex")return jsxs(Fragment$1,{children:[jsx(Box,{flexGrow:1,children:jsx(Text,{bold:true,dimColor:true,children:i.header})}),L&&jsx(Text,{children:L})]},y);let K=i.align==="right"?i.header.padStart(i.width):B(i.header,i.width);return jsxs(Fragment$1,{children:[jsx(Text,{bold:true,dimColor:true,children:K}),L&&jsx(Text,{children:L})]},y)})});if(t.length===0)return jsxs(Fragment,{children:[g,jsx(Text,{dimColor:true,children:l})]});let p=t.slice(r,r+n),f=t.length-r-n;return jsxs(Fragment,{children:[g,p.map((i,y)=>{let k=r+y,L=k===s,K=m?.(i,k);return K!==void 0?jsx(Box,{children:K},o(i,k)):jsx(Box,{children:e.map((X,E)=>{let O=E===e.length-1?"":d,T=X.width==="flex"?void 0:X.width,w=X.render(i,L,T);return X.width==="flex"?jsxs(Fragment$1,{children:[jsx(Box,{flexGrow:1,children:jsx(Text,{wrap:"truncate",children:w})}),O&&jsx(Text,{children:O})]},E):jsxs(Fragment$1,{children:[w,O&&jsx(Text,{children:O})]},E)})},o(i,k))}),jsx(Text,{dimColor:true,children:f>0?"\u2193 more":" "})]})}function Je(e){let t=new WeakMap;function o(s){let r=e(s);if(!r)return;let n=t.get(r);return n||(n=It(r.eventName,r.details),t.set(r,n)),n}return [{header:"Step",width:10,render:(s,r,n)=>{let u=o(s);return u?jsx(Text,{children:B(u.step,n)}):jsx(Text,{children:B("",n)})}},{header:"Adapter",width:10,render:(s,r,n)=>{let u=o(s);return u?jsx(Text,{children:B(u.adapter,n)}):jsx(Text,{children:B("",n)})}},{header:"Exchange",width:12,render:(s,r,n)=>{let u=o(s);return u?jsx(Text,{children:B(u.exchange,n)}):jsx(Text,{children:B("",n)})}},{header:"Duration",width:8,align:"right",render:s=>{let r=o(s);return r?jsx(Text,{children:r.duration.padStart(8)}):jsx(Text,{children:""})}}]}function Te(){return {header:"",width:2,render:(e,t)=>jsx(Text,{...t?{color:"cyan"}:{},bold:t,children:t?"> ":" "})}}var tr=[Te(),{header:"ID",width:"flex",render:(e,t)=>jsx(Text,{...t?{color:"cyan"}:{},bold:t,children:e.id})},{header:"Status",width:9,render:(e,t,o)=>jsx(Text,{color:oe(e.status),children:B(e.status,o)})},{header:"Duration",width:8,align:"right",render:e=>jsx(Text,{children:_(e.durationMs).padStart(8)})},{header:"Time",width:8,render:(e,t,o)=>jsx(Text,{dimColor:true,children:B(e.startedAt.replace("T"," ").slice(11,19),o)})}];function Ve({capabilityId:e,route:t,exchanges:o,selectedIndex:s,scrollOffset:r,width:n,height:u,color:l="gray",activity:m}){let d=Math.ceil((De.length-1)/4),g=t?2+d+2:0,p=Math.max(u-6-g,3),f=t?"EXCHANGES":`EXCHANGES: ${e}`;return jsxs(Box,{flexDirection:"column",width:n,flexGrow:1,children:[t&&jsxs(S,{width:n,children:[jsx(Bt,{route:t}),jsx(Text,{children:" "}),jsx(He,{values:m?m.throughput:[],columns:n-4,label:"Exchanges per 5s bucket"})]}),jsx(S,{title:f,width:n,flexGrow:1,color:l,children:jsx(Ee,{columns:tr,data:o,rowKey:i=>i.id+i.contextId,selectedIndex:s,scrollOffset:r,visibleRows:p,emptyMessage:"No exchanges"})})]})}function rr(e){let t=[],o=/("(?:[^"\\]|\\.)*")\s*:|("(?:[^"\\]|\\.)*")|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|(\btrue\b|\bfalse\b)|(\bnull\b)|([{}[\],:])/g,s=0,r;for(;(r=o.exec(e))!==null;)r.index>s&&t.push({text:e.slice(s,r.index),type:"brace"}),r[1]!==void 0?(t.push({text:r[1],type:"key"}),t.push({text:":",type:"brace"})):r[2]!==void 0?t.push({text:r[2],type:"string"}):r[3]!==void 0?t.push({text:r[3],type:"number"}):r[4]!==void 0?t.push({text:r[4],type:"boolean"}):r[5]!==void 0?t.push({text:r[5],type:"null"}):r[6]!==void 0&&t.push({text:r[6],type:"brace"}),s=r.index+r[0].length;return s<e.length&&t.push({text:e.slice(s),type:"brace"}),t}var nr={key:"cyan",string:"green",number:"yellow",boolean:"yellow",null:"red",brace:"gray"};function Ae(e,t){try{let o=JSON.parse(e);return JSON.stringify(o,null,2).split(`
|
|
2
2
|
`).map(r=>{let n=Math.max(1,t);return r.length>n?r.slice(0,Math.max(0,n-1))+"\u2026":r})}catch{let o=Math.max(1,t);return e.split(`
|
|
3
|
-
`).map(s=>s.length>o?s.slice(0,Math.max(0,o-1))+"\u2026":s)}}function Ue({line:e}){let t=tr(e);return t.length===0?jsx(Text,{children:e}):jsx(Text,{children:t.map((o,s)=>jsx(Text,{color:rr[o.type],children:o.text},s))})}function We(e){try{let t=JSON.parse(e);return typeof t!="object"||t===null||Array.isArray(t)?null:t}catch{return null}}function or(e,t){let o=new Map,s=0;o.set(t,{exchangeId:t,label:"parent",events:[]});for(let r of e){let n=We(r.details),u=n?String(n.exchangeId??""):"",l=!u||u===t?t:u;o.has(l)||(s++,o.set(l,{exchangeId:l,label:`child ${s}`,events:[]})),o.get(l).events.push(r);}return Array.from(o.values())}var sr=[Te(),{header:"Time",width:8,render:(e,t,o)=>{if(!e.event)return jsx(Text,{children:N("",o)});let s=" ".repeat(e.indent);return jsxs(Text,{dimColor:true,children:[s,e.event.timestamp.replace("T"," ").slice(11,19)]})}},{header:"Event",width:"flex",render:e=>e.event?jsx(Text,{color:"cyan",children:e.event.eventName}):jsx(Text,{})},...Je(e=>e.event)];function Xe({exchange:e,events:t,width:o,height:s,scrollOffset:r,selectedIndex:n=-1,color:u="gray"}){let l=e.error?2:0,m=Math.max(s-9-l,3),d=or(t,e.id),g=d.length>1,p=[];for(let i of d){g&&p.push({type:"header",text:`${i.label} (${i.exchangeId.slice(0,8)}) - ${i.events.length} events`,indent:0});for(let y of i.events)p.push({type:"event",event:y,indent:g?2:0});}let f=g?`EXCHANGE FLOW (${d.length} exchanges, ${t.length} events)`:`RELATED EVENTS (${t.length})`;return jsxs(Box,{flexDirection:"column",width:o,flexGrow:1,children:[jsxs(S,{width:o,children:[jsxs(Text,{children:["Capability:"," ",jsx(Text,{bold:true,color:"cyan",children:e.routeId})]}),jsxs(Text,{children:["Exchange:"," ",jsx(Text,{bold:true,color:"cyan",children:ge(e.id,o-14)})]}),jsxs(Text,{children:["Status:"," ",jsx(Text,{bold:true,color:oe(e.status),children:e.status}),e.durationMs!==null&&jsxs(Text,{children:[" ","Duration:"," ",jsx(Text,{bold:true,children:_(e.durationMs)})]})," ","Started: ",jsx(Text,{bold:true,children:e.startedAt}),e.completedAt&&jsxs(Text,{children:[" ","Completed: ",jsx(Text,{bold:true,children:e.completedAt})]})]}),e.error&&e.status==="failed"&&jsxs(Fragment,{children:[jsx(Text,{children:" "}),jsxs(Text,{color:"red",children:["Error: ",e.error]})]}),e.error&&e.status==="dropped"&&jsxs(Fragment,{children:[jsx(Text,{children:" "}),jsxs(Text,{color:"yellow",children:["Reason: ",e.error]})]})]}),jsx(S,{title:f,width:o,flexGrow:1,color:u,children:jsx(Ee,{columns:sr,data:p,rowKey:(i,y)=>i.type==="header"?`h-${y}`:`${i.event?.id??i.event?.timestamp}-${y}`,selectedIndex:n,scrollOffset:r,visibleRows:m,emptyMessage:"No related events found",renderFullRow:i=>i.type==="header"?jsx(Text,{bold:true,color:"yellow",children:i.text}):void 0})})]})}var ar=[Te(),{header:"Time",width:19,render:(e,t,o)=>jsx(Text,{...t?{color:"cyan"}:{},bold:t,children:N(e.timestamp.replace("T"," ").slice(0,19),o)})},{header:"Event",width:"flex",render:e=>jsx(Text,{color:"cyan",children:e.eventName})},...Je(e=>e)];function Ot({events:e,selectedIndex:t,scrollOffset:o,width:s,height:r,color:n="gray"}){let u=Math.max(r-6,5);return jsx(S,{title:"EVENTS",subtitle:jsxs(Text,{dimColor:true,children:["(",e.length," total)"]}),width:s,flexGrow:1,color:n,children:jsx(Ee,{columns:ar,data:e,rowKey:(l,m)=>l.id!==void 0?String(l.id):`${l.timestamp}-${m}`,selectedIndex:t,scrollOffset:o,visibleRows:u,emptyMessage:"No events recorded yet."})})}function Ne({event:e,width:t,height:o,scrollOffset:s,color:r="cyan"}){let n=We(e.details),u=n&&typeof n.routeId=="string"?n.routeId:void 0,l=n&&typeof n.exchangeId=="string"?n.exchangeId:void 0,d=2+(1+(u?1:0)+(l?1:0)+2),g=5,p=Math.max(t-6,20),f=Ae(e.details,p),i=Math.max(o-d-g,3),y=f.slice(s,s+i);return jsxs(Box,{flexDirection:"column",width:t,flexGrow:1,children:[jsxs(S,{width:t,children:[jsxs(Text,{children:["Context:"," ",jsx(Text,{bold:true,color:"cyan",children:e.contextId})]}),u&&jsxs(Text,{children:["Capability:"," ",jsx(Text,{bold:true,color:"cyan",children:u})]}),l&&jsxs(Text,{children:["Exchange:"," ",jsx(Text,{bold:true,color:"cyan",children:l})]}),jsxs(Text,{children:["Event:"," ",jsx(Text,{bold:true,color:"cyan",children:e.eventName})]}),jsxs(Text,{children:["Time: ",jsx(Text,{bold:true,children:e.timestamp.replace("T"," ")})]})]}),jsxs(S,{title:"DETAILS",subtitle:jsxs(Text,{dimColor:true,children:["(",s+1,"-",Math.min(s+i,f.length),"/",f.length," lines)"]}),width:t,flexGrow:1,color:r,children:[y.map((k,L)=>jsx(Ue,{line:k},s+L)),y.length<i&&Array.from({length:i-y.length}).map((k,L)=>jsx(Text,{children:" "},`pad-${L}`))]})]})}function dr(e,t){let o=[];return o.push("HEADERS"),o.push(...Ae(e.headers,t)),o.push(""),o.push("BODY"),e.body!==null?(o.push(...Ae(e.body,t)),e.truncated&&(o.push(""),o.push("[Body truncated to configured limit]"))):o.push("[null]"),o}function qe({exchange:e,snapshot:t,width:o,height:s,scrollOffset:r,color:n="cyan"}){let m=Math.max(o-6,20),d=Math.max(s-5-5,3),p=t!==null?dr(t,m):["[Snapshots not captured]","","Enable with:"," telemetry({ sqlite: { captureSnapshots: true } })"],f=p.slice(r,r+d);return jsxs(Box,{flexDirection:"column",width:o,flexGrow:1,children:[jsxs(S,{width:o,children:[jsxs(Text,{children:["Capability:"," ",jsx(Text,{bold:true,color:"cyan",children:e.routeId})]}),jsxs(Text,{children:["Exchange:"," ",jsx(Text,{bold:true,color:"cyan",children:ge(e.id,o-14)})]}),jsxs(Text,{children:["Status:"," ",jsx(Text,{bold:true,color:oe(e.status),children:e.status}),e.durationMs!==null&&jsxs(Text,{children:[" ","Duration:"," ",jsx(Text,{bold:true,children:_(e.durationMs)})]})]})]}),jsxs(S,{title:"EXCHANGE SNAPSHOT",subtitle:jsxs(Text,{dimColor:true,children:["(",r+1,"-",Math.min(r+d,p.length),"/",p.length," lines)"]}),width:o,flexGrow:1,color:n,children:[f.map((i,y)=>i==="HEADERS"||i==="BODY"?jsx(Text,{bold:true,color:"yellow",children:i},r+y):i.startsWith("[")?jsx(Text,{dimColor:true,children:i},r+y):jsx(Ue,{line:i},r+y)),f.length<d&&Array.from({length:d-f.length}).map((i,y)=>jsx(Text,{children:" "},`pad-${y}`))]})]})}function ur(e){return e.failedExchanges>0?"red":e.totalExchanges>0?"green":"yellow"}function _t({routes:e,selectedIndex:t,listOffset:o,visibleRows:s,width:r}){let n=o;return jsxs(Fragment,{children:[jsx(Text,{children:" "}),jsx(Text,{bold:true,dimColor:true,children:"\u2500".repeat(r+2)}),e.length===0?jsx(Text,{dimColor:true,children:"No capabilities"}):e.slice(n,n+s).map((u,l)=>{let m=n+l;return jsxs(Text,{wrap:"truncate",children:[jsx(Text,{...m===t?{color:"cyan"}:{},bold:m===t,children:m===t?"> ":" "}),jsx(Text,{color:ur(u),children:"\u25CF "}),jsx(Text,{...m===t?{color:"cyan"}:{},bold:m===t,children:ge(u.id,r-2)})]},u.id)}),e.length>s&&jsxs(Text,{dimColor:true,children:[t+1,"/",e.length]})]})}function yr({db:e}){let{exit:t}=useApp(),{stdout:o}=useStdout(),s=o?.columns??80,r=o?.rows??24,[n,u]=useState("capabilities"),[l,m]=useState("nav"),[d,g]=useState(false),[p,f]=useState([]),[i,y]=useState({totalRoutes:0,totalExchanges:0,completedExchanges:0,failedExchanges:0,droppedExchanges:0,errorRate:0,avgDurationMs:null,p90DurationMs:null,p95DurationMs:null,p99DurationMs:null}),[k,L]=useState([]),[K,W]=useState(void 0),E=Ce(),[X,O]=useState([]),T=Ce(),[w,ht]=useState(void 0),[he,ft]=useState([]),[Be,gt]=useState([]),te=Ce(),F=Ce(),[re,bt]=useState(void 0),[G,ce]=useState(false),[Le,ne]=useState(0),[H,Ye]=useState(false),[ze,ke]=useState(0),[Qe,Pt]=useState(null),fe=50,de=10,Oe=Math.max(Math.min(Math.floor(s*.18),26),16),ue=Math.max(Math.min(Math.floor(s*.22),30),20),A=Math.max(s-Oe-ue,30),R=Math.max(r-5,10),Ze=Math.max(R-10,3),et=useCallback(()=>{try{let c=e.getRouteSummary();f(c),y(e.getMetrics());let a=ue-4;L(e.getLiveTrafficBuckets(a,5));let I=A-4,b=c[E.selectedIndex];if(b&&W(e.getSingleRouteActivity(b.id,I,5)),l==="center")return;if(n==="capabilities"){let h=c[E.selectedIndex];h&&O(e.getExchangesByRoute(h.id,fe));}else n==="exchanges"?O(e.getAllExchanges(fe)):n==="errors"?O(e.getFailedExchanges(fe)):n==="events"&>(e.getRecentEvents({limit:200}));}catch{}},[e,n,E.selectedIndex,ue,A,l]);useEffect(()=>{et();let c=setInterval(et,2e3);return ()=>{clearInterval(c);}},[et]);let yt=useCallback(c=>{E.moveTo(c,p.length,Ze),T.reset();let a=p[c];a&&O(e.getExchangesByRoute(a.id,fe));},[e,p,E,T,Ze]),jt=useCallback(c=>{u(c),g(false),ce(false),Ye(false),m("nav"),T.reset(),c==="exchanges"?O(e.getAllExchanges(fe)):c==="errors"?O(e.getFailedExchanges(fe)):c==="events"&&(te.reset(),gt(e.getRecentEvents({limit:200})));},[e,T,te]),tt=p[E.selectedIndex],Gt=Math.ceil((De.length-1)/4),Ft=tt?2+Gt+2:0,_e=Math.max(R-6-Ft,3),Et=Math.max(R-6,5);useInput((c,a)=>{if(c==="q"){e.close(),t();return}let I=Ct.find(b=>b.shortcut===c);if(I){jt(I.key);return}if(n==="events"){if(G){let b=a.ctrl?de:1;c==="j"||a.downArrow?ne(h=>h+b):c==="k"||a.upArrow?ne(h=>Math.max(h-b,0)):(a.escape||a.backspace||a.delete)&&(ce(false),ne(0));return}if(l==="nav")a.return&&m("center");else {let b=a.ctrl?de:1;if(c==="j"||a.downArrow)te.moveBy(b,Be.length,Et);else if(c==="k"||a.upArrow)te.moveBy(-b,Be.length,Et);else if(a.return){let h=Be[te.selectedIndex];h&&(bt(h),ne(0),ce(true));}else (a.escape||a.backspace||a.delete)&&(m("nav"),te.reset());}return}if(H){let b=a.ctrl?de:1;c==="j"||a.downArrow?ke(h=>h+b):c==="k"||a.upArrow?ke(h=>Math.max(h-b,0)):(a.escape||a.backspace||a.delete)&&(Ye(false),ke(0));return}if(d&&!G){let b=w?.error?2:0,h=Math.max(R-9-b,3),$t=new Set(he.map(me=>{try{let Re=JSON.parse(me.details);return String(Re.exchangeId??"")}catch{return ""}})),vt=Math.max($t.size,1),Jt=vt>1?vt:0,wt=he.length+Jt,Rt=a.ctrl?de:1;if(c==="j"||a.downArrow)F.moveBy(Rt,wt,h);else if(c==="k"||a.upArrow)F.moveBy(-Rt,wt,h);else if(a.return){let me=0,Re,Pe=w?.id??"",xe=new Map;xe.set(Pe,[]);for(let Se of he)try{let rt=JSON.parse(Se.details),nt=String(rt.exchangeId??""),ot=!nt||nt===Pe?Pe:nt;xe.has(ot)||xe.set(ot,[]),xe.get(ot).push(Se);}catch{xe.get(Pe).push(Se);}let Vt=xe.size>1;for(let[,Se]of xe){Vt&&me++;for(let rt of Se)me===F.selectedIndex&&(Re=rt),me++;}Re&&(bt(Re),ne(0),ce(true));}else if(c==="e"&&w){let me=e.getExchangeSnapshot(w.id);Pt(me),ke(0),Ye(true);}else (a.escape||a.backspace||a.delete)&&(F.reset(),g(false));return}if(d&&G){let b=a.ctrl?de:1;c==="j"||a.downArrow?ne(h=>h+b):c==="k"||a.upArrow?ne(h=>Math.max(h-b,0)):(a.escape||a.backspace||a.delete)&&(ce(false),ne(0));return}if(n==="capabilities"&&l==="nav"){if(c==="j"||a.downArrow)yt(Math.min(E.selectedIndex+1,p.length-1));else if(c==="k"||a.upArrow)yt(Math.max(E.selectedIndex-1,0));else if(a.return){m("center");let b=p[E.selectedIndex];b&&O(e.getExchangesByRoute(b.id));}return}if(n==="capabilities"&&l==="center"){let b=a.ctrl?de:1;if(c==="j"||a.downArrow)T.moveBy(b,X.length,_e);else if(c==="k"||a.upArrow)T.moveBy(-b,X.length,_e);else if(a.return){let h=X[T.selectedIndex];h&&(ht(h),ft(e.getEventsByExchange(h.id,h.correlationId)),F.reset(),ce(false),g(true));}else (a.escape||a.backspace||a.delete)&&(m("nav"),T.reset());return}if(n==="exchanges"||n==="errors"){if(l==="nav")a.return&&(m("center"),O(n==="exchanges"?e.getAllExchanges():e.getFailedExchanges()));else {let b=a.ctrl?de:1;if(c==="j"||a.downArrow)T.moveBy(b,X.length,_e);else if(c==="k"||a.upArrow)T.moveBy(-b,X.length,_e);else if(a.return){let h=X[T.selectedIndex];h&&(ht(h),ft(e.getEventsByExchange(h.id,h.correlationId)),F.reset(),ce(false),g(true));}else (a.escape||a.backspace||a.delete)&&(m("nav"),T.reset());}return}});let Tt=n==="capabilities"&&l==="nav"&&!d,Ht=!Tt,D=[];return H?(D.push({key:"j/k",action:"Scroll"}),D.push({key:"Ctrl+j/k",action:"Jump 10"}),D.push({key:"Esc",action:"Back"})):l==="center"&&(D.push({key:"j/k",action:"Navigate"}),D.push({key:"Ctrl+j/k",action:"Jump 10"})),H||(l==="nav"?n==="capabilities"?(D.push({key:"j/k",action:"Navigate"}),D.push({key:"Enter",action:"Exchanges"})):D.push({key:"Enter",action:"Browse"}):!d&&!G?(D.push({key:"Enter",action:"Detail"}),D.push({key:"Esc",action:"Back"})):d&&!G?(D.push({key:"Enter",action:"Event detail"}),D.push({key:"e",action:"Snapshot"}),D.push({key:"Esc",action:"Back"})):D.push({key:"Esc",action:"Back"})),D.push({key:"1..4",action:"Switch view"}),D.push({key:"q",action:"Quit"}),jsxs(Box,{flexDirection:"column",width:s,height:r,children:[jsxs(Box,{borderStyle:"round",borderColor:"cyan",paddingX:1,width:s,children:[jsx(Text,{bold:true,color:"cyan",children:"ROUTECRAFT TUI"}),jsx(Text,{children:" "}),jsx(Text,{dimColor:true,children:"v0.4.0"})]}),jsxs(Box,{flexDirection:"row",width:s,flexGrow:1,children:[jsx(Box,{flexDirection:"column",width:Oe,children:jsxs(S,{title:"NAVIGATION",color:Tt?"cyan":"gray",width:Oe,flexGrow:1,children:[st.map((c,a)=>jsxs(Box,{flexDirection:"column",children:[a>0&&jsx(Text,{children:" "}),c.label&&jsx(Text,{dimColor:true,bold:true,children:c.label}),c.items.map(I=>jsxs(Text,{children:[jsxs(Text,{bold:n===I.key,...n===I.key?{color:"cyan"}:{},children:[n===I.key?"> ":" ",I.label]}),jsxs(Text,{dimColor:true,children:[" (",I.shortcut,")"]})]},I.key))]},c.label??a)),n==="capabilities"&&jsx(_t,{routes:p,selectedIndex:E.selectedIndex,listOffset:E.scrollOffset,visibleRows:Ze,width:Oe-6})]})}),n==="capabilities"&&!d&&jsx(Ve,{capabilityId:tt?.id??"",route:tt,exchanges:X,selectedIndex:l==="center"?T.selectedIndex:-1,scrollOffset:l==="center"?T.scrollOffset:0,width:A,height:R,color:Ht?"cyan":"gray",activity:K}),n==="capabilities"&&H&&w&&jsx(qe,{exchange:w,snapshot:Qe,width:A,height:R,scrollOffset:ze,color:"cyan"}),n==="capabilities"&&d&&w&&!G&&!H&&jsx(Xe,{exchange:w,events:he,width:A,height:R,selectedIndex:F.selectedIndex,scrollOffset:F.scrollOffset,color:"cyan"}),n==="capabilities"&&d&&G&&!H&&re&&jsx(Ne,{event:re,width:A,height:R,scrollOffset:Le,color:"cyan"}),n==="exchanges"&&!d&&jsx(Ve,{capabilityId:"All Capabilities",exchanges:X,selectedIndex:l==="center"?T.selectedIndex:-1,scrollOffset:l==="center"?T.scrollOffset:0,width:A,height:R,color:l==="center"?"cyan":"gray"}),n==="exchanges"&&H&&w&&jsx(qe,{exchange:w,snapshot:Qe,width:A,height:R,scrollOffset:ze,color:"cyan"}),n==="exchanges"&&d&&w&&!G&&!H&&jsx(Xe,{exchange:w,events:he,width:A,height:R,selectedIndex:F.selectedIndex,scrollOffset:F.scrollOffset,color:"cyan"}),n==="exchanges"&&d&&G&&!H&&re&&jsx(Ne,{event:re,width:A,height:R,scrollOffset:Le,color:"cyan"}),n==="errors"&&!d&&jsx(Ve,{capabilityId:"Failed Exchanges",exchanges:X,selectedIndex:l==="center"?T.selectedIndex:-1,scrollOffset:l==="center"?T.scrollOffset:0,width:A,height:R,color:l==="center"?"cyan":"gray"}),n==="errors"&&H&&w&&jsx(qe,{exchange:w,snapshot:Qe,width:A,height:R,scrollOffset:ze,color:"cyan"}),n==="errors"&&d&&w&&!G&&!H&&jsx(Xe,{exchange:w,events:he,width:A,height:R,selectedIndex:F.selectedIndex,scrollOffset:F.scrollOffset,color:"cyan"}),n==="errors"&&d&&G&&!H&&re&&jsx(Ne,{event:re,width:A,height:R,scrollOffset:Le,color:"cyan"}),n==="events"&&!G&&jsx(Ot,{events:Be,selectedIndex:l==="center"?te.selectedIndex:-1,scrollOffset:l==="center"?te.scrollOffset:0,width:A,height:R,color:l==="center"?"cyan":"gray"}),n==="events"&&G&&re&&jsx(Ne,{event:re,width:A,height:R,scrollOffset:Le,color:"cyan"}),jsxs(Box,{flexDirection:"column",width:ue,children:[jsxs(S,{title:"METRICS",width:ue,flexGrow:1,children:[jsx(He,{values:k,columns:ue-4,steps:[{max:0,color:"gray"},...Array.from({length:7},(c,a)=>({max:(a+1)*5,color:"green"})),...Array.from({length:7},(c,a)=>({max:35+(a+1)*10,color:"yellow"})),...Array.from({length:6},(c,a)=>({max:105+(a+1)*25,color:"red"}))],label:"Exchanges per 5s bucket"}),jsx(Text,{children:" "}),jsx(Text,{bold:true,dimColor:true,children:"THROUGHPUT"}),[["Exchanges",q(i.totalExchanges),"cyan"],["Completed",q(i.completedExchanges),"green"],["Errors",q(i.failedExchanges),i.failedExchanges>0?"red":"green"],["Dropped",q(i.droppedExchanges),i.droppedExchanges>0?"yellow":"green"],["Error Rate",`${(i.errorRate*100).toFixed(1)}%`,i.errorRate>.1?"red":"green"],["Capabilities",q(i.totalRoutes),""]].map(([c,a,I])=>jsxs(Box,{children:[jsxs(Text,{children:[c,":"]}),jsx(Box,{flexGrow:1,justifyContent:"flex-end",children:jsx(Text,{bold:true,...I?{color:I}:{},children:a})})]},c)),jsx(Text,{children:" "}),jsx(Text,{bold:true,dimColor:true,children:"LATENCY (5m)"}),[["Avg",_(i.avgDurationMs),""],["p90",_(i.p90DurationMs),""],["p95",_(i.p95DurationMs),""],["p99",_(i.p99DurationMs),""]].map(([c,a,I])=>jsxs(Box,{children:[jsxs(Text,{children:[c,":"]}),jsx(Box,{flexGrow:1,justifyContent:"flex-end",children:jsx(Text,{bold:true,...I?{color:I}:{},children:a})})]},c))]}),jsx(S,{title:"KEYMAP",width:ue,children:D.map(c=>jsxs(Box,{children:[jsx(Text,{children:c.action}),jsx(Box,{flexGrow:1,justifyContent:"flex-end",children:jsxs(Text,{color:"yellow",children:["[",c.key,"]"]})})]},c.key))})]})]})]})}async function Zn(e){let t;try{t=await a.open(e);}catch(s){let r=s instanceof Error?s.message:String(s);process.stderr.write(`Failed to open telemetry database: ${r}
|
|
3
|
+
`).map(s=>s.length>o?s.slice(0,Math.max(0,o-1))+"\u2026":s)}}function Ue({line:e}){let t=rr(e);return t.length===0?jsx(Text,{children:e}):jsx(Text,{children:t.map((o,s)=>jsx(Text,{color:nr[o.type],children:o.text},s))})}function Xe(e){try{let t=JSON.parse(e);return typeof t!="object"||t===null||Array.isArray(t)?null:t}catch{return null}}function sr(e,t){let o=new Map,s=0;o.set(t,{exchangeId:t,label:"parent",events:[]});for(let r of e){let n=Xe(r.details),u=n?String(n.exchangeId??""):"",l=!u||u===t?t:u;o.has(l)||(s++,o.set(l,{exchangeId:l,label:`child ${s}`,events:[]})),o.get(l).events.push(r);}return Array.from(o.values())}var ar=[Te(),{header:"Time",width:8,render:(e,t,o)=>{if(!e.event)return jsx(Text,{children:B("",o)});let s=" ".repeat(e.indent);return jsxs(Text,{dimColor:true,children:[s,e.event.timestamp.replace("T"," ").slice(11,19)]})}},{header:"Event",width:"flex",render:e=>e.event?jsx(Text,{color:"cyan",children:e.event.eventName}):jsx(Text,{})},...Je(e=>e.event)];function We({exchange:e,events:t,width:o,height:s,scrollOffset:r,selectedIndex:n=-1,color:u="gray"}){let l=e.error?2:0,m=Math.max(s-9-l,3),d=sr(t,e.id),g=d.length>1,p=[];for(let i of d){g&&p.push({type:"header",text:`${i.label} (${i.exchangeId.slice(0,8)}) - ${i.events.length} events`,indent:0});for(let y of i.events)p.push({type:"event",event:y,indent:g?2:0});}let f=g?`EXCHANGE FLOW (${d.length} exchanges, ${t.length} events)`:`RELATED EVENTS (${t.length})`;return jsxs(Box,{flexDirection:"column",width:o,flexGrow:1,children:[jsxs(S,{width:o,children:[jsxs(Text,{children:["Capability:"," ",jsx(Text,{bold:true,color:"cyan",children:e.routeId})]}),jsxs(Text,{children:["Exchange:"," ",jsx(Text,{bold:true,color:"cyan",children:ge(e.id,o-14)})]}),jsxs(Text,{children:["Status:"," ",jsx(Text,{bold:true,color:oe(e.status),children:e.status}),e.durationMs!==null&&jsxs(Text,{children:[" ","Duration:"," ",jsx(Text,{bold:true,children:_(e.durationMs)})]})," ","Started: ",jsx(Text,{bold:true,children:e.startedAt}),e.completedAt&&jsxs(Text,{children:[" ","Completed: ",jsx(Text,{bold:true,children:e.completedAt})]})]}),e.error&&e.status==="failed"&&jsxs(Fragment,{children:[jsx(Text,{children:" "}),jsxs(Text,{color:"red",children:["Error: ",e.error]})]}),e.error&&e.status==="dropped"&&jsxs(Fragment,{children:[jsx(Text,{children:" "}),jsxs(Text,{color:"yellow",children:["Reason: ",e.error]})]})]}),jsx(S,{title:f,width:o,flexGrow:1,color:u,children:jsx(Ee,{columns:ar,data:p,rowKey:(i,y)=>i.type==="header"?`h-${y}`:`${i.event?.id??i.event?.timestamp}-${y}`,selectedIndex:n,scrollOffset:r,visibleRows:m,emptyMessage:"No related events found",renderFullRow:i=>i.type==="header"?jsx(Text,{bold:true,color:"yellow",children:i.text}):void 0})})]})}var ir=[Te(),{header:"Time",width:19,render:(e,t,o)=>jsx(Text,{...t?{color:"cyan"}:{},bold:t,children:B(e.timestamp.replace("T"," ").slice(0,19),o)})},{header:"Event",width:"flex",render:e=>jsx(Text,{color:"cyan",children:e.eventName})},...Je(e=>e)];function _t({events:e,selectedIndex:t,scrollOffset:o,width:s,height:r,color:n="gray"}){let u=Math.max(r-6,5);return jsx(S,{title:"EVENTS",subtitle:jsxs(Text,{dimColor:true,children:["(",e.length," total)"]}),width:s,flexGrow:1,color:n,children:jsx(Ee,{columns:ir,data:e,rowKey:(l,m)=>l.id!==void 0?String(l.id):`${l.timestamp}-${m}`,selectedIndex:t,scrollOffset:o,visibleRows:u,emptyMessage:"No events recorded yet."})})}function Ne({event:e,width:t,height:o,scrollOffset:s,color:r="cyan"}){let n=Xe(e.details),u=n&&typeof n.routeId=="string"?n.routeId:void 0,l=n&&typeof n.exchangeId=="string"?n.exchangeId:void 0,d=2+(1+(u?1:0)+(l?1:0)+2),g=5,p=Math.max(t-6,20),f=Ae(e.details,p),i=Math.max(o-d-g,3),y=f.slice(s,s+i);return jsxs(Box,{flexDirection:"column",width:t,flexGrow:1,children:[jsxs(S,{width:t,children:[jsxs(Text,{children:["Context:"," ",jsx(Text,{bold:true,color:"cyan",children:e.contextId})]}),u&&jsxs(Text,{children:["Capability:"," ",jsx(Text,{bold:true,color:"cyan",children:u})]}),l&&jsxs(Text,{children:["Exchange:"," ",jsx(Text,{bold:true,color:"cyan",children:l})]}),jsxs(Text,{children:["Event:"," ",jsx(Text,{bold:true,color:"cyan",children:e.eventName})]}),jsxs(Text,{children:["Time: ",jsx(Text,{bold:true,children:e.timestamp.replace("T"," ")})]})]}),jsxs(S,{title:"DETAILS",subtitle:jsxs(Text,{dimColor:true,children:["(",s+1,"-",Math.min(s+i,f.length),"/",f.length," lines)"]}),width:t,flexGrow:1,color:r,children:[y.map((k,L)=>jsx(Ue,{line:k},s+L)),y.length<i&&Array.from({length:i-y.length}).map((k,L)=>jsx(Text,{children:" "},`pad-${L}`))]})]})}function ur(e,t){let o=[];return o.push("HEADERS"),o.push(...Ae(e.headers,t)),o.push(""),o.push("BODY"),e.body!==null?(o.push(...Ae(e.body,t)),e.truncated&&(o.push(""),o.push("[Body truncated to configured limit]"))):o.push("[null]"),o}function qe({exchange:e,snapshot:t,width:o,height:s,scrollOffset:r,color:n="cyan"}){let m=Math.max(o-6,20),d=Math.max(s-5-5,3),p=t!==null?ur(t,m):["[Snapshots not captured]","","Enable with:"," telemetry({ sqlite: { captureSnapshots: true } })"],f=p.slice(r,r+d);return jsxs(Box,{flexDirection:"column",width:o,flexGrow:1,children:[jsxs(S,{width:o,children:[jsxs(Text,{children:["Capability:"," ",jsx(Text,{bold:true,color:"cyan",children:e.routeId})]}),jsxs(Text,{children:["Exchange:"," ",jsx(Text,{bold:true,color:"cyan",children:ge(e.id,o-14)})]}),jsxs(Text,{children:["Status:"," ",jsx(Text,{bold:true,color:oe(e.status),children:e.status}),e.durationMs!==null&&jsxs(Text,{children:[" ","Duration:"," ",jsx(Text,{bold:true,children:_(e.durationMs)})]})]})]}),jsxs(S,{title:"EXCHANGE SNAPSHOT",subtitle:jsxs(Text,{dimColor:true,children:["(",r+1,"-",Math.min(r+d,p.length),"/",p.length," lines)"]}),width:o,flexGrow:1,color:n,children:[f.map((i,y)=>i==="HEADERS"||i==="BODY"?jsx(Text,{bold:true,color:"yellow",children:i},r+y):i.startsWith("[")?jsx(Text,{dimColor:true,children:i},r+y):jsx(Ue,{line:i},r+y)),f.length<d&&Array.from({length:d-f.length}).map((i,y)=>jsx(Text,{children:" "},`pad-${y}`))]})]})}function mr(e){return e.failedExchanges>0?"red":e.totalExchanges>0?"green":"yellow"}function Pt({routes:e,selectedIndex:t,listOffset:o,visibleRows:s,width:r}){let n=o;return jsxs(Fragment,{children:[jsx(Text,{children:" "}),jsx(Text,{bold:true,dimColor:true,children:"\u2500".repeat(r+2)}),e.length===0?jsx(Text,{dimColor:true,children:"No capabilities"}):e.slice(n,n+s).map((u,l)=>{let m=n+l;return jsxs(Text,{wrap:"truncate",children:[jsx(Text,{...m===t?{color:"cyan"}:{},bold:m===t,children:m===t?"> ":" "}),jsx(Text,{color:mr(u),children:"\u25CF "}),jsx(Text,{...m===t?{color:"cyan"}:{},bold:m===t,children:ge(u.id,r-2)})]},u.id)}),e.length>s&&jsxs(Text,{dimColor:true,children:[t+1,"/",e.length]})]})}function Er({db:e}){let{exit:t}=useApp(),{stdout:o}=useStdout(),s=o?.columns??80,r=o?.rows??24,[n,u]=useState("capabilities"),[l,m]=useState("nav"),[d,g]=useState(false),[p,f]=useState([]),[i,y]=useState({totalRoutes:0,totalExchanges:0,completedExchanges:0,failedExchanges:0,droppedExchanges:0,errorRate:0,avgDurationMs:null,p90DurationMs:null,p95DurationMs:null,p99DurationMs:null}),[k,L]=useState([]),[K,X]=useState(void 0),E=Ce(),[W,O]=useState([]),T=Ce(),[w,ht]=useState(void 0),[he,ft]=useState([]),[Be,gt]=useState([]),te=Ce(),F=Ce(),[re,bt]=useState(void 0),[G,ce]=useState(false),[Le,ne]=useState(0),[H,Ye]=useState(false),[ze,ke]=useState(0),[Qe,jt]=useState(null),fe=50,de=10,Oe=Math.max(Math.min(Math.floor(s*.18),26),16),ue=Math.max(Math.min(Math.floor(s*.22),30),20),N=Math.max(s-Oe-ue,30),R=Math.max(r-5,10),Ze=Math.max(R-10,3),et=useCallback(()=>{try{let c=e.getRouteSummary();f(c),y(e.getMetrics());let a=ue-4;L(e.getLiveTrafficBuckets(a,5));let I=N-4,b=c[E.selectedIndex];if(b&&X(e.getSingleRouteActivity(b.id,I,5)),l==="center")return;if(n==="capabilities"){let h=c[E.selectedIndex];h&&O(e.getExchangesByRoute(h.id,fe));}else n==="exchanges"?O(e.getAllExchanges(fe)):n==="errors"?O(e.getFailedExchanges(fe)):n==="events"&>(e.getRecentEvents({limit:200}));}catch{}},[e,n,E.selectedIndex,ue,N,l]);useEffect(()=>{et();let c=setInterval(et,2e3);return ()=>{clearInterval(c);}},[et]);let yt=useCallback(c=>{E.moveTo(c,p.length,Ze),T.reset();let a=p[c];a&&O(e.getExchangesByRoute(a.id,fe));},[e,p,E,T,Ze]),Gt=useCallback(c=>{u(c),g(false),ce(false),Ye(false),m("nav"),T.reset(),c==="exchanges"?O(e.getAllExchanges(fe)):c==="errors"?O(e.getFailedExchanges(fe)):c==="events"&&(te.reset(),gt(e.getRecentEvents({limit:200})));},[e,T,te]),tt=p[E.selectedIndex],Ft=Math.ceil((De.length-1)/4),Ht=tt?2+Ft+2:0,_e=Math.max(R-6-Ht,3),Et=Math.max(R-6,5);useInput((c,a)=>{if(c==="q"){e.close(),t();return}let I=Dt.find(b=>b.shortcut===c);if(I){Gt(I.key);return}if(n==="events"){if(G){let b=a.ctrl?de:1;c==="j"||a.downArrow?ne(h=>h+b):c==="k"||a.upArrow?ne(h=>Math.max(h-b,0)):(a.escape||a.backspace||a.delete)&&(ce(false),ne(0));return}if(l==="nav")a.return&&m("center");else {let b=a.ctrl?de:1;if(c==="j"||a.downArrow)te.moveBy(b,Be.length,Et);else if(c==="k"||a.upArrow)te.moveBy(-b,Be.length,Et);else if(a.return){let h=Be[te.selectedIndex];h&&(bt(h),ne(0),ce(true));}else (a.escape||a.backspace||a.delete)&&(m("nav"),te.reset());}return}if(H){let b=a.ctrl?de:1;c==="j"||a.downArrow?ke(h=>h+b):c==="k"||a.upArrow?ke(h=>Math.max(h-b,0)):(a.escape||a.backspace||a.delete)&&(Ye(false),ke(0));return}if(d&&!G){let b=w?.error?2:0,h=Math.max(R-9-b,3),Jt=new Set(he.map(me=>{try{let Re=JSON.parse(me.details);return String(Re.exchangeId??"")}catch{return ""}})),vt=Math.max(Jt.size,1),Vt=vt>1?vt:0,wt=he.length+Vt,Rt=a.ctrl?de:1;if(c==="j"||a.downArrow)F.moveBy(Rt,wt,h);else if(c==="k"||a.upArrow)F.moveBy(-Rt,wt,h);else if(a.return){let me=0,Re,Pe=w?.id??"",xe=new Map;xe.set(Pe,[]);for(let Se of he)try{let rt=JSON.parse(Se.details),nt=String(rt.exchangeId??""),ot=!nt||nt===Pe?Pe:nt;xe.has(ot)||xe.set(ot,[]),xe.get(ot).push(Se);}catch{xe.get(Pe).push(Se);}let Ut=xe.size>1;for(let[,Se]of xe){Ut&&me++;for(let rt of Se)me===F.selectedIndex&&(Re=rt),me++;}Re&&(bt(Re),ne(0),ce(true));}else if(c==="e"&&w){let me=e.getExchangeSnapshot(w.id);jt(me),ke(0),Ye(true);}else (a.escape||a.backspace||a.delete)&&(F.reset(),g(false));return}if(d&&G){let b=a.ctrl?de:1;c==="j"||a.downArrow?ne(h=>h+b):c==="k"||a.upArrow?ne(h=>Math.max(h-b,0)):(a.escape||a.backspace||a.delete)&&(ce(false),ne(0));return}if(n==="capabilities"&&l==="nav"){if(c==="j"||a.downArrow)yt(Math.min(E.selectedIndex+1,p.length-1));else if(c==="k"||a.upArrow)yt(Math.max(E.selectedIndex-1,0));else if(a.return){m("center");let b=p[E.selectedIndex];b&&O(e.getExchangesByRoute(b.id));}return}if(n==="capabilities"&&l==="center"){let b=a.ctrl?de:1;if(c==="j"||a.downArrow)T.moveBy(b,W.length,_e);else if(c==="k"||a.upArrow)T.moveBy(-b,W.length,_e);else if(a.return){let h=W[T.selectedIndex];h&&(ht(h),ft(e.getEventsByExchange(h.id,h.correlationId)),F.reset(),ce(false),g(true));}else (a.escape||a.backspace||a.delete)&&(m("nav"),T.reset());return}if(n==="exchanges"||n==="errors"){if(l==="nav")a.return&&(m("center"),O(n==="exchanges"?e.getAllExchanges():e.getFailedExchanges()));else {let b=a.ctrl?de:1;if(c==="j"||a.downArrow)T.moveBy(b,W.length,_e);else if(c==="k"||a.upArrow)T.moveBy(-b,W.length,_e);else if(a.return){let h=W[T.selectedIndex];h&&(ht(h),ft(e.getEventsByExchange(h.id,h.correlationId)),F.reset(),ce(false),g(true));}else (a.escape||a.backspace||a.delete)&&(m("nav"),T.reset());}return}});let Tt=n==="capabilities"&&l==="nav"&&!d,$t=!Tt,D=[];return H?(D.push({key:"j/k",action:"Scroll"}),D.push({key:"Ctrl+j/k",action:"Jump 10"}),D.push({key:"Esc",action:"Back"})):l==="center"&&(D.push({key:"j/k",action:"Navigate"}),D.push({key:"Ctrl+j/k",action:"Jump 10"})),H||(l==="nav"?n==="capabilities"?(D.push({key:"j/k",action:"Navigate"}),D.push({key:"Enter",action:"Exchanges"})):D.push({key:"Enter",action:"Browse"}):!d&&!G?(D.push({key:"Enter",action:"Detail"}),D.push({key:"Esc",action:"Back"})):d&&!G?(D.push({key:"Enter",action:"Event detail"}),D.push({key:"e",action:"Snapshot"}),D.push({key:"Esc",action:"Back"})):D.push({key:"Esc",action:"Back"})),D.push({key:"1..4",action:"Switch view"}),D.push({key:"q",action:"Quit"}),jsxs(Box,{flexDirection:"column",width:s,height:r,children:[jsxs(Box,{borderStyle:"round",borderColor:"cyan",paddingX:1,width:s,children:[jsx(Text,{bold:true,color:"cyan",children:"ROUTECRAFT TUI"}),jsx(Text,{children:" "}),jsxs(Text,{dimColor:true,children:["v",a$1]})]}),jsxs(Box,{flexDirection:"row",width:s,flexGrow:1,children:[jsx(Box,{flexDirection:"column",width:Oe,children:jsxs(S,{title:"NAVIGATION",color:Tt?"cyan":"gray",width:Oe,flexGrow:1,children:[st.map((c,a)=>jsxs(Box,{flexDirection:"column",children:[a>0&&jsx(Text,{children:" "}),c.label&&jsx(Text,{dimColor:true,bold:true,children:c.label}),c.items.map(I=>jsxs(Text,{children:[jsxs(Text,{bold:n===I.key,...n===I.key?{color:"cyan"}:{},children:[n===I.key?"> ":" ",I.label]}),jsxs(Text,{dimColor:true,children:[" (",I.shortcut,")"]})]},I.key))]},c.label??a)),n==="capabilities"&&jsx(Pt,{routes:p,selectedIndex:E.selectedIndex,listOffset:E.scrollOffset,visibleRows:Ze,width:Oe-6})]})}),n==="capabilities"&&!d&&jsx(Ve,{capabilityId:tt?.id??"",route:tt,exchanges:W,selectedIndex:l==="center"?T.selectedIndex:-1,scrollOffset:l==="center"?T.scrollOffset:0,width:N,height:R,color:$t?"cyan":"gray",activity:K}),n==="capabilities"&&H&&w&&jsx(qe,{exchange:w,snapshot:Qe,width:N,height:R,scrollOffset:ze,color:"cyan"}),n==="capabilities"&&d&&w&&!G&&!H&&jsx(We,{exchange:w,events:he,width:N,height:R,selectedIndex:F.selectedIndex,scrollOffset:F.scrollOffset,color:"cyan"}),n==="capabilities"&&d&&G&&!H&&re&&jsx(Ne,{event:re,width:N,height:R,scrollOffset:Le,color:"cyan"}),n==="exchanges"&&!d&&jsx(Ve,{capabilityId:"All Capabilities",exchanges:W,selectedIndex:l==="center"?T.selectedIndex:-1,scrollOffset:l==="center"?T.scrollOffset:0,width:N,height:R,color:l==="center"?"cyan":"gray"}),n==="exchanges"&&H&&w&&jsx(qe,{exchange:w,snapshot:Qe,width:N,height:R,scrollOffset:ze,color:"cyan"}),n==="exchanges"&&d&&w&&!G&&!H&&jsx(We,{exchange:w,events:he,width:N,height:R,selectedIndex:F.selectedIndex,scrollOffset:F.scrollOffset,color:"cyan"}),n==="exchanges"&&d&&G&&!H&&re&&jsx(Ne,{event:re,width:N,height:R,scrollOffset:Le,color:"cyan"}),n==="errors"&&!d&&jsx(Ve,{capabilityId:"Failed Exchanges",exchanges:W,selectedIndex:l==="center"?T.selectedIndex:-1,scrollOffset:l==="center"?T.scrollOffset:0,width:N,height:R,color:l==="center"?"cyan":"gray"}),n==="errors"&&H&&w&&jsx(qe,{exchange:w,snapshot:Qe,width:N,height:R,scrollOffset:ze,color:"cyan"}),n==="errors"&&d&&w&&!G&&!H&&jsx(We,{exchange:w,events:he,width:N,height:R,selectedIndex:F.selectedIndex,scrollOffset:F.scrollOffset,color:"cyan"}),n==="errors"&&d&&G&&!H&&re&&jsx(Ne,{event:re,width:N,height:R,scrollOffset:Le,color:"cyan"}),n==="events"&&!G&&jsx(_t,{events:Be,selectedIndex:l==="center"?te.selectedIndex:-1,scrollOffset:l==="center"?te.scrollOffset:0,width:N,height:R,color:l==="center"?"cyan":"gray"}),n==="events"&&G&&re&&jsx(Ne,{event:re,width:N,height:R,scrollOffset:Le,color:"cyan"}),jsxs(Box,{flexDirection:"column",width:ue,children:[jsxs(S,{title:"METRICS",width:ue,flexGrow:1,children:[jsx(He,{values:k,columns:ue-4,steps:[{max:0,color:"gray"},...Array.from({length:7},(c,a)=>({max:(a+1)*5,color:"green"})),...Array.from({length:7},(c,a)=>({max:35+(a+1)*10,color:"yellow"})),...Array.from({length:6},(c,a)=>({max:105+(a+1)*25,color:"red"}))],label:"Exchanges per 5s bucket"}),jsx(Text,{children:" "}),jsx(Text,{bold:true,dimColor:true,children:"THROUGHPUT"}),[["Exchanges",q(i.totalExchanges),"cyan"],["Completed",q(i.completedExchanges),"green"],["Errors",q(i.failedExchanges),i.failedExchanges>0?"red":"green"],["Dropped",q(i.droppedExchanges),i.droppedExchanges>0?"yellow":"green"],["Error Rate",`${(i.errorRate*100).toFixed(1)}%`,i.errorRate>.1?"red":"green"],["Capabilities",q(i.totalRoutes),""]].map(([c,a,I])=>jsxs(Box,{children:[jsxs(Text,{children:[c,":"]}),jsx(Box,{flexGrow:1,justifyContent:"flex-end",children:jsx(Text,{bold:true,...I?{color:I}:{},children:a})})]},c)),jsx(Text,{children:" "}),jsx(Text,{bold:true,dimColor:true,children:"LATENCY (5m)"}),[["Avg",_(i.avgDurationMs),""],["p90",_(i.p90DurationMs),""],["p95",_(i.p95DurationMs),""],["p99",_(i.p99DurationMs),""]].map(([c,a,I])=>jsxs(Box,{children:[jsxs(Text,{children:[c,":"]}),jsx(Box,{flexGrow:1,justifyContent:"flex-end",children:jsx(Text,{bold:true,...I?{color:I}:{},children:a})})]},c))]}),jsx(S,{title:"KEYMAP",width:ue,children:D.map(c=>jsxs(Box,{children:[jsx(Text,{children:c.action}),jsx(Box,{flexGrow:1,justifyContent:"flex-end",children:jsxs(Text,{color:"yellow",children:["[",c.key,"]"]})})]},c.key))})]})]})]})}async function to(e){let t;try{t=await a.open(e);}catch(s){let r=s instanceof Error?s.message:String(s);process.stderr.write(`Failed to open telemetry database: ${r}
|
|
4
4
|
`),process.stderr.write(`Path: ${e}
|
|
5
5
|
`),process.stderr.write(`Make sure you have a running Routecraft context with telemetry() enabled.
|
|
6
|
-
`),process.exit(1);}await render(jsx(
|
|
6
|
+
`),process.exit(1);}await render(jsx(Er,{db:t})).waitUntilExit();}export{to as renderTui};//# sourceMappingURL=app.js.map
|
|
7
7
|
//# sourceMappingURL=app.js.map
|