webmux 0.37.0 → 0.38.0
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 +1551 -259
- package/bin/webmux.js +725 -104
- package/frontend/dist/assets/{DiffDialog-npkRTiLF.js → DiffDialog-Dyoq_LfP.js} +1 -1
- package/frontend/dist/assets/index-CWlBZr5I.css +1 -0
- package/frontend/dist/assets/index-Dru2xSw4.js +37 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-CwsEmpEK.js +0 -36
- package/frontend/dist/assets/index-XCRBR8rv.css +0 -1
package/bin/webmux.js
CHANGED
|
@@ -972,7 +972,7 @@ var require_src = __commonJS((exports, module) => {
|
|
|
972
972
|
module.exports = { cursor, scroll, erase, beep };
|
|
973
973
|
});
|
|
974
974
|
|
|
975
|
-
// node_modules/.bun/@clack+core@1.4.
|
|
975
|
+
// node_modules/.bun/@clack+core@1.4.2/node_modules/@clack/core/dist/index.mjs
|
|
976
976
|
import { styleText } from "util";
|
|
977
977
|
import { stdout, stdin } from "process";
|
|
978
978
|
import l__default from "readline";
|
|
@@ -1608,8 +1608,8 @@ var init_dist3 = __esm(() => {
|
|
|
1608
1608
|
};
|
|
1609
1609
|
o$1 = /* @__PURE__ */ new Set(["up", "down", "left", "right"]);
|
|
1610
1610
|
h = class h extends V {
|
|
1611
|
-
#
|
|
1612
|
-
#
|
|
1611
|
+
#t = false;
|
|
1612
|
+
#s;
|
|
1613
1613
|
focused = "editor";
|
|
1614
1614
|
get userInputWithCursor() {
|
|
1615
1615
|
if (this.state === "submit")
|
|
@@ -1617,10 +1617,10 @@ var init_dist3 = __esm(() => {
|
|
|
1617
1617
|
const t2 = this.userInput;
|
|
1618
1618
|
if (this.cursor >= t2.length)
|
|
1619
1619
|
return `${t2}\u2588`;
|
|
1620
|
-
const s = t2.slice(0, this.cursor), r2 = t2[this.cursor],
|
|
1620
|
+
const s = t2.slice(0, this.cursor), r2 = t2[this.cursor], i = t2.slice(this.cursor + 1);
|
|
1621
1621
|
return r2 === `
|
|
1622
1622
|
` ? `${s}\u2588
|
|
1623
|
-
${
|
|
1623
|
+
${i}` : `${s}${styleText("inverse", r2)}${i}`;
|
|
1624
1624
|
}
|
|
1625
1625
|
get cursor() {
|
|
1626
1626
|
return this._cursor;
|
|
@@ -1650,37 +1650,41 @@ ${e}` : `${s}${styleText("inverse", r2)}${e}`;
|
|
|
1650
1650
|
}
|
|
1651
1651
|
}
|
|
1652
1652
|
_shouldSubmit(t2, s) {
|
|
1653
|
-
if (this.#
|
|
1653
|
+
if (this.#s)
|
|
1654
1654
|
return this.focused === "submit" ? true : (this.#r(`
|
|
1655
1655
|
`), this._cursor++, false);
|
|
1656
|
-
const r2 = this.#
|
|
1657
|
-
return this.#
|
|
1656
|
+
const r2 = this.#t;
|
|
1657
|
+
return this.#t = true, r2 && this.cursor === this.userInput.length ? (this.userInput[this.cursor - 1] === `
|
|
1658
1658
|
` && (this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--), true) : (this.#r(`
|
|
1659
1659
|
`), this._cursor++, false);
|
|
1660
1660
|
}
|
|
1661
1661
|
constructor(t2) {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1662
|
+
const s = t2.initialUserInput ?? t2.initialValue;
|
|
1663
|
+
super({
|
|
1664
|
+
...t2,
|
|
1665
|
+
initialUserInput: s
|
|
1666
|
+
}, false), s !== undefined && (this._cursor = s.length), this.#s = t2.showSubmit ?? false, this.on("key", (r2, i) => {
|
|
1667
|
+
if (i?.name && o$1.has(i.name)) {
|
|
1668
|
+
this.#t = false, this.#i(i.name);
|
|
1665
1669
|
return;
|
|
1666
1670
|
}
|
|
1667
|
-
if (
|
|
1671
|
+
if (r2 === "\t" && this.#s) {
|
|
1668
1672
|
this.focused = this.focused === "editor" ? "submit" : "editor";
|
|
1669
1673
|
return;
|
|
1670
1674
|
}
|
|
1671
|
-
if (
|
|
1672
|
-
if (this.#
|
|
1675
|
+
if (i?.name !== "return") {
|
|
1676
|
+
if (this.#t = false, i?.name === "backspace" && this.cursor > 0) {
|
|
1673
1677
|
this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--;
|
|
1674
1678
|
return;
|
|
1675
1679
|
}
|
|
1676
|
-
if (
|
|
1680
|
+
if (i?.name === "delete" && this.cursor < this.userInput.length) {
|
|
1677
1681
|
this._setUserInput(this.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1));
|
|
1678
1682
|
return;
|
|
1679
1683
|
}
|
|
1680
|
-
|
|
1684
|
+
r2 && (this.#s && this.focused === "submit" && (this.focused = "editor"), this.#r(r2 ?? ""), this._cursor++);
|
|
1681
1685
|
}
|
|
1682
|
-
}), this.on("userInput", (
|
|
1683
|
-
this._setValue(
|
|
1686
|
+
}), this.on("userInput", (r2) => {
|
|
1687
|
+
this._setValue(r2);
|
|
1684
1688
|
}), this.on("finalize", () => {
|
|
1685
1689
|
this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
|
|
1686
1690
|
});
|
|
@@ -1715,7 +1719,7 @@ ${e}` : `${s}${styleText("inverse", r2)}${e}`;
|
|
|
1715
1719
|
};
|
|
1716
1720
|
});
|
|
1717
1721
|
|
|
1718
|
-
// node_modules/.bun/@clack+prompts@1.
|
|
1722
|
+
// node_modules/.bun/@clack+prompts@1.6.0/node_modules/@clack/prompts/dist/index.mjs
|
|
1719
1723
|
import { styleText as styleText2, stripVTControlCharacters } from "util";
|
|
1720
1724
|
import process$1 from "process";
|
|
1721
1725
|
function isUnicodeSupported() {
|
|
@@ -1724,8 +1728,12 @@ function isUnicodeSupported() {
|
|
|
1724
1728
|
}
|
|
1725
1729
|
return Boolean(process$1.env.CI) || Boolean(process$1.env.WT_SESSION) || Boolean(process$1.env.TERMINUS_SUBLIME) || process$1.env.ConEmuTask === "{cmd::Cmder}" || process$1.env.TERM_PROGRAM === "Terminus-Sublime" || process$1.env.TERM_PROGRAM === "vscode" || process$1.env.TERM === "xterm-256color" || process$1.env.TERM === "alacritty" || process$1.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
1726
1730
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1731
|
+
function formatInstructionFooter(o2, e) {
|
|
1732
|
+
const r2 = [`${e ? `${styleText2("cyan", S_BAR)} ` : ""}${o2.join(" \u2022 ")}`];
|
|
1733
|
+
return e && r2.push(styleText2("cyan", S_BAR_END)), r2;
|
|
1734
|
+
}
|
|
1735
|
+
var import_sisteransi2, unicode, unicodeOr = (o2, e) => unicode ? o2 : e, S_STEP_ACTIVE, S_STEP_CANCEL, S_STEP_ERROR, S_STEP_SUBMIT, S_BAR_START, S_BAR, S_BAR_END, S_BAR_START_RIGHT, S_BAR_END_RIGHT, S_RADIO_ACTIVE, S_RADIO_INACTIVE, S_CHECKBOX_ACTIVE, S_CHECKBOX_SELECTED, S_CHECKBOX_INACTIVE, S_PASSWORD_MASK, S_BAR_H, S_CORNER_TOP_RIGHT, S_CONNECT_LEFT, S_CORNER_BOTTOM_RIGHT, S_CORNER_BOTTOM_LEFT, S_CORNER_TOP_LEFT, S_INFO, S_SUCCESS, S_WARN, S_ERROR, symbol = (o2) => {
|
|
1736
|
+
switch (o2) {
|
|
1729
1737
|
case "initial":
|
|
1730
1738
|
case "active":
|
|
1731
1739
|
return styleText2("cyan", S_STEP_ACTIVE);
|
|
@@ -1736,8 +1744,8 @@ var import_sisteransi2, unicode, unicodeOr = (e, o2) => unicode ? e : o2, S_STEP
|
|
|
1736
1744
|
case "submit":
|
|
1737
1745
|
return styleText2("green", S_STEP_SUBMIT);
|
|
1738
1746
|
}
|
|
1739
|
-
}, symbolBar = (
|
|
1740
|
-
switch (
|
|
1747
|
+
}, symbolBar = (o2) => {
|
|
1748
|
+
switch (o2) {
|
|
1741
1749
|
case "initial":
|
|
1742
1750
|
case "active":
|
|
1743
1751
|
return styleText2("cyan", S_BAR);
|
|
@@ -1829,7 +1837,7 @@ ${g2}
|
|
|
1829
1837
|
}
|
|
1830
1838
|
}
|
|
1831
1839
|
}).prompt();
|
|
1832
|
-
}, log, intro = (o2 = "", t2) => {
|
|
1840
|
+
}, MULTISELECT_INSTRUCTIONS, log, intro = (o2 = "", t2) => {
|
|
1833
1841
|
const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_START)} ` : "";
|
|
1834
1842
|
i.write(`${e}${o2}
|
|
1835
1843
|
`);
|
|
@@ -1839,38 +1847,38 @@ ${styleText2("gray", S_BAR_END)} ` : "";
|
|
|
1839
1847
|
i.write(`${e}${o2}
|
|
1840
1848
|
|
|
1841
1849
|
`);
|
|
1842
|
-
}, W$1 = (o2) =>
|
|
1850
|
+
}, W$1 = (o2) => o2, C2 = (o2, e, s) => {
|
|
1843
1851
|
const a2 = {
|
|
1844
1852
|
hard: true,
|
|
1845
1853
|
trim: false
|
|
1846
1854
|
}, i = wrapAnsi(o2, e, a2).split(`
|
|
1847
|
-
`), c2 = i.reduce((n2,
|
|
1855
|
+
`), c2 = i.reduce((n2, t2) => Math.max(dist_default2(t2), n2), 0), u3 = i.map(s).reduce((n2, t2) => Math.max(dist_default2(t2), n2), 0), g2 = e - (u3 - c2);
|
|
1848
1856
|
return wrapAnsi(o2, g2, a2);
|
|
1849
1857
|
}, note = (o2 = "", e = "", s) => {
|
|
1850
1858
|
const a2 = s?.output ?? process$1.stdout, i = s?.withGuide ?? settings.withGuide, c2 = s?.format ?? W$1, g2 = ["", ...C2(o2, getColumns(a2) - 6, c2).split(`
|
|
1851
|
-
`).map(c2), ""], n2 = dist_default2(e),
|
|
1859
|
+
`).map(c2), ""], n2 = dist_default2(e), t2 = Math.max(g2.reduce((m2, F) => {
|
|
1852
1860
|
const O = dist_default2(F);
|
|
1853
1861
|
return O > m2 ? O : m2;
|
|
1854
|
-
}, 0), n2) + 2, h2 = g2.map((m2) => `${styleText2("gray", S_BAR)} ${m2}${" ".repeat(
|
|
1862
|
+
}, 0), n2) + 2, h2 = g2.map((m2) => `${styleText2("gray", S_BAR)} ${m2}${" ".repeat(t2 - dist_default2(m2))}${styleText2("gray", S_BAR)}`).join(`
|
|
1855
1863
|
`), T3 = i ? `${styleText2("gray", S_BAR)}
|
|
1856
1864
|
` : "", l$1 = i ? S_CONNECT_LEFT : S_CORNER_BOTTOM_LEFT;
|
|
1857
|
-
a2.write(`${T3}${styleText2("green", S_STEP_SUBMIT)} ${styleText2("reset", e)} ${styleText2("gray", S_BAR_H.repeat(Math.max(
|
|
1865
|
+
a2.write(`${T3}${styleText2("green", S_STEP_SUBMIT)} ${styleText2("reset", e)} ${styleText2("gray", S_BAR_H.repeat(Math.max(t2 - n2 - 1, 1)) + S_CORNER_TOP_RIGHT)}
|
|
1858
1866
|
${h2}
|
|
1859
|
-
${styleText2("gray", l$1 + S_BAR_H.repeat(
|
|
1867
|
+
${styleText2("gray", l$1 + S_BAR_H.repeat(t2 + 2) + S_CORNER_BOTTOM_RIGHT)}
|
|
1860
1868
|
`);
|
|
1861
|
-
}, u3, c2 = (
|
|
1862
|
-
`) ?
|
|
1863
|
-
`).map((
|
|
1864
|
-
`) : a2(
|
|
1865
|
-
const a2 = (
|
|
1866
|
-
const s =
|
|
1867
|
-
switch (
|
|
1869
|
+
}, u3, SELECT_INSTRUCTIONS, c2 = (t2, a2) => t2.includes(`
|
|
1870
|
+
`) ? t2.split(`
|
|
1871
|
+
`).map((i) => a2(i)).join(`
|
|
1872
|
+
`) : a2(t2), select = (t2) => {
|
|
1873
|
+
const a2 = (i, m2) => {
|
|
1874
|
+
const s = i.label ?? String(i.value);
|
|
1875
|
+
switch (m2) {
|
|
1868
1876
|
case "disabled":
|
|
1869
|
-
return `${styleText2("gray", S_RADIO_INACTIVE)} ${c2(s, (n2) => styleText2("gray", n2))}${
|
|
1877
|
+
return `${styleText2("gray", S_RADIO_INACTIVE)} ${c2(s, (n2) => styleText2("gray", n2))}${i.hint ? ` ${styleText2("dim", `(${i.hint ?? "disabled"})`)}` : ""}`;
|
|
1870
1878
|
case "selected":
|
|
1871
1879
|
return `${c2(s, (n2) => styleText2("dim", n2))}`;
|
|
1872
1880
|
case "active":
|
|
1873
|
-
return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${
|
|
1881
|
+
return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${i.hint ? ` ${styleText2("dim", `(${i.hint})`)}` : ""}`;
|
|
1874
1882
|
case "cancelled":
|
|
1875
1883
|
return `${c2(s, (n2) => styleText2(["strikethrough", "dim"], n2))}`;
|
|
1876
1884
|
default:
|
|
@@ -1878,39 +1886,40 @@ ${styleText2("gray", l$1 + S_BAR_H.repeat(r2 + 2) + S_CORNER_BOTTOM_RIGHT)}
|
|
|
1878
1886
|
}
|
|
1879
1887
|
};
|
|
1880
1888
|
return new a({
|
|
1881
|
-
options:
|
|
1882
|
-
signal:
|
|
1883
|
-
input:
|
|
1884
|
-
output:
|
|
1885
|
-
initialValue:
|
|
1889
|
+
options: t2.options,
|
|
1890
|
+
signal: t2.signal,
|
|
1891
|
+
input: t2.input,
|
|
1892
|
+
output: t2.output,
|
|
1893
|
+
initialValue: t2.initialValue,
|
|
1886
1894
|
render() {
|
|
1887
|
-
const
|
|
1895
|
+
const i = t2.withGuide ?? settings.withGuide, m2 = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, n2 = wrapTextWithPrefix(t2.output, t2.message, s, m2), u4 = `${i ? `${styleText2("gray", S_BAR)}
|
|
1888
1896
|
` : ""}${n2}
|
|
1889
1897
|
`;
|
|
1890
1898
|
switch (this.state) {
|
|
1891
1899
|
case "submit": {
|
|
1892
|
-
const r2 =
|
|
1893
|
-
return `${u4}${
|
|
1900
|
+
const r2 = i ? `${styleText2("gray", S_BAR)} ` : "", o2 = wrapTextWithPrefix(t2.output, a2(this.options[this.cursor], "selected"), r2);
|
|
1901
|
+
return `${u4}${o2}`;
|
|
1894
1902
|
}
|
|
1895
1903
|
case "cancel": {
|
|
1896
|
-
const r2 =
|
|
1897
|
-
return `${u4}${
|
|
1904
|
+
const r2 = i ? `${styleText2("gray", S_BAR)} ` : "", o2 = wrapTextWithPrefix(t2.output, a2(this.options[this.cursor], "cancelled"), r2);
|
|
1905
|
+
return `${u4}${o2}${i ? `
|
|
1898
1906
|
${styleText2("gray", S_BAR)}` : ""}`;
|
|
1899
1907
|
}
|
|
1900
1908
|
default: {
|
|
1901
|
-
const r2 =
|
|
1902
|
-
`).length,
|
|
1909
|
+
const r2 = i ? `${styleText2("cyan", S_BAR)} ` : "", o2 = u4.split(`
|
|
1910
|
+
`).length, $ = formatInstructionFooter(SELECT_INSTRUCTIONS, i), h2 = $.join(`
|
|
1911
|
+
`), b2 = $.length + 1;
|
|
1903
1912
|
return `${u4}${r2}${limitOptions({
|
|
1904
|
-
output:
|
|
1913
|
+
output: t2.output,
|
|
1905
1914
|
cursor: this.cursor,
|
|
1906
1915
|
options: this.options,
|
|
1907
|
-
maxItems:
|
|
1916
|
+
maxItems: t2.maxItems,
|
|
1908
1917
|
columnPadding: r2.length,
|
|
1909
|
-
rowPadding:
|
|
1910
|
-
style: (p2,
|
|
1918
|
+
rowPadding: o2 + b2,
|
|
1919
|
+
style: (p2, x) => a2(p2, p2.disabled ? "disabled" : x ? "active" : "inactive")
|
|
1911
1920
|
}).join(`
|
|
1912
1921
|
${r2}`)}
|
|
1913
|
-
${
|
|
1922
|
+
${h2}
|
|
1914
1923
|
`;
|
|
1915
1924
|
}
|
|
1916
1925
|
}
|
|
@@ -1949,6 +1958,11 @@ var init_dist4 = __esm(() => {
|
|
|
1949
1958
|
S_SUCCESS = unicodeOr("\u25C6", "*");
|
|
1950
1959
|
S_WARN = unicodeOr("\u25B2", "!");
|
|
1951
1960
|
S_ERROR = unicodeOr("\u25A0", "x");
|
|
1961
|
+
MULTISELECT_INSTRUCTIONS = [
|
|
1962
|
+
`${styleText2("dim", "\u2191/\u2193")} to navigate`,
|
|
1963
|
+
`${styleText2("dim", "Space:")} select`,
|
|
1964
|
+
`${styleText2("dim", "Enter:")} confirm`
|
|
1965
|
+
];
|
|
1952
1966
|
log = {
|
|
1953
1967
|
message: (s = [], {
|
|
1954
1968
|
symbol: e = styleText2("gray", S_BAR),
|
|
@@ -1996,6 +2010,10 @@ var init_dist4 = __esm(() => {
|
|
|
1996
2010
|
heavy: unicodeOr("\u2501", "="),
|
|
1997
2011
|
block: unicodeOr("\u2588", "#")
|
|
1998
2012
|
};
|
|
2013
|
+
SELECT_INSTRUCTIONS = [
|
|
2014
|
+
`${styleText2("dim", "\u2191/\u2193")} to navigate`,
|
|
2015
|
+
`${styleText2("dim", "Enter:")} confirm`
|
|
2016
|
+
];
|
|
1999
2017
|
i = `${styleText2("gray", S_BAR)} `;
|
|
2000
2018
|
});
|
|
2001
2019
|
|
|
@@ -7825,7 +7843,7 @@ var init_zod = __esm(() => {
|
|
|
7825
7843
|
init_external();
|
|
7826
7844
|
});
|
|
7827
7845
|
|
|
7828
|
-
// node_modules/.bun/@ts-rest+core@3.52.1+
|
|
7846
|
+
// node_modules/.bun/@ts-rest+core@3.52.1+f5aac5c7d31a6dcb/node_modules/@ts-rest/core/index.esm.mjs
|
|
7829
7847
|
var isZodType = (obj) => {
|
|
7830
7848
|
return typeof (obj === null || obj === undefined ? undefined : obj.safeParse) === "function";
|
|
7831
7849
|
}, isZodObjectStrict = (obj) => {
|
|
@@ -8147,7 +8165,7 @@ function parseLinearTarget(raw) {
|
|
|
8147
8165
|
return { kind: "team", teamKey: trimmed };
|
|
8148
8166
|
return { kind: "invalid", raw: trimmed };
|
|
8149
8167
|
}
|
|
8150
|
-
var BooleanLikeSchema, ErrorResponseSchema, OkResponseSchema, EnabledResponseSchema, BuiltInAgentIdSchema, AgentIdSchema, WorktreeCreateModeSchema, LinearIssueIdSchema, LinearTeamKeySchema, PostWorktreeToLinearTargetSchema, PostWorktreeToLinearRequestSchema, PostWorktreeToLinearResponseSchema, FromLinearInputSchema, OneshotConfigSchema, AgentCapabilitiesSchema, AgentSummarySchema, AgentDetailsSchema, AgentListResponseSchema, UpsertCustomAgentRequestSchema, AgentResponseSchema, ValidateCustomAgentResponseSchema, WorktreeCreationPhaseSchema, AvailableBranchSchema, AvailableBranchesQuerySchema, NumberLikePathParamSchema, BranchListResponseSchema, WorktreeSourceSchema, CreateWorktreeRequestSchema, OpenWorktreeRequestSchema, CreateWorktreeResponseSchema, SetWorktreeArchivedRequestSchema, SetWorktreeArchivedResponseSchema, SetWorktreeLabelRequestSchema, SetWorktreeLabelResponseSchema, ToggleEnabledRequestSchema, SendWorktreePromptRequestSchema, AgentsSendMessageRequestSchema, PullMainRequestSchema, PullMainStatusSchema, PullMainResponseSchema, ServiceStatusSchema, PrCommentSchema, CiCheckSchema, PrEntrySchema, LinearIssueLabelSchema, LinearIssueStateSchema, LinkedLinearIssueSchema, LinearIssueSchema, LinearIssueAvailabilitySchema, LinearIssuesResponseSchema, AutoNameProviderSchema, AutoNameConfigResponseSchema, WorktreeCreationStateSchema, AppNotificationSchema, ProjectWorktreeSnapshotSchema, ProjectSnapshotSchema, WorktreeConversationProviderSchema, CodexWorktreeConversationRefSchema, ClaudeWorktreeConversationRefSchema, WorktreeConversationRefSchema, AgentsUiWorktreeSummarySchema, AgentsUiConversationMessageRoleSchema, AgentsUiConversationMessageStatusSchema, AgentsUiConversationMessageKindSchema, AgentsUiConversationMessageSchema, AgentsUiConversationStateSchema, AgentsUiWorktreeConversationResponseSchema, AgentsUiSendMessageResponseSchema, AgentsUiInterruptResponseSchema, AgentsUiConversationMessageDeltaEventSchema, AgentsUiConversationMessageUpsertEventSchema, AgentsUiConversationStatusEventSchema, AgentsUiConversationErrorEventSchema, AgentsUiConversationEventSchema, WorktreeListResponseSchema, UnpushedCommitSchema, WorktreeDiffResponseSchema, ServiceConfigSchema, ProfileConfigSchema, LinkedRepoInfoSchema, AppConfigSchema, CiLogsResponseSchema, WorktreeNameParamsSchema, NotificationIdParamsSchema, AgentIdParamsSchema, RunIdParamsSchema, InstanceSummarySchema, InstancesResponseSchema;
|
|
8168
|
+
var BooleanLikeSchema, ErrorResponseSchema, OkResponseSchema, EnabledResponseSchema, BuiltInAgentIdSchema, AgentIdSchema, WorktreeCreateModeSchema, LinearIssueIdSchema, LinearTeamKeySchema, PostWorktreeToLinearTargetSchema, PostWorktreeToLinearRequestSchema, PostWorktreeToLinearResponseSchema, FromLinearInputSchema, OneshotConfigSchema, AgentCapabilitiesSchema, AgentSummarySchema, AgentDetailsSchema, AgentListResponseSchema, UpsertCustomAgentRequestSchema, AgentResponseSchema, ValidateCustomAgentResponseSchema, WorktreeCreationPhaseSchema, AvailableBranchSchema, AvailableBranchesQuerySchema, NumberLikePathParamSchema, BranchListResponseSchema, WorktreeSourceSchema, CreateWorktreeRequestSchema, OpenWorktreeRequestSchema, CreateWorktreeResponseSchema, SetWorktreeArchivedRequestSchema, SetWorktreeArchivedResponseSchema, SetWorktreeLabelRequestSchema, SetWorktreeLabelResponseSchema, ToggleEnabledRequestSchema, SendWorktreePromptRequestSchema, AgentsSendMessageRequestSchema, PullMainRequestSchema, PullMainStatusSchema, PullMainResponseSchema, ServiceStatusSchema, PrCommentSchema, CiCheckSchema, PrEntrySchema, LinearIssueLabelSchema, LinearIssueStateSchema, LinkedLinearIssueSchema, LinearIssueSchema, LinearIssueAvailabilitySchema, LinearIssuesResponseSchema, AutoNameProviderSchema, AutoNameConfigResponseSchema, WorktreeCreationStateSchema, AppNotificationSchema, WorktreeTabSchema, ProjectWorktreeSnapshotSchema, ProjectSnapshotSchema, WorktreeConversationProviderSchema, CodexWorktreeConversationRefSchema, ClaudeWorktreeConversationRefSchema, WorktreeConversationRefSchema, AgentsUiWorktreeSummarySchema, AgentsUiConversationMessageRoleSchema, AgentsUiConversationMessageStatusSchema, AgentsUiConversationMessageKindSchema, AgentsUiConversationMessageSchema, AgentsUiConversationStateSchema, AgentsUiWorktreeConversationResponseSchema, AgentsUiSendMessageResponseSchema, AgentsUiInterruptResponseSchema, AgentsUiConversationMessageDeltaEventSchema, AgentsUiConversationMessageUpsertEventSchema, AgentsUiConversationStatusEventSchema, AgentsUiConversationErrorEventSchema, AgentsUiConversationEventSchema, WorktreeListResponseSchema, UnpushedCommitSchema, WorktreeDiffResponseSchema, ServiceConfigSchema, ProfileConfigSchema, LinkedRepoInfoSchema, AppConfigSchema, CiLogsResponseSchema, WorktreeNameParamsSchema, WorktreeTabParamsSchema, CreateTabResponseSchema, NotificationIdParamsSchema, AgentIdParamsSchema, RunIdParamsSchema, InstanceSummarySchema, InstancesResponseSchema;
|
|
8151
8169
|
var init_schemas = __esm(() => {
|
|
8152
8170
|
init_zod();
|
|
8153
8171
|
BooleanLikeSchema = exports_external.union([
|
|
@@ -8403,6 +8421,15 @@ var init_schemas = __esm(() => {
|
|
|
8403
8421
|
url: exports_external.string().optional(),
|
|
8404
8422
|
timestamp: exports_external.number()
|
|
8405
8423
|
});
|
|
8424
|
+
WorktreeTabSchema = exports_external.object({
|
|
8425
|
+
tabId: exports_external.string(),
|
|
8426
|
+
kind: exports_external.enum(["root", "fork"]),
|
|
8427
|
+
label: exports_external.string(),
|
|
8428
|
+
seq: exports_external.number().nullable(),
|
|
8429
|
+
sessionId: exports_external.string().nullable(),
|
|
8430
|
+
paneId: exports_external.string().optional(),
|
|
8431
|
+
createdAt: exports_external.string()
|
|
8432
|
+
});
|
|
8406
8433
|
ProjectWorktreeSnapshotSchema = exports_external.object({
|
|
8407
8434
|
branch: exports_external.string(),
|
|
8408
8435
|
label: exports_external.string().nullable(),
|
|
@@ -8425,7 +8452,9 @@ var init_schemas = __esm(() => {
|
|
|
8425
8452
|
linearIssue: LinkedLinearIssueSchema.nullable(),
|
|
8426
8453
|
creation: WorktreeCreationStateSchema.nullable(),
|
|
8427
8454
|
source: WorktreeSourceSchema,
|
|
8428
|
-
oneshot: OneshotConfigSchema.nullable()
|
|
8455
|
+
oneshot: OneshotConfigSchema.nullable(),
|
|
8456
|
+
tabs: exports_external.array(WorktreeTabSchema).default([]),
|
|
8457
|
+
activeTabId: exports_external.string().nullable().default(null)
|
|
8429
8458
|
});
|
|
8430
8459
|
ProjectSnapshotSchema = exports_external.object({
|
|
8431
8460
|
project: exports_external.object({
|
|
@@ -8508,12 +8537,14 @@ var init_schemas = __esm(() => {
|
|
|
8508
8537
|
AgentsUiSendMessageResponseSchema = exports_external.object({
|
|
8509
8538
|
conversationId: exports_external.string(),
|
|
8510
8539
|
turnId: exports_external.string(),
|
|
8511
|
-
running: exports_external.literal(true)
|
|
8540
|
+
running: exports_external.literal(true),
|
|
8541
|
+
streaming: exports_external.boolean()
|
|
8512
8542
|
});
|
|
8513
8543
|
AgentsUiInterruptResponseSchema = exports_external.object({
|
|
8514
8544
|
conversationId: exports_external.string(),
|
|
8515
8545
|
turnId: exports_external.string(),
|
|
8516
|
-
interrupted: exports_external.literal(true)
|
|
8546
|
+
interrupted: exports_external.literal(true),
|
|
8547
|
+
streaming: exports_external.boolean()
|
|
8517
8548
|
});
|
|
8518
8549
|
AgentsUiConversationMessageDeltaEventSchema = exports_external.object({
|
|
8519
8550
|
type: exports_external.literal("messageDelta"),
|
|
@@ -8594,6 +8625,13 @@ var init_schemas = __esm(() => {
|
|
|
8594
8625
|
WorktreeNameParamsSchema = exports_external.object({
|
|
8595
8626
|
name: exports_external.string()
|
|
8596
8627
|
});
|
|
8628
|
+
WorktreeTabParamsSchema = exports_external.object({
|
|
8629
|
+
name: exports_external.string(),
|
|
8630
|
+
tabId: exports_external.string()
|
|
8631
|
+
});
|
|
8632
|
+
CreateTabResponseSchema = exports_external.object({
|
|
8633
|
+
tab: WorktreeTabSchema
|
|
8634
|
+
});
|
|
8597
8635
|
NotificationIdParamsSchema = exports_external.object({
|
|
8598
8636
|
id: NumberLikePathParamSchema
|
|
8599
8637
|
});
|
|
@@ -8646,6 +8684,9 @@ var init_contract = __esm(() => {
|
|
|
8646
8684
|
postWorktreeToLinear: "/api/worktrees/:name/linear/post",
|
|
8647
8685
|
setWorktreeLabel: "/api/worktrees/:name/label",
|
|
8648
8686
|
sendWorktreePrompt: "/api/worktrees/:name/send",
|
|
8687
|
+
createWorktreeTab: "/api/worktrees/:name/tabs",
|
|
8688
|
+
selectWorktreeTab: "/api/worktrees/:name/tabs/:tabId/select",
|
|
8689
|
+
deleteWorktreeTab: "/api/worktrees/:name/tabs/:tabId",
|
|
8649
8690
|
mergeWorktree: "/api/worktrees/:name/merge",
|
|
8650
8691
|
fetchWorktreeDiff: "/api/worktrees/:name/diff",
|
|
8651
8692
|
fetchLinearIssues: "/api/linear/issues",
|
|
@@ -8900,6 +8941,35 @@ var init_contract = __esm(() => {
|
|
|
8900
8941
|
...commonErrorResponses
|
|
8901
8942
|
}
|
|
8902
8943
|
},
|
|
8944
|
+
createWorktreeTab: {
|
|
8945
|
+
method: "POST",
|
|
8946
|
+
path: apiPaths.createWorktreeTab,
|
|
8947
|
+
pathParams: WorktreeNameParamsSchema,
|
|
8948
|
+
body: c3.noBody(),
|
|
8949
|
+
responses: {
|
|
8950
|
+
201: CreateTabResponseSchema,
|
|
8951
|
+
...commonErrorResponses
|
|
8952
|
+
}
|
|
8953
|
+
},
|
|
8954
|
+
selectWorktreeTab: {
|
|
8955
|
+
method: "POST",
|
|
8956
|
+
path: apiPaths.selectWorktreeTab,
|
|
8957
|
+
pathParams: WorktreeTabParamsSchema,
|
|
8958
|
+
body: c3.noBody(),
|
|
8959
|
+
responses: {
|
|
8960
|
+
200: OkResponseSchema,
|
|
8961
|
+
...commonErrorResponses
|
|
8962
|
+
}
|
|
8963
|
+
},
|
|
8964
|
+
deleteWorktreeTab: {
|
|
8965
|
+
method: "DELETE",
|
|
8966
|
+
path: apiPaths.deleteWorktreeTab,
|
|
8967
|
+
pathParams: WorktreeTabParamsSchema,
|
|
8968
|
+
responses: {
|
|
8969
|
+
200: OkResponseSchema,
|
|
8970
|
+
...commonErrorResponses
|
|
8971
|
+
}
|
|
8972
|
+
},
|
|
8903
8973
|
mergeWorktree: {
|
|
8904
8974
|
method: "POST",
|
|
8905
8975
|
path: apiPaths.mergeWorktree,
|
|
@@ -10156,6 +10226,18 @@ function summarizeToolInput(toolName, jsonText) {
|
|
|
10156
10226
|
}
|
|
10157
10227
|
if (!input)
|
|
10158
10228
|
return truncateInline(jsonText, 100);
|
|
10229
|
+
if (toolName.toLowerCase() === "askuserquestion" && Array.isArray(input.questions)) {
|
|
10230
|
+
const headers = [];
|
|
10231
|
+
for (const question of input.questions) {
|
|
10232
|
+
if (question && typeof question === "object" && !Array.isArray(question)) {
|
|
10233
|
+
const header = question.header;
|
|
10234
|
+
if (typeof header === "string")
|
|
10235
|
+
headers.push(header);
|
|
10236
|
+
}
|
|
10237
|
+
}
|
|
10238
|
+
if (headers.length > 0)
|
|
10239
|
+
return truncateInline(headers.join(", "), 120);
|
|
10240
|
+
}
|
|
10159
10241
|
const keys = TOOL_PRIMARY_KEY[toolName.toLowerCase()];
|
|
10160
10242
|
if (keys) {
|
|
10161
10243
|
const values = [];
|
|
@@ -10201,14 +10283,30 @@ function flushStreamingLine(state) {
|
|
|
10201
10283
|
process.stdout.write(`
|
|
10202
10284
|
`);
|
|
10203
10285
|
state.streamingItemId = null;
|
|
10286
|
+
state.streamingText = "";
|
|
10287
|
+
state.streamingTurnId = null;
|
|
10204
10288
|
state.streamingNeedsHeader = false;
|
|
10205
10289
|
}
|
|
10206
10290
|
}
|
|
10291
|
+
function messageKind(message) {
|
|
10292
|
+
return message.kind ?? "text";
|
|
10293
|
+
}
|
|
10294
|
+
function isSameStreamingMessage(state, message) {
|
|
10295
|
+
if (state.streamingItemId === null)
|
|
10296
|
+
return false;
|
|
10297
|
+
if (state.streamingItemId === message.id)
|
|
10298
|
+
return true;
|
|
10299
|
+
if (message.role !== "assistant" || messageKind(message) !== "text")
|
|
10300
|
+
return false;
|
|
10301
|
+
if (state.streamingText.length === 0)
|
|
10302
|
+
return false;
|
|
10303
|
+
return message.turnId === state.streamingTurnId && (state.streamingText.startsWith(message.text) || message.text.startsWith(state.streamingText));
|
|
10304
|
+
}
|
|
10207
10305
|
function printNewMessages(state, messages) {
|
|
10208
10306
|
for (const message of messages) {
|
|
10209
10307
|
if (state.printedMessageIds.has(message.id))
|
|
10210
10308
|
continue;
|
|
10211
|
-
if (state
|
|
10309
|
+
if (isSameStreamingMessage(state, message)) {
|
|
10212
10310
|
state.printedMessageIds.add(message.id);
|
|
10213
10311
|
if (message.status === "completed")
|
|
10214
10312
|
flushStreamingLine(state);
|
|
@@ -10225,13 +10323,6 @@ function printNewMessages(state, messages) {
|
|
|
10225
10323
|
}
|
|
10226
10324
|
}
|
|
10227
10325
|
function handleConversationEvent(event, state, stderr) {
|
|
10228
|
-
if (event.type === "snapshot") {
|
|
10229
|
-
if (event.revision <= state.lastStreamRevision)
|
|
10230
|
-
return;
|
|
10231
|
-
state.lastStreamRevision = event.revision;
|
|
10232
|
-
printNewMessages(state, event.data.conversation.messages);
|
|
10233
|
-
return;
|
|
10234
|
-
}
|
|
10235
10326
|
if (event.type === "messageDelta") {
|
|
10236
10327
|
if (event.revision <= state.lastStreamRevision)
|
|
10237
10328
|
return;
|
|
@@ -10239,6 +10330,8 @@ function handleConversationEvent(event, state, stderr) {
|
|
|
10239
10330
|
if (state.streamingItemId !== event.itemId) {
|
|
10240
10331
|
flushStreamingLine(state);
|
|
10241
10332
|
state.streamingItemId = event.itemId;
|
|
10333
|
+
state.streamingText = "";
|
|
10334
|
+
state.streamingTurnId = event.turnId;
|
|
10242
10335
|
state.streamingNeedsHeader = true;
|
|
10243
10336
|
}
|
|
10244
10337
|
if (state.streamingNeedsHeader) {
|
|
@@ -10246,6 +10339,7 @@ function handleConversationEvent(event, state, stderr) {
|
|
|
10246
10339
|
state.streamingNeedsHeader = false;
|
|
10247
10340
|
}
|
|
10248
10341
|
process.stdout.write(event.delta);
|
|
10342
|
+
state.streamingText += event.delta;
|
|
10249
10343
|
return;
|
|
10250
10344
|
}
|
|
10251
10345
|
if (event.type === "messageUpsert") {
|
|
@@ -10255,6 +10349,14 @@ function handleConversationEvent(event, state, stderr) {
|
|
|
10255
10349
|
printNewMessages(state, [event.message]);
|
|
10256
10350
|
return;
|
|
10257
10351
|
}
|
|
10352
|
+
if (event.type === "conversationStatus") {
|
|
10353
|
+
if (event.revision <= state.lastStreamRevision)
|
|
10354
|
+
return;
|
|
10355
|
+
state.lastStreamRevision = event.revision;
|
|
10356
|
+
if (!event.running)
|
|
10357
|
+
flushStreamingLine(state);
|
|
10358
|
+
return;
|
|
10359
|
+
}
|
|
10258
10360
|
if (event.type === "error") {
|
|
10259
10361
|
flushStreamingLine(state);
|
|
10260
10362
|
stderr(`[${timestamp()}] [error] ${event.message}`);
|
|
@@ -10632,6 +10734,8 @@ async function runOneshot(parsed, port) {
|
|
|
10632
10734
|
const conversationState = {
|
|
10633
10735
|
printedMessageIds: new Set,
|
|
10634
10736
|
streamingItemId: null,
|
|
10737
|
+
streamingText: "",
|
|
10738
|
+
streamingTurnId: null,
|
|
10635
10739
|
streamingNeedsHeader: false,
|
|
10636
10740
|
lastStreamRevision: 0
|
|
10637
10741
|
};
|
|
@@ -10888,7 +10992,12 @@ var init_linear_commands = __esm(() => {
|
|
|
10888
10992
|
});
|
|
10889
10993
|
|
|
10890
10994
|
// backend/src/domain/model.ts
|
|
10891
|
-
|
|
10995
|
+
function conversationSessionId(conversation) {
|
|
10996
|
+
if (!conversation)
|
|
10997
|
+
return null;
|
|
10998
|
+
return conversation.provider === "codexAppServer" ? conversation.threadId : conversation.sessionId;
|
|
10999
|
+
}
|
|
11000
|
+
var WORKTREE_META_SCHEMA_VERSION = 1, WORKTREE_ARCHIVE_STATE_VERSION = 1, ROOT_TAB_ID = "root";
|
|
10892
11001
|
|
|
10893
11002
|
// backend/src/adapters/fs.ts
|
|
10894
11003
|
import { mkdir } from "fs/promises";
|
|
@@ -11064,10 +11173,28 @@ function normalizeConversationMeta(raw) {
|
|
|
11064
11173
|
function normalizeOptionalString(raw) {
|
|
11065
11174
|
return typeof raw === "string" && raw.trim() ? raw.trim() : undefined;
|
|
11066
11175
|
}
|
|
11176
|
+
function backfillTabs(meta) {
|
|
11177
|
+
if (meta.tabs && meta.tabs.length > 0)
|
|
11178
|
+
return null;
|
|
11179
|
+
const rootTab = {
|
|
11180
|
+
tabId: ROOT_TAB_ID,
|
|
11181
|
+
kind: "root",
|
|
11182
|
+
label: "Root",
|
|
11183
|
+
seq: null,
|
|
11184
|
+
sessionId: conversationSessionId(meta.conversation),
|
|
11185
|
+
createdAt: meta.createdAt
|
|
11186
|
+
};
|
|
11187
|
+
return {
|
|
11188
|
+
tabs: [rootTab],
|
|
11189
|
+
activeTabId: ROOT_TAB_ID,
|
|
11190
|
+
forkCounter: meta.forkCounter ?? 0
|
|
11191
|
+
};
|
|
11192
|
+
}
|
|
11067
11193
|
function normalizeWorktreeMeta(meta) {
|
|
11068
11194
|
const conversation = normalizeConversationMeta(meta.conversation);
|
|
11069
11195
|
const normalizedLabel = normalizeOptionalString(meta.label);
|
|
11070
|
-
|
|
11196
|
+
const tabBackfill = backfillTabs(meta);
|
|
11197
|
+
if (conversation === meta.conversation && normalizedLabel === meta.label && tabBackfill === null) {
|
|
11071
11198
|
return meta;
|
|
11072
11199
|
}
|
|
11073
11200
|
const rest = { ...meta };
|
|
@@ -11076,7 +11203,8 @@ function normalizeWorktreeMeta(meta) {
|
|
|
11076
11203
|
return {
|
|
11077
11204
|
...rest,
|
|
11078
11205
|
...normalizedLabel ? { label: normalizedLabel } : {},
|
|
11079
|
-
...conversation !== undefined ? { conversation } : {}
|
|
11206
|
+
...conversation !== undefined ? { conversation } : {},
|
|
11207
|
+
...tabBackfill ?? {}
|
|
11080
11208
|
};
|
|
11081
11209
|
}
|
|
11082
11210
|
function isPrComment(raw) {
|
|
@@ -11147,6 +11275,9 @@ function buildProjectSessionName(projectRoot) {
|
|
|
11147
11275
|
function buildWorktreeWindowName(branch) {
|
|
11148
11276
|
return `wm-${branch}`;
|
|
11149
11277
|
}
|
|
11278
|
+
function buildWorktreeParkingWindowName(branch) {
|
|
11279
|
+
return `wm-${branch}-tabs`;
|
|
11280
|
+
}
|
|
11150
11281
|
function parseWindowSummaries(output) {
|
|
11151
11282
|
return output.split(`
|
|
11152
11283
|
`).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
@@ -11212,6 +11343,24 @@ class BunTmuxGateway {
|
|
|
11212
11343
|
const output = assertTmuxOk(["list-windows", "-a", "-F", "#{session_name}\t#{window_name}\t#{window_panes}"], "list tmux windows");
|
|
11213
11344
|
return parseWindowSummaries(output);
|
|
11214
11345
|
}
|
|
11346
|
+
getPaneId(target) {
|
|
11347
|
+
return assertTmuxOk(["display-message", "-p", "-t", target, "#{pane_id}"], `resolve tmux pane id for ${target}`);
|
|
11348
|
+
}
|
|
11349
|
+
createParkedPane(opts) {
|
|
11350
|
+
if (!this.hasWindow(opts.sessionName, opts.parkingWindow)) {
|
|
11351
|
+
return assertTmuxOk(["new-window", "-d", "-P", "-F", "#{pane_id}", "-t", opts.sessionName, "-n", opts.parkingWindow, "-c", opts.cwd, opts.command], `create parking window ${opts.sessionName}:${opts.parkingWindow}`);
|
|
11352
|
+
}
|
|
11353
|
+
return assertTmuxOk(["split-window", "-d", "-P", "-F", "#{pane_id}", "-t", `${opts.sessionName}:${opts.parkingWindow}`, "-c", opts.cwd, opts.command], `create parked pane in ${opts.sessionName}:${opts.parkingWindow}`);
|
|
11354
|
+
}
|
|
11355
|
+
swapPanes(source, destination) {
|
|
11356
|
+
assertTmuxOk(["swap-pane", "-s", source, "-t", destination], `swap tmux panes ${source} <-> ${destination}`);
|
|
11357
|
+
}
|
|
11358
|
+
killPane(target) {
|
|
11359
|
+
const result = runTmux(["kill-pane", "-t", target]);
|
|
11360
|
+
if (result.exitCode !== 0 && !result.stderr.includes("can't find pane") && !isIgnorableKillWindowError(result.stderr)) {
|
|
11361
|
+
throw new Error(`kill tmux pane ${target} failed: ${result.stderr}`);
|
|
11362
|
+
}
|
|
11363
|
+
}
|
|
11215
11364
|
}
|
|
11216
11365
|
var init_tmux = () => {};
|
|
11217
11366
|
|
|
@@ -19093,6 +19242,97 @@ class BunPortProbe {
|
|
|
19093
19242
|
}
|
|
19094
19243
|
}
|
|
19095
19244
|
|
|
19245
|
+
// backend/src/lib/type-guards.ts
|
|
19246
|
+
function isRecord5(raw) {
|
|
19247
|
+
return typeof raw === "object" && raw !== null && !Array.isArray(raw);
|
|
19248
|
+
}
|
|
19249
|
+
|
|
19250
|
+
// backend/src/adapters/claude-cli.ts
|
|
19251
|
+
function encodeClaudeProjectDir(cwd) {
|
|
19252
|
+
return cwd.replace(/[^A-Za-z0-9]/g, "-");
|
|
19253
|
+
}
|
|
19254
|
+
var init_claude_cli = __esm(() => {
|
|
19255
|
+
init_log();
|
|
19256
|
+
});
|
|
19257
|
+
|
|
19258
|
+
// backend/src/adapters/session-discovery.ts
|
|
19259
|
+
import { readdir, stat as stat2 } from "fs/promises";
|
|
19260
|
+
import { basename as basename5, join as join12 } from "path";
|
|
19261
|
+
function home() {
|
|
19262
|
+
const value = Bun.env.HOME;
|
|
19263
|
+
if (!value)
|
|
19264
|
+
throw new Error("HOME is required to resolve agent sessions");
|
|
19265
|
+
return value;
|
|
19266
|
+
}
|
|
19267
|
+
function newestFirst(sessions) {
|
|
19268
|
+
return sessions.sort((left, right) => right.mtimeMs - left.mtimeMs).map((entry) => entry.sessionId);
|
|
19269
|
+
}
|
|
19270
|
+
async function listClaudeSessionIds(cwd) {
|
|
19271
|
+
const dir = join12(home(), ".claude", "projects", encodeClaudeProjectDir(cwd));
|
|
19272
|
+
const names = await readdir(dir).catch(() => []);
|
|
19273
|
+
const stamped = await Promise.all(names.filter((name) => name.endsWith(".jsonl")).map(async (name) => {
|
|
19274
|
+
const info = await stat2(join12(dir, name)).catch(() => null);
|
|
19275
|
+
return info ? { sessionId: basename5(name, ".jsonl"), mtimeMs: info.mtimeMs } : null;
|
|
19276
|
+
}));
|
|
19277
|
+
return newestFirst(stamped.filter((entry) => entry !== null));
|
|
19278
|
+
}
|
|
19279
|
+
async function readCodexSessionCwdId(path) {
|
|
19280
|
+
try {
|
|
19281
|
+
const head = await Bun.file(path).slice(0, 16384).text();
|
|
19282
|
+
const firstLine = head.split(`
|
|
19283
|
+
`, 1)[0];
|
|
19284
|
+
if (!firstLine)
|
|
19285
|
+
return null;
|
|
19286
|
+
const parsed = JSON.parse(firstLine);
|
|
19287
|
+
if (!isRecord5(parsed) || parsed.type !== "session_meta" || !isRecord5(parsed.payload))
|
|
19288
|
+
return null;
|
|
19289
|
+
const { id, cwd } = parsed.payload;
|
|
19290
|
+
return typeof id === "string" && typeof cwd === "string" ? { id, cwd } : null;
|
|
19291
|
+
} catch {
|
|
19292
|
+
return null;
|
|
19293
|
+
}
|
|
19294
|
+
}
|
|
19295
|
+
async function listCodexSessionIds(cwd) {
|
|
19296
|
+
const root = join12(home(), ".codex", "sessions");
|
|
19297
|
+
const relPaths = await readdir(root, { recursive: true }).catch(() => []);
|
|
19298
|
+
const rollouts = relPaths.filter((rel) => {
|
|
19299
|
+
const name = basename5(rel);
|
|
19300
|
+
return name.startsWith("rollout-") && name.endsWith(".jsonl");
|
|
19301
|
+
});
|
|
19302
|
+
const stamped = await Promise.all(rollouts.map(async (rel) => {
|
|
19303
|
+
const path = join12(root, rel);
|
|
19304
|
+
const meta = await readCodexSessionCwdId(path);
|
|
19305
|
+
if (!meta || meta.cwd !== cwd)
|
|
19306
|
+
return null;
|
|
19307
|
+
const info = await stat2(path).catch(() => null);
|
|
19308
|
+
return info ? { sessionId: meta.id, mtimeMs: info.mtimeMs } : null;
|
|
19309
|
+
}));
|
|
19310
|
+
return newestFirst(stamped.filter((entry) => entry !== null));
|
|
19311
|
+
}
|
|
19312
|
+
|
|
19313
|
+
class FileSessionDiscovery {
|
|
19314
|
+
async listSessionIds(agent, cwd) {
|
|
19315
|
+
return agent === "claude" ? await listClaudeSessionIds(cwd) : await listCodexSessionIds(cwd);
|
|
19316
|
+
}
|
|
19317
|
+
}
|
|
19318
|
+
async function captureNewSessionId(discovery, agent, cwd, before, options = {}) {
|
|
19319
|
+
const beforeSet = new Set(before);
|
|
19320
|
+
const attempts = options.attempts ?? 20;
|
|
19321
|
+
const delayMs = options.delayMs ?? 150;
|
|
19322
|
+
const sleep = options.sleep ?? ((ms) => Bun.sleep(ms));
|
|
19323
|
+
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
19324
|
+
const after = await discovery.listSessionIds(agent, cwd);
|
|
19325
|
+
const fresh = after.filter((id) => !beforeSet.has(id));
|
|
19326
|
+
if (fresh.length > 0)
|
|
19327
|
+
return fresh[0];
|
|
19328
|
+
await sleep(delayMs);
|
|
19329
|
+
}
|
|
19330
|
+
return null;
|
|
19331
|
+
}
|
|
19332
|
+
var init_session_discovery = __esm(() => {
|
|
19333
|
+
init_claude_cli();
|
|
19334
|
+
});
|
|
19335
|
+
|
|
19096
19336
|
// backend/src/lib/branch-name.ts
|
|
19097
19337
|
import { randomUUID } from "crypto";
|
|
19098
19338
|
function generateFallbackBranchName() {
|
|
@@ -19250,11 +19490,11 @@ var init_archive_state_service = __esm(() => {
|
|
|
19250
19490
|
|
|
19251
19491
|
// backend/src/adapters/agent-runtime.ts
|
|
19252
19492
|
import { chmod as chmod2, mkdir as mkdir3 } from "fs/promises";
|
|
19253
|
-
import { dirname as dirname4, join as
|
|
19493
|
+
import { dirname as dirname4, join as join13, resolve as resolve6 } from "path";
|
|
19254
19494
|
function shellQuote(value) {
|
|
19255
19495
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
19256
19496
|
}
|
|
19257
|
-
function
|
|
19497
|
+
function isRecord6(value) {
|
|
19258
19498
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19259
19499
|
}
|
|
19260
19500
|
function buildAgentCtlScript() {
|
|
@@ -19640,9 +19880,9 @@ function commandStartsWithAgentCtl(command, agentCtlPath) {
|
|
|
19640
19880
|
return trimmedCommand === agentCtlPath || trimmedCommand.startsWith(`${agentCtlPath} `) || trimmedCommand === quotedAgentCtlPath || trimmedCommand.startsWith(`${quotedAgentCtlPath} `);
|
|
19641
19881
|
}
|
|
19642
19882
|
function isWebmuxHookGroup(group, agentCtlPath) {
|
|
19643
|
-
if (!
|
|
19883
|
+
if (!isRecord6(group) || !Array.isArray(group.hooks))
|
|
19644
19884
|
return false;
|
|
19645
|
-
return group.hooks.some((hook) =>
|
|
19885
|
+
return group.hooks.some((hook) => isRecord6(hook) && typeof hook.command === "string" && commandStartsWithAgentCtl(hook.command, agentCtlPath));
|
|
19646
19886
|
}
|
|
19647
19887
|
async function mergeCodexHooksFile(hooksPath, hookSettings, agentCtlPath) {
|
|
19648
19888
|
let existing = {};
|
|
@@ -19657,7 +19897,7 @@ async function mergeCodexHooksFile(hooksPath, hookSettings, agentCtlPath) {
|
|
|
19657
19897
|
} catch {
|
|
19658
19898
|
existing = {};
|
|
19659
19899
|
}
|
|
19660
|
-
const existingHooks =
|
|
19900
|
+
const existingHooks = isRecord6(existing.hooks) ? existing.hooks : {};
|
|
19661
19901
|
const mergedHooks = { ...existingHooks };
|
|
19662
19902
|
for (const [eventName, groups] of Object.entries(hookSettings)) {
|
|
19663
19903
|
const eventGroups = existingHooks[eventName];
|
|
@@ -19669,7 +19909,7 @@ async function mergeCodexHooksFile(hooksPath, hookSettings, agentCtlPath) {
|
|
|
19669
19909
|
}
|
|
19670
19910
|
async function resolveGitCommonDir(gitDir) {
|
|
19671
19911
|
try {
|
|
19672
|
-
const commonDir = (await Bun.file(
|
|
19912
|
+
const commonDir = (await Bun.file(join13(gitDir, "commondir")).text()).trim();
|
|
19673
19913
|
if (!commonDir)
|
|
19674
19914
|
return gitDir;
|
|
19675
19915
|
return commonDir.startsWith("/") ? commonDir : resolve6(gitDir, commonDir);
|
|
@@ -19679,7 +19919,7 @@ async function resolveGitCommonDir(gitDir) {
|
|
|
19679
19919
|
}
|
|
19680
19920
|
async function ensureGeneratedCodexHooksIgnored(gitDir) {
|
|
19681
19921
|
const commonDir = await resolveGitCommonDir(gitDir);
|
|
19682
|
-
const excludePath =
|
|
19922
|
+
const excludePath = join13(commonDir, "info", "exclude");
|
|
19683
19923
|
let existing = "";
|
|
19684
19924
|
try {
|
|
19685
19925
|
existing = await Bun.file(excludePath).text();
|
|
@@ -19699,9 +19939,9 @@ async function ensureGeneratedCodexHooksIgnored(gitDir) {
|
|
|
19699
19939
|
async function ensureAgentRuntimeArtifacts(input) {
|
|
19700
19940
|
const storagePaths = getWorktreeStoragePaths(input.gitDir);
|
|
19701
19941
|
const artifacts = {
|
|
19702
|
-
agentCtlPath:
|
|
19703
|
-
claudeSettingsPath:
|
|
19704
|
-
codexHooksPath:
|
|
19942
|
+
agentCtlPath: join13(storagePaths.webmuxDir, "webmux-agentctl"),
|
|
19943
|
+
claudeSettingsPath: join13(input.worktreePath, ".claude", "settings.local.json"),
|
|
19944
|
+
codexHooksPath: join13(input.worktreePath, ".codex", "hooks.json")
|
|
19705
19945
|
};
|
|
19706
19946
|
await mkdir3(dirname4(artifacts.claudeSettingsPath), { recursive: true });
|
|
19707
19947
|
await mkdir3(dirname4(artifacts.codexHooksPath), { recursive: true });
|
|
@@ -19709,7 +19949,7 @@ async function ensureAgentRuntimeArtifacts(input) {
|
|
|
19709
19949
|
await chmod2(artifacts.agentCtlPath, 493);
|
|
19710
19950
|
const hookSettings = buildClaudeHookSettings(artifacts);
|
|
19711
19951
|
const hooks = hookSettings.hooks;
|
|
19712
|
-
if (!
|
|
19952
|
+
if (!isRecord6(hooks)) {
|
|
19713
19953
|
throw new Error("Invalid Claude hook settings");
|
|
19714
19954
|
}
|
|
19715
19955
|
await mergeClaudeSettings(artifacts.claudeSettingsPath, hooks);
|
|
@@ -19722,6 +19962,63 @@ var init_agent_runtime = __esm(() => {
|
|
|
19722
19962
|
init_fs();
|
|
19723
19963
|
});
|
|
19724
19964
|
|
|
19965
|
+
// backend/src/services/tab-logic.ts
|
|
19966
|
+
function listTabs(meta) {
|
|
19967
|
+
return meta.tabs ?? [];
|
|
19968
|
+
}
|
|
19969
|
+
function findTab(meta, tabId) {
|
|
19970
|
+
return listTabs(meta).find((tab) => tab.tabId === tabId);
|
|
19971
|
+
}
|
|
19972
|
+
function rootTab(meta) {
|
|
19973
|
+
const tabs = listTabs(meta);
|
|
19974
|
+
return tabs.find((tab) => tab.kind === "root") ?? tabs[0];
|
|
19975
|
+
}
|
|
19976
|
+
function activeTabId(meta) {
|
|
19977
|
+
return meta.activeTabId ?? ROOT_TAB_ID;
|
|
19978
|
+
}
|
|
19979
|
+
function nextForkSeq(meta) {
|
|
19980
|
+
return (meta.forkCounter ?? 0) + 1;
|
|
19981
|
+
}
|
|
19982
|
+
function buildForkTab(input) {
|
|
19983
|
+
return {
|
|
19984
|
+
tabId: `fork-${input.seq}`,
|
|
19985
|
+
kind: "fork",
|
|
19986
|
+
label: `Fork ${input.seq}`,
|
|
19987
|
+
seq: input.seq,
|
|
19988
|
+
sessionId: input.sessionId,
|
|
19989
|
+
...input.paneId ? { paneId: input.paneId } : {},
|
|
19990
|
+
createdAt: input.createdAt
|
|
19991
|
+
};
|
|
19992
|
+
}
|
|
19993
|
+
function appendTab(meta, tab) {
|
|
19994
|
+
return {
|
|
19995
|
+
...meta,
|
|
19996
|
+
tabs: [...listTabs(meta), tab],
|
|
19997
|
+
forkCounter: tab.seq ?? meta.forkCounter ?? 0,
|
|
19998
|
+
activeTabId: tab.tabId
|
|
19999
|
+
};
|
|
20000
|
+
}
|
|
20001
|
+
function removeTab(meta, tabId) {
|
|
20002
|
+
return {
|
|
20003
|
+
...meta,
|
|
20004
|
+
tabs: listTabs(meta).filter((tab) => tab.tabId !== tabId),
|
|
20005
|
+
activeTabId: activeTabId(meta) === tabId ? ROOT_TAB_ID : meta.activeTabId
|
|
20006
|
+
};
|
|
20007
|
+
}
|
|
20008
|
+
function updateTab(meta, tabId, patch) {
|
|
20009
|
+
return {
|
|
20010
|
+
...meta,
|
|
20011
|
+
tabs: listTabs(meta).map((tab) => tab.tabId === tabId ? { ...tab, ...patch } : tab)
|
|
20012
|
+
};
|
|
20013
|
+
}
|
|
20014
|
+
function setActiveTab(meta, tabId) {
|
|
20015
|
+
return { ...meta, activeTabId: tabId };
|
|
20016
|
+
}
|
|
20017
|
+
function withTabs(meta, tabs) {
|
|
20018
|
+
return { ...meta, tabs };
|
|
20019
|
+
}
|
|
20020
|
+
var init_tab_logic = () => {};
|
|
20021
|
+
|
|
19725
20022
|
// backend/src/services/agent-service.ts
|
|
19726
20023
|
function quoteShell(value) {
|
|
19727
20024
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
@@ -19737,6 +20034,9 @@ function buildBuiltInAgentInvocation(input) {
|
|
|
19737
20034
|
if (input.agent === "codex") {
|
|
19738
20035
|
const hooksFlag = " --enable hooks";
|
|
19739
20036
|
const yoloFlag2 = input.yolo ? " --yolo" : "";
|
|
20037
|
+
if (input.launchMode === "fork" && input.forkFromSessionId) {
|
|
20038
|
+
return `codex${hooksFlag}${yoloFlag2} fork ${quoteShell(input.forkFromSessionId)}${promptSuffix}`;
|
|
20039
|
+
}
|
|
19740
20040
|
if (input.launchMode === "resume") {
|
|
19741
20041
|
const resumeTarget = input.resumeConversationId ? ` ${quoteShell(input.resumeConversationId)}` : " --last";
|
|
19742
20042
|
return `codex${hooksFlag}${yoloFlag2} resume${resumeTarget}${promptSuffix}`;
|
|
@@ -19747,8 +20047,13 @@ function buildBuiltInAgentInvocation(input) {
|
|
|
19747
20047
|
return `codex${hooksFlag}${yoloFlag2}${promptSuffix}`;
|
|
19748
20048
|
}
|
|
19749
20049
|
const yoloFlag = input.yolo ? " --dangerously-skip-permissions" : "";
|
|
20050
|
+
if (input.launchMode === "fork" && input.forkFromSessionId) {
|
|
20051
|
+
const pin = input.pinSessionId ? ` --session-id ${quoteShell(input.pinSessionId)}` : "";
|
|
20052
|
+
return `claude${yoloFlag} --resume ${quoteShell(input.forkFromSessionId)} --fork-session${pin}${promptSuffix}`;
|
|
20053
|
+
}
|
|
19750
20054
|
if (input.launchMode === "resume") {
|
|
19751
|
-
|
|
20055
|
+
const resumeTarget = input.resumeConversationId ? ` --resume ${quoteShell(input.resumeConversationId)}` : " --continue";
|
|
20056
|
+
return `claude${yoloFlag}${resumeTarget}${promptSuffix}`;
|
|
19752
20057
|
}
|
|
19753
20058
|
if (input.systemPrompt) {
|
|
19754
20059
|
return `claude${yoloFlag} --append-system-prompt ${quoteShell(input.systemPrompt)}${promptSuffix}`;
|
|
@@ -19783,7 +20088,9 @@ function buildAgentInvocation(input) {
|
|
|
19783
20088
|
systemPrompt: input.systemPrompt,
|
|
19784
20089
|
prompt: input.prompt,
|
|
19785
20090
|
launchMode: input.launchMode,
|
|
19786
|
-
resumeConversationId: input.resumeConversationId
|
|
20091
|
+
resumeConversationId: input.resumeConversationId,
|
|
20092
|
+
forkFromSessionId: input.forkFromSessionId,
|
|
20093
|
+
pinSessionId: input.pinSessionId
|
|
19787
20094
|
});
|
|
19788
20095
|
}
|
|
19789
20096
|
return buildCustomAgentInvocation({
|
|
@@ -20167,6 +20474,7 @@ var init_worktree_service = __esm(() => {
|
|
|
20167
20474
|
});
|
|
20168
20475
|
|
|
20169
20476
|
// backend/src/services/lifecycle-service.ts
|
|
20477
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
20170
20478
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
20171
20479
|
import { dirname as dirname5, resolve as resolve8 } from "path";
|
|
20172
20480
|
function toErrorMessage2(error) {
|
|
@@ -20310,6 +20618,15 @@ class LifecycleService {
|
|
|
20310
20618
|
agentTerminalStale: false
|
|
20311
20619
|
});
|
|
20312
20620
|
}
|
|
20621
|
+
await this.restoreWorktreeTabs({
|
|
20622
|
+
branch,
|
|
20623
|
+
gitDir: resolved.gitDir,
|
|
20624
|
+
worktreePath: resolved.entry.path,
|
|
20625
|
+
profile,
|
|
20626
|
+
profileName,
|
|
20627
|
+
agent,
|
|
20628
|
+
runtimeEnvPath: initialized.paths.runtimeEnvPath
|
|
20629
|
+
});
|
|
20313
20630
|
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
20314
20631
|
return {
|
|
20315
20632
|
branch,
|
|
@@ -20328,12 +20645,24 @@ class LifecycleService {
|
|
|
20328
20645
|
const initialized = await this.refreshManagedArtifacts(resolved);
|
|
20329
20646
|
const { profileName, profile } = this.resolveProfile(initialized.meta.profile);
|
|
20330
20647
|
const agent = this.resolveAgentDefinition(initialized.meta.agent);
|
|
20331
|
-
if (agent.kind !== "builtin" || agent.implementation.agent !== "codex") {
|
|
20332
|
-
throw new LifecycleError("Refreshing the agent terminal is only available for
|
|
20648
|
+
if (agent.kind !== "builtin" || agent.implementation.agent !== "codex" && agent.implementation.agent !== "claude") {
|
|
20649
|
+
throw new LifecycleError("Refreshing the agent terminal is only available for built-in agent worktrees", 409);
|
|
20333
20650
|
}
|
|
20334
20651
|
const conversation = initialized.meta.conversation;
|
|
20335
|
-
if (conversation
|
|
20336
|
-
throw new LifecycleError(
|
|
20652
|
+
if (!conversation) {
|
|
20653
|
+
throw new LifecycleError(`No ${agent.label} conversation is available to refresh`, 409);
|
|
20654
|
+
}
|
|
20655
|
+
let resumeConversationId;
|
|
20656
|
+
if (agent.implementation.agent === "codex") {
|
|
20657
|
+
if (conversation.provider !== "codexAppServer") {
|
|
20658
|
+
throw new LifecycleError(`No ${agent.label} conversation is available to refresh`, 409);
|
|
20659
|
+
}
|
|
20660
|
+
resumeConversationId = conversation.threadId;
|
|
20661
|
+
} else {
|
|
20662
|
+
if (conversation.provider !== "claudeCode") {
|
|
20663
|
+
throw new LifecycleError(`No ${agent.label} conversation is available to refresh`, 409);
|
|
20664
|
+
}
|
|
20665
|
+
resumeConversationId = conversation.sessionId;
|
|
20337
20666
|
}
|
|
20338
20667
|
await ensureAgentRuntimeArtifacts({
|
|
20339
20668
|
gitDir: initialized.paths.gitDir,
|
|
@@ -20347,12 +20676,21 @@ class LifecycleService {
|
|
|
20347
20676
|
initialized,
|
|
20348
20677
|
worktreePath: resolved.entry.path,
|
|
20349
20678
|
launchMode: "resume",
|
|
20350
|
-
resumeConversationId
|
|
20679
|
+
resumeConversationId
|
|
20351
20680
|
});
|
|
20352
20681
|
await writeWorktreeMeta(resolved.gitDir, {
|
|
20353
20682
|
...initialized.meta,
|
|
20354
20683
|
agentTerminalStale: false
|
|
20355
20684
|
});
|
|
20685
|
+
await this.restoreWorktreeTabs({
|
|
20686
|
+
branch,
|
|
20687
|
+
gitDir: resolved.gitDir,
|
|
20688
|
+
worktreePath: resolved.entry.path,
|
|
20689
|
+
profile,
|
|
20690
|
+
profileName,
|
|
20691
|
+
agent,
|
|
20692
|
+
runtimeEnvPath: initialized.paths.runtimeEnvPath
|
|
20693
|
+
});
|
|
20356
20694
|
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
20357
20695
|
return {
|
|
20358
20696
|
branch,
|
|
@@ -20362,6 +20700,193 @@ class LifecycleService {
|
|
|
20362
20700
|
throw this.wrapOperationError(error);
|
|
20363
20701
|
}
|
|
20364
20702
|
}
|
|
20703
|
+
async createWorktreeTab(branch) {
|
|
20704
|
+
try {
|
|
20705
|
+
const ctx = await this.prepareTabContext(branch);
|
|
20706
|
+
const rootSessionId = await this.ensureRootSessionId(ctx);
|
|
20707
|
+
if (!rootSessionId) {
|
|
20708
|
+
throw new LifecycleError("The root session hasn't started yet \u2014 interact with it once before forking a tab", 409);
|
|
20709
|
+
}
|
|
20710
|
+
const meta = await this.readMetaOrThrow(ctx.resolved.gitDir);
|
|
20711
|
+
const seq = nextForkSeq(meta);
|
|
20712
|
+
const pinSessionId = ctx.agentKind === "claude" ? randomUUID3() : undefined;
|
|
20713
|
+
const agentCommand = buildAgentPaneCommand({
|
|
20714
|
+
agent: ctx.agent,
|
|
20715
|
+
runtimeEnvPath: ctx.initialized.paths.runtimeEnvPath,
|
|
20716
|
+
repoRoot: this.deps.projectRoot,
|
|
20717
|
+
worktreePath: ctx.worktreePath,
|
|
20718
|
+
branch,
|
|
20719
|
+
profileName: ctx.profileName,
|
|
20720
|
+
yolo: ctx.profile.yolo === true,
|
|
20721
|
+
launchMode: "fork",
|
|
20722
|
+
forkFromSessionId: rootSessionId,
|
|
20723
|
+
pinSessionId
|
|
20724
|
+
});
|
|
20725
|
+
const visibleSlot = `${ctx.sessionName}:${ctx.windowName}.0`;
|
|
20726
|
+
const outgoingActiveId = activeTabId(meta);
|
|
20727
|
+
const outgoingPaneId = this.deps.tmux.getPaneId(visibleSlot);
|
|
20728
|
+
const before = await this.deps.sessionDiscovery.listSessionIds(ctx.agentKind, ctx.worktreePath);
|
|
20729
|
+
const paneId = this.deps.tmux.createParkedPane({
|
|
20730
|
+
sessionName: ctx.sessionName,
|
|
20731
|
+
parkingWindow: ctx.parkingWindow,
|
|
20732
|
+
cwd: ctx.worktreePath,
|
|
20733
|
+
command: buildManagedShellCommand(ctx.initialized.paths.runtimeEnvPath)
|
|
20734
|
+
});
|
|
20735
|
+
this.deps.tmux.runCommand(paneId, agentCommand);
|
|
20736
|
+
const sessionId = pinSessionId ?? await captureNewSessionId(this.deps.sessionDiscovery, ctx.agentKind, ctx.worktreePath, before);
|
|
20737
|
+
const tab = buildForkTab({ seq, sessionId, paneId, createdAt: new Date().toISOString() });
|
|
20738
|
+
let nextMeta = appendTab(meta, tab);
|
|
20739
|
+
nextMeta = updateTab(nextMeta, outgoingActiveId, { paneId: outgoingPaneId });
|
|
20740
|
+
await writeWorktreeMeta(ctx.resolved.gitDir, nextMeta);
|
|
20741
|
+
this.deps.tmux.swapPanes(paneId, visibleSlot);
|
|
20742
|
+
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
20743
|
+
return { tab };
|
|
20744
|
+
} catch (error) {
|
|
20745
|
+
throw this.wrapOperationError(error);
|
|
20746
|
+
}
|
|
20747
|
+
}
|
|
20748
|
+
async selectWorktreeTab(branch, tabId) {
|
|
20749
|
+
try {
|
|
20750
|
+
const ctx = await this.prepareTabContext(branch);
|
|
20751
|
+
const target = findTab(ctx.meta, tabId);
|
|
20752
|
+
if (!target)
|
|
20753
|
+
throw new LifecycleError(`Tab not found: ${tabId}`, 404);
|
|
20754
|
+
const outgoingActiveId = activeTabId(ctx.meta);
|
|
20755
|
+
if (outgoingActiveId === tabId)
|
|
20756
|
+
return;
|
|
20757
|
+
if (!target.paneId)
|
|
20758
|
+
throw new LifecycleError(`Tab ${tabId} has no live pane to show`, 409);
|
|
20759
|
+
const visibleSlot = `${ctx.sessionName}:${ctx.windowName}.0`;
|
|
20760
|
+
const outgoingPaneId = this.deps.tmux.getPaneId(visibleSlot);
|
|
20761
|
+
this.deps.tmux.swapPanes(target.paneId, visibleSlot);
|
|
20762
|
+
let nextMeta = updateTab(ctx.meta, outgoingActiveId, { paneId: outgoingPaneId });
|
|
20763
|
+
nextMeta = setActiveTab(nextMeta, tabId);
|
|
20764
|
+
await writeWorktreeMeta(ctx.resolved.gitDir, nextMeta);
|
|
20765
|
+
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
20766
|
+
} catch (error) {
|
|
20767
|
+
throw this.wrapOperationError(error);
|
|
20768
|
+
}
|
|
20769
|
+
}
|
|
20770
|
+
async deleteWorktreeTab(branch, tabId) {
|
|
20771
|
+
try {
|
|
20772
|
+
const ctx = await this.prepareTabContext(branch);
|
|
20773
|
+
const target = findTab(ctx.meta, tabId);
|
|
20774
|
+
if (!target)
|
|
20775
|
+
throw new LifecycleError(`Tab not found: ${tabId}`, 404);
|
|
20776
|
+
if (target.kind === "root")
|
|
20777
|
+
throw new LifecycleError("The root tab cannot be deleted", 400);
|
|
20778
|
+
const root = rootTab(ctx.meta);
|
|
20779
|
+
if (activeTabId(ctx.meta) === tabId && root?.paneId) {
|
|
20780
|
+
this.deps.tmux.swapPanes(root.paneId, `${ctx.sessionName}:${ctx.windowName}.0`);
|
|
20781
|
+
}
|
|
20782
|
+
if (target.paneId)
|
|
20783
|
+
this.deps.tmux.killPane(target.paneId);
|
|
20784
|
+
await writeWorktreeMeta(ctx.resolved.gitDir, removeTab(ctx.meta, tabId));
|
|
20785
|
+
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
20786
|
+
} catch (error) {
|
|
20787
|
+
throw this.wrapOperationError(error);
|
|
20788
|
+
}
|
|
20789
|
+
}
|
|
20790
|
+
async readMetaOrThrow(gitDir) {
|
|
20791
|
+
const meta = await readWorktreeMeta(gitDir);
|
|
20792
|
+
if (!meta)
|
|
20793
|
+
throw new LifecycleError("Worktree metadata is missing", 409);
|
|
20794
|
+
return meta;
|
|
20795
|
+
}
|
|
20796
|
+
async prepareTabContext(branch) {
|
|
20797
|
+
const resolved = await this.resolveExistingWorktree(branch);
|
|
20798
|
+
if (!resolved.meta)
|
|
20799
|
+
throw new LifecycleError(`Worktree ${branch} has no managed metadata`, 409);
|
|
20800
|
+
const sessionName = buildProjectSessionName(this.deps.projectRoot);
|
|
20801
|
+
const windowName = buildWorktreeWindowName(branch);
|
|
20802
|
+
if (!this.deps.tmux.hasWindow(sessionName, windowName)) {
|
|
20803
|
+
throw new LifecycleError(`Worktree ${branch} is not open`, 409);
|
|
20804
|
+
}
|
|
20805
|
+
const { profileName, profile } = this.resolveProfile(resolved.meta.profile);
|
|
20806
|
+
if (profile.runtime === "docker") {
|
|
20807
|
+
throw new LifecycleError("Tabs are not supported for Docker worktrees", 409);
|
|
20808
|
+
}
|
|
20809
|
+
const agent = this.resolveAgentDefinition(resolved.meta.agent);
|
|
20810
|
+
if (agent.kind !== "builtin") {
|
|
20811
|
+
throw new LifecycleError("Tabs are only available for the built-in Claude and Codex agents", 409);
|
|
20812
|
+
}
|
|
20813
|
+
const initialized = await this.refreshManagedArtifacts(resolved);
|
|
20814
|
+
return {
|
|
20815
|
+
resolved,
|
|
20816
|
+
initialized,
|
|
20817
|
+
meta: initialized.meta,
|
|
20818
|
+
worktreePath: resolved.entry.path,
|
|
20819
|
+
agent,
|
|
20820
|
+
agentKind: agent.implementation.agent,
|
|
20821
|
+
profile,
|
|
20822
|
+
profileName,
|
|
20823
|
+
sessionName,
|
|
20824
|
+
windowName,
|
|
20825
|
+
parkingWindow: buildWorktreeParkingWindowName(branch)
|
|
20826
|
+
};
|
|
20827
|
+
}
|
|
20828
|
+
async ensureRootSessionId(ctx) {
|
|
20829
|
+
const root = rootTab(ctx.meta);
|
|
20830
|
+
if (root?.sessionId)
|
|
20831
|
+
return root.sessionId;
|
|
20832
|
+
const discovered = (await this.deps.sessionDiscovery.listSessionIds(ctx.agentKind, ctx.worktreePath))[0] ?? null;
|
|
20833
|
+
if (discovered && root) {
|
|
20834
|
+
await writeWorktreeMeta(ctx.resolved.gitDir, updateTab(ctx.meta, root.tabId, { sessionId: discovered }));
|
|
20835
|
+
}
|
|
20836
|
+
return discovered;
|
|
20837
|
+
}
|
|
20838
|
+
async restoreWorktreeTabs(input) {
|
|
20839
|
+
if (input.profile.runtime === "docker")
|
|
20840
|
+
return;
|
|
20841
|
+
if (input.agent.kind !== "builtin")
|
|
20842
|
+
return;
|
|
20843
|
+
const meta = await readWorktreeMeta(input.gitDir);
|
|
20844
|
+
const root = meta ? rootTab(meta) : undefined;
|
|
20845
|
+
if (!meta || !root)
|
|
20846
|
+
return;
|
|
20847
|
+
if (!listTabs(meta).some((tab) => tab.kind === "fork"))
|
|
20848
|
+
return;
|
|
20849
|
+
const sessionName = buildProjectSessionName(this.deps.projectRoot);
|
|
20850
|
+
const windowName = buildWorktreeWindowName(input.branch);
|
|
20851
|
+
const parkingWindow = buildWorktreeParkingWindowName(input.branch);
|
|
20852
|
+
this.deps.tmux.killWindow(sessionName, parkingWindow);
|
|
20853
|
+
const visibleSlot = `${sessionName}:${windowName}.0`;
|
|
20854
|
+
const visibleSlotPaneId = this.deps.tmux.getPaneId(visibleSlot);
|
|
20855
|
+
const restored = [{ ...root, paneId: visibleSlotPaneId }];
|
|
20856
|
+
for (const fork of listTabs(meta).filter((tab) => tab.kind === "fork")) {
|
|
20857
|
+
if (!fork.sessionId)
|
|
20858
|
+
continue;
|
|
20859
|
+
const command = buildAgentPaneCommand({
|
|
20860
|
+
agent: input.agent,
|
|
20861
|
+
runtimeEnvPath: input.runtimeEnvPath,
|
|
20862
|
+
repoRoot: this.deps.projectRoot,
|
|
20863
|
+
worktreePath: input.worktreePath,
|
|
20864
|
+
branch: input.branch,
|
|
20865
|
+
profileName: input.profileName,
|
|
20866
|
+
yolo: input.profile.yolo === true,
|
|
20867
|
+
launchMode: "resume",
|
|
20868
|
+
resumeConversationId: fork.sessionId
|
|
20869
|
+
});
|
|
20870
|
+
const paneId = this.deps.tmux.createParkedPane({
|
|
20871
|
+
sessionName,
|
|
20872
|
+
parkingWindow,
|
|
20873
|
+
cwd: input.worktreePath,
|
|
20874
|
+
command: buildManagedShellCommand(input.runtimeEnvPath)
|
|
20875
|
+
});
|
|
20876
|
+
this.deps.tmux.runCommand(paneId, command);
|
|
20877
|
+
restored.push({ ...fork, paneId });
|
|
20878
|
+
}
|
|
20879
|
+
let nextMeta = withTabs(meta, restored);
|
|
20880
|
+
const wantActive = activeTabId(meta);
|
|
20881
|
+
const activeTab = restored.find((tab) => tab.tabId === wantActive && tab.kind === "fork" && tab.paneId);
|
|
20882
|
+
if (activeTab?.paneId) {
|
|
20883
|
+
this.deps.tmux.swapPanes(activeTab.paneId, visibleSlotPaneId);
|
|
20884
|
+
nextMeta = setActiveTab(nextMeta, activeTab.tabId);
|
|
20885
|
+
} else {
|
|
20886
|
+
nextMeta = setActiveTab(nextMeta, ROOT_TAB_ID);
|
|
20887
|
+
}
|
|
20888
|
+
await writeWorktreeMeta(input.gitDir, nextMeta);
|
|
20889
|
+
}
|
|
20365
20890
|
async disarmOneshot(branch) {
|
|
20366
20891
|
let resolved;
|
|
20367
20892
|
try {
|
|
@@ -20640,8 +21165,13 @@ class LifecycleService {
|
|
|
20640
21165
|
}
|
|
20641
21166
|
return nextMeta;
|
|
20642
21167
|
}
|
|
21168
|
+
killWorktreeWindows(branch) {
|
|
21169
|
+
const sessionName = buildProjectSessionName(this.deps.projectRoot);
|
|
21170
|
+
this.deps.tmux.killWindow(sessionName, buildWorktreeWindowName(branch));
|
|
21171
|
+
this.deps.tmux.killWindow(sessionName, buildWorktreeParkingWindowName(branch));
|
|
21172
|
+
}
|
|
20643
21173
|
async closeBranchWindow(branch) {
|
|
20644
|
-
this.
|
|
21174
|
+
this.killWorktreeWindows(branch);
|
|
20645
21175
|
await this.deps.reconciliation.reconcile(this.deps.projectRoot, { force: true });
|
|
20646
21176
|
}
|
|
20647
21177
|
async materializeRuntimeSession(input) {
|
|
@@ -20745,7 +21275,7 @@ ${oneshotPrompt}` : oneshotPrompt ?? baseSystemPrompt;
|
|
|
20745
21275
|
}
|
|
20746
21276
|
}
|
|
20747
21277
|
try {
|
|
20748
|
-
this.
|
|
21278
|
+
this.killWorktreeWindows(branch);
|
|
20749
21279
|
} catch (error) {
|
|
20750
21280
|
cleanupErrors.push(`tmux cleanup failed: ${toErrorMessage2(error)}`);
|
|
20751
21281
|
}
|
|
@@ -20783,7 +21313,7 @@ ${oneshotPrompt}` : oneshotPrompt ?? baseSystemPrompt;
|
|
|
20783
21313
|
if (resolved.meta?.runtime === "docker") {
|
|
20784
21314
|
await this.deps.docker.removeContainer(branch);
|
|
20785
21315
|
}
|
|
20786
|
-
this.
|
|
21316
|
+
this.killWorktreeWindows(branch);
|
|
20787
21317
|
removeManagedWorktree({
|
|
20788
21318
|
repoRoot: this.deps.projectRoot,
|
|
20789
21319
|
worktreePath: resolved.entry.path,
|
|
@@ -20956,6 +21486,8 @@ var init_lifecycle_service = __esm(() => {
|
|
|
20956
21486
|
init_fs();
|
|
20957
21487
|
init_config();
|
|
20958
21488
|
init_tmux();
|
|
21489
|
+
init_session_discovery();
|
|
21490
|
+
init_tab_logic();
|
|
20959
21491
|
init_policies();
|
|
20960
21492
|
init_agent_service();
|
|
20961
21493
|
init_agent_registry();
|
|
@@ -21084,6 +21616,8 @@ function makeDefaultState(input) {
|
|
|
21084
21616
|
agentName: input.agentName ?? null,
|
|
21085
21617
|
source: input.source ?? "ui",
|
|
21086
21618
|
oneshot: input.oneshot ?? null,
|
|
21619
|
+
tabs: input.tabs ?? [],
|
|
21620
|
+
activeTabId: input.activeTabId ?? null,
|
|
21087
21621
|
agentTerminalStale: input.agentTerminalStale === true,
|
|
21088
21622
|
git: {
|
|
21089
21623
|
exists: true,
|
|
@@ -21140,6 +21674,10 @@ class ProjectRuntime {
|
|
|
21140
21674
|
existing.source = input.source;
|
|
21141
21675
|
if (input.oneshot !== undefined)
|
|
21142
21676
|
existing.oneshot = input.oneshot;
|
|
21677
|
+
if (input.tabs !== undefined)
|
|
21678
|
+
existing.tabs = input.tabs;
|
|
21679
|
+
if (input.activeTabId !== undefined)
|
|
21680
|
+
existing.activeTabId = input.activeTabId;
|
|
21143
21681
|
existing.git.exists = true;
|
|
21144
21682
|
existing.git.branch = input.branch;
|
|
21145
21683
|
existing.session.windowName = buildWorktreeWindowName(input.branch);
|
|
@@ -21276,7 +21814,7 @@ async function mapWithConcurrency(items, limit, fn) {
|
|
|
21276
21814
|
}
|
|
21277
21815
|
|
|
21278
21816
|
// backend/src/services/reconciliation-service.ts
|
|
21279
|
-
import { basename as
|
|
21817
|
+
import { basename as basename6, resolve as resolve9 } from "path";
|
|
21280
21818
|
function makeUnmanagedWorktreeId(path) {
|
|
21281
21819
|
return `unmanaged:${resolve9(path)}`;
|
|
21282
21820
|
}
|
|
@@ -21311,7 +21849,7 @@ function findWindow(windows, sessionName, branch) {
|
|
|
21311
21849
|
return windows.find((window) => window.sessionName === sessionName && window.windowName === windowName) ?? null;
|
|
21312
21850
|
}
|
|
21313
21851
|
function resolveBranch(entry, metaBranch) {
|
|
21314
|
-
const fallback =
|
|
21852
|
+
const fallback = basename6(entry.path);
|
|
21315
21853
|
return entry.branch ?? metaBranch ?? (fallback.length > 0 ? fallback : "unknown");
|
|
21316
21854
|
}
|
|
21317
21855
|
|
|
@@ -21374,6 +21912,8 @@ class ReconciliationService {
|
|
|
21374
21912
|
runtime: meta?.runtime ?? "host",
|
|
21375
21913
|
source: meta?.source ?? "ui",
|
|
21376
21914
|
oneshot: meta?.oneshot ?? null,
|
|
21915
|
+
tabs: meta?.tabs ?? [],
|
|
21916
|
+
activeTabId: meta?.activeTabId ?? null,
|
|
21377
21917
|
git: {
|
|
21378
21918
|
dirty: gitStatus.dirty,
|
|
21379
21919
|
aheadCount: gitStatus.aheadCount,
|
|
@@ -21409,7 +21949,9 @@ class ReconciliationService {
|
|
|
21409
21949
|
agentTerminalStale: state.agentTerminalStale,
|
|
21410
21950
|
runtime: state.runtime,
|
|
21411
21951
|
source: state.source,
|
|
21412
|
-
oneshot: state.oneshot
|
|
21952
|
+
oneshot: state.oneshot,
|
|
21953
|
+
tabs: state.tabs,
|
|
21954
|
+
activeTabId: state.activeTabId
|
|
21413
21955
|
});
|
|
21414
21956
|
this.deps.runtime.setGitState(state.worktreeId, {
|
|
21415
21957
|
exists: true,
|
|
@@ -21495,6 +22037,7 @@ function createWebmuxRuntime(options = {}) {
|
|
|
21495
22037
|
archiveState: archiveStateService,
|
|
21496
22038
|
git,
|
|
21497
22039
|
tmux,
|
|
22040
|
+
sessionDiscovery: new FileSessionDiscovery,
|
|
21498
22041
|
docker,
|
|
21499
22042
|
reconciliation: reconciliationService,
|
|
21500
22043
|
hooks,
|
|
@@ -21532,6 +22075,7 @@ var init_runtime = __esm(() => {
|
|
|
21532
22075
|
init_git();
|
|
21533
22076
|
init_hooks();
|
|
21534
22077
|
init_tmux();
|
|
22078
|
+
init_session_discovery();
|
|
21535
22079
|
init_auto_name_service();
|
|
21536
22080
|
init_archive_state_service();
|
|
21537
22081
|
init_lifecycle_service();
|
|
@@ -21543,6 +22087,7 @@ var init_runtime = __esm(() => {
|
|
|
21543
22087
|
var exports_worktree_commands = {};
|
|
21544
22088
|
__export(exports_worktree_commands, {
|
|
21545
22089
|
runWorktreeCommand: () => runWorktreeCommand,
|
|
22090
|
+
parseTabCommandArgs: () => parseTabCommandArgs,
|
|
21546
22091
|
parseSendCommandArgs: () => parseSendCommandArgs,
|
|
21547
22092
|
parseListCommandArgs: () => parseListCommandArgs,
|
|
21548
22093
|
parseLabelCommandArgs: () => parseLabelCommandArgs,
|
|
@@ -21550,7 +22095,7 @@ __export(exports_worktree_commands, {
|
|
|
21550
22095
|
parseAddCommandArgs: () => parseAddCommandArgs,
|
|
21551
22096
|
getWorktreeCommandUsage: () => getWorktreeCommandUsage
|
|
21552
22097
|
});
|
|
21553
|
-
import { basename as
|
|
22098
|
+
import { basename as basename7, resolve as resolve10 } from "path";
|
|
21554
22099
|
function getWorktreeCommandUsage(command) {
|
|
21555
22100
|
switch (command) {
|
|
21556
22101
|
case "add":
|
|
@@ -21630,6 +22175,18 @@ function getWorktreeCommandUsage(command) {
|
|
|
21630
22175
|
case "prune":
|
|
21631
22176
|
return `Usage:
|
|
21632
22177
|
webmux prune`;
|
|
22178
|
+
case "tab":
|
|
22179
|
+
return [
|
|
22180
|
+
"Usage:",
|
|
22181
|
+
" webmux tab <branch> List the agent tabs (\u2605 marks the active one)",
|
|
22182
|
+
" webmux tab <branch> new Create a new forked tab",
|
|
22183
|
+
" webmux tab <branch> switch <tabId> Switch the visible agent pane to a tab",
|
|
22184
|
+
" webmux tab <branch> close <tabId> Delete a forked tab",
|
|
22185
|
+
"",
|
|
22186
|
+
"Options:",
|
|
22187
|
+
" --help Show this help message"
|
|
22188
|
+
].join(`
|
|
22189
|
+
`);
|
|
21633
22190
|
}
|
|
21634
22191
|
}
|
|
21635
22192
|
function readOptionValue3(args, index, flag) {
|
|
@@ -21753,6 +22310,30 @@ function parseAddCommandArgs(args) {
|
|
|
21753
22310
|
}
|
|
21754
22311
|
return { input, detach, fromLinearIssueId, branchExplicit };
|
|
21755
22312
|
}
|
|
22313
|
+
function parseTabCommandArgs(args) {
|
|
22314
|
+
const positional = [];
|
|
22315
|
+
for (const arg of args) {
|
|
22316
|
+
if (arg === "--help" || arg === "-h")
|
|
22317
|
+
return null;
|
|
22318
|
+
if (arg.startsWith("-"))
|
|
22319
|
+
throw new CommandUsageError(`Unknown option: ${arg}`);
|
|
22320
|
+
positional.push(arg);
|
|
22321
|
+
}
|
|
22322
|
+
const [branch, rawAction = "list", tabId, ...rest] = positional;
|
|
22323
|
+
if (!branch)
|
|
22324
|
+
throw new CommandUsageError("Missing required argument: <branch>");
|
|
22325
|
+
if (!isValidWorktreeName(branch))
|
|
22326
|
+
throw new CommandUsageError("Invalid worktree name");
|
|
22327
|
+
if (rawAction !== "list" && rawAction !== "new" && rawAction !== "switch" && rawAction !== "close") {
|
|
22328
|
+
throw new CommandUsageError(`Unknown tab action: ${rawAction}`);
|
|
22329
|
+
}
|
|
22330
|
+
if ((rawAction === "switch" || rawAction === "close") && !tabId) {
|
|
22331
|
+
throw new CommandUsageError(`The "${rawAction}" action requires a <tabId>`);
|
|
22332
|
+
}
|
|
22333
|
+
if (rest.length > 0)
|
|
22334
|
+
throw new CommandUsageError(`Unexpected argument: ${rest[0]}`);
|
|
22335
|
+
return { branch, action: rawAction, ...tabId ? { tabId } : {} };
|
|
22336
|
+
}
|
|
21756
22337
|
function parseBranchCommandArgs(args) {
|
|
21757
22338
|
let branch = null;
|
|
21758
22339
|
for (const arg of args) {
|
|
@@ -21989,7 +22570,7 @@ async function listWorktrees(runtime, stdout2, options) {
|
|
|
21989
22570
|
const projectGitDir = runtime.git.resolveWorktreeGitDir(projectDir);
|
|
21990
22571
|
const archivedPaths = buildArchivedWorktreePathSet(await readWorktreeArchiveState(projectGitDir));
|
|
21991
22572
|
const rows = await Promise.all(entries.map(async (entry) => {
|
|
21992
|
-
const branch = entry.branch ??
|
|
22573
|
+
const branch = entry.branch ?? basename7(entry.path);
|
|
21993
22574
|
const isOpen = openWindows.has(buildWorktreeWindowName(branch));
|
|
21994
22575
|
const gitDir = runtime.git.resolveWorktreeGitDir(entry.path);
|
|
21995
22576
|
const meta = await readWorktreeMeta(gitDir);
|
|
@@ -22156,6 +22737,45 @@ ${parsed.input.prompt}` : seed.data.conversationMarkdown;
|
|
|
22156
22737
|
stdout2(`Sent prompt to ${parsed.branch}`);
|
|
22157
22738
|
return 0;
|
|
22158
22739
|
}
|
|
22740
|
+
if (context.command === "tab") {
|
|
22741
|
+
const parsed = parseTabCommandArgs(context.args);
|
|
22742
|
+
if (!parsed) {
|
|
22743
|
+
stdout2(getWorktreeCommandUsage("tab"));
|
|
22744
|
+
return 0;
|
|
22745
|
+
}
|
|
22746
|
+
const api = createApi(`http://localhost:${context.port}`);
|
|
22747
|
+
await withServerConnection(context.port, async () => {
|
|
22748
|
+
if (parsed.action === "new") {
|
|
22749
|
+
const { tab } = await api.createWorktreeTab({ params: { name: parsed.branch } });
|
|
22750
|
+
stdout2(`Created ${tab.label} (${tab.tabId}) in ${parsed.branch}`);
|
|
22751
|
+
return;
|
|
22752
|
+
}
|
|
22753
|
+
if (parsed.action === "switch" || parsed.action === "close") {
|
|
22754
|
+
const tabId = parsed.tabId;
|
|
22755
|
+
if (!tabId)
|
|
22756
|
+
throw new CommandUsageError(`The "${parsed.action}" action requires a <tabId>`);
|
|
22757
|
+
if (parsed.action === "switch") {
|
|
22758
|
+
await api.selectWorktreeTab({ params: { name: parsed.branch, tabId } });
|
|
22759
|
+
stdout2(`Switched ${parsed.branch} to tab ${tabId}`);
|
|
22760
|
+
} else {
|
|
22761
|
+
await api.deleteWorktreeTab({ params: { name: parsed.branch, tabId } });
|
|
22762
|
+
stdout2(`Closed tab ${tabId} in ${parsed.branch}`);
|
|
22763
|
+
}
|
|
22764
|
+
return;
|
|
22765
|
+
}
|
|
22766
|
+
const { worktrees } = await api.fetchWorktrees();
|
|
22767
|
+
const worktree = worktrees.find((candidate) => candidate.branch === parsed.branch);
|
|
22768
|
+
if (!worktree) {
|
|
22769
|
+
stdout2(`Worktree not found: ${parsed.branch}`);
|
|
22770
|
+
return;
|
|
22771
|
+
}
|
|
22772
|
+
for (const tab of worktree.tabs) {
|
|
22773
|
+
const marker = tab.tabId === worktree.activeTabId ? "\u2605" : " ";
|
|
22774
|
+
stdout2(`${marker} ${tab.label.padEnd(10)} ${tab.tabId}`);
|
|
22775
|
+
}
|
|
22776
|
+
});
|
|
22777
|
+
return 0;
|
|
22778
|
+
}
|
|
22159
22779
|
if (context.command === "label") {
|
|
22160
22780
|
const parsed = parseLabelCommandArgs(context.args);
|
|
22161
22781
|
if (!parsed) {
|
|
@@ -22237,13 +22857,13 @@ var init_worktree_commands = __esm(() => {
|
|
|
22237
22857
|
});
|
|
22238
22858
|
|
|
22239
22859
|
// bin/src/webmux.ts
|
|
22240
|
-
import { resolve as resolve11, dirname as dirname6, join as
|
|
22860
|
+
import { resolve as resolve11, dirname as dirname6, join as join14 } from "path";
|
|
22241
22861
|
import { existsSync as existsSync7 } from "fs";
|
|
22242
22862
|
import { fileURLToPath } from "url";
|
|
22243
22863
|
// package.json
|
|
22244
22864
|
var package_default = {
|
|
22245
22865
|
name: "webmux",
|
|
22246
|
-
version: "0.
|
|
22866
|
+
version: "0.38.0",
|
|
22247
22867
|
description: "Web dashboard for workmux \u2014 browser UI with embedded terminals, PR monitoring, and CI integration",
|
|
22248
22868
|
type: "module",
|
|
22249
22869
|
repository: {
|
|
@@ -22322,6 +22942,7 @@ Usage:
|
|
|
22322
22942
|
webmux remove Remove a worktree
|
|
22323
22943
|
webmux merge Merge a worktree into the main branch and remove it
|
|
22324
22944
|
webmux send Send a prompt to a running worktree agent
|
|
22945
|
+
webmux tab List, create, switch, or close agent tabs in a worktree
|
|
22325
22946
|
webmux prune Remove all worktrees in the current project
|
|
22326
22947
|
webmux linear Post a worktree conversation to a Linear issue/team
|
|
22327
22948
|
webmux completion Generate shell completion script (bash, zsh)
|
|
@@ -22342,7 +22963,7 @@ Environment:
|
|
|
22342
22963
|
`);
|
|
22343
22964
|
}
|
|
22344
22965
|
function isRootCommand(value) {
|
|
22345
|
-
return value === "serve" || value === "init" || value === "service" || value === "update" || value === "add" || value === "oneshot" || value === "list" || value === "open" || value === "close" || value === "refresh" || value === "archive" || value === "unarchive" || value === "label" || value === "remove" || value === "merge" || value === "send" || value === "prune" || value === "linear" || value === "completion";
|
|
22966
|
+
return value === "serve" || value === "init" || value === "service" || value === "update" || value === "add" || value === "oneshot" || value === "list" || value === "open" || value === "close" || value === "refresh" || value === "archive" || value === "unarchive" || value === "label" || value === "remove" || value === "merge" || value === "send" || value === "tab" || value === "prune" || value === "linear" || value === "completion";
|
|
22346
22967
|
}
|
|
22347
22968
|
function isServeRootOption(value) {
|
|
22348
22969
|
return value === "--port" || value === "--prefix" || value === "--app" || value === "--debug" || value === "--help" || value === "-h" || value === "--version" || value === "-V";
|
|
@@ -22420,7 +23041,7 @@ Run webmux --help for usage.`);
|
|
|
22420
23041
|
};
|
|
22421
23042
|
}
|
|
22422
23043
|
function isWorktreeCommand(command) {
|
|
22423
|
-
return command === "add" || command === "list" || command === "open" || command === "close" || command === "refresh" || command === "archive" || command === "unarchive" || command === "label" || command === "remove" || command === "merge" || command === "send" || command === "prune";
|
|
23044
|
+
return command === "add" || command === "list" || command === "open" || command === "close" || command === "refresh" || command === "archive" || command === "unarchive" || command === "label" || command === "remove" || command === "merge" || command === "send" || command === "tab" || command === "prune";
|
|
22424
23045
|
}
|
|
22425
23046
|
async function loadEnvFile(path) {
|
|
22426
23047
|
if (!existsSync7(path))
|
|
@@ -22630,8 +23251,8 @@ Refreshing ${services.length} installed webmux service(s) to pick up the new ver
|
|
|
22630
23251
|
}
|
|
22631
23252
|
process.on("SIGINT", cleanup);
|
|
22632
23253
|
process.on("SIGTERM", cleanup);
|
|
22633
|
-
const backendEntry =
|
|
22634
|
-
const staticDir =
|
|
23254
|
+
const backendEntry = join14(PKG_ROOT, "backend", "dist", "server.js");
|
|
23255
|
+
const staticDir = join14(PKG_ROOT, "frontend", "dist");
|
|
22635
23256
|
if (!existsSync7(staticDir)) {
|
|
22636
23257
|
console.error(`Error: frontend/dist/ not found. Run 'bun run build' first.`);
|
|
22637
23258
|
process.exit(1);
|