claude-flow 3.16.0 → 3.16.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.16.
|
|
3
|
+
"version": "3.16.1",
|
|
4
4
|
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -98,14 +98,26 @@ const startCommand = {
|
|
|
98
98
|
// dedup is process-atomic. Lock holder gets to spawn; competing callers
|
|
99
99
|
// see EEXIST, wait briefly, then re-read the PID file (which the holder
|
|
100
100
|
// has now written), and return "already running" cleanly.
|
|
101
|
+
// #2484 — previously the lockfile was released BEFORE startBackgroundDaemon
|
|
102
|
+
// ran, opening a race window where a concurrent caller could see no lock
|
|
103
|
+
// AND no PID file (PID hadn't been written yet) and proceed to fork ITS
|
|
104
|
+
// OWN background daemon. EDortta reported 4 identical daemons per Claude
|
|
105
|
+
// Code session on v3.10.37 under exactly this pattern (MCP startup
|
|
106
|
+
// racing with a sibling invocation).
|
|
107
|
+
//
|
|
108
|
+
// Fix: hold the lock across the entire spawn lifecycle (dedup check →
|
|
109
|
+
// killStaleDaemons → fork → PID file write), so the lock-loser ALWAYS
|
|
110
|
+
// sees either a live lock or a populated PID file, never the empty
|
|
111
|
+
// window in between.
|
|
112
|
+
let lockFd = null;
|
|
113
|
+
let lockFile = '';
|
|
101
114
|
if (!isDaemonProcess) {
|
|
102
115
|
const stateDir = join(resolve(projectRoot), '.claude-flow');
|
|
103
|
-
|
|
116
|
+
lockFile = join(stateDir, 'daemon.lock');
|
|
104
117
|
try {
|
|
105
118
|
fs.mkdirSync(stateDir, { recursive: true });
|
|
106
119
|
}
|
|
107
120
|
catch { /* exists */ }
|
|
108
|
-
let lockFd = null;
|
|
109
121
|
try {
|
|
110
122
|
lockFd = fs.openSync(lockFile, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY);
|
|
111
123
|
fs.writeSync(lockFd, String(process.pid));
|
|
@@ -139,22 +151,33 @@ const startCommand = {
|
|
|
139
151
|
throw e;
|
|
140
152
|
}
|
|
141
153
|
}
|
|
154
|
+
// Dedup check while holding the lock.
|
|
142
155
|
try {
|
|
143
156
|
const bgPid = getBackgroundDaemonPid(projectRoot);
|
|
144
157
|
if (bgPid && isProcessRunning(bgPid)) {
|
|
145
158
|
if (!quiet) {
|
|
146
159
|
output.printWarning(`Daemon already running in background (PID: ${bgPid}). Stop it first with: daemon stop`);
|
|
147
160
|
}
|
|
161
|
+
// Release the lock on the early-return path.
|
|
162
|
+
if (lockFd !== null) {
|
|
163
|
+
try {
|
|
164
|
+
fs.closeSync(lockFd);
|
|
165
|
+
}
|
|
166
|
+
catch { /* ignore */ }
|
|
167
|
+
try {
|
|
168
|
+
fs.unlinkSync(lockFile);
|
|
169
|
+
}
|
|
170
|
+
catch { /* ignore */ }
|
|
171
|
+
}
|
|
148
172
|
return { success: true };
|
|
149
173
|
}
|
|
150
174
|
// #1551: Kill any stale daemon processes that weren't tracked by PID file
|
|
151
175
|
await killStaleDaemons(projectRoot, quiet);
|
|
152
176
|
}
|
|
153
|
-
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
// post-release re-check finds it.
|
|
177
|
+
catch (e) {
|
|
178
|
+
// Anything that throws here MUST still release the lock before
|
|
179
|
+
// rethrowing so we don't leave the lockfile behind for future
|
|
180
|
+
// invocations to wait on for 5s before recovering.
|
|
158
181
|
if (lockFd !== null) {
|
|
159
182
|
try {
|
|
160
183
|
fs.closeSync(lockFd);
|
|
@@ -164,23 +187,58 @@ const startCommand = {
|
|
|
164
187
|
fs.unlinkSync(lockFile);
|
|
165
188
|
}
|
|
166
189
|
catch { /* ignore */ }
|
|
190
|
+
lockFd = null;
|
|
167
191
|
}
|
|
192
|
+
throw e;
|
|
168
193
|
}
|
|
169
194
|
}
|
|
170
|
-
// Background mode (default): fork a detached process.
|
|
195
|
+
// Background mode (default): fork a detached process. The lock (if held)
|
|
196
|
+
// is released AFTER startBackgroundDaemon's PID file write completes —
|
|
197
|
+
// see the cleanup inside startBackgroundDaemon's caller path below.
|
|
171
198
|
// #1968: previously only forwarded resource thresholds — `--workers`,
|
|
172
199
|
// `--headless`, and `--sandbox` were dropped on the floor when the
|
|
173
200
|
// launcher forked the foreground child, so `daemon start --workers map`
|
|
174
201
|
// got the full default worker set instead.
|
|
175
202
|
if (!foreground) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
203
|
+
try {
|
|
204
|
+
return await startBackgroundDaemon(projectRoot, quiet, {
|
|
205
|
+
maxCpuLoad: rawMaxCpu,
|
|
206
|
+
minFreeMemory: rawMinMem,
|
|
207
|
+
workers: ctx.flags.workers,
|
|
208
|
+
headless: ctx.flags.headless,
|
|
209
|
+
sandbox: ctx.flags.sandbox,
|
|
210
|
+
ttl: rawTtl,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
// Release the lock NOW — startBackgroundDaemon has either written
|
|
215
|
+
// the PID file (success path) or thrown (in which case the next
|
|
216
|
+
// caller's killStaleDaemons sweep handles the orphan).
|
|
217
|
+
if (lockFd !== null) {
|
|
218
|
+
try {
|
|
219
|
+
fs.closeSync(lockFd);
|
|
220
|
+
}
|
|
221
|
+
catch { /* ignore */ }
|
|
222
|
+
try {
|
|
223
|
+
fs.unlinkSync(lockFile);
|
|
224
|
+
}
|
|
225
|
+
catch { /* ignore */ }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Foreground path: release the lock before we start the (potentially
|
|
230
|
+
// long-running) foreground daemon so other callers can dedup against
|
|
231
|
+
// our PID file (foreground writes its own PID).
|
|
232
|
+
if (lockFd !== null) {
|
|
233
|
+
try {
|
|
234
|
+
fs.closeSync(lockFd);
|
|
235
|
+
}
|
|
236
|
+
catch { /* ignore */ }
|
|
237
|
+
try {
|
|
238
|
+
fs.unlinkSync(lockFile);
|
|
239
|
+
}
|
|
240
|
+
catch { /* ignore */ }
|
|
241
|
+
lockFd = null;
|
|
184
242
|
}
|
|
185
243
|
// Foreground mode: run in current process (blocks terminal)
|
|
186
244
|
try {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.16.
|
|
3
|
+
"version": "3.16.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|