@band-app/server 0.59.0 → 0.60.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/client/_shell.html +0 -0
  2. package/dist/client/assets/{TerminalPanel-Dls8_vGY.js → TerminalPanel-C2ewe-bN.js} +1 -1
  3. package/dist/client/assets/{_basePickBy-B5gHumRZ.js → _basePickBy-Dfft9U2Y.js} +1 -1
  4. package/dist/client/assets/{_baseUniq-Bk4j3bVH.js → _baseUniq-VT-Fvpwa.js} +1 -1
  5. package/dist/client/assets/{arc-BE7I7FD7.js → arc-IQNCYyHi.js} +1 -1
  6. package/dist/client/assets/{architectureDiagram-VXUJARFQ-BBWBwpgm.js → architectureDiagram-VXUJARFQ-CEeXYnM1.js} +1 -1
  7. package/dist/client/assets/{blockDiagram-VD42YOAC-CfFgUppR.js → blockDiagram-VD42YOAC-D8t34_qE.js} +1 -1
  8. package/dist/client/assets/{c4Diagram-YG6GDRKO-ClgEIcXl.js → c4Diagram-YG6GDRKO-Bac4BHFl.js} +1 -1
  9. package/dist/client/assets/channel-CFIprjUA.js +1 -0
  10. package/dist/client/assets/{chunk-4BX2VUAB-BTsFCzlB.js → chunk-4BX2VUAB-8OgHJjso.js} +1 -1
  11. package/dist/client/assets/{chunk-55IACEB6-HURI4nLM.js → chunk-55IACEB6-1q6zldbT.js} +1 -1
  12. package/dist/client/assets/{chunk-B4BG7PRW-rC-ot-Mn.js → chunk-B4BG7PRW-CyB0YChj.js} +1 -1
  13. package/dist/client/assets/{chunk-DI55MBZ5-C2_9NN7e.js → chunk-DI55MBZ5-CbH2Vi7l.js} +1 -1
  14. package/dist/client/assets/{chunk-FMBD7UC4-CyCfpT3p.js → chunk-FMBD7UC4-DChsRBYo.js} +1 -1
  15. package/dist/client/assets/{chunk-QN33PNHL-BbkJTAOV.js → chunk-QN33PNHL-CCiqRxGJ.js} +1 -1
  16. package/dist/client/assets/{chunk-QZHKN3VN-BSdemm5A.js → chunk-QZHKN3VN-DSknJcWz.js} +1 -1
  17. package/dist/client/assets/{chunk-TZMSLE5B-D4wvd9Yu.js → chunk-TZMSLE5B-Dntdjhjx.js} +1 -1
  18. package/dist/client/assets/classDiagram-2ON5EDUG-BZeh-LMx.js +1 -0
  19. package/dist/client/assets/classDiagram-v2-WZHVMYZB-BZeh-LMx.js +1 -0
  20. package/dist/client/assets/clone-DTnLV01L.js +1 -0
  21. package/dist/client/assets/{cose-bilkent-S5V4N54A-J0yf-ZRd.js → cose-bilkent-S5V4N54A-Dpk2C2KC.js} +1 -1
  22. package/dist/client/assets/{dagre-6UL2VRFP-D2ktTA5V.js → dagre-6UL2VRFP-BxmsX_AF.js} +1 -1
  23. package/dist/client/assets/{diagram-PSM6KHXK-DlQLNiTP.js → diagram-PSM6KHXK-Cese24hg.js} +1 -1
  24. package/dist/client/assets/{diagram-QEK2KX5R-oY37vdEd.js → diagram-QEK2KX5R-Lux1tuP-.js} +1 -1
  25. package/dist/client/assets/{diagram-S2PKOQOG-DaOX0asw.js → diagram-S2PKOQOG-Da29vVFh.js} +1 -1
  26. package/dist/client/assets/{erDiagram-Q2GNP2WA-BeewDnEp.js → erDiagram-Q2GNP2WA-COz-Bxty.js} +1 -1
  27. package/dist/client/assets/{flowDiagram-NV44I4VS-CgEQ2bXi.js → flowDiagram-NV44I4VS-2AOhgtfg.js} +1 -1
  28. package/dist/client/assets/{ganttDiagram-JELNMOA3-DLIhunms.js → ganttDiagram-JELNMOA3-CvaV3At7.js} +1 -1
  29. package/dist/client/assets/{gitGraphDiagram-V2S2FVAM-BbcljkLL.js → gitGraphDiagram-V2S2FVAM-KL6lDevE.js} +1 -1
  30. package/dist/client/assets/{graph-C1SThxwq.js → graph-BkqeF57r.js} +1 -1
  31. package/dist/client/assets/{highlighted-body-B3W2YXNL-6fAWPSpW.js → highlighted-body-B3W2YXNL-BKvFJmoF.js} +1 -1
  32. package/dist/client/assets/{index-Bk8JlBY0.js → index-1IWusiUu.js} +1 -1
  33. package/dist/client/assets/{index-DNW-icyC.js → index-B5-KuoCn.js} +1 -1
  34. package/dist/client/assets/{index-D13XFok4.js → index-BERL9iqq.js} +1 -1
  35. package/dist/client/assets/{index-CLieczz3.js → index-Bh02-TAC.js} +1 -1
  36. package/dist/client/assets/{index-5YEiuPMI.js → index-By0VjYTD.js} +1 -1
  37. package/dist/client/assets/{index-BAfOs6Gy.js → index-C6ii9XZB.js} +1 -1
  38. package/dist/client/assets/{index-Bh3TSdKT.js → index-Cg0ZZSF_.js} +1 -1
  39. package/dist/client/assets/{index-CiOhy6I5.js → index-CjIZcCsd.js} +1 -1
  40. package/dist/client/assets/{index-DWbGgUyQ.js → index-CrWN-rZL.js} +1 -1
  41. package/dist/client/assets/{index-DMWW3BkM.js → index-Cunx_dNp.js} +1 -1
  42. package/dist/client/assets/{index-B1SUzFra.js → index-GqTH-hCs.js} +1 -1
  43. package/dist/client/assets/{index-Bxgl3ZHO.js → index-elgH9x1b.js} +1 -1
  44. package/dist/client/assets/{index-D7f6vSSM.js → index-oW3oul3c.js} +1 -1
  45. package/dist/client/assets/{infoDiagram-HS3SLOUP-vyRt2Bsk.js → infoDiagram-HS3SLOUP-B_qM6pDv.js} +1 -1
  46. package/dist/client/assets/{journeyDiagram-XKPGCS4Q-DBXVmGpv.js → journeyDiagram-XKPGCS4Q-DTM3hwpa.js} +1 -1
  47. package/dist/client/assets/{kanban-definition-3W4ZIXB7-CEpw89r7.js → kanban-definition-3W4ZIXB7-B-SYRVBz.js} +1 -1
  48. package/dist/client/assets/{layout-Q53PNtzV.js → layout-BKMHlUnr.js} +1 -1
  49. package/dist/client/assets/main-CYIZgqYb.css +1 -0
  50. package/dist/client/assets/{main-doYM8mtx.js → main-DFazIJQl.js} +299 -299
  51. package/dist/client/assets/{mindmap-definition-VGOIOE7T-jFXUryZ1.js → mindmap-definition-VGOIOE7T-Dt3thgpD.js} +1 -1
  52. package/dist/client/assets/{pieDiagram-ADFJNKIX-24FHt1PP.js → pieDiagram-ADFJNKIX-5PIGWxue.js} +1 -1
  53. package/dist/client/assets/{quadrantDiagram-AYHSOK5B-BznmAQTy.js → quadrantDiagram-AYHSOK5B-4zkd2IVs.js} +1 -1
  54. package/dist/client/assets/{requirementDiagram-UZGBJVZJ-auw6LsZk.js → requirementDiagram-UZGBJVZJ-DjXgbXXX.js} +1 -1
  55. package/dist/client/assets/{sankeyDiagram-TZEHDZUN-CX_06gNQ.js → sankeyDiagram-TZEHDZUN-G9X9dJMl.js} +1 -1
  56. package/dist/client/assets/{sequenceDiagram-WL72ISMW-tQMk7iIO.js → sequenceDiagram-WL72ISMW-gEn86t4_.js} +1 -1
  57. package/dist/client/assets/{stateDiagram-FKZM4ZOC-DCZeLtBb.js → stateDiagram-FKZM4ZOC-BKnMRzie.js} +1 -1
  58. package/dist/client/assets/stateDiagram-v2-4FDKWEC3-Be9VMvTq.js +1 -0
  59. package/dist/client/assets/{timeline-definition-IT6M3QCI-CqegSAwa.js → timeline-definition-IT6M3QCI-CZWSQmlt.js} +1 -1
  60. package/dist/client/assets/{treemap-GDKQZRPO-C1eLC_yq.js → treemap-GDKQZRPO-BL3I0InS.js} +1 -1
  61. package/dist/client/assets/{worktree._worktreeId-CNBIqdCa.js → worktree._worktreeId-BdiLGtrz.js} +1 -1
  62. package/dist/client/assets/{worktree._worktreeId-BfJo-vJP.js → worktree._worktreeId-DbLgzjwP.js} +1 -1
  63. package/dist/client/assets/{xychartDiagram-PRI3JC2R-CDiV-aAA.js → xychartDiagram-PRI3JC2R-CcsXglTm.js} +1 -1
  64. package/dist/openapi.json +276 -197
  65. package/dist/start-server.mjs +251 -190
  66. package/package.json +11 -11
  67. package/dist/client/assets/channel-CLuseuD8.js +0 -1
  68. package/dist/client/assets/classDiagram-2ON5EDUG-PhOyZKeC.js +0 -1
  69. package/dist/client/assets/classDiagram-v2-WZHVMYZB-PhOyZKeC.js +0 -1
  70. package/dist/client/assets/clone-CMv9qsOc.js +0 -1
  71. package/dist/client/assets/main-BVyM6Kwo.css +0 -1
  72. package/dist/client/assets/stateDiagram-v2-4FDKWEC3-9Yihsv84.js +0 -1
