clay-server 3.2.2-beta.1 → 3.2.2-beta.3

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.
@@ -6,12 +6,16 @@
6
6
 
7
7
  var fs = require("fs");
8
8
  var { scanWorktrees, isWorktree } = require("./worktree");
9
+ var { daemonSync } = require("./daemon-sync");
9
10
 
10
11
  // --- Worktree tracking state ---
11
12
  var worktreeRegistry = {}; // parentSlug -> [wtSlug, ...]
12
- var worktreeTimers = {}; // parentSlug -> intervalId
13
13
  var worktreeScanning = {}; // parentSlug -> boolean (mutex)
14
14
 
15
+ function getWorktreeSyncKey(parentSlug) {
16
+ return "worktrees:" + parentSlug;
17
+ }
18
+
15
19
  function isWorktreeSlug(slug) {
16
20
  return slug.indexOf("--") !== -1;
17
21
  }
@@ -42,11 +46,9 @@ function scanAndRegisterWorktrees(relay, parentPath, parentSlug, parentIcon, par
42
46
  worktreeRegistry[parentSlug].push(wtSlug);
43
47
  console.log("[daemon] Registered worktree:", wtSlug, "->", wt.path, wt.accessible ? "(accessible)" : "(inaccessible)");
44
48
  }
45
- if (!worktreeTimers[parentSlug]) {
46
- worktreeTimers[parentSlug] = setInterval(function () {
47
- rescanWorktrees(relay, parentPath, parentSlug, parentIcon, parentOwnerId);
48
- }, 10000);
49
- }
49
+ daemonSync.register(getWorktreeSyncKey(parentSlug), function () {
50
+ rescanWorktrees(relay, parentPath, parentSlug, parentIcon, parentOwnerId);
51
+ });
50
52
  }
51
53
 
52
54
  /**
@@ -114,10 +116,7 @@ function cleanupWorktreesForParent(relay, parentSlug) {
114
116
  console.log("[daemon] Cascade removed worktree:", wtSlugs[i]);
115
117
  }
116
118
  delete worktreeRegistry[parentSlug];
117
- if (worktreeTimers[parentSlug]) {
118
- clearInterval(worktreeTimers[parentSlug]);
119
- delete worktreeTimers[parentSlug];
120
- }
119
+ daemonSync.unregister(getWorktreeSyncKey(parentSlug));
121
120
  }
122
121
 
123
122
  /**
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Shared daemon synchronization loop.
3
+ *
4
+ * Periodic daemon work registers here so every project shares one timer.
5
+ */
6
+
7
+ function attachDaemonSync(ctx) {
8
+ var intervalMs = ctx.intervalMs || 10000;
9
+ var scheduleInterval = ctx.setInterval || setInterval;
10
+ var cancelInterval = ctx.clearInterval || clearInterval;
11
+ var onError = ctx.onError || function (key, err) {
12
+ console.error("[daemon] Sync task failed:", key, err);
13
+ };
14
+ var tasks = {};
15
+ var timer = null;
16
+
17
+ function finishTask(task) {
18
+ task.running = false;
19
+ }
20
+
21
+ function runTask(key, task) {
22
+ if (task.running) return;
23
+ task.running = true;
24
+ var result;
25
+ try {
26
+ result = task.run();
27
+ } catch (err) {
28
+ finishTask(task);
29
+ onError(key, err);
30
+ return;
31
+ }
32
+ if (result && typeof result.then === "function") {
33
+ Promise.resolve(result).then(function () {
34
+ finishTask(task);
35
+ }, function (err) {
36
+ finishTask(task);
37
+ onError(key, err);
38
+ });
39
+ return;
40
+ }
41
+ finishTask(task);
42
+ }
43
+
44
+ function tick() {
45
+ var keys = Object.keys(tasks);
46
+ for (var i = 0; i < keys.length; i++) {
47
+ var key = keys[i];
48
+ var task = tasks[key];
49
+ if (task) runTask(key, task);
50
+ }
51
+ }
52
+
53
+ function start() {
54
+ if (timer || Object.keys(tasks).length === 0) return;
55
+ timer = scheduleInterval(tick, intervalMs);
56
+ if (timer && typeof timer.unref === "function") timer.unref();
57
+ }
58
+
59
+ function register(key, run) {
60
+ var current = tasks[key];
61
+ if (current) {
62
+ current.run = run;
63
+ } else {
64
+ tasks[key] = { run: run, running: false };
65
+ }
66
+ start();
67
+ }
68
+
69
+ function unregister(key) {
70
+ delete tasks[key];
71
+ if (Object.keys(tasks).length === 0 && timer) {
72
+ cancelInterval(timer);
73
+ timer = null;
74
+ }
75
+ }
76
+
77
+ function stop() {
78
+ if (timer) cancelInterval(timer);
79
+ timer = null;
80
+ tasks = {};
81
+ }
82
+
83
+ function getTaskCount() {
84
+ return Object.keys(tasks).length;
85
+ }
86
+
87
+ function isRunning() {
88
+ return !!timer;
89
+ }
90
+
91
+ return {
92
+ register: register,
93
+ unregister: unregister,
94
+ tick: tick,
95
+ stop: stop,
96
+ getTaskCount: getTaskCount,
97
+ isRunning: isRunning,
98
+ };
99
+ }
100
+
101
+ var daemonSync = attachDaemonSync({});
102
+
103
+ module.exports = {
104
+ attachDaemonSync: attachDaemonSync,
105
+ daemonSync: daemonSync,
106
+ };
@@ -83,7 +83,7 @@ body.pane-mode #input-area {
83
83
  gap: 6px;
