webmux 0.22.0 → 0.22.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.
@@ -6919,7 +6919,7 @@ var require_public_api = __commonJS((exports) => {
6919
6919
 
6920
6920
  // backend/src/server.ts
6921
6921
  import { randomUUID as randomUUID3 } from "crypto";
6922
- import { join as join6, resolve as resolve7 } from "path";
6922
+ import { join as join7, resolve as resolve7 } from "path";
6923
6923
  import { mkdirSync } from "fs";
6924
6924
  import { networkInterfaces } from "os";
6925
6925
 
@@ -8759,8 +8759,8 @@ function ensureSessionLayout(tmux, plan) {
8759
8759
  import { randomUUID } from "crypto";
8760
8760
 
8761
8761
  // backend/src/adapters/git.ts
8762
- import { rmSync } from "fs";
8763
- import { resolve as resolve4 } from "path";
8762
+ import { readdirSync, rmSync, statSync } from "fs";
8763
+ import { resolve as resolve4, join as join4 } from "path";
8764
8764
  function runGit(args, cwd) {
8765
8765
  const result = Bun.spawnSync(["git", ...args], {
8766
8766
  cwd,
@@ -8816,6 +8816,30 @@ function currentCheckoutRef(cwd) {
8816
8816
  branch: null
8817
8817
  };
8818
8818
  }
8819
+ function resolveRepoRoot(dir) {
8820
+ const direct = tryRunGit(["rev-parse", "--show-toplevel"], dir);
8821
+ if (direct.ok)
8822
+ return resolve4(dir, direct.stdout);
8823
+ let entries;
8824
+ try {
8825
+ entries = readdirSync(dir);
8826
+ } catch {
8827
+ return null;
8828
+ }
8829
+ for (const entry of entries) {
8830
+ const child = join4(dir, entry);
8831
+ try {
8832
+ if (!statSync(child).isDirectory())
8833
+ continue;
8834
+ } catch {
8835
+ continue;
8836
+ }
8837
+ const childResult = tryRunGit(["rev-parse", "--show-toplevel"], child);
8838
+ if (childResult.ok)
8839
+ return resolve4(child, childResult.stdout);
8840
+ }
8841
+ return null;
8842
+ }
8819
8843
  function resolveWorktreeRoot(cwd) {
8820
8844
  const output = runGit(["rev-parse", "--show-toplevel"], cwd);
8821
8845
  return resolve4(cwd, output);
@@ -8923,6 +8947,9 @@ function removeGitWorktree(opts, deps = {}) {
8923
8947
  }
8924
8948
 
8925
8949
  class BunGitGateway {
8950
+ resolveRepoRoot(dir) {
8951
+ return resolveRepoRoot(dir);
8952
+ }
8926
8953
  resolveWorktreeRoot(cwd) {
8927
8954
  return resolveWorktreeRoot(cwd);
8928
8955
  }
@@ -10706,7 +10733,7 @@ async function removeContainer(branch) {
10706
10733
  }
10707
10734
 
10708
10735
  // backend/src/adapters/hooks.ts
10709
- import { join as join5 } from "path";
10736
+ import { join as join6 } from "path";
10710
10737
  function buildErrorMessage(name, exitCode, stdout, stderr) {
10711
10738
  const output = stderr.trim() || stdout.trim();
10712
10739
  if (output) {
@@ -10731,7 +10758,7 @@ class BunLifecycleHookRunner {
10731
10758
  return this.direnvAvailable;
10732
10759
  }
10733
10760
  async buildCommand(cwd, command) {
10734
- if (this.checkDirenv() && await Bun.file(join5(cwd, ".envrc")).exists()) {
10761
+ if (this.checkDirenv() && await Bun.file(join6(cwd, ".envrc")).exists()) {
10735
10762
  Bun.spawnSync(["direnv", "allow"], { cwd, stdout: "pipe", stderr: "pipe" });
10736
10763
  return ["direnv", "exec", cwd, "bash", "-c", command];
10737
10764
  }
@@ -11901,10 +11928,11 @@ async function apiPullMain(req) {
11901
11928
  return errorResponse(`Unknown linked repo: ${repo}`, 404);
11902
11929
  if (!linkedRepo.dir)
11903
11930
  return errorResponse(`Linked repo "${repo}" has no dir configured`, 400);
11904
- projectRoot2 = resolve7(PROJECT_DIR, linkedRepo.dir);
11905
- if (!projectRoot2.startsWith(PROJECT_DIR)) {
11906
- return errorResponse("Invalid linked repo directory", 400);
11907
- }
11931
+ const resolvedDir = resolve7(PROJECT_DIR, linkedRepo.dir);
11932
+ const repoRoot = git.resolveRepoRoot(resolvedDir);
11933
+ if (!repoRoot)
11934
+ return errorResponse(`Linked repo "${repo}" dir is not a git repository: ${resolvedDir}`, 400);
11935
+ projectRoot2 = repoRoot;
11908
11936
  }
11909
11937
  const deps = { git, projectRoot: projectRoot2, mainBranch: config.workspace.mainBranch };
11910
11938
  const result = force ? forcePullMainBranch(deps) : pullMainBranch(deps);
@@ -11990,7 +12018,7 @@ async function apiUploadFiles(name, req) {
11990
12018
  return errorResponse(`File too large: ${entry.name} (max 10MB)`, 400);
11991
12019
  }
11992
12020
  const safeName = `${Date.now()}_${sanitizeFilename(entry.name)}`;
11993
- const destPath = join6(uploadDir, safeName);
12021
+ const destPath = join7(uploadDir, safeName);
11994
12022
  if (!resolve7(destPath).startsWith(uploadDir + "/")) {
11995
12023
  return errorResponse("Invalid filename", 400);
11996
12024
  }
@@ -12123,7 +12151,7 @@ Bun.serve({
12123
12151
  if (STATIC_DIR) {
12124
12152
  const url = new URL(req.url);
12125
12153
  const rawPath = url.pathname === "/" ? "index.html" : url.pathname;
12126
- const filePath = join6(STATIC_DIR, rawPath);
12154
+ const filePath = join7(STATIC_DIR, rawPath);
12127
12155
  const staticRoot = resolve7(STATIC_DIR);
12128
12156
  if (!resolve7(filePath).startsWith(staticRoot + "/")) {
12129
12157
  return new Response("Forbidden", { status: 403 });
@@ -12133,7 +12161,7 @@ Bun.serve({
12133
12161
  const headers = rawPath.startsWith("/assets/") ? { "Cache-Control": "public, max-age=31536000, immutable" } : {};
12134
12162
  return new Response(file, { headers });
12135
12163
  }
12136
- return new Response(Bun.file(join6(STATIC_DIR, "index.html")), {
12164
+ return new Response(Bun.file(join7(STATIC_DIR, "index.html")), {
12137
12165
  headers: { "Cache-Control": "no-cache" }
12138
12166
  });
12139
12167
  }
package/bin/webmux.js CHANGED
@@ -48,8 +48,8 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
48
48
  var __require = import.meta.require;
49
49
 
50
50
  // backend/src/adapters/git.ts
51
- import { rmSync } from "fs";
52
- import { resolve } from "path";
51
+ import { readdirSync, rmSync, statSync } from "fs";
52
+ import { resolve, join } from "path";
53
53
  function runGit(args, cwd) {
54
54
  const result = Bun.spawnSync(["git", ...args], {
55
55
  cwd,
@@ -105,6 +105,30 @@ function currentCheckoutRef(cwd) {
105
105
  branch: null
106
106
  };
107
107
  }
108
+ function resolveRepoRoot(dir) {
109
+ const direct = tryRunGit(["rev-parse", "--show-toplevel"], dir);
110
+ if (direct.ok)
111
+ return resolve(dir, direct.stdout);
112
+ let entries;
113
+ try {
114
+ entries = readdirSync(dir);
115
+ } catch {
116
+ return null;
117
+ }
118
+ for (const entry of entries) {
119
+ const child = join(dir, entry);
120
+ try {
121
+ if (!statSync(child).isDirectory())
122
+ continue;
123
+ } catch {
124
+ continue;
125
+ }
126
+ const childResult = tryRunGit(["rev-parse", "--show-toplevel"], child);
127
+ if (childResult.ok)
128
+ return resolve(child, childResult.stdout);
129
+ }
130
+ return null;
131
+ }
108
132
  function resolveWorktreeRoot(cwd) {
109
133
  const output = runGit(["rev-parse", "--show-toplevel"], cwd);
110
134
  return resolve(cwd, output);
@@ -212,6 +236,9 @@ function removeGitWorktree(opts, deps = {}) {
212
236
  }
213
237
 
214
238
  class BunGitGateway {
239
+ resolveRepoRoot(dir) {
240
+ return resolveRepoRoot(dir);
241
+ }
215
242
  resolveWorktreeRoot(cwd) {
216
243
  return resolveWorktreeRoot(cwd);
217
244
  }
@@ -1436,7 +1463,7 @@ var init_dist2 = __esm(() => {
1436
1463
 
1437
1464
  // bin/src/shared.ts
1438
1465
  import { existsSync, readFileSync } from "fs";
1439
- import { basename as basename2, join } from "path";
1466
+ import { basename as basename2, join as join2 } from "path";
1440
1467
  function run(cmd, args, opts) {
1441
1468
  const result = Bun.spawnSync([cmd, ...args], { stdout: "pipe", stderr: "pipe", ...opts });
1442
1469
  return {
@@ -1455,7 +1482,7 @@ function getGitRoot() {
1455
1482
  return result.stdout.toString().trim();
1456
1483
  }
1457
1484
  function detectProjectName(gitRoot) {
1458
- const pkgPath = join(gitRoot, "package.json");
1485
+ const pkgPath = join2(gitRoot, "package.json");
1459
1486
  if (existsSync(pkgPath)) {
1460
1487
  try {
1461
1488
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
@@ -1470,7 +1497,7 @@ var init_shared = () => {};
1470
1497
  // bin/src/init-helpers.ts
1471
1498
  import { existsSync as existsSync2, readFileSync as readFileSync2, rmSync as rmSync2 } from "fs";
1472
1499
  import { tmpdir } from "os";
1473
- import { join as join2 } from "path";
1500
+ import { join as join3 } from "path";
1474
1501
  function isRecord(value) {
1475
1502
  return typeof value === "object" && value !== null && !Array.isArray(value);
1476
1503
  }
@@ -1478,11 +1505,11 @@ function readString(value) {
1478
1505
  return typeof value === "string" && value.trim().length > 0 ? value : null;
1479
1506
  }
1480
1507
  function detectPackageManager(gitRoot) {
1481
- if (existsSync2(join2(gitRoot, "bun.lock")) || existsSync2(join2(gitRoot, "bun.lockb")))
1508
+ if (existsSync2(join3(gitRoot, "bun.lock")) || existsSync2(join3(gitRoot, "bun.lockb")))
1482
1509
  return "bun";
1483
- if (existsSync2(join2(gitRoot, "pnpm-lock.yaml")))
1510
+ if (existsSync2(join3(gitRoot, "pnpm-lock.yaml")))
1484
1511
  return "pnpm";
1485
- if (existsSync2(join2(gitRoot, "yarn.lock")))
1512
+ if (existsSync2(join3(gitRoot, "yarn.lock")))
1486
1513
  return "yarn";
1487
1514
  return "npm";
1488
1515
  }
@@ -1582,7 +1609,7 @@ function buildInitAgentCommand(agent, prompt, outputPrefix = "webmux-init") {
1582
1609
  ]
1583
1610
  };
1584
1611
  }
1585
- const summaryPath = join2(tmpdir(), `${outputPrefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.txt`);
1612
+ const summaryPath = join3(tmpdir(), `${outputPrefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.txt`);
1586
1613
  return {
1587
1614
  agent,
1588
1615
  cmd: "codex",
@@ -2065,7 +2092,7 @@ var init_init_helpers = __esm(() => {
2065
2092
  // bin/src/init.ts
2066
2093
  var exports_init = {};
2067
2094
  import { existsSync as existsSync3 } from "fs";
2068
- import { join as join3 } from "path";
2095
+ import { join as join4 } from "path";
2069
2096
  function checkDeps() {
2070
2097
  const missing = [];
2071
2098
  for (const dep of deps) {
@@ -2193,7 +2220,7 @@ var init_init = __esm(async () => {
2193
2220
  }
2194
2221
  }
2195
2222
  R2.step("Checking config files...");
2196
- webmuxYaml = join3(gitRoot, ".webmux.yaml");
2223
+ webmuxYaml = join4(gitRoot, ".webmux.yaml");
2197
2224
  if (existsSync3(webmuxYaml)) {
2198
2225
  R2.info(".webmux.yaml already exists, skipping");
2199
2226
  } else {
@@ -2304,7 +2331,7 @@ __export(exports_service, {
2304
2331
  default: () => service
2305
2332
  });
2306
2333
  import { existsSync as existsSync4, mkdirSync, unlinkSync } from "fs";
2307
- import { join as join4 } from "path";
2334
+ import { join as join5 } from "path";
2308
2335
  import { homedir } from "os";
2309
2336
  function getPlatform() {
2310
2337
  const plat = process.platform;
@@ -2334,10 +2361,10 @@ function printRunResult(result) {
2334
2361
  console.error(err);
2335
2362
  }
2336
2363
  function systemdUnitPath(serviceName) {
2337
- return join4(homedir(), ".config", "systemd", "user", `${serviceName}.service`);
2364
+ return join5(homedir(), ".config", "systemd", "user", `${serviceName}.service`);
2338
2365
  }
2339
2366
  function launchdPlistPath(serviceName) {
2340
- return join4(homedir(), "Library", "LaunchAgents", `com.webmux.${serviceName}.plist`);
2367
+ return join5(homedir(), "Library", "LaunchAgents", `com.webmux.${serviceName}.plist`);
2341
2368
  }
2342
2369
  function serviceFilePath(config) {
2343
2370
  if (config.platform === "linux")
@@ -2363,7 +2390,7 @@ WantedBy=default.target
2363
2390
  `;
2364
2391
  }
2365
2392
  function generateLaunchdPlist(config) {
2366
- const logPath = join4(homedir(), "Library", "Logs", `webmux-${config.serviceName}.log`);
2393
+ const logPath = join5(homedir(), "Library", "Logs", `webmux-${config.serviceName}.log`);
2367
2394
  return `<?xml version="1.0" encoding="UTF-8"?>
2368
2395
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2369
2396
  <plist version="1.0">
@@ -2535,7 +2562,7 @@ function logs(config) {
2535
2562
  if (config.platform === "linux") {
2536
2563
  proc = Bun.spawn(["journalctl", "--user", "-u", config.serviceName, "-f", "--no-pager"], { stdout: "inherit", stderr: "inherit" });
2537
2564
  } else {
2538
- const logPath = join4(homedir(), "Library", "Logs", `webmux-${config.serviceName}.log`);
2565
+ const logPath = join5(homedir(), "Library", "Logs", `webmux-${config.serviceName}.log`);
2539
2566
  if (!existsSync4(logPath)) {
2540
2567
  R2.error(`Log file not found: ${logPath}`);
2541
2568
  return;
@@ -2634,7 +2661,7 @@ var init_service = __esm(() => {
2634
2661
 
2635
2662
  // backend/src/adapters/fs.ts
2636
2663
  import { mkdir } from "fs/promises";
2637
- import { join as join5 } from "path";
2664
+ import { join as join6 } from "path";
2638
2665
  function stringifyAllocatedPorts(ports) {
2639
2666
  const entries = Object.entries(ports).map(([key, value]) => [key, String(value)]);
2640
2667
  return Object.fromEntries(entries);
@@ -2666,21 +2693,21 @@ function parseDotenv(content) {
2666
2693
  }
2667
2694
  async function loadDotenvLocal(worktreePath) {
2668
2695
  try {
2669
- const content = await Bun.file(join5(worktreePath, ".env.local")).text();
2696
+ const content = await Bun.file(join6(worktreePath, ".env.local")).text();
2670
2697
  return parseDotenv(content);
2671
2698
  } catch {
2672
2699
  return {};
2673
2700
  }
2674
2701
  }
2675
2702
  function getWorktreeStoragePaths(gitDir) {
2676
- const webmuxDir = join5(gitDir, "webmux");
2703
+ const webmuxDir = join6(gitDir, "webmux");
2677
2704
  return {
2678
2705
  gitDir,
2679
2706
  webmuxDir,
2680
- metaPath: join5(webmuxDir, "meta.json"),
2681
- runtimeEnvPath: join5(webmuxDir, "runtime.env"),
2682
- controlEnvPath: join5(webmuxDir, "control.env"),
2683
- prsPath: join5(webmuxDir, "prs.json")
2707
+ metaPath: join6(webmuxDir, "meta.json"),
2708
+ runtimeEnvPath: join6(webmuxDir, "runtime.env"),
2709
+ controlEnvPath: join6(webmuxDir, "control.env"),
2710
+ prsPath: join6(webmuxDir, "prs.json")
2684
2711
  };
2685
2712
  }
2686
2713
  async function ensureWorktreeStorageDirs(gitDir) {
@@ -9889,7 +9916,7 @@ var init_dist3 = __esm(() => {
9889
9916
 
9890
9917
  // backend/src/adapters/config.ts
9891
9918
  import { readFileSync as readFileSync3 } from "fs";
9892
- import { dirname as dirname2, join as join6, resolve as resolve4 } from "path";
9919
+ import { dirname as dirname2, join as join7, resolve as resolve4 } from "path";
9893
9920
  function clonePanes(panes) {
9894
9921
  return panes.map((pane) => ({ ...pane }));
9895
9922
  }
@@ -10071,10 +10098,10 @@ function getDefaultProfileName(config) {
10071
10098
  return Object.keys(config.profiles)[0] ?? "default";
10072
10099
  }
10073
10100
  function readConfigFile(root) {
10074
- return readFileSync3(join6(root, ".webmux.yaml"), "utf8");
10101
+ return readFileSync3(join7(root, ".webmux.yaml"), "utf8");
10075
10102
  }
10076
10103
  function readLocalConfigFile(root) {
10077
- return readFileSync3(join6(root, ".webmux.local.yaml"), "utf8");
10104
+ return readFileSync3(join7(root, ".webmux.local.yaml"), "utf8");
10078
10105
  }
10079
10106
  function parseConfigDocument(text) {
10080
10107
  const parsed = $parse(text);
@@ -10574,7 +10601,7 @@ var init_docker = __esm(() => {
10574
10601
  });
10575
10602
 
10576
10603
  // backend/src/adapters/hooks.ts
10577
- import { join as join7 } from "path";
10604
+ import { join as join8 } from "path";
10578
10605
  function buildErrorMessage(name, exitCode, stdout, stderr) {
10579
10606
  const output = stderr.trim() || stdout.trim();
10580
10607
  if (output) {
@@ -10599,7 +10626,7 @@ class BunLifecycleHookRunner {
10599
10626
  return this.direnvAvailable;
10600
10627
  }
10601
10628
  async buildCommand(cwd, command) {
10602
- if (this.checkDirenv() && await Bun.file(join7(cwd, ".envrc")).exists()) {
10629
+ if (this.checkDirenv() && await Bun.file(join8(cwd, ".envrc")).exists()) {
10603
10630
  Bun.spawnSync(["direnv", "allow"], { cwd, stdout: "pipe", stderr: "pipe" });
10604
10631
  return ["direnv", "exec", cwd, "bash", "-c", command];
10605
10632
  }
@@ -10809,7 +10836,7 @@ var init_auto_name_service = __esm(() => {
10809
10836
 
10810
10837
  // backend/src/adapters/agent-runtime.ts
10811
10838
  import { chmod as chmod2, mkdir as mkdir3 } from "fs/promises";
10812
- import { dirname as dirname4, join as join8 } from "path";
10839
+ import { dirname as dirname4, join as join9 } from "path";
10813
10840
  function shellQuote(value) {
10814
10841
  return `'${value.replaceAll("'", "'\\''")}'`;
10815
10842
  }
@@ -11073,8 +11100,8 @@ async function mergeClaudeSettings(settingsPath, hookSettings) {
11073
11100
  async function ensureAgentRuntimeArtifacts(input) {
11074
11101
  const storagePaths = getWorktreeStoragePaths(input.gitDir);
11075
11102
  const artifacts = {
11076
- agentCtlPath: join8(storagePaths.webmuxDir, "webmux-agentctl"),
11077
- claudeSettingsPath: join8(input.worktreePath, ".claude", "settings.local.json")
11103
+ agentCtlPath: join9(storagePaths.webmuxDir, "webmux-agentctl"),
11104
+ claudeSettingsPath: join9(input.worktreePath, ".claude", "settings.local.json")
11078
11105
  };
11079
11106
  await mkdir3(dirname4(artifacts.claudeSettingsPath), { recursive: true });
11080
11107
  await Bun.write(artifacts.agentCtlPath, buildAgentCtlScript());
@@ -12910,13 +12937,13 @@ var init_worktree_commands = __esm(() => {
12910
12937
  });
12911
12938
 
12912
12939
  // bin/src/webmux.ts
12913
- import { resolve as resolve9, dirname as dirname6, join as join10 } from "path";
12940
+ import { resolve as resolve9, dirname as dirname6, join as join11 } from "path";
12914
12941
  import { existsSync as existsSync5 } from "fs";
12915
12942
  import { fileURLToPath } from "url";
12916
12943
  // package.json
12917
12944
  var package_default = {
12918
12945
  name: "webmux",
12919
- version: "0.22.0",
12946
+ version: "0.22.1",
12920
12947
  description: "Web dashboard for workmux \u2014 browser UI with embedded terminals, PR monitoring, and CI integration",
12921
12948
  type: "module",
12922
12949
  repository: {
@@ -13231,8 +13258,8 @@ async function main(args = process.argv.slice(2)) {
13231
13258
  }
13232
13259
  process.on("SIGINT", cleanup);
13233
13260
  process.on("SIGTERM", cleanup);
13234
- const backendEntry = join10(PKG_ROOT, "backend", "dist", "server.js");
13235
- const staticDir = join10(PKG_ROOT, "frontend", "dist");
13261
+ const backendEntry = join11(PKG_ROOT, "backend", "dist", "server.js");
13262
+ const staticDir = join11(PKG_ROOT, "frontend", "dist");
13236
13263
  if (!existsSync5(staticDir)) {
13237
13264
  console.error(`Error: frontend/dist/ not found. Run 'bun run build' first.`);
13238
13265
  process.exit(1);