change tools->meToCo
This commit is contained in:
parent
f00e2d3cfd
commit
223d28f094
@ -315,7 +315,7 @@ PUBLIC_BASE_URL=http://localhost:4321
|
|||||||
Die Tools werden in `src/data/tools.yaml` verwaltet. Vollständiges Beispiel:
|
Die Tools werden in `src/data/tools.yaml` verwaltet. Vollständiges Beispiel:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
tools:
|
meToCo:
|
||||||
- name: Autopsy
|
- name: Autopsy
|
||||||
type: software # software|method|concept
|
type: software # software|method|concept
|
||||||
description: >-
|
description: >-
|
||||||
@ -431,7 +431,7 @@ phases:
|
|||||||
domain-agnostic-software:
|
domain-agnostic-software:
|
||||||
- id: collaboration-general
|
- id: collaboration-general
|
||||||
name: Übergreifend & Kollaboration
|
name: Übergreifend & Kollaboration
|
||||||
description: Cross-cutting tools and collaboration platforms
|
description: Cross-cutting meToCo and collaboration platforms
|
||||||
- id: specific-os
|
- id: specific-os
|
||||||
name: Betriebssysteme
|
name: Betriebssysteme
|
||||||
description: Operating Systems which focus on forensics
|
description: Operating Systems which focus on forensics
|
||||||
|
@ -158,7 +158,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools-grid {
|
.meToCo-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
@ -370,7 +370,7 @@
|
|||||||
.stats-grid {
|
.stats-grid {
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
}
|
}
|
||||||
.tools-grid {
|
.meToCo-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
.checkbox-group {
|
.checkbox-group {
|
||||||
@ -396,12 +396,12 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>🔧 DFIR Tools YAML Editor</h1>
|
<h1>🔧 DFIR Tools YAML Editor</h1>
|
||||||
<p>Enhanced editor for Digital Forensics and Incident Response tools, methods, and concepts</p>
|
<p>Enhanced editor for Digital Forensics and Incident Response meToCo, methods, and concepts</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<div class="tab active" onclick="showTab('overview')">📊 Overview</div>
|
<div class="tab active" onclick="showTab('overview')">📊 Overview</div>
|
||||||
<div class="tab" onclick="showTab('tools')">🛠️ Tools & Concepts</div>
|
<div class="tab" onclick="showTab('meToCo')">🛠️ Tools & Concepts</div>
|
||||||
<div class="tab" onclick="showTab('editor')">✏️ Editor</div>
|
<div class="tab" onclick="showTab('editor')">✏️ Editor</div>
|
||||||
<div class="tab" onclick="showTab('bulk')">📋 Bulk Edit</div>
|
<div class="tab" onclick="showTab('bulk')">📋 Bulk Edit</div>
|
||||||
<div class="tab" onclick="showTab('knowledge')">📚 Knowledge Generator</div>
|
<div class="tab" onclick="showTab('knowledge')">📚 Knowledge Generator</div>
|
||||||
@ -413,7 +413,7 @@
|
|||||||
<div style="background: var(--light); padding: 20px; border-radius: 10px; margin-bottom: 20px; border: 2px dashed var(--border); text-align: center;">
|
<div style="background: var(--light); padding: 20px; border-radius: 10px; margin-bottom: 20px; border: 2px dashed var(--border); text-align: center;">
|
||||||
<h3>📁 Load YAML File</h3>
|
<h3>📁 Load YAML File</h3>
|
||||||
<input type="file" id="fileInput" accept=".yaml,.yml" onchange="loadFile()">
|
<input type="file" id="fileInput" accept=".yaml,.yml" onchange="loadFile()">
|
||||||
<p>Select a YAML file to load existing tools data</p>
|
<p>Select a YAML file to load existing meToCo data</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="stats" class="stats-grid"></div>
|
<div id="stats" class="stats-grid"></div>
|
||||||
@ -430,7 +430,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tools Tab with Enhanced Search -->
|
<!-- Tools Tab with Enhanced Search -->
|
||||||
<div id="tools" class="tab-content">
|
<div id="meToCo" class="tab-content">
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<h3>🔍 Search & Filter Tools</h3>
|
<h3>🔍 Search & Filter Tools</h3>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
@ -460,7 +460,7 @@
|
|||||||
<button class="btn btn-secondary" onclick="clearFilters()">Clear All</button>
|
<button class="btn btn-secondary" onclick="clearFilters()">Clear All</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="toolsGrid" class="tools-grid"></div>
|
<div id="meToCoGrid" class="meToCo-grid"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Enhanced Editor Tab -->
|
<!-- Enhanced Editor Tab -->
|
||||||
@ -645,7 +645,7 @@
|
|||||||
<div class="bulk-section">
|
<div class="bulk-section">
|
||||||
<h3>📋 Bulk Operations</h3>
|
<h3>📋 Bulk Operations</h3>
|
||||||
<div id="selectionInfo" style="margin-bottom: 15px; padding: 15px; background: white; border-radius: 8px; font-weight: 600; text-align: center; border: 2px solid var(--primary);">
|
<div id="selectionInfo" style="margin-bottom: 15px; padding: 15px; background: white; border-radius: 8px; font-weight: 600; text-align: center; border: 2px solid var(--primary);">
|
||||||
No tools selected
|
No meToCo selected
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bulk-controls">
|
<div class="bulk-controls">
|
||||||
@ -702,7 +702,7 @@
|
|||||||
<button class="btn btn-danger" onclick="bulkDelete()">💀 Delete Selected Tools</button>
|
<button class="btn btn-danger" onclick="bulkDelete()">💀 Delete Selected Tools</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="bulkToolsGrid" class="tools-grid"></div>
|
<div id="bulkToolsGrid" class="meToCo-grid"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Knowledge Generator Tab -->
|
<!-- Knowledge Generator Tab -->
|
||||||
@ -755,7 +755,7 @@
|
|||||||
// Initialize with correct YAML structure including scenarios
|
// Initialize with correct YAML structure including scenarios
|
||||||
function init() {
|
function init() {
|
||||||
yamlData = {
|
yamlData = {
|
||||||
tools: [],
|
meToCo: [],
|
||||||
domains: [
|
domains: [
|
||||||
{ id: 'incident-response', name: 'Incident Response & Breach-Untersuchung' },
|
{ id: 'incident-response', name: 'Incident Response & Breach-Untersuchung' },
|
||||||
{ id: 'static-investigations', name: 'Datenträgerforensik & Ermittlungen' },
|
{ id: 'static-investigations', name: 'Datenträgerforensik & Ermittlungen' },
|
||||||
@ -773,7 +773,7 @@
|
|||||||
{ id: 'reporting', name: 'Bericht & Präsentation', description: 'Documentation, Visualization, Presentation Tools' }
|
{ id: 'reporting', name: 'Bericht & Präsentation', description: 'Documentation, Visualization, Presentation Tools' }
|
||||||
],
|
],
|
||||||
'domain-agnostic-software': [
|
'domain-agnostic-software': [
|
||||||
{ id: 'collaboration-general', name: 'Übergreifend & Kollaboration', description: 'Cross-cutting tools and collaboration platforms' },
|
{ id: 'collaboration-general', name: 'Übergreifend & Kollaboration', description: 'Cross-cutting meToCo and collaboration platforms' },
|
||||||
{ id: 'specific-os', name: 'Betriebssysteme', description: 'Operating Systems which focus on forensics' }
|
{ id: 'specific-os', name: 'Betriebssysteme', description: 'Operating Systems which focus on forensics' }
|
||||||
],
|
],
|
||||||
scenarios: [
|
scenarios: [
|
||||||
@ -801,7 +801,7 @@
|
|||||||
document.getElementById(tabName).classList.add('active');
|
document.getElementById(tabName).classList.add('active');
|
||||||
event.target.classList.add('active');
|
event.target.classList.add('active');
|
||||||
|
|
||||||
if (tabName === 'tools') renderToolsGrid();
|
if (tabName === 'meToCo') renderToolsGrid();
|
||||||
else if (tabName === 'bulk') renderBulkGrid();
|
else if (tabName === 'bulk') renderBulkGrid();
|
||||||
else if (tabName === 'knowledge') updateKnowledgeToolSelect();
|
else if (tabName === 'knowledge') updateKnowledgeToolSelect();
|
||||||
}
|
}
|
||||||
@ -811,11 +811,11 @@
|
|||||||
applyFilters();
|
applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchTools(tools, searchTerm) {
|
function searchTools(meToCo, searchTerm) {
|
||||||
if (!searchTerm) return tools;
|
if (!searchTerm) return meToCo;
|
||||||
|
|
||||||
const term = searchTerm.toLowerCase();
|
const term = searchTerm.toLowerCase();
|
||||||
return tools.filter(tool => {
|
return meToCo.filter(tool => {
|
||||||
// Search in name
|
// Search in name
|
||||||
if (tool.name && tool.name.toLowerCase().includes(term)) return true;
|
if (tool.name && tool.name.toLowerCase().includes(term)) return true;
|
||||||
|
|
||||||
@ -946,15 +946,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateStats() {
|
function updateStats() {
|
||||||
if (!yamlData?.tools) return;
|
if (!yamlData?.meToCo) return;
|
||||||
|
|
||||||
const stats = {
|
const stats = {
|
||||||
total: yamlData.tools.length,
|
total: yamlData.meToCo.length,
|
||||||
software: yamlData.tools.filter(t => t.type === 'software' || !t.type).length,
|
software: yamlData.meToCo.filter(t => t.type === 'software' || !t.type).length,
|
||||||
methods: yamlData.tools.filter(t => t.type === 'method').length,
|
methods: yamlData.meToCo.filter(t => t.type === 'method').length,
|
||||||
concepts: yamlData.tools.filter(t => t.type === 'concept').length,
|
concepts: yamlData.meToCo.filter(t => t.type === 'concept').length,
|
||||||
withKnowledgebase: yamlData.tools.filter(t => t.knowledgebase).length,
|
withKnowledgebase: yamlData.meToCo.filter(t => t.knowledgebase).length,
|
||||||
withRelatedSoftware: yamlData.tools.filter(t => t.related_software && t.related_software.length > 0).length
|
withRelatedSoftware: yamlData.meToCo.filter(t => t.related_software && t.related_software.length > 0).length
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('stats').innerHTML = `
|
document.getElementById('stats').innerHTML = `
|
||||||
@ -1042,8 +1042,8 @@
|
|||||||
|
|
||||||
function saveTool() {
|
function saveTool() {
|
||||||
try {
|
try {
|
||||||
if (!yamlData) yamlData = { tools: [] };
|
if (!yamlData) yamlData = { meToCo: [] };
|
||||||
if (!yamlData.tools) yamlData.tools = [];
|
if (!yamlData.meToCo) yamlData.meToCo = [];
|
||||||
|
|
||||||
const toolType = document.getElementById('toolType').value;
|
const toolType = document.getElementById('toolType').value;
|
||||||
const tool = {
|
const tool = {
|
||||||
@ -1104,11 +1104,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentEditingIndex >= 0) {
|
if (currentEditingIndex >= 0) {
|
||||||
yamlData.tools[currentEditingIndex] = tool;
|
yamlData.meToCo[currentEditingIndex] = tool;
|
||||||
showMessage('Tool updated successfully!');
|
showMessage('Tool updated successfully!');
|
||||||
currentEditingIndex = -1;
|
currentEditingIndex = -1;
|
||||||
} else {
|
} else {
|
||||||
yamlData.tools.push(tool);
|
yamlData.meToCo.push(tool);
|
||||||
showMessage('Tool added successfully!');
|
showMessage('Tool added successfully!');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1146,7 +1146,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function editTool(index) {
|
function editTool(index) {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
currentEditingIndex = index;
|
currentEditingIndex = index;
|
||||||
|
|
||||||
// Populate form fields
|
// Populate form fields
|
||||||
@ -1208,7 +1208,7 @@
|
|||||||
|
|
||||||
function deleteTool(index) {
|
function deleteTool(index) {
|
||||||
if (confirm('Are you sure you want to delete this tool?')) {
|
if (confirm('Are you sure you want to delete this tool?')) {
|
||||||
yamlData.tools.splice(index, 1);
|
yamlData.meToCo.splice(index, 1);
|
||||||
showMessage('Tool deleted successfully!');
|
showMessage('Tool deleted successfully!');
|
||||||
updateStats();
|
updateStats();
|
||||||
renderToolsGrid();
|
renderToolsGrid();
|
||||||
@ -1217,12 +1217,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderToolsGrid() {
|
function renderToolsGrid() {
|
||||||
const container = document.getElementById('toolsGrid');
|
const container = document.getElementById('meToCoGrid');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
if (!yamlData?.tools) return;
|
if (!yamlData?.meToCo) return;
|
||||||
|
|
||||||
let filteredTools = yamlData.tools;
|
let filteredTools = yamlData.meToCo;
|
||||||
|
|
||||||
// Apply search filter
|
// Apply search filter
|
||||||
const searchTerm = document.getElementById('searchInput')?.value;
|
const searchTerm = document.getElementById('searchInput')?.value;
|
||||||
@ -1243,17 +1243,17 @@
|
|||||||
// Update search results info
|
// Update search results info
|
||||||
const searchResults = document.getElementById('searchResults');
|
const searchResults = document.getElementById('searchResults');
|
||||||
if (searchResults) {
|
if (searchResults) {
|
||||||
const totalTools = yamlData.tools.length;
|
const totalTools = yamlData.meToCo.length;
|
||||||
const filteredCount = filteredTools.length;
|
const filteredCount = filteredTools.length;
|
||||||
if (searchTerm || typeFilter || skillFilter) {
|
if (searchTerm || typeFilter || skillFilter) {
|
||||||
searchResults.textContent = `Showing ${filteredCount} of ${totalTools} tools`;
|
searchResults.textContent = `Showing ${filteredCount} of ${totalTools} meToCo`;
|
||||||
} else {
|
} else {
|
||||||
searchResults.textContent = `Showing all ${totalTools} tools`;
|
searchResults.textContent = `Showing all ${totalTools} meToCo`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredTools.forEach((tool, index) => {
|
filteredTools.forEach((tool, index) => {
|
||||||
const originalIndex = yamlData.tools.indexOf(tool);
|
const originalIndex = yamlData.meToCo.indexOf(tool);
|
||||||
const card = createToolCard(tool, originalIndex);
|
const card = createToolCard(tool, originalIndex);
|
||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
});
|
});
|
||||||
@ -1296,9 +1296,9 @@
|
|||||||
const container = document.getElementById('bulkToolsGrid');
|
const container = document.getElementById('bulkToolsGrid');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
if (!yamlData?.tools) return;
|
if (!yamlData?.meToCo) return;
|
||||||
|
|
||||||
yamlData.tools.forEach((tool, index) => {
|
yamlData.meToCo.forEach((tool, index) => {
|
||||||
const card = createBulkToolCard(tool, index);
|
const card = createBulkToolCard(tool, index);
|
||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
});
|
});
|
||||||
@ -1350,7 +1350,7 @@
|
|||||||
|
|
||||||
function selectAllTools() {
|
function selectAllTools() {
|
||||||
selectedTools.clear();
|
selectedTools.clear();
|
||||||
yamlData.tools.forEach((_, index) => selectedTools.add(index));
|
yamlData.meToCo.forEach((_, index) => selectedTools.add(index));
|
||||||
updateSelectionCount();
|
updateSelectionCount();
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
@ -1365,7 +1365,7 @@
|
|||||||
const count = selectedTools.size;
|
const count = selectedTools.size;
|
||||||
const info = document.getElementById('selectionInfo');
|
const info = document.getElementById('selectionInfo');
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
info.textContent = 'No tools selected';
|
info.textContent = 'No meToCo selected';
|
||||||
info.style.background = 'white';
|
info.style.background = 'white';
|
||||||
info.style.borderColor = 'var(--border)';
|
info.style.borderColor = 'var(--border)';
|
||||||
} else {
|
} else {
|
||||||
@ -1377,175 +1377,175 @@
|
|||||||
|
|
||||||
// Enhanced bulk operations with scenarios and related software
|
// Enhanced bulk operations with scenarios and related software
|
||||||
function bulkSetType() {
|
function bulkSetType() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const newType = prompt('Enter new type (software/method/concept):');
|
const newType = prompt('Enter new type (software/method/concept):');
|
||||||
if (newType && ['software', 'method', 'concept'].includes(newType)) {
|
if (newType && ['software', 'method', 'concept'].includes(newType)) {
|
||||||
selectedTools.forEach(index => yamlData.tools[index].type = newType);
|
selectedTools.forEach(index => yamlData.meToCo[index].type = newType);
|
||||||
showMessage(`Updated type for ${selectedTools.size} tools`);
|
showMessage(`Updated type for ${selectedTools.size} meToCo`);
|
||||||
updateStats();
|
updateStats();
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkSetSkillLevel() {
|
function bulkSetSkillLevel() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const newLevel = prompt('Enter skill level (novice/beginner/intermediate/advanced/expert):');
|
const newLevel = prompt('Enter skill level (novice/beginner/intermediate/advanced/expert):');
|
||||||
if (newLevel && ['novice', 'beginner', 'intermediate', 'advanced', 'expert'].includes(newLevel)) {
|
if (newLevel && ['novice', 'beginner', 'intermediate', 'advanced', 'expert'].includes(newLevel)) {
|
||||||
selectedTools.forEach(index => yamlData.tools[index].skillLevel = newLevel);
|
selectedTools.forEach(index => yamlData.meToCo[index].skillLevel = newLevel);
|
||||||
showMessage(`Updated skill level for ${selectedTools.size} tools`);
|
showMessage(`Updated skill level for ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkAddTags() {
|
function bulkAddTags() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const tags = prompt('Enter tags to add (comma-separated):');
|
const tags = prompt('Enter tags to add (comma-separated):');
|
||||||
if (tags) {
|
if (tags) {
|
||||||
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
tool.tags = [...new Set([...(tool.tags || []), ...tagList])];
|
tool.tags = [...new Set([...(tool.tags || []), ...tagList])];
|
||||||
});
|
});
|
||||||
showMessage(`Added tags to ${selectedTools.size} tools`);
|
showMessage(`Added tags to ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkRemoveTags() {
|
function bulkRemoveTags() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const tags = prompt('Enter tags to remove (comma-separated):');
|
const tags = prompt('Enter tags to remove (comma-separated):');
|
||||||
if (tags) {
|
if (tags) {
|
||||||
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tool.tags) {
|
if (tool.tags) {
|
||||||
tool.tags = tool.tags.filter(tag => !tagList.includes(tag));
|
tool.tags = tool.tags.filter(tag => !tagList.includes(tag));
|
||||||
if (tool.tags.length === 0) delete tool.tags;
|
if (tool.tags.length === 0) delete tool.tags;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Removed tags from ${selectedTools.size} tools`);
|
showMessage(`Removed tags from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkReplaceTags() {
|
function bulkReplaceTags() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const tags = prompt('Enter new tags (comma-separated, will replace all existing tags):');
|
const tags = prompt('Enter new tags (comma-separated, will replace all existing tags):');
|
||||||
if (tags !== null) {
|
if (tags !== null) {
|
||||||
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tagList.length > 0) {
|
if (tagList.length > 0) {
|
||||||
tool.tags = tagList;
|
tool.tags = tagList;
|
||||||
} else {
|
} else {
|
||||||
delete tool.tags;
|
delete tool.tags;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Replaced tags for ${selectedTools.size} tools`);
|
showMessage(`Replaced tags for ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkClearTags() {
|
function bulkClearTags() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
if (confirm(`Are you sure you want to clear ALL tags from ${selectedTools.size} selected tools?`)) {
|
if (confirm(`Are you sure you want to clear ALL tags from ${selectedTools.size} selected meToCo?`)) {
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
delete yamlData.tools[index].tags;
|
delete yamlData.meToCo[index].tags;
|
||||||
});
|
});
|
||||||
showMessage(`Cleared tags from ${selectedTools.size} tools`);
|
showMessage(`Cleared tags from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Domain operations
|
// Domain operations
|
||||||
function bulkAddDomains() {
|
function bulkAddDomains() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const domains = prompt('Enter domain IDs to add (comma-separated):');
|
const domains = prompt('Enter domain IDs to add (comma-separated):');
|
||||||
if (domains) {
|
if (domains) {
|
||||||
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
|
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
tool.domains = [...new Set([...(tool.domains || []), ...domainList])];
|
tool.domains = [...new Set([...(tool.domains || []), ...domainList])];
|
||||||
});
|
});
|
||||||
showMessage(`Added domains to ${selectedTools.size} tools`);
|
showMessage(`Added domains to ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkRemoveDomains() {
|
function bulkRemoveDomains() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const domains = prompt('Enter domain IDs to remove (comma-separated):');
|
const domains = prompt('Enter domain IDs to remove (comma-separated):');
|
||||||
if (domains) {
|
if (domains) {
|
||||||
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
|
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tool.domains) {
|
if (tool.domains) {
|
||||||
tool.domains = tool.domains.filter(domain => !domainList.includes(domain));
|
tool.domains = tool.domains.filter(domain => !domainList.includes(domain));
|
||||||
if (tool.domains.length === 0) delete tool.domains;
|
if (tool.domains.length === 0) delete tool.domains;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Removed domains from ${selectedTools.size} tools`);
|
showMessage(`Removed domains from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkClearDomains() {
|
function bulkClearDomains() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
if (confirm(`Are you sure you want to clear ALL domains from ${selectedTools.size} selected tools?`)) {
|
if (confirm(`Are you sure you want to clear ALL domains from ${selectedTools.size} selected meToCo?`)) {
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
delete yamlData.tools[index].domains;
|
delete yamlData.meToCo[index].domains;
|
||||||
});
|
});
|
||||||
showMessage(`Cleared domains from ${selectedTools.size} tools`);
|
showMessage(`Cleared domains from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase operations
|
// Phase operations
|
||||||
function bulkAddPhases() {
|
function bulkAddPhases() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const phases = prompt('Enter phase IDs to add (comma-separated):');
|
const phases = prompt('Enter phase IDs to add (comma-separated):');
|
||||||
if (phases) {
|
if (phases) {
|
||||||
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
|
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
tool.phases = [...new Set([...(tool.phases || []), ...phaseList])];
|
tool.phases = [...new Set([...(tool.phases || []), ...phaseList])];
|
||||||
});
|
});
|
||||||
showMessage(`Added phases to ${selectedTools.size} tools`);
|
showMessage(`Added phases to ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkRemovePhases() {
|
function bulkRemovePhases() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const phases = prompt('Enter phase IDs to remove (comma-separated):');
|
const phases = prompt('Enter phase IDs to remove (comma-separated):');
|
||||||
if (phases) {
|
if (phases) {
|
||||||
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
|
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tool.phases) {
|
if (tool.phases) {
|
||||||
tool.phases = tool.phases.filter(phase => !phaseList.includes(phase));
|
tool.phases = tool.phases.filter(phase => !phaseList.includes(phase));
|
||||||
if (tool.phases.length === 0) delete tool.phases;
|
if (tool.phases.length === 0) delete tool.phases;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Removed phases from ${selectedTools.size} tools`);
|
showMessage(`Removed phases from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkClearPhases() {
|
function bulkClearPhases() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
if (confirm(`Are you sure you want to clear ALL phases from ${selectedTools.size} selected tools?`)) {
|
if (confirm(`Are you sure you want to clear ALL phases from ${selectedTools.size} selected meToCo?`)) {
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
delete yamlData.tools[index].phases;
|
delete yamlData.meToCo[index].phases;
|
||||||
});
|
});
|
||||||
showMessage(`Cleared phases from ${selectedTools.size} tools`);
|
showMessage(`Cleared phases from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenario operations (work with tags that have scenario: prefix)
|
// Scenario operations (work with tags that have scenario: prefix)
|
||||||
function bulkAddScenarios() {
|
function bulkAddScenarios() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const scenarios = prompt('Enter scenario IDs to add (comma-separated, e.g., scenario:memory_dump,scenario:registry):');
|
const scenarios = prompt('Enter scenario IDs to add (comma-separated, e.g., scenario:memory_dump,scenario:registry):');
|
||||||
if (scenarios) {
|
if (scenarios) {
|
||||||
const scenarioList = scenarios.split(',').map(s => {
|
const scenarioList = scenarios.split(',').map(s => {
|
||||||
@ -1553,16 +1553,16 @@
|
|||||||
return trimmed.startsWith('scenario:') ? trimmed : `scenario:${trimmed}`;
|
return trimmed.startsWith('scenario:') ? trimmed : `scenario:${trimmed}`;
|
||||||
}).filter(s => s !== 'scenario:');
|
}).filter(s => s !== 'scenario:');
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
tool.tags = [...new Set([...(tool.tags || []), ...scenarioList])];
|
tool.tags = [...new Set([...(tool.tags || []), ...scenarioList])];
|
||||||
});
|
});
|
||||||
showMessage(`Added scenario tags to ${selectedTools.size} tools`);
|
showMessage(`Added scenario tags to ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkRemoveScenarios() {
|
function bulkRemoveScenarios() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const scenarios = prompt('Enter scenario IDs to remove (comma-separated):');
|
const scenarios = prompt('Enter scenario IDs to remove (comma-separated):');
|
||||||
if (scenarios) {
|
if (scenarios) {
|
||||||
const scenarioList = scenarios.split(',').map(s => {
|
const scenarioList = scenarios.split(',').map(s => {
|
||||||
@ -1570,168 +1570,168 @@
|
|||||||
return trimmed.startsWith('scenario:') ? trimmed : `scenario:${trimmed}`;
|
return trimmed.startsWith('scenario:') ? trimmed : `scenario:${trimmed}`;
|
||||||
}).filter(s => s !== 'scenario:');
|
}).filter(s => s !== 'scenario:');
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tool.tags) {
|
if (tool.tags) {
|
||||||
tool.tags = tool.tags.filter(tag => !scenarioList.includes(tag));
|
tool.tags = tool.tags.filter(tag => !scenarioList.includes(tag));
|
||||||
if (tool.tags.length === 0) delete tool.tags;
|
if (tool.tags.length === 0) delete tool.tags;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Removed scenario tags from ${selectedTools.size} tools`);
|
showMessage(`Removed scenario tags from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkClearScenarios() {
|
function bulkClearScenarios() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
if (confirm(`Are you sure you want to clear ALL scenario tags from ${selectedTools.size} selected tools?`)) {
|
if (confirm(`Are you sure you want to clear ALL scenario tags from ${selectedTools.size} selected meToCo?`)) {
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tool.tags) {
|
if (tool.tags) {
|
||||||
tool.tags = tool.tags.filter(tag => !tag.startsWith('scenario:'));
|
tool.tags = tool.tags.filter(tag => !tag.startsWith('scenario:'));
|
||||||
if (tool.tags.length === 0) delete tool.tags;
|
if (tool.tags.length === 0) delete tool.tags;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Cleared scenario tags from ${selectedTools.size} tools`);
|
showMessage(`Cleared scenario tags from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Platform operations
|
// Platform operations
|
||||||
function bulkAddPlatforms() {
|
function bulkAddPlatforms() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const platforms = prompt('Enter platforms to add (comma-separated, e.g., Windows,Linux,macOS):');
|
const platforms = prompt('Enter platforms to add (comma-separated, e.g., Windows,Linux,macOS):');
|
||||||
if (platforms) {
|
if (platforms) {
|
||||||
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
|
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
tool.platforms = [...new Set([...(tool.platforms || []), ...platformList])];
|
tool.platforms = [...new Set([...(tool.platforms || []), ...platformList])];
|
||||||
});
|
});
|
||||||
showMessage(`Added platforms to ${selectedTools.size} tools`);
|
showMessage(`Added platforms to ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkRemovePlatforms() {
|
function bulkRemovePlatforms() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const platforms = prompt('Enter platforms to remove (comma-separated):');
|
const platforms = prompt('Enter platforms to remove (comma-separated):');
|
||||||
if (platforms) {
|
if (platforms) {
|
||||||
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
|
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tool.platforms) {
|
if (tool.platforms) {
|
||||||
tool.platforms = tool.platforms.filter(platform => !platformList.includes(platform));
|
tool.platforms = tool.platforms.filter(platform => !platformList.includes(platform));
|
||||||
if (tool.platforms.length === 0) delete tool.platforms;
|
if (tool.platforms.length === 0) delete tool.platforms;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Removed platforms from ${selectedTools.size} tools`);
|
showMessage(`Removed platforms from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkClearPlatforms() {
|
function bulkClearPlatforms() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
if (confirm(`Are you sure you want to clear ALL platforms from ${selectedTools.size} selected tools?`)) {
|
if (confirm(`Are you sure you want to clear ALL platforms from ${selectedTools.size} selected meToCo?`)) {
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
delete yamlData.tools[index].platforms;
|
delete yamlData.meToCo[index].platforms;
|
||||||
});
|
});
|
||||||
showMessage(`Cleared platforms from ${selectedTools.size} tools`);
|
showMessage(`Cleared platforms from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Related concepts operations
|
// Related concepts operations
|
||||||
function bulkAddRelatedConcepts() {
|
function bulkAddRelatedConcepts() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const concepts = prompt('Enter related concept names to add (comma-separated):');
|
const concepts = prompt('Enter related concept names to add (comma-separated):');
|
||||||
if (concepts) {
|
if (concepts) {
|
||||||
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
|
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
tool.related_concepts = [...new Set([...(tool.related_concepts || []), ...conceptList])];
|
tool.related_concepts = [...new Set([...(tool.related_concepts || []), ...conceptList])];
|
||||||
});
|
});
|
||||||
showMessage(`Added related concepts to ${selectedTools.size} tools`);
|
showMessage(`Added related concepts to ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkRemoveRelatedConcepts() {
|
function bulkRemoveRelatedConcepts() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const concepts = prompt('Enter related concept names to remove (comma-separated):');
|
const concepts = prompt('Enter related concept names to remove (comma-separated):');
|
||||||
if (concepts) {
|
if (concepts) {
|
||||||
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
|
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tool.related_concepts) {
|
if (tool.related_concepts) {
|
||||||
tool.related_concepts = tool.related_concepts.filter(concept => !conceptList.includes(concept));
|
tool.related_concepts = tool.related_concepts.filter(concept => !conceptList.includes(concept));
|
||||||
if (tool.related_concepts.length === 0) delete tool.related_concepts;
|
if (tool.related_concepts.length === 0) delete tool.related_concepts;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Removed related concepts from ${selectedTools.size} tools`);
|
showMessage(`Removed related concepts from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkClearRelatedConcepts() {
|
function bulkClearRelatedConcepts() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
if (confirm(`Are you sure you want to clear ALL related concepts from ${selectedTools.size} selected tools?`)) {
|
if (confirm(`Are you sure you want to clear ALL related concepts from ${selectedTools.size} selected meToCo?`)) {
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
delete yamlData.tools[index].related_concepts;
|
delete yamlData.meToCo[index].related_concepts;
|
||||||
});
|
});
|
||||||
showMessage(`Cleared related concepts from ${selectedTools.size} tools`);
|
showMessage(`Cleared related concepts from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Related software operations
|
// NEW: Related software operations
|
||||||
function bulkAddRelatedSoftware() {
|
function bulkAddRelatedSoftware() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const software = prompt('Enter related software names to add (comma-separated):');
|
const software = prompt('Enter related software names to add (comma-separated):');
|
||||||
if (software) {
|
if (software) {
|
||||||
const softwareList = software.split(',').map(s => s.trim()).filter(s => s);
|
const softwareList = software.split(',').map(s => s.trim()).filter(s => s);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
tool.related_software = [...new Set([...(tool.related_software || []), ...softwareList])];
|
tool.related_software = [...new Set([...(tool.related_software || []), ...softwareList])];
|
||||||
});
|
});
|
||||||
showMessage(`Added related software to ${selectedTools.size} tools`);
|
showMessage(`Added related software to ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkRemoveRelatedSoftware() {
|
function bulkRemoveRelatedSoftware() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
const software = prompt('Enter related software names to remove (comma-separated):');
|
const software = prompt('Enter related software names to remove (comma-separated):');
|
||||||
if (software) {
|
if (software) {
|
||||||
const softwareList = software.split(',').map(s => s.trim()).filter(s => s);
|
const softwareList = software.split(',').map(s => s.trim()).filter(s => s);
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
if (tool.related_software) {
|
if (tool.related_software) {
|
||||||
tool.related_software = tool.related_software.filter(sw => !softwareList.includes(sw));
|
tool.related_software = tool.related_software.filter(sw => !softwareList.includes(sw));
|
||||||
if (tool.related_software.length === 0) delete tool.related_software;
|
if (tool.related_software.length === 0) delete tool.related_software;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showMessage(`Removed related software from ${selectedTools.size} tools`);
|
showMessage(`Removed related software from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkClearRelatedSoftware() {
|
function bulkClearRelatedSoftware() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
if (confirm(`Are you sure you want to clear ALL related software from ${selectedTools.size} selected tools?`)) {
|
if (confirm(`Are you sure you want to clear ALL related software from ${selectedTools.size} selected meToCo?`)) {
|
||||||
selectedTools.forEach(index => {
|
selectedTools.forEach(index => {
|
||||||
delete yamlData.tools[index].related_software;
|
delete yamlData.meToCo[index].related_software;
|
||||||
});
|
});
|
||||||
showMessage(`Cleared related software from ${selectedTools.size} tools`);
|
showMessage(`Cleared related software from ${selectedTools.size} meToCo`);
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bulkDelete() {
|
function bulkDelete() {
|
||||||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
|
||||||
if (confirm(`Are you sure you want to delete ${selectedTools.size} selected tools? This action cannot be undone!`)) {
|
if (confirm(`Are you sure you want to delete ${selectedTools.size} selected meToCo? This action cannot be undone!`)) {
|
||||||
const indicesToDelete = Array.from(selectedTools).sort((a, b) => b - a);
|
const indicesToDelete = Array.from(selectedTools).sort((a, b) => b - a);
|
||||||
indicesToDelete.forEach(index => yamlData.tools.splice(index, 1));
|
indicesToDelete.forEach(index => yamlData.meToCo.splice(index, 1));
|
||||||
selectedTools.clear();
|
selectedTools.clear();
|
||||||
showMessage(`Deleted ${indicesToDelete.length} tools successfully!`);
|
showMessage(`Deleted ${indicesToDelete.length} meToCo successfully!`);
|
||||||
updateStats();
|
updateStats();
|
||||||
renderBulkGrid();
|
renderBulkGrid();
|
||||||
updateKnowledgeToolSelect();
|
updateKnowledgeToolSelect();
|
||||||
@ -1743,9 +1743,9 @@
|
|||||||
const select = document.getElementById('knowledgeToolSelect');
|
const select = document.getElementById('knowledgeToolSelect');
|
||||||
select.innerHTML = '<option value="">Choose a tool or concept...</option>';
|
select.innerHTML = '<option value="">Choose a tool or concept...</option>';
|
||||||
|
|
||||||
if (!yamlData?.tools) return;
|
if (!yamlData?.meToCo) return;
|
||||||
|
|
||||||
yamlData.tools.forEach((tool, index) => {
|
yamlData.meToCo.forEach((tool, index) => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = index;
|
option.value = index;
|
||||||
option.textContent = `${tool.icon ? tool.icon + ' ' : ''}${tool.name} (${tool.type || 'software'})`;
|
option.textContent = `${tool.icon ? tool.icon + ' ' : ''}${tool.name} (${tool.type || 'software'})`;
|
||||||
@ -1762,7 +1762,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
const template = createCC24MarkdownTemplate(tool);
|
const template = createCC24MarkdownTemplate(tool);
|
||||||
|
|
||||||
document.getElementById('markdownContent').value = template;
|
document.getElementById('markdownContent').value = template;
|
||||||
@ -1781,10 +1781,10 @@ title: "${tool.name}"
|
|||||||
description: "${tool.description.split('\n')[0].trim()}"
|
description: "${tool.description.split('\n')[0].trim()}"
|
||||||
last_updated: ${new Date().toISOString().split('T')[0]}
|
last_updated: ${new Date().toISOString().split('T')[0]}
|
||||||
tool_name: "${tool.name}"
|
tool_name: "${tool.name}"
|
||||||
related_tools: ${JSON.stringify([...(tool.related_concepts || []), ...(tool.related_software || [])])}
|
related_meToCo: ${JSON.stringify([...(tool.related_concepts || []), ...(tool.related_software || [])])}
|
||||||
author: "CC24-Team"
|
author: "CC24-Team"
|
||||||
difficulty: "${tool.skillLevel || 'intermediate'}"
|
difficulty: "${tool.skillLevel || 'intermediate'}"
|
||||||
categories: ${tool.type === 'concept' ? '["concepts"]' : tool.type === 'method' ? '["methods"]' : '["tools"]'}
|
categories: ${tool.type === 'concept' ? '["concepts"]' : tool.type === 'method' ? '["methods"]' : '["meToCo"]'}
|
||||||
tags: ${tool.tags ? JSON.stringify(tool.tags) : '[]'}
|
tags: ${tool.tags ? JSON.stringify(tool.tags) : '[]'}
|
||||||
published: true
|
published: true
|
||||||
---
|
---
|
||||||
@ -1871,7 +1871,7 @@ TODO: Füge weitere nützliche Links und Ressourcen hinzu.
|
|||||||
const content = document.getElementById('markdownContent').value;
|
const content = document.getElementById('markdownContent').value;
|
||||||
const select = document.getElementById('knowledgeToolSelect');
|
const select = document.getElementById('knowledgeToolSelect');
|
||||||
const index = parseInt(select.value);
|
const index = parseInt(select.value);
|
||||||
const tool = yamlData.tools[index];
|
const tool = yamlData.meToCo[index];
|
||||||
|
|
||||||
const toolSlug = tool.name.toLowerCase()
|
const toolSlug = tool.name.toLowerCase()
|
||||||
.replace(/[^a-z0-9\s-]/g, '')
|
.replace(/[^a-z0-9\s-]/g, '')
|
||||||
@ -1909,13 +1909,13 @@ TODO: Füge weitere nützliche Links und Ressourcen hinzu.
|
|||||||
const validationResults = [];
|
const validationResults = [];
|
||||||
|
|
||||||
// Check required sections
|
// Check required sections
|
||||||
if (!yamlData.tools) validationResults.push('❌ Missing tools section');
|
if (!yamlData.meToCo) validationResults.push('❌ Missing meToCo section');
|
||||||
if (!yamlData.domains) validationResults.push('❌ Missing domains section');
|
if (!yamlData.domains) validationResults.push('❌ Missing domains section');
|
||||||
if (!yamlData.phases) validationResults.push('❌ Missing phases section');
|
if (!yamlData.phases) validationResults.push('❌ Missing phases section');
|
||||||
if (!yamlData.scenarios) validationResults.push('⚠️ Missing scenarios section (for reference)');
|
if (!yamlData.scenarios) validationResults.push('⚠️ Missing scenarios section (for reference)');
|
||||||
|
|
||||||
// Validate tools
|
// Validate meToCo
|
||||||
yamlData.tools?.forEach((tool, index) => {
|
yamlData.meToCo?.forEach((tool, index) => {
|
||||||
if (!tool.name) validationResults.push(`❌ Tool ${index + 1}: Missing name`);
|
if (!tool.name) validationResults.push(`❌ Tool ${index + 1}: Missing name`);
|
||||||
if (!tool.description) validationResults.push(`❌ Tool ${index + 1}: Missing description`);
|
if (!tool.description) validationResults.push(`❌ Tool ${index + 1}: Missing description`);
|
||||||
if (!tool.skillLevel) validationResults.push(`❌ Tool ${index + 1}: Missing skillLevel`);
|
if (!tool.skillLevel) validationResults.push(`❌ Tool ${index + 1}: Missing skillLevel`);
|
||||||
@ -1936,9 +1936,9 @@ TODO: Füge weitere nützliche Links und Ressourcen hinzu.
|
|||||||
// Validate related_software references
|
// Validate related_software references
|
||||||
if (tool.related_software && tool.related_software.length > 0) {
|
if (tool.related_software && tool.related_software.length > 0) {
|
||||||
tool.related_software.forEach(relatedName => {
|
tool.related_software.forEach(relatedName => {
|
||||||
const exists = yamlData.tools.some(t => t.name === relatedName);
|
const exists = yamlData.meToCo.some(t => t.name === relatedName);
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
validationResults.push(`⚠️ Tool ${index + 1}: Related software "${relatedName}" not found in tools`);
|
validationResults.push(`⚠️ Tool ${index + 1}: Related software "${relatedName}" not found in meToCo`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2000,7 +2000,7 @@ TODO: Füge weitere nützliche Links und Ressourcen hinzu.
|
|||||||
|
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = 'tools.json';
|
a.download = 'meToCo.json';
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "dfir-tools-hub",
|
"name": "forensicpathways",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { getToolsData } from '../utils/dataService.js';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
|
|
||||||
const data = await getToolsData();
|
const data = await getToolsData();
|
||||||
const tools = data.tools;
|
const meToCo = data.meToCo;
|
||||||
const phases = data.phases;
|
const phases = data.phases;
|
||||||
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||||
---
|
---
|
||||||
@ -176,7 +176,7 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
|||||||
<div class="micro-step" data-step="scenario">📋 Problemanalyse</div>
|
<div class="micro-step" data-step="scenario">📋 Problemanalyse</div>
|
||||||
<div class="micro-step" data-step="approach">🎯 Ermittlungsansatz</div>
|
<div class="micro-step" data-step="approach">🎯 Ermittlungsansatz</div>
|
||||||
<div class="micro-step" data-step="considerations">⚠️ Herausforderungen</div>
|
<div class="micro-step" data-step="considerations">⚠️ Herausforderungen</div>
|
||||||
<div class="micro-step" data-step="tools">🔧 Methoden</div>
|
<div class="micro-step" data-step="meToCo">🔧 Methoden</div>
|
||||||
<div class="micro-step" data-step="knowledge">📚 Evaluation</div>
|
<div class="micro-step" data-step="knowledge">📚 Evaluation</div>
|
||||||
<div class="micro-step" data-step="final">✅ Audit-Trail</div>
|
<div class="micro-step" data-step="final">✅ Audit-Trail</div>
|
||||||
</div>
|
</div>
|
||||||
@ -210,7 +210,7 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script define:vars={{ tools, phases, domainAgnosticSoftware }}>
|
<script define:vars={{ meToCo, phases, domainAgnosticSoftware }}>
|
||||||
|
|
||||||
class AIQueryInterface {
|
class AIQueryInterface {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -626,7 +626,7 @@ class AIQueryInterface {
|
|||||||
this.showElement(this.elements.microTaskProgress);
|
this.showElement(this.elements.microTaskProgress);
|
||||||
this.currentMicroTaskStep = 0;
|
this.currentMicroTaskStep = 0;
|
||||||
|
|
||||||
const steps = ['scenario', 'approach', 'considerations', 'tools', 'knowledge', 'final'];
|
const steps = ['scenario', 'approach', 'considerations', 'meToCo', 'knowledge', 'final'];
|
||||||
const stepElements = this.elements.microTaskProgress.querySelectorAll('.micro-step');
|
const stepElements = this.elements.microTaskProgress.querySelectorAll('.micro-step');
|
||||||
|
|
||||||
stepElements.forEach(step => {
|
stepElements.forEach(step => {
|
||||||
@ -681,7 +681,7 @@ class AIQueryInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
displayWorkflowResults(recommendation, originalQuery) {
|
displayWorkflowResults(recommendation, originalQuery) {
|
||||||
const toolsByPhase = {};
|
const meToCoByPhase = {};
|
||||||
const phaseOrder = phases.map(phase => phase.id);
|
const phaseOrder = phases.map(phase => phase.id);
|
||||||
const phaseNames = phases.reduce((acc, phase) => {
|
const phaseNames = phases.reduce((acc, phase) => {
|
||||||
acc[phase.id] = phase.name;
|
acc[phase.id] = phase.name;
|
||||||
@ -689,14 +689,14 @@ class AIQueryInterface {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
phaseOrder.forEach(phase => {
|
phaseOrder.forEach(phase => {
|
||||||
toolsByPhase[phase] = [];
|
meToCoByPhase[phase] = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
recommendation.recommended_tools?.forEach(recTool => {
|
recommendation.recommended_meToCo?.forEach(recTool => {
|
||||||
if (toolsByPhase[recTool.phase]) {
|
if (meToCoByPhase[recTool.phase]) {
|
||||||
const fullTool = tools.find(t => t.name === recTool.name);
|
const fullTool = meToCo.find(t => t.name === recTool.name);
|
||||||
if (fullTool) {
|
if (fullTool) {
|
||||||
toolsByPhase[recTool.phase].push({
|
meToCoByPhase[recTool.phase].push({
|
||||||
...fullTool,
|
...fullTool,
|
||||||
recommendation: recTool
|
recommendation: recTool
|
||||||
});
|
});
|
||||||
@ -709,7 +709,7 @@ class AIQueryInterface {
|
|||||||
${this.renderHeader('Untersuchungsansatz', originalQuery)}
|
${this.renderHeader('Untersuchungsansatz', originalQuery)}
|
||||||
${this.renderContextualAnalysis(recommendation, 'workflow')}
|
${this.renderContextualAnalysis(recommendation, 'workflow')}
|
||||||
${this.renderBackgroundKnowledge(recommendation.background_knowledge)}
|
${this.renderBackgroundKnowledge(recommendation.background_knowledge)}
|
||||||
${this.renderWorkflowPhases(toolsByPhase, phaseOrder, phaseNames)}
|
${this.renderWorkflowPhases(meToCoByPhase, phaseOrder, phaseNames)}
|
||||||
${this.renderWorkflowSuggestion(recommendation.workflow_suggestion)}
|
${this.renderWorkflowSuggestion(recommendation.workflow_suggestion)}
|
||||||
${this.renderAuditTrail(recommendation.auditTrail)}
|
${this.renderAuditTrail(recommendation.auditTrail)}
|
||||||
</div>
|
</div>
|
||||||
@ -724,7 +724,7 @@ class AIQueryInterface {
|
|||||||
${this.renderHeader('Handlungsempfehlung', originalQuery)}
|
${this.renderHeader('Handlungsempfehlung', originalQuery)}
|
||||||
${this.renderContextualAnalysis(recommendation, 'tool')}
|
${this.renderContextualAnalysis(recommendation, 'tool')}
|
||||||
${this.renderBackgroundKnowledge(recommendation.background_knowledge)}
|
${this.renderBackgroundKnowledge(recommendation.background_knowledge)}
|
||||||
${this.renderToolRecommendations(recommendation.recommended_tools)}
|
${this.renderToolRecommendations(recommendation.recommended_meToCo)}
|
||||||
${this.renderAdditionalConsiderations(recommendation.additional_considerations)}
|
${this.renderAdditionalConsiderations(recommendation.additional_considerations)}
|
||||||
${this.renderAuditTrail(recommendation.auditTrail)}
|
${this.renderAuditTrail(recommendation.auditTrail)}
|
||||||
</div>
|
</div>
|
||||||
@ -1076,9 +1076,9 @@ class AIQueryInterface {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderWorkflowPhases(toolsByPhase, phaseOrder, phaseNames) {
|
renderWorkflowPhases(meToCoByPhase, phaseOrder, phaseNames) {
|
||||||
return phaseOrder.map((phase, index) => {
|
return phaseOrder.map((phase, index) => {
|
||||||
const phaseTools = toolsByPhase[phase];
|
const phaseTools = meToCoByPhase[phase];
|
||||||
if (phaseTools.length === 0) return '';
|
if (phaseTools.length === 0) return '';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
@ -1087,7 +1087,7 @@ class AIQueryInterface {
|
|||||||
<div class="phase-number">${index + 1}</div>
|
<div class="phase-number">${index + 1}</div>
|
||||||
<div class="phase-info">
|
<div class="phase-info">
|
||||||
<h3 class="phase-title">${phaseNames[phase]}</h3>
|
<h3 class="phase-title">${phaseNames[phase]}</h3>
|
||||||
<div class="phase-tools">
|
<div class="phase-meToCo">
|
||||||
${phaseTools.map(tool => this.renderWorkflowTool(tool)).join('')}
|
${phaseTools.map(tool => this.renderWorkflowTool(tool)).join('')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1139,7 +1139,7 @@ class AIQueryInterface {
|
|||||||
return `
|
return `
|
||||||
<div class="tool-recommendations-grid" style="display: grid; gap: 1.5rem;">
|
<div class="tool-recommendations-grid" style="display: grid; gap: 1.5rem;">
|
||||||
${recommendedTools.map((toolRec, index) => {
|
${recommendedTools.map((toolRec, index) => {
|
||||||
const fullTool = tools.find(t => t.name === toolRec.name);
|
const fullTool = meToCo.find(t => t.name === toolRec.name);
|
||||||
if (!fullTool) return '';
|
if (!fullTool) return '';
|
||||||
|
|
||||||
return this.renderDetailedTool(fullTool, toolRec, index + 1);
|
return this.renderDetailedTool(fullTool, toolRec, index + 1);
|
||||||
|
@ -111,7 +111,7 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
|
|||||||
clickedChip.classList.add('active');
|
clickedChip.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
window.scrollToElementById('tools-grid');
|
window.scrollToElementById('meToCo-grid');
|
||||||
};
|
};
|
||||||
|
|
||||||
window.toggleAllScenarios = function() {
|
window.toggleAllScenarios = function() {
|
||||||
@ -159,7 +159,7 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
|
|||||||
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
|
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
|
||||||
if (gridToggle) {
|
if (gridToggle) {
|
||||||
gridToggle.click();
|
gridToggle.click();
|
||||||
setTimeout(() => window.scrollToElementById('tools-grid'), 200);
|
setTimeout(() => window.scrollToElementById('meToCo-grid'), 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -5,13 +5,13 @@ const data = await getToolsData();
|
|||||||
const domains = data.domains;
|
const domains = data.domains;
|
||||||
const phases = data.phases;
|
const phases = data.phases;
|
||||||
|
|
||||||
const skillLevels = [...new Set(data.tools.map(tool => tool.skillLevel))].filter(Boolean).sort();
|
const skillLevels = [...new Set(data.meToCo.map(tool => tool.skillLevel))].filter(Boolean).sort();
|
||||||
const platforms = [...new Set(data.tools.flatMap(tool => tool.platforms || []))].filter(Boolean).sort();
|
const platforms = [...new Set(data.meToCo.flatMap(tool => tool.platforms || []))].filter(Boolean).sort();
|
||||||
const licenses = [...new Set(data.tools.map(tool => tool.license))].filter(Boolean).sort();
|
const licenses = [...new Set(data.meToCo.map(tool => tool.license))].filter(Boolean).sort();
|
||||||
const toolTypes = [...new Set(data.tools.map(tool => tool.type))].filter(Boolean).sort();
|
const toolTypes = [...new Set(data.meToCo.map(tool => tool.type))].filter(Boolean).sort();
|
||||||
const accessTypes = [...new Set(data.tools.map(tool => tool.accessType))].filter(Boolean).sort();
|
const accessTypes = [...new Set(data.meToCo.map(tool => tool.accessType))].filter(Boolean).sort();
|
||||||
|
|
||||||
const tagFrequency = data.tools.reduce((acc: Record<string, number>, tool: any) => {
|
const tagFrequency = data.meToCo.reduce((acc: Record<string, number>, tool: any) => {
|
||||||
tool.tags.forEach((tag: string) => {
|
tool.tags.forEach((tag: string) => {
|
||||||
acc[tag] = (acc[tag] || 0) + 1;
|
acc[tag] = (acc[tag] || 0) + 1;
|
||||||
});
|
});
|
||||||
@ -282,8 +282,8 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script define:vars={{ toolsData: data.tools, tagFrequency, sortedTags }}>
|
<script define:vars={{ meToCoData: data.meToCo, tagFrequency, sortedTags }}>
|
||||||
window.toolsData = toolsData;
|
window.meToCoData = meToCoData;
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const elements = {
|
const elements = {
|
||||||
@ -488,7 +488,7 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateResultsCounter(count) {
|
function updateResultsCounter(count) {
|
||||||
const total = window.toolsData.length;
|
const total = window.meToCoData.length;
|
||||||
elements.resultsCounter.textContent = count === total
|
elements.resultsCounter.textContent = count === total
|
||||||
? `${total} Tools`
|
? `${total} Tools`
|
||||||
: `${count} von ${total} Tools`;
|
: `${count} von ${total} Tools`;
|
||||||
@ -508,7 +508,7 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
|
|
||||||
const activePhase = selectedPhaseFromSelect || selectedPhase;
|
const activePhase = selectedPhaseFromSelect || selectedPhase;
|
||||||
|
|
||||||
const filtered = window.toolsData.filter(tool => {
|
const filtered = window.meToCoData.filter(tool => {
|
||||||
if (searchTerm && !(
|
if (searchTerm && !(
|
||||||
tool.name.toLowerCase().includes(searchTerm) ||
|
tool.name.toLowerCase().includes(searchTerm) ||
|
||||||
tool.description.toLowerCase().includes(searchTerm) ||
|
tool.description.toLowerCase().includes(searchTerm) ||
|
||||||
@ -566,7 +566,7 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
|
|
||||||
updateResultsCounter(finalResults.length);
|
updateResultsCounter(finalResults.length);
|
||||||
|
|
||||||
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: finalResults }));
|
window.dispatchEvent(new CustomEvent('meToCoFiltered', { detail: finalResults }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetPrimaryFilters() {
|
function resetPrimaryFilters() {
|
||||||
@ -652,8 +652,8 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
window.dispatchEvent(new CustomEvent('viewChanged', { detail: view }));
|
window.dispatchEvent(new CustomEvent('viewChanged', { detail: view }));
|
||||||
|
|
||||||
if (view === 'hosted') {
|
if (view === 'hosted') {
|
||||||
const hosted = window.toolsData.filter(tool => isToolHosted(tool));
|
const hosted = window.meToCoData.filter(tool => isToolHosted(tool));
|
||||||
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: hosted }));
|
window.dispatchEvent(new CustomEvent('meToCoFiltered', { detail: hosted }));
|
||||||
} else {
|
} else {
|
||||||
filterTools();
|
filterTools();
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,12 @@ const data = await getToolsData();
|
|||||||
|
|
||||||
const domains = data.domains;
|
const domains = data.domains;
|
||||||
const phases = data.phases;
|
const phases = data.phases;
|
||||||
const tools = data.tools;
|
const meToCo = data.meToCo;
|
||||||
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||||
|
|
||||||
const domainAgnosticTools = domainAgnosticSoftware.map((section: any) => ({
|
const domainAgnosticTools = domainAgnosticSoftware.map((section: any) => ({
|
||||||
section,
|
section,
|
||||||
tools: tools.filter((tool: any) =>
|
meToCo: meToCo.filter((tool: any) =>
|
||||||
tool['domain-agnostic-software'] && tool['domain-agnostic-software'].includes(section.id)
|
tool['domain-agnostic-software'] && tool['domain-agnostic-software'].includes(section.id)
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
@ -20,7 +20,7 @@ const matrix: Record<string, Record<string, any[]>> = {};
|
|||||||
domains.forEach((domain: any) => {
|
domains.forEach((domain: any) => {
|
||||||
matrix[domain.id] = {};
|
matrix[domain.id] = {};
|
||||||
phases.forEach((phase: any) => {
|
phases.forEach((phase: any) => {
|
||||||
matrix[domain.id][phase.id] = tools.filter((tool: any) =>
|
matrix[domain.id][phase.id] = meToCo.filter((tool: any) =>
|
||||||
tool.type !== 'concept' &&
|
tool.type !== 'concept' &&
|
||||||
tool.domains && tool.domains.includes(domain.id) &&
|
tool.domains && tool.domains.includes(domain.id) &&
|
||||||
tool.phases && tool.phases.includes(phase.id)
|
tool.phases && tool.phases.includes(phase.id)
|
||||||
@ -42,7 +42,7 @@ domains.forEach((domain: any) => {
|
|||||||
<h3 class="m-0 text-lg text-accent">
|
<h3 class="m-0 text-lg text-accent">
|
||||||
{sectionData.section.name}
|
{sectionData.section.name}
|
||||||
<span id={`count-${sectionData.section.id}`} class="badge text-xs" style="background-color: var(--color-text-secondary); color: var(--color-bg); margin-left: 0.5rem;">
|
<span id={`count-${sectionData.section.id}`} class="badge text-xs" style="background-color: var(--color-text-secondary); color: var(--color-bg); margin-left: 0.5rem;">
|
||||||
{sectionData.tools.length}
|
{sectionData.meToCo.length}
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="collaboration-expand-icon">
|
<div class="collaboration-expand-icon">
|
||||||
@ -52,8 +52,8 @@ domains.forEach((domain: any) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collaboration-content hidden">
|
<div class="collaboration-content hidden">
|
||||||
<div class="collaboration-tools-compact" id={`domain-agnostic-tools-${sectionData.section.id}`}>
|
<div class="collaboration-meToCo-compact" id={`domain-agnostic-meToCo-${sectionData.section.id}`}>
|
||||||
{sectionData.tools.map((tool: any) => {
|
{sectionData.meToCo.map((tool: any) => {
|
||||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||||
tool.projectUrl !== null &&
|
tool.projectUrl !== null &&
|
||||||
tool.projectUrl !== "" &&
|
tool.projectUrl !== "" &&
|
||||||
@ -192,7 +192,7 @@ domains.forEach((domain: any) => {
|
|||||||
<div id="tool-links-secondary" class="flex flex-col gap-2"></div>
|
<div id="tool-links-secondary" class="flex flex-col gap-2"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script define:vars={{ toolsData: tools, domainAgnosticSoftware, domainAgnosticTools }}>
|
<script define:vars={{ meToCoData: meToCo, domainAgnosticSoftware, domainAgnosticTools }}>
|
||||||
function getSelectedPhase() {
|
function getSelectedPhase() {
|
||||||
const activePhaseChip = document.querySelector('.phase-chip.active');
|
const activePhaseChip = document.querySelector('.phase-chip.active');
|
||||||
return activePhaseChip ? activePhaseChip.getAttribute('data-phase') : '';
|
return activePhaseChip ? activePhaseChip.getAttribute('data-phase') : '';
|
||||||
@ -443,7 +443,7 @@ domains.forEach((domain: any) => {
|
|||||||
window.toggleDomainAgnosticSection = toggleDomainAgnosticSection;
|
window.toggleDomainAgnosticSection = toggleDomainAgnosticSection;
|
||||||
|
|
||||||
window.showToolDetails = function(toolName, modalType = 'primary') {
|
window.showToolDetails = function(toolName, modalType = 'primary') {
|
||||||
const tool = toolsData.find(t => t.name === toolName);
|
const tool = meToCoData.find(t => t.name === toolName);
|
||||||
if (!tool) {
|
if (!tool) {
|
||||||
console.error('Tool not found:', toolName);
|
console.error('Tool not found:', toolName);
|
||||||
return;
|
return;
|
||||||
@ -523,7 +523,7 @@ domains.forEach((domain: any) => {
|
|||||||
const relatedSoftware = tool.related_software || [];
|
const relatedSoftware = tool.related_software || [];
|
||||||
if (relatedConcepts.length > 0 && modalType === 'primary') {
|
if (relatedConcepts.length > 0 && modalType === 'primary') {
|
||||||
const conceptLinks = relatedConcepts.map(conceptName => {
|
const conceptLinks = relatedConcepts.map(conceptName => {
|
||||||
const concept = toolsData.find(t => t.name === conceptName && t.type === 'concept');
|
const concept = meToCoData.find(t => t.name === conceptName && t.type === 'concept');
|
||||||
if (concept) {
|
if (concept) {
|
||||||
return `<button class="tag cursor-pointer" style="background-color: var(--color-concept-bg); border: 1px solid var(--color-concept); color: var(--color-concept); transition: var(--transition-fast);"
|
return `<button class="tag cursor-pointer" style="background-color: var(--color-concept-bg); border: 1px solid var(--color-concept); color: var(--color-concept); transition: var(--transition-fast);"
|
||||||
onclick="event.stopPropagation(); window.showToolDetails('${conceptName}', 'secondary')"
|
onclick="event.stopPropagation(); window.showToolDetails('${conceptName}', 'secondary')"
|
||||||
@ -559,7 +559,7 @@ domains.forEach((domain: any) => {
|
|||||||
|
|
||||||
if (relatedSoftware.length > 0 && modalType === 'primary') {
|
if (relatedSoftware.length > 0 && modalType === 'primary') {
|
||||||
const softwareLinks = relatedSoftware.map(softwareName => {
|
const softwareLinks = relatedSoftware.map(softwareName => {
|
||||||
const software = toolsData.find(t => t.name === softwareName && (t.type === 'software' || t.type === 'method'));
|
const software = meToCoData.find(t => t.name === softwareName && (t.type === 'software' || t.type === 'method'));
|
||||||
if (software) {
|
if (software) {
|
||||||
const isHosted = window.isToolHosted(software);
|
const isHosted = window.isToolHosted(software);
|
||||||
const isSoftwareMethod = software.type === 'method';
|
const isSoftwareMethod = software.type === 'method';
|
||||||
@ -776,14 +776,14 @@ domains.forEach((domain: any) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('toolsFiltered', (event) => {
|
window.addEventListener('meToCoFiltered', (event) => {
|
||||||
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
||||||
if (currentView === 'matrix') {
|
if (currentView === 'matrix') {
|
||||||
setTimeout(updateMatrixHighlighting, 50);
|
setTimeout(updateMatrixHighlighting, 50);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('toolsFiltered', (event) => {
|
window.addEventListener('meToCoFiltered', (event) => {
|
||||||
const filtered = event.detail;
|
const filtered = event.detail;
|
||||||
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
||||||
|
|
||||||
@ -795,7 +795,7 @@ domains.forEach((domain: any) => {
|
|||||||
|
|
||||||
domainAgnosticSoftware.forEach(sectionData => {
|
domainAgnosticSoftware.forEach(sectionData => {
|
||||||
const section = document.getElementById(`domain-agnostic-section-${sectionData.id}`);
|
const section = document.getElementById(`domain-agnostic-section-${sectionData.id}`);
|
||||||
const container = document.getElementById(`domain-agnostic-tools-${sectionData.id}`);
|
const container = document.getElementById(`domain-agnostic-meToCo-${sectionData.id}`);
|
||||||
|
|
||||||
if (!section || !container) return;
|
if (!section || !container) return;
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,7 @@ const knowledgebaseCollection = defineCollection({
|
|||||||
last_updated: z.date(),
|
last_updated: z.date(),
|
||||||
|
|
||||||
tool_name: z.string().optional(),
|
tool_name: z.string().optional(),
|
||||||
related_tools: z.array(z.string()).default([]),
|
related_meToCo: z.array(z.string()).default([]),
|
||||||
|
|
||||||
author: z.string().default('Anon'),
|
author: z.string().default('Anon'),
|
||||||
difficulty: z.enum(['novice', 'beginner', 'intermediate', 'advanced', 'expert']).optional(),
|
difficulty: z.enum(['novice', 'beginner', 'intermediate', 'advanced', 'expert']).optional(),
|
||||||
|
@ -48,7 +48,7 @@ Open-Source Android Forensik bietet robuste Alternativen zu kommerziellen Lösun
|
|||||||
- USB 3.0+ Anschlüsse
|
- USB 3.0+ Anschlüsse
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
1. Download von [SANS SIFT Workstation](https://www.sans.org/tools/sift-workstation/)
|
1. Download von [SANS SIFT Workstation](https://www.sans.org/meToCo/sift-workstation/)
|
||||||
2. VMware/VirtualBox Import der OVA-Datei
|
2. VMware/VirtualBox Import der OVA-Datei
|
||||||
3. VM-Konfiguration: 8GB+ RAM, 4+ CPU-Kerne
|
3. VM-Konfiguration: 8GB+ RAM, 4+ CPU-Kerne
|
||||||
|
|
||||||
@ -78,10 +78,10 @@ wget https://github.com/sleuthkit/autopsy/releases/latest
|
|||||||
### Android Debug Bridge (ADB)
|
### Android Debug Bridge (ADB)
|
||||||
```bash
|
```bash
|
||||||
# Ubuntu/Debian
|
# Ubuntu/Debian
|
||||||
sudo apt install android-tools-adb android-tools-fastboot
|
sudo apt install android-meToCo-adb android-meToCo-fastboot
|
||||||
|
|
||||||
# Windows - Download Android Platform Tools
|
# Windows - Download Android Platform Tools
|
||||||
# https://developer.android.com/studio/releases/platform-tools
|
# https://developer.android.com/studio/releases/platform-meToCo
|
||||||
```
|
```
|
||||||
|
|
||||||
### ALEAPP Installation
|
### ALEAPP Installation
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
tools:
|
meToCo:
|
||||||
- name: Autopsy
|
- name: Autopsy
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
@ -1051,7 +1051,7 @@ tools:
|
|||||||
- path-finding
|
- path-finding
|
||||||
- bloom-visualization
|
- bloom-visualization
|
||||||
- apoc-procedures
|
- apoc-procedures
|
||||||
- import-tools
|
- import-meToCo
|
||||||
related_concepts:
|
related_concepts:
|
||||||
- SQL
|
- SQL
|
||||||
- name: QGIS
|
- name: QGIS
|
||||||
@ -1465,7 +1465,7 @@ tools:
|
|||||||
- batch-processing
|
- batch-processing
|
||||||
- cloud-artifacts
|
- cloud-artifacts
|
||||||
- community-driven
|
- community-driven
|
||||||
- free-tools
|
- free-meToCo
|
||||||
- scenario:windows-registry
|
- scenario:windows-registry
|
||||||
- scenario:persistence
|
- scenario:persistence
|
||||||
related_concepts:
|
related_concepts:
|
||||||
@ -1711,7 +1711,7 @@ tools:
|
|||||||
domain-agnostic-software: null
|
domain-agnostic-software: null
|
||||||
skillLevel: beginner
|
skillLevel: beginner
|
||||||
accessType: download
|
accessType: download
|
||||||
url: https://www.osforensics.com/tools/mount-disk-images.html
|
url: https://www.osforensics.com/meToCo/mount-disk-images.html
|
||||||
projectUrl: ''
|
projectUrl: ''
|
||||||
license: Proprietary
|
license: Proprietary
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
@ -2629,7 +2629,7 @@ tools:
|
|||||||
- network-forensics
|
- network-forensics
|
||||||
- mobile-forensics
|
- mobile-forensics
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://www.sans.org/tools/sift-workstation/
|
url: https://www.sans.org/meToCo/sift-workstation/
|
||||||
icon: 🧰
|
icon: 🧰
|
||||||
platforms:
|
platforms:
|
||||||
- OS
|
- OS
|
||||||
@ -3109,7 +3109,7 @@ tools:
|
|||||||
- examination
|
- examination
|
||||||
- analysis
|
- analysis
|
||||||
skillLevel: advanced
|
skillLevel: advanced
|
||||||
url: https://github.com/microsoft/ics-forensics-tools
|
url: https://github.com/microsoft/ics-forensics-meToCo
|
||||||
icon: 🏭
|
icon: 🏭
|
||||||
platforms:
|
platforms:
|
||||||
- Windows
|
- Windows
|
||||||
@ -3349,8 +3349,8 @@ tools:
|
|||||||
- name: Collabora Online
|
- name: Collabora Online
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
Web‑basierte Open‑Source‑Office‑Suite mit kompletter
|
Web-basierte Open-Source-Office-Suite mit kompletter
|
||||||
Dokumenten‑Bearbeitung und Live‑Kollaboration.
|
Dokumenten-Bearbeitung und Live-Kollaboration.
|
||||||
skillLevel: beginner
|
skillLevel: beginner
|
||||||
url: https://www.collaboraonline.com
|
url: https://www.collaboraonline.com
|
||||||
icon: 📝
|
icon: 📝
|
||||||
@ -3637,7 +3637,7 @@ tools:
|
|||||||
platforms:
|
platforms:
|
||||||
- Linux
|
- Linux
|
||||||
accessType: download
|
accessType: download
|
||||||
license: AGPL v3
|
license: AGPL v3
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Elasticsearch
|
- name: Elasticsearch
|
||||||
type: software
|
type: software
|
||||||
@ -3744,7 +3744,7 @@ tools:
|
|||||||
platforms:
|
platforms:
|
||||||
- Linux
|
- Linux
|
||||||
accessType: download
|
accessType: download
|
||||||
license: GPL v3
|
license: GPL v3
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: FOCA
|
- name: FOCA
|
||||||
type: software
|
type: software
|
||||||
@ -3778,7 +3778,7 @@ tools:
|
|||||||
platforms:
|
platforms:
|
||||||
- Windows
|
- Windows
|
||||||
accessType: download
|
accessType: download
|
||||||
license: GPL v3
|
license: GPL v3
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Firmware Analysis Toolkit
|
- name: Firmware Analysis Toolkit
|
||||||
type: software
|
type: software
|
||||||
@ -3817,8 +3817,8 @@ tools:
|
|||||||
- name: GCHQ Tools
|
- name: GCHQ Tools
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
Sammlung freier GCHQ‑Utilities, allen voran CyberChef\_– das
|
Sammlung freier GCHQ-Utilities, allen voran CyberChef\_– das
|
||||||
„Cyber‑Schweizer‑Taschenmesser“ für Encoding, Crypto und Datenanalyse.
|
„Cyber-Schweizer-Taschenmesser“ für Encoding, Crypto und Datenanalyse.
|
||||||
skillLevel: beginner
|
skillLevel: beginner
|
||||||
url: https://gchq.github.io/CyberChef
|
url: https://gchq.github.io/CyberChef
|
||||||
icon: 🥄
|
icon: 🥄
|
||||||
@ -3833,7 +3833,7 @@ tools:
|
|||||||
platforms:
|
platforms:
|
||||||
- Web
|
- Web
|
||||||
accessType: download
|
accessType: download
|
||||||
license: Apache 2.0
|
license: Apache 2.0
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Gephi
|
- name: Gephi
|
||||||
type: software
|
type: software
|
||||||
@ -4073,7 +4073,7 @@ tools:
|
|||||||
- Linux
|
- Linux
|
||||||
- Android
|
- Android
|
||||||
accessType: download
|
accessType: download
|
||||||
license: GPL v2
|
license: GPL v2
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Loki
|
- name: Loki
|
||||||
type: software
|
type: software
|
||||||
@ -4107,7 +4107,7 @@ tools:
|
|||||||
- Windows
|
- Windows
|
||||||
- Linux
|
- Linux
|
||||||
accessType: download
|
accessType: download
|
||||||
license: GPL v3
|
license: GPL v3
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Maltego
|
- name: Maltego
|
||||||
type: software
|
type: software
|
||||||
@ -4212,7 +4212,7 @@ tools:
|
|||||||
platforms:
|
platforms:
|
||||||
- Linux
|
- Linux
|
||||||
accessType: download
|
accessType: download
|
||||||
license: AGPL v3
|
license: AGPL v3
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Oxygen Forensic Suite
|
- name: Oxygen Forensic Suite
|
||||||
type: software
|
type: software
|
||||||
@ -4278,7 +4278,7 @@ tools:
|
|||||||
- Linux
|
- Linux
|
||||||
- macOS
|
- macOS
|
||||||
accessType: download
|
accessType: download
|
||||||
license: LGPL v3
|
license: LGPL v3
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Rekall
|
- name: Rekall
|
||||||
type: software
|
type: software
|
||||||
@ -4313,7 +4313,7 @@ tools:
|
|||||||
- Linux
|
- Linux
|
||||||
- macOS
|
- macOS
|
||||||
accessType: download
|
accessType: download
|
||||||
license: Apache 2.0
|
license: Apache 2.0
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Suricata
|
- name: Suricata
|
||||||
type: software
|
type: software
|
||||||
@ -4347,7 +4347,7 @@ tools:
|
|||||||
- Linux
|
- Linux
|
||||||
- Windows
|
- Windows
|
||||||
accessType: download
|
accessType: download
|
||||||
license: GPL v2
|
license: GPL v2
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: VirusTotal
|
- name: VirusTotal
|
||||||
type: software
|
type: software
|
||||||
@ -4443,7 +4443,7 @@ tools:
|
|||||||
platforms:
|
platforms:
|
||||||
- Windows
|
- Windows
|
||||||
accessType: download
|
accessType: download
|
||||||
license: GPL v2
|
license: GPL v2
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Zeek
|
- name: Zeek
|
||||||
type: software
|
type: software
|
||||||
@ -4511,7 +4511,7 @@ tools:
|
|||||||
- Windows
|
- Windows
|
||||||
- macOS
|
- macOS
|
||||||
accessType: download
|
accessType: download
|
||||||
license: Apache 2.0
|
license: Apache 2.0
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: tcpdump
|
- name: tcpdump
|
||||||
type: software
|
type: software
|
||||||
@ -4578,20 +4578,20 @@ tools:
|
|||||||
platforms:
|
platforms:
|
||||||
- Windows
|
- Windows
|
||||||
accessType: download
|
accessType: download
|
||||||
license: GPL v3
|
license: GPL v3
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: grep
|
- name: grep
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
Klassisches Unix-Werkzeug zur Zeilenfilterung, entwickelt 1973 von
|
Klassisches Unix-Werkzeug zur Zeilenfilterung, entwickelt 1973 von
|
||||||
Ken Thompson zur schnellen Suche nach regulären Ausdrücken in Dateien oder
|
Ken Thompson zur schnellen Suche nach regulären Ausdrücken in Dateien oder
|
||||||
Datenströmen. Der Name leitet sich vom ed‑Kommando „g/re/p“ ab und
|
Datenströmen. Der Name leitet sich vom ed-Kommando „g/re/p“ ab und
|
||||||
spiegelt die Funktionsweise wider: globaler Ausdruck, Zeile drucken.
|
spiegelt die Funktionsweise wider: globaler Ausdruck, Zeile drucken.
|
||||||
GNU grep unterstützt drei Regex‑Dialekte (BRE, ERE, PCRE),
|
GNU grep unterstützt drei Regex-Dialekte (BRE, ERE, PCRE),
|
||||||
Farb‑Highlighting und Zero‑Copy‑Block‑Pufferung für Höchstgeschwindigkeit
|
Farb-Highlighting und Zero-Copy-Block-Pufferung für Höchstgeschwindigkeit
|
||||||
auch in riesigen Logs. Dank seiner Pipe‑Kompatibilität ist grep
|
auch in riesigen Logs. Dank seiner Pipe-Kompatibilität ist grep
|
||||||
Grundbaustein unzähliger DFIR‑Einzeiler zum Parsen von Artefakten,
|
Grundbaustein unzähliger DFIR-Einzeiler zum Parsen von Artefakten,
|
||||||
Netzwerk‑Flows und Memory‑Dumps.
|
Netzwerk-Flows und Memory-Dumps.
|
||||||
skillLevel: beginner
|
skillLevel: beginner
|
||||||
url: https://www.gnu.org/software/grep/
|
url: https://www.gnu.org/software/grep/
|
||||||
icon: 🔍
|
icon: 🔍
|
||||||
@ -4618,12 +4618,12 @@ tools:
|
|||||||
- name: md5sum / sha256sum
|
- name: md5sum / sha256sum
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
Klassische Kommandozeilen‑Checksummengeneratoren aus den GNU coreutils,
|
Klassische Kommandozeilen-Checksummengeneratoren aus den GNU coreutils,
|
||||||
die schnell kryptografische Prüfsummen (MD5 bzw. SHA‑256) berechnen und
|
die schnell kryptografische Prüfsummen (MD5 bzw. SHA-256) berechnen und
|
||||||
verifizieren können. Ideal zur Integritätsprüfung von Forensik‑Images,
|
verifizieren können. Ideal zur Integritätsprüfung von Forensik-Images,
|
||||||
Log‑Archiven und Download‑Artefakten; auch als Stream‑Filter einsetzbar
|
Log-Archiven und Download-Artefakten; auch als Stream-Filter einsetzbar
|
||||||
(„cat image.dd | sha256sum“). Minimalistischer Funktionsumfang, aber auf
|
(„cat image.dd | sha256sum“). Minimalistischer Funktionsumfang, aber auf
|
||||||
nahezu jeder Unix‑ähnlichen Plattform vorinstalliert und damit überall
|
nahezu jeder Unix-ähnlichen Plattform vorinstalliert und damit überall
|
||||||
verfügbar.
|
verfügbar.
|
||||||
skillLevel: beginner
|
skillLevel: beginner
|
||||||
url: https://www.gnu.org/software/coreutils/
|
url: https://www.gnu.org/software/coreutils/
|
||||||
@ -4651,11 +4651,11 @@ tools:
|
|||||||
- name: hashdeep
|
- name: hashdeep
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
Multithread‑fähiges Audit‑Werkzeug, das Dateien rekursiv einliest und
|
Multithread-fähiges Audit-Werkzeug, das Dateien rekursiv einliest und
|
||||||
gleich mehrere Hash‑Algorithmen (MD5, SHA‑1, SHA‑256, Tiger u.a.) erzeugt.
|
gleich mehrere Hash-Algorithmen (MD5, SHA-1, SHA-256, Tiger u.a.) erzeugt.
|
||||||
Unterstützt „baseline auditing“ zum automatischen Wiedererkennen neuer
|
Unterstützt „baseline auditing“ zum automatischen Wiedererkennen neuer
|
||||||
oder veränderter Dateien und kann Hash‑Listen in NIST‑NSRL‑ oder
|
oder veränderter Dateien und kann Hash-Listen in NIST-NSRL- oder
|
||||||
CSV‑Format ausgeben – perfekt für große Datenträger‑Batches.
|
CSV-Format ausgeben – perfekt für große Datenträger-Batches.
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://github.com/jessek/hashdeep
|
url: https://github.com/jessek/hashdeep
|
||||||
icon: 🏷️
|
icon: 🏷️
|
||||||
@ -4682,9 +4682,9 @@ tools:
|
|||||||
description: >-
|
description: >-
|
||||||
Tool zur Berechnung „fuzzy hashes“ (Context Triggered Piecewise Hashing,
|
Tool zur Berechnung „fuzzy hashes“ (Context Triggered Piecewise Hashing,
|
||||||
CTPH) für Ähnlichkeitsanalysen von Dateien. Kann binäre
|
CTPH) für Ähnlichkeitsanalysen von Dateien. Kann binäre
|
||||||
Malware‑Varianten, Dokumentschablonen oder Logs selbst bei nur teilweisen
|
Malware-Varianten, Dokumentschablonen oder Logs selbst bei nur teilweisen
|
||||||
Überschneidungen matchen. Ausgabe‑Hashes lassen sich in Datenbanken oder
|
Überschneidungen matchen. Ausgabe-Hashes lassen sich in Datenbanken oder
|
||||||
YARA‑Regeln integrieren.
|
YARA-Regeln integrieren.
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://ssdeep-project.github.io/ssdeep/
|
url: https://ssdeep-project.github.io/ssdeep/
|
||||||
icon: 🔍
|
icon: 🔍
|
||||||
@ -4707,12 +4707,12 @@ tools:
|
|||||||
- name: hashcat
|
- name: hashcat
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
Höchstperformanter Passwort‑Recovery‑Accelerator, der CPUs, GPUs, FPGAs
|
Höchstperformanter Passwort-Recovery-Accelerator, der CPUs, GPUs, FPGAs
|
||||||
und sogar ASICs parallel nutzen kann. Unterstützt über 300 Hash‑Formate
|
und sogar ASICs parallel nutzen kann. Unterstützt über 300 Hash-Formate
|
||||||
(u.a. bcrypt, NTLM, Kerberos, WPA‑PMKID) und besitzt flexible Angriffsmodi
|
(u.a. bcrypt, NTLM, Kerberos, WPA-PMKID) und besitzt flexible Angriffsmodi
|
||||||
(Dictionary, Mask, Hybrid, Rule‑Based). Dank OpenCL‑Backend skaliert
|
(Dictionary, Mask, Hybrid, Rule-Based). Dank OpenCL-Backend skaliert
|
||||||
hashcat von Laptop‑GPU bis zu verteilten Clustern – unverzichtbar für
|
hashcat von Laptop-GPU bis zu verteilten Clustern – unverzichtbar für
|
||||||
Credential‑Audits und Incident‑Response.
|
Credential-Audits und Incident-Response.
|
||||||
skillLevel: advanced
|
skillLevel: advanced
|
||||||
url: https://hashcat.net/hashcat/
|
url: https://hashcat.net/hashcat/
|
||||||
icon: ⚡
|
icon: ⚡
|
||||||
@ -4737,10 +4737,10 @@ tools:
|
|||||||
- name: Linkurious
|
- name: Linkurious
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
Intuitive Graph‑Visualisierungs‑ und Analyseplattform, die
|
Intuitive Graph-Visualisierungs- und Analyseplattform, die
|
||||||
Graphdatenbanken wie Neo4j überlagert und Ermittler:innen dabei
|
Graphdatenbanken wie Neo4j überlagert und Ermittler:innen dabei
|
||||||
unterstützt, versteckte Beziehungen in Betrugs‑, AML‑ und
|
unterstützt, versteckte Beziehungen in Betrugs-, AML- und
|
||||||
Sicherheitsfällen aufzudecken. Leistungsstarke Filter, Geo‑ und
|
Sicherheitsfällen aufzudecken. Leistungsstarke Filter, Geo- und
|
||||||
Zeitachsenansichten sowie Automatisierungsvorlagen erleichtern das Hunten
|
Zeitachsenansichten sowie Automatisierungsvorlagen erleichtern das Hunten
|
||||||
komplexer Netzwerke und das Erstellen aussagekräftiger Beweisgrafiken.
|
komplexer Netzwerke und das Erstellen aussagekräftiger Beweisgrafiken.
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
@ -4767,7 +4767,7 @@ tools:
|
|||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Android Studio
|
- name: Android Studio
|
||||||
type: software
|
type: software
|
||||||
description: "Offizielle IDE von Google für die Android‑Entwicklung, basierend auf JetBrains\_IntelliJ\_IDEA. Integriert ein flexibles Gradle‑Buildsystem, Jetpack‑Compose‑Designer, Geräte‑Emulator, Profiler und Debugging‑Tools, um mobile Apps effizient zu erstellen, zu testen und zu analysieren – auch für forensische Re‑Packaging‑ oder Malware‑Analysen von APKs."
|
description: "Offizielle IDE von Google für die Android-Entwicklung, basierend auf JetBrains\_IntelliJ\_IDEA. Integriert ein flexibles Gradle-Buildsystem, Jetpack-Compose-Designer, Geräte-Emulator, Profiler und Debugging-Tools, um mobile Apps effizient zu erstellen, zu testen und zu analysieren – auch für forensische Re-Packaging- oder Malware-Analysen von APKs."
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://developer.android.com/studio
|
url: https://developer.android.com/studio
|
||||||
icon: 📱
|
icon: 📱
|
||||||
@ -4793,13 +4793,13 @@ tools:
|
|||||||
- name: ADB
|
- name: ADB
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
Vielseitiges Client‑Server‑Kommandozeilenwerkzeug, mit dem Fachleute über
|
Vielseitiges Client-Server-Kommandozeilenwerkzeug, mit dem Fachleute über
|
||||||
USB oder Netzwerk mit Android‑Geräten und Emulatoren kommunizieren: Pakete
|
USB oder Netzwerk mit Android-Geräten und Emulatoren kommunizieren: Pakete
|
||||||
installieren, Dateien extrahieren, Logcats erfassen, Ports weiterleiten
|
installieren, Dateien extrahieren, Logcats erfassen, Ports weiterleiten
|
||||||
und logische wie physische Datensicherungen erstellen. Unverzichtbar für
|
und logische wie physische Datensicherungen erstellen. Unverzichtbar für
|
||||||
mobile Forensik, Incident‑Response und App‑Entwicklung.
|
mobile Forensik, Incident-Response und App-Entwicklung.
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://developer.android.com/tools/adb
|
url: https://developer.android.com/meToCo/adb
|
||||||
icon: 🔌
|
icon: 🔌
|
||||||
domains:
|
domains:
|
||||||
- mobile-forensics
|
- mobile-forensics
|
||||||
@ -4822,7 +4822,7 @@ tools:
|
|||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: dc3dd
|
- name: dc3dd
|
||||||
type: software
|
type: software
|
||||||
description: "dc3dd ist eine forensisch erweiterte Variante des klassischen Unix‑Befehls dd. Sie unterstützt das gleichzeitige Berechnen mehrerer Hash‑Werte (MD5, SHA‑1, SHA‑256, SHA‑512) während der Image‑Erstellung, führt ein detailliertes Log mit Prüfsummen und Fehlerblöcken und zeigt einen Fortschrittsbalken an. Weitere Funktionen sind das Splitten großer Abbilder, das gezielte Überschreiben von Mustern zum sicheren Löschen\_und die Möglichkeit, Fehlersektoren separat auszugeben, was besonders bei defekten Medien hilfreich ist."
|
description: "dc3dd ist eine forensisch erweiterte Variante des klassischen Unix-Befehls dd. Sie unterstützt das gleichzeitige Berechnen mehrerer Hash-Werte (MD5, SHA-1, SHA-256, SHA-512) während der Image-Erstellung, führt ein detailliertes Log mit Prüfsummen und Fehlerblöcken und zeigt einen Fortschrittsbalken an. Weitere Funktionen sind das Splitten großer Abbilder, das gezielte Überschreiben von Mustern zum sicheren Löschen\_und die Möglichkeit, Fehlersektoren separat auszugeben, was besonders bei defekten Medien hilfreich ist."
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://sourceforge.net/projects/dc3dd/
|
url: https://sourceforge.net/projects/dc3dd/
|
||||||
icon: 💾
|
icon: 💾
|
||||||
@ -4848,15 +4848,15 @@ tools:
|
|||||||
- name: ddrescue
|
- name: ddrescue
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
GNU ddrescue ist ein spezialisiertes Datenrettungs‑Tool, das beschädigte
|
GNU ddrescue ist ein spezialisiertes Datenrettungs-Tool, das beschädigte
|
||||||
oder fehlerhafte Blockgeräte sektorweise ausliest und zunächst alle gut
|
oder fehlerhafte Blockgeräte sektorweise ausliest und zunächst alle gut
|
||||||
lesbaren Bereiche sichert, bevor es sich mehrfach an problematischen
|
lesbaren Bereiche sichert, bevor es sich mehrfach an problematischen
|
||||||
Sektoren versucht. Sein Map‑File protokolliert jeden Leseversuch, sodass
|
Sektoren versucht. Sein Map-File protokolliert jeden Leseversuch, sodass
|
||||||
abgebrochene Wiederherstellungen jederzeit fortgesetzt oder optimiert
|
abgebrochene Wiederherstellungen jederzeit fortgesetzt oder optimiert
|
||||||
werden können, ohne bereits gerettete Sektoren erneut zu belasten. Zudem
|
werden können, ohne bereits gerettete Sektoren erneut zu belasten. Zudem
|
||||||
bietet ddrescue einen Fill‑Modus, um schwierige Bereiche gezielt mit
|
bietet ddrescue einen Fill-Modus, um schwierige Bereiche gezielt mit
|
||||||
Platzhaltern zu überschreiben – nützlich für die Vorbereitung
|
Platzhaltern zu überschreiben – nützlich für die Vorbereitung
|
||||||
nachträglicher Dateisystem‑Reparaturen.
|
nachträglicher Dateisystem-Reparaturen.
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://www.gnu.org/software/ddrescue/
|
url: https://www.gnu.org/software/ddrescue/
|
||||||
icon: 🛟
|
icon: 🛟
|
||||||
@ -4881,7 +4881,7 @@ tools:
|
|||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: REMnux
|
- name: REMnux
|
||||||
type: software
|
type: software
|
||||||
description: "REMnux ist eine auf Ubuntu basierende Linux‑Distribution, die eine kuratierte Sammlung frei verfügbarer Tools für Malware‑Analyse, Reverse\_Engineering und Speicherforensik in einer sofort einsatzbereiten VM vereint. Sie erspart Analyst:innen das mühsame Zusammenstellen einzelner Werkzeuge, da Paketverwaltung, Desktop‑Integration und Lab‑Skripte bereits vorkonfiguriert sind. Zu den integrierten Highlights zählen unter anderem Ghidra, Radare2, Volatility, stegdetect sowie diverse Netzwerk‑ und Sandboxing‑Utilities, die regelmäßig in koordinierten Releases aktualisiert werden."
|
description: "REMnux ist eine auf Ubuntu basierende Linux-Distribution, die eine kuratierte Sammlung frei verfügbarer Tools für Malware-Analyse, Reverse\_Engineering und Speicherforensik in einer sofort einsatzbereiten VM vereint. Sie erspart Analyst:innen das mühsame Zusammenstellen einzelner Werkzeuge, da Paketverwaltung, Desktop-Integration und Lab-Skripte bereits vorkonfiguriert sind. Zu den integrierten Highlights zählen unter anderem Ghidra, Radare2, Volatility, stegdetect sowie diverse Netzwerk- und Sandboxing-Utilities, die regelmäßig in koordinierten Releases aktualisiert werden."
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://remnux.org/
|
url: https://remnux.org/
|
||||||
icon: 🐧
|
icon: 🐧
|
||||||
@ -4905,7 +4905,7 @@ tools:
|
|||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: Android Backup Extractor
|
- name: Android Backup Extractor
|
||||||
type: software
|
type: software
|
||||||
description: "Das bewährte Kommandozeilen‑Werkzeug zum Entpacken und Packen von Android‑Backups (.ab) aus »adb backup«\_– inklusive Entschlüsselung passwortgeschützter Archive dank integrierter AES‑Routinen. Ideal für die Extraktion forensisch relevanter App‑Daten ohne Root‑Zugriff und damit ein Must‑have für Mobile‑Analysen. :contentReference[oaicite:0]{index=0}"
|
description: "Das bewährte Kommandozeilen-Werkzeug zum Entpacken und Packen von Android-Backups (.ab) aus »adb backup«\_– inklusive Entschlüsselung passwortgeschützter Archive dank integrierter AES-Routinen. Ideal für die Extraktion forensisch relevanter App-Daten ohne Root-Zugriff und damit ein Must-have für Mobile-Analysen. :contentReference[oaicite:0]{index=0}"
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://github.com/nelenkov/android-backup-extractor
|
url: https://github.com/nelenkov/android-backup-extractor
|
||||||
icon: 📦
|
icon: 📦
|
||||||
@ -4932,7 +4932,7 @@ tools:
|
|||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: CAINE
|
- name: CAINE
|
||||||
type: software
|
type: software
|
||||||
description: "CAINE (Computer Aided INvestigative Environment) ist eine Ubuntu‑basierte Live‑Linux‑Distribution, die mehr als 150 Forensik‑Werkzeuge in einer schreibgeschützten Umgebung bündelt. Version 14.0 »Lightstream« (März\_2025) bringt Kernel\_6.8, UEFI‑Support und das UnBlock‑GUI zum gezielten Aufheben der Write‑Block‑Funktion für einzelne Devices. :contentReference[oaicite:2]{index=2}"
|
description: "CAINE (Computer Aided INvestigative Environment) ist eine Ubuntu-basierte Live-Linux-Distribution, die mehr als 150 Forensik-Werkzeuge in einer schreibgeschützten Umgebung bündelt. Version 14.0 »Lightstream« (März\_2025) bringt Kernel\_6.8, UEFI-Support und das UnBlock-GUI zum gezielten Aufheben der Write-Block-Funktion für einzelne Devices. :contentReference[oaicite:2]{index=2}"
|
||||||
skillLevel: beginner
|
skillLevel: beginner
|
||||||
url: https://www.caine-live.net/
|
url: https://www.caine-live.net/
|
||||||
icon: 💿
|
icon: 💿
|
||||||
@ -5002,7 +5002,7 @@ tools:
|
|||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: WiFi Pineapple
|
- name: WiFi Pineapple
|
||||||
type: software
|
type: software
|
||||||
description: "Die Hak5‑Hardware liefert mit der patentierten PineAP‑Suite einen vollwertigen Rogue‑Access‑Point: automatisches Recon, Handshake‑ und Enterprise‑Credential‑Capture, gezielte Client‑Filterung sowie Cloud\_C²‑Fernsteuerung\_– alles per browserbasierter GUI auf dem Mark\_VII oder Enterprise‑Modell. :contentReference[oaicite:6]{index=6}"
|
description: "Die Hak5-Hardware liefert mit der patentierten PineAP-Suite einen vollwertigen Rogue-Access-Point: automatisches Recon, Handshake- und Enterprise-Credential-Capture, gezielte Client-Filterung sowie Cloud\_C²-Fernsteuerung\_– alles per browserbasierter GUI auf dem Mark\_VII oder Enterprise-Modell. :contentReference[oaicite:6]{index=6}"
|
||||||
skillLevel: intermediate
|
skillLevel: intermediate
|
||||||
url: https://shop.hak5.org/products/wifi-pineapple
|
url: https://shop.hak5.org/products/wifi-pineapple
|
||||||
icon: 📡
|
icon: 📡
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
tools:
|
meToCo:
|
||||||
- name: Autopsy
|
- name: Autopsy
|
||||||
type: software
|
type: software
|
||||||
description: >-
|
description: >-
|
||||||
@ -113,7 +113,7 @@ phases:
|
|||||||
domain-agnostic-software:
|
domain-agnostic-software:
|
||||||
- id: collaboration-general
|
- id: collaboration-general
|
||||||
name: Übergreifend & Kollaboration
|
name: Übergreifend & Kollaboration
|
||||||
description: Cross-cutting tools and collaboration platforms
|
description: Cross-cutting meToCo and collaboration platforms
|
||||||
- id: specific-os
|
- id: specific-os
|
||||||
name: Betriebssysteme
|
name: Betriebssysteme
|
||||||
description: Operating Systems which focus on forensics
|
description: Operating Systems which focus on forensics
|
||||||
|
4
src/env.d.ts
vendored
4
src/env.d.ts
vendored
@ -7,7 +7,7 @@ declare global {
|
|||||||
toggleTheme: () => void;
|
toggleTheme: () => void;
|
||||||
getStoredTheme: () => string;
|
getStoredTheme: () => string;
|
||||||
};
|
};
|
||||||
toolsData: any[];
|
meToCoData: any[];
|
||||||
showToolDetails: (toolName: string, modalType?: string) => void;
|
showToolDetails: (toolName: string, modalType?: string) => void;
|
||||||
hideToolDetails: (modalType?: string) => void;
|
hideToolDetails: (modalType?: string) => void;
|
||||||
hideAllToolDetails: () => void;
|
hideAllToolDetails: () => void;
|
||||||
@ -19,7 +19,7 @@ declare global {
|
|||||||
clearAllFilters?: () => void;
|
clearAllFilters?: () => void;
|
||||||
|
|
||||||
createToolSlug: (toolName: string) => string;
|
createToolSlug: (toolName: string) => string;
|
||||||
findToolByIdentifier: (tools: any[], identifier: string) => any | undefined;
|
findToolByIdentifier: (meToCo: any[], identifier: string) => any | undefined;
|
||||||
isToolHosted: (tool: any) => boolean;
|
isToolHosted: (tool: any) => boolean;
|
||||||
|
|
||||||
checkClientAuth: (context?: string) => Promise<{authenticated: boolean; authRequired: boolean; expires?: string}>;
|
checkClientAuth: (context?: string) => Promise<{authenticated: boolean; authRequired: boolean; expires?: string}>;
|
||||||
|
@ -8,7 +8,7 @@ export interface Props {
|
|||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, description = 'ForensicPathways - A comprehensive directory of digital forensics and incident response tools' } = Astro.props;
|
const { title, description = 'ForensicPathways - A comprehensive directory of digital forensics and incident response meToCo' } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@ -34,10 +34,10 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
|
|||||||
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
||||||
}
|
}
|
||||||
|
|
||||||
function findToolByIdentifier(tools, identifier) {
|
function findToolByIdentifier(meToCo, identifier) {
|
||||||
if (!identifier || !Array.isArray(tools)) return undefined;
|
if (!identifier || !Array.isArray(meToCo)) return undefined;
|
||||||
|
|
||||||
return tools.find(tool =>
|
return meToCo.find(tool =>
|
||||||
tool.name === identifier ||
|
tool.name === identifier ||
|
||||||
createToolSlug(tool.name) === identifier.toLowerCase()
|
createToolSlug(tool.name) === identifier.toLowerCase()
|
||||||
);
|
);
|
||||||
@ -76,14 +76,14 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
|
|||||||
scrollToElement(element, options);
|
scrollToElement(element, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prioritizeSearchResults(tools, searchTerm) {
|
function prioritizeSearchResults(meToCo, searchTerm) {
|
||||||
if (!searchTerm || !searchTerm.trim()) {
|
if (!searchTerm || !searchTerm.trim()) {
|
||||||
return tools;
|
return meToCo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lowerSearchTerm = searchTerm.toLowerCase().trim();
|
const lowerSearchTerm = searchTerm.toLowerCase().trim();
|
||||||
|
|
||||||
return tools.sort((a, b) => {
|
return meToCo.sort((a, b) => {
|
||||||
const aTagsLower = (a.tags || []).map(tag => tag.toLowerCase());
|
const aTagsLower = (a.tags || []).map(tag => tag.toLowerCase());
|
||||||
const bTagsLower = (b.tags || []).map(tag => tag.toLowerCase());
|
const bTagsLower = (b.tags || []).map(tag => tag.toLowerCase());
|
||||||
|
|
||||||
|
@ -84,10 +84,10 @@ async function validateToolData(tool: any, action: string): Promise<{ valid: boo
|
|||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const existingData = { tools: [] };
|
const existingData = { meToCo: [] };
|
||||||
|
|
||||||
if (action === 'add') {
|
if (action === 'add') {
|
||||||
const existingNames = new Set(existingData.tools.map((t: any) => t.name.toLowerCase()));
|
const existingNames = new Set(existingData.meToCo.map((t: any) => t.name.toLowerCase()));
|
||||||
if (existingNames.has(tool.name.toLowerCase())) {
|
if (existingNames.has(tool.name.toLowerCase())) {
|
||||||
errors.push('A tool with this name already exists');
|
errors.push('A tool with this name already exists');
|
||||||
}
|
}
|
||||||
@ -102,10 +102,10 @@ async function validateToolData(tool: any, action: string): Promise<{ valid: boo
|
|||||||
}
|
}
|
||||||
} else if (tool.type === 'software') {
|
} else if (tool.type === 'software') {
|
||||||
if (!tool.platforms || tool.platforms.length === 0) {
|
if (!tool.platforms || tool.platforms.length === 0) {
|
||||||
errors.push('Software tools must specify at least one platform');
|
errors.push('Software meToCo must specify at least one platform');
|
||||||
}
|
}
|
||||||
if (!tool.license) {
|
if (!tool.license) {
|
||||||
errors.push('Software tools must specify a license');
|
errors.push('Software meToCo must specify a license');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ if (authResult instanceof Response) {
|
|||||||
const { authenticated, userEmail, userId } = authResult;
|
const { authenticated, userEmail, userId } = authResult;
|
||||||
|
|
||||||
const data = await getToolsData();
|
const data = await getToolsData();
|
||||||
const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
const sortedTools = data.meToCo.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Contribute Knowledge Base Article">
|
<BaseLayout title="Contribute Knowledge Base Article">
|
||||||
|
@ -17,7 +17,7 @@ const data = await getToolsData();
|
|||||||
const domains = data.domains;
|
const domains = data.domains;
|
||||||
const phases = data.phases;
|
const phases = data.phases;
|
||||||
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||||
const existingTools = data.tools;
|
const existingTools = data.meToCo;
|
||||||
|
|
||||||
const editToolName = Astro.url.searchParams.get('edit');
|
const editToolName = Astro.url.searchParams.get('edit');
|
||||||
const editTool = editToolName ? existingTools.find(tool => tool.name === editToolName) : null;
|
const editTool = editToolName ? existingTools.find(tool => tool.name === editToolName) : null;
|
||||||
|
@ -8,7 +8,7 @@ import TargetedScenarios from '../components/TargetedScenarios.astro';
|
|||||||
import { getToolsData } from '../utils/dataService.js';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
|
|
||||||
const data = await getToolsData();
|
const data = await getToolsData();
|
||||||
const tools = data.tools;
|
const meToCo = data.meToCo;
|
||||||
const phases = data.phases;
|
const phases = data.phases;
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ const phases = data.phases;
|
|||||||
|
|
||||||
<div class="nist-workflow">
|
<div class="nist-workflow">
|
||||||
{phases.map((phase: any, index: number) => {
|
{phases.map((phase: any, index: number) => {
|
||||||
const phaseTools = tools.filter((tool: any) =>
|
const phaseTools = meToCo.filter((tool: any) =>
|
||||||
tool.phases && tool.phases.includes(phase.id)
|
tool.phases && tool.phases.includes(phase.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -155,23 +155,23 @@ const phases = data.phases;
|
|||||||
|
|
||||||
<AIQueryInterface />
|
<AIQueryInterface />
|
||||||
|
|
||||||
<section id="tools-grid" style="padding-bottom: 2rem;">
|
<section id="meToCo-grid" style="padding-bottom: 2rem;">
|
||||||
<div class="grid-auto-fit" id="tools-container">
|
<div class="grid-auto-fit" id="meToCo-container">
|
||||||
{tools.map((tool: any) => (
|
{meToCo.map((tool: any) => (
|
||||||
<ToolCard tool={tool} />
|
<ToolCard tool={tool} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="no-results" style="display: none; text-align: center; padding: 4rem 0;">
|
<div id="no-results" style="display: none; text-align: center; padding: 4rem 0;">
|
||||||
<p class="text-muted" style="font-size: 1.125rem;">No tools found matching your criteria.</p>
|
<p class="text-muted" style="font-size: 1.125rem;">No meToCo found matching your criteria.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<ToolMatrix data={data} />
|
<ToolMatrix data={data} />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script define:vars={{ toolsData: data.tools, phases: data.phases }}>
|
<script define:vars={{ meToCoData: data.meToCo, phases: data.phases }}>
|
||||||
window.toolsData = toolsData;
|
window.meToCoData = meToCoData;
|
||||||
|
|
||||||
window.selectApproach = function(approach) {
|
window.selectApproach = function(approach) {
|
||||||
console.log(`Selected approach: ${approach}`);
|
console.log(`Selected approach: ${approach}`);
|
||||||
@ -221,19 +221,19 @@ const phases = data.phases;
|
|||||||
gridToggle.click();
|
gridToggle.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.scrollToElementById('tools-grid');
|
window.scrollToElementById('meToCo-grid');
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const toolsContainer = document.getElementById('tools-container');
|
const meToCoContainer = document.getElementById('meToCo-container');
|
||||||
const toolsGrid = document.getElementById('tools-grid');
|
const meToCoGrid = document.getElementById('meToCo-grid');
|
||||||
const matrixContainer = document.getElementById('matrix-container');
|
const matrixContainer = document.getElementById('matrix-container');
|
||||||
const aiInterface = document.getElementById('ai-interface');
|
const aiInterface = document.getElementById('ai-interface');
|
||||||
const filtersSection = document.getElementById('filters-section');
|
const filtersSection = document.getElementById('filters-section');
|
||||||
const noResults = document.getElementById('no-results');
|
const noResults = document.getElementById('no-results');
|
||||||
const aiQueryBtn = document.getElementById('ai-query-btn');
|
const aiQueryBtn = document.getElementById('ai-query-btn');
|
||||||
|
|
||||||
if (!toolsContainer || !toolsGrid || !matrixContainer || !noResults || !aiInterface || !filtersSection) {
|
if (!meToCoContainer || !meToCoGrid || !matrixContainer || !noResults || !aiInterface || !filtersSection) {
|
||||||
console.error('Required DOM elements not found');
|
console.error('Required DOM elements not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -250,7 +250,7 @@ const phases = data.phases;
|
|||||||
}
|
}
|
||||||
|
|
||||||
function switchToView(view) {
|
function switchToView(view) {
|
||||||
toolsGrid.style.display = 'none';
|
meToCoGrid.style.display = 'none';
|
||||||
matrixContainer.style.display = 'none';
|
matrixContainer.style.display = 'none';
|
||||||
aiInterface.style.display = 'none';
|
aiInterface.style.display = 'none';
|
||||||
filtersSection.style.display = 'none';
|
filtersSection.style.display = 'none';
|
||||||
@ -279,7 +279,7 @@ const phases = data.phases;
|
|||||||
showFilterControls();
|
showFilterControls();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
toolsGrid.style.display = 'block';
|
meToCoGrid.style.display = 'block';
|
||||||
filtersSection.style.display = 'block';
|
filtersSection.style.display = 'block';
|
||||||
showFilterControls();
|
showFilterControls();
|
||||||
break;
|
break;
|
||||||
@ -357,7 +357,7 @@ const phases = data.phases;
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
console.warn('Tool card not found in grid:', toolName);
|
console.warn('Tool card not found in grid:', toolName);
|
||||||
window.scrollToElementById('tools-grid');
|
window.scrollToElementById('meToCo-grid');
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}, 200);
|
}, 200);
|
||||||
@ -412,7 +412,7 @@ const phases = data.phases;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tool = window.findToolByIdentifier(window.toolsData, toolParam);
|
const tool = window.findToolByIdentifier(window.meToCoData, toolParam);
|
||||||
if (!tool) {
|
if (!tool) {
|
||||||
console.warn('Shared tool not found:', toolParam);
|
console.warn('Shared tool not found:', toolParam);
|
||||||
return;
|
return;
|
||||||
@ -442,7 +442,7 @@ const phases = data.phases;
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('toolsFiltered', (event) => {
|
window.addEventListener('meToCoFiltered', (event) => {
|
||||||
const filtered = event.detail;
|
const filtered = event.detail;
|
||||||
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const allKnowledgebaseEntries = await getCollection('knowledgebase', (entry) =>
|
|||||||
|
|
||||||
const knowledgebaseEntries = allKnowledgebaseEntries.map((entry) => {
|
const knowledgebaseEntries = allKnowledgebaseEntries.map((entry) => {
|
||||||
const associatedTool = entry.data.tool_name
|
const associatedTool = entry.data.tool_name
|
||||||
? data.tools.find((tool: any) => tool.name === entry.data.tool_name)
|
? data.meToCo.find((tool: any) => tool.name === entry.data.tool_name)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -25,7 +25,7 @@ const knowledgebaseEntries = allKnowledgebaseEntries.map((entry) => {
|
|||||||
tags: entry.data.tags || [],
|
tags: entry.data.tags || [],
|
||||||
|
|
||||||
tool_name: entry.data.tool_name,
|
tool_name: entry.data.tool_name,
|
||||||
related_tools: entry.data.related_tools || [],
|
related_meToCo: entry.data.related_meToCo || [],
|
||||||
associatedTool,
|
associatedTool,
|
||||||
|
|
||||||
name: entry.data.title,
|
name: entry.data.title,
|
||||||
@ -41,7 +41,7 @@ const knowledgebaseEntries = allKnowledgebaseEntries.map((entry) => {
|
|||||||
knowledgebaseEntries.sort((a: any, b: any) => a.title.localeCompare(b.title));
|
knowledgebaseEntries.sort((a: any, b: any) => a.title.localeCompare(b.title));
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Knowledgebase" description="Extended documentation and insights for DFIR tools">
|
<BaseLayout title="Knowledgebase" description="Extended documentation and insights for DFIR meToCo">
|
||||||
<section class="section-padding">
|
<section class="section-padding">
|
||||||
<div class="text-center mb-8 p-8 bg-secondary rounded-lg border">
|
<div class="text-center mb-8 p-8 bg-secondary rounded-lg border">
|
||||||
<h1 class="mb-4 text-2xl text-primary">Knowledgebase</h1>
|
<h1 class="mb-4 text-2xl text-primary">Knowledgebase</h1>
|
||||||
|
@ -25,12 +25,12 @@ const { Content } = await entry.render();
|
|||||||
const data = await getToolsData();
|
const data = await getToolsData();
|
||||||
|
|
||||||
const primaryTool = entry.data.tool_name
|
const primaryTool = entry.data.tool_name
|
||||||
? data.tools.find((t: any) => t.name === entry.data.tool_name)
|
? data.meToCo.find((t: any) => t.name === entry.data.tool_name)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const relatedTools = entry.data.related_tools
|
const relatedTools = entry.data.related_meToCo
|
||||||
? entry.data.related_tools.map((toolName: string) =>
|
? entry.data.related_meToCo.map((toolName: string) =>
|
||||||
data.tools.find((t: any) => t.name === toolName)
|
data.meToCo.find((t: any) => t.name === toolName)
|
||||||
).filter(Boolean)
|
).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { getToolsData } from '../utils/dataService.js';
|
|||||||
|
|
||||||
const data = await getToolsData();
|
const data = await getToolsData();
|
||||||
|
|
||||||
const hostedServices = data.tools.filter((tool: any) => {
|
const hostedServices = data.meToCo.filter((tool: any) => {
|
||||||
return tool.projectUrl !== undefined &&
|
return tool.projectUrl !== undefined &&
|
||||||
tool.projectUrl !== null &&
|
tool.projectUrl !== null &&
|
||||||
tool.projectUrl !== "" &&
|
tool.projectUrl !== "" &&
|
||||||
|
@ -883,7 +883,7 @@ input[type="checkbox"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Collaboration Tools - Consolidated */
|
/* Collaboration Tools - Consolidated */
|
||||||
.collaboration-tools-compact {
|
.collaboration-meToCo-compact {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -1107,7 +1107,7 @@ input[type="checkbox"] {
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phase-tools {
|
.phase-meToCo {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
@ -2051,9 +2051,9 @@ footer {
|
|||||||
|
|
||||||
.phase-button { width: 100%; }
|
.phase-button { width: 100%; }
|
||||||
|
|
||||||
.phase-tools { grid-template-columns: 1fr; }
|
.phase-meToCo { grid-template-columns: 1fr; }
|
||||||
|
|
||||||
.collaboration-tools-compact {
|
.collaboration-meToCo-compact {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.collaboration-tool-compact {
|
.collaboration-tool-compact {
|
||||||
|
@ -2065,7 +2065,7 @@ input[type="checkbox"] {
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phase-tools {
|
.phase-meToCo {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
@ -3123,7 +3123,7 @@ footer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Collaboration Tools */
|
/* Collaboration Tools */
|
||||||
.collaboration-tools-compact {
|
.collaboration-meToCo-compact {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -3538,11 +3538,11 @@ footer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (width <= 480px) {
|
@media (width <= 480px) {
|
||||||
.phase-tools {
|
.phase-meToCo {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collaboration-tools-compact {
|
.collaboration-meToCo-compact {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,8 +127,8 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
|
|
||||||
console.log('[AI PIPELINE] Configuration loaded:', {
|
console.log('[AI PIPELINE] Configuration loaded:', {
|
||||||
embeddingCandidates: this.embeddingCandidates,
|
embeddingCandidates: this.embeddingCandidates,
|
||||||
embeddingSelection: `${this.embeddingSelectionLimit} tools, ${this.embeddingConceptsLimit} concepts`,
|
embeddingSelection: `${this.embeddingSelectionLimit} meToCo, ${this.embeddingConceptsLimit} concepts`,
|
||||||
noEmbeddingsLimits: `${this.noEmbeddingsToolLimit || 'unlimited'} tools, ${this.noEmbeddingsConceptLimit || 'unlimited'} concepts`,
|
noEmbeddingsLimits: `${this.noEmbeddingsToolLimit || 'unlimited'} meToCo, ${this.noEmbeddingsConceptLimit || 'unlimited'} concepts`,
|
||||||
auditEnabled: this.auditConfig.enabled
|
auditEnabled: this.auditConfig.enabled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -352,7 +352,7 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getIntelligentCandidates(userQuery: string, toolsData: any, mode: string) {
|
private async getIntelligentCandidates(userQuery: string, meToCoData: any, mode: string) {
|
||||||
let candidateTools: any[] = [];
|
let candidateTools: any[] = [];
|
||||||
let candidateConcepts: any[] = [];
|
let candidateConcepts: any[] = [];
|
||||||
let selectionMethod = 'unknown';
|
let selectionMethod = 'unknown';
|
||||||
@ -378,12 +378,12 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
|
|
||||||
console.log(`[AI PIPELINE] Embeddings found ${similarItems.length} similar items`);
|
console.log(`[AI PIPELINE] Embeddings found ${similarItems.length} similar items`);
|
||||||
|
|
||||||
const toolsMap = new Map<string, any>(toolsData.tools.map((tool: any) => [tool.name, tool]));
|
const meToCoMap = new Map<string, any>(meToCoData.meToCo.map((tool: any) => [tool.name, tool]));
|
||||||
const conceptsMap = new Map<string, any>(toolsData.concepts.map((concept: any) => [concept.name, concept]));
|
const conceptsMap = new Map<string, any>(meToCoData.concepts.map((concept: any) => [concept.name, concept]));
|
||||||
|
|
||||||
const similarTools = similarItems
|
const similarTools = similarItems
|
||||||
.filter((item): item is SimilarityResult => item.type === 'tool')
|
.filter((item): item is SimilarityResult => item.type === 'tool')
|
||||||
.map(item => toolsMap.get(item.name))
|
.map(item => meToCoMap.get(item.name))
|
||||||
.filter((tool): tool is any => tool !== undefined);
|
.filter((tool): tool is any => tool !== undefined);
|
||||||
|
|
||||||
const similarConcepts = similarItems
|
const similarConcepts = similarItems
|
||||||
@ -391,9 +391,9 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
.map(item => conceptsMap.get(item.name))
|
.map(item => conceptsMap.get(item.name))
|
||||||
.filter((concept): concept is any => concept !== undefined);
|
.filter((concept): concept is any => concept !== undefined);
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Similarity-ordered results: ${similarTools.length} tools, ${similarConcepts.length} concepts`);
|
console.log(`[AI PIPELINE] Similarity-ordered results: ${similarTools.length} meToCo, ${similarConcepts.length} concepts`);
|
||||||
|
|
||||||
const totalAvailableTools = toolsData.tools.length;
|
const totalAvailableTools = meToCoData.meToCo.length;
|
||||||
const reductionRatio = similarTools.length / totalAvailableTools;
|
const reductionRatio = similarTools.length / totalAvailableTools;
|
||||||
|
|
||||||
if (similarTools.length >= this.embeddingsMinTools && reductionRatio <= this.embeddingsMaxReductionRatio) {
|
if (similarTools.length >= this.embeddingsMinTools && reductionRatio <= this.embeddingsMaxReductionRatio) {
|
||||||
@ -401,15 +401,15 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
candidateConcepts = similarConcepts;
|
candidateConcepts = similarConcepts;
|
||||||
selectionMethod = 'embeddings_candidates';
|
selectionMethod = 'embeddings_candidates';
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Using embeddings filtering: ${totalAvailableTools} → ${similarTools.length} tools (${(reductionRatio * 100).toFixed(1)}% reduction)`);
|
console.log(`[AI PIPELINE] Using embeddings filtering: ${totalAvailableTools} → ${similarTools.length} meToCo (${(reductionRatio * 100).toFixed(1)}% reduction)`);
|
||||||
} else {
|
} else {
|
||||||
if (similarTools.length < this.embeddingsMinTools) {
|
if (similarTools.length < this.embeddingsMinTools) {
|
||||||
console.log(`[AI PIPELINE] Embeddings found too few tools (${similarTools.length} < ${this.embeddingsMinTools}), using full dataset`);
|
console.log(`[AI PIPELINE] Embeddings found too few meToCo (${similarTools.length} < ${this.embeddingsMinTools}), using full dataset`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`[AI PIPELINE] Embeddings didn't filter enough (${(reductionRatio * 100).toFixed(1)}% > ${(this.embeddingsMaxReductionRatio * 100).toFixed(1)}%), using full dataset`);
|
console.log(`[AI PIPELINE] Embeddings didn't filter enough (${(reductionRatio * 100).toFixed(1)}% > ${(this.embeddingsMaxReductionRatio * 100).toFixed(1)}%), using full dataset`);
|
||||||
}
|
}
|
||||||
candidateTools = toolsData.tools;
|
candidateTools = meToCoData.meToCo;
|
||||||
candidateConcepts = toolsData.concepts;
|
candidateConcepts = meToCoData.concepts;
|
||||||
selectionMethod = 'full_dataset';
|
selectionMethod = 'full_dataset';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +418,7 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
{ query: userQuery, threshold: this.similarityThreshold, candidates: this.embeddingCandidates },
|
{ query: userQuery, threshold: this.similarityThreshold, candidates: this.embeddingCandidates },
|
||||||
{
|
{
|
||||||
candidatesFound: similarItems.length,
|
candidatesFound: similarItems.length,
|
||||||
toolsInOrder: similarTools.slice(0, 3).map((t: any) => t.name),
|
meToCoInOrder: similarTools.slice(0, 3).map((t: any) => t.name),
|
||||||
conceptsInOrder: similarConcepts.slice(0, 3).map((c: any) => c.name),
|
conceptsInOrder: similarConcepts.slice(0, 3).map((c: any) => c.name),
|
||||||
reductionRatio: reductionRatio,
|
reductionRatio: reductionRatio,
|
||||||
usingEmbeddings: selectionMethod === 'embeddings_candidates',
|
usingEmbeddings: selectionMethod === 'embeddings_candidates',
|
||||||
@ -437,20 +437,20 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`[AI PIPELINE] Embeddings disabled or not ready, using full dataset`);
|
console.log(`[AI PIPELINE] Embeddings disabled or not ready, using full dataset`);
|
||||||
candidateTools = toolsData.tools;
|
candidateTools = meToCoData.meToCo;
|
||||||
candidateConcepts = toolsData.concepts;
|
candidateConcepts = meToCoData.concepts;
|
||||||
selectionMethod = 'full_dataset';
|
selectionMethod = 'full_dataset';
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] AI will analyze ${candidateTools.length} candidate tools (method: ${selectionMethod})`);
|
console.log(`[AI PIPELINE] AI will analyze ${candidateTools.length} candidate meToCo (method: ${selectionMethod})`);
|
||||||
const finalSelection = await this.aiSelectionWithFullData(userQuery, candidateTools, candidateConcepts, mode, selectionMethod);
|
const finalSelection = await this.aiSelectionWithFullData(userQuery, candidateTools, candidateConcepts, mode, selectionMethod);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tools: finalSelection.selectedTools,
|
meToCo: finalSelection.selectedTools,
|
||||||
concepts: finalSelection.selectedConcepts,
|
concepts: finalSelection.selectedConcepts,
|
||||||
domains: toolsData.domains,
|
domains: meToCoData.domains,
|
||||||
phases: toolsData.phases,
|
phases: meToCoData.phases,
|
||||||
'domain-agnostic-software': toolsData['domain-agnostic-software']
|
'domain-agnostic-software': meToCoData['domain-agnostic-software']
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +463,7 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
) {
|
) {
|
||||||
const selectionStart = Date.now();
|
const selectionStart = Date.now();
|
||||||
|
|
||||||
const toolsWithFullData = candidateTools.map((tool: any) => ({
|
const meToCoWithFullData = candidateTools.map((tool: any) => ({
|
||||||
name: tool.name,
|
name: tool.name,
|
||||||
type: tool.type,
|
type: tool.type,
|
||||||
description: tool.description,
|
description: tool.description,
|
||||||
@ -492,14 +492,14 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
related_software: concept.related_software || []
|
related_software: concept.related_software || []
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let toolsToSend: any[];
|
let meToCoToSend: any[];
|
||||||
let conceptsToSend: any[];
|
let conceptsToSend: any[];
|
||||||
|
|
||||||
if (selectionMethod === 'embeddings_candidates') {
|
if (selectionMethod === 'embeddings_candidates') {
|
||||||
toolsToSend = toolsWithFullData.slice(0, this.embeddingSelectionLimit);
|
meToCoToSend = meToCoWithFullData.slice(0, this.embeddingSelectionLimit);
|
||||||
conceptsToSend = conceptsWithFullData.slice(0, this.embeddingConceptsLimit);
|
conceptsToSend = conceptsWithFullData.slice(0, this.embeddingConceptsLimit);
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Embeddings enabled: sending top ${toolsToSend.length} similarity-ordered tools`);
|
console.log(`[AI PIPELINE] Embeddings enabled: sending top ${meToCoToSend.length} similarity-ordered meToCo`);
|
||||||
} else {
|
} else {
|
||||||
const maxTools = this.noEmbeddingsToolLimit > 0 ?
|
const maxTools = this.noEmbeddingsToolLimit > 0 ?
|
||||||
Math.min(this.noEmbeddingsToolLimit, candidateTools.length) :
|
Math.min(this.noEmbeddingsToolLimit, candidateTools.length) :
|
||||||
@ -509,23 +509,23 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
Math.min(this.noEmbeddingsConceptLimit, candidateConcepts.length) :
|
Math.min(this.noEmbeddingsConceptLimit, candidateConcepts.length) :
|
||||||
candidateConcepts.length;
|
candidateConcepts.length;
|
||||||
|
|
||||||
toolsToSend = toolsWithFullData.slice(0, maxTools);
|
meToCoToSend = meToCoWithFullData.slice(0, maxTools);
|
||||||
conceptsToSend = conceptsWithFullData.slice(0, maxConcepts);
|
conceptsToSend = conceptsWithFullData.slice(0, maxConcepts);
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Embeddings disabled: sending ${toolsToSend.length}/${candidateTools.length} tools (limit: ${this.noEmbeddingsToolLimit || 'none'})`);
|
console.log(`[AI PIPELINE] Embeddings disabled: sending ${meToCoToSend.length}/${candidateTools.length} meToCo (limit: ${this.noEmbeddingsToolLimit || 'none'})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.maxSelectedItems);
|
const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.maxSelectedItems);
|
||||||
const prompt = `${basePrompt}
|
const prompt = `${basePrompt}
|
||||||
|
|
||||||
VERFÜGBARE TOOLS (mit vollständigen Daten):
|
VERFÜGBARE TOOLS (mit vollständigen Daten):
|
||||||
${JSON.stringify(toolsToSend, null, 2)}
|
${JSON.stringify(meToCoToSend, null, 2)}
|
||||||
|
|
||||||
VERFÜGBARE KONZEPTE (mit vollständigen Daten):
|
VERFÜGBARE KONZEPTE (mit vollständigen Daten):
|
||||||
${JSON.stringify(conceptsToSend, null, 2)}`;
|
${JSON.stringify(conceptsToSend, null, 2)}`;
|
||||||
|
|
||||||
const estimatedTokens = this.estimateTokens(prompt);
|
const estimatedTokens = this.estimateTokens(prompt);
|
||||||
console.log(`[AI PIPELINE] Method: ${selectionMethod}, Tools: ${toolsToSend.length}, Estimated tokens: ~${estimatedTokens}`);
|
console.log(`[AI PIPELINE] Method: ${selectionMethod}, Tools: ${meToCoToSend.length}, Estimated tokens: ~${estimatedTokens}`);
|
||||||
|
|
||||||
if (estimatedTokens > 35000) {
|
if (estimatedTokens > 35000) {
|
||||||
console.warn(`[AI PIPELINE] WARNING: Prompt tokens (${estimatedTokens}) may exceed model limits`);
|
console.warn(`[AI PIPELINE] WARNING: Prompt tokens (${estimatedTokens}) may exceed model limits`);
|
||||||
@ -545,7 +545,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
{ error: 'Invalid JSON structure', response: response.slice(0, 200) },
|
{ error: 'Invalid JSON structure', response: response.slice(0, 200) },
|
||||||
10,
|
10,
|
||||||
selectionStart,
|
selectionStart,
|
||||||
{ aiModel: this.config.model, selectionMethod, tokensSent: estimatedTokens, toolsSent: toolsToSend.length }
|
{ aiModel: this.config.model, selectionMethod, tokensSent: estimatedTokens, meToCoSent: meToCoToSend.length }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,11 +554,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
|
|
||||||
const totalSelected = result.selectedTools.length + result.selectedConcepts.length;
|
const totalSelected = result.selectedTools.length + result.selectedConcepts.length;
|
||||||
if (totalSelected === 0) {
|
if (totalSelected === 0) {
|
||||||
console.error('[AI PIPELINE] AI selection returned no tools');
|
console.error('[AI PIPELINE] AI selection returned no meToCo');
|
||||||
throw new Error('AI selection returned empty selection');
|
throw new Error('AI selection returned empty selection');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] AI selected: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts from ${toolsToSend.length} candidates`);
|
console.log(`[AI PIPELINE] AI selected: ${result.selectedTools.length} meToCo, ${result.selectedConcepts.length} concepts from ${meToCoToSend.length} candidates`);
|
||||||
|
|
||||||
const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
|
const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
|
||||||
const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
|
const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
|
||||||
@ -573,11 +573,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
selectedConceptCount: result.selectedConcepts.length,
|
selectedConceptCount: result.selectedConcepts.length,
|
||||||
reasoning: result.reasoning?.slice(0, 200) + '...',
|
reasoning: result.reasoning?.slice(0, 200) + '...',
|
||||||
finalToolNames: selectedTools.map(t => t.name),
|
finalToolNames: selectedTools.map(t => t.name),
|
||||||
selectionEfficiency: `${toolsToSend.length} → ${result.selectedTools.length}`
|
selectionEfficiency: `${meToCoToSend.length} → ${result.selectedTools.length}`
|
||||||
},
|
},
|
||||||
confidence,
|
confidence,
|
||||||
selectionStart,
|
selectionStart,
|
||||||
{ aiModel: this.config.model, selectionMethod, promptTokens: estimatedTokens, toolsSent: toolsToSend.length }
|
{ aiModel: this.config.model, selectionMethod, promptTokens: estimatedTokens, meToCoSent: meToCoToSend.length }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,7 +710,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async selectToolsForPhase(context: AnalysisContext, phase: any): Promise<MicroTaskResult> {
|
private async selectToolsForPhase(context: AnalysisContext, phase: any): Promise<MicroTaskResult> {
|
||||||
const phaseTools = context.filteredData.tools.filter((tool: any) =>
|
const phaseTools = context.filteredData.meToCo.filter((tool: any) =>
|
||||||
tool.phases && tool.phases.includes(phase.id)
|
tool.phases && tool.phases.includes(phase.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -901,8 +901,8 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
console.log(`[AI PIPELINE] Starting ${mode} query processing with context continuity and audit trail`);
|
console.log(`[AI PIPELINE] Starting ${mode} query processing with context continuity and audit trail`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const toolsData = await getCompressedToolsDataForAI();
|
const meToCoData = await getCompressedToolsDataForAI();
|
||||||
const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode);
|
const filteredData = await this.getIntelligentCandidates(userQuery, meToCoData, mode);
|
||||||
|
|
||||||
const context: AnalysisContext = {
|
const context: AnalysisContext = {
|
||||||
userQuery,
|
userQuery,
|
||||||
@ -917,11 +917,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
|
|
||||||
this.mergeTemporaryAuditEntries(context);
|
this.mergeTemporaryAuditEntries(context);
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Starting micro-tasks with ${filteredData.tools.length} tools visible`);
|
console.log(`[AI PIPELINE] Starting micro-tasks with ${filteredData.meToCo.length} meToCo visible`);
|
||||||
|
|
||||||
this.addAuditEntry(context, 'initialization', 'pipeline-start',
|
this.addAuditEntry(context, 'initialization', 'pipeline-start',
|
||||||
{ userQuery, mode, toolsDataLoaded: !!toolsData },
|
{ userQuery, mode, meToCoDataLoaded: !!meToCoData },
|
||||||
{ candidateTools: filteredData.tools.length, candidateConcepts: filteredData.concepts.length },
|
{ candidateTools: filteredData.meToCo.length, candidateConcepts: filteredData.concepts.length },
|
||||||
90,
|
90,
|
||||||
startTime,
|
startTime,
|
||||||
{ auditEnabled: this.auditConfig.enabled }
|
{ auditEnabled: this.auditConfig.enabled }
|
||||||
@ -946,14 +946,14 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
|
|
||||||
// Task 4: Tool Selection/Evaluation (mode-dependent)
|
// Task 4: Tool Selection/Evaluation (mode-dependent)
|
||||||
if (mode === 'workflow') {
|
if (mode === 'workflow') {
|
||||||
const phases = toolsData.phases || [];
|
const phases = meToCoData.phases || [];
|
||||||
for (const phase of phases) {
|
for (const phase of phases) {
|
||||||
const toolSelectionResult = await this.selectToolsForPhase(context, phase);
|
const toolSelectionResult = await this.selectToolsForPhase(context, phase);
|
||||||
if (toolSelectionResult.success) completedTasks++; else failedTasks++;
|
if (toolSelectionResult.success) completedTasks++; else failedTasks++;
|
||||||
await this.delay(this.microTaskDelay);
|
await this.delay(this.microTaskDelay);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const topTools = filteredData.tools.slice(0, 3);
|
const topTools = filteredData.meToCo.slice(0, 3);
|
||||||
for (let i = 0; i < topTools.length; i++) {
|
for (let i = 0; i < topTools.length; i++) {
|
||||||
const evaluationResult = await this.evaluateSpecificTool(context, topTools[i], i + 1);
|
const evaluationResult = await this.evaluateSpecificTool(context, topTools[i], i + 1);
|
||||||
if (evaluationResult.success) completedTasks++; else failedTasks++;
|
if (evaluationResult.success) completedTasks++; else failedTasks++;
|
||||||
@ -980,7 +980,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
|
|
||||||
const processingStats = {
|
const processingStats = {
|
||||||
embeddingsUsed: embeddingsService.isEnabled(),
|
embeddingsUsed: embeddingsService.isEnabled(),
|
||||||
candidatesFromEmbeddings: filteredData.tools.length,
|
candidatesFromEmbeddings: filteredData.meToCo.length,
|
||||||
finalSelectedItems: (context.selectedTools?.length || 0) +
|
finalSelectedItems: (context.selectedTools?.length || 0) +
|
||||||
(context.backgroundKnowledge?.length || 0),
|
(context.backgroundKnowledge?.length || 0),
|
||||||
processingTimeMs: Date.now() - startTime,
|
processingTimeMs: Date.now() - startTime,
|
||||||
@ -990,7 +990,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
};
|
};
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Completed: ${completedTasks} tasks, Failed: ${failedTasks} tasks`);
|
console.log(`[AI PIPELINE] Completed: ${completedTasks} tasks, Failed: ${failedTasks} tasks`);
|
||||||
console.log(`[AI PIPELINE] Unique tools selected: ${context.seenToolNames.size}`);
|
console.log(`[AI PIPELINE] Unique meToCo selected: ${context.seenToolNames.size}`);
|
||||||
console.log(`[AI PIPELINE] Audit trail entries: ${context.auditTrail.length}`);
|
console.log(`[AI PIPELINE] Audit trail entries: ${context.auditTrail.length}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -1027,7 +1027,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
if (isWorkflow) {
|
if (isWorkflow) {
|
||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
recommended_tools: context.selectedTools?.map(st => ({
|
recommended_meToCo: context.selectedTools?.map(st => ({
|
||||||
name: st.tool.name,
|
name: st.tool.name,
|
||||||
phase: st.phase,
|
phase: st.phase,
|
||||||
priority: st.priority,
|
priority: st.priority,
|
||||||
@ -1038,7 +1038,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
recommended_tools: context.selectedTools?.map(st => ({
|
recommended_meToCo: context.selectedTools?.map(st => ({
|
||||||
name: st.tool.name,
|
name: st.tool.name,
|
||||||
rank: st.tool.evaluation?.rank || 1,
|
rank: st.tool.evaluation?.rank || 1,
|
||||||
suitability_score: st.priority,
|
suitability_score: st.priority,
|
||||||
|
@ -26,7 +26,7 @@ const ToolSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ToolsDataSchema = z.object({
|
const ToolsDataSchema = z.object({
|
||||||
tools: z.array(ToolSchema),
|
meToCo: z.array(ToolSchema),
|
||||||
domains: z.array(z.object({
|
domains: z.array(z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
@ -36,7 +36,7 @@ const ToolsDataSchema = z.object({
|
|||||||
id: z.string(),
|
id: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
typical_tools: z.array(z.string()).optional().default([]),
|
typical_meToCo: z.array(z.string()).optional().default([]),
|
||||||
key_activities: z.array(z.string()).optional().default([])
|
key_activities: z.array(z.string()).optional().default([])
|
||||||
})),
|
})),
|
||||||
'domain-agnostic-software': z.array(z.object({
|
'domain-agnostic-software': z.array(z.object({
|
||||||
@ -63,7 +63,7 @@ const ToolsDataSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
interface ToolsData {
|
interface ToolsData {
|
||||||
tools: any[];
|
meToCo: any[];
|
||||||
domains: any[];
|
domains: any[];
|
||||||
phases: any[];
|
phases: any[];
|
||||||
'domain-agnostic-software': any[];
|
'domain-agnostic-software': any[];
|
||||||
@ -72,7 +72,7 @@ interface ToolsData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface EnhancedCompressedToolsData {
|
interface EnhancedCompressedToolsData {
|
||||||
tools: any[];
|
meToCo: any[];
|
||||||
concepts: any[];
|
concepts: any[];
|
||||||
domains: any[];
|
domains: any[];
|
||||||
phases: any[];
|
phases: any[];
|
||||||
@ -134,7 +134,7 @@ async function loadRawData(): Promise<ToolsData> {
|
|||||||
cachedData.skill_levels = {
|
cachedData.skill_levels = {
|
||||||
novice: "Minimal technical background required, guided interfaces",
|
novice: "Minimal technical background required, guided interfaces",
|
||||||
beginner: "Basic IT knowledge, some command-line familiarity helpful",
|
beginner: "Basic IT knowledge, some command-line familiarity helpful",
|
||||||
intermediate: "Solid technical foundation, comfortable with various tools",
|
intermediate: "Solid technical foundation, comfortable with various meToCo",
|
||||||
advanced: "Extensive experience, deep technical understanding required",
|
advanced: "Extensive experience, deep technical understanding required",
|
||||||
expert: "Specialist knowledge, cutting-edge techniques and complex scenarios"
|
expert: "Specialist knowledge, cutting-edge techniques and complex scenarios"
|
||||||
};
|
};
|
||||||
@ -159,11 +159,11 @@ export async function getToolsData(): Promise<ToolsData> {
|
|||||||
const seed = getDailySeed();
|
const seed = getDailySeed();
|
||||||
const randomFn = seededRandom(seed);
|
const randomFn = seededRandom(seed);
|
||||||
|
|
||||||
const randomizedTools = shuffleArray(rawData.tools, randomFn);
|
const randomizedTools = shuffleArray(rawData.meToCo, randomFn);
|
||||||
|
|
||||||
cachedRandomizedData = {
|
cachedRandomizedData = {
|
||||||
...rawData,
|
...rawData,
|
||||||
tools: randomizedTools
|
meToCo: randomizedTools
|
||||||
};
|
};
|
||||||
|
|
||||||
lastRandomizationDate = today;
|
lastRandomizationDate = today;
|
||||||
@ -177,7 +177,7 @@ export async function getCompressedToolsDataForAI(): Promise<EnhancedCompressedT
|
|||||||
if (!cachedCompressedData) {
|
if (!cachedCompressedData) {
|
||||||
const data = await getToolsData();
|
const data = await getToolsData();
|
||||||
|
|
||||||
const compressedTools = data.tools
|
const compressedTools = data.meToCo
|
||||||
.filter(tool => tool.type !== 'concept')
|
.filter(tool => tool.type !== 'concept')
|
||||||
.map(tool => {
|
.map(tool => {
|
||||||
const { projectUrl, statusUrl, ...compressedTool } = tool;
|
const { projectUrl, statusUrl, ...compressedTool } = tool;
|
||||||
@ -196,7 +196,7 @@ export async function getCompressedToolsDataForAI(): Promise<EnhancedCompressedT
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const concepts = data.tools
|
const concepts = data.meToCo
|
||||||
.filter(tool => tool.type === 'concept')
|
.filter(tool => tool.type === 'concept')
|
||||||
.map(concept => {
|
.map(concept => {
|
||||||
const { projectUrl, statusUrl, platforms, accessType, license, ...compressedConcept } = concept;
|
const { projectUrl, statusUrl, platforms, accessType, license, ...compressedConcept } = concept;
|
||||||
@ -210,7 +210,7 @@ export async function getCompressedToolsDataForAI(): Promise<EnhancedCompressedT
|
|||||||
});
|
});
|
||||||
|
|
||||||
cachedCompressedData = {
|
cachedCompressedData = {
|
||||||
tools: compressedTools,
|
meToCo: compressedTools,
|
||||||
concepts: concepts,
|
concepts: concepts,
|
||||||
domains: data.domains,
|
domains: data.domains,
|
||||||
phases: data.phases,
|
phases: data.phases,
|
||||||
|
@ -73,8 +73,8 @@ class EmbeddingsService {
|
|||||||
// Create data directory if it doesn't exist
|
// Create data directory if it doesn't exist
|
||||||
await fs.mkdir(path.dirname(this.embeddingsPath), { recursive: true });
|
await fs.mkdir(path.dirname(this.embeddingsPath), { recursive: true });
|
||||||
|
|
||||||
const toolsData = await getCompressedToolsDataForAI();
|
const meToCoData = await getCompressedToolsDataForAI();
|
||||||
const currentDataHash = this.hashData(toolsData);
|
const currentDataHash = this.hashData(meToCoData);
|
||||||
|
|
||||||
// Try to load existing embeddings
|
// Try to load existing embeddings
|
||||||
const existingEmbeddings = await this.loadEmbeddings();
|
const existingEmbeddings = await this.loadEmbeddings();
|
||||||
@ -84,7 +84,7 @@ class EmbeddingsService {
|
|||||||
this.embeddings = existingEmbeddings.embeddings;
|
this.embeddings = existingEmbeddings.embeddings;
|
||||||
} else {
|
} else {
|
||||||
console.log('[EMBEDDINGS] Generating new embeddings...');
|
console.log('[EMBEDDINGS] Generating new embeddings...');
|
||||||
await this.generateEmbeddings(toolsData, currentDataHash);
|
await this.generateEmbeddings(meToCoData, currentDataHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
@ -197,10 +197,10 @@ class EmbeddingsService {
|
|||||||
throw new Error('Unknown embeddings API response format');
|
throw new Error('Unknown embeddings API response format');
|
||||||
}
|
}
|
||||||
|
|
||||||
private async generateEmbeddings(toolsData: any, version: string): Promise<void> {
|
private async generateEmbeddings(meToCoData: any, version: string): Promise<void> {
|
||||||
const allItems = [
|
const allItems = [
|
||||||
...toolsData.tools.map((tool: any) => ({ ...tool, type: 'tool' })),
|
...meToCoData.meToCo.map((tool: any) => ({ ...tool, type: 'tool' })),
|
||||||
...toolsData.concepts.map((concept: any) => ({ ...concept, type: 'concept' }))
|
...meToCoData.concepts.map((concept: any) => ({ ...concept, type: 'concept' }))
|
||||||
];
|
];
|
||||||
|
|
||||||
const contents = allItems.map(item => this.createContentString(item));
|
const contents = allItems.map(item => this.createContentString(item));
|
||||||
|
@ -288,7 +288,7 @@ ${data.metadata.contact}
|
|||||||
|
|
||||||
### For Maintainers
|
### For Maintainers
|
||||||
1. Copy the YAML above
|
1. Copy the YAML above
|
||||||
2. Add to \`src/data/tools.yaml\` in the tools array
|
2. Add to \`src/data/tools.yaml\` in the meToCo array
|
||||||
3. Maintain alphabetical order
|
3. Maintain alphabetical order
|
||||||
4. Close this issue when done
|
4. Close this issue when done
|
||||||
|
|
||||||
|
@ -26,10 +26,10 @@ export function createToolSlug(toolName: string): string {
|
|||||||
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findToolByIdentifier(tools: Tool[], identifier: string): Tool | undefined {
|
export function findToolByIdentifier(meToCo: Tool[], identifier: string): Tool | undefined {
|
||||||
if (!identifier || !Array.isArray(tools)) return undefined;
|
if (!identifier || !Array.isArray(meToCo)) return undefined;
|
||||||
|
|
||||||
return tools.find(tool =>
|
return meToCo.find(tool =>
|
||||||
tool.name === identifier ||
|
tool.name === identifier ||
|
||||||
createToolSlug(tool.name) === identifier.toLowerCase()
|
createToolSlug(tool.name) === identifier.toLowerCase()
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user