clay-server 2.41.0 → 2.41.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.
@@ -110,6 +110,24 @@ function attachSessions(ctx) {
110
110
  try { return usersModule.getClaudeOpenMode(uid) || "tui"; } catch (e) { return "tui"; }
111
111
  }
112
112
 
113
+ // Resolve the home directory where Claude Code writes JSONL for a
114
+ // given session. In OS-isolation mode each Clay user runs as a real
115
+ // Linux account so JSONL lives under /home/clay-name; for single-user
116
+ // installs we fall back to the daemon's own home.
117
+ function resolveSessionHome(session) {
118
+ var home = null;
119
+ if (osUsers && session && session.ownerId) {
120
+ try {
121
+ var ownerUser = usersModule.findUserById ? usersModule.findUserById(session.ownerId) : null;
122
+ if (ownerUser && ownerUser.linuxUser) {
123
+ var info = require("./os-users").resolveOsUserInfo(ownerUser.linuxUser);
124
+ if (info && info.home) home = info.home;
125
+ }
126
+ } catch (e) {}
127
+ }
128
+ return home || require("os").homedir();
129
+ }
130
+
113
131
  // Watch the per-session jsonl Claude Code writes and mirror its auto /
114
132
  // user titles into Clay's session.title. Lets TUI sessions move past the
115
133
  // generic "New Session" label without us having to scrape the PTY output.
@@ -120,19 +138,7 @@ function attachSessions(ctx) {
120
138
  if (session._titleWatcherStop) return; // already watching
121
139
  var watcher;
122
140
  try { watcher = require("./claude-jsonl-watcher"); } catch (e) { return; }
123
- // Resolve the owning OS user's home for OS-isolation mode; fall back
124
- // to the daemon's home for single-user mode.
125
- var home = null;
126
- if (osUsers && session.ownerId) {
127
- try {
128
- var ownerUser = usersModule.findUserById ? usersModule.findUserById(session.ownerId) : null;
129
- if (ownerUser && ownerUser.linuxUser) {
130
- var info = require("./os-users").resolveOsUserInfo(ownerUser.linuxUser);
131
- if (info && info.home) home = info.home;
132
- }
133
- } catch (e) {}
134
- }
135
- if (!home) home = require("os").homedir();
141
+ var home = resolveSessionHome(session);
136
142
  var jsonlPath = watcher.jsonlPathFor(home, cwd, session.cliSessionId);
137
143
  if (!jsonlPath) return;
138
144
  var localId = session.localId;
@@ -181,7 +187,7 @@ function attachSessions(ctx) {
181
187
  // message right away. The transcript is small enough that a full
182
188
  // re-send beats maintaining a delta protocol.
183
189
  try {
184
- var newIndex = require("./tui-transcript-index").readAssistantIndex(cwd, s.cliSessionId);
190
+ var newIndex = require("./tui-transcript-index").readAssistantIndex(resolveSessionHome(s), cwd, s.cliSessionId);
185
191
  send({
186
192
  type: "tui_transcript_state",
187
193
  id: s.localId,
@@ -707,7 +713,7 @@ function attachSessions(ctx) {
707
713
  return true;
708
714
  }
709
715
  try {
710
- var tprIndex = require("./tui-transcript-index").readAssistantIndex(cwd, tprSess.cliSessionId);
716
+ var tprIndex = require("./tui-transcript-index").readAssistantIndex(resolveSessionHome(tprSess), cwd, tprSess.cliSessionId);
711
717
  sendTo(ws, {
712
718
  type: "tui_transcript_state",
713
719
  id: tprId,
@@ -24,9 +24,16 @@ var encodeCwd = utils.encodeCwd;
24
24
  // positives when matched against a hovered terminal block.
25
25
  var MIN_MATCH_LEN = 20;
26
26
 
27
- function transcriptFilePath(cwd, cliSessionId) {
27
+ // Claude Code writes its JSONL under the *running user's* home, which
28
+ // is not the daemon's home in OS-isolation mode (where each Clay user
29
+ // gets a real Linux account like /home/clay-name). Callers from the
30
+ // project context resolve the session owner's home and pass it in;
31
+ // when no home is provided we fall back to REAL_HOME for single-user
32
+ // installs.
33
+ function transcriptFilePath(home, cwd, cliSessionId) {
28
34
  if (!cwd || !cliSessionId) return null;
29
- return path.join(REAL_HOME, ".claude", "projects", encodeCwd(cwd), cliSessionId + ".jsonl");
35
+ var base = home || REAL_HOME;
36
+ return path.join(base, ".claude", "projects", encodeCwd(cwd), cliSessionId + ".jsonl");
30
37
  }
31
38
 
32
39
  // matchKey is just whitespace-normalized raw markdown. We intentionally
@@ -91,8 +98,8 @@ function parseAssistantTextLine(line, index) {
91
98
  // session open or on a watcher-driven update is fine. We also return
92
99
  // the mtime/size so callers can short-circuit re-reads when nothing
93
100
  // changed.
94
- function readAssistantIndex(cwd, cliSessionId) {
95
- var file = transcriptFilePath(cwd, cliSessionId);
101
+ function readAssistantIndex(home, cwd, cliSessionId) {
102
+ var file = transcriptFilePath(home, cwd, cliSessionId);
96
103
  if (!file) return { messages: [], mtimeMs: 0, byteLength: 0 };
97
104
  var raw;
98
105
  var stat;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.41.0",
3
+ "version": "2.41.1",
4
4
  "description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",