claude-flow 3.5.74 → 3.5.76
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 +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/embeddings.js +76 -51
- package/v3/@claude-flow/cli/dist/src/commands/hive-mind.js +4 -2
- package/v3/@claude-flow/cli/dist/src/commands/index.d.ts +7 -13
- package/v3/@claude-flow/cli/dist/src/commands/index.js +48 -99
- package/v3/@claude-flow/cli/dist/src/commands/init.js +22 -6
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/backup.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/benchmark.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/import.js +30 -6
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/init.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/migrate.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/optimize.js +2 -1
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/pg-utils.d.ts +14 -0
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/pg-utils.js +41 -0
- package/v3/@claude-flow/cli/dist/src/commands/ruvector/status.js +2 -1
- package/v3/@claude-flow/cli/dist/src/index.js +42 -32
- package/v3/@claude-flow/cli/dist/src/mcp-server.js +23 -5
- package/v3/@claude-flow/cli/dist/src/plugins/manager.js +2 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/lora-adapter.js +4 -1
- package/v3/@claude-flow/cli/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.76",
|
|
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",
|
|
@@ -145,62 +145,87 @@ const searchCommand = {
|
|
|
145
145
|
const queryResult = await generateEmbedding(query);
|
|
146
146
|
const queryEmbedding = queryResult.embedding;
|
|
147
147
|
// Get all entries with embeddings from database
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
148
|
+
// Parameterized query to prevent SQL injection (CRIT-01)
|
|
149
|
+
const embeddingSql = namespace !== 'all'
|
|
150
|
+
? `SELECT id, key, namespace, content, embedding, embedding_dimensions
|
|
151
|
+
FROM memory_entries
|
|
152
|
+
WHERE status = 'active' AND embedding IS NOT NULL AND namespace = ?
|
|
153
|
+
LIMIT 1000`
|
|
154
|
+
: `SELECT id, key, namespace, content, embedding, embedding_dimensions
|
|
155
|
+
FROM memory_entries
|
|
156
|
+
WHERE status = 'active' AND embedding IS NOT NULL
|
|
157
|
+
LIMIT 1000`;
|
|
158
|
+
const embeddingStmt = db.prepare(embeddingSql);
|
|
159
|
+
if (namespace !== 'all') {
|
|
160
|
+
embeddingStmt.bind([namespace]);
|
|
161
|
+
}
|
|
162
|
+
const entryRows = [];
|
|
163
|
+
while (embeddingStmt.step()) {
|
|
164
|
+
entryRows.push(embeddingStmt.get());
|
|
165
|
+
}
|
|
166
|
+
embeddingStmt.free();
|
|
156
167
|
const results = [];
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
catch {
|
|
177
|
-
// Skip entries with invalid embeddings
|
|
168
|
+
for (const row of entryRows) {
|
|
169
|
+
const [id, key, ns, content, embeddingJson] = row;
|
|
170
|
+
if (!embeddingJson)
|
|
171
|
+
continue;
|
|
172
|
+
try {
|
|
173
|
+
const embedding = JSON.parse(embeddingJson);
|
|
174
|
+
// Calculate cosine similarity
|
|
175
|
+
const similarity = cosineSimilarity(queryEmbedding, embedding);
|
|
176
|
+
if (similarity >= threshold) {
|
|
177
|
+
results.push({
|
|
178
|
+
score: similarity,
|
|
179
|
+
id: id.substring(0, 10),
|
|
180
|
+
key: key || id.substring(0, 15),
|
|
181
|
+
content: (content || '').substring(0, 45) + ((content || '').length > 45 ? '...' : ''),
|
|
182
|
+
namespace: ns || 'default'
|
|
183
|
+
});
|
|
178
184
|
}
|
|
179
185
|
}
|
|
186
|
+
catch {
|
|
187
|
+
// Skip entries with invalid embeddings
|
|
188
|
+
}
|
|
180
189
|
}
|
|
181
|
-
//
|
|
190
|
+
// Keyword search fallback with parameterized query (CRIT-01)
|
|
182
191
|
if (results.length < limit) {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
192
|
+
const likePattern = `%${query}%`;
|
|
193
|
+
const remainingLimit = Math.max(0, limit - results.length);
|
|
194
|
+
const keywordSql = namespace !== 'all'
|
|
195
|
+
? `SELECT id, key, namespace, content
|
|
196
|
+
FROM memory_entries
|
|
197
|
+
WHERE status = 'active'
|
|
198
|
+
AND (content LIKE ? OR key LIKE ?)
|
|
199
|
+
AND namespace = ?
|
|
200
|
+
LIMIT ?`
|
|
201
|
+
: `SELECT id, key, namespace, content
|
|
202
|
+
FROM memory_entries
|
|
203
|
+
WHERE status = 'active'
|
|
204
|
+
AND (content LIKE ? OR key LIKE ?)
|
|
205
|
+
LIMIT ?`;
|
|
206
|
+
const keywordStmt = db.prepare(keywordSql);
|
|
207
|
+
if (namespace !== 'all') {
|
|
208
|
+
keywordStmt.bind([likePattern, likePattern, namespace, remainingLimit]);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
keywordStmt.bind([likePattern, likePattern, remainingLimit]);
|
|
212
|
+
}
|
|
213
|
+
const keywordRows = [];
|
|
214
|
+
while (keywordStmt.step()) {
|
|
215
|
+
keywordRows.push(keywordStmt.get());
|
|
216
|
+
}
|
|
217
|
+
keywordStmt.free();
|
|
218
|
+
for (const row of keywordRows) {
|
|
219
|
+
const [id, key, ns, content] = row;
|
|
220
|
+
// Avoid duplicates
|
|
221
|
+
if (!results.some(r => r.id === id.substring(0, 10))) {
|
|
222
|
+
results.push({
|
|
223
|
+
score: 0.5, // Keyword match base score
|
|
224
|
+
id: id.substring(0, 10),
|
|
225
|
+
key: key || id.substring(0, 15),
|
|
226
|
+
content: (content || '').substring(0, 45) + ((content || '').length > 45 ? '...' : ''),
|
|
227
|
+
namespace: ns || 'default'
|
|
228
|
+
});
|
|
204
229
|
}
|
|
205
230
|
}
|
|
206
231
|
}
|
|
@@ -208,8 +208,10 @@ async function spawnClaudeCodeInstance(swarmId, swarmName, objective, workers, f
|
|
|
208
208
|
claudeArgs.push('--verbose');
|
|
209
209
|
output.printInfo('Running in non-interactive mode');
|
|
210
210
|
}
|
|
211
|
-
//
|
|
212
|
-
|
|
211
|
+
// HIGH-02: Strict boolean check (=== true) instead of loose truthiness (!== false)
|
|
212
|
+
// to prevent undefined/null from being treated as "skip permissions".
|
|
213
|
+
// Behavior change: only explicit --dangerously-skip-permissions flag triggers skip.
|
|
214
|
+
const skipPermissions = flags['dangerously-skip-permissions'] === true && !flags['no-auto-permissions'];
|
|
213
215
|
if (skipPermissions) {
|
|
214
216
|
claudeArgs.push('--dangerously-skip-permissions');
|
|
215
217
|
if (!isNonInteractive) {
|
|
@@ -19,18 +19,6 @@ export { swarmCommand } from './swarm.js';
|
|
|
19
19
|
export { memoryCommand } from './memory.js';
|
|
20
20
|
export { mcpCommand } from './mcp.js';
|
|
21
21
|
export { hooksCommand } from './hooks.js';
|
|
22
|
-
export { daemonCommand } from './daemon.js';
|
|
23
|
-
export { doctorCommand } from './doctor.js';
|
|
24
|
-
export { embeddingsCommand } from './embeddings.js';
|
|
25
|
-
export { neuralCommand } from './neural.js';
|
|
26
|
-
export { performanceCommand } from './performance.js';
|
|
27
|
-
export { securityCommand } from './security.js';
|
|
28
|
-
export { ruvectorCommand } from './ruvector/index.js';
|
|
29
|
-
export { hiveMindCommand } from './hive-mind.js';
|
|
30
|
-
export { guidanceCommand } from './guidance.js';
|
|
31
|
-
export { applianceCommand } from './appliance.js';
|
|
32
|
-
export { cleanupCommand } from './cleanup.js';
|
|
33
|
-
export { autopilotCommand } from './autopilot.js';
|
|
34
22
|
export declare function getConfigCommand(): Promise<Command | undefined>;
|
|
35
23
|
export declare function getMigrateCommand(): Promise<Command | undefined>;
|
|
36
24
|
export declare function getWorkflowCommand(): Promise<Command | undefined>;
|
|
@@ -62,7 +50,8 @@ export declare function getAutopilotCommand(): Promise<Command | undefined>;
|
|
|
62
50
|
*/
|
|
63
51
|
export declare const commands: Command[];
|
|
64
52
|
/**
|
|
65
|
-
* Commands organized by category for help display
|
|
53
|
+
* Commands organized by category for help display (synchronous core only).
|
|
54
|
+
* @deprecated Use getCommandsByCategory() for full categorized listing.
|
|
66
55
|
*/
|
|
67
56
|
export declare const commandsByCategory: {
|
|
68
57
|
primary: Command[];
|
|
@@ -71,6 +60,11 @@ export declare const commandsByCategory: {
|
|
|
71
60
|
analysis: Command[];
|
|
72
61
|
management: Command[];
|
|
73
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Async version that loads all commands by category (PERF-03).
|
|
65
|
+
* Use this for help display and full command listings.
|
|
66
|
+
*/
|
|
67
|
+
export declare function getCommandsByCategory(): Promise<Record<string, Command[]>>;
|
|
74
68
|
/**
|
|
75
69
|
* Command registry map for quick lookup
|
|
76
70
|
* Supports both sync (core commands) and async (lazy-loaded) commands
|
|
@@ -99,6 +99,8 @@ async function loadCommand(name) {
|
|
|
99
99
|
// Synchronous Imports for Core Commands (needed immediately at startup)
|
|
100
100
|
// These are the most commonly used commands that need instant access
|
|
101
101
|
// =============================================================================
|
|
102
|
+
// PERF-03: Only import core commands synchronously (~10 most-used).
|
|
103
|
+
// All other commands are lazy-loaded via commandLoaders on demand.
|
|
102
104
|
import { initCommand } from './init.js';
|
|
103
105
|
import { startCommand } from './start.js';
|
|
104
106
|
import { statusCommand } from './status.js';
|
|
@@ -109,34 +111,7 @@ import { swarmCommand } from './swarm.js';
|
|
|
109
111
|
import { memoryCommand } from './memory.js';
|
|
110
112
|
import { mcpCommand } from './mcp.js';
|
|
111
113
|
import { hooksCommand } from './hooks.js';
|
|
112
|
-
|
|
113
|
-
import { doctorCommand } from './doctor.js';
|
|
114
|
-
import { embeddingsCommand } from './embeddings.js';
|
|
115
|
-
import { neuralCommand } from './neural.js';
|
|
116
|
-
import { performanceCommand } from './performance.js';
|
|
117
|
-
import { securityCommand } from './security.js';
|
|
118
|
-
import { ruvectorCommand } from './ruvector/index.js';
|
|
119
|
-
import { hiveMindCommand } from './hive-mind.js';
|
|
120
|
-
// Additional commands for categorized help display
|
|
121
|
-
import { configCommand } from './config.js';
|
|
122
|
-
import { completionsCommand } from './completions.js';
|
|
123
|
-
import { migrateCommand } from './migrate.js';
|
|
124
|
-
import { workflowCommand } from './workflow.js';
|
|
125
|
-
import { analyzeCommand } from './analyze.js';
|
|
126
|
-
import { routeCommand } from './route.js';
|
|
127
|
-
import { progressCommand } from './progress.js';
|
|
128
|
-
import { providersCommand } from './providers.js';
|
|
129
|
-
import { pluginsCommand } from './plugins.js';
|
|
130
|
-
import { deploymentCommand } from './deployment.js';
|
|
131
|
-
import { claimsCommand } from './claims.js';
|
|
132
|
-
import { issuesCommand } from './issues.js';
|
|
133
|
-
import updateCommand from './update.js';
|
|
134
|
-
import { processCommand } from './process.js';
|
|
135
|
-
import { guidanceCommand } from './guidance.js';
|
|
136
|
-
import { applianceCommand } from './appliance.js';
|
|
137
|
-
import { cleanupCommand } from './cleanup.js';
|
|
138
|
-
import { autopilotCommand } from './autopilot.js';
|
|
139
|
-
// Pre-populate cache with core commands
|
|
114
|
+
// Pre-populate cache with core commands only
|
|
140
115
|
loadedCommands.set('init', initCommand);
|
|
141
116
|
loadedCommands.set('start', startCommand);
|
|
142
117
|
loadedCommands.set('status', statusCommand);
|
|
@@ -147,21 +122,10 @@ loadedCommands.set('swarm', swarmCommand);
|
|
|
147
122
|
loadedCommands.set('memory', memoryCommand);
|
|
148
123
|
loadedCommands.set('mcp', mcpCommand);
|
|
149
124
|
loadedCommands.set('hooks', hooksCommand);
|
|
150
|
-
loadedCommands.set('daemon', daemonCommand);
|
|
151
|
-
loadedCommands.set('doctor', doctorCommand);
|
|
152
|
-
loadedCommands.set('embeddings', embeddingsCommand);
|
|
153
|
-
loadedCommands.set('neural', neuralCommand);
|
|
154
|
-
loadedCommands.set('performance', performanceCommand);
|
|
155
|
-
loadedCommands.set('security', securityCommand);
|
|
156
|
-
loadedCommands.set('ruvector', ruvectorCommand);
|
|
157
|
-
loadedCommands.set('hive-mind', hiveMindCommand);
|
|
158
|
-
loadedCommands.set('guidance', guidanceCommand);
|
|
159
|
-
loadedCommands.set('cleanup', cleanupCommand);
|
|
160
|
-
loadedCommands.set('autopilot', autopilotCommand);
|
|
161
125
|
// =============================================================================
|
|
162
126
|
// Exports (maintain backwards compatibility)
|
|
163
127
|
// =============================================================================
|
|
164
|
-
// Export
|
|
128
|
+
// Export core commands (synchronous)
|
|
165
129
|
export { initCommand } from './init.js';
|
|
166
130
|
export { startCommand } from './start.js';
|
|
167
131
|
export { statusCommand } from './status.js';
|
|
@@ -172,18 +136,6 @@ export { swarmCommand } from './swarm.js';
|
|
|
172
136
|
export { memoryCommand } from './memory.js';
|
|
173
137
|
export { mcpCommand } from './mcp.js';
|
|
174
138
|
export { hooksCommand } from './hooks.js';
|
|
175
|
-
export { daemonCommand } from './daemon.js';
|
|
176
|
-
export { doctorCommand } from './doctor.js';
|
|
177
|
-
export { embeddingsCommand } from './embeddings.js';
|
|
178
|
-
export { neuralCommand } from './neural.js';
|
|
179
|
-
export { performanceCommand } from './performance.js';
|
|
180
|
-
export { securityCommand } from './security.js';
|
|
181
|
-
export { ruvectorCommand } from './ruvector/index.js';
|
|
182
|
-
export { hiveMindCommand } from './hive-mind.js';
|
|
183
|
-
export { guidanceCommand } from './guidance.js';
|
|
184
|
-
export { applianceCommand } from './appliance.js';
|
|
185
|
-
export { cleanupCommand } from './cleanup.js';
|
|
186
|
-
export { autopilotCommand } from './autopilot.js';
|
|
187
139
|
// Lazy-loaded command re-exports (for backwards compatibility, but async-only)
|
|
188
140
|
export async function getConfigCommand() { return loadCommand('config'); }
|
|
189
141
|
export async function getMigrateCommand() { return loadCommand('migrate'); }
|
|
@@ -215,7 +167,7 @@ export async function getAutopilotCommand() { return loadCommand('autopilot'); }
|
|
|
215
167
|
* Advanced commands loaded on-demand for faster startup
|
|
216
168
|
*/
|
|
217
169
|
export const commands = [
|
|
218
|
-
// Core commands (synchronously loaded)
|
|
170
|
+
// Core commands (synchronously loaded) — PERF-03
|
|
219
171
|
initCommand,
|
|
220
172
|
startCommand,
|
|
221
173
|
statusCommand,
|
|
@@ -226,20 +178,10 @@ export const commands = [
|
|
|
226
178
|
memoryCommand,
|
|
227
179
|
mcpCommand,
|
|
228
180
|
hooksCommand,
|
|
229
|
-
daemonCommand,
|
|
230
|
-
doctorCommand,
|
|
231
|
-
embeddingsCommand,
|
|
232
|
-
neuralCommand,
|
|
233
|
-
performanceCommand,
|
|
234
|
-
securityCommand,
|
|
235
|
-
ruvectorCommand,
|
|
236
|
-
hiveMindCommand,
|
|
237
|
-
guidanceCommand,
|
|
238
|
-
cleanupCommand,
|
|
239
|
-
autopilotCommand,
|
|
240
181
|
];
|
|
241
182
|
/**
|
|
242
|
-
* Commands organized by category for help display
|
|
183
|
+
* Commands organized by category for help display (synchronous core only).
|
|
184
|
+
* @deprecated Use getCommandsByCategory() for full categorized listing.
|
|
243
185
|
*/
|
|
244
186
|
export const commandsByCategory = {
|
|
245
187
|
primary: [
|
|
@@ -254,41 +196,48 @@ export const commandsByCategory = {
|
|
|
254
196
|
mcpCommand,
|
|
255
197
|
hooksCommand,
|
|
256
198
|
],
|
|
257
|
-
advanced: [
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
embeddingsCommand,
|
|
262
|
-
hiveMindCommand,
|
|
263
|
-
ruvectorCommand,
|
|
264
|
-
guidanceCommand,
|
|
265
|
-
autopilotCommand,
|
|
266
|
-
],
|
|
267
|
-
utility: [
|
|
268
|
-
configCommand,
|
|
269
|
-
doctorCommand,
|
|
270
|
-
daemonCommand,
|
|
271
|
-
completionsCommand,
|
|
272
|
-
migrateCommand,
|
|
273
|
-
workflowCommand,
|
|
274
|
-
],
|
|
275
|
-
analysis: [
|
|
276
|
-
analyzeCommand,
|
|
277
|
-
routeCommand,
|
|
278
|
-
progressCommand,
|
|
279
|
-
],
|
|
280
|
-
management: [
|
|
281
|
-
providersCommand,
|
|
282
|
-
pluginsCommand,
|
|
283
|
-
deploymentCommand,
|
|
284
|
-
claimsCommand,
|
|
285
|
-
issuesCommand,
|
|
286
|
-
updateCommand,
|
|
287
|
-
processCommand,
|
|
288
|
-
applianceCommand,
|
|
289
|
-
cleanupCommand,
|
|
290
|
-
],
|
|
199
|
+
advanced: [],
|
|
200
|
+
utility: [],
|
|
201
|
+
analysis: [],
|
|
202
|
+
management: [],
|
|
291
203
|
};
|
|
204
|
+
/**
|
|
205
|
+
* Async version that loads all commands by category (PERF-03).
|
|
206
|
+
* Use this for help display and full command listings.
|
|
207
|
+
*/
|
|
208
|
+
export async function getCommandsByCategory() {
|
|
209
|
+
const [daemonCmd, doctorCmd, embeddingsCmd, neuralCmd, performanceCmd, securityCmd, ruvectorCmd, hiveMindCmd, configCmd, completionsCmd, migrateCmd, workflowCmd, analyzeCmd, routeCmd, progressCmd, providersCmd, pluginsCmd, deploymentCmd, claimsCmd, issuesCmd, updateCmd, processCmd, guidanceCmd, applianceCmd, cleanupCmd, autopilotCmd,] = await Promise.all([
|
|
210
|
+
loadCommand('daemon'), loadCommand('doctor'), loadCommand('embeddings'), loadCommand('neural'),
|
|
211
|
+
loadCommand('performance'), loadCommand('security'), loadCommand('ruvector'), loadCommand('hive-mind'),
|
|
212
|
+
loadCommand('config'), loadCommand('completions'), loadCommand('migrate'), loadCommand('workflow'),
|
|
213
|
+
loadCommand('analyze'), loadCommand('route'), loadCommand('progress'), loadCommand('providers'),
|
|
214
|
+
loadCommand('plugins'), loadCommand('deployment'), loadCommand('claims'), loadCommand('issues'),
|
|
215
|
+
loadCommand('update'), loadCommand('process'), loadCommand('guidance'), loadCommand('appliance'),
|
|
216
|
+
loadCommand('cleanup'), loadCommand('autopilot'),
|
|
217
|
+
]);
|
|
218
|
+
return {
|
|
219
|
+
primary: [
|
|
220
|
+
initCommand, startCommand, statusCommand, agentCommand,
|
|
221
|
+
swarmCommand, memoryCommand, taskCommand, sessionCommand,
|
|
222
|
+
mcpCommand, hooksCommand,
|
|
223
|
+
],
|
|
224
|
+
advanced: [
|
|
225
|
+
neuralCmd, securityCmd, performanceCmd, embeddingsCmd,
|
|
226
|
+
hiveMindCmd, ruvectorCmd, guidanceCmd, autopilotCmd,
|
|
227
|
+
].filter(Boolean),
|
|
228
|
+
utility: [
|
|
229
|
+
configCmd, doctorCmd, daemonCmd, completionsCmd,
|
|
230
|
+
migrateCmd, workflowCmd,
|
|
231
|
+
].filter(Boolean),
|
|
232
|
+
analysis: [
|
|
233
|
+
analyzeCmd, routeCmd, progressCmd,
|
|
234
|
+
].filter(Boolean),
|
|
235
|
+
management: [
|
|
236
|
+
providersCmd, pluginsCmd, deploymentCmd, claimsCmd,
|
|
237
|
+
issuesCmd, updateCmd, processCmd, applianceCmd, cleanupCmd,
|
|
238
|
+
].filter(Boolean),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
292
241
|
/**
|
|
293
242
|
* Command registry map for quick lookup
|
|
294
243
|
* Supports both sync (core commands) and async (lazy-loaded) commands
|
|
@@ -319,14 +319,22 @@ const initAction = async (ctx) => {
|
|
|
319
319
|
if (withEmbeddings) {
|
|
320
320
|
output.writeln();
|
|
321
321
|
output.printInfo('Initializing ONNX embedding subsystem...');
|
|
322
|
-
const {
|
|
322
|
+
const { execFileSync: execFileInit } = await import('child_process');
|
|
323
|
+
// Validate embeddingModel: must match pattern org/model-name (CRIT-02)
|
|
324
|
+
if (!/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9._-]+$/.test(embeddingModel)) {
|
|
325
|
+
throw new Error(`Invalid embedding model name: ${embeddingModel}`);
|
|
326
|
+
}
|
|
323
327
|
try {
|
|
324
328
|
output.writeln(output.dim(` Model: ${embeddingModel}`));
|
|
325
329
|
output.writeln(output.dim(' Hyperbolic: Enabled (Poincaré ball)'));
|
|
326
|
-
|
|
330
|
+
execFileInit('npx', [
|
|
331
|
+
'@claude-flow/cli@latest', 'embeddings', 'init',
|
|
332
|
+
'--model', embeddingModel,
|
|
333
|
+
'--no-download', '--force',
|
|
334
|
+
], {
|
|
327
335
|
stdio: 'pipe',
|
|
328
336
|
cwd: ctx.cwd,
|
|
329
|
-
timeout: 30000
|
|
337
|
+
timeout: 30000,
|
|
330
338
|
});
|
|
331
339
|
output.writeln(output.success(' ✓ Embeddings initialized'));
|
|
332
340
|
output.writeln(output.dim(' Run "embeddings init --download" to download model'));
|
|
@@ -546,12 +554,20 @@ const wizardCommand = {
|
|
|
546
554
|
if (enableEmbeddings) {
|
|
547
555
|
output.writeln();
|
|
548
556
|
output.printInfo('Initializing ONNX embedding subsystem...');
|
|
549
|
-
const {
|
|
557
|
+
const { execFileSync } = await import('child_process');
|
|
558
|
+
// Validate embeddingModel: must match pattern org/model-name (CRIT-02)
|
|
559
|
+
if (!/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9._-]+$/.test(embeddingModel)) {
|
|
560
|
+
throw new Error(`Invalid embedding model name: ${embeddingModel}`);
|
|
561
|
+
}
|
|
550
562
|
try {
|
|
551
|
-
|
|
563
|
+
execFileSync('npx', [
|
|
564
|
+
'@claude-flow/cli@latest', 'embeddings', 'init',
|
|
565
|
+
'--model', embeddingModel,
|
|
566
|
+
'--no-download', '--force',
|
|
567
|
+
], {
|
|
552
568
|
stdio: 'pipe',
|
|
553
569
|
cwd: ctx.cwd,
|
|
554
|
-
timeout: 30000
|
|
570
|
+
timeout: 30000,
|
|
555
571
|
});
|
|
556
572
|
output.writeln(output.success(' ✓ Embeddings configured'));
|
|
557
573
|
embeddingsInitialized = true;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../../output.js';
|
|
6
6
|
import { confirm, input } from '../../prompt.js';
|
|
7
|
+
import { validateSchemaName } from './pg-utils.js';
|
|
7
8
|
/**
|
|
8
9
|
* Get PostgreSQL connection config from context
|
|
9
10
|
*/
|
|
@@ -15,7 +16,7 @@ function getConnectionConfig(ctx) {
|
|
|
15
16
|
user: ctx.flags.user || process.env.PGUSER || 'postgres',
|
|
16
17
|
password: ctx.flags.password || process.env.PGPASSWORD || '',
|
|
17
18
|
ssl: ctx.flags.ssl || process.env.PGSSLMODE === 'require',
|
|
18
|
-
schema: ctx.flags.schema || 'claude_flow',
|
|
19
|
+
schema: validateSchemaName(ctx.flags.schema || 'claude_flow'),
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../../output.js';
|
|
6
6
|
import { confirm } from '../../prompt.js';
|
|
7
|
+
import { validateSchemaName } from './pg-utils.js';
|
|
7
8
|
/**
|
|
8
9
|
* Get PostgreSQL connection config from context
|
|
9
10
|
*/
|
|
@@ -15,7 +16,7 @@ function getConnectionConfig(ctx) {
|
|
|
15
16
|
user: ctx.flags.user || process.env.PGUSER || 'postgres',
|
|
16
17
|
password: ctx.flags.password || process.env.PGPASSWORD || '',
|
|
17
18
|
ssl: ctx.flags.ssl || process.env.PGSSLMODE === 'require',
|
|
18
|
-
schema: ctx.flags.schema || 'claude_flow',
|
|
19
|
+
schema: validateSchemaName(ctx.flags.schema || 'claude_flow'),
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
@@ -12,10 +12,18 @@
|
|
|
12
12
|
import { output } from '../../output.js';
|
|
13
13
|
import * as fs from 'fs';
|
|
14
14
|
import * as path from 'path';
|
|
15
|
+
import { validateTimestamp } from './pg-utils.js';
|
|
15
16
|
/**
|
|
16
17
|
* Format a ruvector embedding array for PostgreSQL
|
|
18
|
+
* Validates each element is a finite number to prevent SQL injection via crafted arrays.
|
|
17
19
|
*/
|
|
18
20
|
function formatEmbedding(embedding, dimensions = 384) {
|
|
21
|
+
// Validate every element is a finite number (prevents SQL injection via crafted JSON)
|
|
22
|
+
for (let i = 0; i < embedding.length; i++) {
|
|
23
|
+
if (typeof embedding[i] !== 'number' || !Number.isFinite(embedding[i])) {
|
|
24
|
+
throw new Error(`Invalid embedding value at index ${i}: expected finite number, got ${typeof embedding[i]}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
19
27
|
// Ensure correct dimensions by padding or truncating
|
|
20
28
|
const padded = [...embedding];
|
|
21
29
|
while (padded.length < dimensions) {
|
|
@@ -40,12 +48,19 @@ function generateInsertSQL(entry) {
|
|
|
40
48
|
const value = typeof entry.value === 'string'
|
|
41
49
|
? escapeString(entry.value)
|
|
42
50
|
: escapeString(JSON.stringify(entry.value));
|
|
43
|
-
const namespace = entry.namespace || 'default';
|
|
51
|
+
const namespace = escapeString(entry.namespace || 'default');
|
|
44
52
|
const metadata = entry.metadata ? escapeString(JSON.stringify(entry.metadata)) : '{}';
|
|
45
53
|
let embeddingClause = 'NULL';
|
|
46
54
|
if (entry.embedding && Array.isArray(entry.embedding) && entry.embedding.length > 0) {
|
|
47
55
|
embeddingClause = formatEmbedding(entry.embedding);
|
|
48
56
|
}
|
|
57
|
+
// DA-HIGH-3: Validate timestamps to prevent SQL injection via crafted JSON
|
|
58
|
+
const createdAt = entry.created_at
|
|
59
|
+
? `'${escapeString(validateTimestamp(String(entry.created_at)))}'::timestamptz`
|
|
60
|
+
: 'NOW()';
|
|
61
|
+
const updatedAt = entry.updated_at
|
|
62
|
+
? `'${escapeString(validateTimestamp(String(entry.updated_at)))}'::timestamptz`
|
|
63
|
+
: 'NOW()';
|
|
49
64
|
return `INSERT INTO claude_flow.memory_entries (key, value, embedding, namespace, metadata, created_at, updated_at)
|
|
50
65
|
VALUES (
|
|
51
66
|
'${key}',
|
|
@@ -53,8 +68,8 @@ VALUES (
|
|
|
53
68
|
${embeddingClause},
|
|
54
69
|
'${namespace}',
|
|
55
70
|
'${metadata}'::jsonb,
|
|
56
|
-
${
|
|
57
|
-
${
|
|
71
|
+
${createdAt},
|
|
72
|
+
${updatedAt}
|
|
58
73
|
)
|
|
59
74
|
ON CONFLICT (key, namespace) DO UPDATE SET
|
|
60
75
|
value = EXCLUDED.value,
|
|
@@ -288,12 +303,21 @@ export const importCommand = {
|
|
|
288
303
|
output.writeln(output.dim('Command:'));
|
|
289
304
|
output.writeln(output.dim(` docker exec -i ${containerName} psql -U claude -d claude_flow < ${tempFile}`));
|
|
290
305
|
output.writeln();
|
|
291
|
-
// Execute via child_process
|
|
292
|
-
const {
|
|
306
|
+
// Execute via child_process (CRIT-02: use execFileSync to prevent command injection)
|
|
307
|
+
const { execFileSync } = await import('child_process');
|
|
308
|
+
// Validate containerName: alphanumeric, hyphens, underscores, dots only
|
|
309
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(containerName)) {
|
|
310
|
+
throw new Error(`Invalid container name: ${containerName}`);
|
|
311
|
+
}
|
|
293
312
|
try {
|
|
294
|
-
const
|
|
313
|
+
const sqlContent = fs.readFileSync(tempFile, 'utf-8');
|
|
314
|
+
const result = execFileSync('docker', [
|
|
315
|
+
'exec', '-i', containerName,
|
|
316
|
+
'psql', '-U', 'claude', '-d', 'claude_flow',
|
|
317
|
+
], {
|
|
295
318
|
encoding: 'utf-8',
|
|
296
319
|
timeout: 60000,
|
|
320
|
+
input: sqlContent,
|
|
297
321
|
});
|
|
298
322
|
if (verbose) {
|
|
299
323
|
output.writeln(output.dim(result));
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../../output.js';
|
|
6
6
|
import { confirm, input } from '../../prompt.js';
|
|
7
|
+
import { validateSchemaName } from './pg-utils.js';
|
|
7
8
|
/**
|
|
8
9
|
* Get PostgreSQL connection config from context flags and environment
|
|
9
10
|
*/
|
|
@@ -15,7 +16,7 @@ function getConnectionConfig(ctx) {
|
|
|
15
16
|
user: ctx.flags.user || process.env.PGUSER || 'postgres',
|
|
16
17
|
password: ctx.flags.password || process.env.PGPASSWORD || '',
|
|
17
18
|
ssl: ctx.flags.ssl || process.env.PGSSLMODE === 'require',
|
|
18
|
-
schema: ctx.flags.schema || 'claude_flow',
|
|
19
|
+
schema: validateSchemaName(ctx.flags.schema || 'claude_flow'),
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../../output.js';
|
|
6
6
|
import { confirm } from '../../prompt.js';
|
|
7
|
+
import { validateSchemaName } from './pg-utils.js';
|
|
7
8
|
/**
|
|
8
9
|
* Available migrations
|
|
9
10
|
*/
|
|
@@ -141,7 +142,7 @@ function getConnectionConfig(ctx) {
|
|
|
141
142
|
user: ctx.flags.user || process.env.PGUSER || 'postgres',
|
|
142
143
|
password: ctx.flags.password || process.env.PGPASSWORD || '',
|
|
143
144
|
ssl: ctx.flags.ssl || process.env.PGSSLMODE === 'require',
|
|
144
|
-
schema: ctx.flags.schema || 'claude_flow',
|
|
145
|
+
schema: validateSchemaName(ctx.flags.schema || 'claude_flow'),
|
|
145
146
|
};
|
|
146
147
|
}
|
|
147
148
|
/**
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../../output.js';
|
|
6
6
|
import { confirm } from '../../prompt.js';
|
|
7
|
+
import { validateSchemaName } from './pg-utils.js';
|
|
7
8
|
/**
|
|
8
9
|
* Get PostgreSQL connection config from context
|
|
9
10
|
*/
|
|
@@ -15,7 +16,7 @@ function getConnectionConfig(ctx) {
|
|
|
15
16
|
user: ctx.flags.user || process.env.PGUSER || 'postgres',
|
|
16
17
|
password: ctx.flags.password || process.env.PGPASSWORD || '',
|
|
17
18
|
ssl: ctx.flags.ssl || process.env.PGSSLMODE === 'require',
|
|
18
|
-
schema: ctx.flags.schema || 'claude_flow',
|
|
19
|
+
schema: validateSchemaName(ctx.flags.schema || 'claude_flow'),
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL security utilities for RuVector commands.
|
|
3
|
+
* Prevents SQL injection via identifier interpolation.
|
|
4
|
+
*
|
|
5
|
+
* @module v3/cli/commands/ruvector/pg-utils
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Validate a PostgreSQL schema name.
|
|
9
|
+
* Throws if the name contains characters that could enable SQL injection.
|
|
10
|
+
* Safe names are returned as-is (no quoting needed since they match the identifier pattern).
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateSchemaName(schema: string): string;
|
|
13
|
+
export declare function validateTimestamp(value: string): string;
|
|
14
|
+
//# sourceMappingURL=pg-utils.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL security utilities for RuVector commands.
|
|
3
|
+
* Prevents SQL injection via identifier interpolation.
|
|
4
|
+
*
|
|
5
|
+
* @module v3/cli/commands/ruvector/pg-utils
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Valid PostgreSQL identifier pattern.
|
|
9
|
+
* Allows only ASCII letters, digits, and underscores.
|
|
10
|
+
* Must start with a letter or underscore.
|
|
11
|
+
*/
|
|
12
|
+
const VALID_PG_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
13
|
+
/**
|
|
14
|
+
* Validate a PostgreSQL schema name.
|
|
15
|
+
* Throws if the name contains characters that could enable SQL injection.
|
|
16
|
+
* Safe names are returned as-is (no quoting needed since they match the identifier pattern).
|
|
17
|
+
*/
|
|
18
|
+
export function validateSchemaName(schema) {
|
|
19
|
+
if (!schema || schema.length === 0) {
|
|
20
|
+
throw new Error('Schema name must not be empty');
|
|
21
|
+
}
|
|
22
|
+
if (schema.length > 63) {
|
|
23
|
+
throw new Error(`Schema name too long (${schema.length} chars, max 63): "${schema}"`);
|
|
24
|
+
}
|
|
25
|
+
if (!VALID_PG_IDENTIFIER.test(schema)) {
|
|
26
|
+
throw new Error(`Invalid schema name: "${schema}". Must contain only letters, digits, and underscores, and start with a letter or underscore.`);
|
|
27
|
+
}
|
|
28
|
+
return schema;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate a PostgreSQL timestamp string.
|
|
32
|
+
* Only allows ISO 8601 format to prevent SQL injection via timestamp fields.
|
|
33
|
+
*/
|
|
34
|
+
const VALID_TIMESTAMP = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?$/;
|
|
35
|
+
export function validateTimestamp(value) {
|
|
36
|
+
if (!VALID_TIMESTAMP.test(value)) {
|
|
37
|
+
throw new Error(`Invalid timestamp format: "${value}". Expected ISO 8601.`);
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=pg-utils.js.map
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Check connection and schema status
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../../output.js';
|
|
6
|
+
import { validateSchemaName } from './pg-utils.js';
|
|
6
7
|
/**
|
|
7
8
|
* Get PostgreSQL connection config from context
|
|
8
9
|
*/
|
|
@@ -14,7 +15,7 @@ function getConnectionConfig(ctx) {
|
|
|
14
15
|
user: ctx.flags.user || process.env.PGUSER || 'postgres',
|
|
15
16
|
password: ctx.flags.password || process.env.PGPASSWORD || '',
|
|
16
17
|
ssl: ctx.flags.ssl || process.env.PGSSLMODE === 'require',
|
|
17
|
-
schema: ctx.flags.schema || 'claude_flow',
|
|
18
|
+
schema: validateSchemaName(ctx.flags.schema || 'claude_flow'),
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from 'url';
|
|
|
9
9
|
import { dirname, join } from 'path';
|
|
10
10
|
import { commandParser } from './parser.js';
|
|
11
11
|
import { output } from './output.js';
|
|
12
|
-
import { commands,
|
|
12
|
+
import { commands, getCommandsByCategory, getCommand, getCommandAsync, getCommandNames, hasCommand } from './commands/index.js';
|
|
13
13
|
import { suggestCommand } from './suggest.js';
|
|
14
14
|
import { runStartupUpdateCheck } from './update/index.js';
|
|
15
15
|
// Read version from package.json at runtime
|
|
@@ -109,7 +109,7 @@ export class CLI {
|
|
|
109
109
|
process.exit(1);
|
|
110
110
|
}
|
|
111
111
|
else {
|
|
112
|
-
this.showHelp();
|
|
112
|
+
await this.showHelp();
|
|
113
113
|
}
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
@@ -219,7 +219,7 @@ export class CLI {
|
|
|
219
219
|
/**
|
|
220
220
|
* Show main help
|
|
221
221
|
*/
|
|
222
|
-
showHelp() {
|
|
222
|
+
async showHelp() {
|
|
223
223
|
this.output.writeln();
|
|
224
224
|
this.output.writeln(this.output.bold(`${this.name} v${this.version}`));
|
|
225
225
|
this.output.writeln(this.output.dim(this.description));
|
|
@@ -227,9 +227,11 @@ export class CLI {
|
|
|
227
227
|
this.output.writeln(this.output.bold('USAGE:'));
|
|
228
228
|
this.output.writeln(` ${this.name} <command> [subcommand] [options]`);
|
|
229
229
|
this.output.writeln();
|
|
230
|
+
// PERF-03: Load all commands by category (lazy-loaded on demand)
|
|
231
|
+
const categories = await getCommandsByCategory();
|
|
230
232
|
// Primary Commands
|
|
231
233
|
this.output.writeln(this.output.bold('PRIMARY COMMANDS:'));
|
|
232
|
-
for (const cmd of
|
|
234
|
+
for (const cmd of categories.primary) {
|
|
233
235
|
if (cmd.hidden)
|
|
234
236
|
continue;
|
|
235
237
|
const name = cmd.name.padEnd(12);
|
|
@@ -237,41 +239,49 @@ export class CLI {
|
|
|
237
239
|
}
|
|
238
240
|
this.output.writeln();
|
|
239
241
|
// Advanced Commands
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
if (categories.advanced.length > 0) {
|
|
243
|
+
this.output.writeln(this.output.bold('ADVANCED COMMANDS:'));
|
|
244
|
+
for (const cmd of categories.advanced) {
|
|
245
|
+
if (cmd.hidden)
|
|
246
|
+
continue;
|
|
247
|
+
const name = cmd.name.padEnd(12);
|
|
248
|
+
this.output.writeln(` ${this.output.highlight(name)} ${cmd.description}`);
|
|
249
|
+
}
|
|
250
|
+
this.output.writeln();
|
|
246
251
|
}
|
|
247
|
-
this.output.writeln();
|
|
248
252
|
// Utility Commands
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
253
|
+
if (categories.utility.length > 0) {
|
|
254
|
+
this.output.writeln(this.output.bold('UTILITY COMMANDS:'));
|
|
255
|
+
for (const cmd of categories.utility) {
|
|
256
|
+
if (cmd.hidden)
|
|
257
|
+
continue;
|
|
258
|
+
const name = cmd.name.padEnd(12);
|
|
259
|
+
this.output.writeln(` ${this.output.highlight(name)} ${cmd.description}`);
|
|
260
|
+
}
|
|
261
|
+
this.output.writeln();
|
|
255
262
|
}
|
|
256
|
-
this.output.writeln();
|
|
257
263
|
// Analysis Commands
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
if (categories.analysis.length > 0) {
|
|
265
|
+
this.output.writeln(this.output.bold('ANALYSIS COMMANDS:'));
|
|
266
|
+
for (const cmd of categories.analysis) {
|
|
267
|
+
if (cmd.hidden)
|
|
268
|
+
continue;
|
|
269
|
+
const name = cmd.name.padEnd(12);
|
|
270
|
+
this.output.writeln(` ${this.output.highlight(name)} ${cmd.description}`);
|
|
271
|
+
}
|
|
272
|
+
this.output.writeln();
|
|
264
273
|
}
|
|
265
|
-
this.output.writeln();
|
|
266
274
|
// Management Commands
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
275
|
+
if (categories.management.length > 0) {
|
|
276
|
+
this.output.writeln(this.output.bold('MANAGEMENT COMMANDS:'));
|
|
277
|
+
for (const cmd of categories.management) {
|
|
278
|
+
if (cmd.hidden)
|
|
279
|
+
continue;
|
|
280
|
+
const name = cmd.name.padEnd(12);
|
|
281
|
+
this.output.writeln(` ${this.output.highlight(name)} ${cmd.description}`);
|
|
282
|
+
}
|
|
283
|
+
this.output.writeln();
|
|
273
284
|
}
|
|
274
|
-
this.output.writeln();
|
|
275
285
|
this.output.writeln(this.output.bold('GLOBAL OPTIONS:'));
|
|
276
286
|
for (const opt of this.parser.getGlobalOptions()) {
|
|
277
287
|
const flags = opt.short ? `-${opt.short}, --${opt.name}` : ` --${opt.name}`;
|
|
@@ -570,12 +570,30 @@ export class MCPServerManager extends EventEmitter {
|
|
|
570
570
|
return false;
|
|
571
571
|
}
|
|
572
572
|
// Verify it's actually a node process (guards against PID reuse)
|
|
573
|
+
// DA-CRIT-3: Use execFileSync to prevent command injection via PID values
|
|
573
574
|
try {
|
|
574
|
-
const {
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
575
|
+
const { execFileSync } = require('child_process');
|
|
576
|
+
const safePid = String(Math.floor(Math.abs(pid)));
|
|
577
|
+
let cmdline = '';
|
|
578
|
+
try {
|
|
579
|
+
// Try /proc on Linux
|
|
580
|
+
const { readFileSync } = require('fs');
|
|
581
|
+
cmdline = readFileSync(`/proc/${safePid}/cmdline`, 'utf8');
|
|
582
|
+
}
|
|
583
|
+
catch {
|
|
584
|
+
// Fall back to ps on macOS/other
|
|
585
|
+
try {
|
|
586
|
+
cmdline = execFileSync('ps', ['-p', safePid, '-o', 'comm='], {
|
|
587
|
+
encoding: 'utf8',
|
|
588
|
+
timeout: 1000,
|
|
589
|
+
}).trim();
|
|
590
|
+
}
|
|
591
|
+
catch {
|
|
592
|
+
// ps failed — fall through
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (!cmdline)
|
|
596
|
+
return true; // Can't inspect, fall back to kill check
|
|
579
597
|
// Must be a node process to be our MCP server
|
|
580
598
|
return cmdline.includes('node') || cmdline.includes('claude-flow') || cmdline.includes('npx');
|
|
581
599
|
}
|
|
@@ -238,6 +238,8 @@ export class PluginManager {
|
|
|
238
238
|
if (!plugin) {
|
|
239
239
|
return { success: false, error: `Plugin ${packageName} is not installed` };
|
|
240
240
|
}
|
|
241
|
+
// HIGH-04: Warn about unsandboxed plugin execution
|
|
242
|
+
console.warn(`[SECURITY] Plugin loaded without sandboxing: ${packageName}. Plugins run with full process access.`);
|
|
241
243
|
plugin.enabled = true;
|
|
242
244
|
await this.saveManifest();
|
|
243
245
|
return { success: true };
|
|
@@ -394,7 +394,10 @@ export class LoRAAdapter {
|
|
|
394
394
|
if (pipeline) {
|
|
395
395
|
try {
|
|
396
396
|
pipeline.saveCheckpoint(path);
|
|
397
|
-
|
|
397
|
+
// Verify ruvllm actually wrote the file (some versions are no-op)
|
|
398
|
+
const fs = await import('fs');
|
|
399
|
+
if (fs.existsSync(path))
|
|
400
|
+
return true;
|
|
398
401
|
}
|
|
399
402
|
catch { /* fall through to JS fallback */ }
|
|
400
403
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.76",
|
|
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",
|