webmux 0.27.0 → 0.27.1
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/README.md +2 -0
- package/backend/dist/server.js +36 -13
- package/bin/webmux.js +37 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,6 +130,8 @@ profiles:
|
|
|
130
130
|
You are running inside a sandboxed container.
|
|
131
131
|
Backend port: ${PORT}. Frontend port: ${FRONTEND_PORT}.
|
|
132
132
|
|
|
133
|
+
Custom sandbox images should make `claude` or `codex` available on the container's normal `PATH` (for example with `ENV PATH=/your/tool/bin:$PATH`). webmux does not rely on login-shell dotfiles like `.bashrc` to discover agent binaries inside the container.
|
|
134
|
+
|
|
133
135
|
integrations:
|
|
134
136
|
github:
|
|
135
137
|
autoRemoveOnMerge: true
|
package/backend/dist/server.js
CHANGED
|
@@ -8687,12 +8687,16 @@ function allocateServicePorts(existingMetas, services) {
|
|
|
8687
8687
|
}
|
|
8688
8688
|
|
|
8689
8689
|
// backend/src/services/agent-service.ts
|
|
8690
|
+
var DOCKER_PATH_FALLBACK = "/root/.local/bin:/usr/local/bin:/root/.bun/bin:/root/.cargo/bin";
|
|
8690
8691
|
function quoteShell(value) {
|
|
8691
8692
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
8692
8693
|
}
|
|
8693
8694
|
function buildRuntimeBootstrap(runtimeEnvPath) {
|
|
8694
8695
|
return `set -a; . ${quoteShell(runtimeEnvPath)}; set +a`;
|
|
8695
8696
|
}
|
|
8697
|
+
function buildDockerRuntimeBootstrap(runtimeEnvPath) {
|
|
8698
|
+
return `${buildRuntimeBootstrap(runtimeEnvPath)}; export PATH="$PATH:${DOCKER_PATH_FALLBACK}"`;
|
|
8699
|
+
}
|
|
8696
8700
|
function buildAgentInvocation(input) {
|
|
8697
8701
|
if (input.agent === "codex") {
|
|
8698
8702
|
const yoloFlag2 = input.yolo ? " --yolo" : "";
|
|
@@ -8715,11 +8719,11 @@ function buildAgentInvocation(input) {
|
|
|
8715
8719
|
}
|
|
8716
8720
|
return `claude${yoloFlag}${promptSuffix}`;
|
|
8717
8721
|
}
|
|
8718
|
-
function buildAgentCommand(input) {
|
|
8719
|
-
return `${
|
|
8722
|
+
function buildAgentCommand(input, bootstrap = buildRuntimeBootstrap) {
|
|
8723
|
+
return `${bootstrap(input.runtimeEnvPath)}; ${buildAgentInvocation(input)}`;
|
|
8720
8724
|
}
|
|
8721
8725
|
function buildDockerExecCommand(containerName, worktreePath, command) {
|
|
8722
|
-
return `docker exec -it -w ${quoteShell(worktreePath)} ${quoteShell(containerName)}
|
|
8726
|
+
return `docker exec -it -w ${quoteShell(worktreePath)} ${quoteShell(containerName)} /bin/sh -c ${quoteShell(command)}`;
|
|
8723
8727
|
}
|
|
8724
8728
|
function buildManagedShellCommand(runtimeEnvPath, shellPath = Bun.env.SHELL || "/bin/bash") {
|
|
8725
8729
|
return `bash -lc ${quoteShell(`${buildRuntimeBootstrap(runtimeEnvPath)}; exec ${quoteShell(shellPath)} -i`)}`;
|
|
@@ -8727,11 +8731,11 @@ function buildManagedShellCommand(runtimeEnvPath, shellPath = Bun.env.SHELL || "
|
|
|
8727
8731
|
function buildAgentPaneCommand(input) {
|
|
8728
8732
|
return buildAgentCommand(input);
|
|
8729
8733
|
}
|
|
8730
|
-
function buildDockerShellCommand(containerName, worktreePath, runtimeEnvPath, shellPath =
|
|
8731
|
-
return buildDockerExecCommand(containerName, worktreePath, `${
|
|
8734
|
+
function buildDockerShellCommand(containerName, worktreePath, runtimeEnvPath, shellPath = "/bin/bash") {
|
|
8735
|
+
return buildDockerExecCommand(containerName, worktreePath, `${buildDockerRuntimeBootstrap(runtimeEnvPath)}; if [ -x ${quoteShell(shellPath)} ]; then exec ${quoteShell(shellPath)} -i; elif [ -x /bin/sh ]; then exec /bin/sh -i; else echo 'webmux: no shell found in container' >&2; exit 127; fi`);
|
|
8732
8736
|
}
|
|
8733
8737
|
function buildDockerAgentPaneCommand(input) {
|
|
8734
|
-
return
|
|
8738
|
+
return buildAgentCommand(input, buildDockerRuntimeBootstrap);
|
|
8735
8739
|
}
|
|
8736
8740
|
|
|
8737
8741
|
// backend/src/services/session-service.ts
|
|
@@ -9277,12 +9281,33 @@ function generateFallbackBranchName() {
|
|
|
9277
9281
|
}
|
|
9278
9282
|
|
|
9279
9283
|
// backend/src/services/lifecycle-service.ts
|
|
9284
|
+
var DOCKER_CONTROL_HOST = "host.docker.internal";
|
|
9280
9285
|
function toErrorMessage2(error) {
|
|
9281
9286
|
return error instanceof Error ? error.message : String(error);
|
|
9282
9287
|
}
|
|
9283
9288
|
function stringifyStartupEnvValue(value) {
|
|
9284
9289
|
return typeof value === "boolean" ? String(value) : value;
|
|
9285
9290
|
}
|
|
9291
|
+
function trimTrailingSlashes(value) {
|
|
9292
|
+
return value.replace(/\/+$/, "");
|
|
9293
|
+
}
|
|
9294
|
+
function isLoopbackHostname(hostname) {
|
|
9295
|
+
return hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1" || hostname === "[::1]";
|
|
9296
|
+
}
|
|
9297
|
+
function buildRuntimeControlBaseUrl(controlBaseUrl, runtime) {
|
|
9298
|
+
const trimmed = trimTrailingSlashes(controlBaseUrl);
|
|
9299
|
+
if (runtime !== "docker")
|
|
9300
|
+
return trimmed;
|
|
9301
|
+
try {
|
|
9302
|
+
const url = new URL(trimmed);
|
|
9303
|
+
if (isLoopbackHostname(url.hostname)) {
|
|
9304
|
+
url.hostname = DOCKER_CONTROL_HOST;
|
|
9305
|
+
}
|
|
9306
|
+
return trimTrailingSlashes(url.toString());
|
|
9307
|
+
} catch {
|
|
9308
|
+
return trimmed;
|
|
9309
|
+
}
|
|
9310
|
+
}
|
|
9286
9311
|
function prefixAgentBranch(agent, branch) {
|
|
9287
9312
|
return `${agent}-${branch}`;
|
|
9288
9313
|
}
|
|
@@ -9573,7 +9598,7 @@ class LifecycleService {
|
|
|
9573
9598
|
allocatedPorts: await this.allocatePorts(),
|
|
9574
9599
|
runtimeEnvExtras: { WEBMUX_WORKTREE_PATH: resolved.entry.path },
|
|
9575
9600
|
dotenvValues,
|
|
9576
|
-
controlUrl: this.controlUrl(),
|
|
9601
|
+
controlUrl: this.controlUrl(profile.runtime),
|
|
9577
9602
|
controlToken: await this.deps.getControlToken()
|
|
9578
9603
|
});
|
|
9579
9604
|
}
|
|
@@ -9594,7 +9619,7 @@ class LifecycleService {
|
|
|
9594
9619
|
}, dotenvValues);
|
|
9595
9620
|
await writeRuntimeEnv(input.gitDir, runtimeEnv);
|
|
9596
9621
|
const controlEnv = buildControlEnvMap({
|
|
9597
|
-
controlUrl: this.controlUrl(),
|
|
9622
|
+
controlUrl: this.controlUrl(input.meta.runtime),
|
|
9598
9623
|
controlToken: await this.deps.getControlToken(),
|
|
9599
9624
|
worktreeId: input.meta.worktreeId,
|
|
9600
9625
|
branch: input.meta.branch
|
|
@@ -9656,8 +9681,6 @@ class LifecycleService {
|
|
|
9656
9681
|
paneCommands: containerName ? {
|
|
9657
9682
|
agent: buildDockerAgentPaneCommand({
|
|
9658
9683
|
agent: input.agent,
|
|
9659
|
-
containerName,
|
|
9660
|
-
worktreePath: input.worktreePath,
|
|
9661
9684
|
runtimeEnvPath: input.initialized.paths.runtimeEnvPath,
|
|
9662
9685
|
yolo: input.profile.yolo === true,
|
|
9663
9686
|
systemPrompt,
|
|
@@ -9718,8 +9741,8 @@ class LifecycleService {
|
|
|
9718
9741
|
throw new LifecycleError(`Worktree has uncommitted changes: ${entry.branch ?? entry.path}`, 409);
|
|
9719
9742
|
}
|
|
9720
9743
|
}
|
|
9721
|
-
controlUrl() {
|
|
9722
|
-
return `${this.deps.controlBaseUrl
|
|
9744
|
+
controlUrl(runtime) {
|
|
9745
|
+
return `${buildRuntimeControlBaseUrl(this.deps.controlBaseUrl, runtime)}/api/runtime/events`;
|
|
9723
9746
|
}
|
|
9724
9747
|
async removeResolvedWorktree(resolved) {
|
|
9725
9748
|
await this.runLifecycleHook({
|
|
@@ -9822,7 +9845,7 @@ class LifecycleService {
|
|
|
9822
9845
|
startupEnvValues: await this.buildStartupEnvValues(input.envOverrides),
|
|
9823
9846
|
allocatedPorts: await this.allocatePorts(),
|
|
9824
9847
|
runtimeEnvExtras: { WEBMUX_WORKTREE_PATH: worktreePath },
|
|
9825
|
-
controlUrl: this.controlUrl(),
|
|
9848
|
+
controlUrl: this.controlUrl(profile.runtime),
|
|
9826
9849
|
controlToken: await this.deps.getControlToken(),
|
|
9827
9850
|
deleteBranchOnRollback
|
|
9828
9851
|
}, {
|
package/bin/webmux.js
CHANGED
|
@@ -11562,6 +11562,9 @@ function quoteShell(value) {
|
|
|
11562
11562
|
function buildRuntimeBootstrap(runtimeEnvPath) {
|
|
11563
11563
|
return `set -a; . ${quoteShell(runtimeEnvPath)}; set +a`;
|
|
11564
11564
|
}
|
|
11565
|
+
function buildDockerRuntimeBootstrap(runtimeEnvPath) {
|
|
11566
|
+
return `${buildRuntimeBootstrap(runtimeEnvPath)}; export PATH="$PATH:${DOCKER_PATH_FALLBACK}"`;
|
|
11567
|
+
}
|
|
11565
11568
|
function buildAgentInvocation(input) {
|
|
11566
11569
|
if (input.agent === "codex") {
|
|
11567
11570
|
const yoloFlag2 = input.yolo ? " --yolo" : "";
|
|
@@ -11584,11 +11587,11 @@ function buildAgentInvocation(input) {
|
|
|
11584
11587
|
}
|
|
11585
11588
|
return `claude${yoloFlag}${promptSuffix}`;
|
|
11586
11589
|
}
|
|
11587
|
-
function buildAgentCommand(input) {
|
|
11588
|
-
return `${
|
|
11590
|
+
function buildAgentCommand(input, bootstrap = buildRuntimeBootstrap) {
|
|
11591
|
+
return `${bootstrap(input.runtimeEnvPath)}; ${buildAgentInvocation(input)}`;
|
|
11589
11592
|
}
|
|
11590
11593
|
function buildDockerExecCommand(containerName2, worktreePath, command) {
|
|
11591
|
-
return `docker exec -it -w ${quoteShell(worktreePath)} ${quoteShell(containerName2)}
|
|
11594
|
+
return `docker exec -it -w ${quoteShell(worktreePath)} ${quoteShell(containerName2)} /bin/sh -c ${quoteShell(command)}`;
|
|
11592
11595
|
}
|
|
11593
11596
|
function buildManagedShellCommand(runtimeEnvPath, shellPath = Bun.env.SHELL || "/bin/bash") {
|
|
11594
11597
|
return `bash -lc ${quoteShell(`${buildRuntimeBootstrap(runtimeEnvPath)}; exec ${quoteShell(shellPath)} -i`)}`;
|
|
@@ -11596,12 +11599,13 @@ function buildManagedShellCommand(runtimeEnvPath, shellPath = Bun.env.SHELL || "
|
|
|
11596
11599
|
function buildAgentPaneCommand(input) {
|
|
11597
11600
|
return buildAgentCommand(input);
|
|
11598
11601
|
}
|
|
11599
|
-
function buildDockerShellCommand(containerName2, worktreePath, runtimeEnvPath, shellPath =
|
|
11600
|
-
return buildDockerExecCommand(containerName2, worktreePath, `${
|
|
11602
|
+
function buildDockerShellCommand(containerName2, worktreePath, runtimeEnvPath, shellPath = "/bin/bash") {
|
|
11603
|
+
return buildDockerExecCommand(containerName2, worktreePath, `${buildDockerRuntimeBootstrap(runtimeEnvPath)}; if [ -x ${quoteShell(shellPath)} ]; then exec ${quoteShell(shellPath)} -i; elif [ -x /bin/sh ]; then exec /bin/sh -i; else echo 'webmux: no shell found in container' >&2; exit 127; fi`);
|
|
11601
11604
|
}
|
|
11602
11605
|
function buildDockerAgentPaneCommand(input) {
|
|
11603
|
-
return
|
|
11606
|
+
return buildAgentCommand(input, buildDockerRuntimeBootstrap);
|
|
11604
11607
|
}
|
|
11608
|
+
var DOCKER_PATH_FALLBACK = "/root/.local/bin:/usr/local/bin:/root/.bun/bin:/root/.cargo/bin";
|
|
11605
11609
|
|
|
11606
11610
|
// backend/src/services/session-service.ts
|
|
11607
11611
|
import { resolve as resolve6 } from "path";
|
|
@@ -11855,6 +11859,26 @@ function toErrorMessage2(error) {
|
|
|
11855
11859
|
function stringifyStartupEnvValue(value) {
|
|
11856
11860
|
return typeof value === "boolean" ? String(value) : value;
|
|
11857
11861
|
}
|
|
11862
|
+
function trimTrailingSlashes(value) {
|
|
11863
|
+
return value.replace(/\/+$/, "");
|
|
11864
|
+
}
|
|
11865
|
+
function isLoopbackHostname(hostname) {
|
|
11866
|
+
return hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1" || hostname === "[::1]";
|
|
11867
|
+
}
|
|
11868
|
+
function buildRuntimeControlBaseUrl(controlBaseUrl, runtime) {
|
|
11869
|
+
const trimmed = trimTrailingSlashes(controlBaseUrl);
|
|
11870
|
+
if (runtime !== "docker")
|
|
11871
|
+
return trimmed;
|
|
11872
|
+
try {
|
|
11873
|
+
const url = new URL(trimmed);
|
|
11874
|
+
if (isLoopbackHostname(url.hostname)) {
|
|
11875
|
+
url.hostname = DOCKER_CONTROL_HOST;
|
|
11876
|
+
}
|
|
11877
|
+
return trimTrailingSlashes(url.toString());
|
|
11878
|
+
} catch {
|
|
11879
|
+
return trimmed;
|
|
11880
|
+
}
|
|
11881
|
+
}
|
|
11858
11882
|
function prefixAgentBranch(agent, branch) {
|
|
11859
11883
|
return `${agent}-${branch}`;
|
|
11860
11884
|
}
|
|
@@ -12137,7 +12161,7 @@ class LifecycleService {
|
|
|
12137
12161
|
allocatedPorts: await this.allocatePorts(),
|
|
12138
12162
|
runtimeEnvExtras: { WEBMUX_WORKTREE_PATH: resolved.entry.path },
|
|
12139
12163
|
dotenvValues,
|
|
12140
|
-
controlUrl: this.controlUrl(),
|
|
12164
|
+
controlUrl: this.controlUrl(profile.runtime),
|
|
12141
12165
|
controlToken: await this.deps.getControlToken()
|
|
12142
12166
|
});
|
|
12143
12167
|
}
|
|
@@ -12158,7 +12182,7 @@ class LifecycleService {
|
|
|
12158
12182
|
}, dotenvValues);
|
|
12159
12183
|
await writeRuntimeEnv(input.gitDir, runtimeEnv);
|
|
12160
12184
|
const controlEnv = buildControlEnvMap({
|
|
12161
|
-
controlUrl: this.controlUrl(),
|
|
12185
|
+
controlUrl: this.controlUrl(input.meta.runtime),
|
|
12162
12186
|
controlToken: await this.deps.getControlToken(),
|
|
12163
12187
|
worktreeId: input.meta.worktreeId,
|
|
12164
12188
|
branch: input.meta.branch
|
|
@@ -12220,8 +12244,6 @@ class LifecycleService {
|
|
|
12220
12244
|
paneCommands: containerName2 ? {
|
|
12221
12245
|
agent: buildDockerAgentPaneCommand({
|
|
12222
12246
|
agent: input.agent,
|
|
12223
|
-
containerName: containerName2,
|
|
12224
|
-
worktreePath: input.worktreePath,
|
|
12225
12247
|
runtimeEnvPath: input.initialized.paths.runtimeEnvPath,
|
|
12226
12248
|
yolo: input.profile.yolo === true,
|
|
12227
12249
|
systemPrompt,
|
|
@@ -12282,8 +12304,8 @@ class LifecycleService {
|
|
|
12282
12304
|
throw new LifecycleError(`Worktree has uncommitted changes: ${entry.branch ?? entry.path}`, 409);
|
|
12283
12305
|
}
|
|
12284
12306
|
}
|
|
12285
|
-
controlUrl() {
|
|
12286
|
-
return `${this.deps.controlBaseUrl
|
|
12307
|
+
controlUrl(runtime) {
|
|
12308
|
+
return `${buildRuntimeControlBaseUrl(this.deps.controlBaseUrl, runtime)}/api/runtime/events`;
|
|
12287
12309
|
}
|
|
12288
12310
|
async removeResolvedWorktree(resolved) {
|
|
12289
12311
|
await this.runLifecycleHook({
|
|
@@ -12386,7 +12408,7 @@ class LifecycleService {
|
|
|
12386
12408
|
startupEnvValues: await this.buildStartupEnvValues(input.envOverrides),
|
|
12387
12409
|
allocatedPorts: await this.allocatePorts(),
|
|
12388
12410
|
runtimeEnvExtras: { WEBMUX_WORKTREE_PATH: worktreePath },
|
|
12389
|
-
controlUrl: this.controlUrl(),
|
|
12411
|
+
controlUrl: this.controlUrl(profile.runtime),
|
|
12390
12412
|
controlToken: await this.deps.getControlToken(),
|
|
12391
12413
|
deleteBranchOnRollback
|
|
12392
12414
|
}, {
|
|
@@ -12456,7 +12478,7 @@ class LifecycleService {
|
|
|
12456
12478
|
return new LifecycleError(toErrorMessage2(error), 422);
|
|
12457
12479
|
}
|
|
12458
12480
|
}
|
|
12459
|
-
var LifecycleError;
|
|
12481
|
+
var DOCKER_CONTROL_HOST = "host.docker.internal", LifecycleError;
|
|
12460
12482
|
var init_lifecycle_service = __esm(() => {
|
|
12461
12483
|
init_agent_runtime();
|
|
12462
12484
|
init_fs();
|
|
@@ -13590,7 +13612,7 @@ import { fileURLToPath } from "url";
|
|
|
13590
13612
|
// package.json
|
|
13591
13613
|
var package_default = {
|
|
13592
13614
|
name: "webmux",
|
|
13593
|
-
version: "0.27.
|
|
13615
|
+
version: "0.27.1",
|
|
13594
13616
|
description: "Web dashboard for workmux \u2014 browser UI with embedded terminals, PR monitoring, and CI integration",
|
|
13595
13617
|
type: "module",
|
|
13596
13618
|
repository: {
|