claude-flow 3.5.69 → 3.5.71
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/autopilot.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +4 -7
- package/v3/@claude-flow/cli/dist/src/commands/init.js +0 -1
- package/v3/@claude-flow/cli/dist/src/commands/neural.js +1 -0
- package/v3/@claude-flow/cli/dist/src/commands/providers.js +228 -96
- package/v3/@claude-flow/cli/dist/src/commands/security.js +1 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +35 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +81 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/analyze-tools.js +29 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/autopilot-tools.js +4 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.js +146 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/claims-tools.js +116 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/config-tools.js +53 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/coordination-tools.js +31 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/daa-tools.js +61 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/embeddings-tools.js +26 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/github-tools.js +96 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/guidance-tools.js +21 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hive-mind-tools.js +56 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +176 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +18 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +51 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/performance-tools.js +11 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/ruvllm-tools.js +31 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/security-tools.js +36 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/session-tools.js +29 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/swarm-tools.js +30 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/system-tools.js +6 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/task-tools.js +33 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/terminal-tools.js +31 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/transfer-tools.js +51 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/wasm-agent-tools.js +61 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/workflow-tools.js +82 -0
- package/v3/@claude-flow/cli/dist/src/memory/intelligence.d.ts +6 -1
- package/v3/@claude-flow/cli/dist/src/memory/intelligence.js +51 -1
- package/v3/@claude-flow/cli/package.json +1 -1
- package/v3/@claude-flow/guidance/dist/adversarial.d.ts +284 -0
- package/v3/@claude-flow/guidance/dist/adversarial.js +572 -0
- package/v3/@claude-flow/guidance/dist/analyzer.d.ts +530 -0
- package/v3/@claude-flow/guidance/dist/analyzer.js +2518 -0
- package/v3/@claude-flow/guidance/dist/artifacts.d.ts +283 -0
- package/v3/@claude-flow/guidance/dist/artifacts.js +356 -0
- package/v3/@claude-flow/guidance/dist/authority.d.ts +290 -0
- package/v3/@claude-flow/guidance/dist/authority.js +558 -0
- package/v3/@claude-flow/guidance/dist/capabilities.d.ts +209 -0
- package/v3/@claude-flow/guidance/dist/capabilities.js +485 -0
- package/v3/@claude-flow/guidance/dist/coherence.d.ts +233 -0
- package/v3/@claude-flow/guidance/dist/coherence.js +372 -0
- package/v3/@claude-flow/guidance/dist/compiler.d.ts +87 -0
- package/v3/@claude-flow/guidance/dist/compiler.js +419 -0
- package/v3/@claude-flow/guidance/dist/conformance-kit.d.ts +225 -0
- package/v3/@claude-flow/guidance/dist/conformance-kit.js +629 -0
- package/v3/@claude-flow/guidance/dist/continue-gate.d.ts +214 -0
- package/v3/@claude-flow/guidance/dist/continue-gate.js +353 -0
- package/v3/@claude-flow/guidance/dist/crypto-utils.d.ts +17 -0
- package/v3/@claude-flow/guidance/dist/crypto-utils.js +24 -0
- package/v3/@claude-flow/guidance/dist/evolution.d.ts +282 -0
- package/v3/@claude-flow/guidance/dist/evolution.js +500 -0
- package/v3/@claude-flow/guidance/dist/gates.d.ts +79 -0
- package/v3/@claude-flow/guidance/dist/gates.js +302 -0
- package/v3/@claude-flow/guidance/dist/gateway.d.ts +206 -0
- package/v3/@claude-flow/guidance/dist/gateway.js +452 -0
- package/v3/@claude-flow/guidance/dist/generators.d.ts +153 -0
- package/v3/@claude-flow/guidance/dist/generators.js +682 -0
- package/v3/@claude-flow/guidance/dist/headless.d.ts +177 -0
- package/v3/@claude-flow/guidance/dist/headless.js +342 -0
- package/v3/@claude-flow/guidance/dist/hooks.d.ts +109 -0
- package/v3/@claude-flow/guidance/dist/hooks.js +347 -0
- package/v3/@claude-flow/guidance/dist/index.d.ts +205 -0
- package/v3/@claude-flow/guidance/dist/index.js +321 -0
- package/v3/@claude-flow/guidance/dist/ledger.d.ts +162 -0
- package/v3/@claude-flow/guidance/dist/ledger.js +375 -0
- package/v3/@claude-flow/guidance/dist/manifest-validator.d.ts +289 -0
- package/v3/@claude-flow/guidance/dist/manifest-validator.js +838 -0
- package/v3/@claude-flow/guidance/dist/memory-gate.d.ts +222 -0
- package/v3/@claude-flow/guidance/dist/memory-gate.js +382 -0
- package/v3/@claude-flow/guidance/dist/meta-governance.d.ts +265 -0
- package/v3/@claude-flow/guidance/dist/meta-governance.js +348 -0
- package/v3/@claude-flow/guidance/dist/optimizer.d.ts +104 -0
- package/v3/@claude-flow/guidance/dist/optimizer.js +329 -0
- package/v3/@claude-flow/guidance/dist/persistence.d.ts +189 -0
- package/v3/@claude-flow/guidance/dist/persistence.js +464 -0
- package/v3/@claude-flow/guidance/dist/proof.d.ts +185 -0
- package/v3/@claude-flow/guidance/dist/proof.js +238 -0
- package/v3/@claude-flow/guidance/dist/retriever.d.ts +116 -0
- package/v3/@claude-flow/guidance/dist/retriever.js +394 -0
- package/v3/@claude-flow/guidance/dist/ruvbot-integration.d.ts +370 -0
- package/v3/@claude-flow/guidance/dist/ruvbot-integration.js +738 -0
- package/v3/@claude-flow/guidance/dist/temporal.d.ts +426 -0
- package/v3/@claude-flow/guidance/dist/temporal.js +658 -0
- package/v3/@claude-flow/guidance/dist/trust.d.ts +283 -0
- package/v3/@claude-flow/guidance/dist/trust.js +473 -0
- package/v3/@claude-flow/guidance/dist/truth-anchors.d.ts +276 -0
- package/v3/@claude-flow/guidance/dist/truth-anchors.js +488 -0
- package/v3/@claude-flow/guidance/dist/types.d.ts +378 -0
- package/v3/@claude-flow/guidance/dist/types.js +10 -0
- package/v3/@claude-flow/guidance/dist/uncertainty.d.ts +372 -0
- package/v3/@claude-flow/guidance/dist/uncertainty.js +619 -0
- package/v3/@claude-flow/guidance/dist/wasm-kernel.d.ts +48 -0
- package/v3/@claude-flow/guidance/dist/wasm-kernel.js +158 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.71",
|
|
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",
|
|
@@ -287,7 +287,7 @@ const predictCommand = {
|
|
|
287
287
|
else {
|
|
288
288
|
output.writeln(`Action: ${prediction?.action || 'unknown'}`);
|
|
289
289
|
output.writeln(`Confidence: ${prediction?.confidence || 0}`);
|
|
290
|
-
if (prediction?.alternatives
|
|
290
|
+
if (prediction?.alternatives && prediction.alternatives.length > 0)
|
|
291
291
|
output.writeln(`Alternatives: ${prediction.alternatives.join(', ')}`);
|
|
292
292
|
}
|
|
293
293
|
return { success: true };
|
|
@@ -3763,7 +3763,7 @@ const postBashCommand = {
|
|
|
3763
3763
|
// Token Optimizer command - integrates agentic-flow Agent Booster
|
|
3764
3764
|
const tokenOptimizeCommand = {
|
|
3765
3765
|
name: 'token-optimize',
|
|
3766
|
-
description: 'Token optimization via agentic-flow Agent Booster
|
|
3766
|
+
description: 'Token optimization via agentic-flow Agent Booster integration',
|
|
3767
3767
|
options: [
|
|
3768
3768
|
{ name: 'query', short: 'q', type: 'string', description: 'Query for compact context retrieval' },
|
|
3769
3769
|
{ name: 'agents', short: 'A', type: 'number', description: 'Agent count for optimal config', default: '6' },
|
|
@@ -3791,7 +3791,6 @@ const tokenOptimizeCommand = {
|
|
|
3791
3791
|
memoriesRetrieved: 0,
|
|
3792
3792
|
};
|
|
3793
3793
|
let agenticFlowAvailable = false;
|
|
3794
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3795
3794
|
let reasoningBank = null;
|
|
3796
3795
|
try {
|
|
3797
3796
|
// Check if agentic-flow v3 is available
|
|
@@ -3846,10 +3845,8 @@ const tokenOptimizeCommand = {
|
|
|
3846
3845
|
output.writeln();
|
|
3847
3846
|
output.printInfo('ReasoningBank not available - query skipped');
|
|
3848
3847
|
}
|
|
3849
|
-
//
|
|
3850
|
-
|
|
3851
|
-
stats.cacheHits = 2;
|
|
3852
|
-
stats.cacheMisses = 1;
|
|
3848
|
+
// Note: stats reflect only actual measured values from this session.
|
|
3849
|
+
// No simulated/fabricated data is added.
|
|
3853
3850
|
// Show stats
|
|
3854
3851
|
if (showStats || showReport) {
|
|
3855
3852
|
output.writeln();
|
|
@@ -4413,7 +4410,7 @@ export const hooksCommand = {
|
|
|
4413
4410
|
`${output.highlight('coverage-route')} - Route tasks based on coverage gaps (ruvector)`,
|
|
4414
4411
|
`${output.highlight('coverage-suggest')}- Suggest coverage improvements`,
|
|
4415
4412
|
`${output.highlight('coverage-gaps')} - List all coverage gaps with agents`,
|
|
4416
|
-
`${output.highlight('token-optimize')} - Token optimization (
|
|
4413
|
+
`${output.highlight('token-optimize')} - Token optimization (agentic-flow integration)`,
|
|
4417
4414
|
`${output.highlight('model-route')} - Route to optimal model (haiku/sonnet/opus)`,
|
|
4418
4415
|
`${output.highlight('model-outcome')} - Record model routing outcome`,
|
|
4419
4416
|
`${output.highlight('model-stats')} - View model routing statistics`,
|
|
@@ -18,7 +18,6 @@ async function initCodexAction(ctx, options) {
|
|
|
18
18
|
const spinner = output.createSpinner({ text: 'Initializing Codex project...' });
|
|
19
19
|
spinner.start();
|
|
20
20
|
try {
|
|
21
|
-
// Dynamic import of the Codex initializer with lazy loading fallback
|
|
22
21
|
let CodexInitializer;
|
|
23
22
|
// Try multiple resolution strategies for the @claude-flow/codex package
|
|
24
23
|
// Use a variable to prevent TypeScript from statically resolving the optional module
|
|
@@ -1333,6 +1333,7 @@ const benchmarkCommand = {
|
|
|
1333
1333
|
const spinner = output.createSpinner({ text: 'Running benchmarks...', spinner: 'dots' });
|
|
1334
1334
|
spinner.start();
|
|
1335
1335
|
try {
|
|
1336
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic import of optional native WASM module with no type declarations
|
|
1336
1337
|
const attention = await import('@ruvector/attention');
|
|
1337
1338
|
// Manual benchmark since benchmarkAttention has a binding bug
|
|
1338
1339
|
const benchmarkMechanism = async (name, mechanism) => {
|
|
@@ -6,6 +6,91 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { output } from '../output.js';
|
|
8
8
|
import { configManager } from '../services/config-file-manager.js';
|
|
9
|
+
const PROVIDER_CATALOG = [
|
|
10
|
+
{ name: 'Anthropic', type: 'LLM', models: 'claude-3.5-sonnet, opus', envVar: 'ANTHROPIC_API_KEY', configName: 'anthropic' },
|
|
11
|
+
{ name: 'OpenAI', type: 'LLM', models: 'gpt-4o, gpt-4-turbo', envVar: 'OPENAI_API_KEY', configName: 'openai' },
|
|
12
|
+
{ name: 'OpenAI', type: 'Embedding', models: 'text-embedding-3-small/large', envVar: 'OPENAI_API_KEY', configName: 'openai' },
|
|
13
|
+
{ name: 'Google', type: 'LLM', models: 'gemini-pro, gemini-ultra', envVar: 'GOOGLE_API_KEY', configName: 'google' },
|
|
14
|
+
{ name: 'Transformers.js', type: 'Embedding', models: 'Xenova/all-MiniLM-L6-v2' },
|
|
15
|
+
{ name: 'Agentic Flow', type: 'Embedding', models: 'ONNX optimized' },
|
|
16
|
+
{ name: 'Mock', type: 'All', models: 'mock-*' },
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the API key for a provider by checking the config file first,
|
|
20
|
+
* then falling back to well-known environment variables.
|
|
21
|
+
*/
|
|
22
|
+
function resolveApiKey(providerName, configuredProviders) {
|
|
23
|
+
// Check config file entry
|
|
24
|
+
const entry = configuredProviders.find((p) => typeof p.name === 'string' && p.name.toLowerCase() === providerName.toLowerCase());
|
|
25
|
+
if (entry?.apiKey && typeof entry.apiKey === 'string') {
|
|
26
|
+
return entry.apiKey;
|
|
27
|
+
}
|
|
28
|
+
// Check environment variable
|
|
29
|
+
const envMapping = {
|
|
30
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
31
|
+
openai: 'OPENAI_API_KEY',
|
|
32
|
+
google: 'GOOGLE_API_KEY',
|
|
33
|
+
};
|
|
34
|
+
const envVar = envMapping[providerName.toLowerCase()];
|
|
35
|
+
if (envVar && process.env[envVar]) {
|
|
36
|
+
return process.env[envVar];
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Make a lightweight HTTP request to verify provider API key validity.
|
|
42
|
+
* Uses a 5-second timeout. Returns { ok, reason }.
|
|
43
|
+
*/
|
|
44
|
+
async function testProviderConnectivity(providerName, apiKey) {
|
|
45
|
+
const endpoints = {
|
|
46
|
+
anthropic: {
|
|
47
|
+
url: 'https://api.anthropic.com/v1/models',
|
|
48
|
+
headers: {
|
|
49
|
+
'x-api-key': apiKey,
|
|
50
|
+
'anthropic-version': '2023-06-01',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
openai: {
|
|
54
|
+
url: 'https://api.openai.com/v1/models',
|
|
55
|
+
headers: {
|
|
56
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
google: {
|
|
60
|
+
url: `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`,
|
|
61
|
+
headers: {},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
const endpointConfig = endpoints[providerName.toLowerCase()];
|
|
65
|
+
if (!endpointConfig) {
|
|
66
|
+
return { ok: false, reason: 'No test endpoint available for this provider' };
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
71
|
+
const res = await fetch(endpointConfig.url, {
|
|
72
|
+
method: 'GET',
|
|
73
|
+
headers: endpointConfig.headers,
|
|
74
|
+
signal: controller.signal,
|
|
75
|
+
});
|
|
76
|
+
clearTimeout(timeout);
|
|
77
|
+
if (res.ok || res.status === 200) {
|
|
78
|
+
return { ok: true, reason: 'Connected successfully' };
|
|
79
|
+
}
|
|
80
|
+
if (res.status === 401 || res.status === 403) {
|
|
81
|
+
return { ok: false, reason: `Authentication failed (HTTP ${res.status})` };
|
|
82
|
+
}
|
|
83
|
+
// A non-auth error but the server responded — key format may be fine
|
|
84
|
+
return { ok: false, reason: `Unexpected response (HTTP ${res.status})` };
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
88
|
+
return { ok: false, reason: 'Connection timed out (5s)' };
|
|
89
|
+
}
|
|
90
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
91
|
+
return { ok: false, reason: `Connection failed: ${msg}` };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
9
94
|
// List subcommand
|
|
10
95
|
const listCommand = {
|
|
11
96
|
name: 'list',
|
|
@@ -20,26 +105,88 @@ const listCommand = {
|
|
|
20
105
|
],
|
|
21
106
|
action: async (ctx) => {
|
|
22
107
|
const type = ctx.flags.type || 'all';
|
|
23
|
-
|
|
108
|
+
const activeOnly = ctx.flags.active;
|
|
109
|
+
// Load user configuration
|
|
110
|
+
const cwd = process.cwd();
|
|
111
|
+
const config = configManager.getConfig(cwd);
|
|
112
|
+
const agents = (config.agents ?? {});
|
|
113
|
+
const configuredProviders = (agents.providers ?? []);
|
|
114
|
+
// Build table rows from the catalog, enriched with configuration status
|
|
115
|
+
const rows = [];
|
|
116
|
+
for (const entry of PROVIDER_CATALOG) {
|
|
117
|
+
// Apply type filter
|
|
118
|
+
if (type !== 'all' && entry.type.toLowerCase() !== type.toLowerCase()) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
let status;
|
|
122
|
+
let keySource = '';
|
|
123
|
+
if (entry.configName) {
|
|
124
|
+
const apiKey = resolveApiKey(entry.configName, configuredProviders);
|
|
125
|
+
if (apiKey) {
|
|
126
|
+
// Determine the source for the key
|
|
127
|
+
const configEntry = configuredProviders.find((p) => typeof p.name === 'string' && p.name.toLowerCase() === entry.configName.toLowerCase());
|
|
128
|
+
if (configEntry?.apiKey && typeof configEntry.apiKey === 'string') {
|
|
129
|
+
keySource = 'config';
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
keySource = 'env';
|
|
133
|
+
}
|
|
134
|
+
status = output.success(`Configured (${keySource})`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
status = output.warning('Not configured');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (entry.name === 'Mock') {
|
|
141
|
+
status = output.dim('Dev only');
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// Local-only providers (Transformers.js, Agentic Flow) — always available
|
|
145
|
+
status = output.success('Available (local)');
|
|
146
|
+
}
|
|
147
|
+
if (activeOnly && !status.includes('Configured') && !status.includes('Available')) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
rows.push({
|
|
151
|
+
provider: entry.name,
|
|
152
|
+
type: entry.type,
|
|
153
|
+
models: entry.models,
|
|
154
|
+
status,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// Also show any providers in config that are not in the static catalog
|
|
158
|
+
for (const cp of configuredProviders) {
|
|
159
|
+
const cpName = cp.name || '';
|
|
160
|
+
const alreadyListed = PROVIDER_CATALOG.some((e) => e.configName?.toLowerCase() === cpName.toLowerCase() || e.name.toLowerCase() === cpName.toLowerCase());
|
|
161
|
+
if (!alreadyListed && cpName) {
|
|
162
|
+
const hasKey = !!(cp.apiKey || resolveApiKey(cpName, configuredProviders));
|
|
163
|
+
rows.push({
|
|
164
|
+
provider: cpName,
|
|
165
|
+
type: cp.type || 'Custom',
|
|
166
|
+
models: cp.model || output.dim('(not specified)'),
|
|
167
|
+
status: hasKey ? output.success('Configured (config)') : output.warning('Not configured'),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
24
171
|
output.writeln();
|
|
25
|
-
output.writeln(output.bold('
|
|
172
|
+
output.writeln(output.bold('Providers'));
|
|
26
173
|
output.writeln(output.dim('─'.repeat(60)));
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
174
|
+
if (rows.length === 0) {
|
|
175
|
+
output.writeln(output.dim(' No providers match the current filter.'));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
output.printTable({
|
|
179
|
+
columns: [
|
|
180
|
+
{ key: 'provider', header: 'Provider', width: 18 },
|
|
181
|
+
{ key: 'type', header: 'Type', width: 12 },
|
|
182
|
+
{ key: 'models', header: 'Models', width: 25 },
|
|
183
|
+
{ key: 'status', header: 'Status', width: 20 },
|
|
184
|
+
],
|
|
185
|
+
data: rows,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
output.writeln();
|
|
189
|
+
output.writeln(output.dim('Tip: Use "providers configure -p <name> -k <key>" to set API keys.'));
|
|
43
190
|
return { success: true };
|
|
44
191
|
},
|
|
45
192
|
};
|
|
@@ -133,98 +280,83 @@ const testCommand = {
|
|
|
133
280
|
const config = configManager.getConfig(cwd);
|
|
134
281
|
const agents = (config.agents ?? {});
|
|
135
282
|
const configuredProviders = (agents.providers ?? []);
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const knownChecks = [
|
|
141
|
-
{
|
|
142
|
-
name: 'Anthropic',
|
|
143
|
-
test: async () => {
|
|
144
|
-
const key = process.env.ANTHROPIC_API_KEY || getConfigApiKey('anthropic');
|
|
145
|
-
if (key)
|
|
146
|
-
return { pass: true, reason: 'API key found' };
|
|
147
|
-
return { pass: false, reason: 'ANTHROPIC_API_KEY not set and no apiKey in config' };
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
name: 'OpenAI',
|
|
152
|
-
test: async () => {
|
|
153
|
-
const key = process.env.OPENAI_API_KEY || getConfigApiKey('openai');
|
|
154
|
-
if (key)
|
|
155
|
-
return { pass: true, reason: 'API key found' };
|
|
156
|
-
return { pass: false, reason: 'OPENAI_API_KEY not set and no apiKey in config' };
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
name: 'Google',
|
|
161
|
-
test: async () => {
|
|
162
|
-
const key = process.env.GOOGLE_API_KEY || getConfigApiKey('google');
|
|
163
|
-
if (key)
|
|
164
|
-
return { pass: true, reason: 'API key found' };
|
|
165
|
-
return { pass: false, reason: 'GOOGLE_API_KEY not set and no apiKey in config' };
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
name: 'Ollama',
|
|
170
|
-
test: async () => {
|
|
171
|
-
const entry = configuredProviders.find((p) => typeof p.name === 'string' && p.name.toLowerCase() === 'ollama');
|
|
172
|
-
const baseUrl = entry?.baseUrl || 'http://localhost:11434';
|
|
173
|
-
try {
|
|
174
|
-
const controller = new AbortController();
|
|
175
|
-
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
176
|
-
const res = await fetch(baseUrl, { signal: controller.signal });
|
|
177
|
-
clearTimeout(timeout);
|
|
178
|
-
if (res.ok)
|
|
179
|
-
return { pass: true, reason: `Reachable at ${baseUrl}` };
|
|
180
|
-
return { pass: false, reason: `HTTP ${res.status} from ${baseUrl}` };
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
return { pass: false, reason: `Unreachable at ${baseUrl}` };
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
},
|
|
283
|
+
const knownTargets = [
|
|
284
|
+
{ name: 'Anthropic', configName: 'anthropic' },
|
|
285
|
+
{ name: 'OpenAI', configName: 'openai' },
|
|
286
|
+
{ name: 'Google', configName: 'google' },
|
|
187
287
|
];
|
|
188
|
-
//
|
|
189
|
-
|
|
288
|
+
// Add Ollama as a special case (endpoint-based, no API key)
|
|
289
|
+
const ollamaEntry = configuredProviders.find((p) => typeof p.name === 'string' && p.name.toLowerCase() === 'ollama');
|
|
290
|
+
let targets;
|
|
190
291
|
if (testAll || !provider) {
|
|
191
|
-
|
|
292
|
+
targets = [...knownTargets];
|
|
192
293
|
}
|
|
193
294
|
else {
|
|
194
|
-
const match =
|
|
195
|
-
|
|
196
|
-
|
|
295
|
+
const match = knownTargets.find((t) => t.name.toLowerCase() === provider.toLowerCase() || t.configName === provider.toLowerCase());
|
|
296
|
+
targets = match ? [match] : [{ name: provider, configName: provider.toLowerCase() }];
|
|
297
|
+
}
|
|
298
|
+
const results = [];
|
|
299
|
+
// Test API-key-based providers with real connectivity checks
|
|
300
|
+
for (const target of targets) {
|
|
301
|
+
const apiKey = resolveApiKey(target.configName, configuredProviders);
|
|
302
|
+
if (!apiKey) {
|
|
303
|
+
results.push({ name: target.name, pass: false, reason: 'Not configured (no API key found)' });
|
|
304
|
+
continue;
|
|
197
305
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
306
|
+
output.writeln(output.dim(` Testing ${target.name}...`));
|
|
307
|
+
const result = await testProviderConnectivity(target.name, apiKey);
|
|
308
|
+
results.push({ name: target.name, pass: result.ok, reason: result.reason });
|
|
309
|
+
}
|
|
310
|
+
// Test Ollama separately (endpoint-based, no API key needed)
|
|
311
|
+
if (testAll || !provider || provider.toLowerCase() === 'ollama') {
|
|
312
|
+
const baseUrl = ollamaEntry?.baseUrl || 'http://localhost:11434';
|
|
313
|
+
output.writeln(output.dim(` Testing Ollama at ${baseUrl}...`));
|
|
314
|
+
try {
|
|
315
|
+
const controller = new AbortController();
|
|
316
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
317
|
+
const res = await fetch(baseUrl, { signal: controller.signal });
|
|
318
|
+
clearTimeout(timeout);
|
|
319
|
+
if (res.ok) {
|
|
320
|
+
results.push({ name: 'Ollama', pass: true, reason: `Connected at ${baseUrl}` });
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
results.push({ name: 'Ollama', pass: false, reason: `HTTP ${res.status} from ${baseUrl}` });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
results.push({ name: 'Ollama', pass: false, reason: `Unreachable at ${baseUrl}` });
|
|
211
328
|
}
|
|
212
329
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
330
|
+
// Also test any custom providers from config that were not in the known list
|
|
331
|
+
if (testAll || !provider) {
|
|
332
|
+
for (const cp of configuredProviders) {
|
|
333
|
+
const cpName = cp.name || '';
|
|
334
|
+
const alreadyTested = results.some((r) => r.name.toLowerCase() === cpName.toLowerCase());
|
|
335
|
+
if (alreadyTested || !cpName)
|
|
336
|
+
continue;
|
|
337
|
+
const apiKey = resolveApiKey(cpName, configuredProviders);
|
|
338
|
+
if (!apiKey) {
|
|
339
|
+
results.push({ name: cpName, pass: false, reason: 'No API key found' });
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
// For custom providers we can only verify the key exists
|
|
343
|
+
results.push({ name: cpName, pass: true, reason: 'API key found (no test endpoint available)' });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
220
346
|
}
|
|
347
|
+
let anyPassed = false;
|
|
221
348
|
output.writeln();
|
|
222
349
|
for (const r of results) {
|
|
223
350
|
const icon = r.pass ? output.success('PASS') : output.error('FAIL');
|
|
224
351
|
output.writeln(` ${icon} ${r.name}: ${r.reason}`);
|
|
352
|
+
if (r.pass)
|
|
353
|
+
anyPassed = true;
|
|
225
354
|
}
|
|
226
355
|
output.writeln();
|
|
227
|
-
if (
|
|
356
|
+
if (results.length === 0) {
|
|
357
|
+
output.writeln(output.warning('No providers to test. Use "providers configure" to add providers.'));
|
|
358
|
+
}
|
|
359
|
+
else if (anyPassed) {
|
|
228
360
|
output.writeln(output.success(`${results.filter((r) => r.pass).length}/${results.length} provider(s) passed.`));
|
|
229
361
|
}
|
|
230
362
|
else {
|
|
@@ -53,7 +53,7 @@ const scanCommand = {
|
|
|
53
53
|
}
|
|
54
54
|
catch (auditErr) {
|
|
55
55
|
// npm audit exits non-zero when vulnerabilities found — stdout still has JSON
|
|
56
|
-
auditResult = auditErr.stdout || '{}';
|
|
56
|
+
auditResult = (auditErr instanceof Error && 'stdout' in auditErr ? auditErr.stdout : undefined) || '{}';
|
|
57
57
|
}
|
|
58
58
|
try {
|
|
59
59
|
const audit = JSON.parse(auditResult);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { getProjectCwd } from './types.js';
|
|
10
|
-
import { validateAgentSpawn } from './validate-input.js';
|
|
10
|
+
import { validateIdentifier, validateAgentSpawn } from './validate-input.js';
|
|
11
11
|
// Storage paths
|
|
12
12
|
const STORAGE_DIR = '.claude-flow';
|
|
13
13
|
const AGENT_DIR = 'agents';
|
|
@@ -221,6 +221,9 @@ export const agentTools = [
|
|
|
221
221
|
required: ['agentId'],
|
|
222
222
|
},
|
|
223
223
|
handler: async (input) => {
|
|
224
|
+
const v = validateIdentifier(input.agentId, 'agentId');
|
|
225
|
+
if (!v.valid)
|
|
226
|
+
return { success: false, error: `Input validation failed: ${v.error}` };
|
|
224
227
|
const store = loadAgentStore();
|
|
225
228
|
const agentId = input.agentId;
|
|
226
229
|
if (store.agents[agentId]) {
|
|
@@ -252,6 +255,9 @@ export const agentTools = [
|
|
|
252
255
|
required: ['agentId'],
|
|
253
256
|
},
|
|
254
257
|
handler: async (input) => {
|
|
258
|
+
const v = validateIdentifier(input.agentId, 'agentId');
|
|
259
|
+
if (!v.valid)
|
|
260
|
+
return { agentId: input.agentId, status: 'not_found', error: `Input validation failed: ${v.error}` };
|
|
255
261
|
const store = loadAgentStore();
|
|
256
262
|
const agentId = input.agentId;
|
|
257
263
|
const agent = store.agents[agentId];
|
|
@@ -287,6 +293,16 @@ export const agentTools = [
|
|
|
287
293
|
},
|
|
288
294
|
},
|
|
289
295
|
handler: async (input) => {
|
|
296
|
+
if (input.status) {
|
|
297
|
+
const v = validateIdentifier(input.status, 'status');
|
|
298
|
+
if (!v.valid)
|
|
299
|
+
return { agents: [], total: 0, error: `Input validation failed: ${v.error}` };
|
|
300
|
+
}
|
|
301
|
+
if (input.domain) {
|
|
302
|
+
const v = validateIdentifier(input.domain, 'domain');
|
|
303
|
+
if (!v.valid)
|
|
304
|
+
return { agents: [], total: 0, error: `Input validation failed: ${v.error}` };
|
|
305
|
+
}
|
|
290
306
|
const store = loadAgentStore();
|
|
291
307
|
let agents = Object.values(store.agents);
|
|
292
308
|
// Filter by status
|
|
@@ -333,6 +349,11 @@ export const agentTools = [
|
|
|
333
349
|
required: ['action'],
|
|
334
350
|
},
|
|
335
351
|
handler: async (input) => {
|
|
352
|
+
if (input.agentType) {
|
|
353
|
+
const v = validateIdentifier(input.agentType, 'agentType');
|
|
354
|
+
if (!v.valid)
|
|
355
|
+
return { action: input.action, error: `Input validation failed: ${v.error}` };
|
|
356
|
+
}
|
|
336
357
|
const store = loadAgentStore();
|
|
337
358
|
const agents = Object.values(store.agents).filter(a => a.status !== 'terminated');
|
|
338
359
|
const action = input.action || 'status'; // Default to status
|
|
@@ -443,6 +464,11 @@ export const agentTools = [
|
|
|
443
464
|
},
|
|
444
465
|
},
|
|
445
466
|
handler: async (input) => {
|
|
467
|
+
if (input.agentId) {
|
|
468
|
+
const v = validateIdentifier(input.agentId, 'agentId');
|
|
469
|
+
if (!v.valid)
|
|
470
|
+
return { agentId: input.agentId, error: `Input validation failed: ${v.error}` };
|
|
471
|
+
}
|
|
446
472
|
const store = loadAgentStore();
|
|
447
473
|
const agents = Object.values(store.agents).filter(a => a.status !== 'terminated');
|
|
448
474
|
const threshold = input.threshold || 0.5;
|
|
@@ -517,6 +543,14 @@ export const agentTools = [
|
|
|
517
543
|
required: ['agentId'],
|
|
518
544
|
},
|
|
519
545
|
handler: async (input) => {
|
|
546
|
+
const v = validateIdentifier(input.agentId, 'agentId');
|
|
547
|
+
if (!v.valid)
|
|
548
|
+
return { success: false, agentId: input.agentId, error: `Input validation failed: ${v.error}` };
|
|
549
|
+
if (input.status) {
|
|
550
|
+
const vs = validateIdentifier(input.status, 'status');
|
|
551
|
+
if (!vs.valid)
|
|
552
|
+
return { success: false, agentId: input.agentId, error: `Input validation failed: ${vs.error}` };
|
|
553
|
+
}
|
|
520
554
|
const store = loadAgentStore();
|
|
521
555
|
const agentId = input.agentId;
|
|
522
556
|
const agent = store.agents[agentId];
|