@bondsmith/bondsmithd 0.1.7 → 0.1.8
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/index.js +234 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { createServer } from "http";
|
|
5
5
|
import { existsSync } from "fs";
|
|
6
6
|
import { hostname as hostname2 } from "os";
|
|
7
|
-
import { dirname as dirname4, join as
|
|
7
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
10
|
// ../../packages/local-agent-protocol/src/session.ts
|
|
@@ -16,7 +16,7 @@ var REPLAYED_COMMAND_MESSAGE = "command already used (replay rejected)";
|
|
|
16
16
|
var INVALID_MESSAGE_ERROR = "invalid message";
|
|
17
17
|
|
|
18
18
|
// ../../packages/local-agent-protocol/src/protocol-version.ts
|
|
19
|
-
var PROTOCOL_VERSION =
|
|
19
|
+
var PROTOCOL_VERSION = 7;
|
|
20
20
|
|
|
21
21
|
// ../../packages/local-agent-protocol/src/project-path.ts
|
|
22
22
|
var PROJECT_PATH_INVALID_REASONS = [
|
|
@@ -60,6 +60,8 @@ var signedConnectionChallengeSchema = z.object({
|
|
|
60
60
|
var MAX_INSERT_TEXT_BYTES = 32 * 1024;
|
|
61
61
|
var MAX_INPUT_CHARS = 16 * 1024;
|
|
62
62
|
var MAX_WIRE_MESSAGE_BYTES = 256 * 1024;
|
|
63
|
+
var MAX_DAEMON_REPLY_BYTES = 200 * 1024;
|
|
64
|
+
var MAX_RANGE_LINES = 500;
|
|
63
65
|
var MAX_TERMINAL_DIMENSION = 1e3;
|
|
64
66
|
var terminalSizeSchema = z2.object({
|
|
65
67
|
cols: z2.number().int().min(1).max(MAX_TERMINAL_DIMENSION),
|
|
@@ -95,14 +97,32 @@ var workspaceStatusSchema = z2.object({
|
|
|
95
97
|
branch: z2.string().nullable(),
|
|
96
98
|
// .catch(undefined): a malformed informational field must never drop the whole status message.
|
|
97
99
|
baseRef: z2.string().nullable().optional().catch(void 0),
|
|
98
|
-
files: z2.array(workspaceFileEntrySchema)
|
|
100
|
+
files: z2.array(workspaceFileEntrySchema),
|
|
101
|
+
totalFiles: z2.number().int().nonnegative().optional().catch(void 0)
|
|
99
102
|
});
|
|
100
103
|
var workspaceFileDiffSchema = z2.object({
|
|
101
104
|
path: z2.string(),
|
|
102
105
|
staged: z2.boolean(),
|
|
103
106
|
patch: z2.string().nullable(),
|
|
104
|
-
binary: z2.boolean()
|
|
107
|
+
binary: z2.boolean(),
|
|
108
|
+
tooLarge: z2.boolean().optional(),
|
|
109
|
+
version: z2.string().optional()
|
|
105
110
|
});
|
|
111
|
+
var workspaceFileRangeSchema = z2.discriminatedUnion("ok", [
|
|
112
|
+
z2.object({
|
|
113
|
+
ok: z2.literal(true),
|
|
114
|
+
path: z2.string(),
|
|
115
|
+
startLine: z2.number().int().min(1),
|
|
116
|
+
lines: z2.array(z2.string()).max(MAX_RANGE_LINES),
|
|
117
|
+
totalLines: z2.number().int().nonnegative(),
|
|
118
|
+
version: z2.string()
|
|
119
|
+
}),
|
|
120
|
+
z2.object({
|
|
121
|
+
ok: z2.literal(false),
|
|
122
|
+
path: z2.string(),
|
|
123
|
+
reason: z2.enum(["changed", "missing", "binary", "too_large", "not_allowed"])
|
|
124
|
+
})
|
|
125
|
+
]);
|
|
106
126
|
var projectPathValidationSchema = z2.discriminatedUnion("valid", [
|
|
107
127
|
z2.object({ valid: z2.literal(true), path: z2.string() }),
|
|
108
128
|
z2.object({ valid: z2.literal(false), reason: z2.enum(PROJECT_PATH_INVALID_REASONS) })
|
|
@@ -166,6 +186,15 @@ var browserMessageSchema = z2.discriminatedUnion("type", [
|
|
|
166
186
|
sessionId: z2.string(),
|
|
167
187
|
requestId: z2.string().min(1).max(200)
|
|
168
188
|
}),
|
|
189
|
+
z2.object({
|
|
190
|
+
type: z2.literal("workspace.readFileRange"),
|
|
191
|
+
sessionId: z2.string(),
|
|
192
|
+
path: z2.string().min(1).max(4096),
|
|
193
|
+
startLine: z2.number().int().min(1),
|
|
194
|
+
lineCount: z2.number().int().min(1).max(MAX_RANGE_LINES),
|
|
195
|
+
version: z2.string().min(1).max(100),
|
|
196
|
+
requestId: z2.string().min(1).max(200)
|
|
197
|
+
}),
|
|
169
198
|
z2.object({
|
|
170
199
|
type: z2.literal("workspace.getDiff"),
|
|
171
200
|
sessionId: z2.string(),
|
|
@@ -250,6 +279,12 @@ var daemonMessageSchema = z2.discriminatedUnion("type", [
|
|
|
250
279
|
requestId: z2.string(),
|
|
251
280
|
status: workspaceStatusSchema
|
|
252
281
|
}),
|
|
282
|
+
z2.object({
|
|
283
|
+
type: z2.literal("workspace.fileRange"),
|
|
284
|
+
sessionId: z2.string(),
|
|
285
|
+
requestId: z2.string(),
|
|
286
|
+
range: workspaceFileRangeSchema
|
|
287
|
+
}),
|
|
253
288
|
z2.object({
|
|
254
289
|
type: z2.literal("workspace.diff"),
|
|
255
290
|
sessionId: z2.string(),
|
|
@@ -370,6 +405,7 @@ var SIGNABLE_ACTIONS = [
|
|
|
370
405
|
"session.list",
|
|
371
406
|
"workspace.getStatus",
|
|
372
407
|
"workspace.getDiff",
|
|
408
|
+
"workspace.readFileRange",
|
|
373
409
|
"project.validatePath"
|
|
374
410
|
];
|
|
375
411
|
var envelopeSchema = z3.object({
|
|
@@ -954,7 +990,7 @@ import { sign } from "crypto";
|
|
|
954
990
|
import WebSocket from "ws";
|
|
955
991
|
|
|
956
992
|
// src/version.ts
|
|
957
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
993
|
+
var DAEMON_VERSION = true ? "0.1.8" : "dev";
|
|
958
994
|
var VERSION_FLAGS = ["--version", "-v"];
|
|
959
995
|
function versionLine() {
|
|
960
996
|
return `bondsmithd ${DAEMON_VERSION} (protocol ${PROTOCOL_VERSION})`;
|
|
@@ -1378,12 +1414,42 @@ function createRouterConnection(connectionAuth, send, markControllerId, ptyAttac
|
|
|
1378
1414
|
function error(message, requestId) {
|
|
1379
1415
|
return { type: "error", message, requestId };
|
|
1380
1416
|
}
|
|
1417
|
+
function fitDiffReply(reply) {
|
|
1418
|
+
const { patch, tooLarge } = reply.diff;
|
|
1419
|
+
const fits = (patch === null || patch.length <= MAX_DAEMON_REPLY_BYTES) && Buffer.byteLength(JSON.stringify(reply)) <= MAX_DAEMON_REPLY_BYTES;
|
|
1420
|
+
if (tooLarge || fits) return reply;
|
|
1421
|
+
return { ...reply, diff: { ...reply.diff, patch: null, tooLarge: true } };
|
|
1422
|
+
}
|
|
1423
|
+
function fitStatusReply(reply) {
|
|
1424
|
+
if (Buffer.byteLength(JSON.stringify(reply)) <= MAX_DAEMON_REPLY_BYTES) return reply;
|
|
1425
|
+
const { files } = reply.status;
|
|
1426
|
+
const bare = { ...reply, status: { ...reply.status, files: [], totalFiles: files.length } };
|
|
1427
|
+
let budget = MAX_DAEMON_REPLY_BYTES - Buffer.byteLength(JSON.stringify(bare));
|
|
1428
|
+
const kept = [];
|
|
1429
|
+
for (const file of files) {
|
|
1430
|
+
const cost = Buffer.byteLength(JSON.stringify(file)) + 1;
|
|
1431
|
+
if (cost > budget) break;
|
|
1432
|
+
budget -= cost;
|
|
1433
|
+
kept.push(file);
|
|
1434
|
+
}
|
|
1435
|
+
return { ...bare, status: { ...bare.status, files: kept } };
|
|
1436
|
+
}
|
|
1381
1437
|
function errorMessageOf(err, fallback) {
|
|
1382
1438
|
return err instanceof Error ? err.message : fallback;
|
|
1383
1439
|
}
|
|
1384
1440
|
function requestIdOf(message) {
|
|
1385
1441
|
return "requestId" in message ? message.requestId : void 0;
|
|
1386
1442
|
}
|
|
1443
|
+
function stringRequestId(value) {
|
|
1444
|
+
if (typeof value !== "object" || value === null || !("requestId" in value)) return void 0;
|
|
1445
|
+
const { requestId } = value;
|
|
1446
|
+
return typeof requestId === "string" && requestId !== "" ? requestId.slice(0, 200) : void 0;
|
|
1447
|
+
}
|
|
1448
|
+
function requestIdOfUnparsed(raw) {
|
|
1449
|
+
const envelope = typeof raw === "object" && raw !== null && "envelope" in raw ? raw.envelope : void 0;
|
|
1450
|
+
const payload = typeof envelope === "object" && envelope !== null && "payload" in envelope ? envelope.payload : void 0;
|
|
1451
|
+
return stringRequestId(payload) ?? stringRequestId(raw);
|
|
1452
|
+
}
|
|
1387
1453
|
function getSessionOrError(sessionManager, conn, sessionId, requestId) {
|
|
1388
1454
|
const session = sessionManager.get(sessionId);
|
|
1389
1455
|
if (!session) conn.send(error("unknown session", requestId));
|
|
@@ -1395,7 +1461,7 @@ async function handleBrowserMessage(conn, raw, deps) {
|
|
|
1395
1461
|
const { envelope } = signedCommand.data;
|
|
1396
1462
|
const innerMessage = browserMessageSchema.safeParse(envelope.payload);
|
|
1397
1463
|
if (!innerMessage.success || innerMessage.data.type !== envelope.action) {
|
|
1398
|
-
conn.send(error(INVALID_MESSAGE_ERROR));
|
|
1464
|
+
conn.send(error(INVALID_MESSAGE_ERROR, requestIdOfUnparsed(raw)));
|
|
1399
1465
|
return;
|
|
1400
1466
|
}
|
|
1401
1467
|
const admitted = await conn.admitInOrder(async () => {
|
|
@@ -1421,7 +1487,7 @@ async function handleBrowserMessage(conn, raw, deps) {
|
|
|
1421
1487
|
}
|
|
1422
1488
|
const parsed = browserMessageSchema.safeParse(raw);
|
|
1423
1489
|
if (!parsed.success) {
|
|
1424
|
-
conn.send(error(INVALID_MESSAGE_ERROR));
|
|
1490
|
+
conn.send(error(INVALID_MESSAGE_ERROR, requestIdOfUnparsed(raw)));
|
|
1425
1491
|
return;
|
|
1426
1492
|
}
|
|
1427
1493
|
if (parsed.data.type === "connection.authenticate") {
|
|
@@ -1449,6 +1515,7 @@ async function dispatch(conn, message, {
|
|
|
1449
1515
|
broadcast,
|
|
1450
1516
|
getWorkspaceStatus: getWorkspaceStatus2,
|
|
1451
1517
|
getWorkspaceDiff: getWorkspaceDiff2,
|
|
1518
|
+
readFileRange: readFileRange2,
|
|
1452
1519
|
validateProjectPath: validateProjectPath2,
|
|
1453
1520
|
hostname: hostname3,
|
|
1454
1521
|
daemonFingerprint,
|
|
@@ -1584,12 +1651,14 @@ async function dispatch(conn, message, {
|
|
|
1584
1651
|
session.cwd,
|
|
1585
1652
|
sessionManager.getBaseline(session.id)
|
|
1586
1653
|
);
|
|
1587
|
-
conn.send(
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1654
|
+
conn.send(
|
|
1655
|
+
fitStatusReply({
|
|
1656
|
+
type: "workspace.status",
|
|
1657
|
+
sessionId: message.sessionId,
|
|
1658
|
+
requestId: message.requestId,
|
|
1659
|
+
status
|
|
1660
|
+
})
|
|
1661
|
+
);
|
|
1593
1662
|
} catch (err) {
|
|
1594
1663
|
conn.send(error(errorMessageOf(err, "failed to read workspace status"), message.requestId));
|
|
1595
1664
|
}
|
|
@@ -1605,14 +1674,38 @@ async function dispatch(conn, message, {
|
|
|
1605
1674
|
message.staged,
|
|
1606
1675
|
sessionManager.getBaseline(session.id)
|
|
1607
1676
|
);
|
|
1677
|
+
conn.send(
|
|
1678
|
+
fitDiffReply({
|
|
1679
|
+
type: "workspace.diff",
|
|
1680
|
+
sessionId: message.sessionId,
|
|
1681
|
+
requestId: message.requestId,
|
|
1682
|
+
diff
|
|
1683
|
+
})
|
|
1684
|
+
);
|
|
1685
|
+
} catch (err) {
|
|
1686
|
+
conn.send(error(errorMessageOf(err, "failed to read workspace diff"), message.requestId));
|
|
1687
|
+
}
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
case "workspace.readFileRange": {
|
|
1691
|
+
const session = getSessionOrError(sessionManager, conn, message.sessionId, message.requestId);
|
|
1692
|
+
if (!session) return;
|
|
1693
|
+
try {
|
|
1694
|
+
const range = await readFileRange2(
|
|
1695
|
+
session.cwd,
|
|
1696
|
+
message.path,
|
|
1697
|
+
message.startLine,
|
|
1698
|
+
message.lineCount,
|
|
1699
|
+
message.version
|
|
1700
|
+
);
|
|
1608
1701
|
conn.send({
|
|
1609
|
-
type: "workspace.
|
|
1702
|
+
type: "workspace.fileRange",
|
|
1610
1703
|
sessionId: message.sessionId,
|
|
1611
1704
|
requestId: message.requestId,
|
|
1612
|
-
|
|
1705
|
+
range
|
|
1613
1706
|
});
|
|
1614
1707
|
} catch (err) {
|
|
1615
|
-
conn.send(error(errorMessageOf(err, "failed to read
|
|
1708
|
+
conn.send(error(errorMessageOf(err, "failed to read file range"), message.requestId));
|
|
1616
1709
|
}
|
|
1617
1710
|
return;
|
|
1618
1711
|
}
|
|
@@ -2552,9 +2645,99 @@ function presentPendingAuthorizationOnTerminal(store) {
|
|
|
2552
2645
|
};
|
|
2553
2646
|
}
|
|
2554
2647
|
|
|
2648
|
+
// src/sessions/files/read-range.ts
|
|
2649
|
+
import { constants } from "fs";
|
|
2650
|
+
import { lstat, open, realpath, stat as stat2 } from "fs/promises";
|
|
2651
|
+
import { isAbsolute as isAbsolute2, join as join5, sep } from "path";
|
|
2652
|
+
var MAX_RANGE_FILE_BYTES = 2 * 1024 * 1024;
|
|
2653
|
+
var REPLY_ENVELOPE_BYTES = 8 * 1024;
|
|
2654
|
+
var BINARY_SNIFF_BYTES = 8e3;
|
|
2655
|
+
function fileVersion(stats) {
|
|
2656
|
+
return `${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}:${stats.ino}`;
|
|
2657
|
+
}
|
|
2658
|
+
async function locateInside(cwd, path) {
|
|
2659
|
+
if (isAbsolute2(path)) return { refused: "not_allowed" };
|
|
2660
|
+
try {
|
|
2661
|
+
const root = await realpath(cwd);
|
|
2662
|
+
const lexical = join5(root, path);
|
|
2663
|
+
if ((await lstat(lexical)).isSymbolicLink()) return { refused: "not_allowed" };
|
|
2664
|
+
const real = await realpath(lexical);
|
|
2665
|
+
return real === root || real.startsWith(root + sep) ? { found: real } : { refused: "missing" };
|
|
2666
|
+
} catch {
|
|
2667
|
+
return { refused: "missing" };
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
async function readExactly(handle, size) {
|
|
2671
|
+
const buffer = Buffer.allocUnsafe(size + 1);
|
|
2672
|
+
let filled = 0;
|
|
2673
|
+
while (filled < buffer.length) {
|
|
2674
|
+
const { bytesRead } = await handle.read(buffer, filled, buffer.length - filled, filled);
|
|
2675
|
+
if (bytesRead === 0) break;
|
|
2676
|
+
filled += bytesRead;
|
|
2677
|
+
}
|
|
2678
|
+
return filled === size ? buffer.subarray(0, size) : void 0;
|
|
2679
|
+
}
|
|
2680
|
+
async function currentFileVersion(cwd, path) {
|
|
2681
|
+
const located = await locateInside(cwd, path);
|
|
2682
|
+
if (!("found" in located)) return void 0;
|
|
2683
|
+
try {
|
|
2684
|
+
return fileVersion(await stat2(located.found));
|
|
2685
|
+
} catch {
|
|
2686
|
+
return void 0;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
function splitLines(text) {
|
|
2690
|
+
if (text === "") return [];
|
|
2691
|
+
const lines = text.split("\n");
|
|
2692
|
+
if (lines[lines.length - 1] === "") lines.pop();
|
|
2693
|
+
return lines.map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
|
|
2694
|
+
}
|
|
2695
|
+
async function readFileRange(cwd, path, startLine, lineCount, version) {
|
|
2696
|
+
const refuse = (reason) => ({ ok: false, path, reason });
|
|
2697
|
+
const located = await locateInside(cwd, path);
|
|
2698
|
+
if (!("found" in located)) return refuse(located.refused);
|
|
2699
|
+
let content;
|
|
2700
|
+
let handle;
|
|
2701
|
+
try {
|
|
2702
|
+
handle = await open(
|
|
2703
|
+
located.found,
|
|
2704
|
+
constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK
|
|
2705
|
+
);
|
|
2706
|
+
const before = await handle.stat();
|
|
2707
|
+
if (!before.isFile()) return refuse("not_allowed");
|
|
2708
|
+
if (fileVersion(before) !== version) return refuse("changed");
|
|
2709
|
+
if (before.size > MAX_RANGE_FILE_BYTES) return refuse("too_large");
|
|
2710
|
+
const bytes = await readExactly(handle, before.size);
|
|
2711
|
+
if (bytes === void 0 || fileVersion(await handle.stat()) !== version) {
|
|
2712
|
+
return refuse("changed");
|
|
2713
|
+
}
|
|
2714
|
+
content = bytes;
|
|
2715
|
+
} catch {
|
|
2716
|
+
return refuse("missing");
|
|
2717
|
+
} finally {
|
|
2718
|
+
await handle?.close();
|
|
2719
|
+
}
|
|
2720
|
+
if (content.subarray(0, BINARY_SNIFF_BYTES).includes(0)) return refuse("binary");
|
|
2721
|
+
const lines = splitLines(content.toString("utf8"));
|
|
2722
|
+
const page = [];
|
|
2723
|
+
let budget = MAX_DAEMON_REPLY_BYTES - REPLY_ENVELOPE_BYTES;
|
|
2724
|
+
const wanted = Math.min(lineCount, MAX_RANGE_LINES);
|
|
2725
|
+
for (let index = startLine - 1; index < lines.length && page.length < wanted; index++) {
|
|
2726
|
+
const line = lines[index];
|
|
2727
|
+
const cost = Buffer.byteLength(JSON.stringify(line)) + 1;
|
|
2728
|
+
if (cost > budget) {
|
|
2729
|
+
if (page.length === 0) return refuse("too_large");
|
|
2730
|
+
break;
|
|
2731
|
+
}
|
|
2732
|
+
budget -= cost;
|
|
2733
|
+
page.push(line);
|
|
2734
|
+
}
|
|
2735
|
+
return { ok: true, path, startLine, lines: page, totalLines: lines.length, version };
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2555
2738
|
// src/sessions/git/baseline.ts
|
|
2556
|
-
import { lstat } from "fs/promises";
|
|
2557
|
-
import { join as
|
|
2739
|
+
import { lstat as lstat2 } from "fs/promises";
|
|
2740
|
+
import { join as join6 } from "path";
|
|
2558
2741
|
|
|
2559
2742
|
// src/sessions/git/base.ts
|
|
2560
2743
|
var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
@@ -2595,7 +2778,7 @@ var MAX_PREEXISTING_FILES = 200;
|
|
|
2595
2778
|
var DELETED = "deleted";
|
|
2596
2779
|
async function fingerprintFile(cwd, path) {
|
|
2597
2780
|
try {
|
|
2598
|
-
const stats = await
|
|
2781
|
+
const stats = await lstat2(join6(cwd, path));
|
|
2599
2782
|
return `${stats.size}:${Math.trunc(stats.mtimeMs)}`;
|
|
2600
2783
|
} catch {
|
|
2601
2784
|
return DELETED;
|
|
@@ -2852,8 +3035,12 @@ async function isUntracked(cwd, path) {
|
|
|
2852
3035
|
const out = await runGit(["ls-files", "--others", "--exclude-standard", "--", path], cwd);
|
|
2853
3036
|
return out.trim() !== "";
|
|
2854
3037
|
}
|
|
3038
|
+
function isOutputOverflow(err) {
|
|
3039
|
+
return err?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
|
|
3040
|
+
}
|
|
2855
3041
|
async function getWorkspaceDiff(cwd, path, staged, baseline) {
|
|
2856
3042
|
const base = await resolveBaseFor(cwd, baseline);
|
|
3043
|
+
const version = await currentFileVersion(cwd, path);
|
|
2857
3044
|
const pathspec = await withRenameSource(cwd, base.sha, path);
|
|
2858
3045
|
const numstat = await runGitDiff(
|
|
2859
3046
|
["--relative", "--numstat", "-M", base.sha, "--", ...pathspec],
|
|
@@ -2863,13 +3050,27 @@ async function getWorkspaceDiff(cwd, path, staged, baseline) {
|
|
|
2863
3050
|
if (binary) {
|
|
2864
3051
|
return { path, staged, patch: null, binary: true };
|
|
2865
3052
|
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
patch
|
|
2869
|
-
|
|
2870
|
-
|
|
3053
|
+
try {
|
|
3054
|
+
let patch = await runGitDiff(["--relative", "-M", base.sha, "--", ...pathspec], cwd);
|
|
3055
|
+
if (patch.trim() === "" && await isUntracked(cwd, path)) {
|
|
3056
|
+
patch = await runGitDiffAllowingExitCodes(["--no-index", "--", "/dev/null", path], cwd, [
|
|
3057
|
+
1
|
|
3058
|
+
]).catch((err) => {
|
|
3059
|
+
if (isOutputOverflow(err)) throw err;
|
|
3060
|
+
return "";
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
return {
|
|
3064
|
+
path,
|
|
3065
|
+
staged,
|
|
3066
|
+
patch: patch || null,
|
|
3067
|
+
binary: false,
|
|
3068
|
+
...version === void 0 ? {} : { version }
|
|
3069
|
+
};
|
|
3070
|
+
} catch (err) {
|
|
3071
|
+
if (isOutputOverflow(err)) return { path, staged, patch: null, binary: false, tooLarge: true };
|
|
3072
|
+
throw err;
|
|
2871
3073
|
}
|
|
2872
|
-
return { path, staged, patch: patch || null, binary: false };
|
|
2873
3074
|
}
|
|
2874
3075
|
|
|
2875
3076
|
// src/ws/server.ts
|
|
@@ -3092,7 +3293,7 @@ function resolveSessionAttention(report, lastActivityAt, nowMs) {
|
|
|
3092
3293
|
// src/sessions/tmux/create.ts
|
|
3093
3294
|
import { access } from "fs/promises";
|
|
3094
3295
|
import { homedir as homedir5 } from "os";
|
|
3095
|
-
import { join as
|
|
3296
|
+
import { join as join7 } from "path";
|
|
3096
3297
|
var InvalidCwdError = class extends Error {
|
|
3097
3298
|
constructor(cwd) {
|
|
3098
3299
|
super(`working directory does not exist: ${cwd}`);
|
|
@@ -3100,7 +3301,7 @@ var InvalidCwdError = class extends Error {
|
|
|
3100
3301
|
}
|
|
3101
3302
|
};
|
|
3102
3303
|
function worktreePathFor(sessionId) {
|
|
3103
|
-
return
|
|
3304
|
+
return join7(homedir5(), ".bondsmith", "worktrees", sessionId);
|
|
3104
3305
|
}
|
|
3105
3306
|
async function createTmuxSession(input) {
|
|
3106
3307
|
const sourceCwd = expandHome(input.cwd);
|
|
@@ -3484,6 +3685,7 @@ function createDaemonWebSocketServer(httpServer, config, daemonFingerprint, pend
|
|
|
3484
3685
|
broadcast,
|
|
3485
3686
|
getWorkspaceStatus,
|
|
3486
3687
|
getWorkspaceDiff,
|
|
3688
|
+
readFileRange,
|
|
3487
3689
|
validateProjectPath,
|
|
3488
3690
|
hostname: () => hostname(),
|
|
3489
3691
|
daemonFingerprint,
|
|
@@ -3524,10 +3726,10 @@ if (isVersionRequest(process.argv.slice(2))) {
|
|
|
3524
3726
|
process.exit(0);
|
|
3525
3727
|
}
|
|
3526
3728
|
function loadRepoRootEnv() {
|
|
3527
|
-
const repoRoot =
|
|
3528
|
-
if (!existsSync(
|
|
3529
|
-
const envLocal =
|
|
3530
|
-
const envDefault =
|
|
3729
|
+
const repoRoot = join8(dirname4(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
3730
|
+
if (!existsSync(join8(repoRoot, "pnpm-workspace.yaml"))) return;
|
|
3731
|
+
const envLocal = join8(repoRoot, ".env.local");
|
|
3732
|
+
const envDefault = join8(repoRoot, ".env");
|
|
3531
3733
|
loadEnv({ path: existsSync(envLocal) ? envLocal : envDefault });
|
|
3532
3734
|
}
|
|
3533
3735
|
loadRepoRootEnv();
|
|
@@ -3581,6 +3783,7 @@ async function start() {
|
|
|
3581
3783
|
ptyAttachments,
|
|
3582
3784
|
getWorkspaceStatus,
|
|
3583
3785
|
getWorkspaceDiff,
|
|
3786
|
+
readFileRange,
|
|
3584
3787
|
validateProjectPath,
|
|
3585
3788
|
hostname: hostname2,
|
|
3586
3789
|
pendingAuthorizations,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bondsmith/bondsmithd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Local daemon for Bondsmith agent sessions — runs on the user's own machine, pairs with a Bondsmith account, and exposes tmux-backed terminal sessions to the Bondsmith web app over a signed WebSocket protocol.",
|
|
6
6
|
"homepage": "https://bondsmith.dev",
|