fbi-proxy 1.6.2 → 1.8.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/dist/cli.js CHANGED
@@ -2365,14 +2365,14 @@ function usage(yargs, shim2) {
2365
2365
  const logger = yargs.getInternalMethods().getLoggerInstance();
2366
2366
  if (fails.length) {
2367
2367
  for (let i = fails.length - 1;i >= 0; --i) {
2368
- const fail = fails[i];
2369
- if (isBoolean(fail)) {
2368
+ const fail2 = fails[i];
2369
+ if (isBoolean(fail2)) {
2370
2370
  if (err)
2371
2371
  throw err;
2372
2372
  else if (msg)
2373
2373
  throw Error(msg);
2374
2374
  } else {
2375
- fail(msg, err, self);
2375
+ fail2(msg, err, self);
2376
2376
  }
2377
2377
  }
2378
2378
  } else {
@@ -2429,7 +2429,7 @@ function usage(yargs, shim2) {
2429
2429
  examples.push([cmd, description || ""]);
2430
2430
  };
2431
2431
  let commands = [];
2432
- self.command = function command(cmd, description, isDefault, aliases, deprecated = false) {
2432
+ self.command = function command2(cmd, description, isDefault, aliases, deprecated = false) {
2433
2433
  if (isDefault) {
2434
2434
  commands = commands.map((cmdArray) => {
2435
2435
  cmdArray[2] = false;
@@ -5009,6 +5009,8 @@ import { spawn } from "child_process";
5009
5009
 
5010
5010
  // node_modules/from-node-stream/dist/index.js
5011
5011
  function fromReadable(i) {
5012
+ if (i instanceof ReadableStream)
5013
+ return i;
5012
5014
  return new ReadableStream({
5013
5015
  start: (c) => {
5014
5016
  i.on("data", (data) => c.enqueue(data));
@@ -5232,14 +5234,14 @@ await yargs_default(hideBin(process.argv)).option("dev", {
5232
5234
  description: "Run in development mode"
5233
5235
  }).help().argv;
5234
5236
  console.log("Preparing Binaries");
5235
- var FBIPROXY_PORT = String(await getPorts({ port: 2432 }));
5237
+ var FBI_PROXY_PORT = process.env.FBI_PROXY_PORT || String(await getPorts({ port: 2432 }));
5236
5238
  var proxyProcess = await hotMemo(async () => {
5237
5239
  const proxy = await getFbiProxyBinary();
5238
5240
  console.log("Starting Rust proxy server");
5239
5241
  const p = $2.opt({
5240
5242
  env: {
5241
5243
  ...process.env,
5242
- FBIPROXY_PORT
5244
+ FBI_PROXY_PORT
5243
5245
  }
5244
5246
  })`${proxy}`.process;
5245
5247
  p.on("exit", (code) => {
@@ -5250,7 +5252,7 @@ var proxyProcess = await hotMemo(async () => {
5250
5252
  });
5251
5253
  console.log("All services started successfully!");
5252
5254
  console.log(`Proxy server PID: ${proxyProcess.pid}`);
5253
- console.log(`Proxy server running on port: ${FBIPROXY_PORT}`);
5255
+ console.log(`Proxy server running on port: ${FBI_PROXY_PORT}`);
5254
5256
  var exit = () => {
5255
5257
  console.log("Shutting down...");
5256
5258
  proxyProcess?.kill?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fbi-proxy",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "description": "FBI-Proxy provides easy HTTPS access to your local services with intelligent domain routing",
5
5
  "keywords": [
6
6
  "development-tools",
@@ -40,31 +40,46 @@
40
40
  "lint": "prettier --check .",
41
41
  "prepare": "husky",
42
42
  "start": "bun ts/cli.ts",
43
- "start:rs": "./target/release/fbi-proxy"
43
+ "start:rs": "./target/release/fbi-proxy",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest",
46
+ "test:ui": "vitest --ui",
47
+ "test:e2e": "vitest run e2e",
48
+ "test:e2e:watch": "vitest e2e",
49
+ "test:coverage": "vitest run --coverage"
44
50
  },
45
51
  "dependencies": {
46
- "execa": "^9.6.0",
47
- "from-node-stream": "^0.1.0",
52
+ "execa": "^9.6.1",
53
+ "from-node-stream": "^0.1.2",
48
54
  "get-port": "^7.1.0",
49
55
  "hot-memo": "^1.1.1",
50
- "sflow": "^1.24.5",
56
+ "sflow": "^1.27.0",
51
57
  "tsa-composer": "^1.0.0",
52
58
  "yargs": "^17.7.2"
53
59
  },
54
60
  "devDependencies": {
61
+ "@playwright/test": "^1.58.2",
55
62
  "@semantic-release/changelog": "^6.0.3",
56
63
  "@semantic-release/git": "^10.0.1",
57
- "@semantic-release/github": "^9.2.0",
64
+ "@semantic-release/github": "^9.2.6",
65
+ "@semantic-release/npm": "^13.1.3",
58
66
  "@types/bun": "latest",
59
67
  "@types/minimist": "^1.2.5",
68
+ "@types/ws": "^8.18.1",
69
+ "@vitest/ui": "^3.2.4",
60
70
  "husky": "^9.1.7",
61
- "lint-staged": "^16.1.2",
62
- "prettier": "^3.6.2",
63
- "semantic-release": "^22.0.0",
64
- "semantic-release-cargo": "^2.4.0"
71
+ "lint-staged": "^16.2.7",
72
+ "node-fetch": "^3.3.2",
73
+ "playwright": "^1.58.2",
74
+ "prettier": "^3.8.1",
75
+ "semantic-release": "^24.0.0",
76
+ "semantic-release-cargo": "^2.4.2",
77
+ "vite": "^7.3.1",
78
+ "vitest": "^3.2.4",
79
+ "ws": "^8.19.0"
65
80
  },
66
81
  "engines": {
67
- "node": ">=18.0.0"
82
+ "node": ">=22.14.0"
68
83
  },
69
84
  "lint-staged": {
70
85
  "*.{ts,js}": [
@@ -74,5 +89,8 @@
74
89
  "bun --bun prettier --write"
75
90
  ]
76
91
  },
77
- "trustedDependencies": []
92
+ "trustedDependencies": [],
93
+ "overrides": {
94
+ "@semantic-release/npm": "^13.1.3"
95
+ }
78
96
  }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/rs/fbi-proxy.rs CHANGED
@@ -1,20 +1,30 @@
1
1
  use clap::{Arg, Command};
2
2
  use futures_util::{SinkExt, StreamExt};
3
+ use http_body_util::{BodyExt, Full};
4
+ use hyper::body::{Bytes, Incoming};
3
5
  use hyper::header::{HeaderValue, HOST};
4
- use hyper::service::{make_service_fn, service_fn};
5
- use hyper::{Body, Client, Request, Response, Server, StatusCode, Uri};
6
+ use hyper::server::conn::http1;
7
+ use hyper::service::service_fn;
8
+ use hyper::{Method, Request, Response, StatusCode, Uri};
6
9
  use hyper_tungstenite::{HyperWebsocket, WebSocketStream};
10
+ use hyper_util::client::legacy::{Client, connect::HttpConnector};
11
+ use hyper_util::rt::TokioIo;
7
12
  use log::{error, info};
8
13
  use regex::Regex;
9
14
  use std::convert::Infallible;
10
15
  use std::net::SocketAddr;
11
16
  use std::sync::Arc;
17
+ use std::time::Duration;
18
+ use tokio::net::{TcpListener, TcpStream};
19
+ use tokio::time::timeout;
20
+ use tokio::io::copy_bidirectional;
12
21
  use tokio_tungstenite::connect_async;
13
22
 
14
23
  type BoxError = Box<dyn std::error::Error + Send + Sync>;
24
+ type BoxBody = http_body_util::combinators::BoxBody<Bytes, hyper::Error>;
15
25
 
16
26
  pub struct FBIProxy {
17
- client: Client<hyper::client::HttpConnector>,
27
+ client: Client<HttpConnector, BoxBody>,
18
28
  number_regex: Regex,
19
29
  domain_filter: Option<String>,
20
30
  }
@@ -51,8 +61,15 @@ for subdomains
51
61
  */
52
62
  impl FBIProxy {
53
63
  pub fn new(domain_filter: Option<String>) -> Self {
64
+ let mut connector = HttpConnector::new();
65
+ // Set connection timeout to 5 seconds to avoid hanging on invalid hosts
66
+ connector.set_connect_timeout(Some(Duration::from_secs(3)));
67
+
68
+ let client = Client::builder(hyper_util::rt::TokioExecutor::new())
69
+ .build(connector);
70
+
54
71
  Self {
55
- client: Client::new(),
72
+ client,
56
73
  number_regex: Regex::new(r"^\d+$").unwrap(),
57
74
  domain_filter,
58
75
  }
@@ -131,7 +148,7 @@ impl FBIProxy {
131
148
  Some((format!("{}:80", host), host.to_string()))
132
149
  }
133
150
 
134
- pub async fn handle_request(&self, mut req: Request<Body>) -> Result<Response<Body>, BoxError> {
151
+ pub async fn handle_request(&self, req: Request<Incoming>) -> Result<Response<BoxBody>, BoxError> {
135
152
  // Extract host from headers and process according to rules
136
153
  let host_header = req
137
154
  .headers()
@@ -157,13 +174,133 @@ impl FBIProxy {
157
174
  );
158
175
  return Ok(Response::builder()
159
176
  .status(StatusCode::BAD_GATEWAY)
160
- .body(Body::from("Bad Gateway: Host not allowed"))?);
177
+ .body(Full::new(Bytes::from("Bad Gateway: Host not allowed")).map_err(|e| match e {}).boxed())?);
161
178
  }
162
179
  };
163
180
 
164
181
  let method = req.method().clone();
165
182
  let original_uri = req.uri().clone();
166
183
 
184
+ // Handle HTTP CONNECT tunneling (used by browsers for WebSocket/HTTPS through proxy)
185
+ if method == Method::CONNECT {
186
+ // For CONNECT, the URI contains the target authority (host:port)
187
+ // Parse the target from the URI
188
+ let connect_target = if let Some(authority) = req.uri().authority() {
189
+ authority.to_string()
190
+ } else {
191
+ // Fallback to parsed host
192
+ target_host.clone()
193
+ };
194
+
195
+ // Apply domain filtering to CONNECT target
196
+ let connect_host = connect_target.split(':').next().unwrap_or(&connect_target);
197
+ if let Some(ref domain) = self.domain_filter {
198
+ if !domain.is_empty() && !connect_host.ends_with(domain) {
199
+ info!(
200
+ "CONNECT {} => REJECTED{} 502",
201
+ host_header,
202
+ original_uri
203
+ );
204
+ return Ok(Response::builder()
205
+ .status(StatusCode::BAD_GATEWAY)
206
+ .body(Full::new(Bytes::from("Bad Gateway: Host not allowed")).map_err(|e| match e {}).boxed())?);
207
+ }
208
+ }
209
+
210
+ // Parse the connect target for routing
211
+ let tunnel_target = if let Some(ref domain) = self.domain_filter {
212
+ if !domain.is_empty() && connect_host.ends_with(domain) {
213
+ // Strip domain and apply routing rules
214
+ let prefix_len = connect_host.len() - domain.len();
215
+ let stripped = if prefix_len > 0 && connect_host.chars().nth(prefix_len - 1) == Some('.') {
216
+ &connect_host[..prefix_len - 1]
217
+ } else if prefix_len == 0 {
218
+ "localhost"
219
+ } else {
220
+ connect_host
221
+ };
222
+
223
+ // Apply number rule: if stripped is numeric, route to localhost:port
224
+ if self.number_regex.is_match(stripped) {
225
+ // Get the port from the connect target
226
+ let _port = connect_target.split(':').nth(1).unwrap_or("80");
227
+ format!("localhost:{}", stripped)
228
+ } else {
229
+ connect_target.clone()
230
+ }
231
+ } else {
232
+ connect_target.clone()
233
+ }
234
+ } else {
235
+ connect_target.clone()
236
+ };
237
+
238
+ info!(
239
+ "CONNECT {}@{}{} tunneling",
240
+ host_header,
241
+ tunnel_target,
242
+ original_uri
243
+ );
244
+
245
+ // Connect to upstream with timeout
246
+ let connect_result = timeout(
247
+ Duration::from_secs(3),
248
+ TcpStream::connect(&tunnel_target)
249
+ ).await;
250
+
251
+ match connect_result {
252
+ Ok(Ok(upstream)) => {
253
+ // Spawn a task to handle the tunnel
254
+ tokio::spawn(async move {
255
+ // The upgrade happens after we return the response
256
+ // We need to use hyper's upgrade mechanism
257
+ match hyper::upgrade::on(req).await {
258
+ Ok(upgraded) => {
259
+ let mut upgraded = TokioIo::new(upgraded);
260
+ let mut upstream = upstream;
261
+
262
+ // Bidirectional copy
263
+ if let Err(e) = copy_bidirectional(&mut upgraded, &mut upstream).await {
264
+ error!("Tunnel error: {}", e);
265
+ }
266
+ }
267
+ Err(e) => {
268
+ error!("Upgrade error: {}", e);
269
+ }
270
+ }
271
+ });
272
+
273
+ // Return 200 Connection Established
274
+ return Ok(Response::builder()
275
+ .status(StatusCode::OK)
276
+ .body(Full::new(Bytes::new()).map_err(|e| match e {}).boxed())?);
277
+ }
278
+ Ok(Err(e)) => {
279
+ error!(
280
+ "CONNECT {}@{}{} 502 ({})",
281
+ host_header,
282
+ tunnel_target,
283
+ original_uri,
284
+ e
285
+ );
286
+ return Ok(Response::builder()
287
+ .status(StatusCode::BAD_GATEWAY)
288
+ .body(Full::new(Bytes::from("FBIPROXY CONNECT ERROR")).map_err(|e| match e {}).boxed())?);
289
+ }
290
+ Err(_) => {
291
+ error!(
292
+ "CONNECT {}@{}{} 502 (connection timeout)",
293
+ host_header,
294
+ tunnel_target,
295
+ original_uri
296
+ );
297
+ return Ok(Response::builder()
298
+ .status(StatusCode::BAD_GATEWAY)
299
+ .body(Full::new(Bytes::from("FBIPROXY CONNECT TIMEOUT")).map_err(|e| match e {}).boxed())?);
300
+ }
301
+ }
302
+ }
303
+
167
304
  // Handle WebSocket upgrade requests
168
305
  if hyper_tungstenite::is_upgrade_request(&req) {
169
306
  return self
@@ -180,15 +317,26 @@ impl FBIProxy {
180
317
  );
181
318
  let target_uri: Uri = target_url.parse()?;
182
319
 
320
+ // Convert incoming body to a format the client can use
321
+ let (mut parts, incoming_body) = req.into_parts();
322
+ let body = incoming_body.map_err(|e| e).boxed();
323
+
183
324
  // Update request URI and headers
184
- *req.uri_mut() = target_uri;
185
- req.headers_mut()
186
- .insert(HOST, HeaderValue::from_str(&new_host)?);
325
+ parts.uri = target_uri;
326
+ parts.headers.insert(HOST, HeaderValue::from_str(&new_host)?);
187
327
  // Preserve content-encoding header to maintain compression
188
328
 
189
- // Forward the request
190
- match self.client.request(req).await {
191
- Ok(response) => {
329
+ // Rebuild the request with the converted body
330
+ let new_req = Request::from_parts(parts, body);
331
+
332
+ // Forward the request with timeout
333
+ let request_result = timeout(
334
+ Duration::from_secs(3),
335
+ self.client.request(new_req)
336
+ ).await;
337
+
338
+ match request_result {
339
+ Ok(Ok(response)) => {
192
340
  // Preserve content-encoding header in response to maintain compression
193
341
  let status = response.status();
194
342
  info!(
@@ -199,9 +347,12 @@ impl FBIProxy {
199
347
  original_uri,
200
348
  status.as_u16()
201
349
  );
202
- Ok(response)
350
+ // Convert the response body back to BoxBody
351
+ let (parts, body) = response.into_parts();
352
+ let boxed_body = body.map_err(|e| e).boxed();
353
+ Ok(Response::from_parts(parts, boxed_body))
203
354
  }
204
- Err(e) => {
355
+ Ok(Err(e)) => {
205
356
  error!(
206
357
  "{} {}@{}{} 502 ({})",
207
358
  method,
@@ -212,17 +363,29 @@ impl FBIProxy {
212
363
  );
213
364
  Ok(Response::builder()
214
365
  .status(StatusCode::BAD_GATEWAY)
215
- .body(Body::from("FBIPROXY ERROR"))?)
366
+ .body(Full::new(Bytes::from("FBIPROXY ERROR")).map_err(|e| match e {}).boxed())?)
367
+ }
368
+ Err(_) => {
369
+ error!(
370
+ "{} {}@{}{} 502 (request timeout)",
371
+ method,
372
+ host_header,
373
+ target_host,
374
+ original_uri
375
+ );
376
+ Ok(Response::builder()
377
+ .status(StatusCode::BAD_GATEWAY)
378
+ .body(Full::new(Bytes::from("FBIPROXY TIMEOUT")).map_err(|e| match e {}).boxed())?)
216
379
  }
217
380
  }
218
381
  }
219
382
 
220
383
  async fn handle_websocket_upgrade(
221
384
  &self,
222
- req: Request<Body>,
385
+ req: Request<Incoming>,
223
386
  target_host: &str,
224
387
  _new_host: &str, // Currently not used for WebSocket connections, but kept for consistency
225
- ) -> Result<Response<Body>, BoxError> {
388
+ ) -> Result<Response<BoxBody>, BoxError> {
226
389
  let uri = req.uri().clone();
227
390
  let ws_url = format!(
228
391
  "ws://{}{}",
@@ -230,22 +393,23 @@ impl FBIProxy {
230
393
  uri.path_and_query().map(|pq| pq.as_str()).unwrap_or("/")
231
394
  );
232
395
 
233
- // Upgrade the HTTP connection to WebSocket
234
- let (response, websocket) = hyper_tungstenite::upgrade(req, None)?;
235
-
236
- // Connect to upstream WebSocket
396
+ // Step 1: Connect to upstream WebSocket FIRST before upgrading client
397
+ // This ensures we can return proper errors if upstream is unavailable
237
398
  let (upstream_ws, _) = match connect_async(&ws_url).await {
238
399
  Ok(ws) => ws,
239
400
  Err(e) => {
240
- error!("WS :ws:{} => :ws:{}{} 502 ({})", target_host, target_host, uri, e);
401
+ error!("WS :ws:{} => :ws:{}{} 502 (upstream connection failed: {})", target_host, target_host, uri, e);
241
402
  return Ok(Response::builder()
242
403
  .status(StatusCode::BAD_GATEWAY)
243
- .body(Body::from("WebSocket connection failed"))?);
404
+ .body(Full::new(Bytes::from("WebSocket upstream unavailable")).map_err(|e| match e {}).boxed())?);
244
405
  }
245
406
  };
246
407
 
247
- // Spawn task to handle WebSocket forwarding
248
- // let ws_url_clone = ws_url.clone();
408
+ // Step 2: Now upgrade the HTTP connection to WebSocket
409
+ // Only do this after confirming upstream is available
410
+ let (response, websocket) = hyper_tungstenite::upgrade(req, None)?;
411
+
412
+ // Step 3: Spawn task to handle WebSocket forwarding
249
413
  tokio::spawn(async move {
250
414
  if let Err(e) = handle_websocket_forwarding(websocket, upstream_ws).await {
251
415
  error!("WebSocket forwarding error: {}", e);
@@ -253,7 +417,9 @@ impl FBIProxy {
253
417
  });
254
418
 
255
419
  info!("WS :ws:{} => :ws:{}{} 101", target_host, target_host, uri);
256
- Ok(response)
420
+ let (parts, body) = response.into_parts();
421
+ let boxed_body = body.map_err(|_: std::convert::Infallible| unreachable!()).boxed();
422
+ Ok(Response::from_parts(parts, boxed_body))
257
423
  }
258
424
  }
259
425
 
@@ -304,39 +470,27 @@ async fn handle_websocket_forwarding(
304
470
  }
305
471
 
306
472
  async fn handle_connection(
307
- req: Request<Body>,
473
+ req: Request<Incoming>,
308
474
  proxy: Arc<FBIProxy>,
309
- ) -> Result<Response<Body>, Infallible> {
475
+ ) -> Result<Response<BoxBody>, Infallible> {
310
476
  match proxy.handle_request(req).await {
311
477
  Ok(response) => Ok(response),
312
478
  Err(e) => {
313
479
  error!("Request handling error: {}", e);
314
480
  Ok(Response::builder()
315
481
  .status(StatusCode::INTERNAL_SERVER_ERROR)
316
- .body(Body::from("Internal Server Error"))
482
+ .body(Full::new(Bytes::from("Internal Server Error")).map_err(|e| match e {}).boxed())
317
483
  .unwrap())
318
484
  }
319
485
  }
320
486
  }
321
487
 
322
- pub async fn start_proxy_server(port: u16) -> Result<(), BoxError> {
323
- start_proxy_server_with_options("127.0.0.1", port, None).await
324
- }
325
-
326
- pub async fn start_proxy_server_with_host(host: &str, port: u16) -> Result<(), BoxError> {
327
- start_proxy_server_with_options(host, port, None).await
328
- }
329
-
330
- pub async fn start_proxy_server_with_options(host: &str, port: u16, domain_filter: Option<String>) -> Result<(), BoxError> {
488
+ pub async fn start_proxy_server(host: Option<&str>, port: u16, domain_filter: Option<String>) -> Result<(), BoxError> {
489
+ let host = host.unwrap_or("127.0.0.1");
331
490
  let addr: SocketAddr = format!("{}:{}", host, port).parse()?;
332
491
  let proxy = Arc::new(FBIProxy::new(domain_filter.clone()));
333
492
 
334
- let make_svc = make_service_fn(move |_conn: &hyper::server::conn::AddrStream| {
335
- let proxy = proxy.clone();
336
- async move { Ok::<_, Infallible>(service_fn(move |req| handle_connection(req, proxy.clone()))) }
337
- });
338
-
339
- let server = Server::bind(&addr).serve(make_svc);
493
+ let listener = TcpListener::bind(addr).await?;
340
494
 
341
495
  info!("FBI Proxy server running on http://{}", addr);
342
496
  println!("FBI Proxy listening on: http://{}", addr);
@@ -350,11 +504,25 @@ pub async fn start_proxy_server_with_options(host: &str, port: u16, domain_filte
350
504
 
351
505
  info!("Features: HTTP proxying + WebSocket forwarding + Port encoding + Domain filtering");
352
506
 
353
- if let Err(e) = server.await {
354
- error!("Server error: {}", e);
355
- }
507
+ loop {
508
+ let (stream, _) = listener.accept().await?;
509
+ let io = TokioIo::new(stream);
510
+ let proxy = proxy.clone();
356
511
 
357
- Ok(())
512
+ tokio::task::spawn(async move {
513
+ let service = service_fn(move |req| handle_connection(req, proxy.clone()));
514
+
515
+ if let Err(err) = http1::Builder::new()
516
+ .preserve_header_case(true)
517
+ .title_case_headers(true)
518
+ .serve_connection(io, service)
519
+ .with_upgrades()
520
+ .await
521
+ {
522
+ error!("Error serving connection: {:?}", err);
523
+ }
524
+ });
525
+ }
358
526
  }
359
527
 
360
528
  fn main() {
@@ -453,7 +621,7 @@ TRY RUN:
453
621
  "Starting FBI-Proxy on {}:{} with domain filter: {:?}",
454
622
  host, port, domain_filter
455
623
  );
456
- if let Err(e) = start_proxy_server_with_options(host, port, domain_filter).await {
624
+ if let Err(e) = start_proxy_server(Some(host), port, domain_filter).await {
457
625
  error!("Failed to start proxy server: {}", e);
458
626
  }
459
627
  });
package/ts/cli.ts CHANGED
@@ -21,7 +21,8 @@ await yargs(hideBin(process.argv))
21
21
 
22
22
  console.log("Preparing Binaries");
23
23
 
24
- const FBIPROXY_PORT = String(await getPort({ port: 2432 }));
24
+ const FBI_PROXY_PORT =
25
+ process.env.FBI_PROXY_PORT || String(await getPort({ port: 2432 }));
25
26
 
26
27
  const proxyProcess = await hotMemo(async () => {
27
28
  const proxy = await getFbiProxyBinary();
@@ -29,7 +30,7 @@ const proxyProcess = await hotMemo(async () => {
29
30
  const p = $.opt({
30
31
  env: {
31
32
  ...process.env,
32
- FBIPROXY_PORT, // Rust proxy server port
33
+ FBI_PROXY_PORT, // Rust proxy server port
33
34
  },
34
35
  })`${proxy}`.process;
35
36
 
@@ -42,7 +43,7 @@ const proxyProcess = await hotMemo(async () => {
42
43
 
43
44
  console.log("All services started successfully!");
44
45
  console.log(`Proxy server PID: ${proxyProcess.pid}`);
45
- console.log(`Proxy server running on port: ${FBIPROXY_PORT}`);
46
+ console.log(`Proxy server running on port: ${FBI_PROXY_PORT}`);
46
47
 
47
48
  const exit = () => {
48
49
  console.log("Shutting down...");