Refactor API calls to use csrfFetch for enhanced security across multiple views, including loop-monitor, mcp-manager, memory, prompt-history, rules-manager, session-detail, and skills-manager. Additionally, add Phase 1 and Phase 2 documentation for session initialization and orchestration loop in the ccw-loop-b skill.

This commit is contained in:
catlog22
2026-02-07 10:54:12 +08:00
parent f7dfbc0512
commit 92b0d175a7
49 changed files with 2003 additions and 480 deletions

View File

@@ -132,23 +132,40 @@ export async function csrfValidation(ctx: CsrfMiddlewareContext): Promise<boolea
const headerToken = getHeaderValue(req.headers['x-csrf-token']);
const cookies = parseCookieHeader(getHeaderValue(req.headers.cookie));
const cookieToken = cookies['XSRF-TOKEN'];
let bodyToken: string | null = null;
if (!headerToken && !cookieToken) {
const body = await readJsonBody(req);
bodyToken = extractCsrfTokenFromBody(body);
}
const token = headerToken || bodyToken || cookieToken || null;
const sessionId = cookies.ccw_session_id;
if (!token || !sessionId) {
if (!sessionId) {
writeJson(res, 403, { error: 'CSRF validation failed' });
return false;
}
const tokenManager = getCsrfTokenManager();
const ok = tokenManager.validateToken(token, sessionId);
const validate = (token: string | null): boolean => {
if (!token) return false;
return tokenManager.validateToken(token, sessionId);
};
let ok = false;
if (headerToken) {
ok = validate(headerToken);
if (!ok && cookieToken && cookieToken !== headerToken) {
ok = validate(cookieToken);
}
} else if (cookieToken) {
ok = validate(cookieToken);
}
if (!ok) {
let bodyToken: string | null = null;
if (!cookieToken) {
const body = await readJsonBody(req);
bodyToken = extractCsrfTokenFromBody(body);
}
ok = validate(bodyToken);
}
if (!ok) {
writeJson(res, 403, { error: 'CSRF validation failed' });
return false;

View File

@@ -453,7 +453,7 @@ async function deleteExecution(executionId, sourceDir) {
basePath = isAbsolute ? sourceDir : projectPath + '/' + sourceDir;
}
const response = await fetch(`/api/cli/execution?path=${encodeURIComponent(basePath)}&id=${encodeURIComponent(executionId)}`, {
const response = await csrfFetch(`/api/cli/execution?path=${encodeURIComponent(basePath)}&id=${encodeURIComponent(executionId)}`, {
method: 'DELETE'
});

View File

@@ -1434,7 +1434,7 @@ async function submitHookWizard() {
const timeout = wizardConfig.timeout || 300;
try {
const configParams = JSON.stringify({ action: 'configure', threshold, timeout });
const response = await fetch('/api/tools/execute', {
const response = await csrfFetch('/api/tools/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tool: 'memory_queue', params: configParams })
@@ -1559,4 +1559,4 @@ function editTemplateAsNew(templateId) {
command: template.command,
args: template.args || []
});
}
}

View File

@@ -160,7 +160,7 @@ async function markFileAsUpdated() {
if (!selectedFile) return;
try {
var res = await fetch('/api/memory/claude/mark-updated', {
var res = await csrfFetch('/api/memory/claude/mark-updated', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -483,7 +483,7 @@ async function saveClaudeFile() {
var newContent = editor.value;
try {
var res = await fetch('/api/memory/claude/file', {
var res = await csrfFetch('/api/memory/claude/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -682,7 +682,7 @@ async function syncFileWithCLI() {
if (syncButton) syncButton.disabled = true;
try {
var response = await fetch('/api/memory/claude/sync', {
var response = await csrfFetch('/api/memory/claude/sync', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -846,7 +846,7 @@ async function createNewFile() {
}
try {
var res = await fetch('/api/memory/claude/create', {
var res = await csrfFetch('/api/memory/claude/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -885,7 +885,7 @@ async function confirmDeleteFile() {
if (!confirmed) return;
try {
var res = await fetch('/api/memory/claude/file?path=' + encodeURIComponent(selectedFile.path) + '&confirm=true', {
var res = await csrfFetch('/api/memory/claude/file?path=' + encodeURIComponent(selectedFile.path) + '&confirm=true', {
method: 'DELETE'
});
@@ -1083,7 +1083,7 @@ async function confirmBatchDeleteProject() {
);
try {
var res = await fetch('/api/memory/claude/batch-delete', {
var res = await csrfFetch('/api/memory/claude/batch-delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View File

@@ -677,7 +677,7 @@ async function loadFileBrowserDirectory(path) {
}
try {
var response = await fetch('/api/dialog/browse', {
var response = await csrfFetch('/api/dialog/browse', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: path, showHidden: fileBrowserState.showHidden })
@@ -2546,7 +2546,7 @@ async function runCcwUpgrade() {
showRefreshToast(t('ccw.upgradeStarting'), 'info');
try {
var response = await fetch('/api/ccw/upgrade', {
var response = await csrfFetch('/api/ccw/upgrade', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
@@ -2620,7 +2620,7 @@ async function executeCliFromDashboard() {
if (execBtn) execBtn.disabled = true;
try {
var response = await fetch('/api/cli/execute', {
var response = await csrfFetch('/api/cli/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -2851,7 +2851,7 @@ async function startCliInstall(toolName) {
}, 1000);
try {
var response = await fetch('/api/cli/install', {
var response = await csrfFetch('/api/cli/install', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tool: toolName })
@@ -2992,7 +2992,7 @@ async function startCliUninstall(toolName) {
}, 500);
try {
var response = await fetch('/api/cli/uninstall', {
var response = await csrfFetch('/api/cli/uninstall', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tool: toolName })
@@ -3241,7 +3241,7 @@ function initCodexLensConfigEvents(currentConfig) {
saveBtn.innerHTML = '<span class="animate-pulse">' + t('common.saving') + '</span>';
try {
var response = await fetch('/api/codexlens/config', {
var response = await csrfFetch('/api/codexlens/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ index_dir: newIndexDir })
@@ -3478,7 +3478,7 @@ async function downloadModel(profile) {
'</div>';
try {
var response = await fetch('/api/codexlens/models/download', {
var response = await csrfFetch('/api/codexlens/models/download', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ profile: profile })
@@ -3517,7 +3517,7 @@ async function deleteModel(profile) {
'</div>';
try {
var response = await fetch('/api/codexlens/models/delete', {
var response = await csrfFetch('/api/codexlens/models/delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ profile: profile })
@@ -3553,7 +3553,7 @@ async function cleanCurrentWorkspaceIndex() {
// Get current workspace path (projectPath is a global variable from state.js)
var workspacePath = projectPath;
var response = await fetch('/api/codexlens/clean', {
var response = await csrfFetch('/api/codexlens/clean', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: workspacePath })
@@ -3589,7 +3589,7 @@ async function cleanCodexLensIndexes() {
try {
showRefreshToast(t('codexlens.cleaning'), 'info');
var response = await fetch('/api/codexlens/clean', {
var response = await csrfFetch('/api/codexlens/clean', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ all: true })

View File

@@ -186,7 +186,7 @@ async function refreshWorkspaceIndexStatus(forceRefresh) {
} else {
// Fallback: direct fetch if preloadService not available
var path = encodeURIComponent(projectPath || '');
var response = await fetch('/api/codexlens/workspace-status?path=' + path);
var response = await csrfFetch('/api/codexlens/workspace-status?path=' + path);
if (!response.ok) throw new Error('HTTP ' + response.status);
freshData = await response.json();
}
@@ -341,8 +341,8 @@ async function showCodexLensConfigModal(forceRefresh) {
// Fetch current config and status in parallel
const [configResponse, statusResponse] = await Promise.all([
fetch('/api/codexlens/config'),
fetch('/api/codexlens/status')
csrfFetch('/api/codexlens/config'),
csrfFetch('/api/codexlens/status')
]);
config = await configResponse.json();
status = await statusResponse.json();
@@ -778,7 +778,7 @@ function initCodexLensConfigEvents(currentConfig) {
saveBtn.innerHTML = '<span class="animate-pulse">' + t('common.saving') + '</span>';
try {
var response = await fetch('/api/codexlens/config', {
var response = await csrfFetch('/api/codexlens/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -1147,7 +1147,7 @@ async function loadEnvVariables(forceRefresh) {
if (!forceRefresh && isCacheValid('env')) {
result = getCachedData('env');
} else {
var envResponse = await fetch('/api/codexlens/env');
var envResponse = await csrfFetch('/api/codexlens/env');
result = await envResponse.json();
if (result.success) {
setCacheData('env', result);
@@ -1643,7 +1643,7 @@ async function saveEnvVariables() {
});
try {
var response = await fetch('/api/codexlens/env', {
var response = await csrfFetch('/api/codexlens/env', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ env: env })
@@ -1678,7 +1678,7 @@ var cachedRerankerModels = { local: [], api: [], apiModels: [] };
*/
async function detectGpuSupport() {
try {
var response = await fetch('/api/codexlens/gpu/detect');
var response = await csrfFetch('/api/codexlens/gpu/detect');
var result = await response.json();
if (result.success) {
detectedGpuInfo = result;
@@ -1703,7 +1703,7 @@ async function loadSemanticDepsStatus(forceRefresh) {
if (!forceRefresh && isCacheValid('semanticStatus')) {
result = getCachedData('semanticStatus');
} else {
var response = await fetch('/api/codexlens/semantic/status');
var response = await csrfFetch('/api/codexlens/semantic/status');
result = await response.json();
setCacheData('semanticStatus', result);
}
@@ -1870,7 +1870,7 @@ function getSelectedGpuMode() {
*/
async function loadGpuDevices() {
try {
var response = await fetch('/api/codexlens/gpu/list');
var response = await csrfFetch('/api/codexlens/gpu/list');
var result = await response.json();
if (result.success && result.result) {
availableGpuDevices = result.result;
@@ -1949,7 +1949,7 @@ async function selectGpuDevice(deviceId) {
try {
showRefreshToast(t('codexlens.selectingGpu') || 'Selecting GPU...', 'info');
var response = await fetch('/api/codexlens/gpu/select', {
var response = await csrfFetch('/api/codexlens/gpu/select', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ device_id: deviceId })
@@ -1975,7 +1975,7 @@ async function resetGpuDevice() {
try {
showRefreshToast(t('codexlens.resettingGpu') || 'Resetting GPU selection...', 'info');
var response = await fetch('/api/codexlens/gpu/reset', {
var response = await csrfFetch('/api/codexlens/gpu/reset', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
@@ -2019,7 +2019,7 @@ async function installSemanticDepsWithGpu() {
'</div>';
try {
var response = await fetch('/api/codexlens/semantic/install', {
var response = await csrfFetch('/api/codexlens/semantic/install', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gpuMode: gpuMode })
@@ -2059,7 +2059,7 @@ async function loadSpladeStatus() {
if (!container) return;
try {
var response = await fetch('/api/codexlens/splade/status');
var response = await csrfFetch('/api/codexlens/splade/status');
var status = await response.json();
if (status.available) {
@@ -2112,7 +2112,7 @@ async function installSplade(gpu) {
if (window.lucide) lucide.createIcons();
try {
var response = await fetch('/api/codexlens/splade/install', {
var response = await csrfFetch('/api/codexlens/splade/install', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gpu: gpu })
@@ -2413,7 +2413,7 @@ async function reinstallFastEmbed(mode) {
'</div>';
try {
var response = await fetch('/api/codexlens/semantic/install', {
var response = await csrfFetch('/api/codexlens/semantic/install', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gpuMode: mode })
@@ -2455,7 +2455,7 @@ async function loadFastEmbedInstallStatus(forceRefresh) {
result = getCachedData('semanticStatus');
console.log('[CodexLens] Using cached semantic status');
} else {
var semanticResponse = await fetch('/api/codexlens/semantic/status');
var semanticResponse = await csrfFetch('/api/codexlens/semantic/status');
result = await semanticResponse.json();
setCacheData('semanticStatus', result);
}
@@ -2463,7 +2463,7 @@ async function loadFastEmbedInstallStatus(forceRefresh) {
// Load GPU list and LiteLLM status (not cached - less frequently used)
console.log('[CodexLens] Fetching GPU list and LiteLLM status...');
var [gpuResponse, litellmResponse] = await Promise.all([
fetch('/api/codexlens/gpu/list'),
csrfFetch('/api/codexlens/gpu/list'),
fetch('/api/litellm-api/ccw-litellm/status').catch(function() { return { ok: false }; })
]);
@@ -2551,7 +2551,7 @@ async function installFastEmbed() {
'</div>';
try {
var response = await fetch('/api/codexlens/semantic/install', {
var response = await csrfFetch('/api/codexlens/semantic/install', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gpuMode: selectedMode })
@@ -2604,8 +2604,8 @@ async function loadModelList(forceRefresh) {
} else {
// Fetch config and models in parallel
var [configResponse, modelsResponse] = await Promise.all([
fetch('/api/codexlens/config'),
fetch('/api/codexlens/models')
csrfFetch('/api/codexlens/config'),
csrfFetch('/api/codexlens/models')
]);
config = await configResponse.json();
result = await modelsResponse.json();
@@ -2854,7 +2854,7 @@ async function downloadModel(profile) {
'</div>';
try {
var response = await fetch('/api/codexlens/models/download', {
var response = await csrfFetch('/api/codexlens/models/download', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ profile: profile })
@@ -2899,7 +2899,7 @@ async function deleteModel(profile) {
'</div>';
try {
var response = await fetch('/api/codexlens/models/delete', {
var response = await csrfFetch('/api/codexlens/models/delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ profile: profile })
@@ -2948,7 +2948,7 @@ async function downloadCustomModel() {
input.value = '';
try {
var response = await fetch('/api/codexlens/models/download-custom', {
var response = await csrfFetch('/api/codexlens/models/download-custom', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model_name: modelName, model_type: 'embedding' })
@@ -2981,7 +2981,7 @@ async function deleteDiscoveredModel(cachePath) {
}
try {
var response = await fetch('/api/codexlens/models/delete-path', {
var response = await csrfFetch('/api/codexlens/models/delete-path', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cache_path: cachePath })
@@ -3044,8 +3044,8 @@ async function loadRerankerModelList(forceRefresh) {
} else {
// Fetch both config and models list in parallel
var [configResponse, modelsResponse] = await Promise.all([
fetch('/api/codexlens/reranker/config'),
fetch('/api/codexlens/reranker/models')
csrfFetch('/api/codexlens/reranker/config'),
csrfFetch('/api/codexlens/reranker/models')
]);
if (!configResponse.ok) {
@@ -3218,7 +3218,7 @@ async function downloadRerankerModel(profile) {
}
try {
var response = await fetch('/api/codexlens/reranker/models/download', {
var response = await csrfFetch('/api/codexlens/reranker/models/download', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ profile: profile })
@@ -3248,7 +3248,7 @@ async function deleteRerankerModel(profile) {
}
try {
var response = await fetch('/api/codexlens/reranker/models/delete', {
var response = await csrfFetch('/api/codexlens/reranker/models/delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ profile: profile })
@@ -3272,7 +3272,7 @@ async function deleteRerankerModel(profile) {
*/
async function updateRerankerBackend(backend) {
try {
var response = await fetch('/api/codexlens/reranker/config', {
var response = await csrfFetch('/api/codexlens/reranker/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ backend: backend })
@@ -3296,7 +3296,7 @@ async function updateRerankerBackend(backend) {
*/
async function selectRerankerModel(modelName) {
try {
var response = await fetch('/api/codexlens/reranker/config', {
var response = await csrfFetch('/api/codexlens/reranker/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model_name: modelName })
@@ -3321,7 +3321,7 @@ async function selectRerankerModel(modelName) {
async function switchToLocalReranker(modelName) {
try {
// First switch backend to fastembed
var backendResponse = await fetch('/api/codexlens/reranker/config', {
var backendResponse = await csrfFetch('/api/codexlens/reranker/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ backend: 'fastembed' })
@@ -3334,7 +3334,7 @@ async function switchToLocalReranker(modelName) {
}
// Then select the model
var modelResponse = await fetch('/api/codexlens/reranker/config', {
var modelResponse = await csrfFetch('/api/codexlens/reranker/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model_name: modelName })
@@ -3425,7 +3425,7 @@ async function loadGpuDevicesForModeSelector() {
if (!gpuSelect) return;
try {
var response = await fetch('/api/codexlens/gpu/list');
var response = await csrfFetch('/api/codexlens/gpu/list');
if (!response.ok) {
console.warn('[CodexLens] GPU list endpoint returned:', response.status);
gpuSelect.innerHTML = '<option value="auto">Auto</option>';
@@ -3483,7 +3483,7 @@ async function toggleModelModeLock() {
try {
// Save embedding backend preference
var embeddingBackend = mode === 'local' ? 'fastembed' : 'litellm';
await fetch('/api/codexlens/config', {
await csrfFetch('/api/codexlens/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -3494,7 +3494,7 @@ async function toggleModelModeLock() {
// Save reranker backend preference
var rerankerBackend = mode === 'local' ? 'fastembed' : 'litellm';
await fetch('/api/codexlens/reranker/config', {
await csrfFetch('/api/codexlens/reranker/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ backend: rerankerBackend })
@@ -3529,7 +3529,7 @@ async function initModelModeFromConfig() {
if (!modeSelect) return;
try {
var response = await fetch('/api/codexlens/config');
var response = await csrfFetch('/api/codexlens/config');
var config = await response.json();
var embeddingBackend = config.embedding_backend || 'fastembed';
@@ -3550,7 +3550,7 @@ async function updateSemanticStatusBadge() {
if (!badge) return;
try {
var response = await fetch('/api/codexlens/semantic/status');
var response = await csrfFetch('/api/codexlens/semantic/status');
var result = await response.json();
if (result.available) {
@@ -3609,7 +3609,7 @@ async function initCodexLensIndex(indexType, embeddingModel, embeddingBackend, m
// LiteLLM backend uses remote embeddings and does not require fastembed/ONNX deps.
if ((indexType === 'vector' || indexType === 'full') && embeddingBackend !== 'litellm') {
try {
var semanticResponse = await fetch('/api/codexlens/semantic/status');
var semanticResponse = await csrfFetch('/api/codexlens/semantic/status');
var semanticStatus = await semanticResponse.json();
if (!semanticStatus.available) {
@@ -3623,7 +3623,7 @@ async function initCodexLensIndex(indexType, embeddingModel, embeddingBackend, m
// Install semantic dependencies first
showRefreshToast(t('codexlens.installingDeps') || 'Installing semantic dependencies...', 'info');
try {
var installResponse = await csrfFetch('/api/codexlens/semantic/install', { method: 'POST' });
var installResponse = await csrfcsrfFetch('/api/codexlens/semantic/install', { method: 'POST' });
var installResult = await installResponse.json();
if (!installResult.success) {
@@ -3757,7 +3757,7 @@ async function startCodexLensIndexing(indexType, embeddingModel, embeddingBacken
try {
console.log('[CodexLens] Starting index for:', projectPath, 'type:', indexType, 'model:', embeddingModel, 'backend:', embeddingBackend, 'maxWorkers:', maxWorkers, 'incremental:', incremental);
var response = await fetch('/api/codexlens/init', {
var response = await csrfFetch('/api/codexlens/init', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: projectPath, indexType: indexType, embeddingModel: embeddingModel, embeddingBackend: embeddingBackend, maxWorkers: maxWorkers, incremental: incremental })
@@ -3879,7 +3879,7 @@ async function cancelCodexLensIndexing() {
}
try {
var response = await fetch('/api/codexlens/cancel', {
var response = await csrfFetch('/api/codexlens/cancel', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
@@ -4040,7 +4040,7 @@ async function startCodexLensInstallFallback() {
}, 1500);
try {
var response = await fetch('/api/codexlens/bootstrap', {
var response = await csrfFetch('/api/codexlens/bootstrap', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
@@ -4191,7 +4191,7 @@ async function startCodexLensUninstallFallback() {
}, 500);
try {
var response = await fetch('/api/codexlens/uninstall', {
var response = await csrfFetch('/api/codexlens/uninstall', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
@@ -4247,7 +4247,7 @@ async function cleanCurrentWorkspaceIndex() {
// Get current workspace path (projectPath is a global variable from state.js)
var workspacePath = projectPath;
var response = await fetch('/api/codexlens/clean', {
var response = await csrfFetch('/api/codexlens/clean', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: workspacePath })
@@ -4283,7 +4283,7 @@ async function cleanCodexLensIndexes() {
try {
showRefreshToast(t('codexlens.cleaning'), 'info');
var response = await fetch('/api/codexlens/clean', {
var response = await csrfFetch('/api/codexlens/clean', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ all: true })
@@ -4346,7 +4346,7 @@ async function renderCodexLensManager() {
// Fallback to legacy individual calls
console.log('[CodexLens] Fallback to legacy loadCodexLensStatus...');
await loadCodexLensStatus();
var response = await fetch('/api/codexlens/config');
var response = await csrfFetch('/api/codexlens/config');
config = await response.json();
}
@@ -4967,7 +4967,7 @@ window.runFtsIncrementalUpdate = async function runFtsIncrementalUpdate() {
try {
// Use index update endpoint for FTS incremental
var response = await fetch('/api/codexlens/init', {
var response = await csrfFetch('/api/codexlens/init', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -4998,7 +4998,7 @@ window.runVectorFullIndex = async function runVectorFullIndex() {
try {
// Fetch env settings to get the configured embedding model
var envResponse = await fetch('/api/codexlens/env');
var envResponse = await csrfFetch('/api/codexlens/env');
var envData = await envResponse.json();
var embeddingModel = envData.CODEXLENS_EMBEDDING_MODEL || envData.LITELLM_EMBEDDING_MODEL || 'code';
@@ -5020,7 +5020,7 @@ window.runVectorIncrementalUpdate = async function runVectorIncrementalUpdate()
try {
// Fetch env settings to get the configured embedding model
var envResponse = await fetch('/api/codexlens/env');
var envResponse = await csrfFetch('/api/codexlens/env');
var envData = await envResponse.json();
var embeddingModel = envData.CODEXLENS_EMBEDDING_MODEL || envData.LITELLM_EMBEDDING_MODEL || null;
@@ -5037,7 +5037,7 @@ window.runVectorIncrementalUpdate = async function runVectorIncrementalUpdate()
requestBody.model = embeddingModel;
}
var response = await fetch('/api/codexlens/embeddings/generate', {
var response = await csrfFetch('/api/codexlens/embeddings/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
@@ -5067,7 +5067,7 @@ window.runIncrementalUpdate = async function runIncrementalUpdate() {
showRefreshToast('Starting incremental update...', 'info');
try {
var response = await fetch('/api/codexlens/update', {
var response = await csrfFetch('/api/codexlens/update', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: projectPath })
@@ -5098,7 +5098,7 @@ window.toggleWatcher = async function toggleWatcher() {
try {
console.log('[CodexLens] Checking watcher status...');
// Pass path parameter to get specific watcher status
var statusResponse = await fetch('/api/codexlens/watch/status?path=' + encodeURIComponent(projectPath));
var statusResponse = await csrfFetch('/api/codexlens/watch/status?path=' + encodeURIComponent(projectPath));
var statusResult = await statusResponse.json();
console.log('[CodexLens] Status result:', statusResult);
@@ -5121,7 +5121,7 @@ window.toggleWatcher = async function toggleWatcher() {
var action = isRunning ? 'stop' : 'start';
console.log('[CodexLens] Action:', action);
var response = await fetch('/api/codexlens/watch/' + action, {
var response = await csrfFetch('/api/codexlens/watch/' + action, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: projectPath })
@@ -5210,7 +5210,7 @@ function startWatcherPolling() {
watcherPollInterval = setInterval(async function() {
try {
// Must include path parameter to get specific watcher status
var response = await fetch('/api/codexlens/watch/status?path=' + encodeURIComponent(projectPath));
var response = await csrfFetch('/api/codexlens/watch/status?path=' + encodeURIComponent(projectPath));
var result = await response.json();
if (result.success && result.running) {
@@ -5337,7 +5337,7 @@ async function initWatcherStatus() {
try {
var projectPath = window.CCW_PROJECT_ROOT || '.';
// Pass path parameter to get specific watcher status
var response = await fetch('/api/codexlens/watch/status?path=' + encodeURIComponent(projectPath));
var response = await csrfFetch('/api/codexlens/watch/status?path=' + encodeURIComponent(projectPath));
var result = await response.json();
if (result.success) {
// Handle both single watcher response (with path param) and array response (without path param)
@@ -5393,7 +5393,7 @@ function initCodexLensManagerPageEvents(currentConfig) {
saveBtn.disabled = true;
saveBtn.innerHTML = '<span class="animate-pulse">' + t('common.saving') + '</span>';
try {
var response = await csrfFetch('/api/codexlens/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ index_dir: newIndexDir }) });
var response = await csrfcsrfFetch('/api/codexlens/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ index_dir: newIndexDir }) });
var result = await response.json();
if (result.success) { if (window.cacheManager) { window.cacheManager.invalidate('codexlens-config'); } showRefreshToast(t('codexlens.configSaved'), 'success'); renderCodexLensManager(); }
else { showRefreshToast(t('common.saveFailed') + ': ' + result.error, 'error'); }
@@ -5466,7 +5466,7 @@ function showIndexInitModal() {
*/
async function loadIndexStatsForPage() {
try {
var response = await fetch('/api/codexlens/indexes');
var response = await csrfFetch('/api/codexlens/indexes');
if (!response.ok) throw new Error('Failed to load index stats');
var data = await response.json();
renderIndexStatsForPage(data);
@@ -5575,7 +5575,7 @@ async function checkIndexHealth() {
try {
// Get current workspace index info
var indexResponse = await fetch('/api/codexlens/indexes');
var indexResponse = await csrfFetch('/api/codexlens/indexes');
var indexData = await indexResponse.json();
var indexes = indexData.indexes || [];
@@ -5653,7 +5653,7 @@ async function cleanIndexProjectFromPage(projectId) {
try {
showRefreshToast(t('index.cleaning') || 'Cleaning index...', 'info');
var response = await fetch('/api/codexlens/clean', {
var response = await csrfFetch('/api/codexlens/clean', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ projectId: projectId })
@@ -5683,7 +5683,7 @@ async function cleanAllIndexesFromPage() {
try {
showRefreshToast(t('index.cleaning') || 'Cleaning indexes...', 'info');
var response = await fetch('/api/codexlens/clean', {
var response = await csrfFetch('/api/codexlens/clean', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ all: true })
@@ -6010,7 +6010,7 @@ async function saveRotationConfig() {
providers: providers
};
var response = await fetch('/api/litellm-api/codexlens/rotation', {
var response = await csrfFetch('/api/litellm-api/codexlens/rotation', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(rotationConfig)
@@ -6052,7 +6052,7 @@ async function showRerankerConfigModal() {
showRefreshToast(t('codexlens.loadingRerankerConfig') || 'Loading reranker configuration...', 'info');
// Fetch current reranker config
const response = await fetch('/api/codexlens/reranker/config');
const response = await csrfFetch('/api/codexlens/reranker/config');
const config = await response.json();
if (!config.success) {
@@ -6341,7 +6341,7 @@ async function saveRerankerConfig() {
payload.litellm_endpoint = document.getElementById('rerankerLitellmEndpoint').value;
}
var response = await fetch('/api/codexlens/reranker/config', {
var response = await csrfFetch('/api/codexlens/reranker/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
@@ -6373,8 +6373,8 @@ async function showWatcherControlModal() {
// Fetch current watcher status and indexed projects in parallel
const [statusResponse, indexesResponse] = await Promise.all([
fetch('/api/codexlens/watch/status'),
fetch('/api/codexlens/indexes')
csrfFetch('/api/codexlens/watch/status'),
csrfFetch('/api/codexlens/indexes')
]);
const status = await statusResponse.json();
const indexes = await indexesResponse.json();
@@ -6562,7 +6562,7 @@ async function toggleWatcher() {
var watchPath = document.getElementById('watcherPath').value.trim();
var debounceMs = parseInt(document.getElementById('watcherDebounce').value, 10) || 1000;
var response = await fetch('/api/codexlens/watch/start', {
var response = await csrfFetch('/api/codexlens/watch/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: watchPath || undefined, debounce_ms: debounceMs })
@@ -6581,7 +6581,7 @@ async function toggleWatcher() {
}
} else {
// Stop watcher
var response = await fetch('/api/codexlens/watch/stop', {
var response = await csrfFetch('/api/codexlens/watch/stop', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
@@ -6625,7 +6625,7 @@ function startWatcherStatusPolling() {
return;
}
var response = await fetch('/api/codexlens/watch/status');
var response = await csrfFetch('/api/codexlens/watch/status');
var status = await response.json();
if (status.running) {
@@ -6711,7 +6711,7 @@ async function flushWatcherNow() {
var watchPath = document.getElementById('watcherPath');
var path = watchPath ? watchPath.value.trim() : '';
var response = await fetch('/api/codexlens/watch/flush', {
var response = await csrfFetch('/api/codexlens/watch/flush', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: path || undefined })
@@ -6744,7 +6744,7 @@ async function showIndexHistory() {
var watchPath = document.getElementById('watcherPath');
var path = watchPath ? watchPath.value.trim() : '';
var response = await fetch('/api/codexlens/watch/history?limit=10&path=' + encodeURIComponent(path));
var response = await csrfFetch('/api/codexlens/watch/history?limit=10&path=' + encodeURIComponent(path));
var result = await response.json();
if (!result.success || !result.history || result.history.length === 0) {
@@ -6945,7 +6945,7 @@ window.toggleIgnorePatternsSection = toggleIgnorePatternsSection;
*/
async function loadIgnorePatterns() {
try {
var response = await fetch('/api/codexlens/ignore-patterns');
var response = await csrfFetch('/api/codexlens/ignore-patterns');
var data = await response.json();
if (data.success) {
@@ -6987,7 +6987,7 @@ async function saveIgnorePatterns() {
var extensionFilters = filtersInput ? filtersInput.value.split('\n').map(function(p) { return p.trim(); }).filter(function(p) { return p; }) : [];
try {
var response = await fetch('/api/codexlens/ignore-patterns', {
var response = await csrfFetch('/api/codexlens/ignore-patterns', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ patterns: patterns, extensionFilters: extensionFilters })
@@ -7020,7 +7020,7 @@ async function resetIgnorePatterns() {
if (!ignorePatternsDefaults) {
// Load defaults first if not cached
try {
var response = await fetch('/api/codexlens/ignore-patterns');
var response = await csrfFetch('/api/codexlens/ignore-patterns');
var data = await response.json();
if (data.success) {
ignorePatternsDefaults = data.defaults;
@@ -7075,7 +7075,7 @@ async function initIgnorePatternsCount() {
var extensionFilters = fallbackDefaults.extensionFilters;
try {
var response = await fetch('/api/codexlens/ignore-patterns');
var response = await csrfFetch('/api/codexlens/ignore-patterns');
var data = await response.json();
if (data.success) {
@@ -7121,3 +7121,4 @@ window.refreshCodexLensData = async function(forceRefresh) {
await refreshWorkspaceIndexStatus(true);
showRefreshToast(t('common.refreshed') || 'Refreshed', 'success');
};

View File

@@ -399,7 +399,7 @@ async function toggleCommandEnabled(commandName, currentlyEnabled) {
}
try {
var response = await fetch('/api/commands/' + encodeURIComponent(commandName) + '/toggle', {
var response = await csrfFetch('/api/commands/' + encodeURIComponent(commandName) + '/toggle', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -453,7 +453,7 @@ async function toggleGroupEnabled(groupName, currentlyAllEnabled) {
const enable = !currentlyAllEnabled;
try {
const response = await fetch('/api/commands/group/' + encodeURIComponent(groupName) + '/toggle', {
const response = await csrfFetch('/api/commands/group/' + encodeURIComponent(groupName) + '/toggle', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View File

@@ -121,7 +121,7 @@ function switchToSemanticStatus() {
async function triggerEmbedding() {
try {
showNotification(t('coreMemory.embeddingInProgress'), 'info');
const response = await fetch(`/api/core-memory/embed?path=${encodeURIComponent(projectPath)}`, {
const response = await csrfFetch(`/api/core-memory/embed?path=${encodeURIComponent(projectPath)}`, {
method: 'POST'
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
@@ -342,7 +342,7 @@ async function triggerAutoClustering(scope = 'recent') {
try {
showNotification(t('coreMemory.clusteringInProgress'), 'info');
const response = await fetch(`/api/core-memory/clusters/auto?path=${encodeURIComponent(projectPath)}`, {
const response = await csrfFetch(`/api/core-memory/clusters/auto?path=${encodeURIComponent(projectPath)}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ scope })
@@ -375,7 +375,7 @@ async function createCluster() {
if (!name) return;
try {
const response = await fetch(`/api/core-memory/clusters?path=${encodeURIComponent(projectPath)}`, {
const response = await csrfFetch(`/api/core-memory/clusters?path=${encodeURIComponent(projectPath)}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name })
@@ -409,7 +409,7 @@ function editCluster(clusterId) {
*/
async function updateCluster(clusterId, updates) {
try {
const response = await fetch(`/api/core-memory/clusters/${clusterId}?path=${encodeURIComponent(projectPath)}`, {
const response = await csrfFetch(`/api/core-memory/clusters/${clusterId}?path=${encodeURIComponent(projectPath)}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates)
@@ -435,7 +435,7 @@ async function deleteCluster(clusterId) {
if (!confirm(t('coreMemory.confirmDeleteCluster'))) return;
try {
const response = await fetch(`/api/core-memory/clusters/${clusterId}?path=${encodeURIComponent(projectPath)}`, {
const response = await csrfFetch(`/api/core-memory/clusters/${clusterId}?path=${encodeURIComponent(projectPath)}`, {
method: 'DELETE'
});
@@ -459,7 +459,7 @@ async function deleteCluster(clusterId) {
*/
async function removeMember(clusterId, sessionId) {
try {
const response = await fetch(
const response = await csrfFetch(
`/api/core-memory/clusters/${clusterId}/members/${sessionId}?path=${encodeURIComponent(projectPath)}`,
{ method: 'DELETE' }
);

View File

@@ -470,7 +470,7 @@ async function saveMemory() {
}
try {
const response = await fetch('/api/core-memory/memories', {
const response = await csrfFetch('/api/core-memory/memories', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
@@ -491,7 +491,7 @@ async function archiveMemory(memoryId) {
if (!confirm(t('coreMemory.confirmArchive'))) return;
try {
const response = await fetch(`/api/core-memory/memories/${memoryId}/archive?path=${encodeURIComponent(projectPath)}`, {
const response = await csrfFetch(`/api/core-memory/memories/${memoryId}/archive?path=${encodeURIComponent(projectPath)}`, {
method: 'POST'
});
@@ -512,7 +512,7 @@ async function unarchiveMemory(memoryId) {
memory.archived = false;
const response = await fetch('/api/core-memory/memories', {
const response = await csrfFetch('/api/core-memory/memories', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...memory, path: projectPath })
@@ -532,7 +532,7 @@ async function deleteMemory(memoryId) {
if (!confirm(t('coreMemory.confirmDelete'))) return;
try {
const response = await fetch(`/api/core-memory/memories/${memoryId}?path=${encodeURIComponent(projectPath)}`, {
const response = await csrfFetch(`/api/core-memory/memories/${memoryId}?path=${encodeURIComponent(projectPath)}`, {
method: 'DELETE'
});
@@ -551,7 +551,7 @@ async function generateMemorySummary(memoryId) {
try {
showNotification(t('coreMemory.generatingSummary'), 'info');
const response = await fetch(`/api/core-memory/memories/${memoryId}/summary`, {
const response = await csrfFetch(`/api/core-memory/memories/${memoryId}/summary`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tool: 'gemini', path: projectPath })
@@ -861,7 +861,7 @@ async function toggleFavorite(memoryId) {
}
metadata.favorite = !metadata.favorite;
const response = await fetch('/api/core-memory/memories', {
const response = await csrfFetch('/api/core-memory/memories', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...memory, metadata, path: projectPath })

View File

@@ -570,7 +570,7 @@ async function executeUpdateClaudeMd() {
statusEl.innerHTML = '<div class="status-running">⏳ Running update...</div>';
try {
const response = await fetch('/api/update-claude-md', {
const response = await csrfFetch('/api/update-claude-md', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path, tool, strategy })
@@ -808,7 +808,7 @@ async function executeTask(task) {
addGlobalNotification('info', `Processing: ${folderName}`, `Strategy: ${task.strategy}, Tool: ${task.tool}`, 'Explorer');
try {
const response = await fetch('/api/update-claude-md', {
const response = await csrfFetch('/api/update-claude-md', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -885,4 +885,3 @@ async function startTaskQueue() {
// Refresh tree to show updated CLAUDE.md files
await refreshExplorerTree();
}

View File

@@ -362,7 +362,7 @@ async function batchDeleteExecutions(ids) {
showRefreshToast('Deleting ' + ids.length + ' executions...', 'info');
try {
var response = await fetch('/api/cli/batch-delete', {
var response = await csrfFetch('/api/cli/batch-delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View File

@@ -66,7 +66,7 @@ async function renderIssueDiscovery() {
async function loadDiscoveryData() {
discoveryLoading = true;
try {
const response = await fetch('/api/discoveries?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/discoveries?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load discoveries');
const data = await response.json();
discoveryData.discoveries = data.discoveries || [];
@@ -81,7 +81,7 @@ async function loadDiscoveryData() {
async function loadDiscoveryDetail(discoveryId) {
try {
const response = await fetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load discovery detail');
return await response.json();
} catch (err) {
@@ -111,7 +111,7 @@ async function loadDiscoveryFindings(discoveryId) {
async function loadDiscoveryProgress(discoveryId) {
try {
const response = await fetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '/progress?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '/progress?path=' + encodeURIComponent(projectPath));
if (!response.ok) return null;
return await response.json();
} catch (err) {
@@ -568,7 +568,7 @@ async function exportSelectedFindings() {
if (!discoveryId) return;
try {
const response = await fetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '/export?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '/export?path=' + encodeURIComponent(projectPath), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ finding_ids: Array.from(discoveryData.selectedFindings) })
@@ -603,7 +603,7 @@ async function exportSingleFinding(findingId) {
if (!discoveryId) return;
try {
const response = await fetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '/export?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '/export?path=' + encodeURIComponent(projectPath), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ finding_ids: [findingId] })
@@ -629,7 +629,7 @@ async function dismissFinding(findingId) {
if (!discoveryId) return;
try {
const response = await fetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '/findings/' + encodeURIComponent(findingId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '/findings/' + encodeURIComponent(findingId) + '?path=' + encodeURIComponent(projectPath), {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ dismissed: true })
@@ -664,7 +664,7 @@ async function deleteDiscovery(discoveryId) {
if (!confirm(`Delete discovery ${discoveryId}? This cannot be undone.`)) return;
try {
const response = await fetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/discoveries/' + encodeURIComponent(discoveryId) + '?path=' + encodeURIComponent(projectPath), {
method: 'DELETE'
});
@@ -728,3 +728,4 @@ function cleanupDiscoveryView() {
discoveryData.selectedFindings.clear();
discoveryData.viewMode = 'list';
}

View File

@@ -57,7 +57,7 @@ async function renderIssueManager() {
async function loadIssueData() {
issueLoading = true;
try {
const response = await fetch('/api/issues?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/issues?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load issues');
const data = await response.json();
issueData.issues = data.issues || [];
@@ -72,7 +72,7 @@ async function loadIssueData() {
async function loadIssueHistory() {
try {
const response = await fetch('/api/issues/history?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/issues/history?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load issue history');
const data = await response.json();
issueData.historyIssues = data.issues || [];
@@ -84,7 +84,7 @@ async function loadIssueHistory() {
async function loadQueueData() {
try {
const response = await fetch('/api/queue?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/queue?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load queue');
issueData.queue = await response.json();
} catch (err) {
@@ -95,7 +95,7 @@ async function loadQueueData() {
async function loadAllQueues() {
try {
const response = await fetch('/api/queue/history?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/queue/history?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load queue history');
const data = await response.json();
queueData.queues = data.queues || [];
@@ -109,7 +109,7 @@ async function loadAllQueues() {
async function loadIssueDetail(issueId) {
try {
const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load issue detail');
return await response.json();
} catch (err) {
@@ -774,7 +774,7 @@ function toggleQueueExpand(queueId) {
async function activateQueue(queueId) {
try {
const response = await fetch('/api/queue/switch?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/queue/switch?path=' + encodeURIComponent(projectPath), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ queueId })
@@ -795,7 +795,7 @@ async function activateQueue(queueId) {
async function deactivateQueue(queueId) {
try {
const response = await fetch('/api/queue/deactivate?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/queue/deactivate?path=' + encodeURIComponent(projectPath), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ queueId })
@@ -824,7 +824,7 @@ function confirmDeleteQueue(queueId) {
async function deleteQueue(queueId) {
try {
const response = await fetch('/api/queue/' + encodeURIComponent(queueId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/queue/' + encodeURIComponent(queueId) + '?path=' + encodeURIComponent(projectPath), {
method: 'DELETE'
});
const result = await response.json();
@@ -847,7 +847,7 @@ async function renderExpandedQueueView(queueId) {
// Fetch queue detail
let queue;
try {
const response = await fetch('/api/queue/' + encodeURIComponent(queueId) + '?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/queue/' + encodeURIComponent(queueId) + '?path=' + encodeURIComponent(projectPath));
queue = await response.json();
if (queue.error) throw new Error(queue.error);
} catch (err) {
@@ -1107,7 +1107,7 @@ async function deleteQueueItem(queueId, itemId) {
if (!confirm('Delete this item from queue?')) return;
try {
const response = await fetch('/api/queue/' + queueId + '/item/' + encodeURIComponent(itemId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/queue/' + queueId + '/item/' + encodeURIComponent(itemId) + '?path=' + encodeURIComponent(projectPath), {
method: 'DELETE'
});
const result = await response.json();
@@ -1202,7 +1202,7 @@ async function executeQueueMerge(sourceQueueId) {
if (!targetQueueId) return;
try {
const response = await fetch('/api/queue/merge?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/queue/merge?path=' + encodeURIComponent(projectPath), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sourceQueueId, targetQueueId })
@@ -1237,7 +1237,7 @@ async function showSplitQueueModal(queueId) {
// Fetch queue details
let queue;
try {
const response = await fetch('/api/queue/' + encodeURIComponent(queueId) + '?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/queue/' + encodeURIComponent(queueId) + '?path=' + encodeURIComponent(projectPath));
queue = await response.json();
if (queue.error) throw new Error(queue.error);
} catch (err) {
@@ -1384,7 +1384,7 @@ async function executeQueueSplit(sourceQueueId) {
}
try {
const response = await fetch('/api/queue/split?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/queue/split?path=' + encodeURIComponent(projectPath), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sourceQueueId, itemIds: selectedItemIds })
@@ -1714,7 +1714,7 @@ function handleIssueDrop(e) {
async function saveQueueOrder(groupId, newOrder) {
try {
const response = await fetch('/api/queue/reorder?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/queue/reorder?path=' + encodeURIComponent(projectPath), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ groupId, newOrder })
@@ -1896,7 +1896,7 @@ function confirmDeleteIssue(issueId, isArchived) {
async function deleteIssue(issueId, isArchived) {
try {
const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
method: 'DELETE'
});
const result = await response.json();
@@ -1928,7 +1928,7 @@ function confirmArchiveIssue(issueId) {
async function archiveIssue(issueId) {
try {
const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '/archive?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/issues/' + encodeURIComponent(issueId) + '/archive?path=' + encodeURIComponent(projectPath), {
method: 'POST'
});
const result = await response.json();
@@ -2262,7 +2262,7 @@ async function toggleSolutionBind() {
const action = solution.is_bound ? 'unbind' : 'bind';
try {
const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -2372,7 +2372,7 @@ async function saveFieldEdit(issueId, field) {
if (!value) return;
try {
const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ [field]: value })
@@ -2402,7 +2402,7 @@ async function saveContextEdit(issueId) {
const value = textarea.value;
try {
const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ context: value })
@@ -2432,7 +2432,7 @@ function cancelEdit() {
async function updateTaskStatus(issueId, taskId, status) {
try {
const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '/tasks/' + encodeURIComponent(taskId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/issues/' + encodeURIComponent(issueId) + '/tasks/' + encodeURIComponent(taskId) + '?path=' + encodeURIComponent(projectPath), {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status })
@@ -2748,7 +2748,7 @@ async function pullGitHubIssues() {
});
if (labels) params.set('labels', labels);
const response = await fetch('/api/issues/pull?' + params.toString(), {
const response = await csrfFetch('/api/issues/pull?' + params.toString(), {
method: 'POST'
});
@@ -2833,7 +2833,7 @@ async function createIssue() {
}
try {
const response = await fetch('/api/issues?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/issues?path=' + encodeURIComponent(projectPath), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -2871,7 +2871,7 @@ async function deleteIssue(issueId) {
}
try {
const response = await fetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
const response = await csrfFetch('/api/issues/' + encodeURIComponent(issueId) + '?path=' + encodeURIComponent(projectPath), {
method: 'DELETE'
});
@@ -3092,7 +3092,7 @@ function hideQueueHistoryModal() {
async function switchToQueue(queueId) {
try {
const response = await fetch(`/api/queue/switch?path=${encodeURIComponent(projectPath)}`, {
const response = await csrfFetch(`/api/queue/switch?path=${encodeURIComponent(projectPath)}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ queueId })
@@ -3246,3 +3246,4 @@ function copyCommand(command) {
showNotification(t('common.copied') || 'Copied to clipboard', 'success');
});
}

View File

@@ -42,7 +42,7 @@ async function getEnabledTools() {
}
try {
const response = await fetch('/api/cli/tools-config');
const response = await csrfFetch('/api/cli/tools-config');
const result = await response.json();
if (result.tools && typeof result.tools === 'object') {
@@ -238,7 +238,7 @@ function handleLoopUpdate(data) {
async function loadLoops() {
try {
// Fetch v2 loops (new simplified format)
const v2Response = await fetch('/api/loops/v2');
const v2Response = await csrfFetch('/api/loops/v2');
const v2Result = await v2Response.json();
if (v2Result.success && v2Result.data) {
@@ -248,7 +248,7 @@ async function loadLoops() {
}
// Fetch v1 loops (legacy format with task_id)
const v1Response = await fetch('/api/loops');
const v1Response = await csrfFetch('/api/loops');
const v1Result = await v1Response.json();
if (v1Result.success && v1Result.data) {
@@ -276,7 +276,7 @@ async function loadLoops() {
*/
async function showTasksTabIfAny() {
try {
const response = await fetch('/api/tasks');
const response = await csrfFetch('/api/tasks');
const result = await response.json();
if (result.success) {
@@ -642,7 +642,7 @@ async function pauseLoop(loopId) {
const endpoint = isV2 ? `/api/loops/v2/${loopId}/pause` : `/api/loops/${loopId}/pause`;
try {
const response = await fetch(endpoint, { method: 'POST' });
const response = await csrfFetch(endpoint, { method: 'POST' });
const result = await response.json();
if (result.success) {
@@ -666,7 +666,7 @@ async function resumeLoop(loopId) {
const endpoint = isV2 ? `/api/loops/v2/${loopId}/resume` : `/api/loops/${loopId}/resume`;
try {
const response = await fetch(endpoint, { method: 'POST' });
const response = await csrfFetch(endpoint, { method: 'POST' });
const result = await response.json();
if (result.success) {
@@ -708,7 +708,7 @@ async function stopLoop(loopId) {
const endpoint = isV2 ? `/api/loops/v2/${loopId}/stop` : `/api/loops/${loopId}/stop`;
try {
const response = await fetch(endpoint, { method: 'POST' });
const response = await csrfFetch(endpoint, { method: 'POST' });
const result = await response.json();
if (result.success) {
@@ -728,7 +728,7 @@ async function stopLoop(loopId) {
*/
async function startLoopV2(loopId) {
try {
const response = await fetch(`/api/loops/v2/${loopId}/start`, { method: 'POST' });
const response = await csrfFetch(`/api/loops/v2/${loopId}/start`, { method: 'POST' });
const result = await response.json();
if (result.success) {
@@ -984,7 +984,7 @@ function handleTaskDrop(e) {
*/
async function saveTaskOrder(loopId, newOrder) {
try {
const response = await fetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks/reorder`, {
const response = await csrfFetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks/reorder`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ordered_task_ids: newOrder })
@@ -1224,7 +1224,7 @@ async function handleAddTask(event, loopId) {
try {
// Call POST /api/loops/v2/:loopId/tasks
const response = await fetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks`, {
const response = await csrfFetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -1383,7 +1383,7 @@ async function handleEditTask(event, loopId, taskId) {
try {
// Call PUT /api/loops/v2/:loopId/tasks/:taskId
const response = await fetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks/${encodeURIComponent(taskId)}`, {
const response = await csrfFetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks/${encodeURIComponent(taskId)}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -1435,7 +1435,7 @@ async function deleteTask(taskId) {
try {
// Call DELETE /api/loops/v2/:loopId/tasks/:taskId
const response = await fetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks/${encodeURIComponent(taskId)}`, {
const response = await csrfFetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks/${encodeURIComponent(taskId)}`, {
method: 'DELETE'
});
@@ -1912,7 +1912,7 @@ async function handleKanbanDrop(event, loopId, newStatus) {
*/
async function updateLoopStatus(loopId, status) {
try {
const response = await fetch(`/api/loops/v2/${encodeURIComponent(loopId)}/status`, {
const response = await csrfFetch(`/api/loops/v2/${encodeURIComponent(loopId)}/status`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status })
@@ -1953,7 +1953,7 @@ async function updateLoopStatus(loopId, status) {
*/
async function updateLoopMetadata(loopId, metadata) {
try {
const response = await fetch(`/api/loops/v2/${encodeURIComponent(loopId)}`, {
const response = await csrfFetch(`/api/loops/v2/${encodeURIComponent(loopId)}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(metadata)
@@ -1995,7 +1995,7 @@ async function updateLoopMetadata(loopId, metadata) {
*/
async function updateTaskStatus(loopId, taskId, newStatus) {
try {
const response = await fetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks/${encodeURIComponent(taskId)}`, {
const response = await csrfFetch(`/api/loops/v2/${encodeURIComponent(loopId)}/tasks/${encodeURIComponent(taskId)}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status: newStatus })
@@ -2493,7 +2493,7 @@ function renderGroupedLoopList() {
*/
async function showTasksTab() {
try {
const response = await fetch('/api/tasks');
const response = await csrfFetch('/api/tasks');
const result = await response.json();
if (!result.success) {
@@ -2578,7 +2578,7 @@ function renderTaskCard(task) {
*/
async function startLoopFromTask(taskId) {
try {
const response = await fetch('/api/loops', {
const response = await csrfFetch('/api/loops', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ taskId })
@@ -2750,7 +2750,7 @@ window.availableCliTools = [];
*/
async function fetchAvailableCliTools() {
try {
const response = await fetch('/api/cli/status');
const response = await csrfFetch('/api/cli/status');
const data = await response.json();
// Return only available tools (where available: true)
return Object.entries(data)
@@ -2992,7 +2992,7 @@ async function handleSimpleCreateLoop(event) {
try {
// Call POST /api/loops/v2
const response = await fetch('/api/loops/v2', {
const response = await csrfFetch('/api/loops/v2', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -3046,7 +3046,7 @@ function closeCreateLoopModal() {
*/
async function importFromIssue() {
try {
const response = await fetch('/api/issues');
const response = await csrfFetch('/api/issues');
const data = await response.json();
if (!data.issues || data.issues.length === 0) {
@@ -3321,7 +3321,7 @@ async function handleCreateLoopSubmit(event) {
try {
// Create task only (don't auto-start)
const createResponse = await fetch('/api/tasks', {
const createResponse = await csrfFetch('/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(task)
@@ -3343,3 +3343,4 @@ async function handleCreateLoopSubmit(event) {
showError(t('loop.createFailed') + ': ' + err.message);
}
}

View File

@@ -1452,7 +1452,7 @@ async function copyCrossCliServer(name, config, fromCli, targetCli) {
body = { serverName: name, serverConfig: config };
}
const res = await fetch(endpoint, {
const res = await csrfFetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
@@ -2141,7 +2141,7 @@ let mcpTemplates = [];
*/
async function loadMcpTemplates() {
try {
const response = await fetch('/api/mcp-templates');
const response = await csrfFetch('/api/mcp-templates');
const data = await response.json();
if (data.success) {
@@ -2178,7 +2178,7 @@ async function saveMcpAsTemplate(serverName, serverConfig) {
category: 'user'
};
const response = await fetch('/api/mcp-templates', {
const response = await csrfFetch('/api/mcp-templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
@@ -2235,7 +2235,7 @@ async function installFromTemplate(templateName, scope = 'project') {
*/
async function deleteMcpTemplate(templateName) {
try {
const response = await fetch(`/api/mcp-templates/${encodeURIComponent(templateName)}`, {
const response = await csrfFetch(`/api/mcp-templates/${encodeURIComponent(templateName)}`, {
method: 'DELETE'
});
@@ -2261,3 +2261,4 @@ window.closeMcpEditModal = closeMcpEditModal;
window.saveMcpEdit = saveMcpEdit;
window.deleteMcpFromEdit = deleteMcpFromEdit;
window.saveMcpAsTemplate = saveMcpAsTemplate;

View File

@@ -122,7 +122,7 @@ function renderActiveMemoryControls() {
// ========== Data Loading ==========
async function loadMemoryStats() {
try {
var response = await fetch('/api/memory/stats?filter=' + memoryTimeFilter);
var response = await csrfFetch('/api/memory/stats?filter=' + memoryTimeFilter);
if (!response.ok) throw new Error('Failed to load memory stats');
var data = await response.json();
memoryStats = data.stats || { mostRead: [], mostEdited: [] };
@@ -136,7 +136,7 @@ async function loadMemoryStats() {
async function loadMemoryGraph() {
try {
var response = await fetch('/api/memory/graph');
var response = await csrfFetch('/api/memory/graph');
if (!response.ok) throw new Error('Failed to load memory graph');
var data = await response.json();
memoryGraphData = data.graph || { nodes: [], edges: [] };
@@ -150,7 +150,7 @@ async function loadMemoryGraph() {
async function loadRecentContext() {
try {
var response = await fetch('/api/memory/recent');
var response = await csrfFetch('/api/memory/recent');
if (!response.ok) throw new Error('Failed to load recent context');
var data = await response.json();
recentContext = data.recent || [];
@@ -164,7 +164,7 @@ async function loadRecentContext() {
async function loadInsightsHistory() {
try {
var response = await fetch('/api/memory/insights?limit=10');
var response = await csrfFetch('/api/memory/insights?limit=10');
if (!response.ok) throw new Error('Failed to load insights history');
var data = await response.json();
insightsHistory = data.insights || [];
@@ -206,7 +206,7 @@ function stopActiveMemorySyncTimer() {
async function loadActiveMemoryStatus() {
try {
var response = await fetch('/api/memory/active/status');
var response = await csrfFetch('/api/memory/active/status');
if (!response.ok) throw new Error('Failed to load active memory status');
var data = await response.json();
activeMemoryEnabled = data.enabled || false;
@@ -232,7 +232,7 @@ async function loadActiveMemoryStatus() {
async function toggleActiveMemory(enabled) {
try {
var response = await fetch('/api/memory/active/toggle', {
var response = await csrfFetch('/api/memory/active/toggle', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -273,7 +273,7 @@ async function updateActiveMemoryConfig(key, value) {
activeMemoryConfig[key] = value;
try {
var response = await fetch('/api/memory/active/config', {
var response = await csrfFetch('/api/memory/active/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ config: activeMemoryConfig })
@@ -304,7 +304,7 @@ async function syncActiveMemory() {
}
try {
var response = await fetch('/api/memory/active/sync', {
var response = await csrfFetch('/api/memory/active/sync', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -1030,7 +1030,7 @@ function getToolIcon(tool) {
async function showInsightDetail(insightId) {
try {
var response = await fetch('/api/memory/insights/' + insightId);
var response = await csrfFetch('/api/memory/insights/' + insightId);
if (!response.ok) throw new Error('Failed to load insight detail');
var data = await response.json();
selectedInsight = data.insight;
@@ -1114,7 +1114,7 @@ async function deleteInsight(insightId) {
if (!confirm(t('memory.confirmDeleteInsight'))) return;
try {
var response = await csrfFetch('/api/memory/insights/' + insightId, { method: 'DELETE' });
var response = await csrfcsrfFetch('/api/memory/insights/' + insightId, { method: 'DELETE' });
if (!response.ok) throw new Error('Failed to delete insight');
selectedInsight = null;
@@ -1219,3 +1219,4 @@ function formatTimestamp(timestamp) {
// Otherwise show date
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
}

View File

@@ -15,7 +15,7 @@ var selectedPromptInsight = null; // Currently selected insight for detail view
async function loadPromptHistory() {
try {
// Use native Claude history.jsonl as primary source
var response = await fetch('/api/memory/native-history?path=' + encodeURIComponent(projectPath) + '&limit=200');
var response = await csrfFetch('/api/memory/native-history?path=' + encodeURIComponent(projectPath) + '&limit=200');
if (!response.ok) throw new Error('Failed to load prompt history');
var data = await response.json();
promptHistoryData = data.prompts || [];
@@ -30,7 +30,7 @@ async function loadPromptHistory() {
async function loadPromptInsights() {
try {
var response = await fetch('/api/memory/insights?path=' + encodeURIComponent(projectPath));
var response = await csrfFetch('/api/memory/insights?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load insights');
var data = await response.json();
promptInsights = data.insights || null;
@@ -44,7 +44,7 @@ async function loadPromptInsights() {
async function loadPromptInsightsHistory() {
try {
var response = await fetch('/api/memory/insights?limit=20&path=' + encodeURIComponent(projectPath));
var response = await csrfFetch('/api/memory/insights?limit=20&path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load insights history');
var data = await response.json();
promptInsightsHistory = data.insights || [];
@@ -347,7 +347,7 @@ function formatPromptTimestamp(timestamp) {
async function showPromptInsightDetail(insightId) {
try {
var response = await fetch('/api/memory/insights/' + insightId);
var response = await csrfFetch('/api/memory/insights/' + insightId);
if (!response.ok) throw new Error('Failed to load insight detail');
var data = await response.json();
selectedPromptInsight = data.insight;
@@ -431,7 +431,7 @@ async function deletePromptInsight(insightId) {
if (!confirm(isZh() ? '确定要删除这条洞察记录吗?' : 'Are you sure you want to delete this insight?')) return;
try {
var response = await csrfFetch('/api/memory/insights/' + insightId, { method: 'DELETE' });
var response = await csrfcsrfFetch('/api/memory/insights/' + insightId, { method: 'DELETE' });
if (!response.ok) throw new Error('Failed to delete insight');
selectedPromptInsight = null;
@@ -676,7 +676,7 @@ async function triggerCliInsightsAnalysis() {
renderPromptHistoryView();
try {
var response = await fetch('/api/memory/insights/analyze', {
var response = await csrfFetch('/api/memory/insights/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -711,3 +711,4 @@ async function triggerCliInsightsAnalysis() {
renderPromptHistoryView();
}
}

View File

@@ -36,7 +36,7 @@ async function renderRulesManager() {
async function loadRulesData() {
rulesLoading = true;
try {
const response = await fetch('/api/rules?path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/rules?path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load rules');
const data = await response.json();
rulesData = {
@@ -284,7 +284,7 @@ function renderRuleDetailPanel(rule) {
async function showRuleDetail(ruleName, location) {
try {
const response = await fetch('/api/rules/' + encodeURIComponent(ruleName) + '?location=' + location + '&path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/rules/' + encodeURIComponent(ruleName) + '?location=' + location + '&path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load rule detail');
const data = await response.json();
selectedRule = data.rule;
@@ -306,7 +306,7 @@ async function deleteRule(ruleName, location) {
if (!confirm(t('rules.deleteConfirm', { name: ruleName }))) return;
try {
const response = await fetch('/api/rules/' + encodeURIComponent(ruleName), {
const response = await csrfFetch('/api/rules/' + encodeURIComponent(ruleName), {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ location, projectPath })
@@ -847,7 +847,7 @@ async function createRule() {
}
try {
const response = await fetch('/api/rules/create', {
const response = await csrfFetch('/api/rules/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
@@ -878,3 +878,4 @@ async function createRule() {
}
}
}

View File

@@ -567,7 +567,7 @@ async function updateSingleTaskStatus(taskId, newStatus) {
}
try {
const response = await fetch('/api/update-task-status', {
const response = await csrfFetch('/api/update-task-status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -610,7 +610,7 @@ async function bulkSetAllStatus(newStatus) {
}
try {
const response = await fetch('/api/bulk-update-task-status', {
const response = await csrfFetch('/api/bulk-update-task-status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -651,7 +651,7 @@ async function bulkSetPendingToInProgress() {
}
try {
const response = await fetch('/api/bulk-update-task-status', {
const response = await csrfFetch('/api/bulk-update-task-status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -691,7 +691,7 @@ async function bulkSetInProgressToCompleted() {
}
try {
const response = await fetch('/api/bulk-update-task-status', {
const response = await csrfFetch('/api/bulk-update-task-status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -779,3 +779,4 @@ function showToast(message, type = 'info') {
setTimeout(() => toast.remove(), 300);
}, 3000);
}

View File

@@ -39,7 +39,7 @@ async function renderSkillsManager() {
async function loadSkillsData() {
skillsLoading = true;
try {
const response = await fetch('/api/skills?path=' + encodeURIComponent(projectPath) + '&includeDisabled=true');
const response = await csrfFetch('/api/skills?path=' + encodeURIComponent(projectPath) + '&includeDisabled=true');
if (!response.ok) throw new Error('Failed to load skills');
const data = await response.json();
skillsData = {
@@ -380,7 +380,7 @@ function renderSkillDetailPanel(skill) {
async function showSkillDetail(skillName, location) {
try {
const response = await fetch('/api/skills/' + encodeURIComponent(skillName) + '?location=' + location + '&path=' + encodeURIComponent(projectPath));
const response = await csrfFetch('/api/skills/' + encodeURIComponent(skillName) + '?location=' + location + '&path=' + encodeURIComponent(projectPath));
if (!response.ok) throw new Error('Failed to load skill detail');
const data = await response.json();
selectedSkill = data.skill;
@@ -402,7 +402,7 @@ async function deleteSkill(skillName, location) {
if (!confirm(t('skills.deleteConfirm', { name: skillName }))) return;
try {
const response = await fetch('/api/skills/' + encodeURIComponent(skillName), {
const response = await csrfFetch('/api/skills/' + encodeURIComponent(skillName), {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ location, projectPath })
@@ -458,7 +458,7 @@ async function toggleSkillEnabled(skillName, location, currentlyEnabled) {
}
try {
var response = await fetch('/api/skills/' + encodeURIComponent(skillName) + '/' + action, {
var response = await csrfFetch('/api/skills/' + encodeURIComponent(skillName) + '/' + action, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ location: location, projectPath: projectPath })
@@ -821,7 +821,7 @@ async function validateSkillImport() {
showValidationResult({ loading: true });
try {
const response = await fetch('/api/skills/validate-import', {
const response = await csrfFetch('/api/skills/validate-import', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sourcePath })
@@ -910,7 +910,7 @@ async function createSkill() {
}
try {
const response = await fetch('/api/skills/create', {
const response = await csrfFetch('/api/skills/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -975,7 +975,7 @@ async function createSkill() {
showToast(t('skills.generating'), 'info');
}
const response = await fetch('/api/skills/create', {
const response = await csrfFetch('/api/skills/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -1158,7 +1158,7 @@ async function saveSkillFile() {
const { skillName, fileName, location } = skillFileEditorState;
try {
const response = await fetch('/api/skills/' + encodeURIComponent(skillName) + '/file', {
const response = await csrfFetch('/api/skills/' + encodeURIComponent(skillName) + '/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -1280,3 +1280,4 @@ async function toggleSkillFolder(skillName, subPath, location, element) {
}
}