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/
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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/
|
|
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
|
|
2667
|
-
|
|
2668
|
-
|
|
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
|
-
|
|
2814
|
-
|
|
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, ...
|
|
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
|
|
3152
|
+
import { basename, dirname as dirname2, join, relative, resolve as resolve4 } from "path";
|
|
3039
3153
|
import { readdir, stat } from "fs/promises";
|
|
3040
|
-
var
|
|
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
|
|
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 =
|
|
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
|
|
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 ??
|
|
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 ??
|
|
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/
|
|
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
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
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
|
-
|
|
3956
|
+
return candidates[0];
|
|
3948
3957
|
};
|
|
3949
|
-
var
|
|
3950
|
-
const
|
|
3951
|
-
const
|
|
3952
|
-
if (!
|
|
3953
|
-
return
|
|
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
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
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
|
-
|
|
3965
|
-
|
|
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
|
|
3976
|
+
return resolve5(cliDir, filename);
|
|
3968
3977
|
};
|
|
3969
|
-
var
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ??
|
|
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 ?
|
|
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 =
|
|
4125
|
+
publicUrl = normalizePublicUrl(publicUrlOverride);
|
|
4127
4126
|
}
|
|
4128
4127
|
if (!devProxyUrl && backendMode === "sandbox-daytona" && sandboxPreviewPort) {
|
|
4129
4128
|
const previewInfo = await terminalBackend.getPreviewUrl?.(sandboxPreviewPort);
|