@bondsmith/bondsmithd 0.1.5 → 0.1.6
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 +197 -30
- 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 join7 } from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
10
|
// ../../packages/local-agent-protocol/src/session.ts
|
|
@@ -951,7 +951,7 @@ import { sign } from "crypto";
|
|
|
951
951
|
import WebSocket from "ws";
|
|
952
952
|
|
|
953
953
|
// src/version.ts
|
|
954
|
-
var DAEMON_VERSION = true ? "0.1.
|
|
954
|
+
var DAEMON_VERSION = true ? "0.1.6" : "dev";
|
|
955
955
|
var VERSION_FLAGS = ["--version", "-v"];
|
|
956
956
|
function versionLine() {
|
|
957
957
|
return `bondsmithd ${DAEMON_VERSION} (protocol ${PROTOCOL_VERSION})`;
|
|
@@ -1577,7 +1577,10 @@ async function dispatch(conn, message, {
|
|
|
1577
1577
|
const session = getSessionOrError(sessionManager, conn, message.sessionId, message.requestId);
|
|
1578
1578
|
if (!session) return;
|
|
1579
1579
|
try {
|
|
1580
|
-
const status = await getWorkspaceStatus2(
|
|
1580
|
+
const status = await getWorkspaceStatus2(
|
|
1581
|
+
session.cwd,
|
|
1582
|
+
sessionManager.getBaseline(session.id)
|
|
1583
|
+
);
|
|
1581
1584
|
conn.send({
|
|
1582
1585
|
type: "workspace.status",
|
|
1583
1586
|
sessionId: message.sessionId,
|
|
@@ -1593,7 +1596,12 @@ async function dispatch(conn, message, {
|
|
|
1593
1596
|
const session = getSessionOrError(sessionManager, conn, message.sessionId, message.requestId);
|
|
1594
1597
|
if (!session) return;
|
|
1595
1598
|
try {
|
|
1596
|
-
const diff = await getWorkspaceDiff2(
|
|
1599
|
+
const diff = await getWorkspaceDiff2(
|
|
1600
|
+
session.cwd,
|
|
1601
|
+
message.path,
|
|
1602
|
+
message.staged,
|
|
1603
|
+
sessionManager.getBaseline(session.id)
|
|
1604
|
+
);
|
|
1597
1605
|
conn.send({
|
|
1598
1606
|
type: "workspace.diff",
|
|
1599
1607
|
sessionId: message.sessionId,
|
|
@@ -2541,6 +2549,10 @@ function presentPendingAuthorizationOnTerminal(store) {
|
|
|
2541
2549
|
};
|
|
2542
2550
|
}
|
|
2543
2551
|
|
|
2552
|
+
// src/sessions/git/baseline.ts
|
|
2553
|
+
import { lstat } from "fs/promises";
|
|
2554
|
+
import { join as join5 } from "path";
|
|
2555
|
+
|
|
2544
2556
|
// src/sessions/git/base.ts
|
|
2545
2557
|
var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
2546
2558
|
var DEFAULT_BRANCH_CANDIDATES = ["origin/main", "origin/master", "main", "master"];
|
|
@@ -2574,6 +2586,107 @@ async function resolveComparisonBase(cwd) {
|
|
|
2574
2586
|
return mergeBase ? { sha: mergeBase, ref: defaultRef } : { sha: "HEAD", ref: null };
|
|
2575
2587
|
}
|
|
2576
2588
|
|
|
2589
|
+
// src/sessions/git/baseline.ts
|
|
2590
|
+
var SESSION_START_REF = "session start";
|
|
2591
|
+
var MAX_PREEXISTING_FILES = 200;
|
|
2592
|
+
var DELETED = "deleted";
|
|
2593
|
+
async function fingerprintFile(cwd, path) {
|
|
2594
|
+
try {
|
|
2595
|
+
const stats = await lstat(join5(cwd, path));
|
|
2596
|
+
return `${stats.size}:${Math.trunc(stats.mtimeMs)}`;
|
|
2597
|
+
} catch {
|
|
2598
|
+
return DELETED;
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
async function headSha(cwd) {
|
|
2602
|
+
try {
|
|
2603
|
+
return (await runGit(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"], cwd)).trim() || null;
|
|
2604
|
+
} catch {
|
|
2605
|
+
return null;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
async function listChangedPaths(cwd, baseSha) {
|
|
2609
|
+
const [tracked, untracked] = await Promise.all([
|
|
2610
|
+
runGitDiff(["--relative", "--name-only", "-z", baseSha], cwd),
|
|
2611
|
+
runGit(["ls-files", "--others", "--exclude-standard", "-z"], cwd)
|
|
2612
|
+
]);
|
|
2613
|
+
const paths = [...tracked.split("\0"), ...untracked.split("\0")].filter((p) => p.length > 0);
|
|
2614
|
+
return [...new Set(paths)];
|
|
2615
|
+
}
|
|
2616
|
+
async function captureBaseline(cwd, options) {
|
|
2617
|
+
try {
|
|
2618
|
+
await runGit(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
2619
|
+
} catch {
|
|
2620
|
+
return null;
|
|
2621
|
+
}
|
|
2622
|
+
try {
|
|
2623
|
+
const baseSha = await headSha(cwd);
|
|
2624
|
+
const preexisting = /* @__PURE__ */ new Map();
|
|
2625
|
+
if (!options.freshCheckout) {
|
|
2626
|
+
const changed = await listChangedPaths(cwd, baseSha ?? EMPTY_TREE_SHA);
|
|
2627
|
+
for (const path of changed.slice(0, MAX_PREEXISTING_FILES)) {
|
|
2628
|
+
preexisting.set(path, await fingerprintFile(cwd, path));
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
return { baseSha, preexisting };
|
|
2632
|
+
} catch (error2) {
|
|
2633
|
+
console.error("Failed to take a session baseline; comparing against the default branch", {
|
|
2634
|
+
cwd,
|
|
2635
|
+
error: error2
|
|
2636
|
+
});
|
|
2637
|
+
return null;
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
async function commitExists(cwd, sha) {
|
|
2641
|
+
try {
|
|
2642
|
+
await runGit(["rev-parse", "--verify", "--quiet", `${sha}^{commit}`], cwd);
|
|
2643
|
+
return true;
|
|
2644
|
+
} catch {
|
|
2645
|
+
return false;
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
async function resolveBaseFor(cwd, baseline) {
|
|
2649
|
+
if (!baseline) return resolveComparisonBase(cwd);
|
|
2650
|
+
if (baseline.baseSha !== null && !await commitExists(cwd, baseline.baseSha)) {
|
|
2651
|
+
return resolveComparisonBase(cwd);
|
|
2652
|
+
}
|
|
2653
|
+
return { sha: baseline.baseSha ?? EMPTY_TREE_SHA, ref: SESSION_START_REF };
|
|
2654
|
+
}
|
|
2655
|
+
async function dropPreexisting(cwd, entries, baseline) {
|
|
2656
|
+
if (baseline.preexisting.size === 0) return entries;
|
|
2657
|
+
const untouched = await Promise.all(
|
|
2658
|
+
entries.map(async (entry) => {
|
|
2659
|
+
const before = baseline.preexisting.get(entry.path);
|
|
2660
|
+
return before !== void 0 && before === await fingerprintFile(cwd, entry.path);
|
|
2661
|
+
})
|
|
2662
|
+
);
|
|
2663
|
+
return entries.filter((_, index) => !untouched[index]);
|
|
2664
|
+
}
|
|
2665
|
+
function encodeBaseline(baseline) {
|
|
2666
|
+
const serialized = {
|
|
2667
|
+
v: 1,
|
|
2668
|
+
base: baseline.baseSha,
|
|
2669
|
+
files: [...baseline.preexisting]
|
|
2670
|
+
};
|
|
2671
|
+
return Buffer.from(JSON.stringify(serialized), "utf8").toString("base64url");
|
|
2672
|
+
}
|
|
2673
|
+
function isSerializedBaseline(value) {
|
|
2674
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2675
|
+
const candidate = value;
|
|
2676
|
+
return candidate.v === 1 && (candidate.base === null || typeof candidate.base === "string") && Array.isArray(candidate.files) && candidate.files.every(
|
|
2677
|
+
(file) => Array.isArray(file) && file.length === 2 && typeof file[0] === "string" && typeof file[1] === "string"
|
|
2678
|
+
);
|
|
2679
|
+
}
|
|
2680
|
+
function decodeBaseline(encoded) {
|
|
2681
|
+
try {
|
|
2682
|
+
const parsed = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
2683
|
+
if (!isSerializedBaseline(parsed)) return null;
|
|
2684
|
+
return { baseSha: parsed.base, preexisting: new Map(parsed.files) };
|
|
2685
|
+
} catch {
|
|
2686
|
+
return null;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2577
2690
|
// src/sessions/git/status.ts
|
|
2578
2691
|
async function getCurrentBranch(cwd) {
|
|
2579
2692
|
try {
|
|
@@ -2669,15 +2782,15 @@ function parseNameStatus(output) {
|
|
|
2669
2782
|
}
|
|
2670
2783
|
return changed;
|
|
2671
2784
|
}
|
|
2672
|
-
async function getWorkspaceStatus(cwd) {
|
|
2785
|
+
async function getWorkspaceStatus(cwd, baseline) {
|
|
2673
2786
|
const branch = await getCurrentBranch(cwd);
|
|
2674
2787
|
if (branch === null && !await isGitRepo(cwd)) {
|
|
2675
2788
|
return { isGitRepo: false, branch: null, files: [] };
|
|
2676
2789
|
}
|
|
2677
|
-
const base = await
|
|
2790
|
+
const base = await resolveBaseFor(cwd, baseline);
|
|
2678
2791
|
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
2679
|
-
runGitDiff(["--name-status", "-z", "-M", base.sha], cwd),
|
|
2680
|
-
runGitDiff(["--numstat", "-z", "-M", base.sha], cwd),
|
|
2792
|
+
runGitDiff(["--relative", "--name-status", "-z", "-M", base.sha], cwd),
|
|
2793
|
+
runGitDiff(["--relative", "--numstat", "-z", "-M", base.sha], cwd),
|
|
2681
2794
|
runGit(["ls-files", "--others", "--exclude-standard", "-z"], cwd)
|
|
2682
2795
|
]);
|
|
2683
2796
|
const numstat = parseNumstat(numstatRaw);
|
|
@@ -2701,13 +2814,19 @@ async function getWorkspaceStatus(cwd) {
|
|
|
2701
2814
|
deletions: null,
|
|
2702
2815
|
binary: false
|
|
2703
2816
|
}));
|
|
2704
|
-
|
|
2817
|
+
const files = [...tracked, ...untracked];
|
|
2818
|
+
return {
|
|
2819
|
+
isGitRepo: true,
|
|
2820
|
+
branch,
|
|
2821
|
+
baseRef: base.ref,
|
|
2822
|
+
files: baseline ? await dropPreexisting(cwd, files, baseline) : files
|
|
2823
|
+
};
|
|
2705
2824
|
}
|
|
2706
|
-
async function enrichSessionWithGitMetadata(session) {
|
|
2825
|
+
async function enrichSessionWithGitMetadata(session, baseline) {
|
|
2707
2826
|
const [branch, repositoryFullName, status, lastActivityAt] = await Promise.all([
|
|
2708
2827
|
getCurrentBranch(session.cwd),
|
|
2709
2828
|
getRepositoryFullName(session.cwd),
|
|
2710
|
-
getWorkspaceStatus(session.cwd),
|
|
2829
|
+
getWorkspaceStatus(session.cwd, baseline),
|
|
2711
2830
|
getPaneActivity(session.tmuxSessionName)
|
|
2712
2831
|
]);
|
|
2713
2832
|
const changedFileCount = status.isGitRepo ? new Set(status.files.map((f) => f.path)).size : null;
|
|
@@ -2722,7 +2841,7 @@ async function enrichSessionWithGitMetadata(session) {
|
|
|
2722
2841
|
|
|
2723
2842
|
// src/sessions/git/diff.ts
|
|
2724
2843
|
async function withRenameSource(cwd, baseSha, path) {
|
|
2725
|
-
const nameStatus = await runGitDiff(["--name-status", "-z", "-M", baseSha], cwd);
|
|
2844
|
+
const nameStatus = await runGitDiff(["--relative", "--name-status", "-z", "-M", baseSha], cwd);
|
|
2726
2845
|
const previousPath = parseNameStatus(nameStatus).find((c) => c.path === path)?.previousPath;
|
|
2727
2846
|
return previousPath === void 0 ? [path] : [previousPath, path];
|
|
2728
2847
|
}
|
|
@@ -2730,15 +2849,18 @@ async function isUntracked(cwd, path) {
|
|
|
2730
2849
|
const out = await runGit(["ls-files", "--others", "--exclude-standard", "--", path], cwd);
|
|
2731
2850
|
return out.trim() !== "";
|
|
2732
2851
|
}
|
|
2733
|
-
async function getWorkspaceDiff(cwd, path, staged) {
|
|
2734
|
-
const base = await
|
|
2852
|
+
async function getWorkspaceDiff(cwd, path, staged, baseline) {
|
|
2853
|
+
const base = await resolveBaseFor(cwd, baseline);
|
|
2735
2854
|
const pathspec = await withRenameSource(cwd, base.sha, path);
|
|
2736
|
-
const numstat = await runGitDiff(
|
|
2855
|
+
const numstat = await runGitDiff(
|
|
2856
|
+
["--relative", "--numstat", "-M", base.sha, "--", ...pathspec],
|
|
2857
|
+
cwd
|
|
2858
|
+
);
|
|
2737
2859
|
const binary = /^(-\t-\t)/.test(numstat.trim());
|
|
2738
2860
|
if (binary) {
|
|
2739
2861
|
return { path, staged, patch: null, binary: true };
|
|
2740
2862
|
}
|
|
2741
|
-
let patch = await runGitDiff(["-M", base.sha, "--", ...pathspec], cwd);
|
|
2863
|
+
let patch = await runGitDiff(["--relative", "-M", base.sha, "--", ...pathspec], cwd);
|
|
2742
2864
|
if (patch.trim() === "" && await isUntracked(cwd, path)) {
|
|
2743
2865
|
patch = await runGitDiffAllowingExitCodes(["--no-index", "--", "/dev/null", path], cwd, [
|
|
2744
2866
|
1
|
|
@@ -2935,7 +3057,7 @@ async function removeWorktree(sourceCwd, worktreePath) {
|
|
|
2935
3057
|
// src/sessions/tmux/create.ts
|
|
2936
3058
|
import { access } from "fs/promises";
|
|
2937
3059
|
import { homedir as homedir5 } from "os";
|
|
2938
|
-
import { join as
|
|
3060
|
+
import { join as join6 } from "path";
|
|
2939
3061
|
var InvalidCwdError = class extends Error {
|
|
2940
3062
|
constructor(cwd) {
|
|
2941
3063
|
super(`working directory does not exist: ${cwd}`);
|
|
@@ -2943,7 +3065,7 @@ var InvalidCwdError = class extends Error {
|
|
|
2943
3065
|
}
|
|
2944
3066
|
};
|
|
2945
3067
|
function worktreePathFor(sessionId) {
|
|
2946
|
-
return
|
|
3068
|
+
return join6(homedir5(), ".bondsmith", "worktrees", sessionId);
|
|
2947
3069
|
}
|
|
2948
3070
|
async function createTmuxSession(input) {
|
|
2949
3071
|
const sourceCwd = expandHome(input.cwd);
|
|
@@ -2962,6 +3084,7 @@ async function createTmuxSession(input) {
|
|
|
2962
3084
|
}
|
|
2963
3085
|
const tmuxSessionName = tmuxSessionNameFor(input.id);
|
|
2964
3086
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3087
|
+
const baseline = await captureBaseline(cwd, { freshCheckout: worktreeOf !== null });
|
|
2965
3088
|
await runTmux(["new-session", "-d", "-s", tmuxSessionName, "-c", cwd, input.command], {
|
|
2966
3089
|
env: filterSpawnEnv(process.env)
|
|
2967
3090
|
});
|
|
@@ -2973,6 +3096,15 @@ async function createTmuxSession(input) {
|
|
|
2973
3096
|
if (worktreeOf) {
|
|
2974
3097
|
await runTmux(["set-option", "-t", tmuxSessionName, "@bondsmith-worktree-of", worktreeOf]);
|
|
2975
3098
|
}
|
|
3099
|
+
if (baseline) {
|
|
3100
|
+
await runTmux([
|
|
3101
|
+
"set-option",
|
|
3102
|
+
"-t",
|
|
3103
|
+
tmuxSessionName,
|
|
3104
|
+
"@bondsmith-baseline",
|
|
3105
|
+
encodeBaseline(baseline)
|
|
3106
|
+
]);
|
|
3107
|
+
}
|
|
2976
3108
|
const session = {
|
|
2977
3109
|
id: input.id,
|
|
2978
3110
|
title: input.title,
|
|
@@ -2987,7 +3119,7 @@ async function createTmuxSession(input) {
|
|
|
2987
3119
|
changedFileCount: null,
|
|
2988
3120
|
worktreeOf
|
|
2989
3121
|
};
|
|
2990
|
-
return enrichSessionWithGitMetadata(session);
|
|
3122
|
+
return { session: await enrichSessionWithGitMetadata(session, baseline), baseline };
|
|
2991
3123
|
}
|
|
2992
3124
|
async function stopTmuxSession(tmuxSessionName) {
|
|
2993
3125
|
await runTmux(["kill-session", "-t", tmuxSessionName]);
|
|
@@ -2995,6 +3127,7 @@ async function stopTmuxSession(tmuxSessionName) {
|
|
|
2995
3127
|
|
|
2996
3128
|
// src/sessions/tmux/parse.ts
|
|
2997
3129
|
var FIELD_COUNT = 7;
|
|
3130
|
+
var BASELINE_FIELD_INDEX = 7;
|
|
2998
3131
|
function parseTmuxSessions(output) {
|
|
2999
3132
|
const sessions = [];
|
|
3000
3133
|
for (const line of output.split("\n")) {
|
|
@@ -3024,6 +3157,19 @@ function parseTmuxSessions(output) {
|
|
|
3024
3157
|
}
|
|
3025
3158
|
return sessions;
|
|
3026
3159
|
}
|
|
3160
|
+
function parseSessionBaselines(output) {
|
|
3161
|
+
const baselines = /* @__PURE__ */ new Map();
|
|
3162
|
+
for (const line of output.split("\n")) {
|
|
3163
|
+
const fields = line.split(" ");
|
|
3164
|
+
const sessionName = fields[0];
|
|
3165
|
+
const id = fields[1];
|
|
3166
|
+
const encoded = fields[BASELINE_FIELD_INDEX];
|
|
3167
|
+
if (!sessionName || !isBondsmithTmuxSessionName(sessionName) || !id || !encoded) continue;
|
|
3168
|
+
const baseline = decodeBaseline(encoded);
|
|
3169
|
+
if (baseline) baselines.set(id, baseline);
|
|
3170
|
+
}
|
|
3171
|
+
return baselines;
|
|
3172
|
+
}
|
|
3027
3173
|
|
|
3028
3174
|
// src/sessions/tmux/discover.ts
|
|
3029
3175
|
var LIST_FORMAT = [
|
|
@@ -3033,15 +3179,21 @@ var LIST_FORMAT = [
|
|
|
3033
3179
|
"#{@bondsmith-command}",
|
|
3034
3180
|
"#{@bondsmith-cwd}",
|
|
3035
3181
|
"#{@bondsmith-started-at}",
|
|
3036
|
-
"#{@bondsmith-worktree-of}"
|
|
3182
|
+
"#{@bondsmith-worktree-of}",
|
|
3183
|
+
"#{@bondsmith-baseline}"
|
|
3037
3184
|
].join(" ");
|
|
3038
3185
|
async function discoverSessions() {
|
|
3039
3186
|
try {
|
|
3040
3187
|
const output = await listTmuxSessions(LIST_FORMAT);
|
|
3041
|
-
const
|
|
3042
|
-
|
|
3188
|
+
const baselines = parseSessionBaselines(output);
|
|
3189
|
+
const sessions = await Promise.all(
|
|
3190
|
+
parseTmuxSessions(output).map(
|
|
3191
|
+
(session) => enrichSessionWithGitMetadata(session, baselines.get(session.id))
|
|
3192
|
+
)
|
|
3193
|
+
);
|
|
3194
|
+
return { sessions, baselines };
|
|
3043
3195
|
} catch (error2) {
|
|
3044
|
-
if (error2 instanceof TmuxNoServerRunningError) return [];
|
|
3196
|
+
if (error2 instanceof TmuxNoServerRunningError) return { sessions: [], baselines: /* @__PURE__ */ new Map() };
|
|
3045
3197
|
if (error2 instanceof TmuxNotFoundError) {
|
|
3046
3198
|
console.error(`
|
|
3047
3199
|
${error2.message}
|
|
@@ -3055,13 +3207,19 @@ ${error2.message}
|
|
|
3055
3207
|
// src/sessions/session-manager.ts
|
|
3056
3208
|
var SessionManager = class {
|
|
3057
3209
|
sessions = /* @__PURE__ */ new Map();
|
|
3210
|
+
// What each session's folder looked like when it started, so its file list shows only its own changes.
|
|
3211
|
+
baselines = /* @__PURE__ */ new Map();
|
|
3058
3212
|
listeners = /* @__PURE__ */ new Set();
|
|
3059
3213
|
async initialize() {
|
|
3060
3214
|
const discovered = await discoverSessions();
|
|
3061
3215
|
this.sessions.clear();
|
|
3062
|
-
|
|
3216
|
+
this.baselines.clear();
|
|
3217
|
+
for (const session of discovered.sessions) {
|
|
3063
3218
|
this.sessions.set(session.id, session);
|
|
3064
3219
|
}
|
|
3220
|
+
for (const [id, baseline] of discovered.baselines) {
|
|
3221
|
+
this.baselines.set(id, baseline);
|
|
3222
|
+
}
|
|
3065
3223
|
}
|
|
3066
3224
|
list() {
|
|
3067
3225
|
return Array.from(this.sessions.values());
|
|
@@ -3069,6 +3227,10 @@ var SessionManager = class {
|
|
|
3069
3227
|
get(sessionId) {
|
|
3070
3228
|
return this.sessions.get(sessionId);
|
|
3071
3229
|
}
|
|
3230
|
+
/** Undefined for a session started before baselines existed, which compares against the default branch. */
|
|
3231
|
+
getBaseline(sessionId) {
|
|
3232
|
+
return this.baselines.get(sessionId);
|
|
3233
|
+
}
|
|
3072
3234
|
onSessionUpdated(listener) {
|
|
3073
3235
|
this.listeners.add(listener);
|
|
3074
3236
|
return () => this.listeners.delete(listener);
|
|
@@ -3078,8 +3240,9 @@ var SessionManager = class {
|
|
|
3078
3240
|
}
|
|
3079
3241
|
async create(input) {
|
|
3080
3242
|
const id = uuidv4();
|
|
3081
|
-
const session = await createTmuxSession({ id, ...input });
|
|
3243
|
+
const { session, baseline } = await createTmuxSession({ id, ...input });
|
|
3082
3244
|
this.sessions.set(id, session);
|
|
3245
|
+
if (baseline) this.baselines.set(id, baseline);
|
|
3083
3246
|
return session;
|
|
3084
3247
|
}
|
|
3085
3248
|
async stop(sessionId) {
|
|
@@ -3139,7 +3302,10 @@ ${error2.message}
|
|
|
3139
3302
|
return;
|
|
3140
3303
|
}
|
|
3141
3304
|
try {
|
|
3142
|
-
const updated = await enrichSessionWithGitMetadata(
|
|
3305
|
+
const updated = await enrichSessionWithGitMetadata(
|
|
3306
|
+
session,
|
|
3307
|
+
this.baselines.get(session.id)
|
|
3308
|
+
);
|
|
3143
3309
|
if (!gitSnapshotEquals(session, updated)) {
|
|
3144
3310
|
this.sessions.set(session.id, updated);
|
|
3145
3311
|
this.emitUpdated(updated);
|
|
@@ -3156,6 +3322,7 @@ ${error2.message}
|
|
|
3156
3322
|
removeAsExited(session) {
|
|
3157
3323
|
const updated = { ...session, status: "exited" };
|
|
3158
3324
|
this.sessions.delete(session.id);
|
|
3325
|
+
this.baselines.delete(session.id);
|
|
3159
3326
|
this.emitUpdated(updated);
|
|
3160
3327
|
return updated;
|
|
3161
3328
|
}
|
|
@@ -3316,10 +3483,10 @@ if (isVersionRequest(process.argv.slice(2))) {
|
|
|
3316
3483
|
process.exit(0);
|
|
3317
3484
|
}
|
|
3318
3485
|
function loadRepoRootEnv() {
|
|
3319
|
-
const repoRoot =
|
|
3320
|
-
if (!existsSync(
|
|
3321
|
-
const envLocal =
|
|
3322
|
-
const envDefault =
|
|
3486
|
+
const repoRoot = join7(dirname4(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
3487
|
+
if (!existsSync(join7(repoRoot, "pnpm-workspace.yaml"))) return;
|
|
3488
|
+
const envLocal = join7(repoRoot, ".env.local");
|
|
3489
|
+
const envDefault = join7(repoRoot, ".env");
|
|
3323
3490
|
loadEnv({ path: existsSync(envLocal) ? envLocal : envDefault });
|
|
3324
3491
|
}
|
|
3325
3492
|
loadRepoRootEnv();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bondsmith/bondsmithd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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",
|