termbridge 0.3.8 → 0.3.10
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 {
|
|
@@ -2526,7 +2617,7 @@ var TERMINAL_CONTROL_KEYS = [
|
|
|
2526
2617
|
|
|
2527
2618
|
// src/server/static.ts
|
|
2528
2619
|
import { readFile } from "fs/promises";
|
|
2529
|
-
import { extname, resolve as
|
|
2620
|
+
import { extname, resolve as resolve3 } from "path";
|
|
2530
2621
|
var contentTypes = {
|
|
2531
2622
|
".html": "text/html",
|
|
2532
2623
|
".js": "text/javascript",
|
|
@@ -2552,7 +2643,7 @@ var createStaticHandler = (uiDistPath, basePath) => {
|
|
|
2552
2643
|
}
|
|
2553
2644
|
const relative2 = url.pathname.slice(normalizedBase.length) || "/";
|
|
2554
2645
|
const filePath = relative2 === "/" ? "/index.html" : relative2;
|
|
2555
|
-
const absolutePath =
|
|
2646
|
+
const absolutePath = resolve3(uiDistPath, `.${filePath}`);
|
|
2556
2647
|
try {
|
|
2557
2648
|
const payload = await readFile(absolutePath);
|
|
2558
2649
|
response.statusCode = 200;
|
|
@@ -2561,7 +2652,7 @@ var createStaticHandler = (uiDistPath, basePath) => {
|
|
|
2561
2652
|
return true;
|
|
2562
2653
|
} catch {
|
|
2563
2654
|
try {
|
|
2564
|
-
const fallback = await readFile(
|
|
2655
|
+
const fallback = await readFile(resolve3(uiDistPath, "index.html"));
|
|
2565
2656
|
response.statusCode = 200;
|
|
2566
2657
|
response.setHeader("Content-Type", "text/html");
|
|
2567
2658
|
response.end(fallback);
|
|
@@ -3035,9 +3126,9 @@ var installAgents = async (sandbox, options, logger) => {
|
|
|
3035
3126
|
|
|
3036
3127
|
// src/sandbox/daytona/agent-auth.ts
|
|
3037
3128
|
import { homedir } from "os";
|
|
3038
|
-
import { basename, dirname as dirname2, join, relative, resolve as
|
|
3129
|
+
import { basename, dirname as dirname2, join, relative, resolve as resolve4 } from "path";
|
|
3039
3130
|
import { readdir, stat } from "fs/promises";
|
|
3040
|
-
var
|
|
3131
|
+
var expandHome2 = (value, home) => {
|
|
3041
3132
|
if (value === "~") {
|
|
3042
3133
|
return home;
|
|
3043
3134
|
}
|
|
@@ -3048,7 +3139,7 @@ var expandHome = (value, home) => {
|
|
|
3048
3139
|
};
|
|
3049
3140
|
var resolveDestination = (spec, sourcePath, localHome, remoteHome) => {
|
|
3050
3141
|
if (spec.destination) {
|
|
3051
|
-
return
|
|
3142
|
+
return expandHome2(spec.destination, remoteHome);
|
|
3052
3143
|
}
|
|
3053
3144
|
if (sourcePath.startsWith(localHome)) {
|
|
3054
3145
|
const rel = relative(localHome, sourcePath);
|
|
@@ -3088,7 +3179,7 @@ var syncAgentAuth = async (sandbox, options, logger) => {
|
|
|
3088
3179
|
const uploads = [];
|
|
3089
3180
|
const mkdirs = /* @__PURE__ */ new Set();
|
|
3090
3181
|
for (const spec of options.specs) {
|
|
3091
|
-
const sourcePath =
|
|
3182
|
+
const sourcePath = resolve4(expandHome2(spec.source, localHome));
|
|
3092
3183
|
let stats;
|
|
3093
3184
|
try {
|
|
3094
3185
|
stats = await stat(sourcePath);
|
|
@@ -3139,7 +3230,7 @@ var noopLogger = {
|
|
|
3139
3230
|
warn: () => void 0,
|
|
3140
3231
|
error: () => void 0
|
|
3141
3232
|
};
|
|
3142
|
-
var
|
|
3233
|
+
var deriveRepoPath2 = (repoUrl) => {
|
|
3143
3234
|
const trimmed = repoUrl.replace(/\/$/, "");
|
|
3144
3235
|
const last = trimmed.split("/").pop();
|
|
3145
3236
|
if (!last) {
|
|
@@ -3165,7 +3256,7 @@ var createSandboxDaytonaBackend = (options) => {
|
|
|
3165
3256
|
logger.info(`Sandbox (Daytona): creating sandbox ${name}`);
|
|
3166
3257
|
const sandbox = await daytona.create({ name, public: options.public });
|
|
3167
3258
|
await sandbox.start();
|
|
3168
|
-
const repoPath = options.repoPath ??
|
|
3259
|
+
const repoPath = options.repoPath ?? deriveRepoPath2(options.repoUrl);
|
|
3169
3260
|
logger.info(`Sandbox (Daytona): cloning ${options.repoUrl}`);
|
|
3170
3261
|
await sandbox.git.clone(
|
|
3171
3262
|
options.repoUrl,
|
|
@@ -3353,8 +3444,6 @@ var createSandboxDaytonaBackend = (options) => {
|
|
|
3353
3444
|
|
|
3354
3445
|
// src/sandbox/daytona/agent-auto.ts
|
|
3355
3446
|
import { homedir as homedir2 } from "os";
|
|
3356
|
-
import { resolve as resolve4 } from "path";
|
|
3357
|
-
import { statSync } from "fs";
|
|
3358
3447
|
var agentDefinitions = {
|
|
3359
3448
|
"claude-code": {
|
|
3360
3449
|
packages: ["@anthropic-ai/claude-code"],
|
|
@@ -3382,23 +3471,6 @@ var agentAliasMap = {
|
|
|
3382
3471
|
"@openai/codex": "codex",
|
|
3383
3472
|
opencode: "opencode"
|
|
3384
3473
|
};
|
|
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
3474
|
var normalizeAgentName = (value) => {
|
|
3403
3475
|
const normalized = value.trim().toLowerCase();
|
|
3404
3476
|
if (normalized === "") {
|
|
@@ -3522,30 +3594,6 @@ var installLocalTermbridge = async (sandbox, startOptions, logger) => {
|
|
|
3522
3594
|
logger.info("Sandbox (Daytona): installed local termbridge package");
|
|
3523
3595
|
return { useLocal: true };
|
|
3524
3596
|
};
|
|
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
3597
|
var delay = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
3550
3598
|
var ensureTmux = async (sandbox, logger) => {
|
|
3551
3599
|
const check = await sandbox.process.executeCommand("command -v tmux");
|
|
@@ -3673,7 +3721,7 @@ var createSandboxDaytonaServerProvider = (options = {}) => {
|
|
|
3673
3721
|
const sandbox = await daytona.create({ name, public: startOptions.public });
|
|
3674
3722
|
sandboxRef = sandbox;
|
|
3675
3723
|
await sandbox.start();
|
|
3676
|
-
const repoPath = startOptions.repoPath ??
|
|
3724
|
+
const repoPath = startOptions.repoPath ?? deriveRepoPath(startOptions.repoUrl);
|
|
3677
3725
|
logger.info(`Sandbox (Daytona): cloning ${startOptions.repoUrl}`);
|
|
3678
3726
|
await sandbox.git.clone(
|
|
3679
3727
|
startOptions.repoUrl,
|
|
@@ -3837,36 +3885,6 @@ var createDefaultLogger = () => ({
|
|
|
3837
3885
|
warn: (message) => console.warn(message),
|
|
3838
3886
|
error: (message) => console.error(message)
|
|
3839
3887
|
});
|
|
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
3888
|
var defaultAgentEnvKeys = [
|
|
3871
3889
|
"OPENAI_API_KEY",
|
|
3872
3890
|
"OPENAI_BASE_URL",
|
|
@@ -3946,50 +3964,6 @@ var resolveBackendMode = (value) => {
|
|
|
3946
3964
|
}
|
|
3947
3965
|
throw new Error("invalid backend");
|
|
3948
3966
|
};
|
|
3949
|
-
var deriveRepoPath3 = (repoUrl) => {
|
|
3950
|
-
const trimmed = repoUrl.replace(/\/$/, "");
|
|
3951
|
-
const last = trimmed.split("/").pop();
|
|
3952
|
-
if (!last) {
|
|
3953
|
-
return "repo";
|
|
3954
|
-
}
|
|
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");
|
|
3963
|
-
}
|
|
3964
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
3965
|
-
throw new Error("invalid tunnel url");
|
|
3966
|
-
}
|
|
3967
|
-
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
3968
|
-
};
|
|
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
|
-
}
|
|
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
|
-
}
|
|
3988
|
-
const basePath = parsed.pathname.endsWith("/") ? parsed.pathname : `${parsed.pathname}/`;
|
|
3989
|
-
parsed.pathname = `${basePath}__tb/s/${token}`;
|
|
3990
|
-
parsed.hash = "";
|
|
3991
|
-
return parsed.toString();
|
|
3992
|
-
};
|
|
3993
3967
|
var resolveTunnelMode = (value) => {
|
|
3994
3968
|
if (!value) {
|
|
3995
3969
|
return "cloudflare";
|
|
@@ -4020,8 +3994,8 @@ var startCommand = async (options, deps = {}) => {
|
|
|
4020
3994
|
const cookieSameSite = cookieSameSiteRaw === "none" ? "None" : cookieSameSiteRaw === "strict" ? "Strict" : cookieSameSiteRaw === "lax" ? "Lax" : sandboxDirect && !insecureCookie ? "None" : "Lax";
|
|
4021
3995
|
const auth = (deps.createAuth ?? (() => createAuth({
|
|
4022
3996
|
tokenTtlMs: 9e4,
|
|
4023
|
-
sessionIdleMs:
|
|
4024
|
-
sessionMaxMs:
|
|
3997
|
+
sessionIdleMs: Infinity,
|
|
3998
|
+
sessionMaxMs: Infinity,
|
|
4025
3999
|
cookieSecure: !insecureCookie,
|
|
4026
4000
|
cookieSameSite
|
|
4027
4001
|
})))();
|
|
@@ -4047,7 +4021,7 @@ var startCommand = async (options, deps = {}) => {
|
|
|
4047
4021
|
target: env.TERMBRIDGE_DAYTONA_TARGET,
|
|
4048
4022
|
repoUrl: sandboxRepo,
|
|
4049
4023
|
repoBranch: options.sandboxBranch ?? env.TERMBRIDGE_SANDBOX_BRANCH,
|
|
4050
|
-
repoPath: options.sandboxPath ?? env.TERMBRIDGE_SANDBOX_PATH ??
|
|
4024
|
+
repoPath: options.sandboxPath ?? env.TERMBRIDGE_SANDBOX_PATH ?? deriveRepoPath(sandboxRepo),
|
|
4051
4025
|
sandboxName: options.sandboxName ?? env.TERMBRIDGE_SANDBOX_NAME,
|
|
4052
4026
|
public: sandboxPublic,
|
|
4053
4027
|
deleteOnExit: sandboxDeleteOnExit,
|
|
@@ -4112,7 +4086,7 @@ var startCommand = async (options, deps = {}) => {
|
|
|
4112
4086
|
const tunnelTokenRaw = options.tunnelToken ?? env.TERMBRIDGE_TUNNEL_TOKEN;
|
|
4113
4087
|
const tunnelToken = tunnelTokenRaw?.trim() || void 0;
|
|
4114
4088
|
const tunnelUrlRaw = options.tunnelUrl ?? env.TERMBRIDGE_TUNNEL_URL;
|
|
4115
|
-
const tunnelUrl = tunnelToken && tunnelUrlRaw ?
|
|
4089
|
+
const tunnelUrl = tunnelToken && tunnelUrlRaw ? normalizePublicUrl(tunnelUrlRaw) : void 0;
|
|
4116
4090
|
let devProxyUrl = options.devProxyUrl;
|
|
4117
4091
|
let devProxyHeaders;
|
|
4118
4092
|
let publicUrl = "";
|
|
@@ -4123,7 +4097,7 @@ var startCommand = async (options, deps = {}) => {
|
|
|
4123
4097
|
if (tunnelToken || tunnelUrlRaw) {
|
|
4124
4098
|
throw new Error("tunnel token/url not supported when tunnel disabled");
|
|
4125
4099
|
}
|
|
4126
|
-
publicUrl =
|
|
4100
|
+
publicUrl = normalizePublicUrl(publicUrlOverride);
|
|
4127
4101
|
}
|
|
4128
4102
|
if (!devProxyUrl && backendMode === "sandbox-daytona" && sandboxPreviewPort) {
|
|
4129
4103
|
const previewInfo = await terminalBackend.getPreviewUrl?.(sandboxPreviewPort);
|