claude-flow 2.5.0-alpha.141 → 2.7.0-alpha.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/agents/reasoning/README.md +171 -0
- package/.claude/agents/reasoning/agent.md +816 -0
- package/.claude/agents/reasoning/example-reasoning-agent-template.md +362 -0
- package/.claude/agents/reasoning/goal-planner.md +73 -0
- package/.claude/commands/coordination/README.md +9 -0
- package/.claude/commands/memory/README.md +9 -0
- package/.claude/settings.json +3 -3
- package/.claude/sparc-modes.json +108 -0
- package/README.md +1 -6
- package/bin/claude-flow +1 -1
- package/dist/src/cli/command-registry.js +70 -6
- package/dist/src/cli/command-registry.js.map +1 -1
- package/dist/src/cli/help-formatter.js +5 -3
- package/dist/src/cli/help-formatter.js.map +1 -1
- package/dist/src/cli/help-text.js +53 -5
- package/dist/src/cli/help-text.js.map +1 -1
- package/dist/src/cli/simple-cli.js +182 -172
- package/dist/src/cli/simple-cli.js.map +1 -1
- package/dist/src/cli/simple-commands/agent-booster.js +415 -0
- package/dist/src/cli/simple-commands/agent-booster.js.map +1 -0
- package/dist/src/cli/simple-commands/agent.js +856 -13
- package/dist/src/cli/simple-commands/agent.js.map +1 -1
- package/dist/src/cli/simple-commands/config.js +115 -257
- package/dist/src/cli/simple-commands/config.js.map +1 -1
- package/dist/src/cli/simple-commands/env-template.js +180 -0
- package/dist/src/cli/simple-commands/env-template.js.map +1 -0
- package/dist/src/cli/simple-commands/init/help.js +23 -0
- package/dist/src/cli/simple-commands/init/help.js.map +1 -1
- package/dist/src/cli/simple-commands/init/index.js +63 -0
- package/dist/src/cli/simple-commands/init/index.js.map +1 -1
- package/dist/src/cli/simple-commands/memory.js +414 -16
- package/dist/src/cli/simple-commands/memory.js.map +1 -1
- package/dist/src/cli/simple-commands/proxy.js +304 -0
- package/dist/src/cli/simple-commands/proxy.js.map +1 -0
- package/dist/src/cli/simple-commands/sparc.js +16 -19
- package/dist/src/cli/simple-commands/sparc.js.map +1 -1
- package/dist/src/cli/validation-helper.js.map +1 -1
- package/dist/src/core/version.js +1 -1
- package/dist/src/execution/agent-executor.js +181 -0
- package/dist/src/execution/agent-executor.js.map +1 -0
- package/dist/src/execution/index.js +12 -0
- package/dist/src/execution/index.js.map +1 -0
- package/dist/src/execution/provider-manager.js +110 -0
- package/dist/src/execution/provider-manager.js.map +1 -0
- package/dist/src/hooks/redaction-hook.js +89 -0
- package/dist/src/hooks/redaction-hook.js.map +1 -0
- package/dist/src/memory/swarm-memory.js +340 -421
- package/dist/src/memory/swarm-memory.js.map +1 -1
- package/dist/src/reasoningbank/reasoningbank-adapter.js +144 -0
- package/dist/src/reasoningbank/reasoningbank-adapter.js.map +1 -0
- package/dist/src/utils/key-redactor.js +108 -0
- package/dist/src/utils/key-redactor.js.map +1 -0
- package/dist/src/utils/metrics-reader.js.map +1 -1
- package/docs/AGENT-BOOSTER-INTEGRATION.md +407 -0
- package/docs/AGENTIC-FLOW-INTEGRATION-GUIDE.md +753 -0
- package/docs/AGENTIC_FLOW_EXECUTION_FIX_REPORT.md +474 -0
- package/docs/AGENTIC_FLOW_INTEGRATION_STATUS.md +143 -0
- package/docs/AGENTIC_FLOW_MVP_COMPLETE.md +367 -0
- package/docs/AGENTIC_FLOW_SECURITY_TEST_REPORT.md +369 -0
- package/docs/COMMAND-VERIFICATION-REPORT.md +441 -0
- package/docs/COMMIT_SUMMARY.md +247 -0
- package/docs/DEEP_REVIEW_COMPREHENSIVE_REPORT.md +922 -0
- package/docs/DOCKER-VALIDATION-REPORT.md +281 -0
- package/docs/ENV-SETUP-GUIDE.md +270 -0
- package/docs/FINAL_PRE_PUBLISH_VALIDATION.md +823 -0
- package/docs/FINAL_VALIDATION_REPORT.md +165 -0
- package/docs/HOOKS-V2-MODIFICATION.md +146 -0
- package/docs/INDEX.md +568 -0
- package/docs/INTEGRATION_COMPLETE.md +414 -0
- package/docs/MEMORY_REDACTION_TEST_REPORT.md +300 -0
- package/docs/PERFORMANCE-SYSTEMS-STATUS.md +340 -0
- package/docs/PRE_RELEASE_FIXES_REPORT.md +435 -0
- package/docs/README.md +35 -0
- package/docs/REASONING-AGENTS.md +482 -0
- package/docs/REASONINGBANK-AGENT-CREATION-GUIDE.md +813 -0
- package/docs/REASONINGBANK-ANALYSIS-COMPLETE.md +479 -0
- package/docs/REASONINGBANK-BENCHMARK-RESULTS.md +166 -0
- package/docs/REASONINGBANK-BENCHMARK.md +396 -0
- package/docs/REASONINGBANK-CLI-INTEGRATION.md +455 -0
- package/docs/REASONINGBANK-CORE-INTEGRATION.md +658 -0
- package/docs/REASONINGBANK-COST-OPTIMIZATION.md +329 -0
- package/docs/REASONINGBANK-DEMO.md +419 -0
- package/docs/REASONINGBANK-INTEGRATION-COMPLETE.md +249 -0
- package/docs/REASONINGBANK-INTEGRATION-STATUS.md +179 -0
- package/docs/REASONINGBANK-VALIDATION.md +532 -0
- package/docs/REASONINGBANK_ARCHITECTURE.md +475 -0
- package/docs/REASONINGBANK_INTEGRATION_COMPLETE.md +558 -0
- package/docs/REASONINGBANK_INTEGRATION_PLAN.md +1188 -0
- package/docs/REGRESSION-ANALYSIS-REPORT.md +500 -0
- package/docs/RELEASE_v2.6.0-alpha.2.md +658 -0
- package/docs/api/API_DOCUMENTATION.md +721 -0
- package/docs/architecture/ARCHITECTURE.md +1690 -0
- package/docs/ci-cd/README.md +368 -0
- package/docs/development/DEPLOYMENT.md +2348 -0
- package/docs/development/DEVELOPMENT_WORKFLOW.md +1333 -0
- package/docs/development/build-analysis-report.md +252 -0
- package/docs/development/pair-optimization.md +156 -0
- package/docs/development/token-tracking-status.md +103 -0
- package/docs/development/training-pipeline-demo.md +163 -0
- package/docs/development/training-pipeline-real-only.md +196 -0
- package/docs/epic-sdk-integration.md +1269 -0
- package/docs/experimental/RIEMANN_HYPOTHESIS_PROOF.md +124 -0
- package/docs/experimental/computational_verification.py +436 -0
- package/docs/experimental/novel_approaches.md +560 -0
- package/docs/experimental/riemann_hypothesis_analysis.md +263 -0
- package/docs/experimental/riemann_proof_attempt.md +124 -0
- package/docs/experimental/riemann_synthesis.md +277 -0
- package/docs/experimental/verification_results.json +12 -0
- package/docs/experimental/visualization_insights.md +720 -0
- package/docs/guides/USER_GUIDE.md +1138 -0
- package/docs/guides/token-tracking-guide.md +291 -0
- package/docs/reference/AGENTS.md +1011 -0
- package/docs/reference/MCP_TOOLS.md +2188 -0
- package/docs/reference/SPARC.md +717 -0
- package/docs/reference/SWARM.md +2000 -0
- package/docs/sdk/CLAUDE-CODE-SDK-DEEP-ANALYSIS.md +649 -0
- package/docs/sdk/CLAUDE-FLOW-SDK-INTEGRATION-ANALYSIS.md +242 -0
- package/docs/sdk/INTEGRATION-ROADMAP.md +420 -0
- package/docs/sdk/MCP-TOOLS-UPDATE.md +270 -0
- package/docs/sdk/SDK-ADVANCED-FEATURES-INTEGRATION.md +723 -0
- package/docs/sdk/SDK-ALL-FEATURES-INTEGRATION-MATRIX.md +612 -0
- package/docs/sdk/SDK-INTEGRATION-COMPLETE.md +358 -0
- package/docs/sdk/SDK-INTEGRATION-PHASES-V2.5.md +750 -0
- package/docs/sdk/SDK-LEVERAGE-REAL-FEATURES.md +676 -0
- package/docs/sdk/SDK-VALIDATION-RESULTS.md +400 -0
- package/docs/sdk/epic-sdk-integration.md +1269 -0
- package/docs/setup/remote-setup.md +93 -0
- package/docs/validation/final-validation-summary.md +220 -0
- package/docs/validation/verification-integration.md +190 -0
- package/docs/validation/verification-validation.md +349 -0
- package/docs/wiki/background-commands.md +1213 -0
- package/docs/wiki/session-persistence.md +342 -0
- package/docs/wiki/stream-chain-command.md +537 -0
- package/package.json +4 -2
- package/src/cli/command-registry.js +70 -5
- package/src/cli/help-text.js +26 -5
- package/src/cli/simple-cli.ts +18 -7
- package/src/cli/simple-commands/agent-booster.js +515 -0
- package/src/cli/simple-commands/agent.js +1001 -12
- package/src/cli/simple-commands/agent.ts +137 -0
- package/src/cli/simple-commands/config.ts +127 -0
- package/src/cli/simple-commands/env-template.js +190 -0
- package/src/cli/simple-commands/init/help.js +23 -0
- package/src/cli/simple-commands/init/index.js +84 -6
- package/src/cli/simple-commands/memory.js +497 -16
- package/src/cli/simple-commands/proxy.js +384 -0
- package/src/cli/simple-commands/sparc.js +16 -19
- package/src/execution/agent-executor.ts +306 -0
- package/src/execution/index.ts +19 -0
- package/src/execution/provider-manager.ts +187 -0
- package/src/hooks/redaction-hook.ts +115 -0
- package/src/reasoningbank/reasoningbank-adapter.js +191 -0
- package/src/utils/key-redactor.js +178 -0
- package/src/utils/key-redactor.ts +184 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReasoningBank Adapter for Claude-Flow
|
|
3
|
+
*
|
|
4
|
+
* Wraps agentic-flow's ReasoningBank SDK for use in claude-flow memory commands
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { db, initialize, retrieveMemories, computeEmbedding, loadConfig } from 'agentic-flow/dist/reasoningbank/index.js';
|
|
8
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initialize ReasoningBank database
|
|
12
|
+
*/
|
|
13
|
+
export async function initializeReasoningBank() {
|
|
14
|
+
// Set database path
|
|
15
|
+
process.env.CLAUDE_FLOW_DB_PATH = '.swarm/memory.db';
|
|
16
|
+
|
|
17
|
+
await initialize();
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Store a memory in ReasoningBank
|
|
23
|
+
*/
|
|
24
|
+
export async function storeMemory(key, value, options = {}) {
|
|
25
|
+
const memoryId = `mem_${uuidv4()}`;
|
|
26
|
+
|
|
27
|
+
const memory = {
|
|
28
|
+
id: memoryId,
|
|
29
|
+
type: options.type || 'fact',
|
|
30
|
+
pattern_data: JSON.stringify({
|
|
31
|
+
key,
|
|
32
|
+
value,
|
|
33
|
+
namespace: options.namespace || 'default',
|
|
34
|
+
agent: options.agent || 'memory-agent',
|
|
35
|
+
domain: options.domain || 'general',
|
|
36
|
+
}),
|
|
37
|
+
confidence: options.confidence || 0.8,
|
|
38
|
+
usage_count: 0,
|
|
39
|
+
created_at: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Store memory
|
|
43
|
+
db.upsertMemory(memory);
|
|
44
|
+
|
|
45
|
+
// Compute and store embedding for semantic search
|
|
46
|
+
try {
|
|
47
|
+
const config = loadConfig();
|
|
48
|
+
const embeddingModel = config.embeddings.provider || 'claude';
|
|
49
|
+
|
|
50
|
+
const embedding = await computeEmbedding(`${key}: ${value}`);
|
|
51
|
+
const vectorArray = new Float32Array(embedding);
|
|
52
|
+
|
|
53
|
+
db.upsertEmbedding({
|
|
54
|
+
memory_id: memoryId,
|
|
55
|
+
vector: vectorArray,
|
|
56
|
+
model: embeddingModel, // Dynamic model from config
|
|
57
|
+
dims: vectorArray.length, // Required: embedding dimensions
|
|
58
|
+
created_at: new Date().toISOString(),
|
|
59
|
+
});
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.warn('[ReasoningBank] Warning: Could not compute embedding:', error.message);
|
|
62
|
+
// Continue without embedding - memory is still stored
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return memoryId;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Query memories from ReasoningBank
|
|
70
|
+
*/
|
|
71
|
+
export async function queryMemories(searchQuery, options = {}) {
|
|
72
|
+
try {
|
|
73
|
+
// Use ReasoningBank's semantic retrieval
|
|
74
|
+
const memories = await retrieveMemories(searchQuery, {
|
|
75
|
+
domain: options.domain || 'general',
|
|
76
|
+
agent: options.agent || 'memory-agent',
|
|
77
|
+
k: options.limit || 10,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return memories.map(mem => {
|
|
81
|
+
try {
|
|
82
|
+
const data = JSON.parse(mem.pattern_data);
|
|
83
|
+
return {
|
|
84
|
+
id: mem.id,
|
|
85
|
+
key: data.key,
|
|
86
|
+
value: data.value,
|
|
87
|
+
namespace: data.namespace,
|
|
88
|
+
confidence: mem.confidence,
|
|
89
|
+
usage_count: mem.usage_count,
|
|
90
|
+
created_at: mem.created_at,
|
|
91
|
+
score: mem.score || 0,
|
|
92
|
+
};
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}).filter(Boolean);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
// Fallback to simple query if semantic search fails
|
|
99
|
+
console.warn('[ReasoningBank] Semantic search failed, using simple query:', error.message);
|
|
100
|
+
|
|
101
|
+
const dbInstance = db.getDb();
|
|
102
|
+
const rows = dbInstance.prepare(`
|
|
103
|
+
SELECT * FROM patterns
|
|
104
|
+
WHERE pattern_data LIKE ?
|
|
105
|
+
ORDER BY confidence DESC, usage_count DESC
|
|
106
|
+
LIMIT ?
|
|
107
|
+
`).all(`%${searchQuery}%`, options.limit || 10);
|
|
108
|
+
|
|
109
|
+
return rows.map(row => {
|
|
110
|
+
try {
|
|
111
|
+
const data = JSON.parse(row.pattern_data);
|
|
112
|
+
return {
|
|
113
|
+
id: row.id,
|
|
114
|
+
key: data.key,
|
|
115
|
+
value: data.value,
|
|
116
|
+
namespace: data.namespace,
|
|
117
|
+
confidence: row.confidence,
|
|
118
|
+
usage_count: row.usage_count,
|
|
119
|
+
created_at: row.created_at,
|
|
120
|
+
};
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}).filter(Boolean);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* List all memories
|
|
130
|
+
*/
|
|
131
|
+
export async function listMemories(options = {}) {
|
|
132
|
+
const dbInstance = db.getDb();
|
|
133
|
+
|
|
134
|
+
const limit = options.limit || 10;
|
|
135
|
+
const sortBy = options.sort || 'created_at';
|
|
136
|
+
const sortOrder = options.order || 'DESC';
|
|
137
|
+
|
|
138
|
+
const rows = dbInstance.prepare(`
|
|
139
|
+
SELECT * FROM patterns
|
|
140
|
+
ORDER BY ${sortBy} ${sortOrder}
|
|
141
|
+
LIMIT ?
|
|
142
|
+
`).all(limit);
|
|
143
|
+
|
|
144
|
+
return rows.map(row => {
|
|
145
|
+
try {
|
|
146
|
+
const data = JSON.parse(row.pattern_data);
|
|
147
|
+
return {
|
|
148
|
+
id: row.id,
|
|
149
|
+
key: data.key,
|
|
150
|
+
value: data.value,
|
|
151
|
+
namespace: data.namespace,
|
|
152
|
+
confidence: row.confidence,
|
|
153
|
+
usage_count: row.usage_count,
|
|
154
|
+
created_at: row.created_at,
|
|
155
|
+
};
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}).filter(Boolean);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get ReasoningBank statistics
|
|
164
|
+
*/
|
|
165
|
+
export async function getStatus() {
|
|
166
|
+
const dbInstance = db.getDb();
|
|
167
|
+
|
|
168
|
+
const stats = dbInstance.prepare(`
|
|
169
|
+
SELECT
|
|
170
|
+
COUNT(*) as total_memories,
|
|
171
|
+
AVG(confidence) as avg_confidence,
|
|
172
|
+
SUM(usage_count) as total_usage
|
|
173
|
+
FROM patterns
|
|
174
|
+
`).get();
|
|
175
|
+
|
|
176
|
+
const embeddingCount = dbInstance.prepare(`
|
|
177
|
+
SELECT COUNT(*) as count FROM pattern_embeddings
|
|
178
|
+
`).get();
|
|
179
|
+
|
|
180
|
+
const trajectoryCount = dbInstance.prepare(`
|
|
181
|
+
SELECT COUNT(*) as count FROM task_trajectories
|
|
182
|
+
`).get();
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
total_memories: stats.total_memories || 0,
|
|
186
|
+
avg_confidence: stats.avg_confidence || 0,
|
|
187
|
+
total_usage: stats.total_usage || 0,
|
|
188
|
+
total_embeddings: embeddingCount.count || 0,
|
|
189
|
+
total_trajectories: trajectoryCount.count || 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key Redaction Utility
|
|
3
|
+
* Prevents sensitive data from leaking into logs, memory, or git commits
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class KeyRedactor {
|
|
7
|
+
static API_KEY_PATTERNS = [
|
|
8
|
+
// Anthropic API keys
|
|
9
|
+
/sk-ant-[a-zA-Z0-9_-]{95,}/gi,
|
|
10
|
+
|
|
11
|
+
// OpenRouter API keys
|
|
12
|
+
/sk-or-[a-zA-Z0-9_-]{32,}/gi,
|
|
13
|
+
|
|
14
|
+
// Google/Gemini API keys
|
|
15
|
+
/AIza[a-zA-Z0-9_-]{35}/gi,
|
|
16
|
+
|
|
17
|
+
// Generic API keys
|
|
18
|
+
/[a-zA-Z0-9_-]{20,}API[a-zA-Z0-9_-]{20,}/gi,
|
|
19
|
+
|
|
20
|
+
// Bearer tokens
|
|
21
|
+
/Bearer\s+[a-zA-Z0-9_\-\.]{20,}/gi,
|
|
22
|
+
|
|
23
|
+
// Environment variable format
|
|
24
|
+
/([A-Z_]+_API_KEY|[A-Z_]+_TOKEN|[A-Z_]+_SECRET)=["']?([^"'\s]+)["']?/gi,
|
|
25
|
+
|
|
26
|
+
// Supabase keys
|
|
27
|
+
/eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*/gi,
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
static SENSITIVE_FIELDS = [
|
|
31
|
+
'apiKey',
|
|
32
|
+
'api_key',
|
|
33
|
+
'token',
|
|
34
|
+
'secret',
|
|
35
|
+
'password',
|
|
36
|
+
'private_key',
|
|
37
|
+
'privateKey',
|
|
38
|
+
'accessToken',
|
|
39
|
+
'access_token',
|
|
40
|
+
'refreshToken',
|
|
41
|
+
'refresh_token',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Redact API keys and sensitive data from text
|
|
46
|
+
*/
|
|
47
|
+
static redact(text, showPrefix = true) {
|
|
48
|
+
if (!text) return text;
|
|
49
|
+
|
|
50
|
+
let redacted = text;
|
|
51
|
+
|
|
52
|
+
// Redact using patterns
|
|
53
|
+
this.API_KEY_PATTERNS.forEach(pattern => {
|
|
54
|
+
redacted = redacted.replace(pattern, (match) => {
|
|
55
|
+
if (showPrefix && match.length > 8) {
|
|
56
|
+
const prefix = match.substring(0, 8);
|
|
57
|
+
return `${prefix}...[REDACTED]`;
|
|
58
|
+
}
|
|
59
|
+
return '[REDACTED_API_KEY]';
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return redacted;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Redact sensitive fields in objects
|
|
68
|
+
*/
|
|
69
|
+
static redactObject(obj, deep = true) {
|
|
70
|
+
if (!obj || typeof obj !== 'object') return obj;
|
|
71
|
+
|
|
72
|
+
const redacted = { ...obj };
|
|
73
|
+
|
|
74
|
+
Object.keys(redacted).forEach(key => {
|
|
75
|
+
const lowerKey = key.toLowerCase();
|
|
76
|
+
|
|
77
|
+
// Check if field name is sensitive
|
|
78
|
+
const isSensitive = this.SENSITIVE_FIELDS.some(field =>
|
|
79
|
+
lowerKey.includes(field)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (isSensitive && typeof redacted[key] === 'string') {
|
|
83
|
+
const value = redacted[key];
|
|
84
|
+
if (value && value.length > 8) {
|
|
85
|
+
redacted[key] = `${value.substring(0, 4)}...[REDACTED]`;
|
|
86
|
+
} else {
|
|
87
|
+
redacted[key] = '[REDACTED]';
|
|
88
|
+
}
|
|
89
|
+
} else if (deep && typeof redacted[key] === 'object' && redacted[key] !== null) {
|
|
90
|
+
redacted[key] = this.redactObject(redacted[key], deep);
|
|
91
|
+
} else if (typeof redacted[key] === 'string') {
|
|
92
|
+
// Redact any API keys in string values
|
|
93
|
+
redacted[key] = this.redact(redacted[key]);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return redacted;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Sanitize text for safe logging
|
|
102
|
+
*/
|
|
103
|
+
static sanitize(text) {
|
|
104
|
+
return this.redact(text, true);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Sanitize command arguments
|
|
109
|
+
*/
|
|
110
|
+
static sanitizeArgs(args) {
|
|
111
|
+
return args.map(arg => {
|
|
112
|
+
// Check if arg is a flag value pair
|
|
113
|
+
if (arg.includes('key') || arg.includes('token') || arg.includes('secret')) {
|
|
114
|
+
return this.redact(arg);
|
|
115
|
+
}
|
|
116
|
+
return arg;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if text contains unredacted sensitive data
|
|
122
|
+
*/
|
|
123
|
+
static containsSensitiveData(text) {
|
|
124
|
+
return this.API_KEY_PATTERNS.some(pattern => pattern.test(text));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Validate that text is safe for logging/storage
|
|
129
|
+
*/
|
|
130
|
+
static validate(text) {
|
|
131
|
+
const warnings = [];
|
|
132
|
+
|
|
133
|
+
this.API_KEY_PATTERNS.forEach((pattern, index) => {
|
|
134
|
+
if (pattern.test(text)) {
|
|
135
|
+
warnings.push(`Potential API key detected (pattern ${index + 1})`);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
safe: warnings.length === 0,
|
|
141
|
+
warnings,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Redact environment variables
|
|
147
|
+
*/
|
|
148
|
+
static redactEnv(env) {
|
|
149
|
+
const redacted = {};
|
|
150
|
+
|
|
151
|
+
Object.keys(env).forEach(key => {
|
|
152
|
+
const value = env[key];
|
|
153
|
+
if (!value) {
|
|
154
|
+
redacted[key] = '';
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const lowerKey = key.toLowerCase();
|
|
159
|
+
const isSensitive = lowerKey.includes('key') ||
|
|
160
|
+
lowerKey.includes('token') ||
|
|
161
|
+
lowerKey.includes('secret') ||
|
|
162
|
+
lowerKey.includes('password');
|
|
163
|
+
|
|
164
|
+
if (isSensitive) {
|
|
165
|
+
redacted[key] = value.length > 8
|
|
166
|
+
? `${value.substring(0, 4)}...[REDACTED]`
|
|
167
|
+
: '[REDACTED]';
|
|
168
|
+
} else {
|
|
169
|
+
redacted[key] = value;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return redacted;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Export singleton instance
|
|
178
|
+
export const redactor = KeyRedactor;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key Redaction Utility
|
|
3
|
+
* Prevents sensitive data from leaking into logs, memory, or git commits
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface RedactionConfig {
|
|
7
|
+
patterns: RegExp[];
|
|
8
|
+
replacement: string;
|
|
9
|
+
maskLength: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class KeyRedactor {
|
|
13
|
+
private static readonly API_KEY_PATTERNS = [
|
|
14
|
+
// Anthropic API keys
|
|
15
|
+
/sk-ant-[a-zA-Z0-9_-]{95,}/gi,
|
|
16
|
+
|
|
17
|
+
// OpenRouter API keys
|
|
18
|
+
/sk-or-[a-zA-Z0-9_-]{32,}/gi,
|
|
19
|
+
|
|
20
|
+
// Google/Gemini API keys
|
|
21
|
+
/AIza[a-zA-Z0-9_-]{35}/gi,
|
|
22
|
+
|
|
23
|
+
// Generic API keys
|
|
24
|
+
/[a-zA-Z0-9_-]{20,}API[a-zA-Z0-9_-]{20,}/gi,
|
|
25
|
+
|
|
26
|
+
// Bearer tokens
|
|
27
|
+
/Bearer\s+[a-zA-Z0-9_\-\.]{20,}/gi,
|
|
28
|
+
|
|
29
|
+
// Environment variable format
|
|
30
|
+
/([A-Z_]+_API_KEY|[A-Z_]+_TOKEN|[A-Z_]+_SECRET)=["']?([^"'\s]+)["']?/gi,
|
|
31
|
+
|
|
32
|
+
// Supabase keys
|
|
33
|
+
/eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*/gi,
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
private static readonly SENSITIVE_FIELDS = [
|
|
37
|
+
'apiKey',
|
|
38
|
+
'api_key',
|
|
39
|
+
'token',
|
|
40
|
+
'secret',
|
|
41
|
+
'password',
|
|
42
|
+
'private_key',
|
|
43
|
+
'privateKey',
|
|
44
|
+
'accessToken',
|
|
45
|
+
'access_token',
|
|
46
|
+
'refreshToken',
|
|
47
|
+
'refresh_token',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Redact API keys and sensitive data from text
|
|
52
|
+
*/
|
|
53
|
+
static redact(text: string, showPrefix = true): string {
|
|
54
|
+
if (!text) return text;
|
|
55
|
+
|
|
56
|
+
let redacted = text;
|
|
57
|
+
|
|
58
|
+
// Redact using patterns
|
|
59
|
+
this.API_KEY_PATTERNS.forEach(pattern => {
|
|
60
|
+
redacted = redacted.replace(pattern, (match) => {
|
|
61
|
+
if (showPrefix && match.length > 8) {
|
|
62
|
+
const prefix = match.substring(0, 8);
|
|
63
|
+
return `${prefix}...[REDACTED]`;
|
|
64
|
+
}
|
|
65
|
+
return '[REDACTED_API_KEY]';
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return redacted;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Redact sensitive fields in objects
|
|
74
|
+
*/
|
|
75
|
+
static redactObject<T extends Record<string, any>>(obj: T, deep = true): T {
|
|
76
|
+
if (!obj || typeof obj !== 'object') return obj;
|
|
77
|
+
|
|
78
|
+
const redacted = { ...obj };
|
|
79
|
+
|
|
80
|
+
Object.keys(redacted).forEach(key => {
|
|
81
|
+
const lowerKey = key.toLowerCase();
|
|
82
|
+
|
|
83
|
+
// Check if field name is sensitive
|
|
84
|
+
const isSensitive = this.SENSITIVE_FIELDS.some(field =>
|
|
85
|
+
lowerKey.includes(field)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
if (isSensitive && typeof redacted[key] === 'string') {
|
|
89
|
+
const value = redacted[key] as string;
|
|
90
|
+
if (value && value.length > 8) {
|
|
91
|
+
redacted[key] = `${value.substring(0, 4)}...[REDACTED]` as any;
|
|
92
|
+
} else {
|
|
93
|
+
redacted[key] = '[REDACTED]' as any;
|
|
94
|
+
}
|
|
95
|
+
} else if (deep && typeof redacted[key] === 'object' && redacted[key] !== null) {
|
|
96
|
+
redacted[key] = this.redactObject(redacted[key], deep);
|
|
97
|
+
} else if (typeof redacted[key] === 'string') {
|
|
98
|
+
// Redact any API keys in string values
|
|
99
|
+
redacted[key] = this.redact(redacted[key]) as any;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return redacted;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Sanitize text for safe logging
|
|
108
|
+
*/
|
|
109
|
+
static sanitize(text: string): string {
|
|
110
|
+
return this.redact(text, true);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Sanitize command arguments
|
|
115
|
+
*/
|
|
116
|
+
static sanitizeArgs(args: string[]): string[] {
|
|
117
|
+
return args.map(arg => {
|
|
118
|
+
// Check if arg is a flag value pair
|
|
119
|
+
if (arg.includes('key') || arg.includes('token') || arg.includes('secret')) {
|
|
120
|
+
return this.redact(arg);
|
|
121
|
+
}
|
|
122
|
+
return arg;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if text contains unredacted sensitive data
|
|
128
|
+
*/
|
|
129
|
+
static containsSensitiveData(text: string): boolean {
|
|
130
|
+
return this.API_KEY_PATTERNS.some(pattern => pattern.test(text));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Validate that text is safe for logging/storage
|
|
135
|
+
*/
|
|
136
|
+
static validate(text: string): { safe: boolean; warnings: string[] } {
|
|
137
|
+
const warnings: string[] = [];
|
|
138
|
+
|
|
139
|
+
this.API_KEY_PATTERNS.forEach((pattern, index) => {
|
|
140
|
+
if (pattern.test(text)) {
|
|
141
|
+
warnings.push(`Potential API key detected (pattern ${index + 1})`);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
safe: warnings.length === 0,
|
|
147
|
+
warnings,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Redact environment variables
|
|
153
|
+
*/
|
|
154
|
+
static redactEnv(env: Record<string, string | undefined>): Record<string, string> {
|
|
155
|
+
const redacted: Record<string, string> = {};
|
|
156
|
+
|
|
157
|
+
Object.keys(env).forEach(key => {
|
|
158
|
+
const value = env[key];
|
|
159
|
+
if (!value) {
|
|
160
|
+
redacted[key] = '';
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const lowerKey = key.toLowerCase();
|
|
165
|
+
const isSensitive = lowerKey.includes('key') ||
|
|
166
|
+
lowerKey.includes('token') ||
|
|
167
|
+
lowerKey.includes('secret') ||
|
|
168
|
+
lowerKey.includes('password');
|
|
169
|
+
|
|
170
|
+
if (isSensitive) {
|
|
171
|
+
redacted[key] = value.length > 8
|
|
172
|
+
? `${value.substring(0, 4)}...[REDACTED]`
|
|
173
|
+
: '[REDACTED]';
|
|
174
|
+
} else {
|
|
175
|
+
redacted[key] = value;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return redacted;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Export singleton instance
|
|
184
|
+
export const redactor = KeyRedactor;
|