84
84
  padding: 0 5px 0 9px;
85
85
  border-bottom: 1px solid var(--border-subtle);
86
- background: var(--sidebar-bg);
86
+ background: var(--bg);
87
87
  color: var(--text-secondary);
88
88
  flex-shrink: 0;
89
89
  }
@@ -194,7 +194,9 @@ button.split-pair-role:hover { filter: brightness(1.25); }
194
194
  display: none;
195
195
  align-items: center;
196
196
  gap: 6px;
197
- margin: 0 12px;
197
+ width: 100%;
198
+ max-width: var(--content-width);
199
+ margin: 0 auto;
198
200
  padding: 4px 10px;
199
201
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
200
202
  border-bottom: 0;
@@ -205,6 +207,9 @@ button.split-pair-role:hover { filter: brightness(1.25); }
205
207
  line-height: 1.4;
206
208
  }
207
209
  .pane-delegation-notice.visible { display: flex; }
210
+ .pane-delegation-notice.visible + #input-wrapper #input-row {
211
+ border-radius: 0 0 8px 8px;
212
+ }
208
213
  .pane-delegation-notice::before {
209
214
  content: "";
210
215
  width: 6px;
@@ -260,11 +260,12 @@ var workerNoticeEl = null;
260
260
  export function showWorkerDelegationNotice(driverTitle) {
261
261
  if (!store.get('paneMode')) return;
262
262
  var inputArea = document.getElementById("input-area");
263
- if (!inputArea || !inputArea.parentNode) return;
263
+ var inputWrapper = document.getElementById("input-wrapper");
264
+ if (!inputArea || !inputWrapper) return;
264
265
  if (!workerNoticeEl) {
265
266
  workerNoticeEl = document.createElement("div");
266
267
  workerNoticeEl.className = "pane-delegation-notice";
267
- inputArea.parentNode.insertBefore(workerNoticeEl, inputArea);
268
+ inputArea.insertBefore(workerNoticeEl, inputWrapper);
268
269
  }
269
270
  var who = driverTitle ? "“" + driverTitle + "”" : "the Driver";
270
271
  workerNoticeEl.textContent = "Following an instruction from " + who + " — messages you send now join the same turn.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "3.2.2-beta.1",
3
+ "version": "3.2.2-beta.3",
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",