@band-app/server 0.54.3 → 0.54.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/_shell.html +0 -0
- package/dist/client/assets/{TerminalPanel-Xl6oGaup.js → TerminalPanel-CxzXrgZc.js} +1 -1
- package/dist/client/assets/{_basePickBy-CPkUkuQZ.js → _basePickBy-ESHGoJr1.js} +1 -1
- package/dist/client/assets/{_baseUniq-Cwz3JodT.js → _baseUniq-Caza5c4E.js} +1 -1
- package/dist/client/assets/{arc-DwsdIXPp.js → arc-DaZonhJY.js} +1 -1
- package/dist/client/assets/{architectureDiagram-VXUJARFQ-SXYkL9tA.js → architectureDiagram-VXUJARFQ-Cq7YbAnJ.js} +1 -1
- package/dist/client/assets/{blockDiagram-VD42YOAC-BJA-EF6e.js → blockDiagram-VD42YOAC-C8s0ghb3.js} +1 -1
- package/dist/client/assets/{c4Diagram-YG6GDRKO-Dx-xhS0w.js → c4Diagram-YG6GDRKO-DIBhFHGh.js} +1 -1
- package/dist/client/assets/channel-D_oNvIqF.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-hIHAP7Cm.js → chunk-4BX2VUAB-D3SZ3P8V.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-B819cB7q.js → chunk-55IACEB6-BfA-9jtI.js} +1 -1
- package/dist/client/assets/{chunk-B4BG7PRW-B1YMhrD2.js → chunk-B4BG7PRW-DAygCOyX.js} +1 -1
- package/dist/client/assets/{chunk-DI55MBZ5-B6E7V5JJ.js → chunk-DI55MBZ5-CuikvpNq.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-Cto0C9v0.js → chunk-FMBD7UC4-JCmcJDsw.js} +1 -1
- package/dist/client/assets/{chunk-QN33PNHL-FlpvaPrY.js → chunk-QN33PNHL-3o6_7ioT.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-hfVUofCn.js → chunk-QZHKN3VN-5B8nia1f.js} +1 -1
- package/dist/client/assets/{chunk-TZMSLE5B-B6Uwm_RZ.js → chunk-TZMSLE5B-stQp3cfp.js} +1 -1
- package/dist/client/assets/classDiagram-2ON5EDUG-C8EJzaAq.js +1 -0
- package/dist/client/assets/classDiagram-v2-WZHVMYZB-C8EJzaAq.js +1 -0
- package/dist/client/assets/clone-BhU1fGlA.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-N9jnwEcj.js → cose-bilkent-S5V4N54A-CVQzgyKs.js} +1 -1
- package/dist/client/assets/{dagre-6UL2VRFP-Ckg49tw0.js → dagre-6UL2VRFP-C95ZZlB4.js} +1 -1
- package/dist/client/assets/{diagram-PSM6KHXK-Dgp_MGHT.js → diagram-PSM6KHXK-Sb4x5piz.js} +1 -1
- package/dist/client/assets/{diagram-QEK2KX5R-HqtNbPpi.js → diagram-QEK2KX5R-TTDYZins.js} +1 -1
- package/dist/client/assets/{diagram-S2PKOQOG-B9kmoM2C.js → diagram-S2PKOQOG-C8joLdiY.js} +1 -1
- package/dist/client/assets/{erDiagram-Q2GNP2WA-CX0bAjbe.js → erDiagram-Q2GNP2WA-HXkzVcZE.js} +1 -1
- package/dist/client/assets/{flowDiagram-NV44I4VS-BlNstfD-.js → flowDiagram-NV44I4VS-BPzPMXG7.js} +1 -1
- package/dist/client/assets/{ganttDiagram-JELNMOA3-Bngv2UfV.js → ganttDiagram-JELNMOA3-5JGkIYiO.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-V2S2FVAM-DaVgpKKU.js → gitGraphDiagram-V2S2FVAM-CDCkC2dq.js} +1 -1
- package/dist/client/assets/{graph-DJibyMhY.js → graph-CQ3VI3Kb.js} +1 -1
- package/dist/client/assets/{highlighted-body-B3W2YXNL-8sRLcOTz.js → highlighted-body-B3W2YXNL-jA7b7ztt.js} +1 -1
- package/dist/client/assets/{index-ROMcQtWt.js → index-0DuoTbku.js} +1 -1
- package/dist/client/assets/{index-hBffkV04.js → index-B6FlIvZu.js} +1 -1
- package/dist/client/assets/{index-BsY88S9G.js → index-Bcr3D2nL.js} +1 -1
- package/dist/client/assets/{index-D_Hnyp0H.js → index-BedjQBXV.js} +1 -1
- package/dist/client/assets/{index-QiwXMA-_.js → index-C7Xj47iO.js} +1 -1
- package/dist/client/assets/{index-BdPmfxoL.js → index-CZqDBAUL.js} +1 -1
- package/dist/client/assets/{index-SOcZR9zA.js → index-Ce8SticC.js} +1 -1
- package/dist/client/assets/{index-Y9ds9lfC.js → index-DWepK3T1.js} +1 -1
- package/dist/client/assets/{index-BYWdCHQD.js → index-Dt3jdJaQ.js} +1 -1
- package/dist/client/assets/{index-C8jptsrq.js → index-F2GbFYex.js} +1 -1
- package/dist/client/assets/{index-CZkHIWnu.js → index-SjDPUTbU.js} +1 -1
- package/dist/client/assets/{index-BWbHkoFf.js → index-sTT9joMB.js} +1 -1
- package/dist/client/assets/{index-CSlSUYm9.js → index-xEgVFQfI.js} +1 -1
- package/dist/client/assets/{infoDiagram-HS3SLOUP-BaXQhmZS.js → infoDiagram-HS3SLOUP-DXlzs7_8.js} +1 -1
- package/dist/client/assets/{journeyDiagram-XKPGCS4Q-Bn4XhLKd.js → journeyDiagram-XKPGCS4Q-B7mdzrEf.js} +1 -1
- package/dist/client/assets/{kanban-definition-3W4ZIXB7-Dbz9IJS9.js → kanban-definition-3W4ZIXB7-C3yAE4ky.js} +1 -1
- package/dist/client/assets/{layout-KhM6N15I.js → layout-BpyKMBbb.js} +1 -1
- package/dist/client/assets/{main-CqSslSJI.js → main-B169v3Mr.js} +245 -245
- package/dist/client/assets/main-DWmz5QXO.css +1 -0
- package/dist/client/assets/{mindmap-definition-VGOIOE7T-G-zFfV9G.js → mindmap-definition-VGOIOE7T-BV1ylQ_o.js} +1 -1
- package/dist/client/assets/{pieDiagram-ADFJNKIX-BhPxI2x7.js → pieDiagram-ADFJNKIX-C68Mdt_x.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-AYHSOK5B-B-GNhRJU.js → quadrantDiagram-AYHSOK5B-5wmdYa_y.js} +1 -1
- package/dist/client/assets/{requirementDiagram-UZGBJVZJ-DQJdQFIw.js → requirementDiagram-UZGBJVZJ-CXCtBgk5.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-TZEHDZUN-DrPdLIfN.js → sankeyDiagram-TZEHDZUN-CwuKYu6l.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-WL72ISMW-0iaye3zO.js → sequenceDiagram-WL72ISMW-BwFU-IAs.js} +1 -1
- package/dist/client/assets/{stateDiagram-FKZM4ZOC-B2-QB2W0.js → stateDiagram-FKZM4ZOC-B46B2gqH.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-4FDKWEC3-BvDRMNEQ.js +1 -0
- package/dist/client/assets/{timeline-definition-IT6M3QCI-M883VntJ.js → timeline-definition-IT6M3QCI-DHJrxfmL.js} +1 -1
- package/dist/client/assets/{treemap-GDKQZRPO-NQBhY7Vk.js → treemap-GDKQZRPO-Bn3yodwG.js} +1 -1
- package/dist/client/assets/{worktree._worktreeId-D-GKIuuU.js → worktree._worktreeId-7oupLNxr.js} +1 -1
- package/dist/client/assets/{worktree._worktreeId-D3DbNkyK.js → worktree._worktreeId-MU4bksud.js} +1 -1
- package/dist/client/assets/{xychartDiagram-PRI3JC2R-l_IPT77x.js → xychartDiagram-PRI3JC2R-DUqpx4dY.js} +1 -1
- package/dist/migrations/20261028200323_worktree_title/migration.sql +1 -0
- package/dist/migrations/20261028200323_worktree_title/snapshot.json +5228 -0
- package/dist/migrations/20261029080757_worktree_unread/migration.sql +1 -0
- package/dist/migrations/20261029080757_worktree_unread/snapshot.json +5238 -0
- package/dist/openapi.json +186 -1
- package/dist/start-server.mjs +355 -42
- package/package.json +11 -6
- package/dist/client/assets/channel-2mY-lkL6.js +0 -1
- package/dist/client/assets/classDiagram-2ON5EDUG-CxKjlsUh.js +0 -1
- package/dist/client/assets/classDiagram-v2-WZHVMYZB-CxKjlsUh.js +0 -1
- package/dist/client/assets/clone-CTVr6Med.js +0 -1
- package/dist/client/assets/main-BYKQWpTo.css +0 -1
- package/dist/client/assets/stateDiagram-v2-4FDKWEC3-pfnkktuh.js +0 -1
package/dist/start-server.mjs
CHANGED
|
@@ -2556,7 +2556,7 @@ var require_thread_stream = __commonJS({
|
|
|
2556
2556
|
var require_transport = __commonJS({
|
|
2557
2557
|
"../../node_modules/.pnpm/pino@9.14.0/node_modules/pino/lib/transport.js"(exports, module) {
|
|
2558
2558
|
"use strict";
|
|
2559
|
-
var { createRequire:
|
|
2559
|
+
var { createRequire: createRequire3 } = __require("module");
|
|
2560
2560
|
var getCallers = require_caller();
|
|
2561
2561
|
var { join: join59, isAbsolute: isAbsolute7, sep: sep11 } = __require("node:path");
|
|
2562
2562
|
var sleep3 = require_atomic_sleep();
|
|
@@ -2667,7 +2667,7 @@ var require_transport = __commonJS({
|
|
|
2667
2667
|
for (const filePath of callers) {
|
|
2668
2668
|
try {
|
|
2669
2669
|
const context = filePath === "node:repl" ? process.cwd() + sep11 : filePath;
|
|
2670
|
-
fixTarget2 =
|
|
2670
|
+
fixTarget2 = createRequire3(context).resolve(origin);
|
|
2671
2671
|
break;
|
|
2672
2672
|
} catch (err) {
|
|
2673
2673
|
continue;
|
|
@@ -51994,6 +51994,11 @@ var init_schema2 = __esm({
|
|
|
51994
51994
|
// id with no foreign key, like the origin columns: only `RepoQueries.setGroup` writes it, and
|
|
51995
51995
|
// deleting a group nulls it.
|
|
51996
51996
|
groupId: text3("group_id"),
|
|
51997
|
+
// Marked unread by a person: the sidebar shows a dot on every device until the worktree is
|
|
51998
|
+
// opened or marked read. Only `RepoQueries.setUnread` writes it.
|
|
51999
|
+
unread: integer4("unread", { mode: "boolean" }).notNull().default(false),
|
|
52000
|
+
// A custom title shown in place of the name, or null. Only `RepoQueries.setTitle` writes it.
|
|
52001
|
+
title: text3("title"),
|
|
51997
52002
|
// Set when the worktree was restored on another host: the id of the replacement. The copy
|
|
51998
52003
|
// stays on disk untouched and is listed as superseded until a person removes it.
|
|
51999
52004
|
supersededBy: text3("superseded_by"),
|
|
@@ -54078,8 +54083,12 @@ var init_mcp_stdio = __esm({
|
|
|
54078
54083
|
|
|
54079
54084
|
// ../../packages/host-local/src/process/cli-binary.ts
|
|
54080
54085
|
import { lstatSync as lstatSync3, realpathSync as realpathSync4 } from "node:fs";
|
|
54086
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
54081
54087
|
import { platform } from "node:os";
|
|
54082
|
-
import { delimiter as delimiter3, join as join19, resolve as resolve4 } from "node:path";
|
|
54088
|
+
import { delimiter as delimiter3, dirname as dirOf, join as join19, resolve as resolve4 } from "node:path";
|
|
54089
|
+
function cliPackageName(os = platform(), arch = process.arch) {
|
|
54090
|
+
return `@band-app/worker-cli-${os}-${arch}`;
|
|
54091
|
+
}
|
|
54083
54092
|
function findCliBinaryAt(opts) {
|
|
54084
54093
|
const { cwd, dirname: dirname18 } = opts;
|
|
54085
54094
|
const exe = platform() === "win32" ? "band.exe" : "band";
|
|
@@ -54131,6 +54140,15 @@ function findCliBinaryAt(opts) {
|
|
|
54131
54140
|
} catch {
|
|
54132
54141
|
}
|
|
54133
54142
|
}
|
|
54143
|
+
try {
|
|
54144
|
+
const pkgJson = createRequire2(join19(dirname18, "noop.js")).resolve(
|
|
54145
|
+
`${cliPackageName()}/package.json`
|
|
54146
|
+
);
|
|
54147
|
+
const p2 = join19(dirOf(pkgJson), "bin", exe);
|
|
54148
|
+
lstatSync3(p2);
|
|
54149
|
+
return p2;
|
|
54150
|
+
} catch {
|
|
54151
|
+
}
|
|
54134
54152
|
for (const dir of (process.env.PATH ?? "").split(delimiter3)) {
|
|
54135
54153
|
if (!dir) continue;
|
|
54136
54154
|
try {
|
|
@@ -61047,8 +61065,42 @@ import {
|
|
|
61047
61065
|
import { homedir as homedir16, hostname as hostname4, tmpdir as tmpdir2 } from "node:os";
|
|
61048
61066
|
import { join as join34 } from "node:path";
|
|
61049
61067
|
async function createWorktree(spec) {
|
|
61068
|
+
const refs = await execGit(
|
|
61069
|
+
["for-each-ref", "--format=%(refname)", "refs/heads", "refs/remotes/origin"],
|
|
61070
|
+
spec.repoPath
|
|
61071
|
+
);
|
|
61072
|
+
const local = [];
|
|
61073
|
+
const known = [];
|
|
61074
|
+
for (const ref of refs.split("\n").filter(Boolean)) {
|
|
61075
|
+
if (ref.startsWith("refs/heads/")) {
|
|
61076
|
+
local.push(ref.slice("refs/heads/".length));
|
|
61077
|
+
known.push(ref.slice("refs/heads/".length));
|
|
61078
|
+
} else if (ref.startsWith("refs/remotes/origin/")) {
|
|
61079
|
+
known.push(ref.slice("refs/remotes/origin/".length));
|
|
61080
|
+
}
|
|
61081
|
+
}
|
|
61082
|
+
const clash = known.find(
|
|
61083
|
+
(b2) => b2 !== spec.branch && b2.toLowerCase() === spec.branch.toLowerCase()
|
|
61084
|
+
);
|
|
61085
|
+
if (clash) {
|
|
61086
|
+
throw new Error(
|
|
61087
|
+
`Branch "${spec.branch}" differs only in case from the existing branch "${clash}". Use "${clash}" or pick another name.`
|
|
61088
|
+
);
|
|
61089
|
+
}
|
|
61050
61090
|
const args = ["worktree", "add", "-b", spec.branch, spec.path];
|
|
61051
|
-
if (spec.base)
|
|
61091
|
+
if (spec.base) {
|
|
61092
|
+
args.push(spec.base);
|
|
61093
|
+
} else if (!local.includes(spec.branch)) {
|
|
61094
|
+
const remote = `origin/${spec.branch}`;
|
|
61095
|
+
const found = await execGit(
|
|
61096
|
+
["rev-parse", "--verify", "--quiet", `refs/remotes/${remote}`],
|
|
61097
|
+
spec.repoPath
|
|
61098
|
+
).then(() => true).catch(() => false);
|
|
61099
|
+
if (found) {
|
|
61100
|
+
args.splice(2, 0, "--track");
|
|
61101
|
+
args.push(remote);
|
|
61102
|
+
}
|
|
61103
|
+
}
|
|
61052
61104
|
await execGit(args, spec.repoPath);
|
|
61053
61105
|
return { path: spec.path, branch: spec.branch };
|
|
61054
61106
|
}
|
|
@@ -61379,8 +61431,8 @@ var init_local_host = __esm({
|
|
|
61379
61431
|
return new Uint8Array(await readFile8(path2));
|
|
61380
61432
|
},
|
|
61381
61433
|
realpath: (path2) => realpath3(path2),
|
|
61382
|
-
async *readStream(path2) {
|
|
61383
|
-
for await (const chunk of createReadStream3(path2)) {
|
|
61434
|
+
async *readStream(path2, range) {
|
|
61435
|
+
for await (const chunk of createReadStream3(path2, range)) {
|
|
61384
61436
|
const buffer = chunk;
|
|
61385
61437
|
yield new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
61386
61438
|
}
|
|
@@ -61453,7 +61505,9 @@ function reconcileKindForRepo(repo) {
|
|
|
61453
61505
|
path: repo.path,
|
|
61454
61506
|
pinned: false,
|
|
61455
61507
|
...main2?.origin ? { origin: main2.origin } : {},
|
|
61456
|
-
...main2?.groupId ? { groupId: main2.groupId } : {}
|
|
61508
|
+
...main2?.groupId ? { groupId: main2.groupId } : {},
|
|
61509
|
+
...main2?.unread ? { unread: true } : {},
|
|
61510
|
+
...main2?.title ? { title: main2.title } : {}
|
|
61457
61511
|
}
|
|
61458
61512
|
];
|
|
61459
61513
|
}
|
|
@@ -61504,6 +61558,8 @@ var init_repos = __esm({
|
|
|
61504
61558
|
}
|
|
61505
61559
|
} : {},
|
|
61506
61560
|
...row.groupId ? { groupId: row.groupId } : {},
|
|
61561
|
+
...row.unread ? { unread: true } : {},
|
|
61562
|
+
...row.title ? { title: row.title } : {},
|
|
61507
61563
|
...row.supersededBy ? { supersededBy: row.supersededBy } : {},
|
|
61508
61564
|
...row.continuedFrom ? { continuedFrom: row.continuedFrom } : {},
|
|
61509
61565
|
...row.divergence ? { divergence: row.divergence } : {}
|
|
@@ -61557,7 +61613,9 @@ var init_repos = __esm({
|
|
|
61557
61613
|
originWorktreeId: worktrees.originWorktreeId,
|
|
61558
61614
|
originChatId: worktrees.originChatId,
|
|
61559
61615
|
originTerminalId: worktrees.originTerminalId,
|
|
61560
|
-
groupId: worktrees.groupId
|
|
61616
|
+
groupId: worktrees.groupId,
|
|
61617
|
+
unread: worktrees.unread,
|
|
61618
|
+
title: worktrees.title
|
|
61561
61619
|
}).from(worktrees).all().map((w2) => [`${w2.repoName}\0${w2.name}`, w2])
|
|
61562
61620
|
);
|
|
61563
61621
|
const continuation = new Map(
|
|
@@ -61599,6 +61657,8 @@ var init_repos = __esm({
|
|
|
61599
61657
|
originChatId: origin?.originChatId ?? null,
|
|
61600
61658
|
originTerminalId: origin?.originTerminalId ?? null,
|
|
61601
61659
|
groupId: origin?.groupId ?? null,
|
|
61660
|
+
unread: origin?.unread ?? false,
|
|
61661
|
+
title: origin?.title ?? null,
|
|
61602
61662
|
repoName: repo.name,
|
|
61603
61663
|
name: wt.name,
|
|
61604
61664
|
branch: wt.branch,
|
|
@@ -61807,6 +61867,16 @@ var init_repos = __esm({
|
|
|
61807
61867
|
}).from(repos).where(eq2(repos.name, name5)).get();
|
|
61808
61868
|
return row ? { allowedHostIds: row.allowedHostIds ?? null, primaryHostId: row.primaryHostId ?? null } : void 0;
|
|
61809
61869
|
}
|
|
61870
|
+
/** Sets a worktree's custom title, or clears it with `null`. Focused UPDATE, like `setGroup`. */
|
|
61871
|
+
setTitle(repoName, name5, title, hostId2) {
|
|
61872
|
+
getDb().update(worktrees).set({ title }).where(
|
|
61873
|
+
and2(
|
|
61874
|
+
eq2(worktrees.repoName, repoName),
|
|
61875
|
+
eq2(worktrees.name, name5),
|
|
61876
|
+
...hostId2 === void 0 ? [] : [eq2(worktrees.hostId, hostId2)]
|
|
61877
|
+
)
|
|
61878
|
+
).run();
|
|
61879
|
+
}
|
|
61810
61880
|
/**
|
|
61811
61881
|
* Puts a worktree in a group, or in none with `null`. Focused UPDATE, because `saveAll` keeps
|
|
61812
61882
|
* the stored group and never writes the one in its snapshot.
|
|
@@ -61820,6 +61890,19 @@ var init_repos = __esm({
|
|
|
61820
61890
|
)
|
|
61821
61891
|
).run();
|
|
61822
61892
|
}
|
|
61893
|
+
/**
|
|
61894
|
+
* Marks a worktree unread or read. Focused UPDATE, because `saveAll` keeps the stored flag and
|
|
61895
|
+
* never writes the one in its snapshot.
|
|
61896
|
+
*/
|
|
61897
|
+
setUnread(repoName, name5, unread, hostId2) {
|
|
61898
|
+
getDb().update(worktrees).set({ unread }).where(
|
|
61899
|
+
and2(
|
|
61900
|
+
eq2(worktrees.repoName, repoName),
|
|
61901
|
+
eq2(worktrees.name, name5),
|
|
61902
|
+
...hostId2 === void 0 ? [] : [eq2(worktrees.hostId, hostId2)]
|
|
61903
|
+
)
|
|
61904
|
+
).run();
|
|
61905
|
+
}
|
|
61823
61906
|
/**
|
|
61824
61907
|
* Records where a worktree was started from, or clears it with `null`. Focused UPDATE, because
|
|
61825
61908
|
* `saveAll` keeps the stored origin and never writes the one in its snapshot.
|
|
@@ -66901,13 +66984,28 @@ var init_git_refusals = __esm({
|
|
|
66901
66984
|
|
|
66902
66985
|
// ../../packages/shared/src/branch-name.ts
|
|
66903
66986
|
function slugifyBranchName(input2) {
|
|
66904
|
-
|
|
66987
|
+
let name5 = input2.trim().replace(/\s+/g, "-").replace(/[\x00-\x1f\x7f~^:?*[\\]/g, "").replace(/@\{/g, "").replace(/^@$/, "");
|
|
66988
|
+
let previous;
|
|
66989
|
+
do {
|
|
66990
|
+
previous = name5;
|
|
66991
|
+
name5 = name5.replace(/\.{2,}/g, ".").replace(/@\{/g, "").replace(/-{2,}/g, "-").replace(/\/+/g, "/").replace(/\/\./g, "/").replace(/\.lock(?=\/|$)/g, "").replace(/\.+(?=\/|$)/g, "").replace(/^[-/.]+|[-/.]+$/g, "");
|
|
66992
|
+
} while (name5 !== previous);
|
|
66993
|
+
return name5;
|
|
66905
66994
|
}
|
|
66906
66995
|
var init_branch_name = __esm({
|
|
66907
66996
|
"../../packages/shared/src/branch-name.ts"() {
|
|
66908
66997
|
}
|
|
66909
66998
|
});
|
|
66910
66999
|
|
|
67000
|
+
// ../../packages/shared/src/worktree-display.ts
|
|
67001
|
+
var WORKTREE_TITLE_MAX;
|
|
67002
|
+
var init_worktree_display = __esm({
|
|
67003
|
+
"../../packages/shared/src/worktree-display.ts"() {
|
|
67004
|
+
init_worktree_id();
|
|
67005
|
+
WORKTREE_TITLE_MAX = 100;
|
|
67006
|
+
}
|
|
67007
|
+
});
|
|
67008
|
+
|
|
66911
67009
|
// src/server/infra/db/queries/pending-removals.ts
|
|
66912
67010
|
var PendingRemovalQueries;
|
|
66913
67011
|
var init_pending_removals = __esm({
|
|
@@ -68298,6 +68396,11 @@ var init_mime_types = __esm({
|
|
|
68298
68396
|
".ico": "image/x-icon",
|
|
68299
68397
|
".bmp": "image/bmp",
|
|
68300
68398
|
".avif": "image/avif",
|
|
68399
|
+
".mp4": "video/mp4",
|
|
68400
|
+
".m4v": "video/mp4",
|
|
68401
|
+
".mov": "video/quicktime",
|
|
68402
|
+
".webm": "video/webm",
|
|
68403
|
+
".ogv": "video/ogg",
|
|
68301
68404
|
".pdf": "application/pdf",
|
|
68302
68405
|
".json": "application/json",
|
|
68303
68406
|
".txt": "text/plain",
|
|
@@ -68735,7 +68838,7 @@ var init_channel = __esm({
|
|
|
68735
68838
|
});
|
|
68736
68839
|
|
|
68737
68840
|
// ../../packages/link/src/chats.ts
|
|
68738
|
-
var CAPABILITY_CHAT_OWNERSHIP, CHAT_ID_BLOCK_SIZE, METHOD_CHAT_OWN, METHOD_CHAT_FORWARD, METHOD_CHAT_RELEASE, METHOD_CHAT_SET_CONFIG, METHOD_CHAT_LIST_SESSIONS, METHOD_CHAT_REPLICATE, METHOD_CHAT_ID_BLOCK, METHOD_CHAT_HISTORY, METHOD_CHAT_LAUNCH, METHOD_CHAT_SESSION_STATE, NOTIFY_CHAT_LIVE, NOTIFY_CHAT_TRANSIENT, NOTIFY_CHAT_ACTIVITY;
|
|
68841
|
+
var CAPABILITY_CHAT_OWNERSHIP, CHAT_ID_BLOCK_SIZE, METHOD_CHAT_OWN, METHOD_CHAT_FORWARD, METHOD_CHAT_RELEASE, METHOD_CHAT_SET_CONFIG, METHOD_CHAT_LIST_SESSIONS, METHOD_CHAT_VIEW_SESSION, METHOD_CHAT_REPLICATE, METHOD_CHAT_ID_BLOCK, METHOD_CHAT_HISTORY, METHOD_CHAT_LAUNCH, METHOD_CHAT_SESSION_STATE, NOTIFY_CHAT_LIVE, NOTIFY_CHAT_TRANSIENT, NOTIFY_CHAT_ACTIVITY;
|
|
68739
68842
|
var init_chats2 = __esm({
|
|
68740
68843
|
"../../packages/link/src/chats.ts"() {
|
|
68741
68844
|
"use strict";
|
|
@@ -68746,6 +68849,7 @@ var init_chats2 = __esm({
|
|
|
68746
68849
|
METHOD_CHAT_RELEASE = "chat.release";
|
|
68747
68850
|
METHOD_CHAT_SET_CONFIG = "chat.setConfigOption";
|
|
68748
68851
|
METHOD_CHAT_LIST_SESSIONS = "chat.listSessions";
|
|
68852
|
+
METHOD_CHAT_VIEW_SESSION = "chat.viewSession";
|
|
68749
68853
|
METHOD_CHAT_REPLICATE = "chat.replicate";
|
|
68750
68854
|
METHOD_CHAT_ID_BLOCK = "chat.idBlock";
|
|
68751
68855
|
METHOD_CHAT_HISTORY = "chat.history";
|
|
@@ -69688,7 +69792,7 @@ function forwardedTask(id, options) {
|
|
|
69688
69792
|
codingAgentId: options.codingAgentId
|
|
69689
69793
|
};
|
|
69690
69794
|
}
|
|
69691
|
-
var log23, FORWARD_TIMEOUT_MS, RETRY_MS, HISTORY_MAX_ROWS, ChatOwnershipService, chatOwnershipService;
|
|
69795
|
+
var log23, FORWARD_TIMEOUT_MS, VIEW_SESSION_TIMEOUT_MS, RETRY_MS, HISTORY_MAX_ROWS, ChatOwnershipService, chatOwnershipService;
|
|
69692
69796
|
var init_chat_ownership_service = __esm({
|
|
69693
69797
|
"src/server/services/chat-ownership-service.ts"() {
|
|
69694
69798
|
"use strict";
|
|
@@ -69709,6 +69813,7 @@ var init_chat_ownership_service = __esm({
|
|
|
69709
69813
|
init_worktree_service();
|
|
69710
69814
|
log23 = createLogger("chat-ownership");
|
|
69711
69815
|
FORWARD_TIMEOUT_MS = 6e4;
|
|
69816
|
+
VIEW_SESSION_TIMEOUT_MS = 2e5;
|
|
69712
69817
|
RETRY_MS = 5e3;
|
|
69713
69818
|
HISTORY_MAX_ROWS = 1e3;
|
|
69714
69819
|
ChatOwnershipService = class {
|
|
@@ -70139,6 +70244,35 @@ var init_chat_ownership_service = __esm({
|
|
|
70139
70244
|
);
|
|
70140
70245
|
return readPayload(session, enc);
|
|
70141
70246
|
}
|
|
70247
|
+
/**
|
|
70248
|
+
* The user picked another session for the chat: the owner learns of it from the chat's info,
|
|
70249
|
+
* sent again. A chat with a turn running keeps its session, and the worker's own record wins.
|
|
70250
|
+
*/
|
|
70251
|
+
syncActiveSession(chatId) {
|
|
70252
|
+
const hostId2 = this.ownerOf(chatId);
|
|
70253
|
+
if (!hostId2 || taskService.getTask(chatId)?.status === "running") return;
|
|
70254
|
+
this.enqueue(chatId, hostId2, METHOD_CHAT_OWN, { chatId });
|
|
70255
|
+
}
|
|
70256
|
+
/**
|
|
70257
|
+
* Has the owner load a session Band has no log for, and waits until the replayed rows are
|
|
70258
|
+
* here. An offline owner, or an old worker without the method, does nothing, so the replay
|
|
70259
|
+
* arrives with the first prompt as before.
|
|
70260
|
+
*/
|
|
70261
|
+
async viewSession(chatId) {
|
|
70262
|
+
const sessionId = chatService.get(chatId)?.activeSessionId;
|
|
70263
|
+
const hostId2 = this.ownerOf(chatId);
|
|
70264
|
+
const session = hostId2 ? this.sessions.get(hostId2) : void 0;
|
|
70265
|
+
if (!session?.attached) return;
|
|
70266
|
+
try {
|
|
70267
|
+
await session.request(
|
|
70268
|
+
METHOD_CHAT_VIEW_SESSION,
|
|
70269
|
+
{ chatId, sessionId },
|
|
70270
|
+
{ timeoutMs: VIEW_SESSION_TIMEOUT_MS }
|
|
70271
|
+
);
|
|
70272
|
+
} catch (err) {
|
|
70273
|
+
log23.warn({ chatId, err }, "the owner could not load the session history");
|
|
70274
|
+
}
|
|
70275
|
+
}
|
|
70142
70276
|
/** The agent's own session list on the owner, or null when it can't list or is offline. */
|
|
70143
70277
|
async listSessions(chatId) {
|
|
70144
70278
|
const hostId2 = this.ownerOf(chatId);
|
|
@@ -71308,7 +71442,7 @@ var init_remote_host = __esm({
|
|
|
71308
71442
|
stat: (path2, options) => this.rpc.call("fs.stat", { path: path2, followSymlinks: options?.followSymlinks }),
|
|
71309
71443
|
realpath: (path2) => this.rpc.call("fs.realpath", { path: path2 }),
|
|
71310
71444
|
readFile: (path2) => this.rpc.callBytes("fs.readFile", { path: path2 }),
|
|
71311
|
-
readStream: (path2) => this.streamOf("fs.readStream", { path: path2 }),
|
|
71445
|
+
readStream: (path2, range) => this.streamOf("fs.readStream", { path: path2, start: range?.start, end: range?.end }),
|
|
71312
71446
|
writeFile: (path2, data, options) => this.writeFile(path2, data, options),
|
|
71313
71447
|
glob: (pattern, cwd) => this.rpc.call("fs.glob", { pattern, cwd }),
|
|
71314
71448
|
mkdtemp: (prefix) => this.rpc.call("fs.mkdtemp", { prefix }),
|
|
@@ -77460,6 +77594,8 @@ __export(worktree_service_exports, {
|
|
|
77460
77594
|
worktreeSetGroupInput: () => worktreeSetGroupInput,
|
|
77461
77595
|
worktreeSetOriginInput: () => worktreeSetOriginInput,
|
|
77462
77596
|
worktreeSetPinnedInput: () => worktreeSetPinnedInput,
|
|
77597
|
+
worktreeSetTitleInput: () => worktreeSetTitleInput,
|
|
77598
|
+
worktreeSetUnreadInput: () => worktreeSetUnreadInput,
|
|
77463
77599
|
worktreeVia: () => worktreeVia
|
|
77464
77600
|
});
|
|
77465
77601
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
@@ -77505,7 +77641,7 @@ function expandHome(given, home, hostId2) {
|
|
|
77505
77641
|
}
|
|
77506
77642
|
return path2;
|
|
77507
77643
|
}
|
|
77508
|
-
var TEARDOWN_TIMEOUT_MS, log39, worktreeVia, worktreeCreateInput, worktreeRemoveInput, worktreeSetPinnedInput, worktreeSetOriginInput, worktreeRefInput, worktreeSetGroupInput, worktreeReorderInput, worktreeGitInput, worktreeRunScriptInput, RepoNotFoundError, InvalidOriginError, PlainRepoError, WorktreeService, worktreeService;
|
|
77644
|
+
var TEARDOWN_TIMEOUT_MS, log39, worktreeVia, worktreeCreateInput, worktreeRemoveInput, worktreeSetPinnedInput, worktreeSetOriginInput, worktreeRefInput, worktreeSetGroupInput, worktreeSetUnreadInput, worktreeSetTitleInput, worktreeReorderInput, worktreeGitInput, worktreeRunScriptInput, RepoNotFoundError, InvalidOriginError, PlainRepoError, WorktreeService, worktreeService;
|
|
77509
77645
|
var init_worktree_service = __esm({
|
|
77510
77646
|
"src/server/services/worktree-service.ts"() {
|
|
77511
77647
|
"use strict";
|
|
@@ -77516,6 +77652,7 @@ var init_worktree_service = __esm({
|
|
|
77516
77652
|
init_path();
|
|
77517
77653
|
init_src();
|
|
77518
77654
|
init_branch_name();
|
|
77655
|
+
init_worktree_display();
|
|
77519
77656
|
init_worktree_id();
|
|
77520
77657
|
init_zod();
|
|
77521
77658
|
init_errors6();
|
|
@@ -77633,6 +77770,17 @@ var init_worktree_service = __esm({
|
|
|
77633
77770
|
// Also moves every worktree started from this one, on any repo and host, and theirs in turn.
|
|
77634
77771
|
withChildren: external_exports2.boolean().optional()
|
|
77635
77772
|
});
|
|
77773
|
+
worktreeSetUnreadInput = external_exports2.object({
|
|
77774
|
+
worktreeId: external_exports2.string().min(1),
|
|
77775
|
+
unread: external_exports2.boolean(),
|
|
77776
|
+
// Tells two same-named worktrees on different workers apart.
|
|
77777
|
+
hostId: external_exports2.string().optional()
|
|
77778
|
+
});
|
|
77779
|
+
worktreeSetTitleInput = external_exports2.object({
|
|
77780
|
+
worktreeId: external_exports2.string().min(1),
|
|
77781
|
+
// Blank or null clears the title, so the card shows the name again.
|
|
77782
|
+
title: external_exports2.string().max(WORKTREE_TITLE_MAX).nullable()
|
|
77783
|
+
});
|
|
77636
77784
|
worktreeReorderInput = external_exports2.object({
|
|
77637
77785
|
// The worktrees of one list in their new order.
|
|
77638
77786
|
order: external_exports2.array(worktreeRefInput).min(1).max(500),
|
|
@@ -77873,6 +78021,14 @@ var init_worktree_service = __esm({
|
|
|
77873
78021
|
(wt) => sameName(wt) && (wt.hostId ?? hostRegistry.local.id) === hostId2
|
|
77874
78022
|
);
|
|
77875
78023
|
if (existing) return { ok: true, path: existing.path, hostId: hostId2 };
|
|
78024
|
+
const caseClash = repo.worktrees.find(
|
|
78025
|
+
(wt) => (wt.hostId ?? hostRegistry.local.id) === hostId2 && [wt.name, wt.branch].some((n) => n.toLowerCase() === input2.branch.toLowerCase())
|
|
78026
|
+
);
|
|
78027
|
+
if (caseClash) {
|
|
78028
|
+
throw new Error(
|
|
78029
|
+
`Branch "${input2.branch}" differs only in case from the existing worktree "${caseClash.branch}". Use that name or pick another.`
|
|
78030
|
+
);
|
|
78031
|
+
}
|
|
77876
78032
|
assertHostAllowed(repo.name, hostId2);
|
|
77877
78033
|
const elsewhere = listedWorktrees(repo).find(
|
|
77878
78034
|
(wt) => sameName(wt) && !wt.supersededBy && (wt.hostId ?? hostRegistry.local.id) !== hostId2
|
|
@@ -78396,6 +78552,20 @@ var init_worktree_service = __esm({
|
|
|
78396
78552
|
emit({ kind: "worktree-origin-changed", worktreeId: worktreeId4 });
|
|
78397
78553
|
return { ok: true };
|
|
78398
78554
|
}
|
|
78555
|
+
/** Sets the title shown for a worktree, or clears it (blank or null). */
|
|
78556
|
+
setTitle(input2) {
|
|
78557
|
+
const title = input2.title?.trim() || null;
|
|
78558
|
+
const state2 = loadState();
|
|
78559
|
+
for (const repo of state2.repos) {
|
|
78560
|
+
for (const wt of repo.worktrees) {
|
|
78561
|
+
if (toWorktreeId(repo.name, wt.name, wt.hostId) !== input2.worktreeId) continue;
|
|
78562
|
+
this.repoQueries.setTitle(repo.name, wt.name, title, wt.hostId ?? "local");
|
|
78563
|
+
emit({ kind: "worktree-origin-changed", worktreeId: input2.worktreeId });
|
|
78564
|
+
return { ok: true, title };
|
|
78565
|
+
}
|
|
78566
|
+
}
|
|
78567
|
+
throw new WorktreeNotFoundError(input2.worktreeId);
|
|
78568
|
+
}
|
|
78399
78569
|
/**
|
|
78400
78570
|
* Puts a worktree in a group, or in none with `null`. With `withChildren` every worktree
|
|
78401
78571
|
* started from it, and theirs in turn, moves too.
|
|
@@ -78430,6 +78600,42 @@ var init_worktree_service = __esm({
|
|
|
78430
78600
|
emit({ kind: "worktree-origin-changed", worktreeId: input2.worktreeId });
|
|
78431
78601
|
return { ok: true, moved };
|
|
78432
78602
|
}
|
|
78603
|
+
/**
|
|
78604
|
+
* Marks a worktree unread or read. Reading also clears a `done` or `failed` attention dot, the
|
|
78605
|
+
* way `statuses.clearNeedsAttention` does.
|
|
78606
|
+
*/
|
|
78607
|
+
setUnread(input2) {
|
|
78608
|
+
const row = this.findRowById(input2.worktreeId, input2.hostId);
|
|
78609
|
+
if (!row) throw new WorktreeNotFoundError(input2.worktreeId);
|
|
78610
|
+
this.repoQueries.setUnread(row.repoName, row.name, input2.unread, row.hostId);
|
|
78611
|
+
if (!input2.unread) {
|
|
78612
|
+
const status = acknowledgeWorktreeAttention(input2.worktreeId);
|
|
78613
|
+
if (status) emit({ kind: "update", status });
|
|
78614
|
+
}
|
|
78615
|
+
emit({ kind: "worktree-origin-changed", worktreeId: input2.worktreeId });
|
|
78616
|
+
return { ok: true };
|
|
78617
|
+
}
|
|
78618
|
+
/**
|
|
78619
|
+
* Clears the unread flag when the user opens the worktree. Does nothing, and emits nothing,
|
|
78620
|
+
* for a worktree that is not marked unread or is gone.
|
|
78621
|
+
*/
|
|
78622
|
+
clearUnreadOnOpen(worktreeId4) {
|
|
78623
|
+
const row = this.findRowById(worktreeId4);
|
|
78624
|
+
if (!row?.unread) return;
|
|
78625
|
+
this.repoQueries.setUnread(row.repoName, row.name, false, row.hostId);
|
|
78626
|
+
emit({ kind: "worktree-origin-changed", worktreeId: worktreeId4 });
|
|
78627
|
+
}
|
|
78628
|
+
findRowById(worktreeId4, hostId2) {
|
|
78629
|
+
for (const repo of loadState().repos) {
|
|
78630
|
+
for (const wt of repo.worktrees) {
|
|
78631
|
+
if (toWorktreeId(repo.name, wt.name, wt.hostId) !== worktreeId4) continue;
|
|
78632
|
+
const rowHost = wt.hostId ?? "local";
|
|
78633
|
+
if (hostId2 !== void 0 && hostId2 !== rowHost) continue;
|
|
78634
|
+
return { repoName: repo.name, name: wt.name, hostId: rowHost, unread: wt.unread === true };
|
|
78635
|
+
}
|
|
78636
|
+
}
|
|
78637
|
+
return void 0;
|
|
78638
|
+
}
|
|
78433
78639
|
/**
|
|
78434
78640
|
* Stores the order of one list of worktrees, and optionally the new parent of one of them. The
|
|
78435
78641
|
* parent change goes first, so a refused parent leaves the order untouched.
|
|
@@ -83449,7 +83655,10 @@ var init_agent_session_service = __esm({
|
|
|
83449
83655
|
async ensureSession(chatId, purpose, options = {}) {
|
|
83450
83656
|
const chat = chatService.get(chatId);
|
|
83451
83657
|
if (!chat) throw new ChatNotFoundError(chatId);
|
|
83452
|
-
if (chat.ownerHostId)
|
|
83658
|
+
if (chat.ownerHostId) {
|
|
83659
|
+
if (purpose === "view") await chatOwnershipService.viewSession(chatId);
|
|
83660
|
+
return null;
|
|
83661
|
+
}
|
|
83453
83662
|
if (purpose === "prompt") await ephemeralLifecycleService.ensureAwake(chat.worktreeId);
|
|
83454
83663
|
const worktree = worktreeService.resolve(chat.worktreeId);
|
|
83455
83664
|
if (!worktree) throw new Error(`Worktree not found: ${chat.worktreeId}`);
|
|
@@ -94704,6 +94913,8 @@ var RepoService = class {
|
|
|
94704
94913
|
head: wt.head,
|
|
94705
94914
|
pinned: tracked2?.pinned ?? false,
|
|
94706
94915
|
...tracked2?.groupId ? { groupId: tracked2.groupId } : {},
|
|
94916
|
+
...tracked2?.unread ? { unread: true } : {},
|
|
94917
|
+
...tracked2?.title ? { title: tracked2.title } : {},
|
|
94707
94918
|
...tracked2?.sortIndex === void 0 ? {} : { sortIndex: tracked2.sortIndex },
|
|
94708
94919
|
...tracked2?.pinSortIndex === void 0 ? {} : { pinSortIndex: tracked2.pinSortIndex }
|
|
94709
94920
|
};
|
|
@@ -94745,6 +94956,8 @@ var RepoService = class {
|
|
|
94745
94956
|
lastCheckpointAt: lastCheckpoints.get(worktreeId4) ?? null,
|
|
94746
94957
|
agent: status?.agent ?? null,
|
|
94747
94958
|
groupId: wt.groupId ?? null,
|
|
94959
|
+
unread: wt.unread ?? false,
|
|
94960
|
+
title: wt.title ?? null,
|
|
94748
94961
|
origin: links.origins.get(worktreeId4) ?? null,
|
|
94749
94962
|
children: links.children.get(worktreeId4) ?? []
|
|
94750
94963
|
};
|
|
@@ -96220,6 +96433,10 @@ ${diff}`).digest("hex");
|
|
|
96220
96433
|
if (worktree.groupId) {
|
|
96221
96434
|
this.repos.setGroup(repo.name, worktree.name, worktree.groupId, toHostId);
|
|
96222
96435
|
}
|
|
96436
|
+
if (worktree.unread) this.repos.setUnread(repo.name, worktree.name, true, toHostId);
|
|
96437
|
+
if (worktree.title) {
|
|
96438
|
+
this.repos.setTitle(repo.name, worktree.name, worktree.title, toHostId);
|
|
96439
|
+
}
|
|
96223
96440
|
this.repos.setSupersededBy(repo.name, worktree.name, fromHostId, newId);
|
|
96224
96441
|
await Promise.all([
|
|
96225
96442
|
workerLeaseService.pushBlockedPaths(fromHostId),
|
|
@@ -109987,6 +110204,7 @@ init_zod();
|
|
|
109987
110204
|
init_errors6();
|
|
109988
110205
|
init_agent_login_service();
|
|
109989
110206
|
init_agent_session_service();
|
|
110207
|
+
init_chat_ownership_service();
|
|
109990
110208
|
init_chat_service();
|
|
109991
110209
|
init_settings_service();
|
|
109992
110210
|
init_task_service();
|
|
@@ -110158,6 +110376,7 @@ var chatsRouter = t.router({
|
|
|
110158
110376
|
summary,
|
|
110159
110377
|
lastModified
|
|
110160
110378
|
});
|
|
110379
|
+
chatOwnershipService.syncActiveSession(input2.chatId);
|
|
110161
110380
|
return { ok: true };
|
|
110162
110381
|
}),
|
|
110163
110382
|
send: publicProcedure.input(
|
|
@@ -110319,6 +110538,7 @@ init_zod();
|
|
|
110319
110538
|
|
|
110320
110539
|
// src/server/services/cli-service.ts
|
|
110321
110540
|
init_cli_binary();
|
|
110541
|
+
init_local_host_enabled();
|
|
110322
110542
|
import { execFile as execFile15 } from "node:child_process";
|
|
110323
110543
|
import {
|
|
110324
110544
|
accessSync as accessSync3,
|
|
@@ -110339,10 +110559,14 @@ function windowsBinDir() {
|
|
|
110339
110559
|
return join49(localAppData, "band", "bin");
|
|
110340
110560
|
}
|
|
110341
110561
|
var SYMLINK_PATH = platform2() === "win32" ? join49(windowsBinDir(), "band.cmd") : "/usr/local/bin/band";
|
|
110562
|
+
function symlinkPath() {
|
|
110563
|
+
if (platform2() === "win32") return SYMLINK_PATH;
|
|
110564
|
+
return process.env.BAND_CLI_SYMLINK_PATH?.trim() || SYMLINK_PATH;
|
|
110565
|
+
}
|
|
110342
110566
|
function checkCliWindows() {
|
|
110343
110567
|
let contents;
|
|
110344
110568
|
try {
|
|
110345
|
-
contents = readFileSync11(
|
|
110569
|
+
contents = readFileSync11(symlinkPath(), "utf-8");
|
|
110346
110570
|
} catch (err) {
|
|
110347
110571
|
const code = err.code;
|
|
110348
110572
|
if (code === "EACCES") return "NotWritable";
|
|
@@ -110354,18 +110578,22 @@ function checkCliWindows() {
|
|
|
110354
110578
|
if (basename3(target).toLowerCase() !== "band.exe") return "ConflictingBinary";
|
|
110355
110579
|
return existsSync16(target) ? "Installed" : "NotInstalled";
|
|
110356
110580
|
}
|
|
110581
|
+
function isCliCheckApplicable(admin) {
|
|
110582
|
+
return isLocalHostEnabled() && admin;
|
|
110583
|
+
}
|
|
110357
110584
|
async function checkCli() {
|
|
110358
110585
|
if (platform2() === "win32") {
|
|
110359
110586
|
return checkCliWindows();
|
|
110360
110587
|
}
|
|
110588
|
+
const linkPath = symlinkPath();
|
|
110361
110589
|
try {
|
|
110362
|
-
const stat7 = lstatSync4(
|
|
110590
|
+
const stat7 = lstatSync4(linkPath);
|
|
110363
110591
|
if (!stat7.isSymbolicLink()) {
|
|
110364
110592
|
return "ConflictingBinary";
|
|
110365
110593
|
}
|
|
110366
110594
|
let target;
|
|
110367
110595
|
try {
|
|
110368
|
-
target = realpathSync5(
|
|
110596
|
+
target = realpathSync5(linkPath);
|
|
110369
110597
|
} catch {
|
|
110370
110598
|
return "NotInstalled";
|
|
110371
110599
|
}
|
|
@@ -110374,6 +110602,13 @@ async function checkCli() {
|
|
|
110374
110602
|
if (isCargoBuild || isElectronSidecar) {
|
|
110375
110603
|
return "Installed";
|
|
110376
110604
|
}
|
|
110605
|
+
const resolved = findCliBinary();
|
|
110606
|
+
if (resolved) {
|
|
110607
|
+
try {
|
|
110608
|
+
if (realpathSync5(resolved) === target) return "Installed";
|
|
110609
|
+
} catch {
|
|
110610
|
+
}
|
|
110611
|
+
}
|
|
110377
110612
|
const isStaleAppLink = target.toLowerCase().includes("/band.app/contents/macos/");
|
|
110378
110613
|
if (isStaleAppLink) {
|
|
110379
110614
|
return "NotInstalled";
|
|
@@ -110383,7 +110618,7 @@ async function checkCli() {
|
|
|
110383
110618
|
const code = err.code;
|
|
110384
110619
|
if (code === "ENOENT") {
|
|
110385
110620
|
try {
|
|
110386
|
-
lstatSync4(
|
|
110621
|
+
lstatSync4(dirname13(linkPath));
|
|
110387
110622
|
return "NotInstalled";
|
|
110388
110623
|
} catch {
|
|
110389
110624
|
return "DirNotFound";
|
|
@@ -110408,7 +110643,7 @@ function noBinaryError(env = process.env) {
|
|
|
110408
110643
|
return new Error("Bundled CLI binary missing - try reinstalling Band");
|
|
110409
110644
|
}
|
|
110410
110645
|
return new Error(
|
|
110411
|
-
|
|
110646
|
+
`Could not find band CLI binary. Install ${cliPackageName()} or set BAND_CLI_PATH. In a source checkout, build it with: cargo build --release -p band-cli`
|
|
110412
110647
|
);
|
|
110413
110648
|
}
|
|
110414
110649
|
function runReg(args) {
|
|
@@ -110460,39 +110695,43 @@ async function installCli(_opts = {}) {
|
|
|
110460
110695
|
await installCliWindows(binaryPath);
|
|
110461
110696
|
return;
|
|
110462
110697
|
}
|
|
110463
|
-
const
|
|
110698
|
+
const linkPath = symlinkPath();
|
|
110699
|
+
const dir = dirname13(linkPath);
|
|
110464
110700
|
if (isDirWritable(dir)) {
|
|
110465
110701
|
try {
|
|
110466
|
-
lstatSync4(
|
|
110467
|
-
unlinkSync6(
|
|
110702
|
+
lstatSync4(linkPath);
|
|
110703
|
+
unlinkSync6(linkPath);
|
|
110468
110704
|
} catch (err) {
|
|
110469
110705
|
const code = err.code;
|
|
110470
110706
|
if (code !== "ENOENT") throw err;
|
|
110471
110707
|
}
|
|
110472
|
-
symlinkSync(binaryPath,
|
|
110708
|
+
symlinkSync(binaryPath, linkPath);
|
|
110473
110709
|
return;
|
|
110474
110710
|
}
|
|
110475
110711
|
if (platform2() === "darwin") {
|
|
110476
110712
|
throw new Error("elevation-required");
|
|
110477
110713
|
}
|
|
110478
|
-
throw new Error(`
|
|
110714
|
+
throw new Error(`On the hub's server, run: sudo ln -sf "${binaryPath}" "${linkPath}"`);
|
|
110479
110715
|
}
|
|
110480
110716
|
function resolveCliPaths() {
|
|
110481
110717
|
const binaryPath = findCliBinary();
|
|
110482
110718
|
if (!binaryPath) return null;
|
|
110483
|
-
return { binaryPath, symlinkPath:
|
|
110719
|
+
return { binaryPath, symlinkPath: symlinkPath() };
|
|
110484
110720
|
}
|
|
110485
110721
|
|
|
110486
110722
|
// src/server/api/cli/router.ts
|
|
110487
110723
|
var cliRouter = t.router({
|
|
110488
|
-
|
|
110489
|
-
|
|
110490
|
-
|
|
110724
|
+
// `applicable` is false when nobody can act on the answer: the hub runs no worktrees of its
|
|
110725
|
+
// own (workers carry their own CLI), or the caller is not an admin.
|
|
110726
|
+
check: publicProcedure.query(async ({ ctx }) => {
|
|
110727
|
+
if (!isCliCheckApplicable(ctx.admin))
|
|
110728
|
+
return { status: "Installed", applicable: false };
|
|
110729
|
+
return { status: await checkCli(), applicable: true };
|
|
110491
110730
|
}),
|
|
110492
|
-
resolve:
|
|
110731
|
+
resolve: adminProcedure.query(() => {
|
|
110493
110732
|
return resolveCliPaths();
|
|
110494
110733
|
}),
|
|
110495
|
-
install:
|
|
110734
|
+
install: adminProcedure.input(external_exports2.object({ allowPrompt: external_exports2.boolean().optional() }).optional()).mutation(async ({ input: input2 }) => {
|
|
110496
110735
|
try {
|
|
110497
110736
|
await installCli({ allowPrompt: input2?.allowPrompt });
|
|
110498
110737
|
return { ok: true };
|
|
@@ -111798,7 +112037,7 @@ init_registry();
|
|
|
111798
112037
|
// package.json
|
|
111799
112038
|
var package_default = {
|
|
111800
112039
|
name: "@band-app/server",
|
|
111801
|
-
version: "0.54.
|
|
112040
|
+
version: "0.54.4",
|
|
111802
112041
|
type: "module",
|
|
111803
112042
|
types: "src/index.ts",
|
|
111804
112043
|
exports: {
|
|
@@ -112133,6 +112372,7 @@ var RELAY_PROCEDURES = /* @__PURE__ */ new Set([
|
|
|
112133
112372
|
"repos.remove",
|
|
112134
112373
|
"groups.list",
|
|
112135
112374
|
"worktrees.setGroup",
|
|
112375
|
+
"worktrees.setTitle",
|
|
112136
112376
|
"hosts.list",
|
|
112137
112377
|
"hosts.forAgents",
|
|
112138
112378
|
"workers.list",
|
|
@@ -118519,6 +118759,7 @@ init_zod();
|
|
|
118519
118759
|
init_branch_status_poller();
|
|
118520
118760
|
init_state();
|
|
118521
118761
|
init_watcher_service();
|
|
118762
|
+
init_worktree_service();
|
|
118522
118763
|
var statusesRouter = t.router({
|
|
118523
118764
|
get: publicProcedure.input(external_exports2.object({ worktreeId: external_exports2.string() })).query(({ input: input2 }) => {
|
|
118524
118765
|
return getWorktreeStatus(input2.worktreeId);
|
|
@@ -118545,7 +118786,15 @@ var statusesRouter = t.router({
|
|
|
118545
118786
|
refreshBranchStatus: publicProcedure.input(external_exports2.object({ worktreeId: external_exports2.string() })).mutation(async ({ input: input2 }) => ({
|
|
118546
118787
|
refreshed: await branchStatusPoller.refreshWorktree(input2.worktreeId)
|
|
118547
118788
|
})),
|
|
118548
|
-
clearNeedsAttention: publicProcedure.input(
|
|
118789
|
+
clearNeedsAttention: publicProcedure.input(
|
|
118790
|
+
external_exports2.object({
|
|
118791
|
+
worktreeId: external_exports2.string(),
|
|
118792
|
+
// Opening a worktree also drops a manual unread mark. The UI leaves it out for a
|
|
118793
|
+
// worktree it just marked while that worktree was open.
|
|
118794
|
+
clearUnread: external_exports2.boolean().optional()
|
|
118795
|
+
})
|
|
118796
|
+
).mutation(({ input: input2 }) => {
|
|
118797
|
+
if (input2.clearUnread) worktreeService.clearUnreadOnOpen(input2.worktreeId);
|
|
118549
118798
|
const status = acknowledgeWorktreeAttention(input2.worktreeId);
|
|
118550
118799
|
if (status) emit({ kind: "update", status });
|
|
118551
118800
|
return { ok: true };
|
|
@@ -122294,6 +122543,23 @@ var worktreesRouter = t.router({
|
|
|
122294
122543
|
throwAsTrpcError3(err);
|
|
122295
122544
|
}
|
|
122296
122545
|
}),
|
|
122546
|
+
setUnread: publicProcedure.input(worktreeSetUnreadInput).mutation(({ input: input2 }) => {
|
|
122547
|
+
try {
|
|
122548
|
+
return worktreeService.setUnread(input2);
|
|
122549
|
+
} catch (err) {
|
|
122550
|
+
throwAsTrpcError3(err);
|
|
122551
|
+
}
|
|
122552
|
+
}),
|
|
122553
|
+
setTitle: publicProcedure.input(worktreeSetTitleInput).mutation(({ input: input2 }) => {
|
|
122554
|
+
try {
|
|
122555
|
+
return worktreeService.setTitle(input2);
|
|
122556
|
+
} catch (err) {
|
|
122557
|
+
if (err instanceof WorktreeNotFoundError) {
|
|
122558
|
+
throw new TRPCError({ code: "NOT_FOUND", message: err.message, cause: err });
|
|
122559
|
+
}
|
|
122560
|
+
throwAsTrpcError3(err);
|
|
122561
|
+
}
|
|
122562
|
+
}),
|
|
122297
122563
|
gitPull: publicProcedure.input(worktreeGitInput).mutation(async ({ input: input2 }) => {
|
|
122298
122564
|
try {
|
|
122299
122565
|
return await worktreeService.gitPull(input2);
|
|
@@ -122408,6 +122674,7 @@ function discoverProcedures() {
|
|
|
122408
122674
|
if (path2 === "repos.addByUrl") continue;
|
|
122409
122675
|
if (path2 === "repos.inspectFolder" || path2 === "repos.resolveRemote") continue;
|
|
122410
122676
|
if (path2 === "worktrees.setOrigin") continue;
|
|
122677
|
+
if (path2 === "worktrees.setUnread") continue;
|
|
122411
122678
|
if (path2.startsWith("groups.") && path2 !== "groups.list") continue;
|
|
122412
122679
|
if (path2 === "worktrees.reorder") continue;
|
|
122413
122680
|
if (path2 === "environment.build") continue;
|
|
@@ -124686,6 +124953,37 @@ async function handleLspConnection(ws, req) {
|
|
|
124686
124953
|
// start-server.ts
|
|
124687
124954
|
init_webhook();
|
|
124688
124955
|
|
|
124956
|
+
// src/server/services/_utils/byte-range.ts
|
|
124957
|
+
function parseByteRange(header, size) {
|
|
124958
|
+
const match = /^bytes=(\d*)-(\d*)$/.exec(header?.trim() ?? "");
|
|
124959
|
+
if (!match || match[1] === "" && match[2] === "") return null;
|
|
124960
|
+
if (match[1] !== "" && match[2] !== "" && Number(match[2]) < Number(match[1])) return null;
|
|
124961
|
+
let start;
|
|
124962
|
+
let end;
|
|
124963
|
+
if (match[1] === "") {
|
|
124964
|
+
const length = Number(match[2]);
|
|
124965
|
+
if (length === 0) return "unsatisfiable";
|
|
124966
|
+
start = Math.max(0, size - length);
|
|
124967
|
+
end = size - 1;
|
|
124968
|
+
} else {
|
|
124969
|
+
start = Number(match[1]);
|
|
124970
|
+
end = match[2] === "" ? size - 1 : Math.min(Number(match[2]), size - 1);
|
|
124971
|
+
}
|
|
124972
|
+
if (start >= size || start > end) return "unsatisfiable";
|
|
124973
|
+
return { start, end };
|
|
124974
|
+
}
|
|
124975
|
+
|
|
124976
|
+
// src/server/services/_utils/content-disposition.ts
|
|
124977
|
+
function attachmentDisposition(filename) {
|
|
124978
|
+
const clean = filename.replace(/[/\\]/g, "_") || "download";
|
|
124979
|
+
const ascii = clean.replace(/[^\x20-\x7e]/g, "_").replace(/["%;]/g, "_");
|
|
124980
|
+
const encoded = encodeURIComponent(clean).replace(
|
|
124981
|
+
/['()*]/g,
|
|
124982
|
+
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
|
|
124983
|
+
);
|
|
124984
|
+
return `attachment; filename="${ascii}"; filename*=UTF-8''${encoded}`;
|
|
124985
|
+
}
|
|
124986
|
+
|
|
124689
124987
|
// src/server/services/_utils/hosted-file.ts
|
|
124690
124988
|
init_registry();
|
|
124691
124989
|
import { basename as basename6, dirname as dirname17, join as join57 } from "node:path";
|
|
@@ -133247,7 +133545,7 @@ async function serveHostedFile(res, kind, worktreeId4, rawName, chatId) {
|
|
|
133247
133545
|
);
|
|
133248
133546
|
return true;
|
|
133249
133547
|
}
|
|
133250
|
-
async function serveWorktreeFile(res, worktreeId4, rawPath, extraHeaders = {}, visualEditNonce) {
|
|
133548
|
+
async function serveWorktreeFile(res, worktreeId4, rawPath, extraHeaders = {}, visualEditNonce, download = false, rangeHeader) {
|
|
133251
133549
|
const worktree = worktreeService.resolve(worktreeId4);
|
|
133252
133550
|
if (!worktree) {
|
|
133253
133551
|
res.writeHead(404);
|
|
@@ -133270,6 +133568,11 @@ async function serveWorktreeFile(res, worktreeId4, rawPath, extraHeaders = {}, v
|
|
|
133270
133568
|
return;
|
|
133271
133569
|
}
|
|
133272
133570
|
const fileStat = await fs3.stat(target, { followSymlinks: true });
|
|
133571
|
+
if (download && fileStat.kind !== "file") {
|
|
133572
|
+
res.writeHead(400);
|
|
133573
|
+
res.end("Bad request");
|
|
133574
|
+
return;
|
|
133575
|
+
}
|
|
133273
133576
|
const contentType = mimeTypeFromFilename(basename7(target));
|
|
133274
133577
|
const isHtml = contentType.startsWith("text/html");
|
|
133275
133578
|
const editHtml = isHtml && visualEditNonce !== void 0 && fileStat.kind === "file";
|
|
@@ -133285,28 +133588,37 @@ async function serveWorktreeFile(res, worktreeId4, rawPath, extraHeaders = {}, v
|
|
|
133285
133588
|
body = Buffer.from(annotateHtmlForEdit(source, visualEditNonce), "utf8");
|
|
133286
133589
|
}
|
|
133287
133590
|
}
|
|
133288
|
-
|
|
133591
|
+
const range = !body && !trailer && fileStat.kind === "file" ? parseByteRange(rangeHeader, fileStat.size) : null;
|
|
133592
|
+
if (range === "unsatisfiable") {
|
|
133593
|
+
res.writeHead(416, { "Content-Range": `bytes */${fileStat.size}` });
|
|
133594
|
+
res.end();
|
|
133595
|
+
return;
|
|
133596
|
+
}
|
|
133597
|
+
const partial3 = range !== null;
|
|
133598
|
+
res.writeHead(partial3 ? 206 : 200, {
|
|
133289
133599
|
"Content-Type": contentType,
|
|
133290
|
-
"Content-Length": (body?.byteLength ?? fileStat.size + (trailer?.byteLength ?? 0)).toString(),
|
|
133600
|
+
"Content-Length": (partial3 ? range.end - range.start + 1 : body?.byteLength ?? fileStat.size + (trailer?.byteLength ?? 0)).toString(),
|
|
133601
|
+
...partial3 ? { "Content-Range": `bytes ${range.start}-${range.end}/${fileStat.size}` } : {},
|
|
133602
|
+
"Accept-Ranges": "bytes",
|
|
133291
133603
|
"Cache-Control": "private, no-cache",
|
|
133292
|
-
// HTML from a repo must not run with the hub's origin
|
|
133293
|
-
|
|
133294
|
-
|
|
133295
|
-
|
|
133296
|
-
} : {},
|
|
133604
|
+
// HTML and SVG from a repo must not run with the hub's origin, and no
|
|
133605
|
+
// type may be sniffed into one.
|
|
133606
|
+
...hostedFileHeaders(contentType),
|
|
133607
|
+
...download ? { "Content-Disposition": attachmentDisposition(basename7(target)) } : {},
|
|
133297
133608
|
...extraHeaders
|
|
133298
133609
|
});
|
|
133299
133610
|
if (body) {
|
|
133300
133611
|
res.end(body);
|
|
133301
133612
|
return;
|
|
133302
133613
|
}
|
|
133303
|
-
const stream = Readable2.from(fs3.readStream(target), {
|
|
133614
|
+
const stream = Readable2.from(fs3.readStream(target, range ?? void 0), {
|
|
133304
133615
|
objectMode: false,
|
|
133305
133616
|
highWaterMark: 64 * 1024
|
|
133306
133617
|
});
|
|
133307
133618
|
if (trailer) {
|
|
133308
133619
|
stream.on("end", () => res.end(trailer));
|
|
133309
133620
|
stream.on("error", () => res.destroy());
|
|
133621
|
+
res.once("close", () => stream.destroy());
|
|
133310
133622
|
stream.pipe(res, { end: false });
|
|
133311
133623
|
} else {
|
|
133312
133624
|
pipeline2(stream, res, () => {
|
|
@@ -133684,6 +133996,7 @@ async function main() {
|
|
|
133684
133996
|
"/api/worktree-file/".length,
|
|
133685
133997
|
queryIdx === -1 ? void 0 : queryIdx
|
|
133686
133998
|
);
|
|
133999
|
+
const download = queryIdx !== -1 && new URLSearchParams(req.url.slice(queryIdx)).get("download") === "1";
|
|
133687
134000
|
const slashIdx = rest.indexOf("/");
|
|
133688
134001
|
if (slashIdx === -1) {
|
|
133689
134002
|
res.writeHead(400);
|
|
@@ -133705,7 +134018,7 @@ async function main() {
|
|
|
133705
134018
|
res.end("Bad request");
|
|
133706
134019
|
return;
|
|
133707
134020
|
}
|
|
133708
|
-
await serveWorktreeFile(res, wId, filePath);
|
|
134021
|
+
await serveWorktreeFile(res, wId, filePath, {}, void 0, download, req.headers.range);
|
|
133709
134022
|
return;
|
|
133710
134023
|
}
|
|
133711
134024
|
if (req.url?.startsWith("/api/cdp/tabs")) {
|