@@ -4323,11 +4323,11 @@ var require_pino = __commonJS({
4323
4323
  depthLimit: 5,
4324
4324
  edgeLimit: 100
4325
4325
  };
4326
- var normalize3 = createArgsNormalizer(defaultOptions2);
4326
+ var normalize4 = createArgsNormalizer(defaultOptions2);
4327
4327
  var serializers = Object.assign(/* @__PURE__ */ Object.create(null), stdSerializers);
4328
4328
  function pino2(...args) {
4329
4329
  const instance = {};
4330
- const { opts, stream } = normalize3(instance, caller(), ...args);
4330
+ const { opts, stream } = normalize4(instance, caller(), ...args);
4331
4331
  if (opts.level && typeof opts.level === "string" && DEFAULT_LEVELS[opts.level.toLowerCase()] !== void 0) opts.level = opts.level.toLowerCase();
4332
4332
  const {
4333
4333
  redact,
@@ -15184,7 +15184,7 @@ var require_stream = __commonJS({
15184
15184
  };
15185
15185
  duplex._final = function(callback) {
15186
15186
  if (ws.readyState === ws.CONNECTING) {
15187
- ws.once("open", function open4() {
15187
+ ws.once("open", function open3() {
15188
15188
  duplex._final(callback);
15189
15189
  });
15190
15190
  return;
@@ -15205,7 +15205,7 @@ var require_stream = __commonJS({
15205
15205
  };
15206
15206
  duplex._write = function(chunk, encoding, callback) {
15207
15207
  if (ws.readyState === ws.CONNECTING) {
15208
- ws.once("open", function open4() {
15208
+ ws.once("open", function open3() {
15209
15209
  duplex._write(chunk, encoding, callback);
15210
15210
  });
15211
15211
  return;
@@ -48171,27 +48171,27 @@ from generate_subscripts(${name5}, ${sql2.raw(dim.toString())}) ${aliases[dim -
48171
48171
  order by ${aliases[dim - 1]})`;
48172
48172
  return sql2`case when ${name5} is null then null else ${expression} end`;
48173
48173
  };
48174
- arrayCompatNormalize = (normalize3) => {
48174
+ arrayCompatNormalize = (normalize4) => {
48175
48175
  const loop = (value, arrayDimensions) => {
48176
48176
  const innerDimensions = arrayDimensions - 1;
48177
48177
  if (arrayDimensions > 1) for (let i = 0; i < value.length; ++i) loop(value[i], innerDimensions);
48178
- else for (let i = 0; i < value.length; ++i) value[i] = normalize3(value[i]);
48178
+ else for (let i = 0; i < value.length; ++i) value[i] = normalize4(value[i]);
48179
48179
  return value;
48180
48180
  };
48181
48181
  return loop;
48182
48182
  };
48183
- arrayCompatNormalizeInput = (normalize3, transformToPgArray = false) => {
48183
+ arrayCompatNormalizeInput = (normalize4, transformToPgArray = false) => {
48184
48184
  const loop = (value, arrayDimensions) => {
48185
48185
  const innerDimensions = arrayDimensions - 1;
48186
48186
  const out = Array.from({ length: value.length });
48187
48187
  if (arrayDimensions > 1) for (let i = 0; i < value.length; ++i) out[i] = loop(value[i], innerDimensions);
48188
- else for (let i = 0; i < value.length; ++i) out[i] = normalize3(value[i]);
48188
+ else for (let i = 0; i < value.length; ++i) out[i] = normalize4(value[i]);
48189
48189
  return out;
48190
48190
  };
48191
48191
  return transformToPgArray ? (v2, d) => makePgArray(loop(v2, d)) : loop;
48192
48192
  };
48193
- parsePgArrayAndNormalize = (normalize3) => {
48194
- const codec2 = arrayCompatNormalize(normalize3);
48193
+ parsePgArrayAndNormalize = (normalize4) => {
48194
+ const codec2 = arrayCompatNormalize(normalize4);
48195
48195
  return (value, arrayDimensions) => codec2(parsePgArray(value), arrayDimensions);
48196
48196
  };
48197
48197
  parseLineTuple = (v2) => {
@@ -67486,14 +67486,14 @@ function pickPullRequest(allNodes, repoOwner2, isDefaultBranch) {
67486
67486
  );
67487
67487
  const node2 = nodes2.find((n) => n.state === "OPEN") ?? (isDefaultBranch ? void 0 : nodes2.find((n) => n.state === "MERGED") ?? nodes2.find((n) => n.state === "CLOSED"));
67488
67488
  if (!node2) return null;
67489
- const open4 = node2.state === "OPEN";
67490
- const mergeQueue = open4 ? parseMergeQueue(node2) : null;
67491
- const autoMerge = open4 ? parseAutoMerge(node2) : null;
67489
+ const open3 = node2.state === "OPEN";
67490
+ const mergeQueue = open3 ? parseMergeQueue(node2) : null;
67491
+ const autoMerge = open3 ? parseAutoMerge(node2) : null;
67492
67492
  return {
67493
67493
  number: node2.number,
67494
67494
  title: node2.title,
67495
67495
  url: node2.url,
67496
- state: open4 ? "open" : node2.state === "MERGED" ? "merged" : "closed",
67496
+ state: open3 ? "open" : node2.state === "MERGED" ? "merged" : "closed",
67497
67497
  isDraft: node2.isDraft,
67498
67498
  ...mergeQueue && { mergeQueue },
67499
67499
  ...autoMerge && { autoMerge }
@@ -75671,8 +75671,8 @@ var init_placement_service = __esm({
75671
75671
  */
75672
75672
  async placeWorktree(input3, placement) {
75673
75673
  const worktreeId4 = toWorktreeId(input3.repo, input3.branch, void 0);
75674
- const open4 = this.queries.findOpenForWorktree(worktreeId4);
75675
- if (open4) return { kind: "request", requestId: open4.id };
75674
+ const open3 = this.queries.findOpenForWorktree(worktreeId4);
75675
+ if (open3) return { kind: "request", requestId: open3.id };
75676
75676
  const holders = this.holdersOfRemotelessRepo(input3.repo);
75677
75677
  const agent = input3.codingAgentId ? loadSettings().codingAgents?.find((a) => a.id === input3.codingAgentId)?.type ?? input3.codingAgentId : void 0;
75678
75678
  const allowed2 = allowedHostsOf(input3.repo);
@@ -75740,8 +75740,8 @@ var init_placement_service = __esm({
75740
75740
  requestWake(wake, repo, branch) {
75741
75741
  const first = wake.worktreeIds[0];
75742
75742
  if (!first) throw new Error("A wake request names no worktree");
75743
- const open4 = this.queries.findOpenForWorktree(first);
75744
- if (open4) return open4;
75743
+ const open3 = this.queries.findOpenForWorktree(first);
75744
+ if (open3) return open3;
75745
75745
  const earlier = this.queries.latestForHost(wake.hostId);
75746
75746
  const now = this.now();
75747
75747
  const row = {
@@ -79141,13 +79141,13 @@ var init_agent_session_registry_service = __esm({
79141
79141
  * agent sessions gets its row here, the first time its id is known.
79142
79142
  */
79143
79143
  recordChatProviderSession(chat, providerSessionId) {
79144
- const open4 = this.queries.findOpenByChat(chat.id);
79145
- if (open4 && open4.providerSessionId === providerSessionId) return;
79146
- if (open4 && open4.providerSessionId === null) {
79147
- if (providerSessionId) this.update(open4.id, { providerSessionId, state: "running" });
79144
+ const open3 = this.queries.findOpenByChat(chat.id);
79145
+ if (open3 && open3.providerSessionId === providerSessionId) return;
79146
+ if (open3 && open3.providerSessionId === null) {
79147
+ if (providerSessionId) this.update(open3.id, { providerSessionId, state: "running" });
79148
79148
  return;
79149
79149
  }
79150
- if (open4) this.end(open4.id);
79150
+ if (open3) this.end(open3.id);
79151
79151
  if (!providerSessionId) return;
79152
79152
  this.create({
79153
79153
  worktreeId: chat.worktreeId,
@@ -79158,12 +79158,12 @@ var init_agent_session_registry_service = __esm({
79158
79158
  });
79159
79159
  }
79160
79160
  endForChat(chatId) {
79161
- const open4 = this.queries.findOpenByChat(chatId);
79162
- if (open4) this.end(open4.id);
79161
+ const open3 = this.queries.findOpenByChat(chatId);
79162
+ if (open3) this.end(open3.id);
79163
79163
  }
79164
79164
  endForTerminal(terminalId) {
79165
- const open4 = this.queries.findOpenByTerminal(terminalId);
79166
- if (open4) this.end(open4.id);
79165
+ const open3 = this.queries.findOpenByTerminal(terminalId);
79166
+ if (open3) this.end(open3.id);
79167
79167
  }
79168
79168
  end(id) {
79169
79169
  const ended = this.queries.update(id, { state: "ended", updatedAt: Date.now() });
@@ -88785,8 +88785,8 @@ var require_resolve = __commonJS({
88785
88785
  }
88786
88786
  return count3;
88787
88787
  }
88788
- function getFullPath(resolver, id = "", normalize3) {
88789
- if (normalize3 !== false)
88788
+ function getFullPath(resolver, id = "", normalize4) {
88789
+ if (normalize4 !== false)
88790
88790
  id = normalizeId(id);
88791
88791
  const p2 = resolver.parse(id);
88792
88792
  return _getFullPath(resolver, p2);
@@ -90126,7 +90126,7 @@ var require_fast_uri = __commonJS({
90126
90126
  "use strict";
90127
90127
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
90128
90128
  var { SCHEMES, getSchemeHandler } = require_schemes();
90129
- function normalize3(uri, options) {
90129
+ function normalize4(uri, options) {
90130
90130
  if (typeof uri === "string") {
90131
90131
  uri = /** @type {T} */
90132
90132
  serialize2(parse8(uri, options), options);
@@ -90362,7 +90362,7 @@ var require_fast_uri = __commonJS({
90362
90362
  }
90363
90363
  var fastUri = {
90364
90364
  SCHEMES,
90365
- normalize: normalize3,
90365
+ normalize: normalize4,
90366
90366
  resolve: resolve16,
90367
90367
  resolveComponent,
90368
90368
  equal,
@@ -111928,8 +111928,6 @@ var agentSessionsRouter = t.router({
111928
111928
  // src/server/api/browser-host/router.ts
111929
111929
  init_src();
111930
111930
  init_zod();
111931
- import { constants as fsConstants, open as open3 } from "node:fs/promises";
111932
- import { extname as extname2, isAbsolute as isAbsolute3 } from "node:path";
111933
111931
 
111934
111932
  // src/server/infra/browser-host/host-state.ts
111935
111933
  init_src();
@@ -112065,123 +112063,6 @@ var browserHostService = new BrowserHostService();
112065
112063
 
112066
112064
  // src/server/api/browser-host/router.ts
112067
112065
  var log63 = createLogger("trpc.browser-host");
112068
- var MAX_FILE_SIZE = 1024 * 1024;
112069
- var LANG_MAP = {
112070
- ".js": "javascript",
112071
- ".jsx": "jsx",
112072
- ".ts": "typescript",
112073
- ".tsx": "tsx",
112074
- ".mjs": "javascript",
112075
- ".cjs": "javascript",
112076
- ".json": "json",
112077
- ".html": "html",
112078
- ".css": "css",
112079
- ".scss": "scss",
112080
- ".md": "markdown",
112081
- ".yaml": "yaml",
112082
- ".yml": "yaml",
112083
- ".toml": "toml",
112084
- ".py": "python",
112085
- ".rb": "ruby",
112086
- ".rs": "rust",
112087
- ".go": "go",
112088
- ".java": "java",
112089
- ".swift": "swift",
112090
- ".c": "c",
112091
- ".cpp": "cpp",
112092
- ".sh": "bash",
112093
- ".sql": "sql",
112094
- ".graphql": "graphql",
112095
- ".vue": "vue",
112096
- ".svelte": "svelte",
112097
- ".diff": "diff"
112098
- };
112099
- function mapFsError(err) {
112100
- const code = err.code;
112101
- if (code === "ENOENT") return new Error("File not found");
112102
- if (code === "ELOOP") return new Error("Symbolic links are not allowed");
112103
- if (code === "EACCES" || code === "EPERM") return new Error("Permission denied");
112104
- if (code === "EISDIR") return new Error("Cannot operate on a directory");
112105
- return err instanceof Error ? err : new Error(String(err));
112106
- }
112107
- var hostRouter = t.router({
112108
- readFile: publicProcedure.input(external_exports2.object({ absolutePath: external_exports2.string().min(1) })).query(async ({ input: input3 }) => {
112109
- const target = input3.absolutePath;
112110
- if (!isAbsolute3(target)) {
112111
- throw new Error("Absolute path required");
112112
- }
112113
- let fh;
112114
- try {
112115
- fh = await open3(target, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
112116
- } catch (err) {
112117
- throw mapFsError(err);
112118
- }
112119
- try {
112120
- const stats = await fh.stat();
112121
- if (stats.isDirectory()) {
112122
- throw new Error("Cannot operate on a directory");
112123
- }
112124
- if (!stats.isFile()) {
112125
- throw new Error("Not a regular file");
112126
- }
112127
- const size = stats.size;
112128
- if (size > MAX_FILE_SIZE) {
112129
- return { tooLarge: true, size };
112130
- }
112131
- const sampleLen = Math.min(8192, size);
112132
- const sample = Buffer.alloc(sampleLen);
112133
- if (sampleLen > 0) {
112134
- await fh.read(sample, 0, sampleLen, 0);
112135
- if (sample.includes(0)) {
112136
- return { binary: true, size };
112137
- }
112138
- }
112139
- const buffer = await fh.readFile();
112140
- const ext = extname2(target).toLowerCase();
112141
- const language = LANG_MAP[ext];
112142
- return {
112143
- content: buffer.toString("utf-8"),
112144
- size,
112145
- language
112146
- };
112147
- } finally {
112148
- await fh.close();
112149
- }
112150
- }),
112151
- saveFile: publicProcedure.input(
112152
- external_exports2.object({
112153
- absolutePath: external_exports2.string().min(1),
112154
- // Match the read-side cap so a misbehaving client can't fill
112155
- // disk via the save endpoint while the read endpoint refuses
112156
- // anything that wide.
112157
- content: external_exports2.string().max(MAX_FILE_SIZE)
112158
- })
112159
- ).mutation(async ({ input: input3 }) => {
112160
- const target = input3.absolutePath;
112161
- if (!isAbsolute3(target)) {
112162
- throw new Error("Absolute path required");
112163
- }
112164
- let fh;
112165
- try {
112166
- fh = await open3(
112167
- target,
112168
- fsConstants.O_WRONLY | fsConstants.O_TRUNC | fsConstants.O_NOFOLLOW
112169
- );
112170
- } catch (err) {
112171
- throw mapFsError(err);
112172
- }
112173
- try {
112174
- const stats = await fh.stat();
112175
- if (!stats.isFile()) {
112176
- throw new Error("Not a regular file");
112177
- }
112178
- await fh.writeFile(input3.content, "utf-8");
112179
- } finally {
112180
- await fh.close();
112181
- }
112182
- return { ok: true };
112183
- })
112184
- });
112185
112066
  var browserHostRouter = t.router({
112186
112067
  // Diagnostic: the desktop's BrowserHostBridge calls this on mount so we
112187
112068
  // can confirm in the server log that the bridge component actually
@@ -113262,7 +113143,7 @@ function formatFileLocation(filePath, line2, options) {
113262
113143
 
113263
113144
  // ../../packages/worktree-ops/src/resolve-path.ts
113264
113145
  init_resolver();
113265
- import { isAbsolute as isAbsolute4, join as join52, resolve as resolve10, sep as sep6 } from "node:path";
113146
+ import { isAbsolute as isAbsolute3, join as join52, resolve as resolve10, sep as sep6 } from "node:path";
113266
113147
  var PathsService = class {
113267
113148
  constructor(worktrees2) {
113268
113149
  this.worktrees = worktrees2;
@@ -113280,7 +113161,7 @@ var PathsService = class {
113280
113161
  }
113281
113162
  };
113282
113163
  async function resolveTarget(fs3, root, filePath) {
113283
- const absoluteTarget = isAbsolute4(filePath) ? resolve10(filePath) : resolve10(root, filePath);
113164
+ const absoluteTarget = isAbsolute3(filePath) ? resolve10(filePath) : resolve10(root, filePath);
113284
113165
  let canonicalRoot = root;
113285
113166
  try {
113286
113167
  canonicalRoot = await fs3.realpath(root);
@@ -113395,10 +113276,10 @@ async function pumpChanges(worktreeId4, entry, host, root) {
113395
113276
  for (const listener of entry.listeners) listener(null);
113396
113277
  stopWatcher(worktreeId4, entry);
113397
113278
  }
113398
- function subscribeToFileChanges(worktreeId4, open4, listener) {
113279
+ function subscribeToFileChanges(worktreeId4, open3, listener) {
113399
113280
  let entry = watchers.get(worktreeId4);
113400
113281
  if (!entry) {
113401
- const target = open4();
113282
+ const target = open3();
113402
113283
  if (!target) return () => {
113403
113284
  };
113404
113285
  entry = { controller: new AbortController(), listeners: /* @__PURE__ */ new Set(), pendingTimers: /* @__PURE__ */ new Map() };
@@ -113429,7 +113310,7 @@ function subscribeToFileChanges2(worktreeId4, listener) {
113429
113310
  // src/server/services/formatter.ts
113430
113311
  init_src();
113431
113312
  init_registry();
113432
- import { basename as basename4, dirname as dirname15, isAbsolute as isAbsolute5, join as join53, resolve as resolvePath3 } from "node:path";
113313
+ import { basename as basename4, dirname as dirname15, isAbsolute as isAbsolute4, join as join53, resolve as resolvePath3 } from "node:path";
113433
113314
  import prettier from "prettier";
113434
113315
  var log65 = createLogger("formatter");
113435
113316
  var FormatterError = class extends Error {
@@ -113445,7 +113326,7 @@ var FormatterError = class extends Error {
113445
113326
  async function formatFile(worktreePath, filePath, content, options = {}) {
113446
113327
  const fs3 = options.fs ?? hostRegistry.local.fs;
113447
113328
  const onHub = fs3 === hostRegistry.local.fs;
113448
- const absFile = isAbsolute5(filePath) ? filePath : resolvePath3(worktreePath, filePath);
113329
+ const absFile = isAbsolute4(filePath) ? filePath : resolvePath3(worktreePath, filePath);
113449
113330
  if (!await isInsideWorktree(fs3, absFile, worktreePath)) {
113450
113331
  throw new FormatterError(
113451
113332
  "FILE_NOT_IN_WORKTREE",
@@ -113735,7 +113616,7 @@ init_environment_build_service();
113735
113616
  init_src6();
113736
113617
  init_dist();
113737
113618
  init_registry();
113738
- import { basename as basename5, dirname as dirname16, isAbsolute as isAbsolute6 } from "node:path";
113619
+ import { basename as basename5, dirname as dirname16, isAbsolute as isAbsolute5 } from "node:path";
113739
113620
  init_token_service();
113740
113621
  var EnvironmentService = class {
113741
113622
  /** The environment of a repo's main checkout, and which hosts meet its `requires`. */
@@ -113750,7 +113631,7 @@ var EnvironmentService = class {
113750
113631
  * name the `.band/environment.json` file itself.
113751
113632
  */
113752
113633
  async validatePath(path2) {
113753
- if (!isAbsolute6(path2)) {
113634
+ if (!isAbsolute5(path2)) {
113754
113635
  throw new TRPCError({ code: "BAD_REQUEST", message: "path must be absolute" });
113755
113636
  }
113756
113637
  const isFile2 = basename5(path2) === "environment.json" && basename5(dirname16(path2)) === ".band";
@@ -114850,7 +114731,7 @@ init_registry();
114850
114731
  // package.json
114851
114732
  var package_default = {
114852
114733
  name: "@band-app/server",
114853
- version: "0.59.0",
114734
+ version: "0.60.1",
114854
114735
  type: "module",
114855
114736
  types: "src/index.ts",
114856
114737
  exports: {
@@ -123578,18 +123459,53 @@ var directOps = {
123578
123459
  init_dist();
123579
123460
  init_zod();
123580
123461
 
123581
- // src/server/services/_utils/worktree-preview-link.ts
123462
+ // src/server/services/_utils/worktree-download-link.ts
123582
123463
  init_state();
123583
123464
  import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual8 } from "node:crypto";
123465
+ var WORKTREE_DOWNLOAD_PREFIX = "/api/worktree-download/";
123466
+ var DOWNLOAD_TTL_MS = 5 * 60 * 1e3;
123467
+ function signingKey() {
123468
+ const token = resolveAdminToken(process.env.BAND_ADMIN_TOKEN).token;
123469
+ return createHmac2("sha256", token).update("worktree-download-v1").digest();
123470
+ }
123471
+ function normalize3(path2) {
123472
+ return path2.replace(/^\/+/, "");
123473
+ }
123474
+ function sign(worktreeId4, path2, expiresAt) {
123475
+ return createHmac2("sha256", signingKey()).update(`${worktreeId4}
123476
+ ${normalize3(path2)}
123477
+ ${expiresAt}`).digest("hex");
123478
+ }
123479
+ function worktreeDownloadUrl(worktreeId4, path2, now = Date.now()) {
123480
+ const expiresAt = now + DOWNLOAD_TTL_MS;
123481
+ const grant = `${expiresAt}.${sign(worktreeId4, path2, expiresAt)}`;
123482
+ const encodedPath = normalize3(path2).split("/").map(encodeURIComponent).join("/");
123483
+ return `${WORKTREE_DOWNLOAD_PREFIX}${encodeURIComponent(worktreeId4)}/${grant}/${encodedPath}`;
123484
+ }
123485
+ function verifyWorktreeDownloadGrant(worktreeId4, grant, filePath, now = Date.now()) {
123486
+ if (filePath.includes("\0")) return false;
123487
+ const [expiry, signature, ...extra] = grant.split(".");
123488
+ const expiresAt = Number(expiry);
123489
+ if (extra.length > 0 || !signature || !Number.isSafeInteger(expiresAt) || expiresAt < now) {
123490
+ return false;
123491
+ }
123492
+ const expected = Buffer.from(sign(worktreeId4, filePath, expiresAt), "hex");
123493
+ const given = Buffer.from(signature, "hex");
123494
+ return given.length === expected.length && timingSafeEqual8(given, expected);
123495
+ }
123496
+
123497
+ // src/server/services/_utils/worktree-preview-link.ts
123498
+ init_state();
123499
+ import { createHmac as createHmac3, timingSafeEqual as timingSafeEqual9 } from "node:crypto";
123584
123500
  import { posix as posix10 } from "node:path";
123585
123501
  var WORKTREE_PREVIEW_PREFIX = "/api/worktree-preview/";
123586
123502
  var PREVIEW_TTL_MS = 60 * 60 * 1e3;
123587
- function signingKey() {
123503
+ function signingKey2() {
123588
123504
  const token = resolveAdminToken(process.env.BAND_ADMIN_TOKEN).token;
123589
- return createHmac2("sha256", token).update("worktree-preview-v1").digest();
123505
+ return createHmac3("sha256", token).update("worktree-preview-v1").digest();
123590
123506
  }
123591
- function sign(worktreeId4, dir, expiresAt) {
123592
- return createHmac2("sha256", signingKey()).update(`${worktreeId4}
123507
+ function sign2(worktreeId4, dir, expiresAt) {
123508
+ return createHmac3("sha256", signingKey2()).update(`${worktreeId4}
123593
123509
  ${dir}
123594
123510
  ${expiresAt}`).digest("hex");
123595
123511
  }
@@ -123598,7 +123514,7 @@ function worktreePreviewUrl(worktreeId4, path2, now = Date.now()) {
123598
123514
  const dir = posix10.dirname(path2.replace(/^\/+/, ""));
123599
123515
  const encodedDir = Buffer.from(dir, "utf8").toString("base64url");
123600
123516
  const encodedPath = path2.split("/").map(encodeURIComponent).join("/");
123601
- const grant = `${expiresAt}.${encodedDir}.${sign(worktreeId4, dir, expiresAt)}`;
123517
+ const grant = `${expiresAt}.${encodedDir}.${sign2(worktreeId4, dir, expiresAt)}`;
123602
123518
  return `${WORKTREE_PREVIEW_PREFIX}${encodeURIComponent(worktreeId4)}/${grant}/${encodedPath}`;
123603
123519
  }
123604
123520
  function verifyWorktreePreviewGrant(worktreeId4, grant, filePath, now = Date.now()) {
@@ -123609,9 +123525,9 @@ function verifyWorktreePreviewGrant(worktreeId4, grant, filePath, now = Date.now
123609
123525
  return false;
123610
123526
  }
123611
123527
  const dir = Buffer.from(encodedDir, "base64url").toString("utf8");
123612
- const expected = Buffer.from(sign(worktreeId4, dir, expiresAt), "hex");
123528
+ const expected = Buffer.from(sign2(worktreeId4, dir, expiresAt), "hex");
123613
123529
  const given = Buffer.from(signature, "hex");
123614
- if (given.length !== expected.length || !timingSafeEqual8(given, expected)) return false;
123530
+ if (given.length !== expected.length || !timingSafeEqual9(given, expected)) return false;
123615
123531
  const target = posix10.normalize(filePath.replace(/^\/+/, ""));
123616
123532
  if (target === ".." || target.startsWith("../")) return false;
123617
123533
  return dir === "." || target === dir || target.startsWith(`${dir}/`);
@@ -124320,6 +124236,92 @@ var DirectAccessService = class {
124320
124236
  };
124321
124237
  var directAccessService = new DirectAccessService();
124322
124238
 
124239
+ // src/server/services/external-file-service.ts
124240
+ init_errors6();
124241
+ init_worktree_service();
124242
+ import { extname as extname2, isAbsolute as isAbsolute6 } from "node:path";
124243
+ var MAX_FILE_SIZE = 1024 * 1024;
124244
+ var LANG_MAP = {
124245
+ ".js": "javascript",
124246
+ ".jsx": "jsx",
124247
+ ".ts": "typescript",
124248
+ ".tsx": "tsx",
124249
+ ".mjs": "javascript",
124250
+ ".cjs": "javascript",
124251
+ ".json": "json",
124252
+ ".html": "html",
124253
+ ".css": "css",
124254
+ ".scss": "scss",
124255
+ ".md": "markdown",
124256
+ ".yaml": "yaml",
124257
+ ".yml": "yaml",
124258
+ ".toml": "toml",
124259
+ ".py": "python",
124260
+ ".rb": "ruby",
124261
+ ".rs": "rust",
124262
+ ".go": "go",
124263
+ ".java": "java",
124264
+ ".swift": "swift",
124265
+ ".c": "c",
124266
+ ".cpp": "cpp",
124267
+ ".sh": "bash",
124268
+ ".sql": "sql",
124269
+ ".graphql": "graphql",
124270
+ ".vue": "vue",
124271
+ ".svelte": "svelte",
124272
+ ".diff": "diff"
124273
+ };
124274
+ function hostFor2(worktreeId4) {
124275
+ const resolved = worktreeService.resolve(worktreeId4);
124276
+ if (!resolved) throw new WorktreeNotFoundError(worktreeId4);
124277
+ return resolved.host;
124278
+ }
124279
+ async function statFile(fs3, path2) {
124280
+ try {
124281
+ return await fs3.stat(path2);
124282
+ } catch (err) {
124283
+ const text4 = err instanceof Error ? `${err.code ?? ""} ${err.message}` : "";
124284
+ if (/ENOENT|no such file|not found/i.test(text4)) throw new Error("File not found");
124285
+ throw err;
124286
+ }
124287
+ }
124288
+ function assertAbsolute(path2) {
124289
+ if (!isAbsolute6(path2)) throw new Error("Absolute path required");
124290
+ }
124291
+ var ExternalFileService = class {
124292
+ async read(worktreeId4, absolutePath) {
124293
+ assertAbsolute(absolutePath);
124294
+ const { fs: fs3 } = hostFor2(worktreeId4);
124295
+ const stat9 = await statFile(fs3, absolutePath);
124296
+ if (stat9.kind === "symlink") throw new Error("Symbolic links are not allowed");
124297
+ if (stat9.kind === "directory") throw new Error("Cannot operate on a directory");
124298
+ if (stat9.kind !== "file") throw new Error("Not a regular file");
124299
+ if (stat9.size > MAX_FILE_SIZE) return { tooLarge: true, size: stat9.size };
124300
+ const bytes = await fs3.readFile(absolutePath);
124301
+ const buffer = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
124302
+ if (buffer.subarray(0, 8192).includes(0)) return { binary: true, size: stat9.size };
124303
+ return {
124304
+ content: buffer.toString("utf-8"),
124305
+ size: stat9.size,
124306
+ language: LANG_MAP[extname2(absolutePath).toLowerCase()]
124307
+ };
124308
+ }
124309
+ async save(worktreeId4, absolutePath, content) {
124310
+ assertAbsolute(absolutePath);
124311
+ if (Buffer.byteLength(content, "utf-8") > MAX_FILE_SIZE) throw new Error("File too large");
124312
+ const { fs: fs3 } = hostFor2(worktreeId4);
124313
+ const stat9 = await statFile(fs3, absolutePath);
124314
+ if (stat9.kind === "symlink") throw new Error("Symbolic links are not allowed");
124315
+ if (stat9.kind !== "file") throw new Error("Not a regular file");
124316
+ if (absolutePath.split(/[\\/]/).includes(".git")) {
124317
+ throw new Error("Refusing to write inside a .git folder");
124318
+ }
124319
+ await fs3.writeFile(absolutePath, content);
124320
+ return { ok: true };
124321
+ }
124322
+ };
124323
+ var externalFileService = new ExternalFileService();
124324
+
124323
124325
  // ../../packages/worktree-ops/src/files.ts
124324
124326
  init_resolver();
124325
124327
  import { dirname as dirname18, extname as extname3, join as join59, resolve as resolve13, sep as sep10 } from "node:path";
@@ -125120,14 +125122,14 @@ function asciiFuzzyIndex(input3, pattern, caseSensitive) {
125120
125122
  }
125121
125123
  return firstIdx;
125122
125124
  }
125123
- var fuzzyMatchV2 = (caseSensitive, normalize3, forward, input3, pattern, withPos, slab2) => {
125125
+ var fuzzyMatchV2 = (caseSensitive, normalize4, forward, input3, pattern, withPos, slab2) => {
125124
125126
  const M = pattern.length;
125125
125127
  if (M === 0) {
125126
125128
  return [{ start: 0, end: 0, score: 0 }, createPosSet(withPos)];
125127
125129
  }
125128
125130
  const N = input3.length;
125129
125131
  if (slab2 !== null && N * M > slab2.i16.length) {
125130
- return fuzzyMatchV1(caseSensitive, normalize3, forward, input3, pattern, withPos);
125132
+ return fuzzyMatchV1(caseSensitive, normalize4, forward, input3, pattern, withPos);
125131
125133
  }
125132
125134
  const idx = asciiFuzzyIndex(input3, pattern, caseSensitive);
125133
125135
  if (idx < 0) {
@@ -125160,7 +125162,7 @@ var fuzzyMatchV2 = (caseSensitive, normalize3, forward, input3, pattern, withPos
125160
125162
  if (!caseSensitive && charClass === 2) {
125161
125163
  char2 = String.fromCodePoint(char2).toLowerCase().codePointAt(0);
125162
125164
  }
125163
- if (normalize3) {
125165
+ if (normalize4) {
125164
125166
  char2 = normalizeRune(char2);
125165
125167
  }
125166
125168
  }
@@ -125300,7 +125302,7 @@ var fuzzyMatchV2 = (caseSensitive, normalize3, forward, input3, pattern, withPos
125300
125302
  }
125301
125303
  return [{ start: j, end: maxScorePos + 1, score: maxScore }, pos];
125302
125304
  };
125303
- function calculateScore(caseSensitive, normalize3, text4, pattern, sidx, eidx, withPos) {
125305
+ function calculateScore(caseSensitive, normalize4, text4, pattern, sidx, eidx, withPos) {
125304
125306
  let pidx = 0, score = 0, inGap = false, consecutive = 0, firstBonus = toShort(0);
125305
125307
  const pos = createPosSet(withPos);
125306
125308
  let prevCharClass = 0;
@@ -125317,7 +125319,7 @@ function calculateScore(caseSensitive, normalize3, text4, pattern, sidx, eidx, w
125317
125319
  rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0);
125318
125320
  }
125319
125321
  }
125320
- if (normalize3) {
125322
+ if (normalize4) {
125321
125323
  rune = normalizeRune(rune);
125322
125324
  }
125323
125325
  if (rune === pattern[pidx]) {
@@ -125356,7 +125358,7 @@ function calculateScore(caseSensitive, normalize3, text4, pattern, sidx, eidx, w
125356
125358
  }
125357
125359
  return [score, pos];
125358
125360
  }
125359
- var fuzzyMatchV1 = (caseSensitive, normalize3, forward, text4, pattern, withPos, slab2) => {
125361
+ var fuzzyMatchV1 = (caseSensitive, normalize4, forward, text4, pattern, withPos, slab2) => {
125360
125362
  if (pattern.length === 0) {
125361
125363
  return [{ start: 0, end: 0, score: 0 }, null];
125362
125364
  }
@@ -125375,7 +125377,7 @@ var fuzzyMatchV1 = (caseSensitive, normalize3, forward, text4, pattern, withPos,
125375
125377
  rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0);
125376
125378
  }
125377
125379
  }
125378
- if (normalize3) {
125380
+ if (normalize4) {
125379
125381
  rune = normalizeRune(rune);
125380
125382
  }
125381
125383
  const pchar = pattern[indexAt(pidx, lenPattern, forward)];
@@ -125419,7 +125421,7 @@ var fuzzyMatchV1 = (caseSensitive, normalize3, forward, text4, pattern, withPos,
125419
125421
  }
125420
125422
  const [score, pos] = calculateScore(
125421
125423
  caseSensitive,
125422
- normalize3,
125424
+ normalize4,
125423
125425
  text4,
125424
125426
  pattern,
125425
125427
  sidx,
@@ -125430,7 +125432,7 @@ var fuzzyMatchV1 = (caseSensitive, normalize3, forward, text4, pattern, withPos,
125430
125432
  }
125431
125433
  return [{ start: -1, end: -1, score: 0 }, null];
125432
125434
  };
125433
- var exactMatchNaive = (caseSensitive, normalize3, forward, text4, pattern, withPos, slab2) => {
125435
+ var exactMatchNaive = (caseSensitive, normalize4, forward, text4, pattern, withPos, slab2) => {
125434
125436
  if (pattern.length === 0) {
125435
125437
  return [{ start: 0, end: 0, score: 0 }, null];
125436
125438
  }
@@ -125454,7 +125456,7 @@ var exactMatchNaive = (caseSensitive, normalize3, forward, text4, pattern, withP
125454
125456
  rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0);
125455
125457
  }
125456
125458
  }
125457
- if (normalize3) {
125459
+ if (normalize4) {
125458
125460
  rune = normalizeRune(rune);
125459
125461
  }
125460
125462
  const pidx_ = indexAt(pidx, lenPattern, forward);
@@ -125491,7 +125493,7 @@ var exactMatchNaive = (caseSensitive, normalize3, forward, text4, pattern, withP
125491
125493
  sidx = lenRunes - (bestPos + 1);
125492
125494
  eidx = lenRunes - (bestPos - lenPattern + 1);
125493
125495
  }
125494
- const [score] = calculateScore(caseSensitive, normalize3, text4, pattern, sidx, eidx, false);
125496
+ const [score] = calculateScore(caseSensitive, normalize4, text4, pattern, sidx, eidx, false);
125495
125497
  return [{ start: sidx, end: eidx, score }, null];
125496
125498
  }
125497
125499
  return [{ start: -1, end: -1, score: 0 }, null];
@@ -125505,7 +125507,7 @@ function makeSlab(size16, size32) {
125505
125507
  };
125506
125508
  }
125507
125509
  var slab = makeSlab(SLAB_16_SIZE, SLAB_32_SIZE);
125508
- var buildPatternForBasicMatch = (query, casing, normalize3) => {
125510
+ var buildPatternForBasicMatch = (query, casing, normalize4) => {
125509
125511
  let caseSensitive = false;
125510
125512
  switch (casing) {
125511
125513
  case "smart-case":
@@ -125522,7 +125524,7 @@ var buildPatternForBasicMatch = (query, casing, normalize3) => {
125522
125524
  break;
125523
125525
  }
125524
125526
  let queryRunes = strToRunes(query);
125525
- if (normalize3) {
125527
+ if (normalize4) {
125526
125528
  queryRunes = queryRunes.map(normalizeRune);
125527
125529
  }
125528
125530
  return {
@@ -125973,6 +125975,20 @@ var worktreeRouter = t.router({
125973
125975
  listFiles: publicProcedure.input(directOps["worktree.listFiles"].input).query(({ input: input3 }) => directOps["worktree.listFiles"].run(worktreeOps, input3)),
125974
125976
  getFile: publicProcedure.input(directOps["worktree.getFile"].input).query(({ input: input3 }) => directOps["worktree.getFile"].run(worktreeOps, input3)),
125975
125977
  saveFile: publicProcedure.input(directOps["worktree.saveFile"].input).mutation(({ input: input3 }) => directOps["worktree.saveFile"].run(worktreeOps, input3)),
125978
+ /**
125979
+ * A file outside the worktree, read on the worktree's own worker (never the
125980
+ * hub's disk). Left out of the relay and the direct path: device tokens only.
125981
+ */
125982
+ readExternalFile: publicProcedure.input(external_exports2.object({ worktreeId: external_exports2.string().min(1), absolutePath: external_exports2.string().min(1) })).query(({ input: input3 }) => externalFileService.read(input3.worktreeId, input3.absolutePath)),
125983
+ saveExternalFile: publicProcedure.input(
125984
+ external_exports2.object({
125985
+ worktreeId: external_exports2.string().min(1),
125986
+ absolutePath: external_exports2.string().min(1),
125987
+ content: external_exports2.string()
125988
+ })
125989
+ ).mutation(
125990
+ ({ input: input3 }) => externalFileService.save(input3.worktreeId, input3.absolutePath, input3.content)
125991
+ ),
125976
125992
  createFile: publicProcedure.input(directOps["worktree.createFile"].input).mutation(({ input: input3 }) => directOps["worktree.createFile"].run(worktreeOps, input3)),
125977
125993
  createDirectory: publicProcedure.input(directOps["worktree.createDirectory"].input).mutation(({ input: input3 }) => directOps["worktree.createDirectory"].run(worktreeOps, input3)),
125978
125994
  deletePath: publicProcedure.input(directOps["worktree.deletePath"].input).mutation(({ input: input3 }) => directOps["worktree.deletePath"].run(worktreeOps, input3)),
@@ -125985,6 +126001,13 @@ var worktreeRouter = t.router({
125985
126001
  }
125986
126002
  return { url: worktreePreviewUrl(input3.worktreeId, input3.path) };
125987
126003
  }),
126004
+ /** A signed URL (5 minutes, one file) that a drag out of the Explorer hands to another app. */
126005
+ downloadUrl: publicProcedure.input(external_exports2.object({ worktreeId: external_exports2.string(), path: external_exports2.string().min(1) })).query(({ input: input3 }) => {
126006
+ if (!worktreeService.resolve(input3.worktreeId)) {
126007
+ throw new TRPCError({ code: "NOT_FOUND", message: "Worktree not found" });
126008
+ }
126009
+ return { url: worktreeDownloadUrl(input3.worktreeId, input3.path) };
126010
+ }),
125988
126011
  searchFiles: publicProcedure.input(directOps["worktree.searchFiles"].input).query(({ input: input3 }) => directOps["worktree.searchFiles"].run(worktreeOps, input3)),
125989
126012
  // Resolve an absolute (or worktree-relative) path against this
125990
126013
  // worktree: does it exist, is it a regular file, and does it live inside
@@ -125997,7 +126020,7 @@ var worktreeRouter = t.router({
125997
126020
  // path an authenticated caller supplies (not just in-worktree ones), so it
125998
126021
  // is a filesystem-existence oracle. That is acceptable under Band's threat
125999
126022
  // model — the same auth scope already reads arbitrary absolute paths via
126000
- // `host.readFile` — and the `external` flag is load-bearing, not dead code:
126023
+ // `worktree.readExternalFile` — and the `external` flag is load-bearing, not dead code:
126001
126024
  // it's how the client decides between a worktree-relative and an external
126002
126025
  // tab. Do not "harden" this by dropping the out-of-worktree classification.
126003
126026
  resolvePath: publicProcedure.input(resolvePathInput).query(
@@ -126135,7 +126158,6 @@ var appRouter = t.router({
126135
126158
  accountUsage: accountUsageRouter,
126136
126159
  resources: resourcesRouter,
126137
126160
  hooks: hooksRouter,
126138
- host: hostRouter,
126139
126161
  workers: hostsRouter,
126140
126162
  // Old name, kept for desktop, mobile and CLI builds that still call it.
126141
126163
  hosts: hostsRouter,
@@ -126187,7 +126209,9 @@ function discoverProcedures() {
126187
126209
  path2.startsWith("hooks."))
126188
126210
  continue;
126189
126211
  if (path2 === "worktree.previewUrl") continue;
126212
+ if (path2 === "worktree.downloadUrl") continue;
126190
126213
  if (path2 === "worktree.directAccess") continue;
126214
+ if (path2 === "worktree.readExternalFile" || path2 === "worktree.saveExternalFile") continue;
126191
126215
  if (path2 === "environment.validate") continue;
126192
126216
  if (path2 === "repos.addFromWorker") continue;
126193
126217
  if (path2 === "repos.addByUrl") continue;
@@ -127370,7 +127394,7 @@ function parseModeRequest(text4) {
127370
127394
  }
127371
127395
  return null;
127372
127396
  }
127373
- async function bridgePluginStream(ws, label2, open4, onOpenFailed) {
127397
+ async function bridgePluginStream(ws, label2, open3, onOpenFailed) {
127374
127398
  let controlling = false;
127375
127399
  let stream = null;
127376
127400
  ws.on("message", (data, isBinary) => {
@@ -127388,7 +127412,7 @@ async function bridgePluginStream(ws, label2, open4, onOpenFailed) {
127388
127412
  });
127389
127413
  ws.once("close", () => stream?.close());
127390
127414
  try {
127391
- stream = await open4();
127415
+ stream = await open3();
127392
127416
  } catch (err) {
127393
127417
  const message = err instanceof Error ? err.message : String(err);
127394
127418
  log87.warn("%s is unavailable: %s", label2, message);
@@ -128289,18 +128313,18 @@ async function bridgeRemote(ws, worktreeId4) {
128289
128313
  ws.removeAllListeners("message");
128290
128314
  return pending2;
128291
128315
  }
128292
- const open4 = cdp;
128316
+ const open3 = cdp;
128293
128317
  if (ws.readyState !== ws.OPEN) {
128294
- open4.close();
128318
+ open3.close();
128295
128319
  return true;
128296
128320
  }
128297
- for (const msg of pending2) open4.send(msg);
128321
+ for (const msg of pending2) open3.send(msg);
128298
128322
  pending2.length = 0;
128299
- ws.on("close", () => open4.close());
128300
- ws.on("error", () => open4.close());
128323
+ ws.on("close", () => open3.close());
128324
+ ws.on("error", () => open3.close());
128301
128325
  void (async () => {
128302
128326
  try {
128303
- for await (const message of open4.messages) {
128327
+ for await (const message of open3.messages) {
128304
128328
  if (ws.readyState === ws.OPEN) ws.send(message);
128305
128329
  }
128306
128330
  } catch (err) {
@@ -137508,6 +137532,39 @@ async function handleWorktreePreview(req, res) {
137508
137532
  isValidVisualEditNonce(nonce) ? nonce : void 0
137509
137533
  );
137510
137534
  }
137535
+ async function handleWorktreeDownload(req, res) {
137536
+ const [rest = ""] = (req.url ?? "").slice(WORKTREE_DOWNLOAD_PREFIX.length).split("?");
137537
+ const [rawId, grant, ...pathParts] = rest.split("/");
137538
+ let wId;
137539
+ let filePath;
137540
+ try {
137541
+ wId = decodeURIComponent(rawId ?? "");
137542
+ filePath = decodeURIComponent(pathParts.join("/"));
137543
+ } catch {
137544
+ res.writeHead(400);
137545
+ res.end("Bad request");
137546
+ return;
137547
+ }
137548
+ if (!wId || !grant || !filePath) {
137549
+ res.writeHead(400);
137550
+ res.end("Bad request");
137551
+ return;
137552
+ }
137553
+ if (!verifyWorktreeDownloadGrant(wId, grant, filePath)) {
137554
+ res.writeHead(403);
137555
+ res.end("Forbidden");
137556
+ return;
137557
+ }
137558
+ await serveWorktreeFile(
137559
+ res,
137560
+ wId,
137561
+ filePath,
137562
+ { "X-Content-Type-Options": "nosniff", "Cache-Control": "private, no-store" },
137563
+ void 0,
137564
+ true,
137565
+ req.headers.range
137566
+ );
137567
+ }
137511
137568
  var MAX_WEBHOOK_BODY_BYTES = 256 * 1024;
137512
137569
  var MAX_GITHUB_HOOK_BODY_BYTES = 2 * 1024 * 1024;
137513
137570
  async function handleGithubWebhook(req, res) {
@@ -137769,6 +137826,10 @@ async function main() {
137769
137826
  await handleWorktreePreview(req, res);
137770
137827
  return;
137771
137828
  }
137829
+ if (req.method === "GET" && req.url?.startsWith(WORKTREE_DOWNLOAD_PREFIX)) {
137830
+ await handleWorktreeDownload(req, res);
137831
+ return;
137832
+ }
137772
137833
  if (handleAuth(req, res)) return;
137773
137834
  if (req.url?.split("?")[0] === EXT_INSTALL_PATH) {
137774
137835
  await handleExtensionInstall(req, res, requestDevice(req).admin);