termbridge 0.2.0 → 0.3.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/bin.js +86 -14
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
- package/ui/dist/assets/index-B6CAkCwJ.css +1 -0
- package/ui/dist/assets/index-JYV1Fxtz.js +103 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-C9vi5hfL.css +0 -1
- package/ui/dist/assets/index-Cix4a2YI.js +0 -103
package/dist/bin.js
CHANGED
|
@@ -1788,6 +1788,22 @@ var parseArgs = (argv) => {
|
|
|
1788
1788
|
options.port = port;
|
|
1789
1789
|
continue;
|
|
1790
1790
|
}
|
|
1791
|
+
if (current === "--proxy") {
|
|
1792
|
+
const proxy = parseNumber(args.shift());
|
|
1793
|
+
if (!proxy || proxy <= 0) {
|
|
1794
|
+
throw new Error("invalid proxy port");
|
|
1795
|
+
}
|
|
1796
|
+
options.proxy = proxy;
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
if (current === "--dev-proxy-url") {
|
|
1800
|
+
const url = args.shift();
|
|
1801
|
+
if (!url) {
|
|
1802
|
+
throw new Error("missing dev proxy URL");
|
|
1803
|
+
}
|
|
1804
|
+
options.devProxyUrl = url;
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1791
1807
|
if (current === "--session") {
|
|
1792
1808
|
const session = args.shift();
|
|
1793
1809
|
if (!session) {
|
|
@@ -1825,6 +1841,7 @@ Usage:
|
|
|
1825
1841
|
|
|
1826
1842
|
Options:
|
|
1827
1843
|
--port <port> Bind the local server to a fixed port
|
|
1844
|
+
--proxy <port> Proxy a local dev server (e.g., Vite) through termbridge
|
|
1828
1845
|
--session <name> Use a specific tmux session name
|
|
1829
1846
|
--kill-on-exit Kill the tmux session when the CLI exits
|
|
1830
1847
|
--no-qr Disable QR code output
|
|
@@ -2246,9 +2263,9 @@ var createTerminalRegistry = () => {
|
|
|
2246
2263
|
};
|
|
2247
2264
|
|
|
2248
2265
|
// src/server/server.ts
|
|
2249
|
-
import { createServer as createHttpServer } from "http";
|
|
2266
|
+
import { createServer as createHttpServer, request as httpRequest } from "http";
|
|
2250
2267
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
2251
|
-
import { WebSocketServer } from "ws";
|
|
2268
|
+
import { WebSocketServer, WebSocket as WsWebSocket } from "ws";
|
|
2252
2269
|
|
|
2253
2270
|
// ../packages/shared/src/index.ts
|
|
2254
2271
|
var TERMINAL_CONTROL_KEYS = [
|
|
@@ -2385,24 +2402,44 @@ var sendWsMessage = (socket, message) => {
|
|
|
2385
2402
|
};
|
|
2386
2403
|
var createSessionName = () => `termbridge-${randomBytes3(4).toString("hex")}`;
|
|
2387
2404
|
var createAppServer = (deps) => {
|
|
2388
|
-
const staticHandler = createStaticHandler(deps.uiDistPath, "/app");
|
|
2405
|
+
const staticHandler = createStaticHandler(deps.uiDistPath, "/__tb/app");
|
|
2389
2406
|
const wss = new WebSocketServer({ noServer: true });
|
|
2390
2407
|
const connectionInfo = /* @__PURE__ */ new WeakMap();
|
|
2408
|
+
const proxyRequest = (request, response, targetPath, search) => {
|
|
2409
|
+
const targetUrl = `http://localhost:${deps.proxyPort}${targetPath}${search}`;
|
|
2410
|
+
const proxyHeaders = { ...request.headers };
|
|
2411
|
+
delete proxyHeaders.cookie;
|
|
2412
|
+
delete proxyHeaders.host;
|
|
2413
|
+
proxyHeaders.host = `localhost:${deps.proxyPort}`;
|
|
2414
|
+
const proxyReq = httpRequest(
|
|
2415
|
+
targetUrl,
|
|
2416
|
+
{ method: request.method, headers: proxyHeaders },
|
|
2417
|
+
(proxyRes) => {
|
|
2418
|
+
response.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
2419
|
+
proxyRes.pipe(response);
|
|
2420
|
+
}
|
|
2421
|
+
);
|
|
2422
|
+
proxyReq.on("error", () => {
|
|
2423
|
+
response.statusCode = 502;
|
|
2424
|
+
response.end("proxy error");
|
|
2425
|
+
});
|
|
2426
|
+
request.pipe(proxyReq);
|
|
2427
|
+
};
|
|
2391
2428
|
const server = createHttpServer(async (request, response) => {
|
|
2392
2429
|
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
2393
|
-
if (request.method === "GET" && url.pathname === "/healthz") {
|
|
2430
|
+
if (request.method === "GET" && url.pathname === "/__tb/healthz") {
|
|
2394
2431
|
response.statusCode = 200;
|
|
2395
2432
|
response.end("ok");
|
|
2396
2433
|
return;
|
|
2397
2434
|
}
|
|
2398
|
-
if (request.method === "GET" && url.pathname === "/") {
|
|
2435
|
+
if (request.method === "GET" && url.pathname === "/" && !deps.proxyPort) {
|
|
2399
2436
|
response.statusCode = 302;
|
|
2400
|
-
response.setHeader("Location", "/app");
|
|
2437
|
+
response.setHeader("Location", "/__tb/app");
|
|
2401
2438
|
response.end();
|
|
2402
2439
|
return;
|
|
2403
2440
|
}
|
|
2404
|
-
if (request.method === "GET" && url.pathname.startsWith("/s/")) {
|
|
2405
|
-
const token = url.pathname.slice(
|
|
2441
|
+
if (request.method === "GET" && url.pathname.startsWith("/__tb/s/")) {
|
|
2442
|
+
const token = url.pathname.slice("/__tb/s/".length);
|
|
2406
2443
|
const ip = getIp(request);
|
|
2407
2444
|
if (!deps.redemptionLimiter.allow(ip)) {
|
|
2408
2445
|
response.statusCode = 429;
|
|
@@ -2417,11 +2454,11 @@ var createAppServer = (deps) => {
|
|
|
2417
2454
|
}
|
|
2418
2455
|
response.statusCode = 302;
|
|
2419
2456
|
response.setHeader("Set-Cookie", deps.auth.createSessionCookie(session.id));
|
|
2420
|
-
response.setHeader("Location", "/app");
|
|
2457
|
+
response.setHeader("Location", "/__tb/app");
|
|
2421
2458
|
response.end();
|
|
2422
2459
|
return;
|
|
2423
2460
|
}
|
|
2424
|
-
if (url.pathname === "/api/csrf") {
|
|
2461
|
+
if (url.pathname === "/__tb/api/csrf") {
|
|
2425
2462
|
const session = deps.auth.getSessionFromRequest(request);
|
|
2426
2463
|
if (!session) {
|
|
2427
2464
|
response.statusCode = 401;
|
|
@@ -2436,7 +2473,7 @@ var createAppServer = (deps) => {
|
|
|
2436
2473
|
response.end("not found");
|
|
2437
2474
|
return;
|
|
2438
2475
|
}
|
|
2439
|
-
if (url.pathname === "/api/terminals") {
|
|
2476
|
+
if (url.pathname === "/__tb/api/terminals") {
|
|
2440
2477
|
const session = deps.auth.getSessionFromRequest(request);
|
|
2441
2478
|
if (!session) {
|
|
2442
2479
|
response.statusCode = 401;
|
|
@@ -2467,15 +2504,48 @@ var createAppServer = (deps) => {
|
|
|
2467
2504
|
return;
|
|
2468
2505
|
}
|
|
2469
2506
|
}
|
|
2507
|
+
if (request.method === "GET" && url.pathname === "/__tb/api/config") {
|
|
2508
|
+
const session = deps.auth.getSessionFromRequest(request);
|
|
2509
|
+
if (!session) {
|
|
2510
|
+
response.statusCode = 401;
|
|
2511
|
+
response.end("unauthorized");
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
jsonResponse(response, 200, {
|
|
2515
|
+
proxyPort: deps.proxyPort ?? null,
|
|
2516
|
+
devProxyUrl: deps.devProxyUrl ?? null
|
|
2517
|
+
});
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2470
2520
|
const handled = await staticHandler(request, response);
|
|
2471
2521
|
if (!handled) {
|
|
2522
|
+
if (deps.proxyPort && deps.auth.getSessionFromRequest(request)) {
|
|
2523
|
+
proxyRequest(request, response, url.pathname, url.search);
|
|
2524
|
+
return;
|
|
2525
|
+
}
|
|
2472
2526
|
response.statusCode = 404;
|
|
2473
2527
|
response.end("not found");
|
|
2474
2528
|
}
|
|
2475
2529
|
});
|
|
2476
2530
|
server.on("upgrade", (request, socket, head) => {
|
|
2477
2531
|
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
2478
|
-
if (!url.pathname.startsWith("/ws/terminal/")) {
|
|
2532
|
+
if (!url.pathname.startsWith("/__tb/ws/terminal/")) {
|
|
2533
|
+
if (deps.proxyPort && deps.auth.getSessionFromRequest(request)) {
|
|
2534
|
+
const targetUrl = `ws://localhost:${deps.proxyPort}${url.pathname}${url.search}`;
|
|
2535
|
+
const proxyWs = new WsWebSocket(targetUrl);
|
|
2536
|
+
proxyWs.on("open", () => {
|
|
2537
|
+
wss.handleUpgrade(request, socket, head, (clientWs) => {
|
|
2538
|
+
clientWs.on("message", (data) => proxyWs.send(data));
|
|
2539
|
+
proxyWs.on("message", (data) => clientWs.send(data));
|
|
2540
|
+
clientWs.on("close", () => proxyWs.close());
|
|
2541
|
+
proxyWs.on("close", () => clientWs.close());
|
|
2542
|
+
});
|
|
2543
|
+
});
|
|
2544
|
+
proxyWs.on("error", () => {
|
|
2545
|
+
socket.destroy();
|
|
2546
|
+
});
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2479
2549
|
socket.destroy();
|
|
2480
2550
|
return;
|
|
2481
2551
|
}
|
|
@@ -2615,7 +2685,9 @@ var startCommand = async (options, deps = {}) => {
|
|
|
2615
2685
|
uiDistPath: resolveUiDistPath(),
|
|
2616
2686
|
auth,
|
|
2617
2687
|
terminalRegistry,
|
|
2618
|
-
terminalBackend
|
|
2688
|
+
terminalBackend,
|
|
2689
|
+
proxyPort: options.proxy,
|
|
2690
|
+
devProxyUrl: options.devProxyUrl
|
|
2619
2691
|
});
|
|
2620
2692
|
const started = await server.listen(options.port ?? 0);
|
|
2621
2693
|
const localUrl = `http://127.0.0.1:${started.port}`;
|
|
@@ -2640,7 +2712,7 @@ var startCommand = async (options, deps = {}) => {
|
|
|
2640
2712
|
await started.close();
|
|
2641
2713
|
throw error;
|
|
2642
2714
|
}
|
|
2643
|
-
const redeemUrl = `${publicUrl}/s/${token}`;
|
|
2715
|
+
const redeemUrl = `${publicUrl}/__tb/s/${token}`;
|
|
2644
2716
|
logger.info(`Local server: ${localUrl}`);
|
|
2645
2717
|
logger.info(`Tunnel URL: ${redeemUrl}`);
|
|
2646
2718
|
if (!options.noQr && deps.qr) {
|