@claude-flow/cli 3.45.0 → 3.46.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/.claude/helpers/helpers.manifest.json +2 -2
- package/README.md +2 -0
- package/bin/cli.js +13 -0
- package/bin/mcp-server.js +16 -0
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.js +131 -2
- package/dist/src/commands/embeddings.js +50 -7
- package/dist/src/commands/init.js +5 -2
- package/dist/src/commands/mcp.js +27 -4
- package/dist/src/mcp-server.d.ts +50 -6
- package/dist/src/mcp-server.js +267 -33
- package/dist/src/mcp-tools/agent-execute-core.js +7 -0
- package/dist/src/mcp-tools/agentdb-tools.js +20 -8
- package/dist/src/mcp-tools/browser-tools.js +59 -48
- package/dist/src/mcp-tools/memory-tools.js +9 -4
- package/dist/src/mcp-tools/metaharness-tools.js +1 -1
- package/dist/src/mcp-tools/policy-enforcer.d.ts +4 -4
- package/dist/src/mcp-tools/policy-enforcer.js +4 -4
- package/dist/src/mcp-tools/terminal-tools.js +11 -1
- package/dist/src/mcp-tools/workflow-tools.js +1 -1
- package/dist/src/memory/ewc-consolidation.d.ts +16 -1
- package/dist/src/memory/ewc-consolidation.js +63 -13
- package/dist/src/memory/intelligence.d.ts +6 -2
- package/dist/src/memory/intelligence.js +18 -11
- package/dist/src/memory/memory-bridge.js +84 -0
- package/dist/src/runtime/browser-command.d.ts +29 -0
- package/dist/src/runtime/browser-command.js +101 -0
- package/dist/src/ruvector/model-router.js +19 -1
- package/dist/src/services/distill-oracle.js +2 -1
- package/dist/src/services/git-workspace-identity.js +5 -8
- package/dist/src/services/policy-runtime.js +114 -4
- package/dist/src/services/worker-daemon.js +8 -3
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/codex/dist/worktrees/coordinator.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/worktrees/coordinator.js +2 -6
- package/node_modules/@claude-flow/codex/dist/worktrees/coordinator.js.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/transport/http.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/transport/http.js +14 -1
- package/node_modules/@claude-flow/mcp/dist/transport/http.js.map +1 -1
- package/node_modules/@claude-flow/plugin-agent-federation/dist/a2a/planner-descriptor.d.ts +40 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/a2a/planner-descriptor.d.ts.map +1 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/a2a/planner-descriptor.js +100 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/a2a/planner-descriptor.js.map +1 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/index.d.ts +1 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/index.d.ts.map +1 -1
- package/node_modules/@claude-flow/plugin-agent-federation/dist/index.js +1 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/index.js.map +1 -1
- package/node_modules/@claude-flow/plugin-agent-federation/package.json +1 -0
- package/node_modules/@claude-flow/security/dist/input-validator.d.ts +6 -6
- package/node_modules/@claude-flow/security/dist/safe-git.d.ts +22 -0
- package/node_modules/@claude-flow/security/dist/safe-git.d.ts.map +1 -0
- package/node_modules/@claude-flow/security/dist/safe-git.js +63 -0
- package/node_modules/@claude-flow/security/dist/safe-git.js.map +1 -0
- package/package.json +5 -5
- package/plugins/ruflo-metaharness/agents/metaharness-architect.md +1 -1
- package/plugins/ruflo-metaharness/commands/ruflo-metaharness.md +1 -1
- package/plugins/ruflo-metaharness/scripts/_darwin.mjs +84 -12
- package/plugins/ruflo-metaharness/scripts/_harness.mjs +10 -4
- package/plugins/ruflo-metaharness/scripts/_invoke.mjs +120 -6
- package/plugins/ruflo-metaharness/scripts/_redblue.mjs +33 -6
- package/plugins/ruflo-metaharness/scripts/audit-list.mjs +11 -10
- package/plugins/ruflo-metaharness/scripts/audit-trend.mjs +8 -7
- package/plugins/ruflo-metaharness/scripts/drift-from-history.mjs +2 -3
- package/plugins/ruflo-metaharness/scripts/oia-audit.mjs +8 -7
- package/plugins/ruflo-metaharness/scripts/similarity.mjs +9 -8
- package/plugins/ruflo-metaharness/scripts/smoke.sh +31 -0
- package/plugins/ruflo-metaharness/scripts/test-no-tool-time-npx.mjs +319 -0
- package/plugins/ruflo-metaharness/scripts/test-pin-alignment.mjs +182 -0
- package/plugins/ruflo-metaharness/skills/harness-evolve/SKILL.md +4 -2
- package/plugins/ruflo-metaharness/skills/harness-gepa/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-learn/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-mcp-scan/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-score/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-security-bench/SKILL.md +3 -1
- package/plugins/ruflo-metaharness/skills/harness-threat-model/SKILL.md +1 -1
|
@@ -159,14 +159,14 @@ export declare const SpawnAgentSchema: z.ZodObject<{
|
|
|
159
159
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
160
160
|
}, "strip", z.ZodTypeAny, {
|
|
161
161
|
type: "coder" | "reviewer" | "tester" | "planner" | "researcher" | "security-architect" | "security-auditor" | "memory-specialist" | "swarm-specialist" | "integration-architect" | "performance-engineer" | "core-architect" | "test-architect" | "queen-coordinator" | "project-coordinator";
|
|
162
|
-
id?: string | undefined;
|
|
163
162
|
config?: Record<string, unknown> | undefined;
|
|
164
163
|
timeout?: number | undefined;
|
|
164
|
+
id?: string | undefined;
|
|
165
165
|
}, {
|
|
166
166
|
type: "coder" | "reviewer" | "tester" | "planner" | "researcher" | "security-architect" | "security-auditor" | "memory-specialist" | "swarm-specialist" | "integration-architect" | "performance-engineer" | "core-architect" | "test-architect" | "queen-coordinator" | "project-coordinator";
|
|
167
|
-
id?: string | undefined;
|
|
168
167
|
config?: Record<string, unknown> | undefined;
|
|
169
168
|
timeout?: number | undefined;
|
|
169
|
+
id?: string | undefined;
|
|
170
170
|
}>;
|
|
171
171
|
/**
|
|
172
172
|
* Task input schema
|
|
@@ -181,13 +181,13 @@ export declare const TaskInputSchema: z.ZodObject<{
|
|
|
181
181
|
taskId: string;
|
|
182
182
|
content: string;
|
|
183
183
|
agentType: "coder" | "reviewer" | "tester" | "planner" | "researcher" | "security-architect" | "security-auditor" | "memory-specialist" | "swarm-specialist" | "integration-architect" | "performance-engineer" | "core-architect" | "test-architect" | "queen-coordinator" | "project-coordinator";
|
|
184
|
-
priority?: "
|
|
184
|
+
priority?: "critical" | "high" | "medium" | "low" | undefined;
|
|
185
185
|
metadata?: Record<string, unknown> | undefined;
|
|
186
186
|
}, {
|
|
187
187
|
taskId: string;
|
|
188
188
|
content: string;
|
|
189
189
|
agentType: "coder" | "reviewer" | "tester" | "planner" | "researcher" | "security-architect" | "security-auditor" | "memory-specialist" | "swarm-specialist" | "integration-architect" | "performance-engineer" | "core-architect" | "test-architect" | "queen-coordinator" | "project-coordinator";
|
|
190
|
-
priority?: "
|
|
190
|
+
priority?: "critical" | "high" | "medium" | "low" | undefined;
|
|
191
191
|
metadata?: Record<string, unknown> | undefined;
|
|
192
192
|
}>;
|
|
193
193
|
/**
|
|
@@ -234,16 +234,16 @@ export declare const ExecutorConfigSchema: z.ZodObject<{
|
|
|
234
234
|
cwd: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
|
|
235
235
|
allowSudo: z.ZodDefault<z.ZodBoolean>;
|
|
236
236
|
}, "strip", z.ZodTypeAny, {
|
|
237
|
-
timeout: number;
|
|
238
237
|
allowedCommands: string[];
|
|
238
|
+
timeout: number;
|
|
239
239
|
maxBuffer: number;
|
|
240
240
|
allowSudo: boolean;
|
|
241
241
|
blockedPatterns?: string[] | undefined;
|
|
242
242
|
cwd?: string | undefined;
|
|
243
243
|
}, {
|
|
244
244
|
allowedCommands: string[];
|
|
245
|
-
timeout?: number | undefined;
|
|
246
245
|
blockedPatterns?: string[] | undefined;
|
|
246
|
+
timeout?: number | undefined;
|
|
247
247
|
maxBuffer?: number | undefined;
|
|
248
248
|
cwd?: string | undefined;
|
|
249
249
|
allowSudo?: boolean | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface SafeGitSyncOptions {
|
|
2
|
+
readonly maxBuffer?: number;
|
|
3
|
+
readonly timeoutMs?: number;
|
|
4
|
+
readonly stderr?: 'pipe' | 'ignore';
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Build the argument vector used for every RuFlo-controlled Git subprocess.
|
|
8
|
+
*
|
|
9
|
+
* `core.fsmonitor` is command-bearing repository-local configuration. A copied
|
|
10
|
+
* repository can therefore select a host command that Git executes while
|
|
11
|
+
* refreshing its index. Command-line `-c` has higher precedence than local
|
|
12
|
+
* repository configuration, so this invariant must precede every repository
|
|
13
|
+
* operation that RuFlo performs itself.
|
|
14
|
+
*
|
|
15
|
+
* This deliberately does not claim that every command-bearing Git setting is
|
|
16
|
+
* neutralized. Keep the wrapper as the single policy point so additional
|
|
17
|
+
* reviewed overrides can be added without auditing every caller again.
|
|
18
|
+
*/
|
|
19
|
+
export declare function safeGitArgv(repoRoot: string, args: readonly string[]): string[];
|
|
20
|
+
export declare function safeGitTextSync(repoRoot: string, args: readonly string[], options?: SafeGitSyncOptions): string;
|
|
21
|
+
export declare function safeGitBufferSync(repoRoot: string, args: readonly string[], options?: SafeGitSyncOptions): Buffer;
|
|
22
|
+
//# sourceMappingURL=safe-git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-git.d.ts","sourceRoot":"","sources":["../src/safe-git.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CACrC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAI/E;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAQR;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAQR"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
const GIT_CONFIG_OVERRIDE = ['-c', 'core.fsmonitor=false'];
|
|
3
|
+
const DEFAULT_MAX_BUFFER = 4 * 1024 * 1024;
|
|
4
|
+
/**
|
|
5
|
+
* Build the argument vector used for every RuFlo-controlled Git subprocess.
|
|
6
|
+
*
|
|
7
|
+
* `core.fsmonitor` is command-bearing repository-local configuration. A copied
|
|
8
|
+
* repository can therefore select a host command that Git executes while
|
|
9
|
+
* refreshing its index. Command-line `-c` has higher precedence than local
|
|
10
|
+
* repository configuration, so this invariant must precede every repository
|
|
11
|
+
* operation that RuFlo performs itself.
|
|
12
|
+
*
|
|
13
|
+
* This deliberately does not claim that every command-bearing Git setting is
|
|
14
|
+
* neutralized. Keep the wrapper as the single policy point so additional
|
|
15
|
+
* reviewed overrides can be added without auditing every caller again.
|
|
16
|
+
*/
|
|
17
|
+
export function safeGitArgv(repoRoot, args) {
|
|
18
|
+
assertGitPath(repoRoot);
|
|
19
|
+
for (const arg of args)
|
|
20
|
+
assertGitArgument(arg);
|
|
21
|
+
return [...GIT_CONFIG_OVERRIDE, '-C', repoRoot, ...args];
|
|
22
|
+
}
|
|
23
|
+
export function safeGitTextSync(repoRoot, args, options = {}) {
|
|
24
|
+
return execFileSync('git', safeGitArgv(repoRoot, args), {
|
|
25
|
+
encoding: 'utf8',
|
|
26
|
+
stdio: ['ignore', 'pipe', options.stderr ?? 'pipe'],
|
|
27
|
+
maxBuffer: boundedMaxBuffer(options.maxBuffer),
|
|
28
|
+
...(options.timeoutMs === undefined ? {} : { timeout: boundedTimeout(options.timeoutMs) }),
|
|
29
|
+
windowsHide: true,
|
|
30
|
+
}).trim();
|
|
31
|
+
}
|
|
32
|
+
export function safeGitBufferSync(repoRoot, args, options = {}) {
|
|
33
|
+
return execFileSync('git', safeGitArgv(repoRoot, args), {
|
|
34
|
+
encoding: 'buffer',
|
|
35
|
+
stdio: ['ignore', 'pipe', options.stderr ?? 'pipe'],
|
|
36
|
+
maxBuffer: boundedMaxBuffer(options.maxBuffer),
|
|
37
|
+
...(options.timeoutMs === undefined ? {} : { timeout: boundedTimeout(options.timeoutMs) }),
|
|
38
|
+
windowsHide: true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function assertGitPath(value) {
|
|
42
|
+
if (value.length === 0 || value.includes('\0')) {
|
|
43
|
+
throw new Error('Git repository path is invalid');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function assertGitArgument(value) {
|
|
47
|
+
if (value.includes('\0'))
|
|
48
|
+
throw new Error('Git argument contains a NUL byte');
|
|
49
|
+
}
|
|
50
|
+
function boundedMaxBuffer(value) {
|
|
51
|
+
const resolved = value ?? DEFAULT_MAX_BUFFER;
|
|
52
|
+
if (!Number.isSafeInteger(resolved) || resolved < 1024 || resolved > 256 * 1024 * 1024) {
|
|
53
|
+
throw new Error('Git maxBuffer is outside the supported range');
|
|
54
|
+
}
|
|
55
|
+
return resolved;
|
|
56
|
+
}
|
|
57
|
+
function boundedTimeout(value) {
|
|
58
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 120_000) {
|
|
59
|
+
throw new Error('Git timeout is outside the supported range');
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=safe-git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-git.js","sourceRoot":"","sources":["../src/safe-git.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.46.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",
|
|
@@ -98,10 +98,10 @@
|
|
|
98
98
|
"vitest": "^4.1.0"
|
|
99
99
|
},
|
|
100
100
|
"dependencies": {
|
|
101
|
-
"@claude-flow/cli-core": "3.7.0-alpha.
|
|
101
|
+
"@claude-flow/cli-core": "3.7.0-alpha.6",
|
|
102
102
|
"@claude-flow/codex": "3.0.3",
|
|
103
103
|
"@claude-flow/mcp": "3.0.0-alpha.10",
|
|
104
|
-
"@claude-flow/neural": "3.0.0-alpha.
|
|
104
|
+
"@claude-flow/neural": "3.0.0-alpha.10",
|
|
105
105
|
"@claude-flow/plugin-agent-federation": "1.0.0-alpha.18",
|
|
106
106
|
"@claude-flow/security": "3.0.0-alpha.14",
|
|
107
107
|
"@claude-flow/shared": "3.0.0-alpha.7",
|
|
@@ -125,11 +125,11 @@
|
|
|
125
125
|
"ws": "^8.21.0",
|
|
126
126
|
"yaml": "^2.8.0",
|
|
127
127
|
"zod": "^3.22.0",
|
|
128
|
-
"@claude-flow/memory": "3.0.0-alpha.
|
|
128
|
+
"@claude-flow/memory": "3.0.0-alpha.26"
|
|
129
129
|
},
|
|
130
130
|
"optionalDependencies": {
|
|
131
131
|
"@agntcy/slim-bindings": "2.0.0-alpha.5",
|
|
132
|
-
"@claude-flow/memory": "3.0.0-alpha.
|
|
132
|
+
"@claude-flow/memory": "3.0.0-alpha.26",
|
|
133
133
|
"@metaharness/darwin": "~0.10.2",
|
|
134
134
|
"@metaharness/flywheel": "~0.1.10",
|
|
135
135
|
"@metaharness/radio": "~0.1.0",
|
|
@@ -38,7 +38,7 @@ needs its own ADR.
|
|
|
38
38
|
|
|
39
39
|
## Tools (subprocess-only, no library imports)
|
|
40
40
|
|
|
41
|
-
- All skills shell out to the pinned `metaharness` / `harness` binaries (`metaharness@~0.
|
|
41
|
+
- All skills shell out to the pinned `metaharness` / `harness` binaries (`metaharness@~0.4.1`, local install or one-time versioned cache — never `@latest`) via the `_harness.mjs` shared helper.
|
|
42
42
|
- 60s hard timeout per subprocess.
|
|
43
43
|
- Output captured + parsed; `--json` flag forced unless the script opts out.
|
|
44
44
|
- No `@metaharness/*` import statement appears outside the optional-router path in `v3/@claude-flow/cli/src/ruvector/neural-router.ts`.
|
|
@@ -4,7 +4,7 @@ description: MetaHarness integration — score, genome, mint, mcp-scan, threat-m
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
MetaHarness integration commands. All shell out to the PINNED
|
|
7
|
-
`metaharness`/`harness` binaries (`metaharness@~0.
|
|
7
|
+
`metaharness`/`harness` binaries (`metaharness@~0.4.1` — resolved from a local
|
|
8
8
|
install or a one-time `~/.ruflo/metaharness-cache-<pin>` install, never
|
|
9
9
|
`@latest`) via the `_harness.mjs` shared helper; no library imports on
|
|
10
10
|
ruflo's boot path.
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Mirrors `_harness.mjs` for the umbrella `metaharness` / `harness` binaries,
|
|
4
4
|
// but targets the separate darwin binary (`metaharness-darwin`) which is
|
|
5
|
-
// published as its own npm package (`@metaharness/darwin@~0.
|
|
5
|
+
// published as its own npm package (`@metaharness/darwin@~0.10.2`).
|
|
6
6
|
//
|
|
7
7
|
// Shared plumbing (degraded classification, --json injection, trailing-JSON
|
|
8
8
|
// parse, degraded emitter) lives in `_invoke.mjs`. What stays here is the
|
|
9
9
|
// genuinely-darwin part: the ASYNC STREAMING variant (`runDarwinAsync`) that
|
|
10
10
|
// long `evolve` runs need for per-generation progress visibility.
|
|
11
11
|
//
|
|
12
|
-
// Three subcommands surfaced (matches darwin 0.8.x — same verbs as
|
|
13
|
-
// with GEPA-engine evolve flags added upstream: --selection modes,
|
|
14
|
-
// --crossover, --epistasis, --curriculum, --mutator ruvllm, --sandbox
|
|
12
|
+
// Three subcommands surfaced (matches darwin 0.8.x-0.10.x — same verbs as
|
|
13
|
+
// 0.3.x, with GEPA-engine evolve flags added upstream: --selection modes,
|
|
14
|
+
// --crossover, --epistasis, --curriculum, --mutator ruvllm, --sandbox;
|
|
15
|
+
// 0.10.x only ADDS `evolve-numeric`, which this helper does not surface):
|
|
15
16
|
// - `metaharness-darwin evolve <repo> [...]` — harness self-improvement
|
|
16
17
|
// - `metaharness-darwin bench <create|verify> ...` — bench-suite lifecycle
|
|
17
18
|
// - `metaharness-darwin security bench [...]` — Darwin Shield (upstream ADR-155)
|
|
@@ -31,23 +32,43 @@
|
|
|
31
32
|
// matched to their --generations × --children × sandbox cost; the 60s
|
|
32
33
|
// default is for `bench verify` and `security bench --population 2 --cycles 1`
|
|
33
34
|
// smoke shapes only.
|
|
35
|
+
//
|
|
36
|
+
// RESOLUTION (#3366 — was `npx -y -p @metaharness/darwin@<pin>` on every call)
|
|
37
|
+
// Same order as _harness.mjs: (a) an installed @metaharness/darwin that
|
|
38
|
+
// satisfies the pin (the @claude-flow/cli optionalDependency — free), then
|
|
39
|
+
// (b) the one-time versioned `~/.ruflo/darwin-cache-<pin>` install that gepa
|
|
40
|
+
// already shares, then `process.execPath <abs bin>` with shell:false. The
|
|
41
|
+
// npx path had npm resolve the range on every evolve / bench /
|
|
42
|
+
// security-bench call (registry or npx cache state), ignored the copy ruflo
|
|
43
|
+
// ships, and could run a different darwin than the gepa import in the same
|
|
44
|
+
// plugin. (a) searches ruflo's own tree only — see findLocalPackageDir's
|
|
45
|
+
// `fromCwd` in _invoke.mjs.
|
|
34
46
|
|
|
35
47
|
import { spawnSync, spawn } from 'node:child_process';
|
|
36
48
|
import {
|
|
37
49
|
classifyDegraded,
|
|
50
|
+
ensureCachedInstall,
|
|
51
|
+
findLocalPackageDir,
|
|
38
52
|
importOptionalLibrary,
|
|
39
53
|
injectJson,
|
|
40
54
|
makeDegradedEmitter,
|
|
55
|
+
packageBinRelPath,
|
|
41
56
|
parseTrailingJson,
|
|
57
|
+
resolvePackageBin,
|
|
42
58
|
} from './_invoke.mjs';
|
|
43
59
|
|
|
44
60
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
45
61
|
|
|
46
|
-
// Pinned semver range.
|
|
47
|
-
//
|
|
48
|
-
//
|
|
62
|
+
// Pinned semver range. The `~` allows patch upgrades without re-pinning.
|
|
63
|
+
// Must cover the `@metaharness/darwin` optionalDependency range in
|
|
64
|
+
// v3/@claude-flow/cli/package.json (`~0.10.2`). It sat at ~0.8.0 while that
|
|
65
|
+
// range moved to ~0.9.0 (#2958) and ~0.10.2 (#3262), because the pin-drift
|
|
66
|
+
// guard only compared MH_DARWIN_PIN (distill-oracle.ts). This range picks
|
|
67
|
+
// the darwin version the evolve / bench / security-bench tools run and the
|
|
68
|
+
// gepa cache fallback installs; scripts/check-metaharness-pins.mjs now fails
|
|
69
|
+
// when it no longer covers the declared range.
|
|
49
70
|
const DARWIN_PKG = '@metaharness/darwin';
|
|
50
|
-
const DARWIN_PIN_VERSION = '~0.
|
|
71
|
+
const DARWIN_PIN_VERSION = '~0.10.2';
|
|
51
72
|
const DARWIN_PIN = `${DARWIN_PKG}@${DARWIN_PIN_VERSION}`;
|
|
52
73
|
|
|
53
74
|
const REASON_PREFIX = 'metaharness-darwin';
|
|
@@ -68,22 +89,71 @@ function buildArgv(args, wantJson) {
|
|
|
68
89
|
// (This last-block parse is now the family-wide shared implementation.)
|
|
69
90
|
const maybeParseJson = parseTrailingJson;
|
|
70
91
|
|
|
92
|
+
const DARWIN_BIN = 'metaharness-darwin';
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Absolute path of the `metaharness-darwin` bin (see RESOLUTION above).
|
|
96
|
+
* Memoized per process, like _harness.mjs resolveMetaharnessBins().
|
|
97
|
+
*/
|
|
98
|
+
let RESOLVED = null;
|
|
99
|
+
function resolveDarwinBin() {
|
|
100
|
+
if (RESOLVED) return RESOLVED;
|
|
101
|
+
// fromCwd:false — this lookup ends in a spawn of the bin with argv that
|
|
102
|
+
// comes from MCP input, so it only searches the ruflo install that owns
|
|
103
|
+
// this plugin, never the tool's cwd or its ancestors (see _invoke.mjs).
|
|
104
|
+
const localDir = findLocalPackageDir(DARWIN_PKG, DARWIN_PIN_VERSION, { fromCwd: false });
|
|
105
|
+
const localBin = localDir ? resolvePackageBin(localDir, DARWIN_BIN) : null;
|
|
106
|
+
if (localBin) return (RESOLVED = { ok: true, bin: localBin, source: 'local' });
|
|
107
|
+
let cached = ensureCachedInstall({ pkg: DARWIN_PKG, pinVersion: DARWIN_PIN_VERSION });
|
|
108
|
+
let cachedBin = cached.ok ? resolvePackageBin(cached.pkgDir, DARWIN_BIN) : null;
|
|
109
|
+
// The default cache probe is `<pkgDir>/package.json`, so a half-written
|
|
110
|
+
// cache — npm killed by the 180s install timeout, or two first calls
|
|
111
|
+
// installing into the same prefix — would count as installed forever and
|
|
112
|
+
// degrade every later call. npx repaired that by re-resolving; re-run the
|
|
113
|
+
// install once, probing the bin file itself, then give up (#3366).
|
|
114
|
+
if (cached.ok && !cachedBin) {
|
|
115
|
+
cached = ensureCachedInstall({
|
|
116
|
+
pkg: DARWIN_PKG,
|
|
117
|
+
pinVersion: DARWIN_PIN_VERSION,
|
|
118
|
+
cliRelPath: packageBinRelPath(cached.pkgDir, DARWIN_BIN) ?? 'dist/cli.js',
|
|
119
|
+
});
|
|
120
|
+
cachedBin = cached.ok ? resolvePackageBin(cached.pkgDir, DARWIN_BIN) : null;
|
|
121
|
+
}
|
|
122
|
+
if (cachedBin) return (RESOLVED = { ok: true, bin: cachedBin, source: 'cache' });
|
|
123
|
+
return (RESOLVED = {
|
|
124
|
+
ok: false,
|
|
125
|
+
stdout: cached.stdout ?? '',
|
|
126
|
+
stderr: cached.stderr ?? cached.error ?? `${DARWIN_PIN} has no ${DARWIN_BIN} bin`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Same degraded shape the npx path produced when the package was unreachable. */
|
|
131
|
+
function unavailable(resolved, start) {
|
|
132
|
+
return {
|
|
133
|
+
stdout: resolved.stdout, stderr: resolved.stderr,
|
|
134
|
+
exitCode: 127, json: null, durationMs: Date.now() - start,
|
|
135
|
+
degraded: true, reason: `${REASON_PREFIX}-not-available`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
71
139
|
/**
|
|
72
140
|
* Sync invocation. Use for short subcommands (`bench verify`, smoke shapes).
|
|
73
141
|
* Async variant below for long-running `evolve` calls.
|
|
74
142
|
*/
|
|
75
143
|
export function runDarwin(args, opts = {}) {
|
|
76
144
|
const start = Date.now();
|
|
145
|
+
const resolved = resolveDarwinBin();
|
|
146
|
+
if (!resolved.ok) return unavailable(resolved, start);
|
|
77
147
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
78
148
|
const wantJson = opts.json !== false;
|
|
79
149
|
const argv = buildArgv(args, wantJson);
|
|
80
|
-
const r = spawnSync(
|
|
150
|
+
const r = spawnSync(process.execPath, [resolved.bin, ...argv], {
|
|
81
151
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
82
152
|
encoding: 'utf-8',
|
|
83
153
|
timeout: timeoutMs,
|
|
84
154
|
cwd: opts.cwd,
|
|
85
155
|
env: { ...process.env, ...(opts.env || {}) },
|
|
86
|
-
shell:
|
|
156
|
+
shell: false,
|
|
87
157
|
});
|
|
88
158
|
const durationMs = Date.now() - start;
|
|
89
159
|
const stdout = r.stdout || '';
|
|
@@ -119,14 +189,16 @@ export function runDarwin(args, opts = {}) {
|
|
|
119
189
|
export function runDarwinAsync(args, opts = {}) {
|
|
120
190
|
return new Promise((resolve) => {
|
|
121
191
|
const start = Date.now();
|
|
192
|
+
const resolved = resolveDarwinBin();
|
|
193
|
+
if (!resolved.ok) { resolve(unavailable(resolved, start)); return; }
|
|
122
194
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
123
195
|
const wantJson = opts.json !== false;
|
|
124
196
|
const argv = buildArgv(args, wantJson);
|
|
125
|
-
const p = spawn(
|
|
197
|
+
const p = spawn(process.execPath, [resolved.bin, ...argv], {
|
|
126
198
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
127
199
|
cwd: opts.cwd,
|
|
128
200
|
env: { ...process.env, ...(opts.env || {}) },
|
|
129
|
-
shell:
|
|
201
|
+
shell: false,
|
|
130
202
|
});
|
|
131
203
|
let stdout = '', stderr = '', stderrBuf = '';
|
|
132
204
|
p.stdout?.on('data', (d) => { stdout += d.toString(); });
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
// arbitrary code on user machines on the very next skill invocation.
|
|
27
27
|
// The version is now PINNED to METAHARNESS_PIN_VERSION (tilde range —
|
|
28
28
|
// patch updates only) and only bumped deliberately, in lock-step with
|
|
29
|
-
//
|
|
29
|
+
// the range declared in @claude-flow/cli/package.json (enforced by
|
|
30
|
+
// scripts/check-metaharness-pins.mjs).
|
|
30
31
|
// 2. PERF: @latest forced an npm-registry metadata check on EVERY call.
|
|
31
32
|
// Resolution is now (a) an already-installed local metaharness
|
|
32
33
|
// satisfying the pin (walk-up node_modules — free), then (b) a ONE-TIME
|
|
@@ -53,10 +54,15 @@ import {
|
|
|
53
54
|
|
|
54
55
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
55
56
|
|
|
56
|
-
// Pinned semver range.
|
|
57
|
-
//
|
|
57
|
+
// Pinned semver range. NEVER @latest. Must cover the `metaharness` range
|
|
58
|
+
// declared in v3/@claude-flow/cli/package.json (optional peer `^0.4.1`):
|
|
59
|
+
// this constant decides which INSTALLED copy findLocalPackageDir() accepts,
|
|
60
|
+
// so a range that misses the declared one rejects the copy ruflo ships and
|
|
61
|
+
// npm-installs another at tool-call time. It sat at ~0.3.0 after the CLI
|
|
62
|
+
// moved to ^0.4.1 (b8ec03f34) because nothing compared the two;
|
|
63
|
+
// scripts/check-metaharness-pins.mjs now fails when they diverge.
|
|
58
64
|
const METAHARNESS_PKG = 'metaharness';
|
|
59
|
-
const METAHARNESS_PIN_VERSION = '~0.
|
|
65
|
+
const METAHARNESS_PIN_VERSION = '~0.4.1';
|
|
60
66
|
|
|
61
67
|
const REASON_NOT_AVAILABLE = 'metaharness-not-available';
|
|
62
68
|
|
|
@@ -21,9 +21,15 @@
|
|
|
21
21
|
// invalidate stale caches automatically
|
|
22
22
|
// - findLocalPackageDir() walk-up node_modules resolution so an already
|
|
23
23
|
// installed optionalDependency is used for free
|
|
24
|
+
// - resolvePackageBin() realpath'd bin-map entry of a resolved package
|
|
25
|
+
// (#3366 — lets _darwin/_redblue run the installed
|
|
26
|
+
// copy instead of npx / a cache install)
|
|
24
27
|
// - importOptionalLibrary() bare-import → cached-install fallback for
|
|
25
28
|
// library entries (gepa) — never throws on absence
|
|
26
29
|
// - makeDegradedEmitter() the ADR-150 rule-#3 exit-0 degraded payload
|
|
30
|
+
// - runRufloCli() `memory store|list|retrieve` through the ruflo CLI
|
|
31
|
+
// that ships this plugin (#3366) — replaces
|
|
32
|
+
// `npx @claude-flow/cli@latest` in 4 scripts
|
|
27
33
|
//
|
|
28
34
|
// WHAT DOES NOT LIVE HERE (the per-consumer parts):
|
|
29
35
|
// - _redblue's node-direct isMain workaround rationale
|
|
@@ -36,7 +42,7 @@
|
|
|
36
42
|
// - RUFLO_METAHARNESS_SKIP_LOCAL=1 disables local node_modules resolution
|
|
37
43
|
|
|
38
44
|
import { spawnSync } from 'node:child_process';
|
|
39
|
-
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
45
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync } from 'node:fs';
|
|
40
46
|
import { homedir } from 'node:os';
|
|
41
47
|
import { dirname, join } from 'node:path';
|
|
42
48
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -102,11 +108,22 @@ export function satisfiesTildeRange(version, pinVersion) {
|
|
|
102
108
|
* user's ruflo install shipped with). Returns the package dir or null.
|
|
103
109
|
* When `pinVersion` is given, only a copy satisfying the pin is accepted —
|
|
104
110
|
* a stale major/minor in an ancestor node_modules is skipped, not used.
|
|
111
|
+
*
|
|
112
|
+
* `{ fromCwd: false }` (#3366) searches ONLY the ruflo install that owns this
|
|
113
|
+
* plugin. The $CWD walk reaches every ancestor of the tool's working
|
|
114
|
+
* directory, i.e. directories the user's project — or anything above it —
|
|
115
|
+
* controls. That is tolerable for the pre-existing `metaharness` lookup,
|
|
116
|
+
* which this PR does not change, but the two lookups added here (_darwin,
|
|
117
|
+
* _redblue) END IN A SPAWN of the package's bin with MCP-supplied argv, so
|
|
118
|
+
* they stay inside ruflo's own tree and fall back to the pinned cache
|
|
119
|
+
* install instead.
|
|
105
120
|
*/
|
|
106
|
-
export function findLocalPackageDir(pkg, pinVersion) {
|
|
121
|
+
export function findLocalPackageDir(pkg, pinVersion, { fromCwd = true } = {}) {
|
|
107
122
|
if (process.env.RUFLO_METAHARNESS_SKIP_LOCAL === '1') return null;
|
|
108
123
|
const segments = pkg.split('/');
|
|
109
|
-
const starts =
|
|
124
|
+
const starts = fromCwd
|
|
125
|
+
? [dirname(fileURLToPath(import.meta.url)), process.cwd()]
|
|
126
|
+
: [dirname(fileURLToPath(import.meta.url))];
|
|
110
127
|
const seen = new Set();
|
|
111
128
|
for (const start of starts) {
|
|
112
129
|
let dir = start;
|
|
@@ -131,17 +148,51 @@ export function findLocalPackageDir(pkg, pinVersion) {
|
|
|
131
148
|
return null;
|
|
132
149
|
}
|
|
133
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Absolute path of `binName` from the package.json `bin` map of an already
|
|
153
|
+
* resolved package dir (findLocalPackageDir / ensureCachedInstall), or null.
|
|
154
|
+
* Read from the bin map rather than hardcoded, same as _harness.mjs.
|
|
155
|
+
*
|
|
156
|
+
* REALPATH'd on purpose (#3366): pnpm and other symlinked layouts hand us a
|
|
157
|
+
* symlinked package dir, and @metaharness/redblue's CLI only dispatches when
|
|
158
|
+
* `import.meta.url === file://${process.argv[1]}` (see _redblue.mjs) — Node
|
|
159
|
+
* reports import.meta.url as the REAL path, so argv[1] has to be one too or
|
|
160
|
+
* the CLI exits 0 having done nothing.
|
|
161
|
+
*/
|
|
162
|
+
export function resolvePackageBin(pkgDir, binName) {
|
|
163
|
+
const rel = packageBinRelPath(pkgDir, binName);
|
|
164
|
+
if (!rel) return null;
|
|
165
|
+
try {
|
|
166
|
+
const abs = join(pkgDir, rel);
|
|
167
|
+
return existsSync(abs) ? realpathSync(abs) : null;
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The package.json `bin` entry for `binName`, as declared (relative), or null. */
|
|
174
|
+
export function packageBinRelPath(pkgDir, binName) {
|
|
175
|
+
try {
|
|
176
|
+
const pj = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf-8'));
|
|
177
|
+
const rel = typeof pj.bin === 'string' ? pj.bin : pj.bin?.[binName];
|
|
178
|
+
return rel || null;
|
|
179
|
+
} catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
134
184
|
/**
|
|
135
185
|
* One-time versioned cache install of a PINNED range. Generalizes
|
|
136
186
|
* gepa.mjs:98-121 / _redblue.mjs:72-102. The cache dir is
|
|
137
187
|
* `<base>/<shortname>-cache-<pin-digits>` (e.g. redblue-cache-0.1.4,
|
|
138
|
-
* darwin-cache-0.
|
|
188
|
+
* darwin-cache-0.10.2, metaharness-cache-0.4.1) so existing caches created
|
|
139
189
|
* by the pre-consolidation helpers remain valid, and bumping the pin
|
|
140
|
-
* invalidates stale installs automatically.
|
|
190
|
+
* invalidates stale installs automatically (the pre-bump darwin-cache-0.8.0
|
|
191
|
+
* and metaharness-cache-0.3.0 dirs are simply no longer selected).
|
|
141
192
|
*
|
|
142
193
|
* @param {object} spec
|
|
143
194
|
* @param {string} spec.pkg npm package name (may be scoped)
|
|
144
|
-
* @param {string} spec.pinVersion tilde range, e.g. '~0.
|
|
195
|
+
* @param {string} spec.pinVersion tilde range, e.g. '~0.4.1'
|
|
145
196
|
* @param {string} [spec.cliRelPath] path inside the package that must exist
|
|
146
197
|
* post-install (also returned as cliPath)
|
|
147
198
|
* @param {number} [spec.timeoutMs] install timeout (default 180s)
|
|
@@ -228,3 +279,66 @@ export function makeDegradedEmitter(pkg, pinVersion) {
|
|
|
228
279
|
process.exit(0);
|
|
229
280
|
};
|
|
230
281
|
}
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// The ruflo CLI that ships this plugin (#3366)
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
// audit-list / audit-trend / oia-audit / similarity read and write the
|
|
287
|
+
// `metaharness-audit` memory namespace through the ruflo CLI. They used to
|
|
288
|
+
// spawn `npx @claude-flow/cli@latest memory …`, which on every call:
|
|
289
|
+
// - resolved `latest` through the npm registry (the "metadata check on
|
|
290
|
+
// EVERY call" _harness.mjs removed for its own npx path); without registry
|
|
291
|
+
// access it fails, so oia_audit reports `persisted: false` and audit_list
|
|
292
|
+
// silently reports 0 records;
|
|
293
|
+
// - could run a DIFFERENT @claude-flow/cli than the one that spawned the
|
|
294
|
+
// tool (npx cache vs registry skew — #3306), i.e. another memory backend /
|
|
295
|
+
// schema than the MCP server's own memory_* tools see;
|
|
296
|
+
// - cold-fetched the whole CLI when the npx cache was empty (#3145, #3154).
|
|
297
|
+
// The plugin is published INSIDE @claude-flow/cli
|
|
298
|
+
// (<cli>/plugins/ruflo-metaharness/scripts — see prepare-publish.mjs), and the
|
|
299
|
+
// MCP server / `ruflo metaharness` dispatcher locate it by walking up from
|
|
300
|
+
// their own dist/, so the owning CLI is two directories above this plugin.
|
|
301
|
+
// It is run with process.execPath + bin/cli.js (the mcp-launch.cjs /
|
|
302
|
+
// daemon-autostart.ts pattern). A candidate only counts when dist/src/index.js
|
|
303
|
+
// exists next to bin/cli.js — mcp-launch.cjs resolveLocalCliBin()'s guard
|
|
304
|
+
// against an unbuilt checkout — AND its package.json is named
|
|
305
|
+
// @claude-flow/cli, a check added here so an unrelated directory two levels up
|
|
306
|
+
// can never qualify. Unchanged:
|
|
307
|
+
// - CLI_CORE=1 still opts into `npx @claude-flow/cli-core@alpha` (ADR-100);
|
|
308
|
+
// - no usable local CLI (e.g. a marketplace clone with no build) still falls
|
|
309
|
+
// back to `npx @claude-flow/cli@latest`.
|
|
310
|
+
const PLUGIN_SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));
|
|
311
|
+
|
|
312
|
+
let RUFLO_CLI = null;
|
|
313
|
+
/** `{ command, args, shell, source }` for invoking the ruflo CLI. Memoized. */
|
|
314
|
+
export function resolveRufloCli() {
|
|
315
|
+
if (RUFLO_CLI) return RUFLO_CLI;
|
|
316
|
+
if (process.env.CLI_CORE === '1') {
|
|
317
|
+
return (RUFLO_CLI = { command: 'npx', args: ['@claude-flow/cli-core@alpha'], shell: process.platform === 'win32', source: 'npx-cli-core' });
|
|
318
|
+
}
|
|
319
|
+
const candidates = [
|
|
320
|
+
join(PLUGIN_SCRIPTS_DIR, '..', '..', '..'), // published: <cli>/plugins/ruflo-metaharness/scripts
|
|
321
|
+
join(PLUGIN_SCRIPTS_DIR, '..', '..', '..', 'v3', '@claude-flow', 'cli'), // repo checkout / marketplace clone
|
|
322
|
+
];
|
|
323
|
+
for (const dir of candidates) {
|
|
324
|
+
try {
|
|
325
|
+
const bin = join(dir, 'bin', 'cli.js');
|
|
326
|
+
const pj = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'));
|
|
327
|
+
if (pj.name === '@claude-flow/cli' && existsSync(bin) && existsSync(join(dir, 'dist', 'src', 'index.js'))) {
|
|
328
|
+
return (RUFLO_CLI = { command: process.execPath, args: [bin], shell: false, source: 'local' });
|
|
329
|
+
}
|
|
330
|
+
} catch { /* not this layout — try the next */ }
|
|
331
|
+
}
|
|
332
|
+
return (RUFLO_CLI = { command: 'npx', args: ['@claude-flow/cli@latest'], shell: process.platform === 'win32', source: 'npx' });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** spawnSync the ruflo CLI with `args` (e.g. ['memory', 'list', …]); same result shape as before. */
|
|
336
|
+
export function runRufloCli(args, opts = {}) {
|
|
337
|
+
const cli = resolveRufloCli();
|
|
338
|
+
return spawnSync(cli.command, [...cli.args, ...args], {
|
|
339
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
340
|
+
encoding: 'utf-8',
|
|
341
|
+
shell: cli.shell,
|
|
342
|
+
...opts,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
@@ -32,6 +32,16 @@
|
|
|
32
32
|
// (file separately — when fixed, we could go back to a bin-shim pattern,
|
|
33
33
|
// though the node-direct cached path is now the family-wide standard anyway).
|
|
34
34
|
//
|
|
35
|
+
// INSTALLED COPY FIRST (#3366): an @metaharness/redblue already on disk that
|
|
36
|
+
// satisfies the pin (a ruflo 3.42.4 install carries 0.1.4, via metaharness /
|
|
37
|
+
// ruvector) is used before the cache install, same order as _harness.mjs /
|
|
38
|
+
// _darwin.mjs. Previously the first call always ran `npm install` into the
|
|
39
|
+
// cache even with that copy present.
|
|
40
|
+
// Whichever copy is chosen, its CLI path is realpath'd
|
|
41
|
+
// (_invoke.resolvePackageBin / realpathSync): a pnpm-symlinked package dir,
|
|
42
|
+
// or a cache base under a symlink such as macOS /tmp -> /private/tmp, would
|
|
43
|
+
// otherwise hit the same isMain mismatch described above.
|
|
44
|
+
//
|
|
35
45
|
// CONTRACT (matches runMetaharness/runDarwin):
|
|
36
46
|
// - returns `{ stdout, stderr, exitCode, durationMs, degraded, reason? }`
|
|
37
47
|
// - subprocess hard timeout (default 120s; --mock-judge runs are seconds)
|
|
@@ -47,12 +57,15 @@
|
|
|
47
57
|
// $OPENROUTER_API_KEY which we never inject.
|
|
48
58
|
|
|
49
59
|
import { spawnSync } from 'node:child_process';
|
|
60
|
+
import { realpathSync } from 'node:fs';
|
|
50
61
|
import { homedir } from 'node:os';
|
|
51
62
|
import { join } from 'node:path';
|
|
52
63
|
import {
|
|
53
64
|
classifyDegraded,
|
|
54
65
|
ensureCachedInstall,
|
|
66
|
+
findLocalPackageDir,
|
|
55
67
|
makeDegradedEmitter,
|
|
68
|
+
resolvePackageBin,
|
|
56
69
|
} from './_invoke.mjs';
|
|
57
70
|
|
|
58
71
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
@@ -80,8 +93,15 @@ export function runRedblue(args, opts = {}) {
|
|
|
80
93
|
const start = Date.now();
|
|
81
94
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
82
95
|
|
|
83
|
-
//
|
|
84
|
-
|
|
96
|
+
// 0. An installed copy satisfying the pin — free, no npm (#3366).
|
|
97
|
+
// fromCwd:false: the resolved bin is SPAWNED with argv that comes from
|
|
98
|
+
// MCP input, so only ruflo's own tree is searched, never the tool's cwd
|
|
99
|
+
// or its ancestors (see findLocalPackageDir in _invoke.mjs).
|
|
100
|
+
const localDir = findLocalPackageDir(REDBLUE_PKG, REDBLUE_PIN_VERSION, { fromCwd: false });
|
|
101
|
+
const localCli = localDir ? resolvePackageBin(localDir, 'redblue') : null;
|
|
102
|
+
|
|
103
|
+
// 1. Otherwise ensure redblue is installed in our cache dir (one-time install).
|
|
104
|
+
const install = localCli ? { ok: true, cliPath: localCli } : ensureCachedInstall({
|
|
85
105
|
pkg: REDBLUE_PKG,
|
|
86
106
|
pinVersion: REDBLUE_PIN_VERSION,
|
|
87
107
|
cliRelPath: CLI_REL_PATH,
|
|
@@ -99,15 +119,22 @@ export function runRedblue(args, opts = {}) {
|
|
|
99
119
|
};
|
|
100
120
|
}
|
|
101
121
|
|
|
102
|
-
// 2. Invoke
|
|
103
|
-
// succeeds (argv[1] matches import.meta.url)
|
|
104
|
-
|
|
122
|
+
// 2. Invoke `<this node> <real-path-to-cli> <args>` so upstream's isMain
|
|
123
|
+
// check succeeds (argv[1] matches import.meta.url) — realpath'd, see
|
|
124
|
+
// header. process.execPath + shell:false, like _darwin/_harness: the
|
|
125
|
+
// argv carries MCP-supplied values (--config / --out / --in), and with
|
|
126
|
+
// shell:true on win32 Node hands cmd.exe one unquoted string, so a value
|
|
127
|
+
// such as `x.yaml & whoami` would run as a command (and a path with a
|
|
128
|
+
// space — `C:\Program Files\…` — would break). node needs no shell.
|
|
129
|
+
let cliPath = install.cliPath;
|
|
130
|
+
try { cliPath = realpathSync(cliPath); } catch { /* missing — node's MODULE_NOT_FOUND → degraded below */ }
|
|
131
|
+
const r = spawnSync(process.execPath, [cliPath, ...args], {
|
|
105
132
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
106
133
|
encoding: 'utf-8',
|
|
107
134
|
timeout: timeoutMs,
|
|
108
135
|
cwd: opts.cwd,
|
|
109
136
|
env: { ...process.env, ...(opts.env || {}) },
|
|
110
|
-
shell:
|
|
137
|
+
shell: false,
|
|
111
138
|
});
|
|
112
139
|
const durationMs = Date.now() - start;
|
|
113
140
|
const stdout = r.stdout || '';
|