@claude-flow/cli 3.38.9 → 3.38.12
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/.claude/helpers/.helpers-version +1 -1
- package/.claude/helpers/helpers.manifest.json +2 -2
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/doctor.js +53 -8
- package/dist/src/commands/hooks.js +3 -2
- package/dist/src/commands/mcp.js +4 -3
- package/dist/src/init/executor.d.ts +6 -0
- package/dist/src/init/executor.js +5 -3
- package/dist/src/init/helper-refresh.d.ts +16 -9
- package/dist/src/init/helper-refresh.js +177 -20
- package/dist/src/init/settings-generator.js +6 -2
- package/dist/src/log-filters.d.ts +3 -3
- package/dist/src/mcp-server.d.ts +1 -0
- package/dist/src/mcp-server.js +39 -3
- package/dist/src/mcp-tools/neural-tools.js +4 -3
- package/dist/src/memory/memory-bridge.d.ts +8 -0
- package/dist/src/memory/memory-bridge.js +18 -0
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/mcp/README.md +429 -0
- package/node_modules/@claude-flow/mcp/dist/connection-pool.d.ts +36 -0
- package/node_modules/@claude-flow/mcp/dist/connection-pool.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/connection-pool.js +273 -0
- package/node_modules/@claude-flow/mcp/dist/connection-pool.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/index.d.ts +75 -0
- package/node_modules/@claude-flow/mcp/dist/index.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/index.js +85 -0
- package/node_modules/@claude-flow/mcp/dist/index.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/oauth.d.ts +146 -0
- package/node_modules/@claude-flow/mcp/dist/oauth.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/oauth.js +318 -0
- package/node_modules/@claude-flow/mcp/dist/oauth.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/prompt-registry.d.ts +90 -0
- package/node_modules/@claude-flow/mcp/dist/prompt-registry.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/prompt-registry.js +209 -0
- package/node_modules/@claude-flow/mcp/dist/prompt-registry.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/rate-limiter.d.ts +86 -0
- package/node_modules/@claude-flow/mcp/dist/rate-limiter.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/rate-limiter.js +197 -0
- package/node_modules/@claude-flow/mcp/dist/rate-limiter.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/resource-registry.d.ts +144 -0
- package/node_modules/@claude-flow/mcp/dist/resource-registry.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/resource-registry.js +405 -0
- package/node_modules/@claude-flow/mcp/dist/resource-registry.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/sampling.d.ts +102 -0
- package/node_modules/@claude-flow/mcp/dist/sampling.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/sampling.js +268 -0
- package/node_modules/@claude-flow/mcp/dist/sampling.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/schema-validator.d.ts +30 -0
- package/node_modules/@claude-flow/mcp/dist/schema-validator.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/schema-validator.js +182 -0
- package/node_modules/@claude-flow/mcp/dist/schema-validator.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/server.d.ts +125 -0
- package/node_modules/@claude-flow/mcp/dist/server.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/server.js +849 -0
- package/node_modules/@claude-flow/mcp/dist/server.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/session-manager.d.ts +55 -0
- package/node_modules/@claude-flow/mcp/dist/session-manager.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/session-manager.js +252 -0
- package/node_modules/@claude-flow/mcp/dist/session-manager.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/task-manager.d.ts +81 -0
- package/node_modules/@claude-flow/mcp/dist/task-manager.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/task-manager.js +337 -0
- package/node_modules/@claude-flow/mcp/dist/task-manager.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/tool-registry.d.ts +96 -0
- package/node_modules/@claude-flow/mcp/dist/tool-registry.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/tool-registry.js +399 -0
- package/node_modules/@claude-flow/mcp/dist/tool-registry.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/transport/http.d.ts +60 -0
- package/node_modules/@claude-flow/mcp/dist/transport/http.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/transport/http.js +503 -0
- package/node_modules/@claude-flow/mcp/dist/transport/http.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/transport/index.d.ts +50 -0
- package/node_modules/@claude-flow/mcp/dist/transport/index.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/transport/index.js +181 -0
- package/node_modules/@claude-flow/mcp/dist/transport/index.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/transport/stdio.d.ts +43 -0
- package/node_modules/@claude-flow/mcp/dist/transport/stdio.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/transport/stdio.js +194 -0
- package/node_modules/@claude-flow/mcp/dist/transport/stdio.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/transport/websocket.d.ts +65 -0
- package/node_modules/@claude-flow/mcp/dist/transport/websocket.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/transport/websocket.js +314 -0
- package/node_modules/@claude-flow/mcp/dist/transport/websocket.js.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/types.d.ts +482 -0
- package/node_modules/@claude-flow/mcp/dist/types.d.ts.map +1 -0
- package/node_modules/@claude-flow/mcp/dist/types.js +40 -0
- package/node_modules/@claude-flow/mcp/dist/types.js.map +1 -0
- package/node_modules/@claude-flow/mcp/package.json +50 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/node_modules/@claude-flow/security/dist/path-validator.d.ts +23 -0
- package/node_modules/@claude-flow/security/dist/path-validator.d.ts.map +1 -1
- package/node_modules/@claude-flow/security/dist/path-validator.js +128 -27
- package/node_modules/@claude-flow/security/dist/path-validator.js.map +1 -1
- package/package.json +9 -2
- package/.claude/.proven-config-version +0 -1
- package/.claude/proven-config.json +0 -42
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.38.12
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version": "3.38.
|
|
3
|
+
"version": "3.38.12",
|
|
4
4
|
"files": {
|
|
5
5
|
"auto-memory-hook.mjs": "85fe05c757421c52137c0bc8545a0896bab6b4714538c2a11d1d0835bfcc8c1c",
|
|
6
6
|
"hook-handler.cjs": "dae295fb9ae2626b89899c19a20cc911541af82b52d2eeb9b214d618b96e9a86",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"statusline.cjs": "0457fe53f8cd2c56458ff178392536a5868efd1a573665fa43bc01d2d95ca677"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"signature": "
|
|
11
|
+
"signature": "8JINtA8uubiBqNQbsz8OL2fcc4xmKGk6gKPGoa1ib7/21nYDaYWiWrZB/4xli8AGUdmQuMS9GT6ANbpPjqAcCw==",
|
|
12
12
|
"algorithm": "ed25519"
|
|
13
13
|
}
|
package/catalog-manifest.json
CHANGED
|
@@ -361,6 +361,38 @@ function isMemoryDbEncryptedAtRest(dbPath) {
|
|
|
361
361
|
return false;
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Distinguish an unavailable native addon from a database that the native
|
|
366
|
+
* driver opened and found malformed. Importing better-sqlite3 can succeed even
|
|
367
|
+
* when its postinstall script was skipped: the JavaScript wrapper loads, then
|
|
368
|
+
* the first Database construction throws because better_sqlite3.node is absent
|
|
369
|
+
* or incompatible with the active Node ABI.
|
|
370
|
+
*/
|
|
371
|
+
function isNativeSqliteBindingUnavailable(error) {
|
|
372
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
373
|
+
return [
|
|
374
|
+
/Could not locate the bindings file/i,
|
|
375
|
+
/better_sqlite3\.node/i,
|
|
376
|
+
/NODE_MODULE_VERSION/i,
|
|
377
|
+
/compiled against a different Node\.js version/i,
|
|
378
|
+
/ERR_DLOPEN_FAILED/i,
|
|
379
|
+
/invalid ELF header/i,
|
|
380
|
+
/wrong ELF class/i,
|
|
381
|
+
/not a valid Win32 application/i,
|
|
382
|
+
/Module did not self-register/i,
|
|
383
|
+
/no suitable image found/i,
|
|
384
|
+
].some((pattern) => pattern.test(message));
|
|
385
|
+
}
|
|
386
|
+
function nativeBindingUnavailableCheck(name, dbPath, error, suffix) {
|
|
387
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
388
|
+
const summary = details.split(/\r?\n/, 1)[0].replace(/\s+Tried:\s*$/i, '');
|
|
389
|
+
return {
|
|
390
|
+
name,
|
|
391
|
+
status: 'warn',
|
|
392
|
+
message: `${dbPath} — better-sqlite3 package found, but its native binding is unavailable: ${summary} ${suffix}`,
|
|
393
|
+
fix: 'reinstall with npm install scripts enabled or run `npm rebuild better-sqlite3`; then rerun this check',
|
|
394
|
+
};
|
|
395
|
+
}
|
|
364
396
|
// #2737 part 1 — bare `doctor` (no --component flag) never actually opened
|
|
365
397
|
// memory.db: checkMemoryDatabase above is existsSync()+statSync() only, so
|
|
366
398
|
// it PASSES on any file that exists and can be stat'd, corrupt or not, and
|
|
@@ -416,8 +448,8 @@ async function checkMemoryStructuralIntegrity() {
|
|
|
416
448
|
return {
|
|
417
449
|
name: NAME,
|
|
418
450
|
status: 'warn',
|
|
419
|
-
message: `${dbPath} — better-sqlite3 not installed; structural-only check
|
|
420
|
-
fix: '
|
|
451
|
+
message: `${dbPath} — better-sqlite3 not installed; native WAL-backed persistence and the structural-only check are unavailable`,
|
|
452
|
+
fix: 'install better-sqlite3 with npm install scripts enabled, then rerun this check',
|
|
421
453
|
};
|
|
422
454
|
}
|
|
423
455
|
let db;
|
|
@@ -425,6 +457,9 @@ async function checkMemoryStructuralIntegrity() {
|
|
|
425
457
|
db = new Database(dbPath, { readonly: true, fileMustExist: true });
|
|
426
458
|
}
|
|
427
459
|
catch (e) {
|
|
460
|
+
if (isNativeSqliteBindingUnavailable(e)) {
|
|
461
|
+
return nativeBindingUnavailableCheck(NAME, dbPath, e, '[structural-only; durable WAL-backed persistence unavailable]');
|
|
462
|
+
}
|
|
428
463
|
const msg = e.message || String(e);
|
|
429
464
|
// Encryption already ruled out above — an unencrypted file
|
|
430
465
|
// better-sqlite3 can't even open is definitive corruption.
|
|
@@ -498,9 +533,9 @@ async function checkMemoryIntegrity() {
|
|
|
498
533
|
}
|
|
499
534
|
// Prefer native better-sqlite3 (WAL-aware, full integrity_check). Module
|
|
500
535
|
// load is isolated in its own try/catch so ONLY "not installed" falls
|
|
501
|
-
// through to the sql.js fallback below
|
|
502
|
-
//
|
|
503
|
-
//
|
|
536
|
+
// through to the sql.js fallback below. A loaded JavaScript wrapper can
|
|
537
|
+
// still lack its native binding (#2968); that capability failure is a warn,
|
|
538
|
+
// while genuine open/query failures remain authoritative failures here.
|
|
504
539
|
let Database;
|
|
505
540
|
try {
|
|
506
541
|
Database = (await import('better-sqlite3')).default;
|
|
@@ -514,6 +549,9 @@ async function checkMemoryIntegrity() {
|
|
|
514
549
|
db = new Database(dbPath, { readonly: true, fileMustExist: true });
|
|
515
550
|
}
|
|
516
551
|
catch (e) {
|
|
552
|
+
if (isNativeSqliteBindingUnavailable(e)) {
|
|
553
|
+
return nativeBindingUnavailableCheck('Memory Integrity', dbPath, e, '[native WAL-backed persistence unavailable]');
|
|
554
|
+
}
|
|
517
555
|
const msg = e.message || String(e);
|
|
518
556
|
return {
|
|
519
557
|
name: 'Memory Integrity',
|
|
@@ -574,7 +612,12 @@ async function checkMemoryIntegrity() {
|
|
|
574
612
|
const res = db.exec('PRAGMA integrity_check');
|
|
575
613
|
const rows = res[0]?.values?.map((v) => String(v[0])) ?? [];
|
|
576
614
|
if (rows.length === 1 && rows[0] === 'ok') {
|
|
577
|
-
return {
|
|
615
|
+
return {
|
|
616
|
+
name: 'Memory Integrity',
|
|
617
|
+
status: 'warn',
|
|
618
|
+
message: `${dbPath} — PRAGMA integrity_check: ok, but only for the main image [sql.js fallback; native WAL-backed persistence unavailable]`,
|
|
619
|
+
fix: 'install better-sqlite3 with npm install scripts enabled, then rerun this check',
|
|
620
|
+
};
|
|
578
621
|
}
|
|
579
622
|
return {
|
|
580
623
|
name: 'Memory Integrity',
|
|
@@ -885,8 +928,10 @@ async function checkApiKeys() {
|
|
|
885
928
|
found.push(key);
|
|
886
929
|
}
|
|
887
930
|
}
|
|
888
|
-
// Detect Claude Code environment — API keys are managed internally
|
|
889
|
-
|
|
931
|
+
// Detect Claude Code environment — API keys are managed internally.
|
|
932
|
+
// Claude Code sets CLAUDECODE (no underscore); CLAUDE_CODE is kept for
|
|
933
|
+
// compatibility with anything else that might set it.
|
|
934
|
+
const inClaudeCode = !!(process.env.CLAUDECODE || process.env.CLAUDE_CODE || process.env.CLAUDE_PROJECT_DIR || process.env.MCP_SESSION_ID);
|
|
890
935
|
if (found.includes('ANTHROPIC_API_KEY') || found.includes('CLAUDE_API_KEY')) {
|
|
891
936
|
return { name: 'API Keys', status: 'pass', message: `Found: ${found.join(', ')}` };
|
|
892
937
|
}
|
|
@@ -4457,7 +4457,8 @@ const teammateIdleCommand = {
|
|
|
4457
4457
|
short: 'a',
|
|
4458
4458
|
description: 'Automatically assign pending tasks to idle teammate',
|
|
4459
4459
|
type: 'boolean',
|
|
4460
|
-
|
|
4460
|
+
// #3031: explicit opt-in only. Idle/liveness is not assignment authority.
|
|
4461
|
+
default: false
|
|
4461
4462
|
},
|
|
4462
4463
|
{
|
|
4463
4464
|
name: 'check-task-list',
|
|
@@ -4483,7 +4484,7 @@ const teammateIdleCommand = {
|
|
|
4483
4484
|
{ command: 'claude-flow hooks teammate-idle -t worker-1 --check-task-list', description: 'Check tasks for specific teammate' }
|
|
4484
4485
|
],
|
|
4485
4486
|
action: async (ctx) => {
|
|
4486
|
-
const autoAssign = ctx.flags.autoAssign
|
|
4487
|
+
const autoAssign = ctx.flags.autoAssign === true;
|
|
4487
4488
|
const checkTaskList = ctx.flags.checkTaskList !== false;
|
|
4488
4489
|
const teammateId = ctx.flags.teammateId;
|
|
4489
4490
|
const teamName = ctx.flags.teamName;
|
package/dist/src/commands/mcp.js
CHANGED
|
@@ -64,8 +64,7 @@ const startCommand = {
|
|
|
64
64
|
{
|
|
65
65
|
name: 'tools',
|
|
66
66
|
description: 'Tools to advertise (comma-separated categories, prefixes, exact names, or "all")',
|
|
67
|
-
type: 'string'
|
|
68
|
-
default: 'all'
|
|
67
|
+
type: 'string'
|
|
69
68
|
},
|
|
70
69
|
{
|
|
71
70
|
name: 'daemon',
|
|
@@ -92,7 +91,9 @@ const startCommand = {
|
|
|
92
91
|
const port = ctx.flags.port ?? 3000;
|
|
93
92
|
const host = ctx.flags.host ?? 'localhost';
|
|
94
93
|
const transport = ctx.flags.transport ?? 'stdio';
|
|
95
|
-
const tools = ctx.flags.tools
|
|
94
|
+
const tools = ctx.flags.tools
|
|
95
|
+
|| process.env.CLAUDE_FLOW_MCP_TOOLS
|
|
96
|
+
|| 'all';
|
|
96
97
|
const daemon = ctx.flags.daemon ?? false;
|
|
97
98
|
const force = ctx.flags.force ?? false;
|
|
98
99
|
output.writeln();
|
|
@@ -23,6 +23,12 @@ export interface UpgradeResult {
|
|
|
23
23
|
/** Added by --settings flag */
|
|
24
24
|
settingsUpdated?: string[];
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Merge new settings into existing settings.json
|
|
28
|
+
* Preserves user customizations while adding new features like Agent Teams
|
|
29
|
+
* Uses platform-specific commands for Mac, Linux, and Windows
|
|
30
|
+
*/
|
|
31
|
+
export declare function mergeSettingsForUpgrade(existing: Record<string, unknown>): Record<string, unknown>;
|
|
26
32
|
/**
|
|
27
33
|
* Execute upgrade - updates helpers and creates missing metrics without losing data
|
|
28
34
|
* This is safe for existing users who want the latest statusline fixes
|
|
@@ -276,7 +276,7 @@ export async function executeInit(options) {
|
|
|
276
276
|
* Preserves user customizations while adding new features like Agent Teams
|
|
277
277
|
* Uses platform-specific commands for Mac, Linux, and Windows
|
|
278
278
|
*/
|
|
279
|
-
function mergeSettingsForUpgrade(existing) {
|
|
279
|
+
export function mergeSettingsForUpgrade(existing) {
|
|
280
280
|
const merged = { ...existing };
|
|
281
281
|
const platform = detectPlatform();
|
|
282
282
|
const isWindows = platform.os === 'windows';
|
|
@@ -440,13 +440,15 @@ function mergeSettingsForUpgrade(existing) {
|
|
|
440
440
|
taskListEnabled: true,
|
|
441
441
|
mailboxEnabled: true,
|
|
442
442
|
coordination: {
|
|
443
|
-
|
|
443
|
+
// #3031: an upgrade must remove the unsafe generated default. Idle
|
|
444
|
+
// status is not proof that a task is unowned or inside agent scope.
|
|
445
|
+
autoAssignOnIdle: false,
|
|
444
446
|
trainPatternsOnComplete: true,
|
|
445
447
|
notifyLeadOnComplete: true,
|
|
446
448
|
sharedMemoryNamespace: 'agent-teams',
|
|
447
449
|
},
|
|
448
450
|
hooks: {
|
|
449
|
-
teammateIdle: { enabled: true, autoAssign:
|
|
451
|
+
teammateIdle: { enabled: true, autoAssign: false, checkTaskList: true },
|
|
450
452
|
taskCompleted: { enabled: true, trainPatterns: true, notifyLead: true },
|
|
451
453
|
},
|
|
452
454
|
},
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
export declare const HELPERS_STAMP_FILE = ".helpers-version";
|
|
2
|
+
export declare const HELPERS_REFRESH_LOCK_FILE = ".helpers-refresh.lock";
|
|
3
|
+
interface RefreshOptions {
|
|
4
|
+
sourceDirOverride?: string;
|
|
5
|
+
pubkeyPemOverride?: string;
|
|
6
|
+
versionOverride?: string;
|
|
7
|
+
alsoRefreshGlobal?: boolean;
|
|
8
|
+
beforeWriteOverride?: () => void | Promise<void>;
|
|
9
|
+
lockWaitMsOverride?: number;
|
|
10
|
+
lockRetryMsOverride?: number;
|
|
11
|
+
malformedLockStaleMsOverride?: number;
|
|
12
|
+
}
|
|
2
13
|
/**
|
|
3
14
|
* ruflo-owned helpers that carry hook logic (or the render surface for the
|
|
4
15
|
* funnel disclosure row) and must track the package version. Adding to this
|
|
@@ -40,9 +51,9 @@ export declare function getInstalledCliVersion(): string;
|
|
|
40
51
|
* running `daemon start` (or any command) would see its own older version !=
|
|
41
52
|
* the project's newer stamp and silently overwrite hand-fixed
|
|
42
53
|
* `hook-handler.cjs`/`intelligence.cjs` with its own older, already-superseded
|
|
43
|
-
* bundled copies.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
54
|
+
* bundled copies. The semver guard is therefore re-evaluated while holding a
|
|
55
|
+
* per-directory cross-process lock; otherwise an older process can pass the
|
|
56
|
+
* guard first but finish writing last.
|
|
46
57
|
*
|
|
47
58
|
* `opts` exists for tests ONLY (mirrors daemon-autostart.ts's injectable
|
|
48
59
|
* `SpawnDaemonFn` pattern): the real signed-copy path is otherwise coupled
|
|
@@ -59,12 +70,7 @@ export declare function getInstalledCliVersion(): string;
|
|
|
59
70
|
* compat with pre-3.31.3 callers). If the global pass ran, its own result is
|
|
60
71
|
* carried in the optional `global` field.
|
|
61
72
|
*/
|
|
62
|
-
export declare function autoRefreshHelpersIfStale(cwd: string, opts?: {
|
|
63
|
-
sourceDirOverride?: string;
|
|
64
|
-
pubkeyPemOverride?: string;
|
|
65
|
-
versionOverride?: string;
|
|
66
|
-
alsoRefreshGlobal?: boolean;
|
|
67
|
-
}): Promise<{
|
|
73
|
+
export declare function autoRefreshHelpersIfStale(cwd: string, opts?: RefreshOptions): Promise<{
|
|
68
74
|
refreshed: boolean;
|
|
69
75
|
from?: string;
|
|
70
76
|
to?: string;
|
|
@@ -76,4 +82,5 @@ export declare function autoRefreshHelpersIfStale(cwd: string, opts?: {
|
|
|
76
82
|
blocked?: string;
|
|
77
83
|
};
|
|
78
84
|
}>;
|
|
85
|
+
export {};
|
|
79
86
|
//# sourceMappingURL=helper-refresh.d.ts.map
|
|
@@ -53,6 +53,11 @@ function findPackageRoot(startDir, maxUp = 6) {
|
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
55
|
export const HELPERS_STAMP_FILE = '.helpers-version';
|
|
56
|
+
export const HELPERS_REFRESH_LOCK_FILE = '.helpers-refresh.lock';
|
|
57
|
+
const DEFAULT_LOCK_WAIT_MS = 2_000;
|
|
58
|
+
const DEFAULT_LOCK_RETRY_MS = 10;
|
|
59
|
+
const DEFAULT_MALFORMED_LOCK_STALE_MS = 5 * 60_000;
|
|
60
|
+
let tempFileCounter = 0;
|
|
56
61
|
/**
|
|
57
62
|
* ruflo-owned helpers that carry hook logic (or the render surface for the
|
|
58
63
|
* funnel disclosure row) and must track the package version. Adding to this
|
|
@@ -67,6 +72,139 @@ export const CRITICAL_HELPERS = [
|
|
|
67
72
|
// existing installs on the next `ruflo` command, not only fresh `ruflo init`.
|
|
68
73
|
'statusline.cjs',
|
|
69
74
|
];
|
|
75
|
+
function errorCode(error) {
|
|
76
|
+
return typeof error === 'object' && error !== null && 'code' in error
|
|
77
|
+
? String(error.code)
|
|
78
|
+
: undefined;
|
|
79
|
+
}
|
|
80
|
+
function processIsAlive(pid) {
|
|
81
|
+
try {
|
|
82
|
+
process.kill(pid, 0);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return errorCode(error) !== 'ESRCH';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function removeAbandonedLock(lockPath, malformedStaleMs) {
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(fs.readFileSync(lockPath, 'utf-8'));
|
|
92
|
+
if (Number.isSafeInteger(parsed.pid) && Number(parsed.pid) > 0) {
|
|
93
|
+
if (processIsAlive(Number(parsed.pid)))
|
|
94
|
+
return false;
|
|
95
|
+
fs.unlinkSync(lockPath);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (errorCode(error) === 'ENOENT')
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
// A process can die after the exclusive create but before writing metadata.
|
|
104
|
+
// Only reclaim such malformed locks after a generous age; valid locks with a
|
|
105
|
+
// live PID are never age-evicted, so a slow refresh cannot lose ownership.
|
|
106
|
+
try {
|
|
107
|
+
if (Date.now() - fs.statSync(lockPath).mtimeMs >= malformedStaleMs) {
|
|
108
|
+
fs.unlinkSync(lockPath);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (errorCode(error) === 'ENOENT')
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
async function acquireRefreshLock(helpersDir, opts) {
|
|
119
|
+
const lockPath = path.join(helpersDir, HELPERS_REFRESH_LOCK_FILE);
|
|
120
|
+
const waitMs = opts.lockWaitMsOverride ?? DEFAULT_LOCK_WAIT_MS;
|
|
121
|
+
const retryMs = opts.lockRetryMsOverride ?? DEFAULT_LOCK_RETRY_MS;
|
|
122
|
+
const malformedStaleMs = opts.malformedLockStaleMsOverride ?? DEFAULT_MALFORMED_LOCK_STALE_MS;
|
|
123
|
+
const deadline = Date.now() + Math.max(0, waitMs);
|
|
124
|
+
while (true) {
|
|
125
|
+
const token = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
126
|
+
let fd;
|
|
127
|
+
let created = false;
|
|
128
|
+
try {
|
|
129
|
+
fd = fs.openSync(lockPath, 'wx');
|
|
130
|
+
created = true;
|
|
131
|
+
fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, token }), 'utf-8');
|
|
132
|
+
fs.closeSync(fd);
|
|
133
|
+
fd = undefined;
|
|
134
|
+
return () => {
|
|
135
|
+
try {
|
|
136
|
+
const current = JSON.parse(fs.readFileSync(lockPath, 'utf-8'));
|
|
137
|
+
if (current.token === token)
|
|
138
|
+
fs.unlinkSync(lockPath);
|
|
139
|
+
}
|
|
140
|
+
catch { /* best-effort release; a replaced lock is never removed */ }
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
if (fd !== undefined) {
|
|
145
|
+
try {
|
|
146
|
+
fs.closeSync(fd);
|
|
147
|
+
}
|
|
148
|
+
catch { /* best-effort */ }
|
|
149
|
+
}
|
|
150
|
+
if (created) {
|
|
151
|
+
try {
|
|
152
|
+
fs.unlinkSync(lockPath);
|
|
153
|
+
}
|
|
154
|
+
catch { /* best-effort */ }
|
|
155
|
+
}
|
|
156
|
+
if (errorCode(error) !== 'EEXIST')
|
|
157
|
+
throw error;
|
|
158
|
+
if (removeAbandonedLock(lockPath, malformedStaleMs))
|
|
159
|
+
continue;
|
|
160
|
+
if (Date.now() >= deadline)
|
|
161
|
+
return null;
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, Math.max(1, retryMs)));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function temporarySiblingPath(target) {
|
|
167
|
+
tempFileCounter += 1;
|
|
168
|
+
return `${target}.tmp-${process.pid}-${Date.now()}-${tempFileCounter}`;
|
|
169
|
+
}
|
|
170
|
+
function atomicCopyFileSync(source, target, mode) {
|
|
171
|
+
const temporary = temporarySiblingPath(target);
|
|
172
|
+
try {
|
|
173
|
+
fs.copyFileSync(source, temporary, fs.constants.COPYFILE_EXCL);
|
|
174
|
+
if (mode) {
|
|
175
|
+
try {
|
|
176
|
+
fs.chmodSync(temporary, mode);
|
|
177
|
+
}
|
|
178
|
+
catch { /* non-fatal */ }
|
|
179
|
+
}
|
|
180
|
+
fs.renameSync(temporary, target);
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
try {
|
|
184
|
+
fs.unlinkSync(temporary);
|
|
185
|
+
}
|
|
186
|
+
catch { /* already renamed or never created */ }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function atomicWriteFileSync(target, content, mode) {
|
|
190
|
+
const temporary = temporarySiblingPath(target);
|
|
191
|
+
try {
|
|
192
|
+
fs.writeFileSync(temporary, content, { encoding: 'utf-8', flag: 'wx' });
|
|
193
|
+
if (mode) {
|
|
194
|
+
try {
|
|
195
|
+
fs.chmodSync(temporary, mode);
|
|
196
|
+
}
|
|
197
|
+
catch { /* non-fatal */ }
|
|
198
|
+
}
|
|
199
|
+
fs.renameSync(temporary, target);
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
try {
|
|
203
|
+
fs.unlinkSync(temporary);
|
|
204
|
+
}
|
|
205
|
+
catch { /* already renamed or never created */ }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
70
208
|
/** Installed @claude-flow/cli version — the value the helpers are stamped with. */
|
|
71
209
|
export function getInstalledCliVersion() {
|
|
72
210
|
try {
|
|
@@ -148,20 +286,16 @@ async function writeCriticalHelpers(helpersDir, version, opts = {}) {
|
|
|
148
286
|
let wrote = false;
|
|
149
287
|
for (const name of toCopy) {
|
|
150
288
|
const tp = path.join(helpersDir, name);
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
fs.chmodSync(tp, '755');
|
|
154
|
-
}
|
|
155
|
-
catch { /* non-fatal */ }
|
|
289
|
+
atomicCopyFileSync(path.join(source, name), tp, '755');
|
|
156
290
|
wrote = true;
|
|
157
291
|
}
|
|
158
292
|
try {
|
|
159
|
-
|
|
293
|
+
atomicCopyFileSync(path.join(source, HELPERS_MANIFEST_FILE), path.join(helpersDir, HELPERS_MANIFEST_FILE));
|
|
160
294
|
}
|
|
161
295
|
catch { /* non-fatal */ }
|
|
162
296
|
if (wrote) {
|
|
163
297
|
try {
|
|
164
|
-
|
|
298
|
+
atomicWriteFileSync(path.join(helpersDir, HELPERS_STAMP_FILE), version);
|
|
165
299
|
}
|
|
166
300
|
catch { /* non-fatal */ }
|
|
167
301
|
}
|
|
@@ -187,16 +321,12 @@ async function writeCriticalHelpers(helpersDir, version, opts = {}) {
|
|
|
187
321
|
let wrote = false;
|
|
188
322
|
for (const [name, content] of Object.entries(files)) {
|
|
189
323
|
const tp = path.join(helpersDir, name);
|
|
190
|
-
|
|
191
|
-
try {
|
|
192
|
-
fs.chmodSync(tp, '755');
|
|
193
|
-
}
|
|
194
|
-
catch { /* non-fatal */ }
|
|
324
|
+
atomicWriteFileSync(tp, content, '755');
|
|
195
325
|
wrote = true;
|
|
196
326
|
}
|
|
197
327
|
if (wrote) {
|
|
198
328
|
try {
|
|
199
|
-
|
|
329
|
+
atomicWriteFileSync(path.join(helpersDir, HELPERS_STAMP_FILE), version);
|
|
200
330
|
}
|
|
201
331
|
catch { /* non-fatal */ }
|
|
202
332
|
}
|
|
@@ -216,9 +346,9 @@ async function writeCriticalHelpers(helpersDir, version, opts = {}) {
|
|
|
216
346
|
* running `daemon start` (or any command) against THIS project directory
|
|
217
347
|
* would see its own older version != the project's newer stamp and silently
|
|
218
348
|
* overwrite hand-fixed `hook-handler.cjs`/`intelligence.cjs` with its own
|
|
219
|
-
* older, already-superseded bundled copies.
|
|
220
|
-
*
|
|
221
|
-
*
|
|
349
|
+
* older, already-superseded bundled copies. The semver guard is therefore
|
|
350
|
+
* re-evaluated while holding a per-directory cross-process lock; otherwise
|
|
351
|
+
* an older process can pass the guard first but finish writing last.
|
|
222
352
|
*
|
|
223
353
|
* `opts` exists for tests ONLY (mirrors daemon-autostart.ts's injectable
|
|
224
354
|
* `SpawnDaemonFn` pattern): the real signed-copy path is otherwise coupled to
|
|
@@ -231,7 +361,7 @@ async function writeCriticalHelpers(helpersDir, version, opts = {}) {
|
|
|
231
361
|
* signed fixture and get real, deterministic coverage of the verify → hash →
|
|
232
362
|
* copy logic without depending on that.
|
|
233
363
|
*/
|
|
234
|
-
async function
|
|
364
|
+
async function refreshOneHelpersDirLocked(helpersDir, version, opts) {
|
|
235
365
|
if (!fs.existsSync(path.join(helpersDir, 'hook-handler.cjs')))
|
|
236
366
|
return { refreshed: false };
|
|
237
367
|
// .LOCKED marker: users developing ruflo itself (or any project with
|
|
@@ -258,6 +388,7 @@ async function refreshOneHelpersDir(helpersDir, version, opts) {
|
|
|
258
388
|
// would silently DOWNGRADE the helpers. Skip, untouched.
|
|
259
389
|
return { refreshed: false };
|
|
260
390
|
}
|
|
391
|
+
await opts.beforeWriteOverride?.();
|
|
261
392
|
const res = await writeCriticalHelpers(helpersDir, version, {
|
|
262
393
|
sourceDirOverride: opts.sourceDirOverride,
|
|
263
394
|
pubkeyPemOverride: opts.pubkeyPemOverride,
|
|
@@ -266,6 +397,32 @@ async function refreshOneHelpersDir(helpersDir, version, opts) {
|
|
|
266
397
|
return { refreshed: false, blocked: res.blocked };
|
|
267
398
|
return res.wrote ? { refreshed: true, from: stamped || '(unstamped)', to: version } : { refreshed: false };
|
|
268
399
|
}
|
|
400
|
+
async function refreshOneHelpersDir(helpersDir, version, opts) {
|
|
401
|
+
if (!fs.existsSync(path.join(helpersDir, 'hook-handler.cjs')))
|
|
402
|
+
return { refreshed: false };
|
|
403
|
+
// Respect the repository opt-out before creating even a transient lock file
|
|
404
|
+
// in a directory whose helpers are intentionally maintained by hand. Keep
|
|
405
|
+
// the check in the locked path too in case the marker appears while waiting.
|
|
406
|
+
if (fs.existsSync(path.join(helpersDir, '.LOCKED'))) {
|
|
407
|
+
return { refreshed: false, blocked: '.LOCKED marker present — refresh skipped (delete to re-enable)' };
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
if (fs.readFileSync(path.join(helpersDir, HELPERS_STAMP_FILE), 'utf-8').trim() === version)
|
|
411
|
+
return { refreshed: false };
|
|
412
|
+
}
|
|
413
|
+
catch { /* unstamped: continue to the locked path */ }
|
|
414
|
+
const releaseLock = await acquireRefreshLock(helpersDir, opts);
|
|
415
|
+
if (!releaseLock)
|
|
416
|
+
return { refreshed: false, blocked: 'helper refresh already in progress' };
|
|
417
|
+
try {
|
|
418
|
+
// Re-read the stamp and .LOCKED marker under the cross-process lock. A
|
|
419
|
+
// concurrent newer CLI may have completed while this caller waited.
|
|
420
|
+
return await refreshOneHelpersDirLocked(helpersDir, version, opts);
|
|
421
|
+
}
|
|
422
|
+
finally {
|
|
423
|
+
releaseLock();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
269
426
|
/**
|
|
270
427
|
* On CLI startup, refresh critical helpers if their stamp is older than the
|
|
271
428
|
* installed CLI version. Two passes:
|
|
@@ -298,9 +455,9 @@ async function refreshOneHelpersDir(helpersDir, version, opts) {
|
|
|
298
455
|
* running `daemon start` (or any command) would see its own older version !=
|
|
299
456
|
* the project's newer stamp and silently overwrite hand-fixed
|
|
300
457
|
* `hook-handler.cjs`/`intelligence.cjs` with its own older, already-superseded
|
|
301
|
-
* bundled copies.
|
|
302
|
-
*
|
|
303
|
-
*
|
|
458
|
+
* bundled copies. The semver guard is therefore re-evaluated while holding a
|
|
459
|
+
* per-directory cross-process lock; otherwise an older process can pass the
|
|
460
|
+
* guard first but finish writing last.
|
|
304
461
|
*
|
|
305
462
|
* `opts` exists for tests ONLY (mirrors daemon-autostart.ts's injectable
|
|
306
463
|
* `SpawnDaemonFn` pattern): the real signed-copy path is otherwise coupled
|
|
@@ -83,7 +83,11 @@ export function generateSettings(options) {
|
|
|
83
83
|
taskListEnabled: true,
|
|
84
84
|
mailboxEnabled: true,
|
|
85
85
|
coordination: {
|
|
86
|
-
|
|
86
|
+
// #3031: Liveness alone is not authority. Keep idle assignment off
|
|
87
|
+
// until the scheduler can prove task ownership, agent scope, and a
|
|
88
|
+
// refusal/back-off state. Users may explicitly opt in after supplying
|
|
89
|
+
// those controls in their host configuration.
|
|
90
|
+
autoAssignOnIdle: false,
|
|
87
91
|
trainPatternsOnComplete: true, // Train neural patterns when tasks complete
|
|
88
92
|
notifyLeadOnComplete: true, // Notify team lead when tasks complete
|
|
89
93
|
sharedMemoryNamespace: 'agent-teams', // Memory namespace for team coordination
|
|
@@ -91,7 +95,7 @@ export function generateSettings(options) {
|
|
|
91
95
|
hooks: {
|
|
92
96
|
teammateIdle: {
|
|
93
97
|
enabled: true,
|
|
94
|
-
autoAssign:
|
|
98
|
+
autoAssign: false,
|
|
95
99
|
checkTaskList: true,
|
|
96
100
|
},
|
|
97
101
|
taskCompleted: {
|
|
@@ -40,7 +40,7 @@ declare const STDERR_REDIRECT_PREFIXES: string[];
|
|
|
40
40
|
declare const AGENTDB_MOCK_FALLBACK_DROP_PREFIXES: string[];
|
|
41
41
|
declare const shouldRedirectToStderr: (msg: unknown) => boolean;
|
|
42
42
|
declare const isAgentdbMockFallbackNoise: (msg: unknown) => boolean;
|
|
43
|
-
declare const origWarn: (...
|
|
44
|
-
declare const origLog: (...
|
|
45
|
-
declare const origError: (...
|
|
43
|
+
declare const origWarn: (message?: any, ...optionalParams: any[]) => void;
|
|
44
|
+
declare const origLog: (message?: any, ...optionalParams: any[]) => void;
|
|
45
|
+
declare const origError: (message?: any, ...optionalParams: any[]) => void;
|
|
46
46
|
//# sourceMappingURL=log-filters.d.ts.map
|
package/dist/src/mcp-server.d.ts
CHANGED
package/dist/src/mcp-server.js
CHANGED
|
@@ -101,6 +101,7 @@ export class MCPServerManager extends EventEmitter {
|
|
|
101
101
|
server;
|
|
102
102
|
startTime;
|
|
103
103
|
healthCheckInterval;
|
|
104
|
+
mcpServers = [];
|
|
104
105
|
constructor(options = {}) {
|
|
105
106
|
super();
|
|
106
107
|
// `options.tools`, populated by the `mcp start --tools` CLI flag, is
|
|
@@ -185,6 +186,11 @@ export class MCPServerManager extends EventEmitter {
|
|
|
185
186
|
});
|
|
186
187
|
this.server = undefined;
|
|
187
188
|
}
|
|
189
|
+
if (this.mcpServers.length > 0) {
|
|
190
|
+
const servers = this.mcpServers;
|
|
191
|
+
this.mcpServers = [];
|
|
192
|
+
await Promise.all(servers.map((server) => server.stop()));
|
|
193
|
+
}
|
|
188
194
|
// Remove PID file
|
|
189
195
|
await this.removePidFile();
|
|
190
196
|
this.startTime = undefined;
|
|
@@ -560,18 +566,48 @@ export class MCPServerManager extends EventEmitter {
|
|
|
560
566
|
warn: (msg, data) => this.emit('log', { level: 'warn', msg, data }),
|
|
561
567
|
error: (msg, data) => this.emit('log', { level: 'error', msg, data }),
|
|
562
568
|
};
|
|
569
|
+
const { listMCPTools, callMCPTool } = await import('./mcp-client.js');
|
|
570
|
+
const fallbackSessionId = `http-${randomUUID()}`;
|
|
571
|
+
const cliTools = filterAdvertisedMcpTools(listMCPTools(), this.options.tools).map((tool) => ({
|
|
572
|
+
name: tool.name,
|
|
573
|
+
description: tool.description,
|
|
574
|
+
inputSchema: tool.inputSchema,
|
|
575
|
+
category: tool.category,
|
|
576
|
+
tags: tool.tags,
|
|
577
|
+
version: tool.version,
|
|
578
|
+
cacheable: tool.cacheable,
|
|
579
|
+
cacheTTL: tool.cacheTTL,
|
|
580
|
+
handler: async (input, context) => {
|
|
581
|
+
try {
|
|
582
|
+
const result = await callMCPTool(tool.name, input || {}, { sessionId: context?.sessionId || fallbackSessionId });
|
|
583
|
+
trackRequest(tool.name, true);
|
|
584
|
+
return result;
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
trackRequest(tool.name, false);
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
}));
|
|
592
|
+
// Use one MCP server with two HTTP transports for the localhost default.
|
|
593
|
+
// Both loopback sockets therefore share sessions, tools, and notifications.
|
|
594
|
+
const dualLoopback = this.options.host === 'localhost';
|
|
563
595
|
const mcpServer = createMCPServer({
|
|
564
596
|
name: 'Claude-Flow MCP Server V3',
|
|
565
597
|
version: '3.0.0',
|
|
566
598
|
transport: this.options.transport,
|
|
567
|
-
host: this.options.host,
|
|
599
|
+
host: dualLoopback ? '127.0.0.1' : this.options.host,
|
|
600
|
+
additionalHosts: dualLoopback && this.options.transport === 'http' ? ['::1'] : undefined,
|
|
568
601
|
port: this.options.port,
|
|
569
602
|
enableMetrics: true,
|
|
570
603
|
enableCaching: true,
|
|
571
604
|
}, logger);
|
|
605
|
+
const registration = mcpServer.registerTools(cliTools);
|
|
606
|
+
if (registration.failed.length > 0) {
|
|
607
|
+
throw new Error(`Failed to register MCP tools: ${registration.failed.join(', ')}`);
|
|
608
|
+
}
|
|
572
609
|
await mcpServer.start();
|
|
573
|
-
|
|
574
|
-
this._mcpServer = mcpServer;
|
|
610
|
+
this.mcpServers = [mcpServer];
|
|
575
611
|
}
|
|
576
612
|
/**
|
|
577
613
|
* Wait for server to be ready
|