webmux 0.23.0 → 0.24.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/backend/dist/server.js +199 -117
- package/bin/webmux.js +180 -108
- package/frontend/dist/assets/index-BRoz1T_W.js +151 -0
- package/frontend/dist/assets/{index-Dxe_PLr0.css → index-D9R5ycW2.css} +1 -1
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-C5K6jdn8.js +0 -150
package/bin/webmux.js
CHANGED
|
@@ -257,6 +257,9 @@ class BunGitGateway {
|
|
|
257
257
|
readWorktreeStatus(cwd) {
|
|
258
258
|
return readGitWorktreeStatus(cwd);
|
|
259
259
|
}
|
|
260
|
+
readStatus(cwd) {
|
|
261
|
+
return runGit(["status", "--short", "--untracked-files=all"], cwd);
|
|
262
|
+
}
|
|
260
263
|
createWorktree(opts) {
|
|
261
264
|
const args = ["worktree", "add"];
|
|
262
265
|
if (opts.mode === "new") {
|
|
@@ -10762,11 +10765,12 @@ function buildClaudeArgs(model, systemPrompt, prompt) {
|
|
|
10762
10765
|
systemPrompt,
|
|
10763
10766
|
"--output-format",
|
|
10764
10767
|
"text",
|
|
10765
|
-
"--no-session-persistence"
|
|
10768
|
+
"--no-session-persistence",
|
|
10769
|
+
"--model",
|
|
10770
|
+
model || DEFAULT_AUTO_NAME_MODEL,
|
|
10771
|
+
"--effort",
|
|
10772
|
+
"low"
|
|
10766
10773
|
];
|
|
10767
|
-
if (model) {
|
|
10768
|
-
args.push("--model", model);
|
|
10769
|
-
}
|
|
10770
10774
|
args.push(prompt);
|
|
10771
10775
|
return args;
|
|
10772
10776
|
}
|
|
@@ -10812,8 +10816,11 @@ class AutoNameService {
|
|
|
10812
10816
|
throw new Error(`'${cli}' CLI not found. Install it or check your PATH.`);
|
|
10813
10817
|
}
|
|
10814
10818
|
if (result.exitCode !== 0) {
|
|
10815
|
-
const
|
|
10816
|
-
|
|
10819
|
+
const stderr = result.stderr.trim();
|
|
10820
|
+
const stdout = result.stdout.trim();
|
|
10821
|
+
const output2 = stderr || stdout || `exit ${result.exitCode}`;
|
|
10822
|
+
const command = args.join(" ");
|
|
10823
|
+
throw new Error(`${cli} failed (command: ${command}): ${output2}`);
|
|
10817
10824
|
}
|
|
10818
10825
|
const output = result.stdout.trim();
|
|
10819
10826
|
if (!output) {
|
|
@@ -10822,7 +10829,7 @@ class AutoNameService {
|
|
|
10822
10829
|
return normalizeGeneratedBranchName(output);
|
|
10823
10830
|
}
|
|
10824
10831
|
}
|
|
10825
|
-
var MAX_BRANCH_LENGTH = 40, DEFAULT_SYSTEM_PROMPT;
|
|
10832
|
+
var MAX_BRANCH_LENGTH = 40, DEFAULT_AUTO_NAME_MODEL = "claude-haiku-4-5-20251001", DEFAULT_SYSTEM_PROMPT;
|
|
10826
10833
|
var init_auto_name_service = __esm(() => {
|
|
10827
10834
|
init_policies();
|
|
10828
10835
|
DEFAULT_SYSTEM_PROMPT = [
|
|
@@ -11425,120 +11432,65 @@ function toErrorMessage2(error) {
|
|
|
11425
11432
|
function stringifyStartupEnvValue(value) {
|
|
11426
11433
|
return typeof value === "boolean" ? String(value) : value;
|
|
11427
11434
|
}
|
|
11435
|
+
function prefixAgentBranch(agent, branch) {
|
|
11436
|
+
return `${agent}-${branch}`;
|
|
11437
|
+
}
|
|
11438
|
+
function buildCreateWorktreeTargets(branch, agentSelection) {
|
|
11439
|
+
if (agentSelection === "both") {
|
|
11440
|
+
return [
|
|
11441
|
+
{ branch: prefixAgentBranch("claude", branch), agent: "claude" },
|
|
11442
|
+
{ branch: prefixAgentBranch("codex", branch), agent: "codex" }
|
|
11443
|
+
];
|
|
11444
|
+
}
|
|
11445
|
+
return [{ branch, agent: agentSelection }];
|
|
11446
|
+
}
|
|
11428
11447
|
|
|
11429
11448
|
class LifecycleService {
|
|
11430
11449
|
deps;
|
|
11431
11450
|
constructor(deps2) {
|
|
11432
11451
|
this.deps = deps2;
|
|
11433
11452
|
}
|
|
11434
|
-
async
|
|
11453
|
+
async createWorktrees(input) {
|
|
11435
11454
|
const mode = input.mode ?? "new";
|
|
11436
|
-
const
|
|
11437
|
-
if (
|
|
11438
|
-
throw new LifecycleError("
|
|
11439
|
-
}
|
|
11440
|
-
if (requestedBaseBranch && mode === "existing") {
|
|
11441
|
-
throw new LifecycleError("Base branch is only supported for new worktrees", 400);
|
|
11455
|
+
const agentSelection = input.agent ?? this.deps.config.workspace.defaultAgent;
|
|
11456
|
+
if (agentSelection === "both" && mode === "existing") {
|
|
11457
|
+
throw new LifecycleError("Creating both agents is only supported for new worktrees", 400);
|
|
11442
11458
|
}
|
|
11443
11459
|
const branch = await this.resolveBranch(input.branch, input.prompt, mode);
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
}
|
|
11447
|
-
const baseBranch = mode === "new" ? requestedBaseBranch || this.deps.config.workspace.mainBranch : undefined;
|
|
11448
|
-
const branchAvailability = this.resolveBranchAvailability(branch, mode);
|
|
11449
|
-
const { profileName, profile } = this.resolveProfile(input.profile);
|
|
11450
|
-
const agent = this.resolveAgent(input.agent);
|
|
11451
|
-
const worktreePath = this.resolveWorktreePath(branch);
|
|
11452
|
-
const createProgressBase = {
|
|
11453
|
-
branch,
|
|
11454
|
-
...baseBranch ? { baseBranch } : {},
|
|
11455
|
-
path: worktreePath,
|
|
11456
|
-
profile: profileName,
|
|
11457
|
-
agent
|
|
11458
|
-
};
|
|
11459
|
-
const deleteBranchOnRollback = mode === "new" || branchAvailability.deleteBranchOnRollback;
|
|
11460
|
-
let initialized = null;
|
|
11460
|
+
const targets = buildCreateWorktreeTargets(branch, agentSelection);
|
|
11461
|
+
const createdBranches = [];
|
|
11461
11462
|
try {
|
|
11462
|
-
|
|
11463
|
-
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
mode,
|
|
11472
|
-
...baseBranch ? { baseBranch } : {},
|
|
11473
|
-
...branchAvailability.startPoint ? { startPoint: branchAvailability.startPoint } : {},
|
|
11474
|
-
profile: profileName,
|
|
11475
|
-
agent,
|
|
11476
|
-
runtime: profile.runtime,
|
|
11477
|
-
startupEnvValues: await this.buildStartupEnvValues(input.envOverrides),
|
|
11478
|
-
allocatedPorts: await this.allocatePorts(),
|
|
11479
|
-
runtimeEnvExtras: { WEBMUX_WORKTREE_PATH: worktreePath },
|
|
11480
|
-
controlUrl: this.controlUrl(),
|
|
11481
|
-
controlToken: await this.deps.getControlToken(),
|
|
11482
|
-
deleteBranchOnRollback
|
|
11483
|
-
}, {
|
|
11484
|
-
git: this.deps.git
|
|
11485
|
-
});
|
|
11486
|
-
await this.reportCreateProgress({
|
|
11487
|
-
...createProgressBase,
|
|
11488
|
-
phase: "running_post_create_hook"
|
|
11489
|
-
});
|
|
11490
|
-
await this.runLifecycleHook({
|
|
11491
|
-
name: "postCreate",
|
|
11492
|
-
command: this.deps.config.lifecycleHooks.postCreate,
|
|
11493
|
-
meta: initialized.meta,
|
|
11494
|
-
worktreePath
|
|
11495
|
-
});
|
|
11496
|
-
initialized = await this.refreshManagedArtifactsFromMeta({
|
|
11497
|
-
gitDir: initialized.paths.gitDir,
|
|
11498
|
-
meta: initialized.meta,
|
|
11499
|
-
worktreePath
|
|
11500
|
-
});
|
|
11501
|
-
await this.reportCreateProgress({
|
|
11502
|
-
...createProgressBase,
|
|
11503
|
-
phase: "preparing_runtime"
|
|
11504
|
-
});
|
|
11505
|
-
await ensureAgentRuntimeArtifacts({
|
|
11506
|
-
gitDir: initialized.paths.gitDir,
|
|
11507
|
-
worktreePath
|
|
11508
|
-
});
|
|
11509
|
-
await this.reportCreateProgress({
|
|
11510
|
-
...createProgressBase,
|
|
11511
|
-
phase: "starting_session"
|
|
11512
|
-
});
|
|
11513
|
-
await this.materializeRuntimeSession({
|
|
11514
|
-
branch,
|
|
11515
|
-
profile,
|
|
11516
|
-
agent,
|
|
11517
|
-
initialized,
|
|
11518
|
-
worktreePath,
|
|
11519
|
-
prompt: input.prompt,
|
|
11520
|
-
launchMode: "fresh"
|
|
11521
|
-
});
|
|
11522
|
-
await this.reportCreateProgress({
|
|
11523
|
-
...createProgressBase,
|
|
11524
|
-
phase: "reconciling"
|
|
11525
|
-
});
|
|
11526
|
-
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
11527
|
-
return {
|
|
11528
|
-
branch,
|
|
11529
|
-
worktreeId: initialized.meta.worktreeId
|
|
11530
|
-
};
|
|
11463
|
+
for (const target of targets) {
|
|
11464
|
+
const created = await this.createResolvedWorktree({
|
|
11465
|
+
...input,
|
|
11466
|
+
mode,
|
|
11467
|
+
branch: target.branch,
|
|
11468
|
+
agent: target.agent
|
|
11469
|
+
});
|
|
11470
|
+
createdBranches.push(created.branch);
|
|
11471
|
+
}
|
|
11531
11472
|
} catch (error) {
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
throw this.wrapOperationError(new Error(`${toErrorMessage2(error)}; ${cleanupError}`));
|
|
11536
|
-
}
|
|
11473
|
+
const rollbackError = await this.rollbackCreatedWorktrees(createdBranches);
|
|
11474
|
+
if (rollbackError) {
|
|
11475
|
+
throw this.wrapOperationError(new Error(`${toErrorMessage2(error)}; ${rollbackError}`));
|
|
11537
11476
|
}
|
|
11538
11477
|
throw this.wrapOperationError(error);
|
|
11539
|
-
} finally {
|
|
11540
|
-
await this.finishCreateProgress(branch);
|
|
11541
11478
|
}
|
|
11479
|
+
return {
|
|
11480
|
+
primaryBranch: createdBranches[0],
|
|
11481
|
+
branches: createdBranches
|
|
11482
|
+
};
|
|
11483
|
+
}
|
|
11484
|
+
async createWorktree(input) {
|
|
11485
|
+
const mode = input.mode ?? "new";
|
|
11486
|
+
const branch = await this.resolveBranch(input.branch, input.prompt, mode);
|
|
11487
|
+
const agent = this.resolveAgent(input.agent);
|
|
11488
|
+
return await this.createResolvedWorktree({
|
|
11489
|
+
...input,
|
|
11490
|
+
mode,
|
|
11491
|
+
branch,
|
|
11492
|
+
agent
|
|
11493
|
+
});
|
|
11542
11494
|
}
|
|
11543
11495
|
async openWorktree(branch) {
|
|
11544
11496
|
try {
|
|
@@ -11939,6 +11891,123 @@ class LifecycleService {
|
|
|
11939
11891
|
async finishCreateProgress(branch) {
|
|
11940
11892
|
await this.deps.onCreateFinished?.(branch);
|
|
11941
11893
|
}
|
|
11894
|
+
async rollbackCreatedWorktrees(branches) {
|
|
11895
|
+
const cleanupErrors = [];
|
|
11896
|
+
for (const branch of [...branches].reverse()) {
|
|
11897
|
+
try {
|
|
11898
|
+
await this.removeWorktree(branch);
|
|
11899
|
+
} catch (error) {
|
|
11900
|
+
cleanupErrors.push(`rollback failed for ${branch}: ${toErrorMessage2(error)}`);
|
|
11901
|
+
}
|
|
11902
|
+
}
|
|
11903
|
+
return cleanupErrors.length > 0 ? cleanupErrors.join("; ") : null;
|
|
11904
|
+
}
|
|
11905
|
+
async createResolvedWorktree(input) {
|
|
11906
|
+
const requestedBaseBranch = input.baseBranch?.trim();
|
|
11907
|
+
if (requestedBaseBranch && !isValidBranchName(requestedBaseBranch)) {
|
|
11908
|
+
throw new LifecycleError("Invalid base branch name", 400);
|
|
11909
|
+
}
|
|
11910
|
+
if (requestedBaseBranch && input.mode === "existing") {
|
|
11911
|
+
throw new LifecycleError("Base branch is only supported for new worktrees", 400);
|
|
11912
|
+
}
|
|
11913
|
+
if (requestedBaseBranch && requestedBaseBranch === input.branch) {
|
|
11914
|
+
throw new LifecycleError("Base branch must differ from branch name", 400);
|
|
11915
|
+
}
|
|
11916
|
+
const baseBranch = input.mode === "new" ? requestedBaseBranch || this.deps.config.workspace.mainBranch : undefined;
|
|
11917
|
+
const branchAvailability = this.resolveBranchAvailability(input.branch, input.mode);
|
|
11918
|
+
const { profileName, profile } = this.resolveProfile(input.profile);
|
|
11919
|
+
const worktreePath = this.resolveWorktreePath(input.branch);
|
|
11920
|
+
const createProgressBase = {
|
|
11921
|
+
branch: input.branch,
|
|
11922
|
+
...baseBranch ? { baseBranch } : {},
|
|
11923
|
+
path: worktreePath,
|
|
11924
|
+
profile: profileName,
|
|
11925
|
+
agent: input.agent
|
|
11926
|
+
};
|
|
11927
|
+
const deleteBranchOnRollback = input.mode === "new" || branchAvailability.deleteBranchOnRollback;
|
|
11928
|
+
let initialized = null;
|
|
11929
|
+
try {
|
|
11930
|
+
await this.reportCreateProgress({
|
|
11931
|
+
...createProgressBase,
|
|
11932
|
+
phase: "creating_worktree"
|
|
11933
|
+
});
|
|
11934
|
+
await mkdir4(dirname5(worktreePath), { recursive: true });
|
|
11935
|
+
initialized = await createManagedWorktree({
|
|
11936
|
+
repoRoot: this.deps.projectRoot,
|
|
11937
|
+
worktreePath,
|
|
11938
|
+
branch: input.branch,
|
|
11939
|
+
mode: input.mode,
|
|
11940
|
+
...baseBranch ? { baseBranch } : {},
|
|
11941
|
+
...branchAvailability.startPoint ? { startPoint: branchAvailability.startPoint } : {},
|
|
11942
|
+
profile: profileName,
|
|
11943
|
+
agent: input.agent,
|
|
11944
|
+
runtime: profile.runtime,
|
|
11945
|
+
startupEnvValues: await this.buildStartupEnvValues(input.envOverrides),
|
|
11946
|
+
allocatedPorts: await this.allocatePorts(),
|
|
11947
|
+
runtimeEnvExtras: { WEBMUX_WORKTREE_PATH: worktreePath },
|
|
11948
|
+
controlUrl: this.controlUrl(),
|
|
11949
|
+
controlToken: await this.deps.getControlToken(),
|
|
11950
|
+
deleteBranchOnRollback
|
|
11951
|
+
}, {
|
|
11952
|
+
git: this.deps.git
|
|
11953
|
+
});
|
|
11954
|
+
await this.reportCreateProgress({
|
|
11955
|
+
...createProgressBase,
|
|
11956
|
+
phase: "running_post_create_hook"
|
|
11957
|
+
});
|
|
11958
|
+
await this.runLifecycleHook({
|
|
11959
|
+
name: "postCreate",
|
|
11960
|
+
command: this.deps.config.lifecycleHooks.postCreate,
|
|
11961
|
+
meta: initialized.meta,
|
|
11962
|
+
worktreePath
|
|
11963
|
+
});
|
|
11964
|
+
initialized = await this.refreshManagedArtifactsFromMeta({
|
|
11965
|
+
gitDir: initialized.paths.gitDir,
|
|
11966
|
+
meta: initialized.meta,
|
|
11967
|
+
worktreePath
|
|
11968
|
+
});
|
|
11969
|
+
await this.reportCreateProgress({
|
|
11970
|
+
...createProgressBase,
|
|
11971
|
+
phase: "preparing_runtime"
|
|
11972
|
+
});
|
|
11973
|
+
await ensureAgentRuntimeArtifacts({
|
|
11974
|
+
gitDir: initialized.paths.gitDir,
|
|
11975
|
+
worktreePath
|
|
11976
|
+
});
|
|
11977
|
+
await this.reportCreateProgress({
|
|
11978
|
+
...createProgressBase,
|
|
11979
|
+
phase: "starting_session"
|
|
11980
|
+
});
|
|
11981
|
+
await this.materializeRuntimeSession({
|
|
11982
|
+
branch: input.branch,
|
|
11983
|
+
profile,
|
|
11984
|
+
agent: input.agent,
|
|
11985
|
+
initialized,
|
|
11986
|
+
worktreePath,
|
|
11987
|
+
prompt: input.prompt,
|
|
11988
|
+
launchMode: "fresh"
|
|
11989
|
+
});
|
|
11990
|
+
await this.reportCreateProgress({
|
|
11991
|
+
...createProgressBase,
|
|
11992
|
+
phase: "reconciling"
|
|
11993
|
+
});
|
|
11994
|
+
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
11995
|
+
return {
|
|
11996
|
+
branch: input.branch,
|
|
11997
|
+
worktreeId: initialized.meta.worktreeId
|
|
11998
|
+
};
|
|
11999
|
+
} catch (error) {
|
|
12000
|
+
if (initialized) {
|
|
12001
|
+
const cleanupError = await this.cleanupFailedCreate(input.branch, worktreePath, profile.runtime, deleteBranchOnRollback);
|
|
12002
|
+
if (cleanupError) {
|
|
12003
|
+
throw this.wrapOperationError(new Error(`${toErrorMessage2(error)}; ${cleanupError}`));
|
|
12004
|
+
}
|
|
12005
|
+
}
|
|
12006
|
+
throw this.wrapOperationError(error);
|
|
12007
|
+
} finally {
|
|
12008
|
+
await this.finishCreateProgress(input.branch);
|
|
12009
|
+
}
|
|
12010
|
+
}
|
|
11942
12011
|
wrapOperationError(error) {
|
|
11943
12012
|
if (error instanceof LifecycleError) {
|
|
11944
12013
|
return error;
|
|
@@ -12817,6 +12886,9 @@ async function runWorktreeCommand(context, deps2 = {}) {
|
|
|
12817
12886
|
stdout(PHASE_LABELS[progress.phase] ?? progress.phase);
|
|
12818
12887
|
}
|
|
12819
12888
|
});
|
|
12889
|
+
if (!parsed.input.branch && parsed.input.prompt && runtime2.config.autoName) {
|
|
12890
|
+
stdout("Generating branch name...");
|
|
12891
|
+
}
|
|
12820
12892
|
const result = await runtime2.lifecycleService.createWorktree(parsed.input);
|
|
12821
12893
|
stdout(`Created worktree ${result.branch}`);
|
|
12822
12894
|
if (!parsed.detach) {
|
|
@@ -12954,7 +13026,7 @@ import { fileURLToPath } from "url";
|
|
|
12954
13026
|
// package.json
|
|
12955
13027
|
var package_default = {
|
|
12956
13028
|
name: "webmux",
|
|
12957
|
-
version: "0.
|
|
13029
|
+
version: "0.24.1",
|
|
12958
13030
|
description: "Web dashboard for workmux \u2014 browser UI with embedded terminals, PR monitoring, and CI integration",
|
|
12959
13031
|
type: "module",
|
|
12960
13032
|
repository: {
|