@band-app/server 0.54.4 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/_shell.html +0 -0
- package/dist/client/assets/{TerminalPanel-CxzXrgZc.js → TerminalPanel-Wv-LbAcs.js} +1 -1
- package/dist/client/assets/{_basePickBy-ESHGoJr1.js → _basePickBy-CgvoOFSS.js} +1 -1
- package/dist/client/assets/{_baseUniq-Caza5c4E.js → _baseUniq-DF-p-6fY.js} +1 -1
- package/dist/client/assets/{arc-DaZonhJY.js → arc-ukMuAfYV.js} +1 -1
- package/dist/client/assets/{architectureDiagram-VXUJARFQ-Cq7YbAnJ.js → architectureDiagram-VXUJARFQ-BHOsjgJ7.js} +1 -1
- package/dist/client/assets/{blockDiagram-VD42YOAC-C8s0ghb3.js → blockDiagram-VD42YOAC-V0Q-tFQE.js} +1 -1
- package/dist/client/assets/{c4Diagram-YG6GDRKO-DIBhFHGh.js → c4Diagram-YG6GDRKO-BK1nC-Zr.js} +1 -1
- package/dist/client/assets/channel-C1UOZUKN.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-D3SZ3P8V.js → chunk-4BX2VUAB-Cc2lXQih.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-BfA-9jtI.js → chunk-55IACEB6-ByQepK2K.js} +1 -1
- package/dist/client/assets/{chunk-B4BG7PRW-DAygCOyX.js → chunk-B4BG7PRW-BQQywZlE.js} +1 -1
- package/dist/client/assets/{chunk-DI55MBZ5-CuikvpNq.js → chunk-DI55MBZ5-C0Q1yI7a.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-JCmcJDsw.js → chunk-FMBD7UC4-G_Ta25S5.js} +1 -1
- package/dist/client/assets/{chunk-QN33PNHL-3o6_7ioT.js → chunk-QN33PNHL-vrj5lpjf.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-5B8nia1f.js → chunk-QZHKN3VN-DIoJiZ6Q.js} +1 -1
- package/dist/client/assets/{chunk-TZMSLE5B-stQp3cfp.js → chunk-TZMSLE5B-DbF2At2q.js} +1 -1
- package/dist/client/assets/classDiagram-2ON5EDUG-CBwXamAz.js +1 -0
- package/dist/client/assets/classDiagram-v2-WZHVMYZB-CBwXamAz.js +1 -0
- package/dist/client/assets/clone-CnowCdRV.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-CVQzgyKs.js → cose-bilkent-S5V4N54A-DmniYlnr.js} +1 -1
- package/dist/client/assets/{dagre-6UL2VRFP-C95ZZlB4.js → dagre-6UL2VRFP-xNi8WScD.js} +1 -1
- package/dist/client/assets/{diagram-PSM6KHXK-Sb4x5piz.js → diagram-PSM6KHXK-CL4dq7uj.js} +1 -1
- package/dist/client/assets/{diagram-QEK2KX5R-TTDYZins.js → diagram-QEK2KX5R-C7WQ7eoU.js} +1 -1
- package/dist/client/assets/{diagram-S2PKOQOG-C8joLdiY.js → diagram-S2PKOQOG-Cl6jL5JB.js} +1 -1
- package/dist/client/assets/{erDiagram-Q2GNP2WA-HXkzVcZE.js → erDiagram-Q2GNP2WA-DOCd-FW1.js} +1 -1
- package/dist/client/assets/{flowDiagram-NV44I4VS-BPzPMXG7.js → flowDiagram-NV44I4VS-BcjVCXzs.js} +1 -1
- package/dist/client/assets/{ganttDiagram-JELNMOA3-5JGkIYiO.js → ganttDiagram-JELNMOA3-Cp5IVV1X.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-V2S2FVAM-CDCkC2dq.js → gitGraphDiagram-V2S2FVAM-D81a2lqP.js} +1 -1
- package/dist/client/assets/{graph-CQ3VI3Kb.js → graph-DAQsyN1O.js} +1 -1
- package/dist/client/assets/{highlighted-body-B3W2YXNL-jA7b7ztt.js → highlighted-body-B3W2YXNL-BHdTpazT.js} +1 -1
- package/dist/client/assets/{index-C7Xj47iO.js → index-4wTCyGVZ.js} +1 -1
- package/dist/client/assets/{index-0DuoTbku.js → index-BIBgEJc_.js} +1 -1
- package/dist/client/assets/{index-F2GbFYex.js → index-BbUBghw-.js} +1 -1
- package/dist/client/assets/{index-BedjQBXV.js → index-C69wUmqS.js} +1 -1
- package/dist/client/assets/{index-CZqDBAUL.js → index-CKC2JTdn.js} +1 -1
- package/dist/client/assets/{index-DWepK3T1.js → index-CYW-8-2k.js} +1 -1
- package/dist/client/assets/{index-SjDPUTbU.js → index-CzW0lrV1.js} +1 -1
- package/dist/client/assets/{index-xEgVFQfI.js → index-D6mUuQso.js} +1 -1
- package/dist/client/assets/{index-B6FlIvZu.js → index-DCOA8K1E.js} +1 -1
- package/dist/client/assets/{index-Ce8SticC.js → index-DFnmEpf1.js} +1 -1
- package/dist/client/assets/{index-sTT9joMB.js → index-DcgU8Py3.js} +1 -1
- package/dist/client/assets/{index-Bcr3D2nL.js → index-DdbCWPks.js} +1 -1
- package/dist/client/assets/{index-Dt3jdJaQ.js → index-U7nQzvUZ.js} +1 -1
- package/dist/client/assets/{infoDiagram-HS3SLOUP-DXlzs7_8.js → infoDiagram-HS3SLOUP-Cm5s9O89.js} +1 -1
- package/dist/client/assets/{journeyDiagram-XKPGCS4Q-B7mdzrEf.js → journeyDiagram-XKPGCS4Q-BEmfY1L5.js} +1 -1
- package/dist/client/assets/{kanban-definition-3W4ZIXB7-C3yAE4ky.js → kanban-definition-3W4ZIXB7-BR4wUQUP.js} +1 -1
- package/dist/client/assets/{layout-BpyKMBbb.js → layout-eg8UUTsn.js} +1 -1
- package/dist/client/assets/main-BxvTwvKU.css +1 -0
- package/dist/client/assets/{main-B169v3Mr.js → main-lic-ksql.js} +339 -339
- package/dist/client/assets/{mindmap-definition-VGOIOE7T-BV1ylQ_o.js → mindmap-definition-VGOIOE7T-aO87fIIJ.js} +1 -1
- package/dist/client/assets/{pieDiagram-ADFJNKIX-C68Mdt_x.js → pieDiagram-ADFJNKIX-D1-R_4cR.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-AYHSOK5B-5wmdYa_y.js → quadrantDiagram-AYHSOK5B-DG_jZYS7.js} +1 -1
- package/dist/client/assets/{requirementDiagram-UZGBJVZJ-CXCtBgk5.js → requirementDiagram-UZGBJVZJ-BtxpIvi0.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-TZEHDZUN-CwuKYu6l.js → sankeyDiagram-TZEHDZUN-CEZw5ngO.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-WL72ISMW-BwFU-IAs.js → sequenceDiagram-WL72ISMW-Da7IJD8T.js} +1 -1
- package/dist/client/assets/{stateDiagram-FKZM4ZOC-B46B2gqH.js → stateDiagram-FKZM4ZOC-D9MZaz-Z.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-4FDKWEC3-BZauII8k.js +1 -0
- package/dist/client/assets/{timeline-definition-IT6M3QCI-DHJrxfmL.js → timeline-definition-IT6M3QCI-Dpy1CJBt.js} +1 -1
- package/dist/client/assets/{treemap-GDKQZRPO-Bn3yodwG.js → treemap-GDKQZRPO-CwxFs7qg.js} +1 -1
- package/dist/client/assets/{worktree._worktreeId-7oupLNxr.js → worktree._worktreeId-BrMMD4KZ.js} +1 -1
- package/dist/client/assets/{worktree._worktreeId-MU4bksud.js → worktree._worktreeId-D8tRL5dj.js} +1 -1
- package/dist/client/assets/{xychartDiagram-PRI3JC2R-DUqpx4dY.js → xychartDiagram-PRI3JC2R-C42Fu89p.js} +1 -1
- package/dist/openapi.json +1192 -40
- package/dist/start-server.mjs +61895 -59586
- package/dist/terminal-daemon.mjs +301 -283
- package/package.json +10 -9
- package/dist/client/assets/channel-D_oNvIqF.js +0 -1
- package/dist/client/assets/classDiagram-2ON5EDUG-C8EJzaAq.js +0 -1
- package/dist/client/assets/classDiagram-v2-WZHVMYZB-C8EJzaAq.js +0 -1
- package/dist/client/assets/clone-BhU1fGlA.js +0 -1
- package/dist/client/assets/main-DWmz5QXO.css +0 -1
- package/dist/client/assets/stateDiagram-v2-4FDKWEC3-BvDRMNEQ.js +0 -1
package/dist/terminal-daemon.mjs
CHANGED
|
@@ -8929,11 +8929,268 @@ function createLogger(name) {
|
|
|
8929
8929
|
}
|
|
8930
8930
|
|
|
8931
8931
|
// ../../packages/host-local/src/terminals/daemon/daemon-server.ts
|
|
8932
|
-
import { randomBytes as
|
|
8933
|
-
import { chmodSync as chmodSync2, lstatSync as lstatSync2
|
|
8932
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
8933
|
+
import { chmodSync as chmodSync2, lstatSync as lstatSync2 } from "node:fs";
|
|
8934
8934
|
import { createServer } from "node:net";
|
|
8935
8935
|
import { dirname as dirname4, join as join7 } from "node:path";
|
|
8936
8936
|
|
|
8937
|
+
// ../../packages/host-local/src/daemon-kit/endpoint.ts
|
|
8938
|
+
import { randomBytes } from "node:crypto";
|
|
8939
|
+
import {
|
|
8940
|
+
chmodSync,
|
|
8941
|
+
linkSync,
|
|
8942
|
+
lstatSync,
|
|
8943
|
+
mkdirSync as mkdirSync2,
|
|
8944
|
+
renameSync as renameSync2,
|
|
8945
|
+
statSync as statSync2,
|
|
8946
|
+
unlinkSync
|
|
8947
|
+
} from "node:fs";
|
|
8948
|
+
import { connect } from "node:net";
|
|
8949
|
+
import { dirname as dirname2, join } from "node:path";
|
|
8950
|
+
var PROBE_TIMEOUT_MS = 500;
|
|
8951
|
+
var PUBLISH_ATTEMPTS = 3;
|
|
8952
|
+
function isProvenDead(outcome) {
|
|
8953
|
+
return outcome === "missing" || outcome === "refused";
|
|
8954
|
+
}
|
|
8955
|
+
function probeEndpoint(socketPath) {
|
|
8956
|
+
let occupied = false;
|
|
8957
|
+
try {
|
|
8958
|
+
lstatSync(socketPath);
|
|
8959
|
+
occupied = true;
|
|
8960
|
+
} catch (err) {
|
|
8961
|
+
return Promise.resolve(errorCode(err) === "ENOENT" ? "missing" : "unknown");
|
|
8962
|
+
}
|
|
8963
|
+
return new Promise((resolve) => {
|
|
8964
|
+
const socket = connect({ path: socketPath });
|
|
8965
|
+
let settled = false;
|
|
8966
|
+
const settle = (outcome) => {
|
|
8967
|
+
if (settled) return;
|
|
8968
|
+
settled = true;
|
|
8969
|
+
clearTimeout(timer);
|
|
8970
|
+
socket.destroy();
|
|
8971
|
+
resolve(outcome);
|
|
8972
|
+
};
|
|
8973
|
+
const timer = setTimeout(() => settle("unknown"), PROBE_TIMEOUT_MS);
|
|
8974
|
+
socket.once("connect", () => settle("connected"));
|
|
8975
|
+
socket.once("error", (err) => {
|
|
8976
|
+
const code = errorCode(err);
|
|
8977
|
+
if (code === "ECONNREFUSED" || code === "ENOTSOCK") settle("refused");
|
|
8978
|
+
else if (code === "ENOENT") settle(occupied ? "refused" : "missing");
|
|
8979
|
+
else settle("unknown");
|
|
8980
|
+
});
|
|
8981
|
+
});
|
|
8982
|
+
}
|
|
8983
|
+
function privateBindPath(socketPath) {
|
|
8984
|
+
return join(dirname2(socketPath), `.p${randomBytes(5).toString("hex")}`);
|
|
8985
|
+
}
|
|
8986
|
+
async function publishEndpoint(boundPath, canonicalPath, supersede) {
|
|
8987
|
+
const identity = entryIdentity(boundPath, statSync2);
|
|
8988
|
+
if (!identity) throw new Error(`Cannot stat the bound daemon socket at ${boundPath}`);
|
|
8989
|
+
for (let attempt = 0; attempt < PUBLISH_ATTEMPTS; attempt++) {
|
|
8990
|
+
try {
|
|
8991
|
+
linkSync(boundPath, canonicalPath);
|
|
8992
|
+
} catch (err) {
|
|
8993
|
+
if (errorCode(err) !== "EEXIST") throw err;
|
|
8994
|
+
if (supersede && retireIncumbent(canonicalPath, supersede)) {
|
|
8995
|
+
renameSync2(boundPath, canonicalPath);
|
|
8996
|
+
return confirmPublished(canonicalPath, identity, true);
|
|
8997
|
+
}
|
|
8998
|
+
const replaced = await replaceProvenDead(boundPath, canonicalPath);
|
|
8999
|
+
if (replaced === "stale") continue;
|
|
9000
|
+
if (replaced !== "renamed") return replaced;
|
|
9001
|
+
return confirmPublished(canonicalPath, identity, false);
|
|
9002
|
+
}
|
|
9003
|
+
try {
|
|
9004
|
+
unlinkSync(boundPath);
|
|
9005
|
+
} catch {
|
|
9006
|
+
}
|
|
9007
|
+
return confirmPublished(canonicalPath, identity, false);
|
|
9008
|
+
}
|
|
9009
|
+
return { status: "inconclusive" };
|
|
9010
|
+
}
|
|
9011
|
+
async function replaceProvenDead(boundPath, canonicalPath) {
|
|
9012
|
+
const proven = entryIdentity(canonicalPath, lstatSync);
|
|
9013
|
+
const first = await probeEndpoint(canonicalPath);
|
|
9014
|
+
if (first === "connected") return { status: "occupied" };
|
|
9015
|
+
if (!isProvenDead(first)) return { status: "inconclusive" };
|
|
9016
|
+
const now = entryIdentity(canonicalPath, lstatSync);
|
|
9017
|
+
if (!proven || !now || proven.dev !== now.dev || proven.ino !== now.ino) return "stale";
|
|
9018
|
+
const second = await probeEndpoint(canonicalPath);
|
|
9019
|
+
if (second === "connected") return { status: "occupied" };
|
|
9020
|
+
if (!isProvenDead(second)) return { status: "inconclusive" };
|
|
9021
|
+
renameSync2(boundPath, canonicalPath);
|
|
9022
|
+
return "renamed";
|
|
9023
|
+
}
|
|
9024
|
+
function retireIncumbent(canonicalPath, supersede) {
|
|
9025
|
+
if (!sameEntry(entryIdentity(canonicalPath, lstatSync), supersede.incumbent)) return false;
|
|
9026
|
+
try {
|
|
9027
|
+
linkSync(canonicalPath, supersede.retiredPath);
|
|
9028
|
+
} catch {
|
|
9029
|
+
return false;
|
|
9030
|
+
}
|
|
9031
|
+
if (sameEntry(entryIdentity(supersede.retiredPath, lstatSync), supersede.incumbent)) return true;
|
|
9032
|
+
try {
|
|
9033
|
+
unlinkSync(supersede.retiredPath);
|
|
9034
|
+
} catch {
|
|
9035
|
+
}
|
|
9036
|
+
return false;
|
|
9037
|
+
}
|
|
9038
|
+
function sameEntry(a, b) {
|
|
9039
|
+
return a !== null && a.dev === b.dev && a.ino === b.ino;
|
|
9040
|
+
}
|
|
9041
|
+
function confirmPublished(canonicalPath, identity, superseded) {
|
|
9042
|
+
let published;
|
|
9043
|
+
try {
|
|
9044
|
+
const stats = statSync2(canonicalPath, { bigint: true });
|
|
9045
|
+
published = { dev: stats.dev, ino: stats.ino };
|
|
9046
|
+
} catch (err) {
|
|
9047
|
+
return errorCode(err) === "ENOENT" ? { status: "lost" } : { status: "inconclusive" };
|
|
9048
|
+
}
|
|
9049
|
+
return published.dev === identity.dev && published.ino === identity.ino ? { status: "published", identity: published, superseded } : { status: "lost" };
|
|
9050
|
+
}
|
|
9051
|
+
function endpointOwnership(canonicalPath, owned) {
|
|
9052
|
+
try {
|
|
9053
|
+
const stats = statSync2(canonicalPath, { bigint: true });
|
|
9054
|
+
return stats.dev === owned.dev && stats.ino === owned.ino ? "owned" : "lost";
|
|
9055
|
+
} catch (err) {
|
|
9056
|
+
return errorCode(err) === "ENOENT" ? "lost" : "indeterminate";
|
|
9057
|
+
}
|
|
9058
|
+
}
|
|
9059
|
+
function ensurePrivateDir(dir) {
|
|
9060
|
+
mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
9061
|
+
const stats = lstatSync(dir);
|
|
9062
|
+
const uid = process.getuid?.();
|
|
9063
|
+
if (!stats.isDirectory()) throw new Error(`${dir} is not a directory`);
|
|
9064
|
+
if (uid !== void 0 && stats.uid !== uid) throw new Error(`${dir} is owned by another user`);
|
|
9065
|
+
if ((stats.mode & 63) !== 0) chmodSync(dir, 448);
|
|
9066
|
+
}
|
|
9067
|
+
function entryIdentity(path, stat) {
|
|
9068
|
+
try {
|
|
9069
|
+
const stats = stat(path, { bigint: true });
|
|
9070
|
+
return { dev: stats.dev, ino: stats.ino };
|
|
9071
|
+
} catch {
|
|
9072
|
+
return null;
|
|
9073
|
+
}
|
|
9074
|
+
}
|
|
9075
|
+
function errorCode(err) {
|
|
9076
|
+
return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
9077
|
+
}
|
|
9078
|
+
|
|
9079
|
+
// ../../packages/host-local/src/daemon-kit/private-files.ts
|
|
9080
|
+
import { randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
9081
|
+
import { readFileSync, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
|
|
9082
|
+
function writePrivateFile(path, content) {
|
|
9083
|
+
const tmp = `${path}.${randomBytes2(4).toString("hex")}.tmp`;
|
|
9084
|
+
writeFileSync(tmp, content, { mode: 384 });
|
|
9085
|
+
renameSync3(tmp, path);
|
|
9086
|
+
}
|
|
9087
|
+
function copyPrivateFile(from, to) {
|
|
9088
|
+
let content;
|
|
9089
|
+
try {
|
|
9090
|
+
content = readFileSync(from, "utf8");
|
|
9091
|
+
} catch {
|
|
9092
|
+
return;
|
|
9093
|
+
}
|
|
9094
|
+
writePrivateFile(to, content);
|
|
9095
|
+
}
|
|
9096
|
+
function removeRetiredFiles(retired) {
|
|
9097
|
+
for (const path of [retired.socket, retired.token, retired.pid]) {
|
|
9098
|
+
try {
|
|
9099
|
+
unlinkSync2(path);
|
|
9100
|
+
} catch {
|
|
9101
|
+
}
|
|
9102
|
+
}
|
|
9103
|
+
}
|
|
9104
|
+
function tokenMatches(expected, candidate) {
|
|
9105
|
+
const a = Buffer.from(expected);
|
|
9106
|
+
const b = Buffer.from(candidate);
|
|
9107
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
9108
|
+
}
|
|
9109
|
+
|
|
9110
|
+
// ../../packages/host-local/src/daemon-kit/protocol.ts
|
|
9111
|
+
import { createHash } from "node:crypto";
|
|
9112
|
+
import { basename, dirname as dirname3, join as join2 } from "node:path";
|
|
9113
|
+
var EXIT_ENDPOINT_OCCUPIED = 20;
|
|
9114
|
+
var EXIT_ENDPOINT_UNAVAILABLE = 21;
|
|
9115
|
+
var MAX_SOCKET_PATH_BYTES = 103;
|
|
9116
|
+
function daemonPathsFor(spec) {
|
|
9117
|
+
const { runDir, name, shortPrefix, version } = spec;
|
|
9118
|
+
const inRunDir = join2(runDir, `${name}-v${version}.sock`);
|
|
9119
|
+
const socket = Buffer.byteLength(inRunDir) <= MAX_SOCKET_PATH_BYTES ? inRunDir : join2(
|
|
9120
|
+
shortSocketDir(),
|
|
9121
|
+
`${shortPrefix}-${createHash("sha256").update(runDir).digest("hex").slice(0, 16)}-v${version}.sock`
|
|
9122
|
+
);
|
|
9123
|
+
return {
|
|
9124
|
+
runDir,
|
|
9125
|
+
socket,
|
|
9126
|
+
token: join2(runDir, `${name}-v${version}.token`),
|
|
9127
|
+
pid: join2(runDir, `${name}-v${version}.pid`),
|
|
9128
|
+
log: join2(runDir, `${name}.log`)
|
|
9129
|
+
};
|
|
9130
|
+
}
|
|
9131
|
+
function retiredDaemonPaths(paths, tag) {
|
|
9132
|
+
return {
|
|
9133
|
+
...paths,
|
|
9134
|
+
socket: join2(dirname3(paths.socket), `.r${tag}`),
|
|
9135
|
+
token: paths.token.replace(/\.token$/, `.retired-${tag}.token`),
|
|
9136
|
+
pid: paths.pid.replace(/\.pid$/, `.retired-${tag}.pid`)
|
|
9137
|
+
};
|
|
9138
|
+
}
|
|
9139
|
+
function shortSocketDir() {
|
|
9140
|
+
return join2("/tmp", `band-${process.getuid?.() ?? "user"}`);
|
|
9141
|
+
}
|
|
9142
|
+
function parseHello(frame) {
|
|
9143
|
+
if (typeof frame !== "object" || frame === null) return null;
|
|
9144
|
+
const hello = frame;
|
|
9145
|
+
if (hello.t !== "hello" || typeof hello.protocol !== "number" || typeof hello.token !== "string" || hello.role !== "control" && hello.role !== "stream" || typeof hello.clientId !== "string" || typeof hello.buildId !== "string") {
|
|
9146
|
+
return null;
|
|
9147
|
+
}
|
|
9148
|
+
return hello;
|
|
9149
|
+
}
|
|
9150
|
+
function writeFrame(socket, message) {
|
|
9151
|
+
socket.write(`${JSON.stringify(message)}
|
|
9152
|
+
`);
|
|
9153
|
+
}
|
|
9154
|
+
function readFrames(socket, onFrame, maxLineBytes = 64 * 1024 * 1024) {
|
|
9155
|
+
let chunks = [];
|
|
9156
|
+
let pendingBytes = 0;
|
|
9157
|
+
socket.setEncoding("utf8");
|
|
9158
|
+
socket.on("data", (chunk) => {
|
|
9159
|
+
if (!chunk.includes("\n")) {
|
|
9160
|
+
chunks.push(chunk);
|
|
9161
|
+
pendingBytes += chunk.length;
|
|
9162
|
+
if (pendingBytes > maxLineBytes) socket.destroy(new Error("Frame too large"));
|
|
9163
|
+
return;
|
|
9164
|
+
}
|
|
9165
|
+
chunks.push(chunk);
|
|
9166
|
+
let buffer = chunks.length === 1 ? chunk : chunks.join("");
|
|
9167
|
+
chunks = [];
|
|
9168
|
+
pendingBytes = 0;
|
|
9169
|
+
let newline = buffer.indexOf("\n");
|
|
9170
|
+
while (newline !== -1) {
|
|
9171
|
+
const line = buffer.slice(0, newline);
|
|
9172
|
+
buffer = buffer.slice(newline + 1);
|
|
9173
|
+
if (line.length > 0) {
|
|
9174
|
+
let frame;
|
|
9175
|
+
try {
|
|
9176
|
+
frame = JSON.parse(line);
|
|
9177
|
+
} catch {
|
|
9178
|
+
socket.destroy(new Error("Malformed frame"));
|
|
9179
|
+
return;
|
|
9180
|
+
}
|
|
9181
|
+
onFrame(frame);
|
|
9182
|
+
if (socket.destroyed) return;
|
|
9183
|
+
}
|
|
9184
|
+
newline = buffer.indexOf("\n");
|
|
9185
|
+
}
|
|
9186
|
+
if (buffer.length > 0) {
|
|
9187
|
+
chunks.push(buffer);
|
|
9188
|
+
pendingBytes = buffer.length;
|
|
9189
|
+
if (pendingBytes > maxLineBytes) socket.destroy(new Error("Frame too large"));
|
|
9190
|
+
}
|
|
9191
|
+
});
|
|
9192
|
+
}
|
|
9193
|
+
|
|
8937
9194
|
// ../../packages/host-local/src/terminals/node-pty-loader.ts
|
|
8938
9195
|
var cachedImport;
|
|
8939
9196
|
function preloadNodePty() {
|
|
@@ -8943,9 +9200,9 @@ function preloadNodePty() {
|
|
|
8943
9200
|
|
|
8944
9201
|
// ../../packages/host-local/src/terminals/terminal-pool.ts
|
|
8945
9202
|
import { randomUUID } from "node:crypto";
|
|
8946
|
-
import { existsSync as existsSync3, unlinkSync, writeFileSync } from "node:fs";
|
|
9203
|
+
import { existsSync as existsSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8947
9204
|
import { tmpdir } from "node:os";
|
|
8948
|
-
import { join as
|
|
9205
|
+
import { join as join5, sep } from "node:path";
|
|
8949
9206
|
|
|
8950
9207
|
// ../../packages/host-local/src/process/path.ts
|
|
8951
9208
|
import { execFile } from "node:child_process";
|
|
@@ -9008,9 +9265,9 @@ async function shellPath() {
|
|
|
9008
9265
|
}
|
|
9009
9266
|
|
|
9010
9267
|
// ../../packages/host-local/src/terminals/claude-resume.ts
|
|
9011
|
-
import { readdirSync, statSync as
|
|
9268
|
+
import { readdirSync, statSync as statSync3 } from "node:fs";
|
|
9012
9269
|
import { homedir } from "node:os";
|
|
9013
|
-
import { join } from "node:path";
|
|
9270
|
+
import { join as join3 } from "node:path";
|
|
9014
9271
|
|
|
9015
9272
|
// ../../packages/shared/src/claude-session.ts
|
|
9016
9273
|
function claudeProjectSlug(cwd) {
|
|
@@ -9027,7 +9284,7 @@ function isResumableClaudeCommand(command) {
|
|
|
9027
9284
|
return CLAUDE_COMMAND_PATTERN.test(trimmed) && !CLAUDE_RESUME_FLAG_PATTERN.test(trimmed);
|
|
9028
9285
|
}
|
|
9029
9286
|
function claudeProjectDir(cwd) {
|
|
9030
|
-
return
|
|
9287
|
+
return join3(homedir(), ".claude", "projects", claudeProjectSlug(cwd));
|
|
9031
9288
|
}
|
|
9032
9289
|
function findLatestClaudeSessionId(cwd) {
|
|
9033
9290
|
const dir = claudeProjectDir(cwd);
|
|
@@ -9041,7 +9298,7 @@ function findLatestClaudeSessionId(cwd) {
|
|
|
9041
9298
|
for (const name of names) {
|
|
9042
9299
|
let mtimeMs;
|
|
9043
9300
|
try {
|
|
9044
|
-
mtimeMs =
|
|
9301
|
+
mtimeMs = statSync3(join3(dir, name)).mtimeMs;
|
|
9045
9302
|
} catch {
|
|
9046
9303
|
continue;
|
|
9047
9304
|
}
|
|
@@ -9071,9 +9328,9 @@ function hintForPtySpawnError(err) {
|
|
|
9071
9328
|
}
|
|
9072
9329
|
|
|
9073
9330
|
// ../../packages/host-local/src/terminals/terminal-history.ts
|
|
9074
|
-
import { readdirSync as readdirSync2, readFileSync, rmSync as rmSync2 } from "node:fs";
|
|
9331
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2 } from "node:fs";
|
|
9075
9332
|
import { mkdir, rename, writeFile } from "node:fs/promises";
|
|
9076
|
-
import { join as
|
|
9333
|
+
import { join as join4 } from "node:path";
|
|
9077
9334
|
var log = createLogger("terminal-history");
|
|
9078
9335
|
var CHECKPOINT_MAX_BYTES = 5 * 1024 * 1024;
|
|
9079
9336
|
var TerminalHistoryManager = class {
|
|
@@ -9081,14 +9338,14 @@ var TerminalHistoryManager = class {
|
|
|
9081
9338
|
this.historyDir = historyDir;
|
|
9082
9339
|
}
|
|
9083
9340
|
sessionDir(terminalId) {
|
|
9084
|
-
return
|
|
9341
|
+
return join4(this.historyDir, encodeURIComponent(terminalId));
|
|
9085
9342
|
}
|
|
9086
9343
|
readMeta(terminalId) {
|
|
9087
|
-
return readJson(
|
|
9344
|
+
return readJson(join4(this.sessionDir(terminalId), "meta.json"));
|
|
9088
9345
|
}
|
|
9089
9346
|
readCheckpoint(terminalId) {
|
|
9090
9347
|
return readJson(
|
|
9091
|
-
|
|
9348
|
+
join4(this.sessionDir(terminalId), "checkpoint.json")
|
|
9092
9349
|
);
|
|
9093
9350
|
}
|
|
9094
9351
|
/**
|
|
@@ -9134,8 +9391,8 @@ var TerminalHistoryManager = class {
|
|
|
9134
9391
|
return;
|
|
9135
9392
|
}
|
|
9136
9393
|
for (const name of names) {
|
|
9137
|
-
const dir =
|
|
9138
|
-
const meta = readJson(
|
|
9394
|
+
const dir = join4(this.historyDir, name);
|
|
9395
|
+
const meta = readJson(join4(dir, "meta.json"));
|
|
9139
9396
|
if (meta?.worktreeId !== worktreeId) continue;
|
|
9140
9397
|
try {
|
|
9141
9398
|
rmSync2(dir, { recursive: true, force: true });
|
|
@@ -9147,7 +9404,7 @@ var TerminalHistoryManager = class {
|
|
|
9147
9404
|
};
|
|
9148
9405
|
function readJson(path) {
|
|
9149
9406
|
try {
|
|
9150
|
-
return JSON.parse(
|
|
9407
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
9151
9408
|
} catch {
|
|
9152
9409
|
return null;
|
|
9153
9410
|
}
|
|
@@ -9155,7 +9412,7 @@ function readJson(path) {
|
|
|
9155
9412
|
async function writeJson(dir, name, value, serialized) {
|
|
9156
9413
|
try {
|
|
9157
9414
|
await mkdir(dir, { recursive: true, mode: 448 });
|
|
9158
|
-
const path =
|
|
9415
|
+
const path = join4(dir, name);
|
|
9159
9416
|
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
9160
9417
|
await writeFile(tmp, serialized ?? JSON.stringify(value), { mode: 384 });
|
|
9161
9418
|
await rename(tmp, path);
|
|
@@ -9303,7 +9560,7 @@ var TerminalPool = class {
|
|
|
9303
9560
|
env.BAND_WORKTREE_ID = worktreeId;
|
|
9304
9561
|
let cwd = worktreeRoot;
|
|
9305
9562
|
if (options?.cwd) {
|
|
9306
|
-
cwd = resolveSafeCwd(
|
|
9563
|
+
cwd = resolveSafeCwd(join5(worktreeRoot, options.cwd), worktreeRoot, "cwd", options.cwd) ?? cwd;
|
|
9307
9564
|
} else if (checkpoint?.cwd) {
|
|
9308
9565
|
cwd = resolveSafeCwd(
|
|
9309
9566
|
checkpoint.cwd,
|
|
@@ -9434,7 +9691,7 @@ var TerminalPool = class {
|
|
|
9434
9691
|
}
|
|
9435
9692
|
if (session.autoRunFile) {
|
|
9436
9693
|
try {
|
|
9437
|
-
|
|
9694
|
+
unlinkSync3(session.autoRunFile);
|
|
9438
9695
|
} catch {
|
|
9439
9696
|
}
|
|
9440
9697
|
}
|
|
@@ -9568,8 +9825,8 @@ var TerminalPool = class {
|
|
|
9568
9825
|
const pty = session.pty;
|
|
9569
9826
|
let cmdFile;
|
|
9570
9827
|
try {
|
|
9571
|
-
cmdFile =
|
|
9572
|
-
|
|
9828
|
+
cmdFile = join5(tmpdir(), `band-autorun-${randomUUID()}.sh`);
|
|
9829
|
+
writeFileSync2(cmdFile, `${command}
|
|
9573
9830
|
`, { encoding: "utf-8", mode: 384, flag: "wx" });
|
|
9574
9831
|
session.autoRunFile = cmdFile;
|
|
9575
9832
|
} catch (err) {
|
|
@@ -9972,247 +10229,40 @@ function resolveSafeCwd(candidate, worktreeRoot, label, original) {
|
|
|
9972
10229
|
return candidate;
|
|
9973
10230
|
}
|
|
9974
10231
|
|
|
9975
|
-
// ../../packages/host-local/src/
|
|
9976
|
-
import { randomBytes } from "node:crypto";
|
|
9977
|
-
import {
|
|
9978
|
-
chmodSync,
|
|
9979
|
-
linkSync,
|
|
9980
|
-
lstatSync,
|
|
9981
|
-
mkdirSync as mkdirSync2,
|
|
9982
|
-
renameSync as renameSync2,
|
|
9983
|
-
statSync as statSync3,
|
|
9984
|
-
unlinkSync as unlinkSync2
|
|
9985
|
-
} from "node:fs";
|
|
9986
|
-
import { connect } from "node:net";
|
|
9987
|
-
import { dirname as dirname2, join as join4 } from "node:path";
|
|
9988
|
-
var PROBE_TIMEOUT_MS = 500;
|
|
9989
|
-
var PUBLISH_ATTEMPTS = 3;
|
|
9990
|
-
function isProvenDead(outcome) {
|
|
9991
|
-
return outcome === "missing" || outcome === "refused";
|
|
9992
|
-
}
|
|
9993
|
-
function probeEndpoint(socketPath) {
|
|
9994
|
-
let occupied = false;
|
|
9995
|
-
try {
|
|
9996
|
-
lstatSync(socketPath);
|
|
9997
|
-
occupied = true;
|
|
9998
|
-
} catch (err) {
|
|
9999
|
-
return Promise.resolve(errorCode(err) === "ENOENT" ? "missing" : "unknown");
|
|
10000
|
-
}
|
|
10001
|
-
return new Promise((resolve) => {
|
|
10002
|
-
const socket = connect({ path: socketPath });
|
|
10003
|
-
let settled = false;
|
|
10004
|
-
const settle = (outcome) => {
|
|
10005
|
-
if (settled) return;
|
|
10006
|
-
settled = true;
|
|
10007
|
-
clearTimeout(timer);
|
|
10008
|
-
socket.destroy();
|
|
10009
|
-
resolve(outcome);
|
|
10010
|
-
};
|
|
10011
|
-
const timer = setTimeout(() => settle("unknown"), PROBE_TIMEOUT_MS);
|
|
10012
|
-
socket.once("connect", () => settle("connected"));
|
|
10013
|
-
socket.once("error", (err) => {
|
|
10014
|
-
const code = errorCode(err);
|
|
10015
|
-
if (code === "ECONNREFUSED" || code === "ENOTSOCK") settle("refused");
|
|
10016
|
-
else if (code === "ENOENT") settle(occupied ? "refused" : "missing");
|
|
10017
|
-
else settle("unknown");
|
|
10018
|
-
});
|
|
10019
|
-
});
|
|
10020
|
-
}
|
|
10021
|
-
function privateBindPath(socketPath) {
|
|
10022
|
-
return join4(dirname2(socketPath), `.p${randomBytes(5).toString("hex")}`);
|
|
10023
|
-
}
|
|
10024
|
-
async function publishEndpoint(boundPath, canonicalPath, supersede) {
|
|
10025
|
-
const identity = entryIdentity(boundPath, statSync3);
|
|
10026
|
-
if (!identity) throw new Error(`Cannot stat the bound daemon socket at ${boundPath}`);
|
|
10027
|
-
for (let attempt = 0; attempt < PUBLISH_ATTEMPTS; attempt++) {
|
|
10028
|
-
try {
|
|
10029
|
-
linkSync(boundPath, canonicalPath);
|
|
10030
|
-
} catch (err) {
|
|
10031
|
-
if (errorCode(err) !== "EEXIST") throw err;
|
|
10032
|
-
if (supersede && retireIncumbent(canonicalPath, supersede)) {
|
|
10033
|
-
renameSync2(boundPath, canonicalPath);
|
|
10034
|
-
return confirmPublished(canonicalPath, identity, true);
|
|
10035
|
-
}
|
|
10036
|
-
const replaced = await replaceProvenDead(boundPath, canonicalPath);
|
|
10037
|
-
if (replaced === "stale") continue;
|
|
10038
|
-
if (replaced !== "renamed") return replaced;
|
|
10039
|
-
return confirmPublished(canonicalPath, identity, false);
|
|
10040
|
-
}
|
|
10041
|
-
try {
|
|
10042
|
-
unlinkSync2(boundPath);
|
|
10043
|
-
} catch {
|
|
10044
|
-
}
|
|
10045
|
-
return confirmPublished(canonicalPath, identity, false);
|
|
10046
|
-
}
|
|
10047
|
-
return { status: "inconclusive" };
|
|
10048
|
-
}
|
|
10049
|
-
async function replaceProvenDead(boundPath, canonicalPath) {
|
|
10050
|
-
const proven = entryIdentity(canonicalPath, lstatSync);
|
|
10051
|
-
const first = await probeEndpoint(canonicalPath);
|
|
10052
|
-
if (first === "connected") return { status: "occupied" };
|
|
10053
|
-
if (!isProvenDead(first)) return { status: "inconclusive" };
|
|
10054
|
-
const now = entryIdentity(canonicalPath, lstatSync);
|
|
10055
|
-
if (!proven || !now || proven.dev !== now.dev || proven.ino !== now.ino) return "stale";
|
|
10056
|
-
const second = await probeEndpoint(canonicalPath);
|
|
10057
|
-
if (second === "connected") return { status: "occupied" };
|
|
10058
|
-
if (!isProvenDead(second)) return { status: "inconclusive" };
|
|
10059
|
-
renameSync2(boundPath, canonicalPath);
|
|
10060
|
-
return "renamed";
|
|
10061
|
-
}
|
|
10062
|
-
function retireIncumbent(canonicalPath, supersede) {
|
|
10063
|
-
if (!sameEntry(entryIdentity(canonicalPath, lstatSync), supersede.incumbent)) return false;
|
|
10064
|
-
try {
|
|
10065
|
-
linkSync(canonicalPath, supersede.retiredPath);
|
|
10066
|
-
} catch {
|
|
10067
|
-
return false;
|
|
10068
|
-
}
|
|
10069
|
-
if (sameEntry(entryIdentity(supersede.retiredPath, lstatSync), supersede.incumbent)) return true;
|
|
10070
|
-
try {
|
|
10071
|
-
unlinkSync2(supersede.retiredPath);
|
|
10072
|
-
} catch {
|
|
10073
|
-
}
|
|
10074
|
-
return false;
|
|
10075
|
-
}
|
|
10076
|
-
function sameEntry(a, b) {
|
|
10077
|
-
return a !== null && a.dev === b.dev && a.ino === b.ino;
|
|
10078
|
-
}
|
|
10079
|
-
function confirmPublished(canonicalPath, identity, superseded) {
|
|
10080
|
-
let published;
|
|
10081
|
-
try {
|
|
10082
|
-
const stats = statSync3(canonicalPath, { bigint: true });
|
|
10083
|
-
published = { dev: stats.dev, ino: stats.ino };
|
|
10084
|
-
} catch (err) {
|
|
10085
|
-
return errorCode(err) === "ENOENT" ? { status: "lost" } : { status: "inconclusive" };
|
|
10086
|
-
}
|
|
10087
|
-
return published.dev === identity.dev && published.ino === identity.ino ? { status: "published", identity: published, superseded } : { status: "lost" };
|
|
10088
|
-
}
|
|
10089
|
-
function endpointOwnership(canonicalPath, owned) {
|
|
10090
|
-
try {
|
|
10091
|
-
const stats = statSync3(canonicalPath, { bigint: true });
|
|
10092
|
-
return stats.dev === owned.dev && stats.ino === owned.ino ? "owned" : "lost";
|
|
10093
|
-
} catch (err) {
|
|
10094
|
-
return errorCode(err) === "ENOENT" ? "lost" : "indeterminate";
|
|
10095
|
-
}
|
|
10096
|
-
}
|
|
10097
|
-
function ensurePrivateDir(dir) {
|
|
10098
|
-
mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
10099
|
-
const stats = lstatSync(dir);
|
|
10100
|
-
const uid = process.getuid?.();
|
|
10101
|
-
if (!stats.isDirectory()) throw new Error(`${dir} is not a directory`);
|
|
10102
|
-
if (uid !== void 0 && stats.uid !== uid) throw new Error(`${dir} is owned by another user`);
|
|
10103
|
-
if ((stats.mode & 63) !== 0) chmodSync(dir, 448);
|
|
10104
|
-
}
|
|
10105
|
-
function entryIdentity(path, stat) {
|
|
10106
|
-
try {
|
|
10107
|
-
const stats = stat(path, { bigint: true });
|
|
10108
|
-
return { dev: stats.dev, ino: stats.ino };
|
|
10109
|
-
} catch {
|
|
10110
|
-
return null;
|
|
10111
|
-
}
|
|
10112
|
-
}
|
|
10113
|
-
function errorCode(err) {
|
|
10114
|
-
return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
10115
|
-
}
|
|
10116
|
-
|
|
10117
|
-
// ../../packages/host-local/src/terminals/daemon/exit-records.ts
|
|
10232
|
+
// ../../packages/host-local/src/daemon-kit/exit-records.ts
|
|
10118
10233
|
import {
|
|
10119
10234
|
mkdirSync as mkdirSync3,
|
|
10120
10235
|
readdirSync as readdirSync3,
|
|
10121
|
-
readFileSync as
|
|
10122
|
-
renameSync as
|
|
10123
|
-
unlinkSync as
|
|
10124
|
-
writeFileSync as
|
|
10236
|
+
readFileSync as readFileSync3,
|
|
10237
|
+
renameSync as renameSync4,
|
|
10238
|
+
unlinkSync as unlinkSync4,
|
|
10239
|
+
writeFileSync as writeFileSync3
|
|
10125
10240
|
} from "node:fs";
|
|
10126
|
-
import { join as
|
|
10241
|
+
import { join as join6 } from "node:path";
|
|
10127
10242
|
var EXITS_DIR = "exits";
|
|
10128
10243
|
function exitsDir(runDir) {
|
|
10129
|
-
return
|
|
10244
|
+
return join6(runDir, EXITS_DIR);
|
|
10130
10245
|
}
|
|
10131
|
-
function writeExitRecord(runDir,
|
|
10246
|
+
function writeExitRecord(runDir, key, record) {
|
|
10132
10247
|
const dir = exitsDir(runDir);
|
|
10133
10248
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
10134
|
-
const file2 =
|
|
10249
|
+
const file2 = join6(dir, `${encodeURIComponent(key)}.json`);
|
|
10135
10250
|
const tmp = `${file2}.tmp`;
|
|
10136
|
-
|
|
10137
|
-
|
|
10251
|
+
writeFileSync3(tmp, JSON.stringify(record), { mode: 384 });
|
|
10252
|
+
renameSync4(tmp, file2);
|
|
10253
|
+
}
|
|
10254
|
+
|
|
10255
|
+
// ../../packages/host-local/src/terminals/daemon/exit-records.ts
|
|
10256
|
+
function writeExitRecord2(runDir, event) {
|
|
10257
|
+
writeExitRecord(runDir, event.terminalId, event);
|
|
10138
10258
|
}
|
|
10139
10259
|
|
|
10140
10260
|
// ../../packages/host-local/src/terminals/daemon/protocol.ts
|
|
10141
|
-
import { createHash } from "node:crypto";
|
|
10142
|
-
import { basename, dirname as dirname3, join as join6 } from "node:path";
|
|
10143
10261
|
var PROTOCOL_VERSION = 3;
|
|
10144
|
-
var EXIT_ENDPOINT_OCCUPIED = 20;
|
|
10145
|
-
var EXIT_ENDPOINT_UNAVAILABLE = 21;
|
|
10146
10262
|
var EXIT_NODE_PTY_UNAVAILABLE = 22;
|
|
10147
10263
|
var ENDPOINT_LOST_ERROR = "Terminal daemon no longer owns its endpoint";
|
|
10148
|
-
var MAX_SOCKET_PATH_BYTES = 103;
|
|
10149
10264
|
function daemonPaths(runDir, version = PROTOCOL_VERSION) {
|
|
10150
|
-
|
|
10151
|
-
const socket = Buffer.byteLength(inRunDir) <= MAX_SOCKET_PATH_BYTES ? inRunDir : join6(
|
|
10152
|
-
shortSocketDir(),
|
|
10153
|
-
`td-${createHash("sha256").update(runDir).digest("hex").slice(0, 16)}-v${version}.sock`
|
|
10154
|
-
);
|
|
10155
|
-
return {
|
|
10156
|
-
runDir,
|
|
10157
|
-
socket,
|
|
10158
|
-
token: join6(runDir, `terminal-daemon-v${version}.token`),
|
|
10159
|
-
pid: join6(runDir, `terminal-daemon-v${version}.pid`),
|
|
10160
|
-
log: join6(runDir, "terminal-daemon.log")
|
|
10161
|
-
};
|
|
10162
|
-
}
|
|
10163
|
-
function retiredDaemonPaths(paths, tag) {
|
|
10164
|
-
return {
|
|
10165
|
-
...paths,
|
|
10166
|
-
socket: join6(dirname3(paths.socket), `.r${tag}`),
|
|
10167
|
-
token: paths.token.replace(/\.token$/, `.retired-${tag}.token`),
|
|
10168
|
-
pid: paths.pid.replace(/\.pid$/, `.retired-${tag}.pid`)
|
|
10169
|
-
};
|
|
10170
|
-
}
|
|
10171
|
-
function shortSocketDir() {
|
|
10172
|
-
return join6("/tmp", `band-${process.getuid?.() ?? "user"}`);
|
|
10173
|
-
}
|
|
10174
|
-
function writeFrame(socket, message) {
|
|
10175
|
-
socket.write(`${JSON.stringify(message)}
|
|
10176
|
-
`);
|
|
10177
|
-
}
|
|
10178
|
-
function readFrames(socket, onFrame, maxLineBytes = 64 * 1024 * 1024) {
|
|
10179
|
-
let chunks = [];
|
|
10180
|
-
let pendingBytes = 0;
|
|
10181
|
-
socket.setEncoding("utf8");
|
|
10182
|
-
socket.on("data", (chunk) => {
|
|
10183
|
-
if (!chunk.includes("\n")) {
|
|
10184
|
-
chunks.push(chunk);
|
|
10185
|
-
pendingBytes += chunk.length;
|
|
10186
|
-
if (pendingBytes > maxLineBytes) socket.destroy(new Error("Frame too large"));
|
|
10187
|
-
return;
|
|
10188
|
-
}
|
|
10189
|
-
chunks.push(chunk);
|
|
10190
|
-
let buffer = chunks.length === 1 ? chunk : chunks.join("");
|
|
10191
|
-
chunks = [];
|
|
10192
|
-
pendingBytes = 0;
|
|
10193
|
-
let newline = buffer.indexOf("\n");
|
|
10194
|
-
while (newline !== -1) {
|
|
10195
|
-
const line = buffer.slice(0, newline);
|
|
10196
|
-
buffer = buffer.slice(newline + 1);
|
|
10197
|
-
if (line.length > 0) {
|
|
10198
|
-
let frame;
|
|
10199
|
-
try {
|
|
10200
|
-
frame = JSON.parse(line);
|
|
10201
|
-
} catch {
|
|
10202
|
-
socket.destroy(new Error("Malformed frame"));
|
|
10203
|
-
return;
|
|
10204
|
-
}
|
|
10205
|
-
onFrame(frame);
|
|
10206
|
-
if (socket.destroyed) return;
|
|
10207
|
-
}
|
|
10208
|
-
newline = buffer.indexOf("\n");
|
|
10209
|
-
}
|
|
10210
|
-
if (buffer.length > 0) {
|
|
10211
|
-
chunks.push(buffer);
|
|
10212
|
-
pendingBytes = buffer.length;
|
|
10213
|
-
if (pendingBytes > maxLineBytes) socket.destroy(new Error("Frame too large"));
|
|
10214
|
-
}
|
|
10215
|
-
});
|
|
10265
|
+
return daemonPathsFor({ runDir, name: "terminal-daemon", shortPrefix: "td", version });
|
|
10216
10266
|
}
|
|
10217
10267
|
|
|
10218
10268
|
// ../../packages/host-local/src/terminals/daemon/daemon-server.ts
|
|
@@ -10234,7 +10284,7 @@ async function runDaemon(options) {
|
|
|
10234
10284
|
}
|
|
10235
10285
|
ensurePrivateDir(paths.runDir);
|
|
10236
10286
|
ensurePrivateDir(dirname4(paths.socket));
|
|
10237
|
-
const token =
|
|
10287
|
+
const token = randomBytes3(32).toString("hex");
|
|
10238
10288
|
const pool = new TerminalPool({ historyDir: join7(dirname4(paths.runDir), "terminal-history") });
|
|
10239
10289
|
const clients = /* @__PURE__ */ new Map();
|
|
10240
10290
|
let shuttingDown = false;
|
|
@@ -10252,7 +10302,7 @@ async function runDaemon(options) {
|
|
|
10252
10302
|
});
|
|
10253
10303
|
});
|
|
10254
10304
|
chmodSync2(bindPath, 384);
|
|
10255
|
-
let retired = supersede ? retiredDaemonPaths(paths,
|
|
10305
|
+
let retired = supersede ? retiredDaemonPaths(paths, randomBytes3(4).toString("hex")) : null;
|
|
10256
10306
|
if (retired) {
|
|
10257
10307
|
copyPrivateFile(paths.token, retired.token);
|
|
10258
10308
|
copyPrivateFile(paths.pid, retired.pid);
|
|
@@ -10290,7 +10340,7 @@ async function runDaemon(options) {
|
|
|
10290
10340
|
pool.onExit((event) => {
|
|
10291
10341
|
if (options.recordExits && !event.killed && clients.size === 0) {
|
|
10292
10342
|
try {
|
|
10293
|
-
|
|
10343
|
+
writeExitRecord2(paths.runDir, event);
|
|
10294
10344
|
} catch (err) {
|
|
10295
10345
|
log3.warn({ err, terminalId: event.terminalId }, "could not record a terminal exit");
|
|
10296
10346
|
}
|
|
@@ -10390,7 +10440,7 @@ async function runDaemon(options) {
|
|
|
10390
10440
|
if (!role && !mismatched) {
|
|
10391
10441
|
clearTimeout(helloTimer);
|
|
10392
10442
|
const hello = parseHello(frame);
|
|
10393
|
-
if (!hello || !
|
|
10443
|
+
if (!hello || !tokenMatches2(hello.token)) {
|
|
10394
10444
|
writeFrame(socket, { t: "rejected", reason: "bad hello" });
|
|
10395
10445
|
socket.end();
|
|
10396
10446
|
return;
|
|
@@ -10613,10 +10663,8 @@ async function runDaemon(options) {
|
|
|
10613
10663
|
cleanupOnExit: entry.cleanupOnExit
|
|
10614
10664
|
};
|
|
10615
10665
|
}
|
|
10616
|
-
function
|
|
10617
|
-
|
|
10618
|
-
const actual = Buffer.from(candidate);
|
|
10619
|
-
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
10666
|
+
function tokenMatches2(candidate) {
|
|
10667
|
+
return tokenMatches(token, candidate);
|
|
10620
10668
|
}
|
|
10621
10669
|
}
|
|
10622
10670
|
function hasValidIds(message) {
|
|
@@ -10631,36 +10679,6 @@ function hasValidIds(message) {
|
|
|
10631
10679
|
}
|
|
10632
10680
|
return true;
|
|
10633
10681
|
}
|
|
10634
|
-
function parseHello(frame) {
|
|
10635
|
-
if (typeof frame !== "object" || frame === null) return null;
|
|
10636
|
-
const hello = frame;
|
|
10637
|
-
if (hello.t !== "hello" || typeof hello.protocol !== "number" || typeof hello.token !== "string" || hello.role !== "control" && hello.role !== "stream" || typeof hello.clientId !== "string" || typeof hello.buildId !== "string") {
|
|
10638
|
-
return null;
|
|
10639
|
-
}
|
|
10640
|
-
return hello;
|
|
10641
|
-
}
|
|
10642
|
-
function writePrivateFile(path, content) {
|
|
10643
|
-
const tmp = `${path}.${randomBytes2(4).toString("hex")}.tmp`;
|
|
10644
|
-
writeFileSync3(tmp, content, { mode: 384 });
|
|
10645
|
-
renameSync4(tmp, path);
|
|
10646
|
-
}
|
|
10647
|
-
function copyPrivateFile(from, to) {
|
|
10648
|
-
let content;
|
|
10649
|
-
try {
|
|
10650
|
-
content = readFileSync3(from, "utf8");
|
|
10651
|
-
} catch {
|
|
10652
|
-
return;
|
|
10653
|
-
}
|
|
10654
|
-
writePrivateFile(to, content);
|
|
10655
|
-
}
|
|
10656
|
-
function removeRetiredFiles(retired) {
|
|
10657
|
-
for (const path of [retired.socket, retired.token, retired.pid]) {
|
|
10658
|
-
try {
|
|
10659
|
-
unlinkSync4(path);
|
|
10660
|
-
} catch {
|
|
10661
|
-
}
|
|
10662
|
-
}
|
|
10663
|
-
}
|
|
10664
10682
|
function exists(path) {
|
|
10665
10683
|
try {
|
|
10666
10684
|
lstatSync2(path);
|