termbridge 0.3.9 → 0.3.11

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 CHANGED
@@ -1756,7 +1756,7 @@ import { config as loadEnv } from "dotenv";
1756
1756
  // src/cli/run.ts
1757
1757
  import qrcode2 from "qrcode-terminal";
1758
1758
 
1759
- // src/cli/args.ts
1759
+ // src/utils/parse.ts
1760
1760
  var parseNumber = (value) => {
1761
1761
  if (!value) {
1762
1762
  return null;
@@ -1764,6 +1764,97 @@ var parseNumber = (value) => {
1764
1764
  const parsed = Number.parseInt(value, 10);
1765
1765
  return Number.isNaN(parsed) ? null : parsed;
1766
1766
  };
1767
+ var parseOptionalNumber = (value) => {
1768
+ if (!value) {
1769
+ return void 0;
1770
+ }
1771
+ const parsed = Number.parseInt(value, 10);
1772
+ return Number.isFinite(parsed) ? parsed : void 0;
1773
+ };
1774
+ var parseBoolean = (value) => {
1775
+ if (!value) {
1776
+ return false;
1777
+ }
1778
+ const normalized = value.trim().toLowerCase();
1779
+ return normalized === "1" || normalized === "true" || normalized === "yes";
1780
+ };
1781
+ var parseList = (value) => {
1782
+ if (!value) {
1783
+ return [];
1784
+ }
1785
+ return value.split(/[,\s]+/).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
1786
+ };
1787
+ var parseSessionCount = (value) => {
1788
+ if (!value) {
1789
+ return 1;
1790
+ }
1791
+ const parsed = Number.parseInt(value, 10);
1792
+ if (!Number.isFinite(parsed) || parsed < 1) {
1793
+ return 1;
1794
+ }
1795
+ return parsed;
1796
+ };
1797
+
1798
+ // src/utils/url.ts
1799
+ var normalizePublicUrl = (value) => {
1800
+ const trimmed = value.trim();
1801
+ if (!trimmed) {
1802
+ throw new Error("missing public url");
1803
+ }
1804
+ let parsed;
1805
+ try {
1806
+ parsed = new URL(trimmed);
1807
+ } catch {
1808
+ throw new Error("invalid public url");
1809
+ }
1810
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
1811
+ throw new Error("invalid public url");
1812
+ }
1813
+ return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
1814
+ };
1815
+ var buildShareUrl = (publicUrl, token) => {
1816
+ let parsed;
1817
+ try {
1818
+ parsed = new URL(publicUrl);
1819
+ } catch {
1820
+ throw new Error("invalid public url");
1821
+ }
1822
+ const basePath = parsed.pathname.endsWith("/") ? parsed.pathname : `${parsed.pathname}/`;
1823
+ parsed.pathname = `${basePath}__tb/s/${token}`;
1824
+ parsed.hash = "";
1825
+ return parsed.toString();
1826
+ };
1827
+ var deriveRepoPath = (repoUrl) => {
1828
+ const trimmed = repoUrl.replace(/\/$/, "");
1829
+ const last = trimmed.split("/").pop();
1830
+ if (!last) {
1831
+ return "repo";
1832
+ }
1833
+ return last.endsWith(".git") ? last.slice(0, -4) : last;
1834
+ };
1835
+
1836
+ // src/utils/path.ts
1837
+ import { resolve } from "path";
1838
+ import { statSync } from "fs";
1839
+ var expandHome = (value, home) => {
1840
+ if (value === "~") {
1841
+ return home;
1842
+ }
1843
+ if (value.startsWith("~/")) {
1844
+ return `${home}/${value.slice(2)}`;
1845
+ }
1846
+ return value;
1847
+ };
1848
+ var resolvePath = (value, home) => resolve(expandHome(value, home));
1849
+ var safeStat = (path) => {
1850
+ try {
1851
+ return statSync(path);
1852
+ } catch {
1853
+ return null;
1854
+ }
1855
+ };
1856
+
1857
+ // src/cli/args.ts
1767
1858
  var parseArgs = (argv) => {
1768
1859
  const args = [...argv];
1769
1860
  let command = "start";
@@ -1969,7 +2060,7 @@ import { EventEmitter } from "events";
1969
2060
  import { execFile as execFileCallback } from "child_process";
1970
2061
  import { promisify } from "util";
1971
2062
  import { accessSync, chmodSync, constants as fsConstants } from "fs";
1972
- import { dirname, resolve } from "path";
2063
+ import { dirname, resolve as resolve2 } from "path";
1973
2064
  import { createRequire } from "module";
1974
2065
  import * as pty from "node-pty";
1975
2066
  var execFile = promisify(execFileCallback);
@@ -2009,7 +2100,7 @@ var ensureSpawnHelperExecutable = (platform) => {
2009
2100
  const { loadNativeModule } = requireFromHere("node-pty/lib/utils");
2010
2101
  const native = loadNativeModule("pty");
2011
2102
  const unixTerminalPath = requireFromHere.resolve("node-pty/lib/unixTerminal");
2012
- const helperPath = resolve(dirname(unixTerminalPath), `${native.dir}/spawn-helper`);
2103
+ const helperPath = resolve2(dirname(unixTerminalPath), `${native.dir}/spawn-helper`);
2013
2104
  try {
2014
2105
  accessSync(helperPath, fsConstants.X_OK);
2015
2106
  } catch {
@@ -2389,8 +2480,8 @@ var createAuth = ({
2389
2480
  if (!record) {
2390
2481
  return null;
2391
2482
  }
2483
+ wsTokens.delete(token);
2392
2484
  if (record.expiresAt <= clock()) {
2393
- wsTokens.delete(token);
2394
2485
  return null;
2395
2486
  }
2396
2487
  return getSession(record.sessionId);
@@ -2508,25 +2599,13 @@ var createTerminalRegistry = () => {
2508
2599
  };
2509
2600
 
2510
2601
  // src/server/server.ts
2511
- import { createServer as createHttpServer, request as httpRequest } from "http";
2512
- import { request as httpsRequest } from "https";
2602
+ import { createServer as createHttpServer } from "http";
2513
2603
  import { randomBytes as randomBytes3 } from "crypto";
2514
2604
  import { WebSocketServer, WebSocket as WsWebSocket } from "ws";
2515
2605
 
2516
- // ../packages/shared/src/index.ts
2517
- var TERMINAL_CONTROL_KEYS = [
2518
- "ctrl_c",
2519
- "esc",
2520
- "tab",
2521
- "up",
2522
- "down",
2523
- "left",
2524
- "right"
2525
- ];
2526
-
2527
2606
  // src/server/static.ts
2528
2607
  import { readFile } from "fs/promises";
2529
- import { extname, resolve as resolve2 } from "path";
2608
+ import { extname, resolve as resolve3 } from "path";
2530
2609
  var contentTypes = {
2531
2610
  ".html": "text/html",
2532
2611
  ".js": "text/javascript",
@@ -2552,7 +2631,7 @@ var createStaticHandler = (uiDistPath, basePath) => {
2552
2631
  }
2553
2632
  const relative2 = url.pathname.slice(normalizedBase.length) || "/";
2554
2633
  const filePath = relative2 === "/" ? "/index.html" : relative2;
2555
- const absolutePath = resolve2(uiDistPath, `.${filePath}`);
2634
+ const absolutePath = resolve3(uiDistPath, `.${filePath}`);
2556
2635
  try {
2557
2636
  const payload = await readFile(absolutePath);
2558
2637
  response.statusCode = 200;
@@ -2561,7 +2640,7 @@ var createStaticHandler = (uiDistPath, basePath) => {
2561
2640
  return true;
2562
2641
  } catch {
2563
2642
  try {
2564
- const fallback = await readFile(resolve2(uiDistPath, "index.html"));
2643
+ const fallback = await readFile(resolve3(uiDistPath, "index.html"));
2565
2644
  response.statusCode = 200;
2566
2645
  response.setHeader("Content-Type", "text/html");
2567
2646
  response.end(fallback);
@@ -2575,10 +2654,8 @@ var createStaticHandler = (uiDistPath, basePath) => {
2575
2654
  };
2576
2655
  };
2577
2656
 
2578
- // src/server/server.ts
2657
+ // src/server/http-utils.ts
2579
2658
  var MAX_HTTP_BODY_SIZE = 64 * 1024;
2580
- var MAX_WS_MESSAGE_SIZE = 1024 * 1024;
2581
- var MAX_INPUT_LENGTH = 64 * 1024;
2582
2659
  var jsonResponse = (response, status, payload) => {
2583
2660
  const body = JSON.stringify(payload);
2584
2661
  response.statusCode = status;
@@ -2625,6 +2702,21 @@ var isAllowedOrigin = (origin, host, forwardedHost) => {
2625
2702
  }
2626
2703
  };
2627
2704
  var resolveForwardedHost = (header) => Array.isArray(header) ? header[0] : header;
2705
+
2706
+ // ../packages/shared/src/index.ts
2707
+ var TERMINAL_CONTROL_KEYS = [
2708
+ "ctrl_c",
2709
+ "esc",
2710
+ "tab",
2711
+ "up",
2712
+ "down",
2713
+ "left",
2714
+ "right"
2715
+ ];
2716
+
2717
+ // src/server/ws-utils.ts
2718
+ var MAX_WS_MESSAGE_SIZE = 1024 * 1024;
2719
+ var MAX_INPUT_LENGTH = 64 * 1024;
2628
2720
  var allowedControlKeys = new Set(TERMINAL_CONTROL_KEYS);
2629
2721
  var parseClientMessage = (payload) => {
2630
2722
  const size = typeof payload === "string" ? payload.length : Array.isArray(payload) ? payload.reduce((sum, buf) => sum + buf.length, 0) : payload.byteLength;
@@ -2657,50 +2749,74 @@ var parseClientMessage = (payload) => {
2657
2749
  var sendWsMessage = (socket, message) => {
2658
2750
  socket.send(JSON.stringify(message));
2659
2751
  };
2752
+
2753
+ // src/server/proxy.ts
2754
+ import { request as httpRequest } from "http";
2755
+ import { request as httpsRequest } from "https";
2756
+ var resolveProxyUrl = (config, targetPath, search) => {
2757
+ if (typeof config.proxyPort === "number") {
2758
+ return new URL(`http://localhost:${config.proxyPort}${targetPath}${search}`);
2759
+ }
2760
+ if (config.devProxyUrl) {
2761
+ try {
2762
+ return new URL(`${targetPath}${search}`, config.devProxyUrl);
2763
+ } catch {
2764
+ return null;
2765
+ }
2766
+ }
2767
+ return null;
2768
+ };
2769
+ var proxyRequest = (config, request, response, targetPath, search) => {
2770
+ const targetUrl = resolveProxyUrl(config, targetPath, search);
2771
+ if (!targetUrl) {
2772
+ response.statusCode = 502;
2773
+ response.end("proxy error");
2774
+ return;
2775
+ }
2776
+ const proxyHeaders = { ...request.headers, ...config.devProxyHeaders ?? {} };
2777
+ delete proxyHeaders.cookie;
2778
+ delete proxyHeaders.host;
2779
+ proxyHeaders.host = targetUrl.host;
2780
+ const requestImpl = targetUrl.protocol === "https:" ? httpsRequest : httpRequest;
2781
+ const proxyReq = requestImpl(
2782
+ targetUrl,
2783
+ { method: request.method, headers: proxyHeaders },
2784
+ (proxyRes) => {
2785
+ response.writeHead(proxyRes.statusCode, proxyRes.headers);
2786
+ proxyRes.pipe(response);
2787
+ }
2788
+ );
2789
+ proxyReq.on("error", () => {
2790
+ response.statusCode = 502;
2791
+ response.end("proxy error");
2792
+ });
2793
+ request.pipe(proxyReq);
2794
+ };
2795
+ var getProxyWebSocketUrl = (config) => {
2796
+ try {
2797
+ if (typeof config.proxyPort === "number") {
2798
+ return new URL(`http://localhost:${config.proxyPort}`);
2799
+ }
2800
+ if (config.devProxyUrl) {
2801
+ return new URL(config.devProxyUrl);
2802
+ }
2803
+ } catch {
2804
+ return null;
2805
+ }
2806
+ return null;
2807
+ };
2808
+
2809
+ // src/server/server.ts
2660
2810
  var createSessionName = () => `termbridge-${randomBytes3(4).toString("hex")}`;
2661
2811
  var createAppServer = (deps) => {
2662
2812
  const staticHandler = createStaticHandler(deps.uiDistPath, "/__tb/app");
2663
2813
  const wss = new WebSocketServer({ noServer: true });
2664
2814
  const connectionInfo = /* @__PURE__ */ new WeakMap();
2665
2815
  const hasProxy = typeof deps.proxyPort === "number" || deps.devProxyUrl !== void 0;
2666
- const resolveProxyUrl = (targetPath, search) => {
2667
- if (typeof deps.proxyPort === "number") {
2668
- return new URL(`http://localhost:${deps.proxyPort}${targetPath}${search}`);
2669
- }
2670
- if (deps.devProxyUrl) {
2671
- try {
2672
- return new URL(`${targetPath}${search}`, deps.devProxyUrl);
2673
- } catch {
2674
- return null;
2675
- }
2676
- }
2677
- return null;
2678
- };
2679
- const proxyRequest = (request, response, targetPath, search) => {
2680
- const targetUrl = resolveProxyUrl(targetPath, search);
2681
- if (!targetUrl) {
2682
- response.statusCode = 502;
2683
- response.end("proxy error");
2684
- return;
2685
- }
2686
- const proxyHeaders = { ...request.headers, ...deps.devProxyHeaders ?? {} };
2687
- delete proxyHeaders.cookie;
2688
- delete proxyHeaders.host;
2689
- proxyHeaders.host = targetUrl.host;
2690
- const requestImpl = targetUrl.protocol === "https:" ? httpsRequest : httpRequest;
2691
- const proxyReq = requestImpl(
2692
- targetUrl,
2693
- { method: request.method, headers: proxyHeaders },
2694
- (proxyRes) => {
2695
- response.writeHead(proxyRes.statusCode, proxyRes.headers);
2696
- proxyRes.pipe(response);
2697
- }
2698
- );
2699
- proxyReq.on("error", () => {
2700
- response.statusCode = 502;
2701
- response.end("proxy error");
2702
- });
2703
- request.pipe(proxyReq);
2816
+ const proxyConfig = {
2817
+ proxyPort: deps.proxyPort,
2818
+ devProxyUrl: deps.devProxyUrl,
2819
+ devProxyHeaders: deps.devProxyHeaders
2704
2820
  };
2705
2821
  const server = createHttpServer(async (request, response) => {
2706
2822
  const url = new URL(request.url, `http://${request.headers.host}`);
@@ -2799,7 +2915,7 @@ var createAppServer = (deps) => {
2799
2915
  const handled = await staticHandler(request, response);
2800
2916
  if (!handled) {
2801
2917
  if (hasProxy && deps.auth.getSessionFromRequest(request)) {
2802
- proxyRequest(request, response, url.pathname, url.search);
2918
+ proxyRequest(proxyConfig, request, response, url.pathname, url.search);
2803
2919
  return;
2804
2920
  }
2805
2921
  response.statusCode = 404;
@@ -2810,17 +2926,15 @@ var createAppServer = (deps) => {
2810
2926
  const url = new URL(request.url, `http://${request.headers.host}`);
2811
2927
  if (!url.pathname.startsWith("/__tb/ws/terminal/")) {
2812
2928
  if (hasProxy && deps.auth.getSessionFromRequest(request)) {
2813
- let baseUrl = null;
2814
- try {
2815
- baseUrl = typeof deps.proxyPort === "number" ? new URL(`http://localhost:${deps.proxyPort}`) : new URL(deps.devProxyUrl);
2816
- } catch {
2929
+ const baseUrl = getProxyWebSocketUrl(proxyConfig);
2930
+ if (!baseUrl) {
2817
2931
  socket.destroy();
2818
2932
  return;
2819
2933
  }
2820
2934
  const wsProtocol = baseUrl.protocol === "https:" ? "wss:" : "ws:";
2821
2935
  const targetUrl = new URL(`${url.pathname}${url.search}`, baseUrl);
2822
2936
  targetUrl.protocol = wsProtocol;
2823
- const proxyHeaders = { ...request.headers, ...deps.devProxyHeaders ?? {} };
2937
+ const proxyHeaders = { ...request.headers, ...proxyConfig.devProxyHeaders ?? {} };
2824
2938
  delete proxyHeaders.cookie;
2825
2939
  delete proxyHeaders.host;
2826
2940
  proxyHeaders.host = targetUrl.host;
@@ -3035,9 +3149,9 @@ var installAgents = async (sandbox, options, logger) => {
3035
3149
 
3036
3150
  // src/sandbox/daytona/agent-auth.ts
3037
3151
  import { homedir } from "os";
3038
- import { basename, dirname as dirname2, join, relative, resolve as resolve3 } from "path";
3152
+ import { basename, dirname as dirname2, join, relative, resolve as resolve4 } from "path";
3039
3153
  import { readdir, stat } from "fs/promises";
3040
- var expandHome = (value, home) => {
3154
+ var expandHome2 = (value, home) => {
3041
3155
  if (value === "~") {
3042
3156
  return home;
3043
3157
  }
@@ -3048,7 +3162,7 @@ var expandHome = (value, home) => {
3048
3162
  };
3049
3163
  var resolveDestination = (spec, sourcePath, localHome, remoteHome) => {
3050
3164
  if (spec.destination) {
3051
- return expandHome(spec.destination, remoteHome);
3165
+ return expandHome2(spec.destination, remoteHome);
3052
3166
  }
3053
3167
  if (sourcePath.startsWith(localHome)) {
3054
3168
  const rel = relative(localHome, sourcePath);
@@ -3088,7 +3202,7 @@ var syncAgentAuth = async (sandbox, options, logger) => {
3088
3202
  const uploads = [];
3089
3203
  const mkdirs = /* @__PURE__ */ new Set();
3090
3204
  for (const spec of options.specs) {
3091
- const sourcePath = resolve3(expandHome(spec.source, localHome));
3205
+ const sourcePath = resolve4(expandHome2(spec.source, localHome));
3092
3206
  let stats;
3093
3207
  try {
3094
3208
  stats = await stat(sourcePath);
@@ -3139,7 +3253,7 @@ var noopLogger = {
3139
3253
  warn: () => void 0,
3140
3254
  error: () => void 0
3141
3255
  };
3142
- var deriveRepoPath = (repoUrl) => {
3256
+ var deriveRepoPath2 = (repoUrl) => {
3143
3257
  const trimmed = repoUrl.replace(/\/$/, "");
3144
3258
  const last = trimmed.split("/").pop();
3145
3259
  if (!last) {
@@ -3165,7 +3279,7 @@ var createSandboxDaytonaBackend = (options) => {
3165
3279
  logger.info(`Sandbox (Daytona): creating sandbox ${name}`);
3166
3280
  const sandbox = await daytona.create({ name, public: options.public });
3167
3281
  await sandbox.start();
3168
- const repoPath = options.repoPath ?? deriveRepoPath(options.repoUrl);
3282
+ const repoPath = options.repoPath ?? deriveRepoPath2(options.repoUrl);
3169
3283
  logger.info(`Sandbox (Daytona): cloning ${options.repoUrl}`);
3170
3284
  await sandbox.git.clone(
3171
3285
  options.repoUrl,
@@ -3353,8 +3467,6 @@ var createSandboxDaytonaBackend = (options) => {
3353
3467
 
3354
3468
  // src/sandbox/daytona/agent-auto.ts
3355
3469
  import { homedir as homedir2 } from "os";
3356
- import { resolve as resolve4 } from "path";
3357
- import { statSync } from "fs";
3358
3470
  var agentDefinitions = {
3359
3471
  "claude-code": {
3360
3472
  packages: ["@anthropic-ai/claude-code"],
@@ -3382,23 +3494,6 @@ var agentAliasMap = {
3382
3494
  "@openai/codex": "codex",
3383
3495
  opencode: "opencode"
3384
3496
  };
3385
- var expandHome2 = (value, home) => {
3386
- if (value === "~") {
3387
- return home;
3388
- }
3389
- if (value.startsWith("~/")) {
3390
- return `${home}/${value.slice(2)}`;
3391
- }
3392
- return value;
3393
- };
3394
- var resolvePath = (value, home) => resolve4(expandHome2(value, home));
3395
- var safeStat = (path) => {
3396
- try {
3397
- return statSync(path);
3398
- } catch {
3399
- return null;
3400
- }
3401
- };
3402
3497
  var normalizeAgentName = (value) => {
3403
3498
  const normalized = value.trim().toLowerCase();
3404
3499
  if (normalized === "") {
@@ -3522,30 +3617,6 @@ var installLocalTermbridge = async (sandbox, startOptions, logger) => {
3522
3617
  logger.info("Sandbox (Daytona): installed local termbridge package");
3523
3618
  return { useLocal: true };
3524
3619
  };
3525
- var deriveRepoPath2 = (repoUrl) => {
3526
- const trimmed = repoUrl.replace(/\/$/, "");
3527
- const last = trimmed.split("/").pop();
3528
- if (!last) {
3529
- return "repo";
3530
- }
3531
- return last.endsWith(".git") ? last.slice(0, -4) : last;
3532
- };
3533
- var normalizePublicUrl = (value) => {
3534
- const trimmed = value.trim();
3535
- if (!trimmed) {
3536
- throw new Error("missing public url");
3537
- }
3538
- let parsed;
3539
- try {
3540
- parsed = new URL(trimmed);
3541
- } catch {
3542
- throw new Error("invalid public url");
3543
- }
3544
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
3545
- throw new Error("invalid public url");
3546
- }
3547
- return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
3548
- };
3549
3620
  var delay = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
3550
3621
  var ensureTmux = async (sandbox, logger) => {
3551
3622
  const check = await sandbox.process.executeCommand("command -v tmux");
@@ -3673,7 +3744,7 @@ var createSandboxDaytonaServerProvider = (options = {}) => {
3673
3744
  const sandbox = await daytona.create({ name, public: startOptions.public });
3674
3745
  sandboxRef = sandbox;
3675
3746
  await sandbox.start();
3676
- const repoPath = startOptions.repoPath ?? deriveRepoPath2(startOptions.repoUrl);
3747
+ const repoPath = startOptions.repoPath ?? deriveRepoPath(startOptions.repoUrl);
3677
3748
  logger.info(`Sandbox (Daytona): cloning ${startOptions.repoUrl}`);
3678
3749
  await sandbox.git.clone(
3679
3750
  startOptions.repoUrl,
@@ -3796,77 +3867,7 @@ var createSandboxDaytonaServerProvider = (options = {}) => {
3796
3867
  };
3797
3868
  };
3798
3869
 
3799
- // src/cli/start.ts
3800
- var resolveUiDistPath = () => {
3801
- const currentDir = dirname3(fileURLToPath(import.meta.url));
3802
- const candidates = [
3803
- resolve5(currentDir, "../../ui/dist"),
3804
- resolve5(currentDir, "../ui/dist"),
3805
- resolve5(process.cwd(), "ui/dist"),
3806
- resolve5(process.cwd(), "cli/ui/dist")
3807
- ];
3808
- for (const candidate of candidates) {
3809
- if (existsSync(candidate)) {
3810
- return candidate;
3811
- }
3812
- }
3813
- return candidates[0];
3814
- };
3815
- var packLocalCli = (logger) => {
3816
- const cliDir = resolve5(dirname3(fileURLToPath(import.meta.url)), "..");
3817
- const packageJson = resolve5(cliDir, "package.json");
3818
- if (!existsSync(packageJson)) {
3819
- return void 0;
3820
- }
3821
- const result = spawnSync("npm", ["pack"], { cwd: cliDir, encoding: "utf8" });
3822
- if (result.status !== 0) {
3823
- const stderr = result.stderr?.toString().trim();
3824
- logger.warn(`Local CLI pack failed${stderr ? ` (${stderr})` : ""}`);
3825
- return void 0;
3826
- }
3827
- const lines = result.stdout?.toString().trim().split(/\r?\n/).filter(Boolean) ?? [];
3828
- const filename = lines[lines.length - 1];
3829
- if (!filename) {
3830
- logger.warn("Local CLI pack failed (no output)");
3831
- return void 0;
3832
- }
3833
- return resolve5(cliDir, filename);
3834
- };
3835
- var createDefaultLogger = () => ({
3836
- info: (message) => console.log(message),
3837
- warn: (message) => console.warn(message),
3838
- error: (message) => console.error(message)
3839
- });
3840
- var parseSessionCount = (value) => {
3841
- if (!value) {
3842
- return 1;
3843
- }
3844
- const parsed = Number.parseInt(value, 10);
3845
- if (!Number.isFinite(parsed) || parsed < 1) {
3846
- return 1;
3847
- }
3848
- return parsed;
3849
- };
3850
- var parseBoolean = (value) => {
3851
- if (!value) {
3852
- return false;
3853
- }
3854
- const normalized = value.trim().toLowerCase();
3855
- return normalized === "1" || normalized === "true" || normalized === "yes";
3856
- };
3857
- var parseOptionalNumber = (value) => {
3858
- if (!value) {
3859
- return void 0;
3860
- }
3861
- const parsed = Number.parseInt(value, 10);
3862
- return Number.isFinite(parsed) ? parsed : void 0;
3863
- };
3864
- var parseList = (value) => {
3865
- if (!value) {
3866
- return [];
3867
- }
3868
- return value.split(/[,\s]+/).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
3869
- };
3870
+ // src/cli/agent-config.ts
3870
3871
  var defaultAgentEnvKeys = [
3871
3872
  "OPENAI_API_KEY",
3872
3873
  "OPENAI_BASE_URL",
@@ -3937,58 +3938,56 @@ var resolveAutoAgentNames = (env) => {
3937
3938
  const auto = parseBoolean(env.TERMBRIDGE_SANDBOX_AGENT_AUTO);
3938
3939
  return auto ? ["all"] : [];
3939
3940
  };
3940
- var resolveBackendMode = (value) => {
3941
- if (!value) {
3942
- return "tmux";
3943
- }
3944
- if (value === "tmux" || value === "sandbox-daytona") {
3945
- return value;
3941
+
3942
+ // src/cli/start.ts
3943
+ var resolveUiDistPath = () => {
3944
+ const currentDir = dirname3(fileURLToPath(import.meta.url));
3945
+ const candidates = [
3946
+ resolve5(currentDir, "../../ui/dist"),
3947
+ resolve5(currentDir, "../ui/dist"),
3948
+ resolve5(process.cwd(), "ui/dist"),
3949
+ resolve5(process.cwd(), "cli/ui/dist")
3950
+ ];
3951
+ for (const candidate of candidates) {
3952
+ if (existsSync(candidate)) {
3953
+ return candidate;
3954
+ }
3946
3955
  }
3947
- throw new Error("invalid backend");
3956
+ return candidates[0];
3948
3957
  };
3949
- var deriveRepoPath3 = (repoUrl) => {
3950
- const trimmed = repoUrl.replace(/\/$/, "");
3951
- const last = trimmed.split("/").pop();
3952
- if (!last) {
3953
- return "repo";
3958
+ var packLocalCli = (logger) => {
3959
+ const cliDir = resolve5(dirname3(fileURLToPath(import.meta.url)), "..");
3960
+ const packageJson = resolve5(cliDir, "package.json");
3961
+ if (!existsSync(packageJson)) {
3962
+ return void 0;
3954
3963
  }
3955
- return last.endsWith(".git") ? last.slice(0, -4) : last;
3956
- };
3957
- var normalizePublicUrl2 = (value) => {
3958
- let parsed;
3959
- try {
3960
- parsed = new URL(value);
3961
- } catch {
3962
- throw new Error("invalid tunnel url");
3964
+ const result = spawnSync("npm", ["pack"], { cwd: cliDir, encoding: "utf8" });
3965
+ if (result.status !== 0) {
3966
+ const stderr = result.stderr?.toString().trim();
3967
+ logger.warn(`Local CLI pack failed${stderr ? ` (${stderr})` : ""}`);
3968
+ return void 0;
3963
3969
  }
3964
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
3965
- throw new Error("invalid tunnel url");
3970
+ const lines = result.stdout?.toString().trim().split(/\r?\n/).filter(Boolean) ?? [];
3971
+ const filename = lines[lines.length - 1];
3972
+ if (!filename) {
3973
+ logger.warn("Local CLI pack failed (no output)");
3974
+ return void 0;
3966
3975
  }
3967
- return value.endsWith("/") ? value.slice(0, -1) : value;
3976
+ return resolve5(cliDir, filename);
3968
3977
  };
3969
- var normalizeExternalUrl = (value) => {
3970
- let parsed;
3971
- try {
3972
- parsed = new URL(value);
3973
- } catch {
3974
- throw new Error("invalid public url");
3975
- }
3976
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
3977
- throw new Error("invalid public url");
3978
+ var createDefaultLogger = () => ({
3979
+ info: (message) => console.log(message),
3980
+ warn: (message) => console.warn(message),
3981
+ error: (message) => console.error(message)
3982
+ });
3983
+ var resolveBackendMode = (value) => {
3984
+ if (!value) {
3985
+ return "tmux";
3978
3986
  }
3979
- return value.endsWith("/") ? value.slice(0, -1) : value;
3980
- };
3981
- var buildShareUrl = (publicUrl, token) => {
3982
- let parsed;
3983
- try {
3984
- parsed = new URL(publicUrl);
3985
- } catch {
3986
- throw new Error("invalid public url");
3987
+ if (value === "tmux" || value === "sandbox-daytona") {
3988
+ return value;
3987
3989
  }
3988
- const basePath = parsed.pathname.endsWith("/") ? parsed.pathname : `${parsed.pathname}/`;
3989
- parsed.pathname = `${basePath}__tb/s/${token}`;
3990
- parsed.hash = "";
3991
- return parsed.toString();
3990
+ throw new Error("invalid backend");
3992
3991
  };
3993
3992
  var resolveTunnelMode = (value) => {
3994
3993
  if (!value) {
@@ -4047,7 +4046,7 @@ var startCommand = async (options, deps = {}) => {
4047
4046
  target: env.TERMBRIDGE_DAYTONA_TARGET,
4048
4047
  repoUrl: sandboxRepo,
4049
4048
  repoBranch: options.sandboxBranch ?? env.TERMBRIDGE_SANDBOX_BRANCH,
4050
- repoPath: options.sandboxPath ?? env.TERMBRIDGE_SANDBOX_PATH ?? deriveRepoPath3(sandboxRepo),
4049
+ repoPath: options.sandboxPath ?? env.TERMBRIDGE_SANDBOX_PATH ?? deriveRepoPath(sandboxRepo),
4051
4050
  sandboxName: options.sandboxName ?? env.TERMBRIDGE_SANDBOX_NAME,
4052
4051
  public: sandboxPublic,
4053
4052
  deleteOnExit: sandboxDeleteOnExit,
@@ -4112,7 +4111,7 @@ var startCommand = async (options, deps = {}) => {
4112
4111
  const tunnelTokenRaw = options.tunnelToken ?? env.TERMBRIDGE_TUNNEL_TOKEN;
4113
4112
  const tunnelToken = tunnelTokenRaw?.trim() || void 0;
4114
4113
  const tunnelUrlRaw = options.tunnelUrl ?? env.TERMBRIDGE_TUNNEL_URL;
4115
- const tunnelUrl = tunnelToken && tunnelUrlRaw ? normalizePublicUrl2(tunnelUrlRaw) : void 0;
4114
+ const tunnelUrl = tunnelToken && tunnelUrlRaw ? normalizePublicUrl(tunnelUrlRaw) : void 0;
4116
4115
  let devProxyUrl = options.devProxyUrl;
4117
4116
  let devProxyHeaders;
4118
4117
  let publicUrl = "";
@@ -4123,7 +4122,7 @@ var startCommand = async (options, deps = {}) => {
4123
4122
  if (tunnelToken || tunnelUrlRaw) {
4124
4123
  throw new Error("tunnel token/url not supported when tunnel disabled");
4125
4124
  }
4126
- publicUrl = normalizeExternalUrl(publicUrlOverride);
4125
+ publicUrl = normalizePublicUrl(publicUrlOverride);
4127
4126
  }
4128
4127
  if (!devProxyUrl && backendMode === "sandbox-daytona" && sandboxPreviewPort) {
4129
4128
  const previewInfo = await terminalBackend.getPreviewUrl?.(sandboxPreviewPort);