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

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
+ };
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.2",
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",