mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-05 09:33:07 +00:00
feat: standardize logging across UI components
- Replaced console.log and console.error statements with logger methods from @automaker/utils in various UI components, ensuring consistent log formatting and improved readability. - Enhanced error handling by utilizing logger methods to provide clearer context for issues encountered during operations. - Updated multiple views and hooks to integrate the new logging system, improving maintainability and debugging capabilities. This update significantly enhances the observability of UI components, facilitating easier troubleshooting and monitoring.
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
import { LogLevel, createLogger, getLogLevel, setLogLevel } from '@automaker/utils';
|
import {
|
||||||
|
LogLevel,
|
||||||
|
createLogger,
|
||||||
|
getLogLevel,
|
||||||
|
setLogLevel,
|
||||||
|
setColorsEnabled,
|
||||||
|
setTimestampsEnabled,
|
||||||
|
} from '@automaker/utils';
|
||||||
|
|
||||||
describe('logger.ts', () => {
|
describe('logger.ts', () => {
|
||||||
let consoleSpy: {
|
let consoleSpy: {
|
||||||
@@ -11,6 +18,9 @@ describe('logger.ts', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
originalLogLevel = getLogLevel();
|
originalLogLevel = getLogLevel();
|
||||||
|
// Disable colors and timestamps for predictable test output
|
||||||
|
setColorsEnabled(false);
|
||||||
|
setTimestampsEnabled(false);
|
||||||
consoleSpy = {
|
consoleSpy = {
|
||||||
log: vi.spyOn(console, 'log').mockImplementation(() => {}),
|
log: vi.spyOn(console, 'log').mockImplementation(() => {}),
|
||||||
warn: vi.spyOn(console, 'warn').mockImplementation(() => {}),
|
warn: vi.spyOn(console, 'warn').mockImplementation(() => {}),
|
||||||
@@ -51,7 +61,8 @@ describe('logger.ts', () => {
|
|||||||
|
|
||||||
logger.info('test message');
|
logger.info('test message');
|
||||||
|
|
||||||
expect(consoleSpy.log).toHaveBeenCalledWith('[TestContext]', 'test message');
|
// New format: 'LEVEL [Context]' as first arg, then message
|
||||||
|
expect(consoleSpy.log).toHaveBeenCalledWith('INFO [TestContext]', 'test message');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log error at all log levels', () => {
|
it('should log error at all log levels', () => {
|
||||||
@@ -59,7 +70,7 @@ describe('logger.ts', () => {
|
|||||||
|
|
||||||
setLogLevel(LogLevel.ERROR);
|
setLogLevel(LogLevel.ERROR);
|
||||||
logger.error('error message');
|
logger.error('error message');
|
||||||
expect(consoleSpy.error).toHaveBeenCalledWith('[Test]', 'error message');
|
expect(consoleSpy.error).toHaveBeenCalledWith('ERROR [Test]', 'error message');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log warn when level is WARN or higher', () => {
|
it('should log warn when level is WARN or higher', () => {
|
||||||
@@ -71,7 +82,7 @@ describe('logger.ts', () => {
|
|||||||
|
|
||||||
setLogLevel(LogLevel.WARN);
|
setLogLevel(LogLevel.WARN);
|
||||||
logger.warn('warn message 2');
|
logger.warn('warn message 2');
|
||||||
expect(consoleSpy.warn).toHaveBeenCalledWith('[Test]', 'warn message 2');
|
expect(consoleSpy.warn).toHaveBeenCalledWith('WARN [Test]', 'warn message 2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log info when level is INFO or higher', () => {
|
it('should log info when level is INFO or higher', () => {
|
||||||
@@ -83,7 +94,7 @@ describe('logger.ts', () => {
|
|||||||
|
|
||||||
setLogLevel(LogLevel.INFO);
|
setLogLevel(LogLevel.INFO);
|
||||||
logger.info('info message 2');
|
logger.info('info message 2');
|
||||||
expect(consoleSpy.log).toHaveBeenCalledWith('[Test]', 'info message 2');
|
expect(consoleSpy.log).toHaveBeenCalledWith('INFO [Test]', 'info message 2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log debug only when level is DEBUG', () => {
|
it('should log debug only when level is DEBUG', () => {
|
||||||
@@ -95,7 +106,7 @@ describe('logger.ts', () => {
|
|||||||
|
|
||||||
setLogLevel(LogLevel.DEBUG);
|
setLogLevel(LogLevel.DEBUG);
|
||||||
logger.debug('debug message 2');
|
logger.debug('debug message 2');
|
||||||
expect(consoleSpy.log).toHaveBeenCalledWith('[Test]', '[DEBUG]', 'debug message 2');
|
expect(consoleSpy.log).toHaveBeenCalledWith('DEBUG [Test]', 'debug message 2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass multiple arguments to log functions', () => {
|
it('should pass multiple arguments to log functions', () => {
|
||||||
@@ -103,7 +114,27 @@ describe('logger.ts', () => {
|
|||||||
const logger = createLogger('Multi');
|
const logger = createLogger('Multi');
|
||||||
|
|
||||||
logger.info('message', { data: 'value' }, 123);
|
logger.info('message', { data: 'value' }, 123);
|
||||||
expect(consoleSpy.log).toHaveBeenCalledWith('[Multi]', 'message', { data: 'value' }, 123);
|
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||||
|
'INFO [Multi]',
|
||||||
|
'message',
|
||||||
|
{ data: 'value' },
|
||||||
|
123
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should include timestamps when enabled', () => {
|
||||||
|
setTimestampsEnabled(true);
|
||||||
|
setLogLevel(LogLevel.INFO);
|
||||||
|
const logger = createLogger('Timestamp');
|
||||||
|
|
||||||
|
logger.info('test');
|
||||||
|
|
||||||
|
// First arg should contain ISO timestamp format
|
||||||
|
const firstArg = consoleSpy.log.mock.calls[0][0];
|
||||||
|
expect(firstArg).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z INFO \[Timestamp\]$/);
|
||||||
|
expect(consoleSpy.log.mock.calls[0][1]).toBe('test');
|
||||||
|
|
||||||
|
setTimestampsEnabled(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -248,9 +248,9 @@ describe('claude-provider.ts', () => {
|
|||||||
await expect(collectAsyncGenerator(generator)).rejects.toThrow('SDK execution failed');
|
await expect(collectAsyncGenerator(generator)).rejects.toThrow('SDK execution failed');
|
||||||
|
|
||||||
// Should log error with classification info (via logger)
|
// Should log error with classification info (via logger)
|
||||||
// Logger format: [Context] message, data
|
// Logger format: 'ERROR [Context]' message, data
|
||||||
const errorCall = consoleErrorSpy.mock.calls[0];
|
const errorCall = consoleErrorSpy.mock.calls[0];
|
||||||
expect(errorCall[0]).toBe('[ClaudeProvider]');
|
expect(errorCall[0]).toMatch(/ERROR.*\[ClaudeProvider\]/);
|
||||||
expect(errorCall[1]).toBe('executeQuery() error during execution:');
|
expect(errorCall[1]).toBe('executeQuery() error during execution:');
|
||||||
expect(errorCall[2]).toMatchObject({
|
expect(errorCall[2]).toMatchObject({
|
||||||
type: expect.any(String),
|
type: expect.any(String),
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ describe('feature-loader.ts', () => {
|
|||||||
expect(result).toHaveLength(1);
|
expect(result).toHaveLength(1);
|
||||||
expect(result[0].id).toBe('feature-2');
|
expect(result[0].id).toBe('feature-2');
|
||||||
expect(consoleSpy).toHaveBeenCalledWith(
|
expect(consoleSpy).toHaveBeenCalledWith(
|
||||||
'[FeatureLoader]',
|
expect.stringMatching(/WARN.*\[FeatureLoader\]/),
|
||||||
expect.stringContaining("missing required 'id' field")
|
expect.stringContaining("missing required 'id' field")
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ describe('feature-loader.ts', () => {
|
|||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual([]);
|
||||||
expect(consoleSpy).toHaveBeenCalledWith(
|
expect(consoleSpy).toHaveBeenCalledWith(
|
||||||
'[FeatureLoader]',
|
expect.stringMatching(/WARN.*\[FeatureLoader\]/),
|
||||||
expect.stringContaining('Failed to parse feature.json')
|
expect.stringContaining('Failed to parse feature.json')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -363,7 +363,7 @@ describe('feature-loader.ts', () => {
|
|||||||
|
|
||||||
expect(result).toBe(false);
|
expect(result).toBe(false);
|
||||||
expect(consoleSpy).toHaveBeenCalledWith(
|
expect(consoleSpy).toHaveBeenCalledWith(
|
||||||
'[FeatureLoader]',
|
expect.stringMatching(/ERROR.*\[FeatureLoader\]/),
|
||||||
expect.stringContaining('Failed to delete feature'),
|
expect.stringContaining('Failed to delete feature'),
|
||||||
expect.objectContaining({ message: 'Permission denied' })
|
expect.objectContaining({ message: 'Permission denied' })
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "npx eslint",
|
"lint": "npx eslint",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
"pretest": "node scripts/kill-test-servers.mjs && node scripts/setup-e2e-fixtures.mjs",
|
"pretest": "node scripts/kill-test-servers.mjs && node scripts/setup-e2e-fixtures.mjs",
|
||||||
"test": "playwright test",
|
"test": "playwright test",
|
||||||
"test:headed": "playwright test --headed",
|
"test:headed": "playwright test --headed",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { RouterProvider } from '@tanstack/react-router';
|
import { RouterProvider } from '@tanstack/react-router';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { router } from './utils/router';
|
import { router } from './utils/router';
|
||||||
import { SplashScreen } from './components/splash-screen';
|
import { SplashScreen } from './components/splash-screen';
|
||||||
import { useSettingsMigration } from './hooks/use-settings-migration';
|
import { useSettingsMigration } from './hooks/use-settings-migration';
|
||||||
@@ -7,6 +8,8 @@ import { useCursorStatusInit } from './hooks/use-cursor-status-init';
|
|||||||
import './styles/global.css';
|
import './styles/global.css';
|
||||||
import './styles/theme-imports';
|
import './styles/theme-imports';
|
||||||
|
|
||||||
|
const logger = createLogger('App');
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [showSplash, setShowSplash] = useState(() => {
|
const [showSplash, setShowSplash] = useState(() => {
|
||||||
// Only show splash once per session
|
// Only show splash once per session
|
||||||
@@ -19,7 +22,7 @@ export default function App() {
|
|||||||
// Run settings migration on startup (localStorage -> file storage)
|
// Run settings migration on startup (localStorage -> file storage)
|
||||||
const migrationState = useSettingsMigration();
|
const migrationState = useSettingsMigration();
|
||||||
if (migrationState.migrated) {
|
if (migrationState.migrated) {
|
||||||
console.log('[App] Settings migrated to file storage');
|
logger.info('Settings migrated to file storage');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Cursor CLI status at startup
|
// Initialize Cursor CLI status at startup
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { ImageIcon, Upload, Loader2, Trash2 } from 'lucide-react';
|
import { ImageIcon, Upload, Loader2, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
|
const logger = createLogger('BoardBackgroundModal');
|
||||||
import {
|
import {
|
||||||
Sheet,
|
Sheet,
|
||||||
SheetContent,
|
SheetContent,
|
||||||
@@ -113,7 +116,7 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
setPreviewImage(null);
|
setPreviewImage(null);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to process image:', error);
|
logger.error('Failed to process image:', error);
|
||||||
toast.error('Failed to process image');
|
toast.error('Failed to process image');
|
||||||
setPreviewImage(null);
|
setPreviewImage(null);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -185,7 +188,7 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
toast.error(result.error || 'Failed to clear background image');
|
toast.error(result.error || 'Failed to clear background image');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to clear background:', error);
|
logger.error('Failed to clear background:', error);
|
||||||
toast.error('Failed to clear background');
|
toast.error('Failed to clear background');
|
||||||
} finally {
|
} finally {
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -29,6 +30,8 @@ import { cn } from '@/lib/utils';
|
|||||||
import { useFileBrowser } from '@/contexts/file-browser-context';
|
import { useFileBrowser } from '@/contexts/file-browser-context';
|
||||||
import { getDefaultWorkspaceDirectory, saveLastProjectDirectory } from '@/lib/workspace-config';
|
import { getDefaultWorkspaceDirectory, saveLastProjectDirectory } from '@/lib/workspace-config';
|
||||||
|
|
||||||
|
const logger = createLogger('NewProjectModal');
|
||||||
|
|
||||||
interface ValidationErrors {
|
interface ValidationErrors {
|
||||||
projectName?: boolean;
|
projectName?: boolean;
|
||||||
workspaceDir?: boolean;
|
workspaceDir?: boolean;
|
||||||
@@ -78,7 +81,7 @@ export function NewProjectModal({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Failed to get default workspace directory:', error);
|
logger.error('Failed to get default workspace directory:', error);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsLoadingWorkspace(false);
|
setIsLoadingWorkspace(false);
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { ShieldX, RefreshCw, Container, Copy, Check } from 'lucide-react';
|
import { ShieldX, RefreshCw, Container, Copy, Check } from 'lucide-react';
|
||||||
|
|
||||||
|
const logger = createLogger('SandboxRejectionScreen');
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
const DOCKER_COMMAND = 'npm run dev:docker';
|
const DOCKER_COMMAND = 'npm run dev:docker';
|
||||||
@@ -26,7 +29,7 @@ export function SandboxRejectionScreen() {
|
|||||||
setCopied(true);
|
setCopied(true);
|
||||||
setTimeout(() => setCopied(false), 2000);
|
setTimeout(() => setCopied(false), 2000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to copy:', err);
|
logger.error('Failed to copy:', err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { ShieldAlert, Copy, Check } from 'lucide-react';
|
import { ShieldAlert, Copy, Check } from 'lucide-react';
|
||||||
|
|
||||||
|
const logger = createLogger('SandboxRiskDialog');
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -43,7 +46,7 @@ export function SandboxRiskDialog({ open, onConfirm, onDeny }: SandboxRiskDialog
|
|||||||
setCopied(true);
|
setCopied(true);
|
||||||
setTimeout(() => setCopied(false), 2000);
|
setTimeout(() => setCopied(false), 2000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to copy:', err);
|
logger.error('Failed to copy:', err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useNavigate, useLocation } from '@tanstack/react-router';
|
import { useNavigate, useLocation } from '@tanstack/react-router';
|
||||||
|
|
||||||
|
const logger = createLogger('Sidebar');
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useAppStore, type ThemeMode } from '@/store/app-store';
|
import { useAppStore, type ThemeMode } from '@/store/app-store';
|
||||||
import { useKeyboardShortcuts, useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
|
import { useKeyboardShortcuts, useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
|
||||||
@@ -215,7 +218,7 @@ export function Sidebar() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Sidebar] Failed to open project:', error);
|
logger.error('Failed to open project:', error);
|
||||||
toast.error('Failed to open project', {
|
toast.error('Failed to open project', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('ProjectCreation');
|
||||||
import { initializeProject } from '@/lib/project-init';
|
import { initializeProject } from '@/lib/project-init';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import type { StarterTemplate } from '@/lib/templates';
|
import type { StarterTemplate } from '@/lib/templates';
|
||||||
@@ -82,7 +85,7 @@ export function useProjectCreation({
|
|||||||
|
|
||||||
toast.success('Project created successfully');
|
toast.success('Project created successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ProjectCreation] Failed to finalize project:', error);
|
logger.error('Failed to finalize project:', error);
|
||||||
toast.error('Failed to initialize project', {
|
toast.error('Failed to initialize project', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -108,7 +111,7 @@ export function useProjectCreation({
|
|||||||
// Finalize project setup
|
// Finalize project setup
|
||||||
await finalizeProjectCreation(projectPath, projectName);
|
await finalizeProjectCreation(projectPath, projectName);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ProjectCreation] Failed to create blank project:', error);
|
logger.error('Failed to create blank project:', error);
|
||||||
toast.error('Failed to create project', {
|
toast.error('Failed to create project', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -180,7 +183,7 @@ export function useProjectCreation({
|
|||||||
description: `Created ${projectName} from ${template.name}`,
|
description: `Created ${projectName} from ${template.name}`,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ProjectCreation] Failed to create from template:', error);
|
logger.error('Failed to create from template:', error);
|
||||||
toast.error('Failed to create project from template', {
|
toast.error('Failed to create project from template', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -252,7 +255,7 @@ export function useProjectCreation({
|
|||||||
description: `Created ${projectName} from ${repoUrl}`,
|
description: `Created ${projectName} from ${repoUrl}`,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ProjectCreation] Failed to create from custom URL:', error);
|
logger.error('Failed to create from custom URL:', error);
|
||||||
toast.error('Failed to create project from URL', {
|
toast.error('Failed to create project from URL', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('RunningAgents');
|
||||||
|
|
||||||
export function useRunningAgents() {
|
export function useRunningAgents() {
|
||||||
const [runningAgentsCount, setRunningAgentsCount] = useState(0);
|
const [runningAgentsCount, setRunningAgentsCount] = useState(0);
|
||||||
|
|
||||||
@@ -15,7 +18,7 @@ export function useRunningAgents() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Sidebar] Error fetching running agents count:', error);
|
logger.error('Error fetching running agents count:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('SetupDialog');
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import type { FeatureCount } from '@/components/views/spec-view/types';
|
import type { FeatureCount } from '@/components/views/spec-view/types';
|
||||||
|
|
||||||
@@ -53,7 +56,7 @@ export function useSetupDialog({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
console.error('[SetupDialog] Failed to start spec creation:', result.error);
|
logger.error('Failed to start spec creation:', result.error);
|
||||||
setSpecCreatingForProject(null);
|
setSpecCreatingForProject(null);
|
||||||
toast.error('Failed to create specification', {
|
toast.error('Failed to create specification', {
|
||||||
description: result.error,
|
description: result.error,
|
||||||
@@ -66,7 +69,7 @@ export function useSetupDialog({
|
|||||||
}
|
}
|
||||||
// If successful, we'll wait for the events to update the state
|
// If successful, we'll wait for the events to update the state
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[SetupDialog] Failed to create spec:', error);
|
logger.error('Failed to create spec:', error);
|
||||||
setSpecCreatingForProject(null);
|
setSpecCreatingForProject(null);
|
||||||
toast.error('Failed to create specification', {
|
toast.error('Failed to create specification', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
const logger = createLogger('SpecRegeneration');
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import type { SpecRegenerationEvent } from '@/types/electron';
|
import type { SpecRegenerationEvent } from '@/types/electron';
|
||||||
|
|
||||||
@@ -30,16 +33,11 @@ export function useSpecRegeneration({
|
|||||||
if (!api.specRegeneration) return;
|
if (!api.specRegeneration) return;
|
||||||
|
|
||||||
const unsubscribe = api.specRegeneration.onEvent((event: SpecRegenerationEvent) => {
|
const unsubscribe = api.specRegeneration.onEvent((event: SpecRegenerationEvent) => {
|
||||||
console.log(
|
logger.debug('Spec regeneration event:', event.type, 'for project:', event.projectPath);
|
||||||
'[Sidebar] Spec regeneration event:',
|
|
||||||
event.type,
|
|
||||||
'for project:',
|
|
||||||
event.projectPath
|
|
||||||
);
|
|
||||||
|
|
||||||
// Only handle events for the project we're currently setting up
|
// Only handle events for the project we're currently setting up
|
||||||
if (event.projectPath !== creatingSpecProjectPath && event.projectPath !== setupProjectPath) {
|
if (event.projectPath !== creatingSpecProjectPath && event.projectPath !== setupProjectPath) {
|
||||||
console.log('[Sidebar] Ignoring event - not for project being set up');
|
logger.debug('Ignoring event - not for project being set up');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
const logger = createLogger('TrashOperations');
|
||||||
import { getElectronAPI, type TrashedProject } from '@/lib/electron';
|
import { getElectronAPI, type TrashedProject } from '@/lib/electron';
|
||||||
|
|
||||||
interface UseTrashOperationsProps {
|
interface UseTrashOperationsProps {
|
||||||
@@ -24,7 +27,7 @@ export function useTrashOperations({
|
|||||||
description: 'Added back to your project list.',
|
description: 'Added back to your project list.',
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Sidebar] Failed to restore project:', error);
|
logger.error('Failed to restore project:', error);
|
||||||
toast.error('Failed to restore project', {
|
toast.error('Failed to restore project', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -52,7 +55,7 @@ export function useTrashOperations({
|
|||||||
description: trashedProject.path,
|
description: trashedProject.path,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Sidebar] Failed to delete project from disk:', error);
|
logger.error('Failed to delete project from disk:', error);
|
||||||
toast.error('Failed to delete project folder', {
|
toast.error('Failed to delete project folder', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -69,7 +72,7 @@ export function useTrashOperations({
|
|||||||
emptyTrash();
|
emptyTrash();
|
||||||
toast.success('Recycle bin cleared');
|
toast.success('Recycle bin cleared');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Sidebar] Failed to empty trash:', error);
|
logger.error('Failed to empty trash:', error);
|
||||||
toast.error('Failed to clear recycle bin', {
|
toast.error('Failed to clear recycle bin', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('UnviewedValidations');
|
||||||
import type { Project, StoredValidation } from '@/lib/electron';
|
import type { Project, StoredValidation } from '@/lib/electron';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +41,7 @@ export function useUnviewedValidations(currentProject: Project | null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useUnviewedValidations] Failed to load count:', err);
|
logger.error('Failed to load count:', err);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
|
||||||
|
const logger = createLogger('SessionManager');
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { HotkeyButton } from '@/components/ui/hotkey-button';
|
import { HotkeyButton } from '@/components/ui/hotkey-button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
@@ -126,7 +129,7 @@ export function SessionManager({
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Ignore errors for individual session checks
|
// Ignore errors for individual session checks
|
||||||
console.warn(`[SessionManager] Failed to check running state for ${session.id}:`, err);
|
logger.warn(`Failed to check running state for ${session.id}:`, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +230,7 @@ export function SessionManager({
|
|||||||
const handleArchiveSession = async (sessionId: string) => {
|
const handleArchiveSession = async (sessionId: string) => {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.sessions) {
|
if (!api?.sessions) {
|
||||||
console.error('[SessionManager] Sessions API not available');
|
logger.error('[SessionManager] Sessions API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,10 +243,10 @@ export function SessionManager({
|
|||||||
}
|
}
|
||||||
await loadSessions();
|
await loadSessions();
|
||||||
} else {
|
} else {
|
||||||
console.error('[SessionManager] Archive failed:', result.error);
|
logger.error('[SessionManager] Archive failed:', result.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[SessionManager] Archive error:', error);
|
logger.error('[SessionManager] Archive error:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -251,7 +254,7 @@ export function SessionManager({
|
|||||||
const handleUnarchiveSession = async (sessionId: string) => {
|
const handleUnarchiveSession = async (sessionId: string) => {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.sessions) {
|
if (!api?.sessions) {
|
||||||
console.error('[SessionManager] Sessions API not available');
|
logger.error('[SessionManager] Sessions API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,10 +263,10 @@ export function SessionManager({
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
await loadSessions();
|
await loadSessions();
|
||||||
} else {
|
} else {
|
||||||
console.error('[SessionManager] Unarchive failed:', result.error);
|
logger.error('[SessionManager] Unarchive failed:', result.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[SessionManager] Unarchive error:', error);
|
logger.error('[SessionManager] Unarchive error:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import React, { useState, useRef, useCallback } from 'react';
|
import React, { useState, useRef, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('DescriptionImageDropZone');
|
||||||
import { ImageIcon, X, Loader2, FileText } from 'lucide-react';
|
import { ImageIcon, X, Loader2, FileText } from 'lucide-react';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
@@ -108,7 +111,7 @@ export function DescriptionImageDropZone({
|
|||||||
// Check if saveImageToTemp method exists
|
// Check if saveImageToTemp method exists
|
||||||
if (!api.saveImageToTemp) {
|
if (!api.saveImageToTemp) {
|
||||||
// Fallback path when saveImageToTemp is not available
|
// Fallback path when saveImageToTemp is not available
|
||||||
console.log('[DescriptionImageDropZone] Using fallback path for image');
|
logger.info('Using fallback path for image');
|
||||||
return `.automaker/images/${Date.now()}_${filename}`;
|
return `.automaker/images/${Date.now()}_${filename}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,10 +121,10 @@ export function DescriptionImageDropZone({
|
|||||||
if (result.success && result.path) {
|
if (result.success && result.path) {
|
||||||
return result.path;
|
return result.path;
|
||||||
}
|
}
|
||||||
console.error('[DescriptionImageDropZone] Failed to save image:', result.error);
|
logger.error('Failed to save image:', result.error);
|
||||||
return null;
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[DescriptionImageDropZone] Error saving image:', error);
|
logger.error('Error saving image:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -216,7 +219,7 @@ export function DescriptionImageDropZone({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
console.warn('File upload errors:', errors);
|
logger.warn('File upload errors:', errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newImages.length > 0) {
|
if (newImages.length > 0) {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import React, { useState, useRef, useCallback } from 'react';
|
import React, { useState, useRef, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('FeatureImageUpload');
|
||||||
import { ImageIcon, X, Upload } from 'lucide-react';
|
import { ImageIcon, X, Upload } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
fileToBase64,
|
fileToBase64,
|
||||||
@@ -77,7 +80,7 @@ export function FeatureImageUpload({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
console.warn('Image upload errors:', errors);
|
logger.warn('Image upload errors:', errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newImages.length > 0) {
|
if (newImages.length > 0) {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import React, { useState, useRef, useCallback } from 'react';
|
import React, { useState, useRef, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('ImageDropZone');
|
||||||
import { ImageIcon, X, Upload } from 'lucide-react';
|
import { ImageIcon, X, Upload } from 'lucide-react';
|
||||||
import type { ImageAttachment } from '@/store/app-store';
|
import type { ImageAttachment } from '@/store/app-store';
|
||||||
import {
|
import {
|
||||||
@@ -88,7 +91,7 @@ export function ImageDropZone({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
console.warn('Image upload errors:', errors);
|
logger.warn('Image upload errors:', errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newImages.length > 0) {
|
if (newImages.length > 0) {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('TaskProgressPanel');
|
||||||
import { Check, Loader2, Circle, ChevronDown, ChevronRight, FileCode } from 'lucide-react';
|
import { Check, Loader2, Circle, ChevronDown, ChevronRight, FileCode } from 'lucide-react';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import type { AutoModeEvent } from '@/types/electron';
|
import type { AutoModeEvent } from '@/types/electron';
|
||||||
@@ -72,7 +75,7 @@ export function TaskProgressPanel({
|
|||||||
setCurrentTaskId(currentId || null);
|
setCurrentTaskId(currentId || null);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load initial tasks:', error);
|
logger.error('Failed to load initial tasks:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -19,6 +20,8 @@ import {
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('AgentToolsView');
|
||||||
|
|
||||||
interface ToolResult {
|
interface ToolResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
output?: string;
|
output?: string;
|
||||||
@@ -62,7 +65,7 @@ export function AgentToolsView() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Simulate agent requesting file read
|
// Simulate agent requesting file read
|
||||||
console.log(`[Agent Tool] Requesting to read file: ${readFilePath}`);
|
logger.info(`[Agent Tool] Requesting to read file: ${readFilePath}`);
|
||||||
|
|
||||||
const result = await api.readFile(readFilePath);
|
const result = await api.readFile(readFilePath);
|
||||||
|
|
||||||
@@ -72,14 +75,14 @@ export function AgentToolsView() {
|
|||||||
output: result.content,
|
output: result.content,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
});
|
});
|
||||||
console.log(`[Agent Tool] File read successful: ${readFilePath}`);
|
logger.info(`[Agent Tool] File read successful: ${readFilePath}`);
|
||||||
} else {
|
} else {
|
||||||
setReadFileResult({
|
setReadFileResult({
|
||||||
success: false,
|
success: false,
|
||||||
error: result.error || 'Failed to read file',
|
error: result.error || 'Failed to read file',
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
});
|
});
|
||||||
console.log(`[Agent Tool] File read failed: ${result.error}`);
|
logger.info(`[Agent Tool] File read failed: ${result.error}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setReadFileResult({
|
setReadFileResult({
|
||||||
@@ -101,7 +104,7 @@ export function AgentToolsView() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Simulate agent requesting file write
|
// Simulate agent requesting file write
|
||||||
console.log(`[Agent Tool] Requesting to write file: ${writeFilePath}`);
|
logger.info(`[Agent Tool] Requesting to write file: ${writeFilePath}`);
|
||||||
|
|
||||||
const result = await api.writeFile(writeFilePath, writeFileContent);
|
const result = await api.writeFile(writeFilePath, writeFileContent);
|
||||||
|
|
||||||
@@ -111,14 +114,14 @@ export function AgentToolsView() {
|
|||||||
output: `File written successfully: ${writeFilePath}`,
|
output: `File written successfully: ${writeFilePath}`,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
});
|
});
|
||||||
console.log(`[Agent Tool] File write successful: ${writeFilePath}`);
|
logger.info(`[Agent Tool] File write successful: ${writeFilePath}`);
|
||||||
} else {
|
} else {
|
||||||
setWriteFileResult({
|
setWriteFileResult({
|
||||||
success: false,
|
success: false,
|
||||||
error: result.error || 'Failed to write file',
|
error: result.error || 'Failed to write file',
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
});
|
});
|
||||||
console.log(`[Agent Tool] File write failed: ${result.error}`);
|
logger.info(`[Agent Tool] File write failed: ${result.error}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setWriteFileResult({
|
setWriteFileResult({
|
||||||
@@ -140,7 +143,7 @@ export function AgentToolsView() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Terminal command simulation for demonstration purposes
|
// Terminal command simulation for demonstration purposes
|
||||||
console.log(`[Agent Tool] Simulating command: ${terminalCommand}`);
|
logger.info(`[Agent Tool] Simulating command: ${terminalCommand}`);
|
||||||
|
|
||||||
// Simulated outputs for common commands (preview mode)
|
// Simulated outputs for common commands (preview mode)
|
||||||
// In production, the agent executes commands via Claude SDK
|
// In production, the agent executes commands via Claude SDK
|
||||||
@@ -165,7 +168,7 @@ export function AgentToolsView() {
|
|||||||
output: output,
|
output: output,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
});
|
});
|
||||||
console.log(`[Agent Tool] Command executed successfully: ${terminalCommand}`);
|
logger.info(`[Agent Tool] Command executed successfully: ${terminalCommand}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setTerminalResult({
|
setTerminalResult({
|
||||||
success: false,
|
success: false,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('AgentSession');
|
||||||
|
|
||||||
interface UseAgentSessionOptions {
|
interface UseAgentSessionOptions {
|
||||||
projectPath: string | undefined;
|
projectPath: string | undefined;
|
||||||
}
|
}
|
||||||
@@ -44,7 +47,7 @@ export function useAgentSession({ projectPath }: UseAgentSessionOptions): UseAge
|
|||||||
|
|
||||||
const lastSessionId = getLastSelectedSession(projectPath);
|
const lastSessionId = getLastSelectedSession(projectPath);
|
||||||
if (lastSessionId) {
|
if (lastSessionId) {
|
||||||
console.log('[AgentView] Restoring last selected session:', lastSessionId);
|
logger.info('Restoring last selected session:', lastSessionId);
|
||||||
setCurrentSessionId(lastSessionId);
|
setCurrentSessionId(lastSessionId);
|
||||||
}
|
}
|
||||||
}, [projectPath, getLastSelectedSession]);
|
}, [projectPath, getLastSelectedSession]);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('FileAttachments');
|
||||||
import {
|
import {
|
||||||
fileToBase64,
|
fileToBase64,
|
||||||
generateImageId,
|
generateImageId,
|
||||||
@@ -138,7 +141,7 @@ export function useFileAttachments({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
console.warn('File upload errors:', errors);
|
logger.warn('File upload errors:', errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newImages.length > 0) {
|
if (newImages.length > 0) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore, FileTreeNode, ProjectAnalysis } from '@/store/app-store';
|
import { useAppStore, FileTreeNode, ProjectAnalysis } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
@@ -21,6 +22,8 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('AnalysisView');
|
||||||
|
|
||||||
const IGNORE_PATTERNS = [
|
const IGNORE_PATTERNS = [
|
||||||
'node_modules',
|
'node_modules',
|
||||||
'.git',
|
'.git',
|
||||||
@@ -109,7 +112,7 @@ export function AnalysisView() {
|
|||||||
|
|
||||||
return nodes;
|
return nodes;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to scan directory:', path, error);
|
logger.error('Failed to scan directory:', path, error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -165,7 +168,7 @@ export function AnalysisView() {
|
|||||||
|
|
||||||
setProjectAnalysis(analysis);
|
setProjectAnalysis(analysis);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Analysis failed:', error);
|
logger.error('Analysis failed:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsAnalyzing(false);
|
setIsAnalyzing(false);
|
||||||
}
|
}
|
||||||
@@ -373,7 +376,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
|||||||
setSpecError(writeResult.error || 'Failed to write spec file');
|
setSpecError(writeResult.error || 'Failed to write spec file');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to generate spec:', error);
|
logger.error('Failed to generate spec:', error);
|
||||||
setSpecError(error instanceof Error ? error.message : 'Failed to generate spec');
|
setSpecError(error instanceof Error ? error.message : 'Failed to generate spec');
|
||||||
} finally {
|
} finally {
|
||||||
setIsGeneratingSpec(false);
|
setIsGeneratingSpec(false);
|
||||||
@@ -644,7 +647,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
|||||||
|
|
||||||
setFeatureListGenerated(true);
|
setFeatureListGenerated(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to generate feature list:', error);
|
logger.error('Failed to generate feature list:', error);
|
||||||
setFeatureListError(
|
setFeatureListError(
|
||||||
error instanceof Error ? error.message : 'Failed to generate feature list'
|
error instanceof Error ? error.message : 'Failed to generate feature list'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState, useCallback, useMemo, useRef } from 'react';
|
import { useEffect, useState, useCallback, useMemo, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
PointerSensor,
|
PointerSensor,
|
||||||
useSensor,
|
useSensor,
|
||||||
@@ -62,6 +63,8 @@ import {
|
|||||||
// Stable empty array to avoid infinite loop in selector
|
// Stable empty array to avoid infinite loop in selector
|
||||||
const EMPTY_WORKTREES: ReturnType<ReturnType<typeof useAppStore.getState>['getWorktrees']> = [];
|
const EMPTY_WORKTREES: ReturnType<ReturnType<typeof useAppStore.getState>['getWorktrees']> = [];
|
||||||
|
|
||||||
|
const logger = createLogger('Board');
|
||||||
|
|
||||||
export function BoardView() {
|
export function BoardView() {
|
||||||
const {
|
const {
|
||||||
currentProject,
|
currentProject,
|
||||||
@@ -188,7 +191,7 @@ export function BoardView() {
|
|||||||
|
|
||||||
return result.success && result.exists === true;
|
return result.success && result.exists === true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error checking context:', error);
|
logger.error('Error checking context:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -222,7 +225,7 @@ export function BoardView() {
|
|||||||
setPipelineConfig(currentProject.path, result.config);
|
setPipelineConfig(currentProject.path, result.config);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Failed to load pipeline config:', error);
|
logger.error('Failed to load pipeline config:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -288,7 +291,7 @@ export function BoardView() {
|
|||||||
setBranchSuggestions(localBranches);
|
setBranchSuggestions(localBranches);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[BoardView] Error fetching branches:', error);
|
logger.error('Error fetching branches:', error);
|
||||||
setBranchSuggestions([]);
|
setBranchSuggestions([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -497,7 +500,7 @@ export function BoardView() {
|
|||||||
if (newFeature) {
|
if (newFeature) {
|
||||||
await handleStartImplementation(newFeature);
|
await handleStartImplementation(newFeature);
|
||||||
} else {
|
} else {
|
||||||
console.error('Could not find newly created feature to start it automatically.');
|
logger.error('Could not find newly created feature to start it automatically.');
|
||||||
toast.error('Failed to auto-start feature', {
|
toast.error('Failed to auto-start feature', {
|
||||||
description: 'The feature was created but could not be started automatically.',
|
description: 'The feature was created but could not be started automatically.',
|
||||||
});
|
});
|
||||||
@@ -538,7 +541,7 @@ export function BoardView() {
|
|||||||
if (newFeature) {
|
if (newFeature) {
|
||||||
await handleStartImplementation(newFeature);
|
await handleStartImplementation(newFeature);
|
||||||
} else {
|
} else {
|
||||||
console.error('Could not find newly created feature to start it automatically.');
|
logger.error('Could not find newly created feature to start it automatically.');
|
||||||
toast.error('Failed to auto-start feature', {
|
toast.error('Failed to auto-start feature', {
|
||||||
description: 'The feature was created but could not be started automatically.',
|
description: 'The feature was created but could not be started automatically.',
|
||||||
});
|
});
|
||||||
@@ -561,7 +564,7 @@ export function BoardView() {
|
|||||||
if (newFeature) {
|
if (newFeature) {
|
||||||
await handleStartImplementation(newFeature);
|
await handleStartImplementation(newFeature);
|
||||||
} else {
|
} else {
|
||||||
console.error('Could not find newly created feature to start it automatically.');
|
logger.error('Could not find newly created feature to start it automatically.');
|
||||||
toast.error('Failed to auto-start feature', {
|
toast.error('Failed to auto-start feature', {
|
||||||
description: 'The feature was created but could not be started automatically.',
|
description: 'The feature was created but could not be started automatically.',
|
||||||
});
|
});
|
||||||
@@ -889,10 +892,10 @@ export function BoardView() {
|
|||||||
// Reload features from server to ensure sync
|
// Reload features from server to ensure sync
|
||||||
loadFeatures();
|
loadFeatures();
|
||||||
} else {
|
} else {
|
||||||
console.error('[Board] Failed to approve plan:', result.error);
|
logger.error('Failed to approve plan:', result.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error approving plan:', error);
|
logger.error('Error approving plan:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsPlanApprovalLoading(false);
|
setIsPlanApprovalLoading(false);
|
||||||
setPendingPlanApproval(null);
|
setPendingPlanApproval(null);
|
||||||
@@ -945,10 +948,10 @@ export function BoardView() {
|
|||||||
// Reload features from server to ensure sync
|
// Reload features from server to ensure sync
|
||||||
loadFeatures();
|
loadFeatures();
|
||||||
} else {
|
} else {
|
||||||
console.error('[Board] Failed to reject plan:', result.error);
|
logger.error('Failed to reject plan:', result.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error rejecting plan:', error);
|
logger.error('Error rejecting plan:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsPlanApprovalLoading(false);
|
setIsPlanApprovalLoading(false);
|
||||||
setPendingPlanApproval(null);
|
setPendingPlanApproval(null);
|
||||||
@@ -1407,7 +1410,7 @@ export function BoardView() {
|
|||||||
// Persist changes asynchronously and in parallel
|
// Persist changes asynchronously and in parallel
|
||||||
Promise.all(
|
Promise.all(
|
||||||
featuresToUpdate.map((feature) => persistFeatureUpdate(feature.id, { prUrl }))
|
featuresToUpdate.map((feature) => persistFeatureUpdate(feature.id, { prUrl }))
|
||||||
).catch(console.error);
|
).catch((err) => logger.error('Error in handleMove:', err));
|
||||||
}
|
}
|
||||||
setWorktreeRefreshKey((k) => k + 1);
|
setWorktreeRefreshKey((k) => k + 1);
|
||||||
setSelectedWorktreeForAction(null);
|
setSelectedWorktreeForAction(null);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -64,6 +65,8 @@ import {
|
|||||||
} from '@automaker/dependency-resolver';
|
} from '@automaker/dependency-resolver';
|
||||||
import { isCursorModel, PROVIDER_PREFIXES } from '@automaker/types';
|
import { isCursorModel, PROVIDER_PREFIXES } from '@automaker/types';
|
||||||
|
|
||||||
|
const logger = createLogger('AddFeatureDialog');
|
||||||
|
|
||||||
type FeatureData = {
|
type FeatureData = {
|
||||||
title: string;
|
title: string;
|
||||||
category: string;
|
category: string;
|
||||||
@@ -331,7 +334,7 @@ export function AddFeatureDialog({
|
|||||||
toast.error(result?.error || 'Failed to enhance description');
|
toast.error(result?.error || 'Failed to enhance description');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Enhancement failed:', error);
|
logger.error('Enhancement failed:', error);
|
||||||
toast.error('Failed to enhance description');
|
toast.error('Failed to enhance description');
|
||||||
} finally {
|
} finally {
|
||||||
setIsEnhancing(false);
|
setIsEnhancing(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -29,6 +30,8 @@ interface AgentOutputModalProps {
|
|||||||
|
|
||||||
type ViewMode = 'parsed' | 'raw' | 'changes';
|
type ViewMode = 'parsed' | 'raw' | 'changes';
|
||||||
|
|
||||||
|
const logger = createLogger('AgentOutputModal');
|
||||||
|
|
||||||
export function AgentOutputModal({
|
export function AgentOutputModal({
|
||||||
open,
|
open,
|
||||||
onClose,
|
onClose,
|
||||||
@@ -88,7 +91,7 @@ export function AgentOutputModal({
|
|||||||
setOutput('');
|
setOutput('');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load output:', error);
|
logger.error('Failed to load output:', error);
|
||||||
setOutput('');
|
setOutput('');
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@@ -105,11 +108,11 @@ export function AgentOutputModal({
|
|||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.autoMode) return;
|
if (!api?.autoMode) return;
|
||||||
|
|
||||||
console.log('[AgentOutputModal] Subscribing to events for featureId:', featureId);
|
logger.info('Subscribing to events for featureId:', featureId);
|
||||||
|
|
||||||
const unsubscribe = api.autoMode.onEvent((event) => {
|
const unsubscribe = api.autoMode.onEvent((event) => {
|
||||||
console.log(
|
logger.debug(
|
||||||
'[AgentOutputModal] Received event:',
|
'Received event:',
|
||||||
event.type,
|
event.type,
|
||||||
'featureId:',
|
'featureId:',
|
||||||
'featureId' in event ? event.featureId : 'none',
|
'featureId' in event ? event.featureId : 'none',
|
||||||
@@ -119,7 +122,7 @@ export function AgentOutputModal({
|
|||||||
|
|
||||||
// Filter events for this specific feature only (skip events without featureId)
|
// Filter events for this specific feature only (skip events without featureId)
|
||||||
if ('featureId' in event && event.featureId !== featureId) {
|
if ('featureId' in event && event.featureId !== featureId) {
|
||||||
console.log('[AgentOutputModal] Skipping event - featureId mismatch');
|
logger.debug('Skipping event - featureId mismatch');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -22,6 +23,8 @@ interface WorktreeInfo {
|
|||||||
changedFilesCount?: number;
|
changedFilesCount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logger = createLogger('CreateBranchDialog');
|
||||||
|
|
||||||
interface CreateBranchDialogProps {
|
interface CreateBranchDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
@@ -77,7 +80,7 @@ export function CreateBranchDialog({
|
|||||||
setError(result.error || 'Failed to create branch');
|
setError(result.error || 'Failed to create branch');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Create branch failed:', err);
|
logger.error('Create branch failed:', err);
|
||||||
setError('Failed to create branch');
|
setError('Failed to create branch');
|
||||||
} finally {
|
} finally {
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -57,6 +58,8 @@ import {
|
|||||||
import { DependencyTreeDialog } from './dependency-tree-dialog';
|
import { DependencyTreeDialog } from './dependency-tree-dialog';
|
||||||
import { isCursorModel, PROVIDER_PREFIXES } from '@automaker/types';
|
import { isCursorModel, PROVIDER_PREFIXES } from '@automaker/types';
|
||||||
|
|
||||||
|
const logger = createLogger('EditFeatureDialog');
|
||||||
|
|
||||||
interface EditFeatureDialogProps {
|
interface EditFeatureDialogProps {
|
||||||
feature: Feature | null;
|
feature: Feature | null;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -248,7 +251,7 @@ export function EditFeatureDialog({
|
|||||||
toast.error(result?.error || 'Failed to enhance description');
|
toast.error(result?.error || 'Failed to enhance description');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Enhancement failed:', error);
|
logger.error('Enhancement failed:', error);
|
||||||
toast.error('Failed to enhance description');
|
toast.error('Failed to enhance description');
|
||||||
} finally {
|
} finally {
|
||||||
setIsEnhancing(false);
|
setIsEnhancing(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useRef, useState, useCallback } from 'react';
|
import { useEffect, useRef, useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -34,6 +35,8 @@ import { useAppStore, Feature } from '@/store/app-store';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { LogViewer } from '@/components/ui/log-viewer';
|
import { LogViewer } from '@/components/ui/log-viewer';
|
||||||
|
|
||||||
|
const logger = createLogger('FeatureSuggestions');
|
||||||
|
|
||||||
interface FeatureSuggestionsDialogProps {
|
interface FeatureSuggestionsDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -176,7 +179,7 @@ export function FeatureSuggestionsDialog({
|
|||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to generate suggestions:', error);
|
logger.error('Failed to generate suggestions:', error);
|
||||||
toast.error('Failed to start generation');
|
toast.error('Failed to start generation');
|
||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
}
|
}
|
||||||
@@ -194,7 +197,7 @@ export function FeatureSuggestionsDialog({
|
|||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
toast.info('Generation stopped');
|
toast.info('Generation stopped');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to stop generation:', error);
|
logger.error('Failed to stop generation:', error);
|
||||||
}
|
}
|
||||||
}, [setIsGenerating]);
|
}, [setIsGenerating]);
|
||||||
|
|
||||||
@@ -280,7 +283,7 @@ export function FeatureSuggestionsDialog({
|
|||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to import features:', error);
|
logger.error('Failed to import features:', error);
|
||||||
toast.error('Failed to import features');
|
toast.error('Failed to import features');
|
||||||
} finally {
|
} finally {
|
||||||
setIsImporting(false);
|
setIsImporting(false);
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import { toast } from 'sonner';
|
|||||||
import { useAutoMode } from '@/hooks/use-auto-mode';
|
import { useAutoMode } from '@/hooks/use-auto-mode';
|
||||||
import { truncateDescription } from '@/lib/utils';
|
import { truncateDescription } from '@/lib/utils';
|
||||||
import { getBlockingDependencies } from '@automaker/dependency-resolver';
|
import { getBlockingDependencies } from '@automaker/dependency-resolver';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('BoardActions');
|
||||||
|
|
||||||
interface UseBoardActionsProps {
|
interface UseBoardActionsProps {
|
||||||
currentProject: { path: string; id: string } | null;
|
currentProject: { path: string; id: string } | null;
|
||||||
@@ -112,8 +115,8 @@ export function useBoardActions({
|
|||||||
if (api?.worktree?.create) {
|
if (api?.worktree?.create) {
|
||||||
const result = await api.worktree.create(currentProject.path, finalBranchName);
|
const result = await api.worktree.create(currentProject.path, finalBranchName);
|
||||||
if (result.success && result.worktree) {
|
if (result.success && result.worktree) {
|
||||||
console.log(
|
logger.info(
|
||||||
`[Board] Worktree for branch "${finalBranchName}" ${
|
`Worktree for branch "${finalBranchName}" ${
|
||||||
result.worktree?.isNew ? 'created' : 'already exists'
|
result.worktree?.isNew ? 'created' : 'already exists'
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
@@ -125,8 +128,8 @@ export function useBoardActions({
|
|||||||
// Refresh worktree list in UI
|
// Refresh worktree list in UI
|
||||||
onWorktreeCreated?.();
|
onWorktreeCreated?.();
|
||||||
} else if (!result.success) {
|
} else if (!result.success) {
|
||||||
console.error(
|
logger.error(
|
||||||
`[Board] Failed to create worktree for branch "${finalBranchName}":`,
|
`Failed to create worktree for branch "${finalBranchName}":`,
|
||||||
result.error
|
result.error
|
||||||
);
|
);
|
||||||
toast.error('Failed to create worktree', {
|
toast.error('Failed to create worktree', {
|
||||||
@@ -135,7 +138,7 @@ export function useBoardActions({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error creating worktree:', error);
|
logger.error('Error creating worktree:', error);
|
||||||
toast.error('Failed to create worktree', {
|
toast.error('Failed to create worktree', {
|
||||||
description: error instanceof Error ? error.message : 'An error occurred',
|
description: error instanceof Error ? error.message : 'An error occurred',
|
||||||
});
|
});
|
||||||
@@ -180,7 +183,7 @@ export function useBoardActions({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('[Board] Error generating title:', error);
|
logger.error('Error generating title:', error);
|
||||||
// Clear generating flag on error
|
// Clear generating flag on error
|
||||||
const titleUpdates = { titleGenerating: false };
|
const titleUpdates = { titleGenerating: false };
|
||||||
updateFeature(createdFeature.id, titleUpdates);
|
updateFeature(createdFeature.id, titleUpdates);
|
||||||
@@ -229,16 +232,16 @@ export function useBoardActions({
|
|||||||
if (api?.worktree?.create) {
|
if (api?.worktree?.create) {
|
||||||
const result = await api.worktree.create(currentProject.path, finalBranchName);
|
const result = await api.worktree.create(currentProject.path, finalBranchName);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log(
|
logger.info(
|
||||||
`[Board] Worktree for branch "${finalBranchName}" ${
|
`Worktree for branch "${finalBranchName}" ${
|
||||||
result.worktree?.isNew ? 'created' : 'already exists'
|
result.worktree?.isNew ? 'created' : 'already exists'
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
// Refresh worktree list in UI
|
// Refresh worktree list in UI
|
||||||
onWorktreeCreated?.();
|
onWorktreeCreated?.();
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
logger.error(
|
||||||
`[Board] Failed to create worktree for branch "${finalBranchName}":`,
|
`Failed to create worktree for branch "${finalBranchName}":`,
|
||||||
result.error
|
result.error
|
||||||
);
|
);
|
||||||
toast.error('Failed to create worktree', {
|
toast.error('Failed to create worktree', {
|
||||||
@@ -247,7 +250,7 @@ export function useBoardActions({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error creating worktree:', error);
|
logger.error('Error creating worktree:', error);
|
||||||
toast.error('Failed to create worktree', {
|
toast.error('Failed to create worktree', {
|
||||||
description: error instanceof Error ? error.message : 'An error occurred',
|
description: error instanceof Error ? error.message : 'An error occurred',
|
||||||
});
|
});
|
||||||
@@ -292,7 +295,7 @@ export function useBoardActions({
|
|||||||
description: `Stopped and deleted: ${truncateDescription(feature.description)}`,
|
description: `Stopped and deleted: ${truncateDescription(feature.description)}`,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error stopping feature before delete:', error);
|
logger.error('Error stopping feature before delete:', error);
|
||||||
toast.error('Failed to stop agent', {
|
toast.error('Failed to stop agent', {
|
||||||
description: 'The feature will still be deleted.',
|
description: 'The feature will still be deleted.',
|
||||||
});
|
});
|
||||||
@@ -305,13 +308,13 @@ export function useBoardActions({
|
|||||||
for (const imagePathObj of feature.imagePaths) {
|
for (const imagePathObj of feature.imagePaths) {
|
||||||
try {
|
try {
|
||||||
await api.deleteFile(imagePathObj.path);
|
await api.deleteFile(imagePathObj.path);
|
||||||
console.log(`[Board] Deleted image: ${imagePathObj.path}`);
|
logger.info(`Deleted image: ${imagePathObj.path}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Board] Failed to delete image ${imagePathObj.path}:`, error);
|
logger.error(`Failed to delete image ${imagePathObj.path}:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Board] Error deleting images for feature ${featureId}:`, error);
|
logger.error(`Error deleting images for feature ${featureId}:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +331,7 @@ export function useBoardActions({
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.autoMode) {
|
if (!api?.autoMode) {
|
||||||
console.error('Auto mode API not available');
|
logger.error('Auto mode API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,16 +344,13 @@ export function useBoardActions({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log(
|
logger.info('Feature run started successfully, branch:', feature.branchName || 'default');
|
||||||
'[Board] Feature run started successfully, branch:',
|
|
||||||
feature.branchName || 'default'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
console.error('[Board] Failed to run feature:', result.error);
|
logger.error('Failed to run feature:', result.error);
|
||||||
await loadFeatures();
|
await loadFeatures();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error running feature:', error);
|
logger.error('Error running feature:', error);
|
||||||
await loadFeatures();
|
await loadFeatures();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -392,7 +392,7 @@ export function useBoardActions({
|
|||||||
updateFeature(feature.id, updates);
|
updateFeature(feature.id, updates);
|
||||||
// Must await to ensure feature status is persisted before starting agent
|
// Must await to ensure feature status is persisted before starting agent
|
||||||
await persistFeatureUpdate(feature.id, updates);
|
await persistFeatureUpdate(feature.id, updates);
|
||||||
console.log('[Board] Feature moved to in_progress, starting agent...');
|
logger.info('Feature moved to in_progress, starting agent...');
|
||||||
await handleRunFeature(feature);
|
await handleRunFeature(feature);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@@ -413,20 +413,20 @@ export function useBoardActions({
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.autoMode) {
|
if (!api?.autoMode) {
|
||||||
console.error('Auto mode API not available');
|
logger.error('Auto mode API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await api.autoMode.verifyFeature(currentProject.path, feature.id);
|
const result = await api.autoMode.verifyFeature(currentProject.path, feature.id);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log('[Board] Feature verification started successfully');
|
logger.info('Feature verification started successfully');
|
||||||
} else {
|
} else {
|
||||||
console.error('[Board] Failed to verify feature:', result.error);
|
logger.error('Failed to verify feature:', result.error);
|
||||||
await loadFeatures();
|
await loadFeatures();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error verifying feature:', error);
|
logger.error('Error verifying feature:', error);
|
||||||
await loadFeatures();
|
await loadFeatures();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -435,20 +435,20 @@ export function useBoardActions({
|
|||||||
|
|
||||||
const handleResumeFeature = useCallback(
|
const handleResumeFeature = useCallback(
|
||||||
async (feature: Feature) => {
|
async (feature: Feature) => {
|
||||||
console.log('[Board] handleResumeFeature called for feature:', feature.id);
|
logger.info('handleResumeFeature called for feature:', feature.id);
|
||||||
if (!currentProject) {
|
if (!currentProject) {
|
||||||
console.error('[Board] No current project');
|
logger.error('No current project');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.autoMode) {
|
if (!api?.autoMode) {
|
||||||
console.error('[Board] Auto mode API not available');
|
logger.error('Auto mode API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[Board] Calling resumeFeature API...', {
|
logger.info('Calling resumeFeature API...', {
|
||||||
projectPath: currentProject.path,
|
projectPath: currentProject.path,
|
||||||
featureId: feature.id,
|
featureId: feature.id,
|
||||||
useWorktrees,
|
useWorktrees,
|
||||||
@@ -460,16 +460,16 @@ export function useBoardActions({
|
|||||||
useWorktrees
|
useWorktrees
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('[Board] resumeFeature result:', result);
|
logger.info('resumeFeature result:', result);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log('[Board] Feature resume started successfully');
|
logger.info('Feature resume started successfully');
|
||||||
} else {
|
} else {
|
||||||
console.error('[Board] Failed to resume feature:', result.error);
|
logger.error('Failed to resume feature:', result.error);
|
||||||
await loadFeatures();
|
await loadFeatures();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error resuming feature:', error);
|
logger.error('Error resuming feature:', error);
|
||||||
await loadFeatures();
|
await loadFeatures();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -523,7 +523,7 @@ export function useBoardActions({
|
|||||||
|
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.autoMode?.followUpFeature) {
|
if (!api?.autoMode?.followUpFeature) {
|
||||||
console.error('Follow-up feature API not available');
|
logger.error('Follow-up feature API not available');
|
||||||
toast.error('Follow-up not available', {
|
toast.error('Follow-up not available', {
|
||||||
description: 'This feature is not available in the current version.',
|
description: 'This feature is not available in the current version.',
|
||||||
});
|
});
|
||||||
@@ -559,7 +559,7 @@ export function useBoardActions({
|
|||||||
// No worktreePath - server derives from feature.branchName
|
// No worktreePath - server derives from feature.branchName
|
||||||
)
|
)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('[Board] Error sending follow-up:', error);
|
logger.error('Error sending follow-up:', error);
|
||||||
toast.error('Failed to send follow-up', {
|
toast.error('Failed to send follow-up', {
|
||||||
description: error instanceof Error ? error.message : 'An error occurred',
|
description: error instanceof Error ? error.message : 'An error occurred',
|
||||||
});
|
});
|
||||||
@@ -587,7 +587,7 @@ export function useBoardActions({
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.autoMode?.commitFeature) {
|
if (!api?.autoMode?.commitFeature) {
|
||||||
console.error('Commit feature API not available');
|
logger.error('Commit feature API not available');
|
||||||
toast.error('Commit not available', {
|
toast.error('Commit not available', {
|
||||||
description: 'This feature is not available in the current version.',
|
description: 'This feature is not available in the current version.',
|
||||||
});
|
});
|
||||||
@@ -610,14 +610,14 @@ export function useBoardActions({
|
|||||||
// Refresh worktree selector to update commit counts
|
// Refresh worktree selector to update commit counts
|
||||||
onWorktreeCreated?.();
|
onWorktreeCreated?.();
|
||||||
} else {
|
} else {
|
||||||
console.error('[Board] Failed to commit feature:', result.error);
|
logger.error('Failed to commit feature:', result.error);
|
||||||
toast.error('Failed to commit feature', {
|
toast.error('Failed to commit feature', {
|
||||||
description: result.error || 'An error occurred',
|
description: result.error || 'An error occurred',
|
||||||
});
|
});
|
||||||
await loadFeatures();
|
await loadFeatures();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error committing feature:', error);
|
logger.error('Error committing feature:', error);
|
||||||
toast.error('Failed to commit feature', {
|
toast.error('Failed to commit feature', {
|
||||||
description: error instanceof Error ? error.message : 'An error occurred',
|
description: error instanceof Error ? error.message : 'An error occurred',
|
||||||
});
|
});
|
||||||
@@ -634,7 +634,7 @@ export function useBoardActions({
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.worktree?.mergeFeature) {
|
if (!api?.worktree?.mergeFeature) {
|
||||||
console.error('Worktree API not available');
|
logger.error('Worktree API not available');
|
||||||
toast.error('Merge not available', {
|
toast.error('Merge not available', {
|
||||||
description: 'This feature is not available in the current version.',
|
description: 'This feature is not available in the current version.',
|
||||||
});
|
});
|
||||||
@@ -651,13 +651,13 @@ export function useBoardActions({
|
|||||||
)}`,
|
)}`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error('[Board] Failed to merge feature:', result.error);
|
logger.error('Failed to merge feature:', result.error);
|
||||||
toast.error('Failed to merge feature', {
|
toast.error('Failed to merge feature', {
|
||||||
description: result.error || 'An error occurred',
|
description: result.error || 'An error occurred',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error merging feature:', error);
|
logger.error('Error merging feature:', error);
|
||||||
toast.error('Failed to merge feature', {
|
toast.error('Failed to merge feature', {
|
||||||
description: error instanceof Error ? error.message : 'An error occurred',
|
description: error instanceof Error ? error.message : 'An error occurred',
|
||||||
});
|
});
|
||||||
@@ -747,7 +747,7 @@ export function useBoardActions({
|
|||||||
: `Stopped working on: ${truncateDescription(feature.description)}`,
|
: `Stopped working on: ${truncateDescription(feature.description)}`,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error stopping feature:', error);
|
logger.error('Error stopping feature:', error);
|
||||||
toast.error('Failed to stop agent', {
|
toast.error('Failed to stop agent', {
|
||||||
description: error instanceof Error ? error.message : 'An error occurred',
|
description: error instanceof Error ? error.message : 'An error occurred',
|
||||||
});
|
});
|
||||||
@@ -857,7 +857,7 @@ export function useBoardActions({
|
|||||||
try {
|
try {
|
||||||
await autoMode.stopFeature(feature.id);
|
await autoMode.stopFeature(feature.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Error stopping feature before archive:', error);
|
logger.error('Error stopping feature before archive:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Archive the feature by setting status to completed
|
// Archive the feature by setting status to completed
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
|
import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
|
||||||
import { Feature } from '@/store/app-store';
|
import { Feature } from '@/store/app-store';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { COLUMNS, ColumnId } from '../constants';
|
import { COLUMNS, ColumnId } from '../constants';
|
||||||
|
|
||||||
|
const logger = createLogger('BoardDragDrop');
|
||||||
|
|
||||||
interface UseBoardDragDropProps {
|
interface UseBoardDragDropProps {
|
||||||
features: Feature[];
|
features: Feature[];
|
||||||
currentProject: { path: string; id: string } | null;
|
currentProject: { path: string; id: string } | null;
|
||||||
@@ -63,7 +66,7 @@ export function useBoardDragDrop({
|
|||||||
if (draggedFeature.status === 'in_progress') {
|
if (draggedFeature.status === 'in_progress') {
|
||||||
// Only allow dragging in_progress if it's not currently running
|
// Only allow dragging in_progress if it's not currently running
|
||||||
if (isRunningTask) {
|
if (isRunningTask) {
|
||||||
console.log('[Board] Cannot drag feature - currently running');
|
logger.debug('Cannot drag feature - currently running');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('BoardEffects');
|
||||||
|
|
||||||
interface UseBoardEffectsProps {
|
interface UseBoardEffectsProps {
|
||||||
currentProject: { path: string; id: string } | null;
|
currentProject: { path: string; id: string } | null;
|
||||||
@@ -70,12 +73,7 @@ export function useBoardEffects({
|
|||||||
if (!api.specRegeneration) return;
|
if (!api.specRegeneration) return;
|
||||||
|
|
||||||
const unsubscribe = api.specRegeneration.onEvent((event) => {
|
const unsubscribe = api.specRegeneration.onEvent((event) => {
|
||||||
console.log(
|
logger.info('Spec regeneration event:', event.type, 'for project:', event.projectPath);
|
||||||
'[BoardView] Spec regeneration event:',
|
|
||||||
event.type,
|
|
||||||
'for project:',
|
|
||||||
event.projectPath
|
|
||||||
);
|
|
||||||
|
|
||||||
if (event.projectPath !== specCreatingForProject) {
|
if (event.projectPath !== specCreatingForProject) {
|
||||||
return;
|
return;
|
||||||
@@ -108,7 +106,7 @@ export function useBoardEffects({
|
|||||||
const { clearRunningTasks, addRunningTask } = useAppStore.getState();
|
const { clearRunningTasks, addRunningTask } = useAppStore.getState();
|
||||||
|
|
||||||
if (status.runningFeatures) {
|
if (status.runningFeatures) {
|
||||||
console.log('[Board] Syncing running tasks from backend:', status.runningFeatures);
|
logger.info('Syncing running tasks from backend:', status.runningFeatures);
|
||||||
|
|
||||||
clearRunningTasks(projectId);
|
clearRunningTasks(projectId);
|
||||||
|
|
||||||
@@ -118,7 +116,7 @@ export function useBoardEffects({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Board] Failed to sync running tasks:', error);
|
logger.error('Failed to sync running tasks:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { useState, useCallback, useEffect, useRef } from 'react';
|
|||||||
import { useAppStore, Feature } from '@/store/app-store';
|
import { useAppStore, Feature } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('BoardFeatures');
|
||||||
|
|
||||||
interface UseBoardFeaturesProps {
|
interface UseBoardFeaturesProps {
|
||||||
currentProject: { path: string; id: string } | null;
|
currentProject: { path: string; id: string } | null;
|
||||||
@@ -32,7 +35,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
|||||||
// If project switched, mark it but don't clear features yet
|
// If project switched, mark it but don't clear features yet
|
||||||
// We'll clear after successful API load to prevent data loss
|
// We'll clear after successful API load to prevent data loss
|
||||||
if (isProjectSwitch) {
|
if (isProjectSwitch) {
|
||||||
console.log(`[BoardView] Project switch detected: ${previousPath} -> ${currentPath}`);
|
logger.info(`Project switch detected: ${previousPath} -> ${currentPath}`);
|
||||||
isSwitchingProjectRef.current = true;
|
isSwitchingProjectRef.current = true;
|
||||||
isInitialLoadRef.current = true;
|
isInitialLoadRef.current = true;
|
||||||
}
|
}
|
||||||
@@ -48,7 +51,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api.features) {
|
if (!api.features) {
|
||||||
console.error('[BoardView] Features API not available');
|
logger.error('Features API not available');
|
||||||
// Keep cached features if API is unavailable
|
// Keep cached features if API is unavailable
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -73,7 +76,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
|||||||
setPersistedCategories([]);
|
setPersistedCategories([]);
|
||||||
}
|
}
|
||||||
} else if (!result.success && result.error) {
|
} else if (!result.success && result.error) {
|
||||||
console.error('[BoardView] API returned error:', result.error);
|
logger.error('API returned error:', result.error);
|
||||||
// If it's a new project or the error indicates no features found,
|
// If it's a new project or the error indicates no features found,
|
||||||
// that's expected - start with empty array
|
// that's expected - start with empty array
|
||||||
if (isProjectSwitch) {
|
if (isProjectSwitch) {
|
||||||
@@ -83,7 +86,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
|||||||
// Otherwise keep cached features
|
// Otherwise keep cached features
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load features:', error);
|
logger.error('Failed to load features:', error);
|
||||||
// On error, keep existing cached features for the current project
|
// On error, keep existing cached features for the current project
|
||||||
// Only clear on project switch if we have no features from server
|
// Only clear on project switch if we have no features from server
|
||||||
if (isProjectSwitch && cachedFeatures.length === 0) {
|
if (isProjectSwitch && cachedFeatures.length === 0) {
|
||||||
@@ -115,7 +118,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
|||||||
setPersistedCategories([]);
|
setPersistedCategories([]);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load categories:', error);
|
logger.error('Failed to load categories:', error);
|
||||||
// If file doesn't exist, ensure categories are cleared
|
// If file doesn't exist, ensure categories are cleared
|
||||||
setPersistedCategories([]);
|
setPersistedCategories([]);
|
||||||
}
|
}
|
||||||
@@ -147,7 +150,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
|||||||
setPersistedCategories(categories);
|
setPersistedCategories(categories);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save category:', error);
|
logger.error('Failed to save category:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currentProject, persistedCategories]
|
[currentProject, persistedCategories]
|
||||||
@@ -165,7 +168,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
|||||||
currentProject &&
|
currentProject &&
|
||||||
event.projectPath === currentProject.path
|
event.projectPath === currentProject.path
|
||||||
) {
|
) {
|
||||||
console.log('[BoardView] Spec regeneration complete, refreshing features');
|
logger.info('Spec regeneration complete, refreshing features');
|
||||||
loadFeatures();
|
loadFeatures();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -190,27 +193,27 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
|||||||
|
|
||||||
if (event.type === 'auto_mode_feature_complete') {
|
if (event.type === 'auto_mode_feature_complete') {
|
||||||
// Reload features when a feature is completed
|
// Reload features when a feature is completed
|
||||||
console.log('[Board] Feature completed, reloading features...');
|
logger.info('Feature completed, reloading features...');
|
||||||
loadFeatures();
|
loadFeatures();
|
||||||
// Play ding sound when feature is done (unless muted)
|
// Play ding sound when feature is done (unless muted)
|
||||||
const { muteDoneSound } = useAppStore.getState();
|
const { muteDoneSound } = useAppStore.getState();
|
||||||
if (!muteDoneSound) {
|
if (!muteDoneSound) {
|
||||||
const audio = new Audio('/sounds/ding.mp3');
|
const audio = new Audio('/sounds/ding.mp3');
|
||||||
audio.play().catch((err) => console.warn('Could not play ding sound:', err));
|
audio.play().catch((err) => logger.warn('Could not play ding sound:', err));
|
||||||
}
|
}
|
||||||
} else if (event.type === 'plan_approval_required') {
|
} else if (event.type === 'plan_approval_required') {
|
||||||
// Reload features when plan is generated and requires approval
|
// Reload features when plan is generated and requires approval
|
||||||
// This ensures the feature card shows the "Approve Plan" button
|
// This ensures the feature card shows the "Approve Plan" button
|
||||||
console.log('[Board] Plan approval required, reloading features...');
|
logger.info('Plan approval required, reloading features...');
|
||||||
loadFeatures();
|
loadFeatures();
|
||||||
} else if (event.type === 'pipeline_step_started') {
|
} else if (event.type === 'pipeline_step_started') {
|
||||||
// Pipeline steps update the feature status to `pipeline_*` before the step runs.
|
// Pipeline steps update the feature status to `pipeline_*` before the step runs.
|
||||||
// Reload so the card moves into the correct pipeline column immediately.
|
// Reload so the card moves into the correct pipeline column immediately.
|
||||||
console.log('[Board] Pipeline step started, reloading features...');
|
logger.info('Pipeline step started, reloading features...');
|
||||||
loadFeatures();
|
loadFeatures();
|
||||||
} else if (event.type === 'auto_mode_error') {
|
} else if (event.type === 'auto_mode_error') {
|
||||||
// Reload features when an error occurs (feature moved to waiting_approval)
|
// Reload features when an error occurs (feature moved to waiting_approval)
|
||||||
console.log('[Board] Feature error, reloading features...', event.error);
|
logger.info('Feature error, reloading features...', event.error);
|
||||||
|
|
||||||
// Remove from running tasks so it moves to the correct column
|
// Remove from running tasks so it moves to the correct column
|
||||||
if (event.featureId) {
|
if (event.featureId) {
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { useCallback } from 'react';
|
|||||||
import { Feature } from '@/store/app-store';
|
import { Feature } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('BoardPersistence');
|
||||||
|
|
||||||
interface UseBoardPersistenceProps {
|
interface UseBoardPersistenceProps {
|
||||||
currentProject: { path: string; id: string } | null;
|
currentProject: { path: string; id: string } | null;
|
||||||
@@ -18,7 +21,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api.features) {
|
if (!api.features) {
|
||||||
console.error('[BoardView] Features API not available');
|
logger.error('Features API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +30,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
|||||||
updateFeature(result.feature.id, result.feature);
|
updateFeature(result.feature.id, result.feature);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to persist feature update:', error);
|
logger.error('Failed to persist feature update:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currentProject, updateFeature]
|
[currentProject, updateFeature]
|
||||||
@@ -41,7 +44,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api.features) {
|
if (!api.features) {
|
||||||
console.error('[BoardView] Features API not available');
|
logger.error('Features API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +53,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
|||||||
updateFeature(result.feature.id, result.feature);
|
updateFeature(result.feature.id, result.feature);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to persist feature creation:', error);
|
logger.error('Failed to persist feature creation:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currentProject, updateFeature]
|
[currentProject, updateFeature]
|
||||||
@@ -64,13 +67,13 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api.features) {
|
if (!api.features) {
|
||||||
console.error('[BoardView] Features API not available');
|
logger.error('Features API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await api.features.delete(currentProject.path, featureId);
|
await api.features.delete(currentProject.path, featureId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to persist feature deletion:', error);
|
logger.error('Failed to persist feature deletion:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currentProject]
|
[currentProject]
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import type { BranchInfo, GitRepoStatus } from '../types';
|
import type { BranchInfo, GitRepoStatus } from '../types';
|
||||||
|
|
||||||
|
const logger = createLogger('Branches');
|
||||||
|
|
||||||
export function useBranches() {
|
export function useBranches() {
|
||||||
const [branches, setBranches] = useState<BranchInfo[]>([]);
|
const [branches, setBranches] = useState<BranchInfo[]>([]);
|
||||||
const [aheadCount, setAheadCount] = useState(0);
|
const [aheadCount, setAheadCount] = useState(0);
|
||||||
@@ -26,7 +29,7 @@ export function useBranches() {
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.worktree?.listBranches) {
|
if (!api?.worktree?.listBranches) {
|
||||||
console.warn('List branches API not available');
|
logger.warn('List branches API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await api.worktree.listBranches(worktreePath);
|
const result = await api.worktree.listBranches(worktreePath);
|
||||||
@@ -45,11 +48,11 @@ export function useBranches() {
|
|||||||
setGitRepoStatus({ isGitRepo: true, hasCommits: false });
|
setGitRepoStatus({ isGitRepo: true, hasCommits: false });
|
||||||
} else if (!result.success) {
|
} else if (!result.success) {
|
||||||
// Other errors - log them
|
// Other errors - log them
|
||||||
console.warn('Failed to fetch branches:', result.error);
|
logger.warn('Failed to fetch branches:', result.error);
|
||||||
resetBranchState();
|
resetBranchState();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch branches:', error);
|
logger.error('Failed to fetch branches:', error);
|
||||||
resetBranchState();
|
resetBranchState();
|
||||||
// Reset git status to unknown state on network/API errors
|
// Reset git status to unknown state on network/API errors
|
||||||
setGitRepoStatus({ isGitRepo: true, hasCommits: true });
|
setGitRepoStatus({ isGitRepo: true, hasCommits: true });
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('DefaultEditor');
|
||||||
|
|
||||||
export function useDefaultEditor() {
|
export function useDefaultEditor() {
|
||||||
const [defaultEditorName, setDefaultEditorName] = useState<string>('Editor');
|
const [defaultEditorName, setDefaultEditorName] = useState<string>('Editor');
|
||||||
|
|
||||||
@@ -15,7 +18,7 @@ export function useDefaultEditor() {
|
|||||||
setDefaultEditorName(result.result.editorName);
|
setDefaultEditorName(result.result.editorName);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch default editor:', error);
|
logger.error('Failed to fetch default editor:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { normalizePath } from '@/lib/utils';
|
import { normalizePath } from '@/lib/utils';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import type { DevServerInfo, WorktreeInfo } from '../types';
|
import type { DevServerInfo, WorktreeInfo } from '../types';
|
||||||
|
|
||||||
|
const logger = createLogger('DevServers');
|
||||||
|
|
||||||
interface UseDevServersOptions {
|
interface UseDevServersOptions {
|
||||||
projectPath: string;
|
projectPath: string;
|
||||||
}
|
}
|
||||||
@@ -27,7 +30,7 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
|
|||||||
setRunningDevServers(serversMap);
|
setRunningDevServers(serversMap);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch dev servers:', error);
|
logger.error('Failed to fetch dev servers:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -73,7 +76,7 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
|
|||||||
toast.error(result.error || 'Failed to start dev server');
|
toast.error(result.error || 'Failed to start dev server');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Start dev server failed:', error);
|
logger.error('Start dev server failed:', error);
|
||||||
toast.error('Failed to start dev server');
|
toast.error('Failed to start dev server');
|
||||||
} finally {
|
} finally {
|
||||||
setIsStartingDevServer(false);
|
setIsStartingDevServer(false);
|
||||||
@@ -105,7 +108,7 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
|
|||||||
toast.error(result.error || 'Failed to stop dev server');
|
toast.error(result.error || 'Failed to stop dev server');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Stop dev server failed:', error);
|
logger.error('Stop dev server failed:', error);
|
||||||
toast.error('Failed to stop dev server');
|
toast.error('Failed to stop dev server');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import type { WorktreeInfo } from '../types';
|
import type { WorktreeInfo } from '../types';
|
||||||
|
|
||||||
|
const logger = createLogger('WorktreeActions');
|
||||||
|
|
||||||
// Error codes that need special user-friendly handling
|
// Error codes that need special user-friendly handling
|
||||||
const GIT_STATUS_ERROR_CODES = ['NOT_GIT_REPO', 'NO_COMMITS'] as const;
|
const GIT_STATUS_ERROR_CODES = ['NOT_GIT_REPO', 'NO_COMMITS'] as const;
|
||||||
type GitStatusErrorCode = (typeof GIT_STATUS_ERROR_CODES)[number];
|
type GitStatusErrorCode = (typeof GIT_STATUS_ERROR_CODES)[number];
|
||||||
@@ -56,7 +59,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
|||||||
toast.error(result.error || 'Failed to switch branch');
|
toast.error(result.error || 'Failed to switch branch');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Switch branch failed:', error);
|
logger.error('Switch branch failed:', error);
|
||||||
toast.error('Failed to switch branch');
|
toast.error('Failed to switch branch');
|
||||||
} finally {
|
} finally {
|
||||||
setIsSwitching(false);
|
setIsSwitching(false);
|
||||||
@@ -84,7 +87,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
|||||||
toast.error(result.error || 'Failed to pull latest changes');
|
toast.error(result.error || 'Failed to pull latest changes');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Pull failed:', error);
|
logger.error('Pull failed:', error);
|
||||||
toast.error('Failed to pull latest changes');
|
toast.error('Failed to pull latest changes');
|
||||||
} finally {
|
} finally {
|
||||||
setIsPulling(false);
|
setIsPulling(false);
|
||||||
@@ -113,7 +116,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
|||||||
toast.error(result.error || 'Failed to push changes');
|
toast.error(result.error || 'Failed to push changes');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Push failed:', error);
|
logger.error('Push failed:', error);
|
||||||
toast.error('Failed to push changes');
|
toast.error('Failed to push changes');
|
||||||
} finally {
|
} finally {
|
||||||
setIsPushing(false);
|
setIsPushing(false);
|
||||||
@@ -126,7 +129,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.worktree?.openInEditor) {
|
if (!api?.worktree?.openInEditor) {
|
||||||
console.warn('Open in editor API not available');
|
logger.warn('Open in editor API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await api.worktree.openInEditor(worktree.path);
|
const result = await api.worktree.openInEditor(worktree.path);
|
||||||
@@ -136,7 +139,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
|||||||
toast.error(result.error);
|
toast.error(result.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Open in editor failed:', error);
|
logger.error('Open in editor failed:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { pathsEqual } from '@/lib/utils';
|
import { pathsEqual } from '@/lib/utils';
|
||||||
import type { WorktreeInfo } from '../types';
|
import type { WorktreeInfo } from '../types';
|
||||||
|
|
||||||
|
const logger = createLogger('Worktrees');
|
||||||
|
|
||||||
interface UseWorktreesOptions {
|
interface UseWorktreesOptions {
|
||||||
projectPath: string;
|
projectPath: string;
|
||||||
refreshTrigger?: number;
|
refreshTrigger?: number;
|
||||||
@@ -33,7 +36,7 @@ export function useWorktrees({
|
|||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.worktree?.listAll) {
|
if (!api?.worktree?.listAll) {
|
||||||
console.warn('Worktree API not available');
|
logger.warn('Worktree API not available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await api.worktree.listAll(projectPath, true);
|
const result = await api.worktree.listAll(projectPath, true);
|
||||||
@@ -44,7 +47,7 @@ export function useWorktrees({
|
|||||||
// Return removed worktrees so they can be handled by the caller
|
// Return removed worktrees so they can be handled by the caller
|
||||||
return result.removedWorktrees;
|
return result.removedWorktrees;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch worktrees:', error);
|
logger.error('Failed to fetch worktrees:', error);
|
||||||
return undefined;
|
return undefined;
|
||||||
} finally {
|
} finally {
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState, useCallback } from 'react';
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
@@ -6,6 +7,8 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { File, Folder, FolderOpen, ChevronRight, ChevronDown, RefreshCw, Code } from 'lucide-react';
|
import { File, Folder, FolderOpen, ChevronRight, ChevronDown, RefreshCw, Code } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('CodeView');
|
||||||
|
|
||||||
interface FileTreeNode {
|
interface FileTreeNode {
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
@@ -60,7 +63,7 @@ export function CodeView() {
|
|||||||
setFileTree(entries);
|
setFileTree(entries);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load file tree:', error);
|
logger.error('Failed to load file tree:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -91,7 +94,7 @@ export function CodeView() {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load subdirectory:', error);
|
logger.error('Failed to load subdirectory:', error);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
@@ -107,7 +110,7 @@ export function CodeView() {
|
|||||||
setSelectedFile(path);
|
setSelectedFile(path);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load file:', error);
|
logger.error('Failed to load file:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState, useCallback, useMemo, useRef } from 'react';
|
import { useEffect, useState, useCallback, useMemo, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
@@ -38,6 +39,8 @@ import {
|
|||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('ContextView');
|
||||||
import { sanitizeFilename } from '@/lib/image-utils';
|
import { sanitizeFilename } from '@/lib/image-utils';
|
||||||
import { Markdown } from '../ui/markdown';
|
import { Markdown } from '../ui/markdown';
|
||||||
import {
|
import {
|
||||||
@@ -160,7 +163,7 @@ export function ContextView() {
|
|||||||
const metadataPath = `${contextPath}/context-metadata.json`;
|
const metadataPath = `${contextPath}/context-metadata.json`;
|
||||||
await api.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
await api.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save metadata:', error);
|
logger.error('Failed to save metadata:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[getContextPath]
|
[getContextPath]
|
||||||
@@ -202,7 +205,7 @@ export function ContextView() {
|
|||||||
setContextFiles(files);
|
setContextFiles(files);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load context files:', error);
|
logger.error('Failed to load context files:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -223,7 +226,7 @@ export function ContextView() {
|
|||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load file content:', error);
|
logger.error('Failed to load file content:', error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -247,7 +250,7 @@ export function ContextView() {
|
|||||||
setSelectedFile({ ...selectedFile, content: editedContent });
|
setSelectedFile({ ...selectedFile, content: editedContent });
|
||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save file:', error);
|
logger.error('Failed to save file:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@@ -279,7 +282,7 @@ export function ContextView() {
|
|||||||
result.error || `Automaker couldn't generate a description for “${fileName}”.`;
|
result.error || `Automaker couldn't generate a description for “${fileName}”.`;
|
||||||
toast.error('Failed to generate description', { description: message });
|
toast.error('Failed to generate description', { description: message });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to generate description:', error);
|
logger.error('Failed to generate description:', error);
|
||||||
const message =
|
const message =
|
||||||
error instanceof Error
|
error instanceof Error
|
||||||
? error.message
|
? error.message
|
||||||
@@ -315,7 +318,7 @@ export function ContextView() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to generate description:', error);
|
logger.error('Failed to generate description:', error);
|
||||||
} finally {
|
} finally {
|
||||||
// Remove from generating set
|
// Remove from generating set
|
||||||
setGeneratingDescriptions((prev) => {
|
setGeneratingDescriptions((prev) => {
|
||||||
@@ -401,7 +404,7 @@ export function ContextView() {
|
|||||||
// For images, use the path in the images directory
|
// For images, use the path in the images directory
|
||||||
generateDescriptionAsync(imagePathForDescription || filePath, fileName, isImage);
|
generateDescriptionAsync(imagePathForDescription || filePath, fileName, isImage);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to upload file:', error);
|
logger.error('Failed to upload file:', error);
|
||||||
toast.error('Failed to upload file', {
|
toast.error('Failed to upload file', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -492,7 +495,7 @@ export function ContextView() {
|
|||||||
setNewMarkdownDescription('');
|
setNewMarkdownDescription('');
|
||||||
setNewMarkdownContent('');
|
setNewMarkdownContent('');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create markdown:', error);
|
logger.error('Failed to create markdown:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -515,7 +518,7 @@ export function ContextView() {
|
|||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
await loadContextFiles();
|
await loadContextFiles();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to delete file:', error);
|
logger.error('Failed to delete file:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -537,14 +540,14 @@ export function ContextView() {
|
|||||||
// Check if file with new name already exists
|
// Check if file with new name already exists
|
||||||
const exists = await api.exists(newPath);
|
const exists = await api.exists(newPath);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
console.error('A file with this name already exists');
|
logger.error('A file with this name already exists');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read current file content
|
// Read current file content
|
||||||
const result = await api.readFile(selectedFile.path);
|
const result = await api.readFile(selectedFile.path);
|
||||||
if (!result.success || result.content === undefined) {
|
if (!result.success || result.content === undefined) {
|
||||||
console.error('Failed to read file for rename');
|
logger.error('Failed to read file for rename');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,7 +581,7 @@ export function ContextView() {
|
|||||||
};
|
};
|
||||||
setSelectedFile(renamedFile);
|
setSelectedFile(renamedFile);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to rename file:', error);
|
logger.error('Failed to rename file:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -603,7 +606,7 @@ export function ContextView() {
|
|||||||
setEditDescriptionValue('');
|
setEditDescriptionValue('');
|
||||||
setEditDescriptionFileName('');
|
setEditDescriptionFileName('');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save description:', error);
|
logger.error('Failed to save description:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -634,7 +637,7 @@ export function ContextView() {
|
|||||||
|
|
||||||
await loadContextFiles();
|
await loadContextFiles();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to delete file:', error);
|
logger.error('Failed to delete file:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useCallback, useMemo } from 'react';
|
import { useState, useCallback, useMemo } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { CircleDot, RefreshCw } from 'lucide-react';
|
import { CircleDot, RefreshCw } from 'lucide-react';
|
||||||
import { getElectronAPI, GitHubIssue, IssueValidationResult } from '@/lib/electron';
|
import { getElectronAPI, GitHubIssue, IssueValidationResult } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
@@ -14,6 +15,8 @@ import { formatDate, getFeaturePriority } from './github-issues-view/utils';
|
|||||||
import { useModelOverride } from '@/components/shared';
|
import { useModelOverride } from '@/components/shared';
|
||||||
import type { ValidateIssueOptions } from './github-issues-view/types';
|
import type { ValidateIssueOptions } from './github-issues-view/types';
|
||||||
|
|
||||||
|
const logger = createLogger('GitHubIssuesView');
|
||||||
|
|
||||||
export function GitHubIssuesView() {
|
export function GitHubIssuesView() {
|
||||||
const [selectedIssue, setSelectedIssue] = useState<GitHubIssue | null>(null);
|
const [selectedIssue, setSelectedIssue] = useState<GitHubIssue | null>(null);
|
||||||
const [validationResult, setValidationResult] = useState<IssueValidationResult | null>(null);
|
const [validationResult, setValidationResult] = useState<IssueValidationResult | null>(null);
|
||||||
@@ -118,7 +121,7 @@ export function GitHubIssuesView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[GitHubIssuesView] Convert to task error:', err);
|
logger.error('Convert to task error:', err);
|
||||||
toast.error(err instanceof Error ? err.message : 'Failed to create task');
|
toast.error(err instanceof Error ? err.message : 'Failed to create task');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -247,7 +250,7 @@ export function GitHubIssuesView() {
|
|||||||
confirmText="Re-validate"
|
confirmText="Re-validate"
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
if (selectedIssue && pendingRevalidateOptions) {
|
if (selectedIssue && pendingRevalidateOptions) {
|
||||||
console.log('[GitHubIssuesView] Revalidating with options:', {
|
logger.info('Revalidating with options:', {
|
||||||
commentsCount: pendingRevalidateOptions.comments?.length ?? 0,
|
commentsCount: pendingRevalidateOptions.comments?.length ?? 0,
|
||||||
linkedPRsCount: pendingRevalidateOptions.linkedPRs?.length ?? 0,
|
linkedPRsCount: pendingRevalidateOptions.linkedPRs?.length ?? 0,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI, GitHubIssue } from '@/lib/electron';
|
import { getElectronAPI, GitHubIssue } from '@/lib/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('GitHubIssues');
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
export function useGithubIssues() {
|
export function useGithubIssues() {
|
||||||
@@ -38,7 +41,7 @@ export function useGithubIssues() {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isMountedRef.current) {
|
if (isMountedRef.current) {
|
||||||
console.error('[GitHubIssuesView] Error fetching issues:', err);
|
logger.error('Error fetching issues:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to fetch issues');
|
setError(err instanceof Error ? err.message : 'Failed to fetch issues');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI, GitHubComment } from '@/lib/electron';
|
import { getElectronAPI, GitHubComment } from '@/lib/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('IssueComments');
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
interface UseIssueCommentsResult {
|
interface UseIssueCommentsResult {
|
||||||
@@ -69,7 +72,7 @@ export function useIssueComments(issueNumber: number | null): UseIssueCommentsRe
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isMountedRef.current) {
|
if (isMountedRef.current) {
|
||||||
console.error('[useIssueComments] Error fetching comments:', err);
|
logger.error('Error fetching comments:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to fetch comments');
|
setError(err instanceof Error ? err.message : 'Failed to fetch comments');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
getElectronAPI,
|
getElectronAPI,
|
||||||
GitHubIssue,
|
GitHubIssue,
|
||||||
@@ -12,6 +13,8 @@ import { useAppStore } from '@/store/app-store';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { isValidationStale } from '../utils';
|
import { isValidationStale } from '../utils';
|
||||||
|
|
||||||
|
const logger = createLogger('IssueValidation');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract model string from PhaseModelEntry or string (handles both formats)
|
* Extract model string from PhaseModelEntry or string (handles both formats)
|
||||||
*/
|
*/
|
||||||
@@ -78,7 +81,7 @@ export function useIssueValidation({
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isMounted) {
|
if (isMounted) {
|
||||||
console.error('[GitHubIssuesView] Failed to load cached validations:', err);
|
logger.error('Failed to load cached validations:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -107,7 +110,7 @@ export function useIssueValidation({
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isMounted) {
|
if (isMounted) {
|
||||||
console.error('[GitHubIssuesView] Failed to load running validations:', err);
|
logger.error('Failed to load running validations:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -283,7 +286,7 @@ export function useIssueValidation({
|
|||||||
// On success, the result will come through the event stream
|
// On success, the result will come through the event stream
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[GitHubIssuesView] Validation error:', err);
|
logger.error('Validation error:', err);
|
||||||
toast.error(err instanceof Error ? err.message : 'Failed to validate issue');
|
toast.error(err instanceof Error ? err.message : 'Failed to validate issue');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -325,7 +328,7 @@ export function useIssueValidation({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[GitHubIssuesView] Failed to mark validation as viewed:', err);
|
logger.error('Failed to mark validation as viewed:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { GitPullRequest, Loader2, RefreshCw, ExternalLink, GitMerge, X } from 'lucide-react';
|
import { GitPullRequest, Loader2, RefreshCw, ExternalLink, GitMerge, X } from 'lucide-react';
|
||||||
import { getElectronAPI, GitHubPR } from '@/lib/electron';
|
import { getElectronAPI, GitHubPR } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
@@ -6,6 +7,8 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Markdown } from '@/components/ui/markdown';
|
import { Markdown } from '@/components/ui/markdown';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('GitHubPRsView');
|
||||||
|
|
||||||
export function GitHubPRsView() {
|
export function GitHubPRsView() {
|
||||||
const [openPRs, setOpenPRs] = useState<GitHubPR[]>([]);
|
const [openPRs, setOpenPRs] = useState<GitHubPR[]>([]);
|
||||||
const [mergedPRs, setMergedPRs] = useState<GitHubPR[]>([]);
|
const [mergedPRs, setMergedPRs] = useState<GitHubPR[]>([]);
|
||||||
@@ -35,7 +38,7 @@ export function GitHubPRsView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[GitHubPRsView] Error fetching PRs:', err);
|
logger.error('Error fetching PRs:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to fetch pull requests');
|
setError(err instanceof Error ? err.message : 'Failed to fetch pull requests');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore, Feature } from '@/store/app-store';
|
import { useAppStore, Feature } from '@/store/app-store';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -12,6 +13,8 @@ import { toast } from 'sonner';
|
|||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
import { getDefaultWorkspaceDirectory, saveLastProjectDirectory } from '@/lib/workspace-config';
|
import { getDefaultWorkspaceDirectory, saveLastProjectDirectory } from '@/lib/workspace-config';
|
||||||
|
|
||||||
|
const logger = createLogger('InterviewView');
|
||||||
|
|
||||||
interface InterviewMessage {
|
interface InterviewMessage {
|
||||||
id: string;
|
id: string;
|
||||||
role: 'user' | 'assistant';
|
role: 'user' | 'assistant';
|
||||||
@@ -97,7 +100,7 @@ export function InterviewView() {
|
|||||||
setProjectPath(defaultDir);
|
setProjectPath(defaultDir);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load default workspace directory:', error);
|
logger.error('Failed to load default workspace directory:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -367,7 +370,7 @@ export function InterviewView() {
|
|||||||
addProject(project);
|
addProject(project);
|
||||||
setCurrentProject(project);
|
setCurrentProject(project);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create project:', error);
|
logger.error('Failed to create project:', error);
|
||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { Bot, Folder, Loader2, RefreshCw, Square, Activity, FileText } from 'lucide-react';
|
import { Bot, Folder, Loader2, RefreshCw, Square, Activity, FileText } from 'lucide-react';
|
||||||
import { getElectronAPI, RunningAgent } from '@/lib/electron';
|
import { getElectronAPI, RunningAgent } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
@@ -7,6 +8,8 @@ import { cn } from '@/lib/utils';
|
|||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
import { AgentOutputModal } from './board-view/dialogs/agent-output-modal';
|
import { AgentOutputModal } from './board-view/dialogs/agent-output-modal';
|
||||||
|
|
||||||
|
const logger = createLogger('RunningAgentsView');
|
||||||
|
|
||||||
export function RunningAgentsView() {
|
export function RunningAgentsView() {
|
||||||
const [runningAgents, setRunningAgents] = useState<RunningAgent[]>([]);
|
const [runningAgents, setRunningAgents] = useState<RunningAgent[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -25,7 +28,7 @@ export function RunningAgentsView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[RunningAgentsView] Error fetching running agents:', error);
|
logger.error('Error fetching running agents:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
@@ -78,7 +81,7 @@ export function RunningAgentsView() {
|
|||||||
fetchRunningAgents();
|
fetchRunningAgents();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[RunningAgentsView] Error stopping agent:', error);
|
logger.error('Error stopping agent:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[fetchRunningAgents]
|
[fetchRunningAgents]
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('ApiKeyManagement');
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import type { ProviderConfigParams } from '@/config/api-providers';
|
import type { ProviderConfigParams } from '@/config/api-providers';
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@ export function useApiKeyManagement() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to check API key status:', error);
|
logger.error('Failed to check API key status:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useSetupStore } from '@/store/setup-store';
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
|
|
||||||
|
const logger = createLogger('CliStatus');
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
interface CliStatusResult {
|
interface CliStatusResult {
|
||||||
@@ -40,7 +43,7 @@ export function useCliStatus() {
|
|||||||
const status = await api.checkClaudeCli();
|
const status = await api.checkClaudeCli();
|
||||||
setClaudeCliStatus(status);
|
setClaudeCliStatus(status);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to check Claude CLI status:', error);
|
logger.error('Failed to check Claude CLI status:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +87,7 @@ export function useCliStatus() {
|
|||||||
setClaudeAuthStatus(authStatus);
|
setClaudeAuthStatus(authStatus);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to check Claude auth status:', error);
|
logger.error('Failed to check Claude auth status:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -102,7 +105,7 @@ export function useCliStatus() {
|
|||||||
setClaudeCliStatus(status);
|
setClaudeCliStatus(status);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to refresh Claude CLI status:', error);
|
logger.error('Failed to refresh Claude CLI status:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsCheckingClaudeCli(false);
|
setIsCheckingClaudeCli(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
const logger = createLogger('CursorPermissions');
|
||||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
import type { CursorPermissionProfile } from '@automaker/types';
|
import type { CursorPermissionProfile } from '@automaker/types';
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ export function useCursorPermissions(projectPath?: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load Cursor permissions:', error);
|
logger.error('Failed to load Cursor permissions:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingPermissions(false);
|
setIsLoadingPermissions(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
const logger = createLogger('CursorStatus');
|
||||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
import { useSetupStore } from '@/store/setup-store';
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
|
|
||||||
@@ -48,7 +51,7 @@ export function useCursorStatus() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load Cursor settings:', error);
|
logger.error('Failed to load Cursor settings:', error);
|
||||||
toast.error('Failed to load Cursor settings');
|
toast.error('Failed to load Cursor settings');
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('MCPServers');
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import type { MCPServerConfig } from '@automaker/types';
|
import type { MCPServerConfig } from '@automaker/types';
|
||||||
import { syncSettingsToServer, loadMCPServersFromServer } from '@/hooks/use-settings-migration';
|
import { syncSettingsToServer, loadMCPServersFromServer } from '@/hooks/use-settings-migration';
|
||||||
@@ -72,7 +75,7 @@ export function useMCPServers() {
|
|||||||
// Auto-load MCP servers from settings file on mount
|
// Auto-load MCP servers from settings file on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadMCPServersFromServer().catch((error) => {
|
loadMCPServersFromServer().catch((error) => {
|
||||||
console.error('Failed to load MCP servers on mount:', error);
|
logger.error('Failed to load MCP servers on mount:', error);
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -431,7 +434,7 @@ export function useMCPServers() {
|
|||||||
|
|
||||||
if (serverData.type === 'stdio') {
|
if (serverData.type === 'stdio') {
|
||||||
if (!serverConfig.command) {
|
if (!serverConfig.command) {
|
||||||
console.warn(`Skipping ${name}: no command specified`);
|
logger.warn(`Skipping ${name}: no command specified`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +461,7 @@ export function useMCPServers() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!serverConfig.url) {
|
if (!serverConfig.url) {
|
||||||
console.warn(`Skipping ${name}: no url specified`);
|
logger.warn(`Skipping ${name}: no url specified`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
serverData.url = serverConfig.url as string;
|
serverData.url = serverConfig.url as string;
|
||||||
@@ -491,7 +494,7 @@ export function useMCPServers() {
|
|||||||
const name = config.name as string;
|
const name = config.name as string;
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
console.warn('Skipping server: no name specified');
|
logger.warn('Skipping server: no name specified');
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useSetupStore } from '@/store/setup-store';
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
import { StepIndicator } from './setup-view/components';
|
import { StepIndicator } from './setup-view/components';
|
||||||
import {
|
import {
|
||||||
@@ -10,6 +11,8 @@ import {
|
|||||||
} from './setup-view/steps';
|
} from './setup-view/steps';
|
||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
|
|
||||||
|
const logger = createLogger('SetupView');
|
||||||
|
|
||||||
// Main Setup View
|
// Main Setup View
|
||||||
export function SetupView() {
|
export function SetupView() {
|
||||||
const { currentStep, setCurrentStep, completeSetup, setSkipClaudeSetup } = useSetupStore();
|
const { currentStep, setCurrentStep, completeSetup, setSkipClaudeSetup } = useSetupStore();
|
||||||
@@ -28,33 +31,33 @@ export function SetupView() {
|
|||||||
const currentIndex = steps.indexOf(getStepName());
|
const currentIndex = steps.indexOf(getStepName());
|
||||||
|
|
||||||
const handleNext = (from: string) => {
|
const handleNext = (from: string) => {
|
||||||
console.log('[Setup Flow] handleNext called from:', from, 'currentStep:', currentStep);
|
logger.debug('[Setup Flow] handleNext called from:', from, 'currentStep:', currentStep);
|
||||||
switch (from) {
|
switch (from) {
|
||||||
case 'welcome':
|
case 'welcome':
|
||||||
console.log('[Setup Flow] Moving to theme step');
|
logger.debug('[Setup Flow] Moving to theme step');
|
||||||
setCurrentStep('theme');
|
setCurrentStep('theme');
|
||||||
break;
|
break;
|
||||||
case 'theme':
|
case 'theme':
|
||||||
console.log('[Setup Flow] Moving to claude_detect step');
|
logger.debug('[Setup Flow] Moving to claude_detect step');
|
||||||
setCurrentStep('claude_detect');
|
setCurrentStep('claude_detect');
|
||||||
break;
|
break;
|
||||||
case 'claude':
|
case 'claude':
|
||||||
console.log('[Setup Flow] Moving to cursor step');
|
logger.debug('[Setup Flow] Moving to cursor step');
|
||||||
setCurrentStep('cursor');
|
setCurrentStep('cursor');
|
||||||
break;
|
break;
|
||||||
case 'cursor':
|
case 'cursor':
|
||||||
console.log('[Setup Flow] Moving to github step');
|
logger.debug('[Setup Flow] Moving to github step');
|
||||||
setCurrentStep('github');
|
setCurrentStep('github');
|
||||||
break;
|
break;
|
||||||
case 'github':
|
case 'github':
|
||||||
console.log('[Setup Flow] Moving to complete step');
|
logger.debug('[Setup Flow] Moving to complete step');
|
||||||
setCurrentStep('complete');
|
setCurrentStep('complete');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBack = (from: string) => {
|
const handleBack = (from: string) => {
|
||||||
console.log('[Setup Flow] handleBack called from:', from);
|
logger.debug('[Setup Flow] handleBack called from:', from);
|
||||||
switch (from) {
|
switch (from) {
|
||||||
case 'theme':
|
case 'theme':
|
||||||
setCurrentStep('welcome');
|
setCurrentStep('welcome');
|
||||||
@@ -72,25 +75,25 @@ export function SetupView() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSkipClaude = () => {
|
const handleSkipClaude = () => {
|
||||||
console.log('[Setup Flow] Skipping Claude setup');
|
logger.debug('[Setup Flow] Skipping Claude setup');
|
||||||
setSkipClaudeSetup(true);
|
setSkipClaudeSetup(true);
|
||||||
setCurrentStep('cursor');
|
setCurrentStep('cursor');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSkipCursor = () => {
|
const handleSkipCursor = () => {
|
||||||
console.log('[Setup Flow] Skipping Cursor setup');
|
logger.debug('[Setup Flow] Skipping Cursor setup');
|
||||||
setCurrentStep('github');
|
setCurrentStep('github');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSkipGithub = () => {
|
const handleSkipGithub = () => {
|
||||||
console.log('[Setup Flow] Skipping GitHub setup');
|
logger.debug('[Setup Flow] Skipping GitHub setup');
|
||||||
setCurrentStep('complete');
|
setCurrentStep('complete');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFinish = () => {
|
const handleFinish = () => {
|
||||||
console.log('[Setup Flow] handleFinish called - completing setup');
|
logger.debug('[Setup Flow] handleFinish called - completing setup');
|
||||||
completeSetup();
|
completeSetup();
|
||||||
console.log('[Setup Flow] Setup completed, redirecting to welcome view');
|
logger.debug('[Setup Flow] Setup completed, redirecting to welcome view');
|
||||||
navigate({ to: '/' });
|
navigate({ to: '/' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('CliInstallation');
|
||||||
|
|
||||||
interface UseCliInstallationOptions {
|
interface UseCliInstallationOptions {
|
||||||
cliType: 'claude';
|
cliType: 'claude';
|
||||||
@@ -82,7 +85,7 @@ export function useCliInstallation({
|
|||||||
toast.error('Installation failed', { description: result.error });
|
toast.error('Installation failed', { description: result.error });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to install ${cliType}:`, error);
|
logger.error(`Failed to install ${cliType}:`, error);
|
||||||
toast.error('Installation failed');
|
toast.error('Installation failed');
|
||||||
} finally {
|
} finally {
|
||||||
setIsInstalling(false);
|
setIsInstalling(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
interface UseCliStatusOptions {
|
interface UseCliStatusOptions {
|
||||||
cliType: 'claude';
|
cliType: 'claude';
|
||||||
@@ -14,13 +15,14 @@ export function useCliStatus({
|
|||||||
setAuthStatus,
|
setAuthStatus,
|
||||||
}: UseCliStatusOptions) {
|
}: UseCliStatusOptions) {
|
||||||
const [isChecking, setIsChecking] = useState(false);
|
const [isChecking, setIsChecking] = useState(false);
|
||||||
|
const logger = createLogger('CliStatus');
|
||||||
|
|
||||||
const checkStatus = useCallback(async () => {
|
const checkStatus = useCallback(async () => {
|
||||||
console.log(`[${cliType} Setup] Starting status check...`);
|
logger.info(`Starting status check for ${cliType}...`);
|
||||||
setIsChecking(true);
|
setIsChecking(true);
|
||||||
try {
|
try {
|
||||||
const result = await statusApi();
|
const result = await statusApi();
|
||||||
console.log(`[${cliType} Setup] Raw status result:`, result);
|
logger.info(`Raw status result for ${cliType}:`, result);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const cliStatus = {
|
const cliStatus = {
|
||||||
@@ -29,7 +31,7 @@ export function useCliStatus({
|
|||||||
version: result.version || null,
|
version: result.version || null,
|
||||||
method: result.method || 'none',
|
method: result.method || 'none',
|
||||||
};
|
};
|
||||||
console.log(`[${cliType} Setup] CLI Status:`, cliStatus);
|
logger.info(`CLI Status for ${cliType}:`, cliStatus);
|
||||||
setCliStatus(cliStatus);
|
setCliStatus(cliStatus);
|
||||||
|
|
||||||
if (result.auth) {
|
if (result.auth) {
|
||||||
@@ -60,11 +62,11 @@ export function useCliStatus({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[${cliType} Setup] Failed to check status:`, error);
|
logger.error(`Failed to check status for ${cliType}:`, error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsChecking(false);
|
setIsChecking(false);
|
||||||
}
|
}
|
||||||
}, [cliType, statusApi, setCliStatus, setAuthStatus]);
|
}, [cliType, statusApi, setCliStatus, setAuthStatus, logger]);
|
||||||
|
|
||||||
return { isChecking, checkStatus };
|
return { isChecking, checkStatus };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('TokenSave');
|
||||||
|
|
||||||
interface UseTokenSaveOptions {
|
interface UseTokenSaveOptions {
|
||||||
provider: string; // e.g., "anthropic_oauth_token", "anthropic", "openai"
|
provider: string; // e.g., "anthropic_oauth_token", "anthropic", "openai"
|
||||||
@@ -24,7 +27,7 @@ export function useTokenSave({ provider, onSuccess }: UseTokenSaveOptions) {
|
|||||||
|
|
||||||
if (setupApi?.storeApiKey) {
|
if (setupApi?.storeApiKey) {
|
||||||
const result = await setupApi.storeApiKey(provider, tokenValue);
|
const result = await setupApi.storeApiKey(provider, tokenValue);
|
||||||
console.log(`[Token Save] Store result for ${provider}:`, result);
|
logger.info(`Store result for ${provider}:`, result);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const tokenType = provider.includes('oauth') ? 'subscription token' : 'API key';
|
const tokenType = provider.includes('oauth') ? 'subscription token' : 'API key';
|
||||||
@@ -42,7 +45,7 @@ export function useTokenSave({ provider, onSuccess }: UseTokenSaveOptions) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Token Save] Failed to save ${provider}:`, error);
|
logger.error(`Failed to save ${provider}:`, error);
|
||||||
toast.error('Failed to save token');
|
toast.error('Failed to save token');
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
@@ -19,6 +20,8 @@ import {
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { StatusBadge } from '../components';
|
import { StatusBadge } from '../components';
|
||||||
|
|
||||||
|
const logger = createLogger('CursorSetupStep');
|
||||||
|
|
||||||
interface CursorSetupStepProps {
|
interface CursorSetupStepProps {
|
||||||
onNext: () => void;
|
onNext: () => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
@@ -67,7 +70,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to check Cursor status:', error);
|
logger.error('Failed to check Cursor status:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsChecking(false);
|
setIsChecking(false);
|
||||||
}
|
}
|
||||||
@@ -140,7 +143,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
|
|||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Login failed:', error);
|
logger.error('Login failed:', error);
|
||||||
toast.error('Failed to start login process');
|
toast.error('Failed to start login process');
|
||||||
setIsLoggingIn(false);
|
setIsLoggingIn(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { useSetupStore } from '@/store/setup-store';
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
@@ -18,6 +19,8 @@ import {
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { StatusBadge } from '../components';
|
import { StatusBadge } from '../components';
|
||||||
|
|
||||||
|
const logger = createLogger('GitHubSetupStep');
|
||||||
|
|
||||||
interface GitHubSetupStepProps {
|
interface GitHubSetupStepProps {
|
||||||
onNext: () => void;
|
onNext: () => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
@@ -46,7 +49,7 @@ export function GitHubSetupStep({ onNext, onBack, onSkip }: GitHubSetupStepProps
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to check gh status:', error);
|
logger.error('Failed to check gh status:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsChecking(false);
|
setIsChecking(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useEffect, useState, useCallback, useRef } from 'react';
|
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('SpecGeneration');
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { CheckCircle2 } from 'lucide-react';
|
import { CheckCircle2 } from 'lucide-react';
|
||||||
@@ -79,7 +82,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const status = await api.specRegeneration.status();
|
const status = await api.specRegeneration.status();
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Status check on mount:',
|
'[useSpecGeneration] Status check on mount:',
|
||||||
status,
|
status,
|
||||||
'for project:',
|
'for project:',
|
||||||
@@ -87,7 +90,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (status.success && status.isRunning) {
|
if (status.success && status.isRunning) {
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Spec generation is running globally. Tentatively showing loader.'
|
'[useSpecGeneration] Spec generation is running globally. Tentatively showing loader.'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,7 +106,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
clearTimeout(pendingStatusTimeoutRef.current);
|
clearTimeout(pendingStatusTimeoutRef.current);
|
||||||
}
|
}
|
||||||
pendingStatusTimeoutRef.current = setTimeout(() => {
|
pendingStatusTimeoutRef.current = setTimeout(() => {
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] No events received for current project - clearing tentative state'
|
'[useSpecGeneration] No events received for current project - clearing tentative state'
|
||||||
);
|
);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
@@ -118,7 +121,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
stateRestoredRef.current = false;
|
stateRestoredRef.current = false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[useSpecGeneration] Failed to check status:', error);
|
logger.error('[useSpecGeneration] Failed to check status:', error);
|
||||||
} finally {
|
} finally {
|
||||||
statusCheckRef.current = false;
|
statusCheckRef.current = false;
|
||||||
}
|
}
|
||||||
@@ -141,10 +144,10 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
if (!api.specRegeneration) return;
|
if (!api.specRegeneration) return;
|
||||||
|
|
||||||
const status = await api.specRegeneration.status();
|
const status = await api.specRegeneration.status();
|
||||||
console.log('[useSpecGeneration] Visibility change - status check:', status);
|
logger.debug('[useSpecGeneration] Visibility change - status check:', status);
|
||||||
|
|
||||||
if (!status.isRunning) {
|
if (!status.isRunning) {
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Visibility change: Backend indicates generation complete - clearing state'
|
'[useSpecGeneration] Visibility change: Backend indicates generation complete - clearing state'
|
||||||
);
|
);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
@@ -157,7 +160,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
setCurrentPhase(status.currentPhase);
|
setCurrentPhase(status.currentPhase);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[useSpecGeneration] Failed to check status on visibility change:', error);
|
logger.error('[useSpecGeneration] Failed to check status on visibility change:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -180,7 +183,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
const status = await api.specRegeneration.status();
|
const status = await api.specRegeneration.status();
|
||||||
|
|
||||||
if (!status.isRunning) {
|
if (!status.isRunning) {
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Periodic check: Backend indicates generation complete - clearing state'
|
'[useSpecGeneration] Periodic check: Backend indicates generation complete - clearing state'
|
||||||
);
|
);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
@@ -190,14 +193,14 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
stateRestoredRef.current = false;
|
stateRestoredRef.current = false;
|
||||||
loadSpec();
|
loadSpec();
|
||||||
} else if (status.currentPhase && status.currentPhase !== currentPhase) {
|
} else if (status.currentPhase && status.currentPhase !== currentPhase) {
|
||||||
console.log('[useSpecGeneration] Periodic check: Phase updated from backend', {
|
logger.debug('[useSpecGeneration] Periodic check: Phase updated from backend', {
|
||||||
old: currentPhase,
|
old: currentPhase,
|
||||||
new: status.currentPhase,
|
new: status.currentPhase,
|
||||||
});
|
});
|
||||||
setCurrentPhase(status.currentPhase);
|
setCurrentPhase(status.currentPhase);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[useSpecGeneration] Periodic status check error:', error);
|
logger.error('[useSpecGeneration] Periodic status check error:', error);
|
||||||
}
|
}
|
||||||
}, STATUS_CHECK_INTERVAL_MS);
|
}, STATUS_CHECK_INTERVAL_MS);
|
||||||
|
|
||||||
@@ -214,7 +217,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
if (!api.specRegeneration) return;
|
if (!api.specRegeneration) return;
|
||||||
|
|
||||||
const unsubscribe = api.specRegeneration.onEvent((event: SpecRegenerationEvent) => {
|
const unsubscribe = api.specRegeneration.onEvent((event: SpecRegenerationEvent) => {
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Regeneration event:',
|
'[useSpecGeneration] Regeneration event:',
|
||||||
event.type,
|
event.type,
|
||||||
'for project:',
|
'for project:',
|
||||||
@@ -224,14 +227,14 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (event.projectPath !== currentProject?.path) {
|
if (event.projectPath !== currentProject?.path) {
|
||||||
console.log('[useSpecGeneration] Ignoring event - not for current project');
|
logger.debug('[useSpecGeneration] Ignoring event - not for current project');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pendingStatusTimeoutRef.current) {
|
if (pendingStatusTimeoutRef.current) {
|
||||||
clearTimeout(pendingStatusTimeoutRef.current);
|
clearTimeout(pendingStatusTimeoutRef.current);
|
||||||
pendingStatusTimeoutRef.current = null;
|
pendingStatusTimeoutRef.current = null;
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Event confirmed this is for current project - clearing timeout'
|
'[useSpecGeneration] Event confirmed this is for current project - clearing timeout'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -244,10 +247,10 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
if (phaseMatch) {
|
if (phaseMatch) {
|
||||||
const phase = phaseMatch[1];
|
const phase = phaseMatch[1];
|
||||||
setCurrentPhase(phase);
|
setCurrentPhase(phase);
|
||||||
console.log(`[useSpecGeneration] Phase updated: ${phase}`);
|
logger.debug(`[useSpecGeneration] Phase updated: ${phase}`);
|
||||||
|
|
||||||
if (phase === 'complete') {
|
if (phase === 'complete') {
|
||||||
console.log('[useSpecGeneration] Phase is complete - clearing state');
|
logger.debug('[useSpecGeneration] Phase is complete - clearing state');
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
setIsRegenerating(false);
|
setIsRegenerating(false);
|
||||||
stateRestoredRef.current = false;
|
stateRestoredRef.current = false;
|
||||||
@@ -261,7 +264,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
event.content.includes('All tasks completed') ||
|
event.content.includes('All tasks completed') ||
|
||||||
event.content.includes('✓ All tasks completed')
|
event.content.includes('✓ All tasks completed')
|
||||||
) {
|
) {
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Detected completion in progress message - clearing state'
|
'[useSpecGeneration] Detected completion in progress message - clearing state'
|
||||||
);
|
);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
@@ -276,7 +279,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
const newLog = logsRef.current + event.content;
|
const newLog = logsRef.current + event.content;
|
||||||
logsRef.current = newLog;
|
logsRef.current = newLog;
|
||||||
setLogs(newLog);
|
setLogs(newLog);
|
||||||
console.log('[useSpecGeneration] Progress:', event.content.substring(0, 100));
|
logger.debug('[useSpecGeneration] Progress:', event.content.substring(0, 100));
|
||||||
|
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
@@ -292,7 +295,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
setCurrentPhase('feature_generation');
|
setCurrentPhase('feature_generation');
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
setIsRegenerating(true);
|
setIsRegenerating(true);
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Detected feature creation tool - setting phase to feature_generation'
|
'[useSpecGeneration] Detected feature creation tool - setting phase to feature_generation'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -305,7 +308,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
const newLog = logsRef.current + toolLog;
|
const newLog = logsRef.current + toolLog;
|
||||||
logsRef.current = newLog;
|
logsRef.current = newLog;
|
||||||
setLogs(newLog);
|
setLogs(newLog);
|
||||||
console.log('[useSpecGeneration] Tool:', event.tool, event.input);
|
logger.debug('[useSpecGeneration] Tool:', event.tool, event.input);
|
||||||
} else if (event.type === 'spec_regeneration_complete') {
|
} else if (event.type === 'spec_regeneration_complete') {
|
||||||
const completionLog = logsRef.current + `\n[Complete] ${event.message}\n`;
|
const completionLog = logsRef.current + `\n[Complete] ${event.message}\n`;
|
||||||
logsRef.current = completionLog;
|
logsRef.current = completionLog;
|
||||||
@@ -328,7 +331,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
(isFinalCompletionMessage || hasCompletePhase) && !isIntermediateCompletion;
|
(isFinalCompletionMessage || hasCompletePhase) && !isIntermediateCompletion;
|
||||||
|
|
||||||
if (shouldComplete) {
|
if (shouldComplete) {
|
||||||
console.log('[useSpecGeneration] Final completion detected - clearing state', {
|
logger.debug('[useSpecGeneration] Final completion detected - clearing state', {
|
||||||
isFinalCompletionMessage,
|
isFinalCompletionMessage,
|
||||||
hasCompletePhase,
|
hasCompletePhase,
|
||||||
message: event.message,
|
message: event.message,
|
||||||
@@ -367,12 +370,12 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
setIsRegenerating(true);
|
setIsRegenerating(true);
|
||||||
setCurrentPhase('feature_generation');
|
setCurrentPhase('feature_generation');
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Intermediate completion, continuing with feature generation'
|
'[useSpecGeneration] Intermediate completion, continuing with feature generation'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[useSpecGeneration] Spec generation event:', event.message);
|
logger.debug('[useSpecGeneration] Spec generation event:', event.message);
|
||||||
} else if (event.type === 'spec_regeneration_error') {
|
} else if (event.type === 'spec_regeneration_error') {
|
||||||
setIsRegenerating(false);
|
setIsRegenerating(false);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
@@ -383,7 +386,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
const errorLog = logsRef.current + `\n\n[ERROR] ${event.error}\n`;
|
const errorLog = logsRef.current + `\n\n[ERROR] ${event.error}\n`;
|
||||||
logsRef.current = errorLog;
|
logsRef.current = errorLog;
|
||||||
setLogs(errorLog);
|
setLogs(errorLog);
|
||||||
console.error('[useSpecGeneration] Regeneration error:', event.error);
|
logger.error('[useSpecGeneration] Regeneration error:', event.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -402,11 +405,11 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
logsRef.current = '';
|
logsRef.current = '';
|
||||||
setLogs('');
|
setLogs('');
|
||||||
console.log('[useSpecGeneration] Starting spec creation, generateFeatures:', generateFeatures);
|
logger.debug('[useSpecGeneration] Starting spec creation, generateFeatures:', generateFeatures);
|
||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api.specRegeneration) {
|
if (!api.specRegeneration) {
|
||||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
logger.error('[useSpecGeneration] Spec regeneration not available');
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -420,7 +423,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
const errorMsg = result.error || 'Unknown error';
|
const errorMsg = result.error || 'Unknown error';
|
||||||
console.error('[useSpecGeneration] Failed to start spec creation:', errorMsg);
|
logger.error('[useSpecGeneration] Failed to start spec creation:', errorMsg);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
setCurrentPhase('error');
|
setCurrentPhase('error');
|
||||||
setErrorMessage(errorMsg);
|
setErrorMessage(errorMsg);
|
||||||
@@ -430,7 +433,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
console.error('[useSpecGeneration] Failed to create spec:', errorMsg);
|
logger.error('[useSpecGeneration] Failed to create spec:', errorMsg);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
setCurrentPhase('error');
|
setCurrentPhase('error');
|
||||||
setErrorMessage(errorMsg);
|
setErrorMessage(errorMsg);
|
||||||
@@ -455,14 +458,14 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
logsRef.current = '';
|
logsRef.current = '';
|
||||||
setLogs('');
|
setLogs('');
|
||||||
console.log(
|
logger.debug(
|
||||||
'[useSpecGeneration] Starting spec regeneration, generateFeatures:',
|
'[useSpecGeneration] Starting spec regeneration, generateFeatures:',
|
||||||
generateFeaturesOnRegenerate
|
generateFeaturesOnRegenerate
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api.specRegeneration) {
|
if (!api.specRegeneration) {
|
||||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
logger.error('[useSpecGeneration] Spec regeneration not available');
|
||||||
setIsRegenerating(false);
|
setIsRegenerating(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -476,7 +479,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
const errorMsg = result.error || 'Unknown error';
|
const errorMsg = result.error || 'Unknown error';
|
||||||
console.error('[useSpecGeneration] Failed to start regeneration:', errorMsg);
|
logger.error('[useSpecGeneration] Failed to start regeneration:', errorMsg);
|
||||||
setIsRegenerating(false);
|
setIsRegenerating(false);
|
||||||
setCurrentPhase('error');
|
setCurrentPhase('error');
|
||||||
setErrorMessage(errorMsg);
|
setErrorMessage(errorMsg);
|
||||||
@@ -486,7 +489,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
console.error('[useSpecGeneration] Failed to regenerate spec:', errorMsg);
|
logger.error('[useSpecGeneration] Failed to regenerate spec:', errorMsg);
|
||||||
setIsRegenerating(false);
|
setIsRegenerating(false);
|
||||||
setCurrentPhase('error');
|
setCurrentPhase('error');
|
||||||
setErrorMessage(errorMsg);
|
setErrorMessage(errorMsg);
|
||||||
@@ -511,11 +514,11 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
logsRef.current = '';
|
logsRef.current = '';
|
||||||
setLogs('');
|
setLogs('');
|
||||||
console.log('[useSpecGeneration] Starting feature generation from existing spec');
|
logger.debug('[useSpecGeneration] Starting feature generation from existing spec');
|
||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api.specRegeneration) {
|
if (!api.specRegeneration) {
|
||||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
logger.error('[useSpecGeneration] Spec regeneration not available');
|
||||||
setIsGeneratingFeatures(false);
|
setIsGeneratingFeatures(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -523,7 +526,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
const errorMsg = result.error || 'Unknown error';
|
const errorMsg = result.error || 'Unknown error';
|
||||||
console.error('[useSpecGeneration] Failed to start feature generation:', errorMsg);
|
logger.error('[useSpecGeneration] Failed to start feature generation:', errorMsg);
|
||||||
setIsGeneratingFeatures(false);
|
setIsGeneratingFeatures(false);
|
||||||
setCurrentPhase('error');
|
setCurrentPhase('error');
|
||||||
setErrorMessage(errorMsg);
|
setErrorMessage(errorMsg);
|
||||||
@@ -533,7 +536,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
console.error('[useSpecGeneration] Failed to generate features:', errorMsg);
|
logger.error('[useSpecGeneration] Failed to generate features:', errorMsg);
|
||||||
setIsGeneratingFeatures(false);
|
setIsGeneratingFeatures(false);
|
||||||
setCurrentPhase('error');
|
setCurrentPhase('error');
|
||||||
setErrorMessage(errorMsg);
|
setErrorMessage(errorMsg);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useEffect, useState, useCallback } from 'react';
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('SpecLoading');
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
export function useSpecLoading() {
|
export function useSpecLoading() {
|
||||||
@@ -24,7 +27,7 @@ export function useSpecLoading() {
|
|||||||
setSpecExists(false);
|
setSpecExists(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load spec:', error);
|
logger.error('Failed to load spec:', error);
|
||||||
setSpecExists(false);
|
setSpecExists(false);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('SpecSave');
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
export function useSpecSave() {
|
export function useSpecSave() {
|
||||||
@@ -16,7 +19,7 @@ export function useSpecSave() {
|
|||||||
await api.writeFile(`${currentProject.path}/.automaker/app_spec.txt`, appSpec);
|
await api.writeFile(`${currentProject.path}/.automaker/app_spec.txt`, appSpec);
|
||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save spec:', error);
|
logger.error('Failed to save spec:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
Terminal as TerminalIcon,
|
Terminal as TerminalIcon,
|
||||||
Plus,
|
Plus,
|
||||||
@@ -50,6 +51,8 @@ import { cn } from '@/lib/utils';
|
|||||||
import { apiFetch, apiGet, apiPost, apiDeleteRaw, getAuthHeaders } from '@/lib/api-fetch';
|
import { apiFetch, apiGet, apiPost, apiDeleteRaw, getAuthHeaders } from '@/lib/api-fetch';
|
||||||
import { getApiKey } from '@/lib/http-api-client';
|
import { getApiKey } from '@/lib/http-api-client';
|
||||||
|
|
||||||
|
const logger = createLogger('Terminal');
|
||||||
|
|
||||||
interface TerminalStatus {
|
interface TerminalStatus {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
passwordRequired: boolean;
|
passwordRequired: boolean;
|
||||||
@@ -301,7 +304,7 @@ export function TerminalView() {
|
|||||||
headers['X-Terminal-Token'] = terminalState.authToken;
|
headers['X-Terminal-Token'] = terminalState.authToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[Terminal] Killing ${sessionIds.length} sessions on server`);
|
logger.info(`Killing ${sessionIds.length} sessions on server`);
|
||||||
|
|
||||||
// Kill all sessions in parallel
|
// Kill all sessions in parallel
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
@@ -309,7 +312,7 @@ export function TerminalView() {
|
|||||||
try {
|
try {
|
||||||
await apiDeleteRaw(`/api/terminal/sessions/${sessionId}`, { headers });
|
await apiDeleteRaw(`/api/terminal/sessions/${sessionId}`, { headers });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[Terminal] Failed to kill session ${sessionId}:`, err);
|
logger.error(`Failed to kill session ${sessionId}:`, err);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -320,7 +323,7 @@ export function TerminalView() {
|
|||||||
const canCreateTerminal = (debounceMessage: string): boolean => {
|
const canCreateTerminal = (debounceMessage: string): boolean => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - lastCreateTimeRef.current < CREATE_COOLDOWN_MS || isCreatingRef.current) {
|
if (now - lastCreateTimeRef.current < CREATE_COOLDOWN_MS || isCreatingRef.current) {
|
||||||
console.log(debounceMessage);
|
logger.debug(debounceMessage);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
lastCreateTimeRef.current = now;
|
lastCreateTimeRef.current = now;
|
||||||
@@ -447,7 +450,7 @@ export function TerminalView() {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError('Failed to connect to server');
|
setError('Failed to connect to server');
|
||||||
console.error('[Terminal] Status fetch error:', err);
|
logger.error('Status fetch error:', err);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -469,7 +472,7 @@ export function TerminalView() {
|
|||||||
setServerSessionInfo({ current: data.data.currentSessions, max: data.data.maxSessions });
|
setServerSessionInfo({ current: data.data.currentSessions, max: data.data.maxSessions });
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Failed to fetch server settings:', err);
|
logger.error('Failed to fetch server settings:', err);
|
||||||
}
|
}
|
||||||
}, [terminalState.isUnlocked, terminalState.authToken]);
|
}, [terminalState.isUnlocked, terminalState.authToken]);
|
||||||
|
|
||||||
@@ -573,7 +576,7 @@ export function TerminalView() {
|
|||||||
|
|
||||||
// If no saved layout or no tabs, we're done - terminal starts fresh for this project
|
// If no saved layout or no tabs, we're done - terminal starts fresh for this project
|
||||||
if (!savedLayout || savedLayout.tabs.length === 0) {
|
if (!savedLayout || savedLayout.tabs.length === 0) {
|
||||||
console.log('[Terminal] No saved layout for project, starting fresh');
|
logger.info('No saved layout for project, starting fresh');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +588,7 @@ export function TerminalView() {
|
|||||||
const restoreLayout = async () => {
|
const restoreLayout = async () => {
|
||||||
// Check if we're still restoring the same project (user may have switched)
|
// Check if we're still restoring the same project (user may have switched)
|
||||||
if (restoringProjectPathRef.current !== currentPath) {
|
if (restoringProjectPathRef.current !== currentPath) {
|
||||||
console.log('[Terminal] Restore cancelled - project changed');
|
logger.info('Restore cancelled - project changed');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,7 +626,7 @@ export function TerminalView() {
|
|||||||
);
|
);
|
||||||
return data.success && data.data ? data.data.id : null;
|
return data.success && data.data ? data.data.id : null;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Failed to create terminal session:', err);
|
logger.error('Failed to create terminal session:', err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -691,7 +694,7 @@ export function TerminalView() {
|
|||||||
for (let tabIndex = 0; tabIndex < savedLayout.tabs.length; tabIndex++) {
|
for (let tabIndex = 0; tabIndex < savedLayout.tabs.length; tabIndex++) {
|
||||||
// Check if project changed during restore - bail out early
|
// Check if project changed during restore - bail out early
|
||||||
if (restoringProjectPathRef.current !== currentPath) {
|
if (restoringProjectPathRef.current !== currentPath) {
|
||||||
console.log('[Terminal] Restore cancelled mid-loop - project changed');
|
logger.info('Restore cancelled mid-loop - project changed');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -730,7 +733,7 @@ export function TerminalView() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Failed to restore terminal layout:', err);
|
logger.error('Failed to restore terminal layout:', err);
|
||||||
toast.error('Failed to restore terminals', {
|
toast.error('Failed to restore terminals', {
|
||||||
description: 'Could not restore terminal layout. Please try creating new terminals.',
|
description: 'Could not restore terminal layout. Please try creating new terminals.',
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
@@ -806,7 +809,7 @@ export function TerminalView() {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setAuthError('Failed to authenticate');
|
setAuthError('Failed to authenticate');
|
||||||
console.error('[Terminal] Auth error:', err);
|
logger.error('Auth error:', err);
|
||||||
} finally {
|
} finally {
|
||||||
setAuthLoading(false);
|
setAuthLoading(false);
|
||||||
}
|
}
|
||||||
@@ -851,14 +854,14 @@ export function TerminalView() {
|
|||||||
`Please close unused terminals. Limit: ${data.maxSessions || 'unknown'}`,
|
`Please close unused terminals. Limit: ${data.maxSessions || 'unknown'}`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error('[Terminal] Failed to create session:', data.error);
|
logger.error('Failed to create session:', data.error);
|
||||||
toast.error('Failed to create terminal', {
|
toast.error('Failed to create terminal', {
|
||||||
description: data.error || 'Unknown error',
|
description: data.error || 'Unknown error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Create session error:', err);
|
logger.error('Create session error:', err);
|
||||||
toast.error('Failed to create terminal', {
|
toast.error('Failed to create terminal', {
|
||||||
description: 'Could not connect to server',
|
description: 'Could not connect to server',
|
||||||
});
|
});
|
||||||
@@ -915,7 +918,7 @@ export function TerminalView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Create session error:', err);
|
logger.error('Create session error:', err);
|
||||||
// Remove the empty tab on error
|
// Remove the empty tab on error
|
||||||
const { removeTerminalTab } = useAppStore.getState();
|
const { removeTerminalTab } = useAppStore.getState();
|
||||||
removeTerminalTab(tabId);
|
removeTerminalTab(tabId);
|
||||||
@@ -943,16 +946,13 @@ export function TerminalView() {
|
|||||||
if (!response.ok && response.status !== 404) {
|
if (!response.ok && response.status !== 404) {
|
||||||
// Log non-404 errors but still proceed with UI cleanup
|
// Log non-404 errors but still proceed with UI cleanup
|
||||||
const data = await response.json().catch(() => ({}));
|
const data = await response.json().catch(() => ({}));
|
||||||
console.error(
|
logger.error('Server failed to kill session:', data.error || response.statusText);
|
||||||
'[Terminal] Server failed to kill session:',
|
|
||||||
data.error || response.statusText
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh session count
|
// Refresh session count
|
||||||
fetchServerSettings();
|
fetchServerSettings();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Kill session error:', err);
|
logger.error('Kill session error:', err);
|
||||||
// Still remove from UI on network error - better UX than leaving broken terminal
|
// Still remove from UI on network error - better UX than leaving broken terminal
|
||||||
removeTerminalFromLayout(sessionId);
|
removeTerminalFromLayout(sessionId);
|
||||||
}
|
}
|
||||||
@@ -983,7 +983,7 @@ export function TerminalView() {
|
|||||||
try {
|
try {
|
||||||
await apiDeleteRaw(`/api/terminal/sessions/${sessionId}`, { headers });
|
await apiDeleteRaw(`/api/terminal/sessions/${sessionId}`, { headers });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[Terminal] Failed to kill session ${sessionId}:`, err);
|
logger.error(`Failed to kill session ${sessionId}:`, err);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -1210,9 +1210,7 @@ export function TerminalView() {
|
|||||||
onSessionInvalid={() => {
|
onSessionInvalid={() => {
|
||||||
// Auto-remove stale session when server says it doesn't exist
|
// Auto-remove stale session when server says it doesn't exist
|
||||||
// This handles cases like server restart where sessions are lost
|
// This handles cases like server restart where sessions are lost
|
||||||
console.log(
|
logger.info(`Session ${content.sessionId} is invalid, removing from layout`);
|
||||||
`[Terminal] Session ${content.sessionId} is invalid, removing from layout`
|
|
||||||
);
|
|
||||||
killTerminal(content.sessionId);
|
killTerminal(content.sessionId);
|
||||||
}}
|
}}
|
||||||
isDragging={activeDragId === content.sessionId}
|
isDragging={activeDragId === content.sessionId}
|
||||||
@@ -1587,9 +1585,7 @@ export function TerminalView() {
|
|||||||
onNewTab={createTerminalInNewTab}
|
onNewTab={createTerminalInNewTab}
|
||||||
onSessionInvalid={() => {
|
onSessionInvalid={() => {
|
||||||
const sessionId = terminalState.maximizedSessionId!;
|
const sessionId = terminalState.maximizedSessionId!;
|
||||||
console.log(
|
logger.info(`Maximized session ${sessionId} is invalid, removing from layout`);
|
||||||
`[Terminal] Maximized session ${sessionId} is invalid, removing from layout`
|
|
||||||
);
|
|
||||||
killTerminal(sessionId);
|
killTerminal(sessionId);
|
||||||
}}
|
}}
|
||||||
isDragging={false}
|
isDragging={false}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import React, { Component, ErrorInfo } from 'react';
|
import React, { Component, ErrorInfo } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { AlertCircle, RefreshCw } from 'lucide-react';
|
import { AlertCircle, RefreshCw } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const logger = createLogger('TerminalErrorBoundary');
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
@@ -30,7 +33,7 @@ export class TerminalErrorBoundary extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||||
console.error('[TerminalErrorBoundary] Terminal crashed:', {
|
logger.error('Terminal crashed:', {
|
||||||
sessionId: this.props.sessionId,
|
sessionId: this.props.sessionId,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useRef, useCallback, useState } from 'react';
|
import { useEffect, useRef, useCallback, useState } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
X,
|
X,
|
||||||
SplitSquareHorizontal,
|
SplitSquareHorizontal,
|
||||||
@@ -42,6 +43,8 @@ import { toast } from 'sonner';
|
|||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { getApiKey, getSessionToken, getServerUrlSync } from '@/lib/http-api-client';
|
import { getApiKey, getSessionToken, getServerUrlSync } from '@/lib/http-api-client';
|
||||||
|
|
||||||
|
const logger = createLogger('Terminal');
|
||||||
|
|
||||||
// Font size constraints
|
// Font size constraints
|
||||||
const MIN_FONT_SIZE = 8;
|
const MIN_FONT_SIZE = 8;
|
||||||
const MAX_FONT_SIZE = 32;
|
const MAX_FONT_SIZE = 32;
|
||||||
@@ -296,7 +299,7 @@ export function TerminalPanel({
|
|||||||
toast.success('Copied to clipboard');
|
toast.success('Copied to clipboard');
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Copy failed:', err);
|
logger.error('Copy failed:', err);
|
||||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||||
toast.error('Copy failed', {
|
toast.error('Copy failed', {
|
||||||
description: errorMessage.includes('permission')
|
description: errorMessage.includes('permission')
|
||||||
@@ -361,7 +364,7 @@ export function TerminalPanel({
|
|||||||
|
|
||||||
await sendTextInChunks(text);
|
await sendTextInChunks(text);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Paste failed:', err);
|
logger.error('Paste failed:', err);
|
||||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||||
toast.error('Paste failed', {
|
toast.error('Paste failed', {
|
||||||
description: errorMessage.includes('permission')
|
description: errorMessage.includes('permission')
|
||||||
@@ -504,7 +507,7 @@ export function TerminalPanel({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.warn('[Terminal] Failed to fetch wsToken:', response.status);
|
logger.warn('Failed to fetch wsToken:', response.status);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,7 +518,7 @@ export function TerminalPanel({
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Terminal] Error fetching wsToken:', error);
|
logger.error('Error fetching wsToken:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [serverUrl]);
|
}, [serverUrl]);
|
||||||
@@ -595,7 +598,7 @@ export function TerminalPanel({
|
|||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (api?.openExternalLink) {
|
if (api?.openExternalLink) {
|
||||||
api.openExternalLink(uri).catch((error) => {
|
api.openExternalLink(uri).catch((error) => {
|
||||||
console.error('[Terminal] Failed to open URL:', error);
|
logger.error('Failed to open URL:', error);
|
||||||
// Fallback to window.open if Electron API fails
|
// Fallback to window.open if Electron API fails
|
||||||
window.open(uri, '_blank', 'noopener,noreferrer');
|
window.open(uri, '_blank', 'noopener,noreferrer');
|
||||||
});
|
});
|
||||||
@@ -697,7 +700,7 @@ export function TerminalPanel({
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// If we can't get home path, just use the path as-is
|
// If we can't get home path, just use the path as-is
|
||||||
console.warn('[Terminal] Could not resolve home directory path');
|
logger.warn('Could not resolve home directory path');
|
||||||
}
|
}
|
||||||
} else if (!clickedPath.startsWith('/') && !clickedPath.match(/^[a-zA-Z]:\\/)) {
|
} else if (!clickedPath.startsWith('/') && !clickedPath.match(/^[a-zA-Z]:\\/)) {
|
||||||
// Relative path - resolve against project path
|
// Relative path - resolve against project path
|
||||||
@@ -721,7 +724,7 @@ export function TerminalPanel({
|
|||||||
toast.error('Failed to open in editor', { description: result.error });
|
toast.error('Failed to open in editor', { description: result.error });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Terminal] Failed to open file:', error);
|
logger.error('Failed to open file:', error);
|
||||||
toast.error('Failed to open file', {
|
toast.error('Failed to open file', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -744,7 +747,7 @@ export function TerminalPanel({
|
|||||||
});
|
});
|
||||||
terminal.loadAddon(webglAddon);
|
terminal.loadAddon(webglAddon);
|
||||||
} catch {
|
} catch {
|
||||||
console.warn('[Terminal] WebGL addon not available, falling back to canvas');
|
logger.warn('WebGL addon not available, falling back to canvas');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fit terminal to container - wait for stable dimensions
|
// Fit terminal to container - wait for stable dimensions
|
||||||
@@ -770,7 +773,7 @@ export function TerminalPanel({
|
|||||||
try {
|
try {
|
||||||
fitAddon.fit();
|
fitAddon.fit();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Initial fit error:', err);
|
logger.error('Initial fit error:', err);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1000,7 +1003,7 @@ export function TerminalPanel({
|
|||||||
wsRef.current = ws;
|
wsRef.current = ws;
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
console.log(`[Terminal] WebSocket connected for session ${sessionId}`);
|
logger.info(`WebSocket connected for session ${sessionId}`);
|
||||||
|
|
||||||
setConnectionStatus('connected');
|
setConnectionStatus('connected');
|
||||||
reconnectAttemptsRef.current = 0;
|
reconnectAttemptsRef.current = 0;
|
||||||
@@ -1037,7 +1040,7 @@ export function TerminalPanel({
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'connected': {
|
case 'connected': {
|
||||||
console.log(`[Terminal] Session connected: ${msg.shell} in ${msg.cwd}`);
|
logger.info(`Session connected: ${msg.shell} in ${msg.cwd}`);
|
||||||
// Detect shell type from path
|
// Detect shell type from path
|
||||||
const shellPath = (msg.shell || '').toLowerCase();
|
const shellPath = (msg.shell || '').toLowerCase();
|
||||||
// Windows shells use backslash paths and include powershell/pwsh/cmd
|
// Windows shells use backslash paths and include powershell/pwsh/cmd
|
||||||
@@ -1088,16 +1091,12 @@ export function TerminalPanel({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Message parse error:', err);
|
logger.error('Message parse error:', err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onclose = (event) => {
|
ws.onclose = (event) => {
|
||||||
console.log(
|
logger.info(`WebSocket closed for session ${sessionId}: ${event.code} ${event.reason}`);
|
||||||
`[Terminal] WebSocket closed for session ${sessionId}:`,
|
|
||||||
event.code,
|
|
||||||
event.reason
|
|
||||||
);
|
|
||||||
wsRef.current = null;
|
wsRef.current = null;
|
||||||
|
|
||||||
// Clear heartbeat interval
|
// Clear heartbeat interval
|
||||||
@@ -1167,8 +1166,8 @@ export function TerminalPanel({
|
|||||||
// Attempt reconnect after exponential delay
|
// Attempt reconnect after exponential delay
|
||||||
reconnectTimeoutRef.current = setTimeout(() => {
|
reconnectTimeoutRef.current = setTimeout(() => {
|
||||||
if (xtermRef.current) {
|
if (xtermRef.current) {
|
||||||
console.log(
|
logger.info(
|
||||||
`[Terminal] Attempting reconnect for session ${sessionId} (attempt ${reconnectAttemptsRef.current}/${MAX_RECONNECT_ATTEMPTS})`
|
`Attempting reconnect for session ${sessionId} (attempt ${reconnectAttemptsRef.current}/${MAX_RECONNECT_ATTEMPTS})`
|
||||||
);
|
);
|
||||||
connect();
|
connect();
|
||||||
}
|
}
|
||||||
@@ -1176,7 +1175,7 @@ export function TerminalPanel({
|
|||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = (error) => {
|
ws.onerror = (error) => {
|
||||||
console.error(`[Terminal] WebSocket error for session ${sessionId}:`, error);
|
logger.error(`WebSocket error for session ${sessionId}:`, error);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1234,7 +1233,7 @@ export function TerminalPanel({
|
|||||||
wsRef.current.send(JSON.stringify({ type: 'resize', cols, rows }));
|
wsRef.current.send(JSON.stringify({ type: 'resize', cols, rows }));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Terminal] Resize error:', err);
|
logger.error('Resize error:', err);
|
||||||
}
|
}
|
||||||
}, RESIZE_DEBOUNCE_MS);
|
}, RESIZE_DEBOUNCE_MS);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -1551,7 +1550,7 @@ export function TerminalPanel({
|
|||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api.saveImageToTemp) {
|
if (!api.saveImageToTemp) {
|
||||||
// Fallback path when Electron API is not available (browser mode)
|
// Fallback path when Electron API is not available (browser mode)
|
||||||
console.warn('[Terminal] saveImageToTemp not available, returning fallback path');
|
logger.warn('saveImageToTemp not available, returning fallback path');
|
||||||
return `.automaker/images/${Date.now()}_${filename}`;
|
return `.automaker/images/${Date.now()}_${filename}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1560,10 +1559,10 @@ export function TerminalPanel({
|
|||||||
if (result.success && result.path) {
|
if (result.success && result.path) {
|
||||||
return result.path;
|
return result.path;
|
||||||
}
|
}
|
||||||
console.error('[Terminal] Failed to save image:', result.error);
|
logger.error('Failed to save image:', result.error);
|
||||||
return null;
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Terminal] Error saving image:', error);
|
logger.error('Error saving image:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1662,7 +1661,7 @@ export function TerminalPanel({
|
|||||||
toast.error(`Failed to save: ${file.name}`);
|
toast.error(`Failed to save: ${file.name}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Terminal] Error processing image:', error);
|
logger.error('Error processing image:', error);
|
||||||
toast.error(`Error processing: ${file.name}`);
|
toast.error(`Error processing: ${file.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -34,6 +35,8 @@ import { getHttpApiClient } from '@/lib/http-api-client';
|
|||||||
import type { StarterTemplate } from '@/lib/templates';
|
import type { StarterTemplate } from '@/lib/templates';
|
||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
|
|
||||||
|
const logger = createLogger('WelcomeView');
|
||||||
|
|
||||||
export function WelcomeView() {
|
export function WelcomeView() {
|
||||||
const {
|
const {
|
||||||
projects,
|
projects,
|
||||||
@@ -65,13 +68,13 @@ export function WelcomeView() {
|
|||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
|
|
||||||
if (!api.autoMode?.analyzeProject) {
|
if (!api.autoMode?.analyzeProject) {
|
||||||
console.log('[Welcome] Auto mode API not available, skipping analysis');
|
logger.info('[Welcome] Auto mode API not available, skipping analysis');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsAnalyzing(true);
|
setIsAnalyzing(true);
|
||||||
try {
|
try {
|
||||||
console.log('[Welcome] Starting project analysis for:', projectPath);
|
logger.info('[Welcome] Starting project analysis for:', projectPath);
|
||||||
const result = await api.autoMode.analyzeProject(projectPath);
|
const result = await api.autoMode.analyzeProject(projectPath);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -79,10 +82,10 @@ export function WelcomeView() {
|
|||||||
description: 'AI agent has analyzed your project structure',
|
description: 'AI agent has analyzed your project structure',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error('[Welcome] Project analysis failed:', result.error);
|
logger.error('[Welcome] Project analysis failed:', result.error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Welcome] Failed to analyze project:', error);
|
logger.error('[Welcome] Failed to analyze project:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsAnalyzing(false);
|
setIsAnalyzing(false);
|
||||||
}
|
}
|
||||||
@@ -125,8 +128,8 @@ export function WelcomeView() {
|
|||||||
setShowInitDialog(true);
|
setShowInitDialog(true);
|
||||||
|
|
||||||
// Kick off agent to analyze the project and update app_spec.txt
|
// Kick off agent to analyze the project and update app_spec.txt
|
||||||
console.log('[Welcome] Project initialized, created files:', initResult.createdFiles);
|
logger.info('[Welcome] Project initialized, created files:', initResult.createdFiles);
|
||||||
console.log('[Welcome] Kicking off project analysis agent...');
|
logger.info('[Welcome] Kicking off project analysis agent...');
|
||||||
|
|
||||||
// Start analysis in background (don't await, let it run async)
|
// Start analysis in background (don't await, let it run async)
|
||||||
analyzeProject(path);
|
analyzeProject(path);
|
||||||
@@ -139,7 +142,7 @@ export function WelcomeView() {
|
|||||||
// Navigate to the board view
|
// Navigate to the board view
|
||||||
navigate({ to: '/board' });
|
navigate({ to: '/board' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Welcome] Failed to open project:', error);
|
logger.error('[Welcome] Failed to open project:', error);
|
||||||
toast.error('Failed to open project', {
|
toast.error('Failed to open project', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -179,7 +182,7 @@ export function WelcomeView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Welcome] Failed to check workspace config:', error);
|
logger.error('[Welcome] Failed to check workspace config:', error);
|
||||||
// Fall back to current behavior on error
|
// Fall back to current behavior on error
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
const result = await api.openDirectory();
|
const result = await api.openDirectory();
|
||||||
@@ -317,7 +320,7 @@ export function WelcomeView() {
|
|||||||
});
|
});
|
||||||
setShowInitDialog(true);
|
setShowInitDialog(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create project:', error);
|
logger.error('Failed to create project:', error);
|
||||||
toast.error('Failed to create project', {
|
toast.error('Failed to create project', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -418,7 +421,7 @@ export function WelcomeView() {
|
|||||||
// Kick off project analysis
|
// Kick off project analysis
|
||||||
analyzeProject(projectPath);
|
analyzeProject(projectPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create project from template:', error);
|
logger.error('Failed to create project from template:', error);
|
||||||
toast.error('Failed to create project', {
|
toast.error('Failed to create project', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
@@ -515,7 +518,7 @@ export function WelcomeView() {
|
|||||||
// Kick off project analysis
|
// Kick off project analysis
|
||||||
analyzeProject(projectPath);
|
analyzeProject(projectPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create project from custom URL:', error);
|
logger.error('Failed to create project from custom URL:', error);
|
||||||
toast.error('Failed to create project', {
|
toast.error('Failed to create project', {
|
||||||
description: error instanceof Error ? error.message : 'Unknown error',
|
description: error instanceof Error ? error.message : 'Unknown error',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { useEffect, useCallback, useMemo } from 'react';
|
import { useEffect, useCallback, useMemo } from 'react';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import type { AutoModeEvent } from '@/types/electron';
|
import type { AutoModeEvent } from '@/types/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('AutoMode');
|
||||||
|
|
||||||
// Type guard for plan_approval_required event
|
// Type guard for plan_approval_required event
|
||||||
function isPlanApprovalEvent(
|
function isPlanApprovalEvent(
|
||||||
event: AutoModeEvent
|
event: AutoModeEvent
|
||||||
@@ -67,7 +70,7 @@ export function useAutoMode() {
|
|||||||
if (!api?.autoMode) return;
|
if (!api?.autoMode) return;
|
||||||
|
|
||||||
const unsubscribe = api.autoMode.onEvent((event: AutoModeEvent) => {
|
const unsubscribe = api.autoMode.onEvent((event: AutoModeEvent) => {
|
||||||
console.log('[AutoMode Event]', event);
|
logger.info('Event:', event);
|
||||||
|
|
||||||
// Events include projectPath from backend - use it to look up project ID
|
// Events include projectPath from backend - use it to look up project ID
|
||||||
// Fall back to current projectId if not provided in event
|
// Fall back to current projectId if not provided in event
|
||||||
@@ -84,7 +87,7 @@ export function useAutoMode() {
|
|||||||
|
|
||||||
// Skip event if we couldn't determine the project
|
// Skip event if we couldn't determine the project
|
||||||
if (!eventProjectId) {
|
if (!eventProjectId) {
|
||||||
console.warn('[AutoMode] Could not determine project for event:', event);
|
logger.warn('Could not determine project for event:', event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +106,7 @@ export function useAutoMode() {
|
|||||||
case 'auto_mode_feature_complete':
|
case 'auto_mode_feature_complete':
|
||||||
// Feature completed - remove from running tasks and UI will reload features on its own
|
// Feature completed - remove from running tasks and UI will reload features on its own
|
||||||
if (event.featureId) {
|
if (event.featureId) {
|
||||||
console.log('[AutoMode] Feature completed:', event.featureId, 'passes:', event.passes);
|
logger.info('Feature completed:', event.featureId, 'passes:', event.passes);
|
||||||
removeRunningTask(eventProjectId, event.featureId);
|
removeRunningTask(eventProjectId, event.featureId);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
featureId: event.featureId,
|
featureId: event.featureId,
|
||||||
@@ -121,7 +124,7 @@ export function useAutoMode() {
|
|||||||
// Check if this is a user-initiated cancellation or abort (not a real error)
|
// Check if this is a user-initiated cancellation or abort (not a real error)
|
||||||
if (event.errorType === 'cancellation' || event.errorType === 'abort') {
|
if (event.errorType === 'cancellation' || event.errorType === 'abort') {
|
||||||
// User cancelled/aborted the feature - just log as info, not an error
|
// User cancelled/aborted the feature - just log as info, not an error
|
||||||
console.log('[AutoMode] Feature cancelled/aborted:', event.error);
|
logger.info('Feature cancelled/aborted:', event.error);
|
||||||
// Remove from running tasks
|
// Remove from running tasks
|
||||||
if (eventProjectId) {
|
if (eventProjectId) {
|
||||||
removeRunningTask(eventProjectId, event.featureId);
|
removeRunningTask(eventProjectId, event.featureId);
|
||||||
@@ -130,7 +133,7 @@ export function useAutoMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Real error - log and show to user
|
// Real error - log and show to user
|
||||||
console.error('[AutoMode Error]', event.error);
|
logger.error('Error:', event.error);
|
||||||
|
|
||||||
// Check for authentication errors and provide a more helpful message
|
// Check for authentication errors and provide a more helpful message
|
||||||
const isAuthError =
|
const isAuthError =
|
||||||
@@ -182,7 +185,7 @@ export function useAutoMode() {
|
|||||||
case 'auto_mode_phase':
|
case 'auto_mode_phase':
|
||||||
// Log phase transitions (Planning, Action, Verification)
|
// Log phase transitions (Planning, Action, Verification)
|
||||||
if (event.featureId && event.phase && event.message) {
|
if (event.featureId && event.phase && event.message) {
|
||||||
console.log(`[AutoMode] Phase: ${event.phase} for ${event.featureId}`);
|
logger.debug(`[AutoMode] Phase: ${event.phase} for ${event.featureId}`);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
featureId: event.featureId,
|
featureId: event.featureId,
|
||||||
type: event.phase,
|
type: event.phase,
|
||||||
@@ -195,7 +198,7 @@ export function useAutoMode() {
|
|||||||
case 'plan_approval_required':
|
case 'plan_approval_required':
|
||||||
// Plan requires user approval before proceeding
|
// Plan requires user approval before proceeding
|
||||||
if (isPlanApprovalEvent(event)) {
|
if (isPlanApprovalEvent(event)) {
|
||||||
console.log(`[AutoMode] Plan approval required for ${event.featureId}`);
|
logger.debug(`[AutoMode] Plan approval required for ${event.featureId}`);
|
||||||
setPendingPlanApproval({
|
setPendingPlanApproval({
|
||||||
featureId: event.featureId,
|
featureId: event.featureId,
|
||||||
projectPath: event.projectPath || currentProject?.path || '',
|
projectPath: event.projectPath || currentProject?.path || '',
|
||||||
@@ -208,7 +211,7 @@ export function useAutoMode() {
|
|||||||
case 'planning_started':
|
case 'planning_started':
|
||||||
// Log when planning phase begins
|
// Log when planning phase begins
|
||||||
if (event.featureId && event.mode && event.message) {
|
if (event.featureId && event.mode && event.message) {
|
||||||
console.log(`[AutoMode] Planning started (${event.mode}) for ${event.featureId}`);
|
logger.debug(`[AutoMode] Planning started (${event.mode}) for ${event.featureId}`);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
featureId: event.featureId,
|
featureId: event.featureId,
|
||||||
type: 'planning',
|
type: 'planning',
|
||||||
@@ -221,7 +224,7 @@ export function useAutoMode() {
|
|||||||
case 'plan_approved':
|
case 'plan_approved':
|
||||||
// Log when plan is approved by user
|
// Log when plan is approved by user
|
||||||
if (event.featureId) {
|
if (event.featureId) {
|
||||||
console.log(`[AutoMode] Plan approved for ${event.featureId}`);
|
logger.debug(`[AutoMode] Plan approved for ${event.featureId}`);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
featureId: event.featureId,
|
featureId: event.featureId,
|
||||||
type: 'action',
|
type: 'action',
|
||||||
@@ -236,7 +239,7 @@ export function useAutoMode() {
|
|||||||
case 'plan_auto_approved':
|
case 'plan_auto_approved':
|
||||||
// Log when plan is auto-approved (requirePlanApproval=false)
|
// Log when plan is auto-approved (requirePlanApproval=false)
|
||||||
if (event.featureId) {
|
if (event.featureId) {
|
||||||
console.log(`[AutoMode] Plan auto-approved for ${event.featureId}`);
|
logger.debug(`[AutoMode] Plan auto-approved for ${event.featureId}`);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
featureId: event.featureId,
|
featureId: event.featureId,
|
||||||
type: 'action',
|
type: 'action',
|
||||||
@@ -253,7 +256,7 @@ export function useAutoMode() {
|
|||||||
AutoModeEvent,
|
AutoModeEvent,
|
||||||
{ type: 'plan_revision_requested' }
|
{ type: 'plan_revision_requested' }
|
||||||
>;
|
>;
|
||||||
console.log(
|
logger.debug(
|
||||||
`[AutoMode] Plan revision requested for ${event.featureId} (v${revisionEvent.planVersion})`
|
`[AutoMode] Plan revision requested for ${event.featureId} (v${revisionEvent.planVersion})`
|
||||||
);
|
);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
@@ -269,7 +272,7 @@ export function useAutoMode() {
|
|||||||
// Task started - show which task is being worked on
|
// Task started - show which task is being worked on
|
||||||
if (event.featureId && 'taskId' in event && 'taskDescription' in event) {
|
if (event.featureId && 'taskId' in event && 'taskDescription' in event) {
|
||||||
const taskEvent = event as Extract<AutoModeEvent, { type: 'auto_mode_task_started' }>;
|
const taskEvent = event as Extract<AutoModeEvent, { type: 'auto_mode_task_started' }>;
|
||||||
console.log(
|
logger.debug(
|
||||||
`[AutoMode] Task ${taskEvent.taskId} started for ${event.featureId}: ${taskEvent.taskDescription}`
|
`[AutoMode] Task ${taskEvent.taskId} started for ${event.featureId}: ${taskEvent.taskDescription}`
|
||||||
);
|
);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
@@ -284,7 +287,7 @@ export function useAutoMode() {
|
|||||||
// Task completed - show progress
|
// Task completed - show progress
|
||||||
if (event.featureId && 'taskId' in event) {
|
if (event.featureId && 'taskId' in event) {
|
||||||
const taskEvent = event as Extract<AutoModeEvent, { type: 'auto_mode_task_complete' }>;
|
const taskEvent = event as Extract<AutoModeEvent, { type: 'auto_mode_task_complete' }>;
|
||||||
console.log(
|
logger.debug(
|
||||||
`[AutoMode] Task ${taskEvent.taskId} completed for ${event.featureId} (${taskEvent.tasksCompleted}/${taskEvent.tasksTotal})`
|
`[AutoMode] Task ${taskEvent.taskId} completed for ${event.featureId} (${taskEvent.tasksCompleted}/${taskEvent.tasksTotal})`
|
||||||
);
|
);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
@@ -302,7 +305,7 @@ export function useAutoMode() {
|
|||||||
AutoModeEvent,
|
AutoModeEvent,
|
||||||
{ type: 'auto_mode_phase_complete' }
|
{ type: 'auto_mode_phase_complete' }
|
||||||
>;
|
>;
|
||||||
console.log(
|
logger.debug(
|
||||||
`[AutoMode] Phase ${phaseEvent.phaseNumber} completed for ${event.featureId}`
|
`[AutoMode] Phase ${phaseEvent.phaseNumber} completed for ${event.featureId}`
|
||||||
);
|
);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
@@ -330,18 +333,18 @@ export function useAutoMode() {
|
|||||||
// Start auto mode - UI only, feature pickup is handled in board-view.tsx
|
// Start auto mode - UI only, feature pickup is handled in board-view.tsx
|
||||||
const start = useCallback(() => {
|
const start = useCallback(() => {
|
||||||
if (!currentProject) {
|
if (!currentProject) {
|
||||||
console.error('No project selected');
|
logger.error('No project selected');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAutoModeRunning(currentProject.id, true);
|
setAutoModeRunning(currentProject.id, true);
|
||||||
console.log(`[AutoMode] Started with maxConcurrency: ${maxConcurrency}`);
|
logger.debug(`[AutoMode] Started with maxConcurrency: ${maxConcurrency}`);
|
||||||
}, [currentProject, setAutoModeRunning, maxConcurrency]);
|
}, [currentProject, setAutoModeRunning, maxConcurrency]);
|
||||||
|
|
||||||
// Stop auto mode - UI only, running tasks continue until natural completion
|
// Stop auto mode - UI only, running tasks continue until natural completion
|
||||||
const stop = useCallback(() => {
|
const stop = useCallback(() => {
|
||||||
if (!currentProject) {
|
if (!currentProject) {
|
||||||
console.error('No project selected');
|
logger.error('No project selected');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,14 +353,14 @@ export function useAutoMode() {
|
|||||||
// Stopping auto mode only turns off the toggle to prevent new features
|
// Stopping auto mode only turns off the toggle to prevent new features
|
||||||
// from being picked up. Running tasks will complete naturally and be
|
// from being picked up. Running tasks will complete naturally and be
|
||||||
// removed via the auto_mode_feature_complete event.
|
// removed via the auto_mode_feature_complete event.
|
||||||
console.log('[AutoMode] Stopped - running tasks will continue');
|
logger.info('Stopped - running tasks will continue');
|
||||||
}, [currentProject, setAutoModeRunning]);
|
}, [currentProject, setAutoModeRunning]);
|
||||||
|
|
||||||
// Stop a specific feature
|
// Stop a specific feature
|
||||||
const stopFeature = useCallback(
|
const stopFeature = useCallback(
|
||||||
async (featureId: string) => {
|
async (featureId: string) => {
|
||||||
if (!currentProject) {
|
if (!currentProject) {
|
||||||
console.error('No project selected');
|
logger.error('No project selected');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +374,7 @@ export function useAutoMode() {
|
|||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
removeRunningTask(currentProject.id, featureId);
|
removeRunningTask(currentProject.id, featureId);
|
||||||
console.log('[AutoMode] Feature stopped successfully:', featureId);
|
logger.info('Feature stopped successfully:', featureId);
|
||||||
addAutoModeActivity({
|
addAutoModeActivity({
|
||||||
featureId,
|
featureId,
|
||||||
type: 'complete',
|
type: 'complete',
|
||||||
@@ -379,11 +382,11 @@ export function useAutoMode() {
|
|||||||
passes: false,
|
passes: false,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error('[AutoMode] Failed to stop feature:', result.error);
|
logger.error('Failed to stop feature:', result.error);
|
||||||
throw new Error(result.error || 'Failed to stop feature');
|
throw new Error(result.error || 'Failed to stop feature');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[AutoMode] Error stopping feature:', error);
|
logger.error('Error stopping feature:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
const logger = createLogger('BoardBackground');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook for managing board background settings with automatic persistence to server
|
* Hook for managing board background settings with automatic persistence to server
|
||||||
*/
|
*/
|
||||||
@@ -19,11 +22,11 @@ export function useBoardBackgroundSettings() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
console.error('Failed to persist settings:', result.error);
|
logger.error('Failed to persist settings:', result.error);
|
||||||
toast.error('Failed to save settings');
|
toast.error('Failed to save settings');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to persist settings:', error);
|
logger.error('Failed to persist settings:', error);
|
||||||
toast.error('Failed to save settings');
|
toast.error('Failed to save settings');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { useMessageQueue } from './use-message-queue';
|
|||||||
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { sanitizeFilename } from '@/lib/image-utils';
|
import { sanitizeFilename } from '@/lib/image-utils';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('ElectronAgent');
|
||||||
|
|
||||||
interface UseElectronAgentOptions {
|
interface UseElectronAgentOptions {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
@@ -93,7 +96,7 @@ export function useElectronAgent({
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('[useElectronAgent] Sending message directly', {
|
logger.info('Sending message directly', {
|
||||||
hasImages: images && images.length > 0,
|
hasImages: images && images.length > 0,
|
||||||
imageCount: images?.length || 0,
|
imageCount: images?.length || 0,
|
||||||
hasTextFiles: textFiles && textFiles.length > 0,
|
hasTextFiles: textFiles && textFiles.length > 0,
|
||||||
@@ -123,9 +126,9 @@ export function useElectronAgent({
|
|||||||
);
|
);
|
||||||
if (result.success && result.path) {
|
if (result.success && result.path) {
|
||||||
imagePaths.push(result.path);
|
imagePaths.push(result.path);
|
||||||
console.log('[useElectronAgent] Saved image to .automaker/images:', result.path);
|
logger.info('Saved image to .automaker/images:', result.path);
|
||||||
} else {
|
} else {
|
||||||
console.error('[useElectronAgent] Failed to save image:', result.error);
|
logger.error('Failed to save image:', result.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +149,7 @@ export function useElectronAgent({
|
|||||||
// Note: We don't set isProcessing to false here because
|
// Note: We don't set isProcessing to false here because
|
||||||
// it will be set by the "complete" or "error" stream event
|
// it will be set by the "complete" or "error" stream event
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useElectronAgent] Failed to send message:', err);
|
logger.error('Failed to send message:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to send message');
|
setError(err instanceof Error ? err.message : 'Failed to send message');
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
throw err;
|
throw err;
|
||||||
@@ -191,13 +194,13 @@ export function useElectronAgent({
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('[useElectronAgent] Starting session:', sessionId);
|
logger.info('Starting session:', sessionId);
|
||||||
const result = await api.agent!.start(sessionId, workingDirectory);
|
const result = await api.agent!.start(sessionId, workingDirectory);
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
if (result.success && result.messages) {
|
if (result.success && result.messages) {
|
||||||
console.log('[useElectronAgent] Loaded', result.messages.length, 'messages');
|
logger.info('Loaded', result.messages.length, 'messages');
|
||||||
setMessages(result.messages);
|
setMessages(result.messages);
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
|
|
||||||
@@ -205,7 +208,7 @@ export function useElectronAgent({
|
|||||||
const historyResult = await api.agent!.getHistory(sessionId);
|
const historyResult = await api.agent!.getHistory(sessionId);
|
||||||
if (mounted && historyResult.success) {
|
if (mounted && historyResult.success) {
|
||||||
const isRunning = historyResult.isRunning || false;
|
const isRunning = historyResult.isRunning || false;
|
||||||
console.log('[useElectronAgent] Session running state:', isRunning);
|
logger.info('Session running state:', isRunning);
|
||||||
setIsProcessing(isRunning);
|
setIsProcessing(isRunning);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -214,7 +217,7 @@ export function useElectronAgent({
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
console.error('[useElectronAgent] Failed to initialize:', err);
|
logger.error('Failed to initialize:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to initialize');
|
setError(err instanceof Error ? err.message : 'Failed to initialize');
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
@@ -230,7 +233,7 @@ export function useElectronAgent({
|
|||||||
// Auto-process queue when agent finishes processing
|
// Auto-process queue when agent finishes processing
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isProcessing && !isProcessingQueue && queuedMessages.length > 0) {
|
if (!isProcessing && !isProcessingQueue && queuedMessages.length > 0) {
|
||||||
console.log('[useElectronAgent] Auto-processing next queued message');
|
logger.info('Auto-processing next queued message');
|
||||||
processNext();
|
processNext();
|
||||||
}
|
}
|
||||||
}, [isProcessing, isProcessingQueue, queuedMessages.length, processNext]);
|
}, [isProcessing, isProcessingQueue, queuedMessages.length, processNext]);
|
||||||
@@ -241,21 +244,21 @@ export function useElectronAgent({
|
|||||||
if (!api?.agent) return;
|
if (!api?.agent) return;
|
||||||
if (!sessionId) return; // Don't subscribe if no session
|
if (!sessionId) return; // Don't subscribe if no session
|
||||||
|
|
||||||
console.log('[useElectronAgent] Subscribing to stream events for session:', sessionId);
|
logger.info('Subscribing to stream events for session:', sessionId);
|
||||||
|
|
||||||
const handleStream = (event: StreamEvent) => {
|
const handleStream = (event: StreamEvent) => {
|
||||||
// CRITICAL: Only process events for our specific session
|
// CRITICAL: Only process events for our specific session
|
||||||
if (event.sessionId !== sessionId) {
|
if (event.sessionId !== sessionId) {
|
||||||
console.log('[useElectronAgent] Ignoring event for different session:', event.sessionId);
|
logger.info('Ignoring event for different session:', event.sessionId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[useElectronAgent] Stream event for', sessionId, ':', event.type);
|
logger.info('Stream event for', sessionId, ':', event.type);
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case 'started':
|
case 'started':
|
||||||
// Agent started processing (including from queue)
|
// Agent started processing (including from queue)
|
||||||
console.log('[useElectronAgent] Agent started processing for session:', sessionId);
|
logger.info('Agent started processing for session:', sessionId);
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -300,13 +303,13 @@ export function useElectronAgent({
|
|||||||
|
|
||||||
case 'tool_use':
|
case 'tool_use':
|
||||||
// Tool being used
|
// Tool being used
|
||||||
console.log('[useElectronAgent] Tool use:', event.tool.name);
|
logger.info('Tool use:', event.tool.name);
|
||||||
onToolUse?.(event.tool.name, event.tool.input);
|
onToolUse?.(event.tool.name, event.tool.input);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'complete':
|
case 'complete':
|
||||||
// Agent finished processing for THIS session
|
// Agent finished processing for THIS session
|
||||||
console.log('[useElectronAgent] Processing complete for session:', sessionId);
|
logger.info('Processing complete for session:', sessionId);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
if (event.messageId) {
|
if (event.messageId) {
|
||||||
setMessages((prev) =>
|
setMessages((prev) =>
|
||||||
@@ -319,7 +322,7 @@ export function useElectronAgent({
|
|||||||
|
|
||||||
case 'error':
|
case 'error':
|
||||||
// Error occurred for THIS session
|
// Error occurred for THIS session
|
||||||
console.error('[useElectronAgent] Agent error for session:', sessionId, event.error);
|
logger.error('Agent error for session:', sessionId, event.error);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
setError(event.error);
|
setError(event.error);
|
||||||
if (event.message) {
|
if (event.message) {
|
||||||
@@ -330,13 +333,13 @@ export function useElectronAgent({
|
|||||||
|
|
||||||
case 'queue_updated':
|
case 'queue_updated':
|
||||||
// Server queue was updated
|
// Server queue was updated
|
||||||
console.log('[useElectronAgent] Queue updated:', event.queue);
|
logger.info('Queue updated:', event.queue);
|
||||||
setServerQueue(event.queue || []);
|
setServerQueue(event.queue || []);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'queue_error':
|
case 'queue_error':
|
||||||
// Error processing a queued prompt
|
// Error processing a queued prompt
|
||||||
console.error('[useElectronAgent] Queue error:', event.error);
|
logger.error('Queue error:', event.error);
|
||||||
setError(event.error);
|
setError(event.error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -346,7 +349,7 @@ export function useElectronAgent({
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (unsubscribeRef.current) {
|
if (unsubscribeRef.current) {
|
||||||
console.log('[useElectronAgent] Unsubscribing from stream events for session:', sessionId);
|
logger.info('Unsubscribing from stream events for session:', sessionId);
|
||||||
unsubscribeRef.current();
|
unsubscribeRef.current();
|
||||||
unsubscribeRef.current = null;
|
unsubscribeRef.current = null;
|
||||||
}
|
}
|
||||||
@@ -363,7 +366,7 @@ export function useElectronAgent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isProcessing) {
|
if (isProcessing) {
|
||||||
console.warn('[useElectronAgent] Already processing a message');
|
logger.warn('Already processing a message');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +374,7 @@ export function useElectronAgent({
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('[useElectronAgent] Sending message', {
|
logger.info('Sending message', {
|
||||||
hasImages: images && images.length > 0,
|
hasImages: images && images.length > 0,
|
||||||
imageCount: images?.length || 0,
|
imageCount: images?.length || 0,
|
||||||
hasTextFiles: textFiles && textFiles.length > 0,
|
hasTextFiles: textFiles && textFiles.length > 0,
|
||||||
@@ -401,9 +404,9 @@ export function useElectronAgent({
|
|||||||
);
|
);
|
||||||
if (result.success && result.path) {
|
if (result.success && result.path) {
|
||||||
imagePaths.push(result.path);
|
imagePaths.push(result.path);
|
||||||
console.log('[useElectronAgent] Saved image to .automaker/images:', result.path);
|
logger.info('Saved image to .automaker/images:', result.path);
|
||||||
} else {
|
} else {
|
||||||
console.error('[useElectronAgent] Failed to save image:', result.error);
|
logger.error('Failed to save image:', result.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,7 +427,7 @@ export function useElectronAgent({
|
|||||||
// Note: We don't set isProcessing to false here because
|
// Note: We don't set isProcessing to false here because
|
||||||
// it will be set by the "complete" or "error" stream event
|
// it will be set by the "complete" or "error" stream event
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useElectronAgent] Failed to send message:', err);
|
logger.error('Failed to send message:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to send message');
|
setError(err instanceof Error ? err.message : 'Failed to send message');
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
@@ -441,7 +444,7 @@ export function useElectronAgent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('[useElectronAgent] Stopping execution');
|
logger.info('Stopping execution');
|
||||||
const result = await api.agent!.stop(sessionId);
|
const result = await api.agent!.stop(sessionId);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
@@ -450,7 +453,7 @@ export function useElectronAgent({
|
|||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useElectronAgent] Failed to stop:', err);
|
logger.error('Failed to stop:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to stop execution');
|
setError(err instanceof Error ? err.message : 'Failed to stop execution');
|
||||||
}
|
}
|
||||||
}, [sessionId]);
|
}, [sessionId]);
|
||||||
@@ -464,7 +467,7 @@ export function useElectronAgent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('[useElectronAgent] Clearing history');
|
logger.info('Clearing history');
|
||||||
const result = await api.agent!.clear(sessionId);
|
const result = await api.agent!.clear(sessionId);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -474,7 +477,7 @@ export function useElectronAgent({
|
|||||||
setError(result.error || 'Failed to clear history');
|
setError(result.error || 'Failed to clear history');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useElectronAgent] Failed to clear:', err);
|
logger.error('Failed to clear:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to clear history');
|
setError(err instanceof Error ? err.message : 'Failed to clear history');
|
||||||
}
|
}
|
||||||
}, [sessionId]);
|
}, [sessionId]);
|
||||||
@@ -516,14 +519,14 @@ export function useElectronAgent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[useElectronAgent] Adding to server queue');
|
logger.info('Adding to server queue');
|
||||||
const result = await api.agent.queueAdd(sessionId, messageContent, imagePaths, model);
|
const result = await api.agent.queueAdd(sessionId, messageContent, imagePaths, model);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
setError(result.error || 'Failed to add to queue');
|
setError(result.error || 'Failed to add to queue');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useElectronAgent] Failed to add to queue:', err);
|
logger.error('Failed to add to queue:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to add to queue');
|
setError(err instanceof Error ? err.message : 'Failed to add to queue');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -540,14 +543,14 @@ export function useElectronAgent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('[useElectronAgent] Removing from server queue:', promptId);
|
logger.info('Removing from server queue:', promptId);
|
||||||
const result = await api.agent.queueRemove(sessionId, promptId);
|
const result = await api.agent.queueRemove(sessionId, promptId);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
setError(result.error || 'Failed to remove from queue');
|
setError(result.error || 'Failed to remove from queue');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useElectronAgent] Failed to remove from queue:', err);
|
logger.error('Failed to remove from queue:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to remove from queue');
|
setError(err instanceof Error ? err.message : 'Failed to remove from queue');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -563,14 +566,14 @@ export function useElectronAgent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('[useElectronAgent] Clearing server queue');
|
logger.info('Clearing server queue');
|
||||||
const result = await api.agent.queueClear(sessionId);
|
const result = await api.agent.queueClear(sessionId);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
setError(result.error || 'Failed to clear queue');
|
setError(result.error || 'Failed to clear queue');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[useElectronAgent] Failed to clear queue:', err);
|
logger.error('Failed to clear queue:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to clear queue');
|
setError(err instanceof Error ? err.message : 'Failed to clear queue');
|
||||||
}
|
}
|
||||||
}, [sessionId]);
|
}, [sessionId]);
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('MessageQueue');
|
||||||
|
|
||||||
export interface QueuedMessage {
|
export interface QueuedMessage {
|
||||||
id: string;
|
id: string;
|
||||||
content: string;
|
content: string;
|
||||||
@@ -72,7 +75,7 @@ export function useMessageQueue({ onProcessNext }: UseMessageQueueOptions): UseM
|
|||||||
// Remove the processed message from queue
|
// Remove the processed message from queue
|
||||||
setQueuedMessages((prev) => prev.slice(1));
|
setQueuedMessages((prev) => prev.slice(1));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error processing queued message:', error);
|
logger.error('Error processing queued message:', error);
|
||||||
// Keep the message in queue for retry or manual removal
|
// Keep the message in queue for retry or manual removal
|
||||||
} finally {
|
} finally {
|
||||||
setIsProcessingQueue(false);
|
setIsProcessingQueue(false);
|
||||||
|
|||||||
@@ -18,11 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getHttpApiClient, waitForApiKeyInit } from '@/lib/http-api-client';
|
import { getHttpApiClient, waitForApiKeyInit } from '@/lib/http-api-client';
|
||||||
import { isElectron } from '@/lib/electron';
|
import { isElectron } from '@/lib/electron';
|
||||||
import { getItem, removeItem } from '@/lib/storage';
|
import { getItem, removeItem } from '@/lib/storage';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
|
const logger = createLogger('SettingsMigration');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* State returned by useSettingsMigration hook
|
* State returned by useSettingsMigration hook
|
||||||
*/
|
*/
|
||||||
@@ -109,7 +112,7 @@ export function useSettingsMigration(): MigrationState {
|
|||||||
const status = await api.settings.getStatus();
|
const status = await api.settings.getStatus();
|
||||||
|
|
||||||
if (!status.success) {
|
if (!status.success) {
|
||||||
console.error('[Settings Migration] Failed to get status:', status);
|
logger.error('Failed to get status:', status);
|
||||||
setState({
|
setState({
|
||||||
checked: true,
|
checked: true,
|
||||||
migrated: false,
|
migrated: false,
|
||||||
@@ -120,7 +123,7 @@ export function useSettingsMigration(): MigrationState {
|
|||||||
|
|
||||||
// If settings files already exist, no migration needed
|
// If settings files already exist, no migration needed
|
||||||
if (!status.needsMigration) {
|
if (!status.needsMigration) {
|
||||||
console.log('[Settings Migration] Settings files exist, no migration needed');
|
logger.info('Settings files exist, no migration needed');
|
||||||
setState({ checked: true, migrated: false, error: null });
|
setState({ checked: true, migrated: false, error: null });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -128,12 +131,12 @@ export function useSettingsMigration(): MigrationState {
|
|||||||
// Check if we have localStorage data to migrate
|
// Check if we have localStorage data to migrate
|
||||||
const automakerStorage = getItem('automaker-storage');
|
const automakerStorage = getItem('automaker-storage');
|
||||||
if (!automakerStorage) {
|
if (!automakerStorage) {
|
||||||
console.log('[Settings Migration] No localStorage data to migrate');
|
logger.info('No localStorage data to migrate');
|
||||||
setState({ checked: true, migrated: false, error: null });
|
setState({ checked: true, migrated: false, error: null });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[Settings Migration] Starting migration...');
|
logger.info('Starting migration...');
|
||||||
|
|
||||||
// Collect all localStorage data
|
// Collect all localStorage data
|
||||||
const localStorageData: Record<string, string> = {};
|
const localStorageData: Record<string, string> = {};
|
||||||
@@ -148,7 +151,7 @@ export function useSettingsMigration(): MigrationState {
|
|||||||
const result = await api.settings.migrate(localStorageData);
|
const result = await api.settings.migrate(localStorageData);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log('[Settings Migration] Migration successful:', {
|
logger.info('Migration successful:', {
|
||||||
globalSettings: result.migratedGlobalSettings,
|
globalSettings: result.migratedGlobalSettings,
|
||||||
credentials: result.migratedCredentials,
|
credentials: result.migratedCredentials,
|
||||||
projects: result.migratedProjectCount,
|
projects: result.migratedProjectCount,
|
||||||
@@ -161,7 +164,7 @@ export function useSettingsMigration(): MigrationState {
|
|||||||
|
|
||||||
setState({ checked: true, migrated: true, error: null });
|
setState({ checked: true, migrated: true, error: null });
|
||||||
} else {
|
} else {
|
||||||
console.warn('[Settings Migration] Migration had errors:', result.errors);
|
logger.warn('Migration had errors:', result.errors);
|
||||||
setState({
|
setState({
|
||||||
checked: true,
|
checked: true,
|
||||||
migrated: false,
|
migrated: false,
|
||||||
@@ -169,7 +172,7 @@ export function useSettingsMigration(): MigrationState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Settings Migration] Migration failed:', error);
|
logger.error('Migration failed:', error);
|
||||||
setState({
|
setState({
|
||||||
checked: true,
|
checked: true,
|
||||||
migrated: false,
|
migrated: false,
|
||||||
@@ -244,7 +247,7 @@ export async function syncSettingsToServer(): Promise<boolean> {
|
|||||||
const result = await api.settings.updateGlobal(updates);
|
const result = await api.settings.updateGlobal(updates);
|
||||||
return result.success;
|
return result.success;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Settings Sync] Failed to sync settings:', error);
|
logger.error('Failed to sync settings:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,7 +274,7 @@ export async function syncCredentialsToServer(apiKeys: {
|
|||||||
const result = await api.settings.updateCredentials({ apiKeys });
|
const result = await api.settings.updateCredentials({ apiKeys });
|
||||||
return result.success;
|
return result.success;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Settings Sync] Failed to sync credentials:', error);
|
logger.error('Failed to sync credentials:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,7 +315,7 @@ export async function syncProjectSettingsToServer(
|
|||||||
const result = await api.settings.updateProject(projectPath, updates);
|
const result = await api.settings.updateProject(projectPath, updates);
|
||||||
return result.success;
|
return result.success;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Settings Sync] Failed to sync project settings:', error);
|
logger.error('Failed to sync project settings:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,7 +335,7 @@ export async function loadMCPServersFromServer(): Promise<boolean> {
|
|||||||
const result = await api.settings.getGlobal();
|
const result = await api.settings.getGlobal();
|
||||||
|
|
||||||
if (!result.success || !result.settings) {
|
if (!result.success || !result.settings) {
|
||||||
console.error('[Settings Load] Failed to load settings:', result.error);
|
logger.error('Failed to load settings:', result.error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,10 +347,10 @@ export async function loadMCPServersFromServer(): Promise<boolean> {
|
|||||||
// We need to update the store directly since we can't use hooks here
|
// We need to update the store directly since we can't use hooks here
|
||||||
useAppStore.setState({ mcpServers, mcpAutoApproveTools, mcpUnrestrictedTools });
|
useAppStore.setState({ mcpServers, mcpAutoApproveTools, mcpUnrestrictedTools });
|
||||||
|
|
||||||
console.log(`[Settings Load] Loaded ${mcpServers.length} MCP servers from server`);
|
logger.info(`Loaded ${mcpServers.length} MCP servers from server`);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Settings Load] Failed to load MCP servers:', error);
|
logger.error('Failed to load MCP servers:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// Type definitions for Electron IPC API
|
// Type definitions for Electron IPC API
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import type { SessionListItem, Message } from '@/types/electron';
|
import type { SessionListItem, Message } from '@/types/electron';
|
||||||
import type { ClaudeUsageResponse } from '@/store/app-store';
|
import type { ClaudeUsageResponse } from '@/store/app-store';
|
||||||
import type {
|
import type {
|
||||||
@@ -94,6 +95,8 @@ import type {
|
|||||||
ProviderStatus,
|
ProviderStatus,
|
||||||
} from '@/types/electron';
|
} from '@/types/electron';
|
||||||
|
|
||||||
|
const logger = createLogger('Electron');
|
||||||
|
|
||||||
// Import HTTP API client (ES module)
|
// Import HTTP API client (ES module)
|
||||||
import { getHttpApiClient, getServerUrlSync } from './http-api-client';
|
import { getHttpApiClient, getServerUrlSync } from './http-api-client';
|
||||||
|
|
||||||
@@ -774,8 +777,8 @@ export const getCurrentApiMode = (): 'http' => {
|
|||||||
// Debug helpers
|
// Debug helpers
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
(window as any).__checkApiMode = () => {
|
(window as any).__checkApiMode = () => {
|
||||||
console.log('Current API mode:', getCurrentApiMode());
|
logger.info('Current API mode:', getCurrentApiMode());
|
||||||
console.log('isElectron():', isElectron());
|
logger.info('isElectron():', isElectron());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,7 +1019,7 @@ const getMockElectronAPI = (): ElectronAPI => {
|
|||||||
// Store the image data in mock file system for testing
|
// Store the image data in mock file system for testing
|
||||||
mockFileSystem[tempFilePath] = data;
|
mockFileSystem[tempFilePath] = data;
|
||||||
|
|
||||||
console.log('[Mock] Saved image to temp:', tempFilePath);
|
logger.info('Mock saved image to temp:', tempFilePath);
|
||||||
return { success: true, path: tempFilePath };
|
return { success: true, path: tempFilePath };
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1061,7 +1064,7 @@ const getMockElectronAPI = (): ElectronAPI => {
|
|||||||
// Mock Claude API
|
// Mock Claude API
|
||||||
claude: {
|
claude: {
|
||||||
getUsage: async () => {
|
getUsage: async () => {
|
||||||
console.log('[Mock] Getting Claude usage');
|
logger.info('Mock getting Claude usage');
|
||||||
return {
|
return {
|
||||||
sessionTokensUsed: 0,
|
sessionTokensUsed: 0,
|
||||||
sessionLimit: 0,
|
sessionLimit: 0,
|
||||||
@@ -1168,7 +1171,7 @@ interface SetupAPI {
|
|||||||
function createMockSetupAPI(): SetupAPI {
|
function createMockSetupAPI(): SetupAPI {
|
||||||
return {
|
return {
|
||||||
getClaudeStatus: async () => {
|
getClaudeStatus: async () => {
|
||||||
console.log('[Mock] Getting Claude status');
|
logger.info('Mock Getting Claude status');
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status: 'not_installed',
|
status: 'not_installed',
|
||||||
@@ -1185,7 +1188,7 @@ function createMockSetupAPI(): SetupAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
installClaude: async () => {
|
installClaude: async () => {
|
||||||
console.log('[Mock] Installing Claude CLI');
|
logger.info('Mock Installing Claude CLI');
|
||||||
// Simulate installation delay
|
// Simulate installation delay
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
return {
|
return {
|
||||||
@@ -1196,7 +1199,7 @@ function createMockSetupAPI(): SetupAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
authClaude: async () => {
|
authClaude: async () => {
|
||||||
console.log('[Mock] Auth Claude CLI');
|
logger.info('Mock Auth Claude CLI');
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
requiresManualAuth: true,
|
requiresManualAuth: true,
|
||||||
@@ -1205,13 +1208,13 @@ function createMockSetupAPI(): SetupAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
storeApiKey: async (provider: string, apiKey: string) => {
|
storeApiKey: async (provider: string, apiKey: string) => {
|
||||||
console.log('[Mock] Storing API key for:', provider);
|
logger.info('Mock Storing API key for:', provider);
|
||||||
// In mock mode, we just pretend to store it (it's already in the app store)
|
// In mock mode, we just pretend to store it (it's already in the app store)
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
|
|
||||||
getApiKeys: async () => {
|
getApiKeys: async () => {
|
||||||
console.log('[Mock] Getting API keys');
|
logger.info('Mock Getting API keys');
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
hasAnthropicKey: false,
|
hasAnthropicKey: false,
|
||||||
@@ -1220,7 +1223,7 @@ function createMockSetupAPI(): SetupAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deleteApiKey: async (provider: string) => {
|
deleteApiKey: async (provider: string) => {
|
||||||
console.log('[Mock] Deleting API key for:', provider);
|
logger.info('Mock Deleting API key for:', provider);
|
||||||
return { success: true, message: `API key for ${provider} deleted` };
|
return { success: true, message: `API key for ${provider} deleted` };
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1237,8 +1240,8 @@ function createMockSetupAPI(): SetupAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
verifyClaudeAuth: async (authMethod?: 'cli' | 'api_key', apiKey?: string) => {
|
verifyClaudeAuth: async (authMethod?: 'cli' | 'api_key', apiKey?: string) => {
|
||||||
console.log(
|
logger.info(
|
||||||
'[Mock] Verifying Claude auth with method:',
|
'Mock verifying Claude auth with method:',
|
||||||
authMethod,
|
authMethod,
|
||||||
apiKey ? '(with key)' : ''
|
apiKey ? '(with key)' : ''
|
||||||
);
|
);
|
||||||
@@ -1251,7 +1254,7 @@ function createMockSetupAPI(): SetupAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getGhStatus: async () => {
|
getGhStatus: async () => {
|
||||||
console.log('[Mock] Getting GitHub CLI status');
|
logger.info('Mock Getting GitHub CLI status');
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
installed: false,
|
installed: false,
|
||||||
@@ -1278,7 +1281,7 @@ function createMockSetupAPI(): SetupAPI {
|
|||||||
function createMockWorktreeAPI(): WorktreeAPI {
|
function createMockWorktreeAPI(): WorktreeAPI {
|
||||||
return {
|
return {
|
||||||
mergeFeature: async (projectPath: string, featureId: string, options?: object) => {
|
mergeFeature: async (projectPath: string, featureId: string, options?: object) => {
|
||||||
console.log('[Mock] Merging feature:', {
|
logger.info('Mock Merging feature:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
featureId,
|
featureId,
|
||||||
options,
|
options,
|
||||||
@@ -1287,7 +1290,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getInfo: async (projectPath: string, featureId: string) => {
|
getInfo: async (projectPath: string, featureId: string) => {
|
||||||
console.log('[Mock] Getting worktree info:', { projectPath, featureId });
|
logger.info('Mock Getting worktree info:', { projectPath, featureId });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
worktreePath: `/mock/worktrees/${featureId}`,
|
worktreePath: `/mock/worktrees/${featureId}`,
|
||||||
@@ -1297,7 +1300,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getStatus: async (projectPath: string, featureId: string) => {
|
getStatus: async (projectPath: string, featureId: string) => {
|
||||||
console.log('[Mock] Getting worktree status:', {
|
logger.info('Mock Getting worktree status:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
featureId,
|
featureId,
|
||||||
});
|
});
|
||||||
@@ -1311,12 +1314,12 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
list: async (projectPath: string) => {
|
list: async (projectPath: string) => {
|
||||||
console.log('[Mock] Listing worktrees:', { projectPath });
|
logger.info('Mock Listing worktrees:', { projectPath });
|
||||||
return { success: true, worktrees: [] };
|
return { success: true, worktrees: [] };
|
||||||
},
|
},
|
||||||
|
|
||||||
listAll: async (projectPath: string, includeDetails?: boolean) => {
|
listAll: async (projectPath: string, includeDetails?: boolean) => {
|
||||||
console.log('[Mock] Listing all worktrees:', {
|
logger.info('Mock Listing all worktrees:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
includeDetails,
|
includeDetails,
|
||||||
});
|
});
|
||||||
@@ -1337,7 +1340,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
create: async (projectPath: string, branchName: string, baseBranch?: string) => {
|
create: async (projectPath: string, branchName: string, baseBranch?: string) => {
|
||||||
console.log('[Mock] Creating worktree:', {
|
logger.info('Mock Creating worktree:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
branchName,
|
branchName,
|
||||||
baseBranch,
|
baseBranch,
|
||||||
@@ -1353,7 +1356,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
delete: async (projectPath: string, worktreePath: string, deleteBranch?: boolean) => {
|
delete: async (projectPath: string, worktreePath: string, deleteBranch?: boolean) => {
|
||||||
console.log('[Mock] Deleting worktree:', {
|
logger.info('Mock Deleting worktree:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
worktreePath,
|
worktreePath,
|
||||||
deleteBranch,
|
deleteBranch,
|
||||||
@@ -1368,7 +1371,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
commit: async (worktreePath: string, message: string) => {
|
commit: async (worktreePath: string, message: string) => {
|
||||||
console.log('[Mock] Committing changes:', { worktreePath, message });
|
logger.info('Mock Committing changes:', { worktreePath, message });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1381,7 +1384,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
push: async (worktreePath: string, force?: boolean) => {
|
push: async (worktreePath: string, force?: boolean) => {
|
||||||
console.log('[Mock] Pushing worktree:', { worktreePath, force });
|
logger.info('Mock Pushing worktree:', { worktreePath, force });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1393,7 +1396,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
createPR: async (worktreePath: string, options?: any) => {
|
createPR: async (worktreePath: string, options?: any) => {
|
||||||
console.log('[Mock] Creating PR:', { worktreePath, options });
|
logger.info('Mock Creating PR:', { worktreePath, options });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1408,7 +1411,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDiffs: async (projectPath: string, featureId: string) => {
|
getDiffs: async (projectPath: string, featureId: string) => {
|
||||||
console.log('[Mock] Getting file diffs:', { projectPath, featureId });
|
logger.info('Mock Getting file diffs:', { projectPath, featureId });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
diff: "diff --git a/src/feature.ts b/src/feature.ts\n+++ new file\n@@ -0,0 +1,10 @@\n+export function feature() {\n+ return 'hello';\n+}",
|
diff: "diff --git a/src/feature.ts b/src/feature.ts\n+++ new file\n@@ -0,0 +1,10 @@\n+export function feature() {\n+ return 'hello';\n+}",
|
||||||
@@ -1421,7 +1424,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getFileDiff: async (projectPath: string, featureId: string, filePath: string) => {
|
getFileDiff: async (projectPath: string, featureId: string, filePath: string) => {
|
||||||
console.log('[Mock] Getting file diff:', {
|
logger.info('Mock Getting file diff:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
featureId,
|
featureId,
|
||||||
filePath,
|
filePath,
|
||||||
@@ -1434,7 +1437,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
pull: async (worktreePath: string) => {
|
pull: async (worktreePath: string) => {
|
||||||
console.log('[Mock] Pulling latest changes for:', worktreePath);
|
logger.info('Mock Pulling latest changes for:', worktreePath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1446,7 +1449,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
checkoutBranch: async (worktreePath: string, branchName: string) => {
|
checkoutBranch: async (worktreePath: string, branchName: string) => {
|
||||||
console.log('[Mock] Creating and checking out branch:', {
|
logger.info('Mock Creating and checking out branch:', {
|
||||||
worktreePath,
|
worktreePath,
|
||||||
branchName,
|
branchName,
|
||||||
});
|
});
|
||||||
@@ -1461,7 +1464,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
listBranches: async (worktreePath: string) => {
|
listBranches: async (worktreePath: string) => {
|
||||||
console.log('[Mock] Listing branches for:', worktreePath);
|
logger.info('Mock Listing branches for:', worktreePath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1478,7 +1481,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
switchBranch: async (worktreePath: string, branchName: string) => {
|
switchBranch: async (worktreePath: string, branchName: string) => {
|
||||||
console.log('[Mock] Switching to branch:', { worktreePath, branchName });
|
logger.info('Mock Switching to branch:', { worktreePath, branchName });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1490,7 +1493,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
openInEditor: async (worktreePath: string) => {
|
openInEditor: async (worktreePath: string) => {
|
||||||
console.log('[Mock] Opening in editor:', worktreePath);
|
logger.info('Mock Opening in editor:', worktreePath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1501,7 +1504,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDefaultEditor: async () => {
|
getDefaultEditor: async () => {
|
||||||
console.log('[Mock] Getting default editor');
|
logger.info('Mock Getting default editor');
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1512,7 +1515,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
initGit: async (projectPath: string) => {
|
initGit: async (projectPath: string) => {
|
||||||
console.log('[Mock] Initializing git:', projectPath);
|
logger.info('Mock Initializing git:', projectPath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1523,7 +1526,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
startDevServer: async (projectPath: string, worktreePath: string) => {
|
startDevServer: async (projectPath: string, worktreePath: string) => {
|
||||||
console.log('[Mock] Starting dev server:', { projectPath, worktreePath });
|
logger.info('Mock Starting dev server:', { projectPath, worktreePath });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1536,7 +1539,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
stopDevServer: async (worktreePath: string) => {
|
stopDevServer: async (worktreePath: string) => {
|
||||||
console.log('[Mock] Stopping dev server:', worktreePath);
|
logger.info('Mock Stopping dev server:', worktreePath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1547,7 +1550,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
listDevServers: async () => {
|
listDevServers: async () => {
|
||||||
console.log('[Mock] Listing dev servers');
|
logger.info('Mock Listing dev servers');
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1557,7 +1560,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getPRInfo: async (worktreePath: string, branchName: string) => {
|
getPRInfo: async (worktreePath: string, branchName: string) => {
|
||||||
console.log('[Mock] Getting PR info:', { worktreePath, branchName });
|
logger.info('Mock Getting PR info:', { worktreePath, branchName });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
@@ -1573,7 +1576,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
function createMockGitAPI(): GitAPI {
|
function createMockGitAPI(): GitAPI {
|
||||||
return {
|
return {
|
||||||
getDiffs: async (projectPath: string) => {
|
getDiffs: async (projectPath: string) => {
|
||||||
console.log('[Mock] Getting git diffs for project:', { projectPath });
|
logger.info('Mock Getting git diffs for project:', { projectPath });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
diff: "diff --git a/src/feature.ts b/src/feature.ts\n+++ new file\n@@ -0,0 +1,10 @@\n+export function feature() {\n+ return 'hello';\n+}",
|
diff: "diff --git a/src/feature.ts b/src/feature.ts\n+++ new file\n@@ -0,0 +1,10 @@\n+export function feature() {\n+ return 'hello';\n+}",
|
||||||
@@ -1586,7 +1589,7 @@ function createMockGitAPI(): GitAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getFileDiff: async (projectPath: string, filePath: string) => {
|
getFileDiff: async (projectPath: string, filePath: string) => {
|
||||||
console.log('[Mock] Getting git file diff:', { projectPath, filePath });
|
logger.info('Mock Getting git file diff:', { projectPath, filePath });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
diff: `diff --git a/${filePath} b/${filePath}\n+++ new file\n@@ -0,0 +1,5 @@\n+// New content`,
|
diff: `diff --git a/${filePath} b/${filePath}\n+++ new file\n@@ -0,0 +1,5 @@\n+// New content`,
|
||||||
@@ -1610,7 +1613,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockAutoModeRunning = true;
|
mockAutoModeRunning = true;
|
||||||
console.log(`[Mock] Auto mode started with maxConcurrency: ${maxConcurrency || 3}`);
|
logger.info(`Mock auto mode started with maxConcurrency: ${maxConcurrency || 3}`);
|
||||||
const featureId = 'auto-mode-0';
|
const featureId = 'auto-mode-0';
|
||||||
mockRunningFeatures.add(featureId);
|
mockRunningFeatures.add(featureId);
|
||||||
|
|
||||||
@@ -1679,8 +1682,8 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
logger.info(
|
||||||
`[Mock] Running feature ${featureId} with useWorktrees: ${useWorktrees}, worktreePath: ${worktreePath}`
|
`Mock running feature ${featureId} with useWorktrees: ${useWorktrees}, worktreePath: ${worktreePath}`
|
||||||
);
|
);
|
||||||
mockRunningFeatures.add(featureId);
|
mockRunningFeatures.add(featureId);
|
||||||
simulateAutoModeLoop(projectPath, featureId);
|
simulateAutoModeLoop(projectPath, featureId);
|
||||||
@@ -1847,7 +1850,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[Mock] Follow-up feature:', {
|
logger.info('Mock Follow-up feature:', {
|
||||||
featureId,
|
featureId,
|
||||||
prompt,
|
prompt,
|
||||||
imagePaths,
|
imagePaths,
|
||||||
@@ -1864,7 +1867,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
commitFeature: async (projectPath: string, featureId: string, worktreePath?: string) => {
|
commitFeature: async (projectPath: string, featureId: string, worktreePath?: string) => {
|
||||||
console.log('[Mock] Committing feature:', {
|
logger.info('Mock Committing feature:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
featureId,
|
featureId,
|
||||||
worktreePath,
|
worktreePath,
|
||||||
@@ -1909,7 +1912,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
|||||||
editedPlan?: string,
|
editedPlan?: string,
|
||||||
feedback?: string
|
feedback?: string
|
||||||
) => {
|
) => {
|
||||||
console.log('[Mock] Plan approval:', {
|
logger.info('Mock Plan approval:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
featureId,
|
featureId,
|
||||||
approved,
|
approved,
|
||||||
@@ -2070,7 +2073,7 @@ function createMockSuggestionsAPI(): SuggestionsAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockSuggestionsRunning = true;
|
mockSuggestionsRunning = true;
|
||||||
console.log(`[Mock] Generating ${suggestionType} suggestions for: ${projectPath}`);
|
logger.info(`Mock generating ${suggestionType} suggestions for: ${projectPath}`);
|
||||||
|
|
||||||
// Simulate async suggestion generation
|
// Simulate async suggestion generation
|
||||||
simulateSuggestionsGeneration(suggestionType);
|
simulateSuggestionsGeneration(suggestionType);
|
||||||
@@ -2294,8 +2297,8 @@ function createMockSpecRegenerationAPI(): SpecRegenerationAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockSpecRegenerationRunning = true;
|
mockSpecRegenerationRunning = true;
|
||||||
console.log(
|
logger.info(
|
||||||
`[Mock] Creating initial spec for: ${projectPath}, generateFeatures: ${generateFeatures}, maxFeatures: ${maxFeatures}`
|
`Mock creating initial spec for: ${projectPath}, generateFeatures: ${generateFeatures}, maxFeatures: ${maxFeatures}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Simulate async spec creation
|
// Simulate async spec creation
|
||||||
@@ -2319,8 +2322,8 @@ function createMockSpecRegenerationAPI(): SpecRegenerationAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockSpecRegenerationRunning = true;
|
mockSpecRegenerationRunning = true;
|
||||||
console.log(
|
logger.info(
|
||||||
`[Mock] Regenerating spec for: ${projectPath}, generateFeatures: ${generateFeatures}, maxFeatures: ${maxFeatures}`
|
`Mock regenerating spec for: ${projectPath}, generateFeatures: ${generateFeatures}, maxFeatures: ${maxFeatures}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Simulate async spec regeneration
|
// Simulate async spec regeneration
|
||||||
@@ -2338,8 +2341,8 @@ function createMockSpecRegenerationAPI(): SpecRegenerationAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockSpecRegenerationRunning = true;
|
mockSpecRegenerationRunning = true;
|
||||||
console.log(
|
logger.info(
|
||||||
`[Mock] Generating features from existing spec for: ${projectPath}, maxFeatures: ${maxFeatures}`
|
`Mock generating features from existing spec for: ${projectPath}, maxFeatures: ${maxFeatures}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Simulate async feature generation
|
// Simulate async feature generation
|
||||||
@@ -2604,7 +2607,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
|||||||
// Store features in mock file system using features/{id}/feature.json pattern
|
// Store features in mock file system using features/{id}/feature.json pattern
|
||||||
return {
|
return {
|
||||||
getAll: async (projectPath: string) => {
|
getAll: async (projectPath: string) => {
|
||||||
console.log('[Mock] Getting all features for:', projectPath);
|
logger.info('Mock Getting all features for:', projectPath);
|
||||||
|
|
||||||
// Check if test has set mock features via global variable
|
// Check if test has set mock features via global variable
|
||||||
const testFeatures = (window as any).__mockFeatures;
|
const testFeatures = (window as any).__mockFeatures;
|
||||||
@@ -2629,7 +2632,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
|||||||
features.push(feature);
|
features.push(feature);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Mock] Failed to parse feature:', error);
|
logger.error('Mock Failed to parse feature:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2642,7 +2645,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
get: async (projectPath: string, featureId: string) => {
|
get: async (projectPath: string, featureId: string) => {
|
||||||
console.log('[Mock] Getting feature:', { projectPath, featureId });
|
logger.info('Mock Getting feature:', { projectPath, featureId });
|
||||||
const featurePath = `${projectPath}/.automaker/features/${featureId}/feature.json`;
|
const featurePath = `${projectPath}/.automaker/features/${featureId}/feature.json`;
|
||||||
const content = mockFileSystem[featurePath];
|
const content = mockFileSystem[featurePath];
|
||||||
if (content) {
|
if (content) {
|
||||||
@@ -2652,7 +2655,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
create: async (projectPath: string, feature: Feature) => {
|
create: async (projectPath: string, feature: Feature) => {
|
||||||
console.log('[Mock] Creating feature:', {
|
logger.info('Mock Creating feature:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
featureId: feature.id,
|
featureId: feature.id,
|
||||||
});
|
});
|
||||||
@@ -2662,7 +2665,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
update: async (projectPath: string, featureId: string, updates: Partial<Feature>) => {
|
update: async (projectPath: string, featureId: string, updates: Partial<Feature>) => {
|
||||||
console.log('[Mock] Updating feature:', {
|
logger.info('Mock Updating feature:', {
|
||||||
projectPath,
|
projectPath,
|
||||||
featureId,
|
featureId,
|
||||||
updates,
|
updates,
|
||||||
@@ -2678,7 +2681,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
delete: async (projectPath: string, featureId: string) => {
|
delete: async (projectPath: string, featureId: string) => {
|
||||||
console.log('[Mock] Deleting feature:', { projectPath, featureId });
|
logger.info('Mock Deleting feature:', { projectPath, featureId });
|
||||||
const featurePath = `${projectPath}/.automaker/features/${featureId}/feature.json`;
|
const featurePath = `${projectPath}/.automaker/features/${featureId}/feature.json`;
|
||||||
delete mockFileSystem[featurePath];
|
delete mockFileSystem[featurePath];
|
||||||
// Also delete agent-output.md if it exists
|
// Also delete agent-output.md if it exists
|
||||||
@@ -2688,14 +2691,14 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getAgentOutput: async (projectPath: string, featureId: string) => {
|
getAgentOutput: async (projectPath: string, featureId: string) => {
|
||||||
console.log('[Mock] Getting agent output:', { projectPath, featureId });
|
logger.info('Mock Getting agent output:', { projectPath, featureId });
|
||||||
const agentOutputPath = `${projectPath}/.automaker/features/${featureId}/agent-output.md`;
|
const agentOutputPath = `${projectPath}/.automaker/features/${featureId}/agent-output.md`;
|
||||||
const content = mockFileSystem[agentOutputPath];
|
const content = mockFileSystem[agentOutputPath];
|
||||||
return { success: true, content: content || null };
|
return { success: true, content: content || null };
|
||||||
},
|
},
|
||||||
|
|
||||||
generateTitle: async (description: string) => {
|
generateTitle: async (description: string) => {
|
||||||
console.log('[Mock] Generating title for:', description.substring(0, 50));
|
logger.info('Mock Generating title for:', description.substring(0, 50));
|
||||||
// Mock title generation - just take first few words
|
// Mock title generation - just take first few words
|
||||||
const words = description.split(/\s+/).slice(0, 6).join(' ');
|
const words = description.split(/\s+/).slice(0, 6).join(' ');
|
||||||
const title = words.length > 40 ? words.substring(0, 40) + '...' : words;
|
const title = words.length > 40 ? words.substring(0, 40) + '...' : words;
|
||||||
@@ -2708,7 +2711,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
|||||||
function createMockRunningAgentsAPI(): RunningAgentsAPI {
|
function createMockRunningAgentsAPI(): RunningAgentsAPI {
|
||||||
return {
|
return {
|
||||||
getAll: async () => {
|
getAll: async () => {
|
||||||
console.log('[Mock] Getting all running agents');
|
logger.info('Mock Getting all running agents');
|
||||||
// Return running agents from mock auto mode state
|
// Return running agents from mock auto mode state
|
||||||
const runningAgents: RunningAgent[] = Array.from(mockRunningFeatures).map((featureId) => ({
|
const runningAgents: RunningAgent[] = Array.from(mockRunningFeatures).map((featureId) => ({
|
||||||
featureId,
|
featureId,
|
||||||
@@ -2733,7 +2736,7 @@ let mockValidationCallbacks: ((event: IssueValidationEvent) => void)[] = [];
|
|||||||
function createMockGitHubAPI(): GitHubAPI {
|
function createMockGitHubAPI(): GitHubAPI {
|
||||||
return {
|
return {
|
||||||
checkRemote: async (projectPath: string) => {
|
checkRemote: async (projectPath: string) => {
|
||||||
console.log('[Mock] Checking GitHub remote for:', projectPath);
|
logger.info('Mock Checking GitHub remote for:', projectPath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
hasGitHubRemote: false,
|
hasGitHubRemote: false,
|
||||||
@@ -2743,7 +2746,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
listIssues: async (projectPath: string) => {
|
listIssues: async (projectPath: string) => {
|
||||||
console.log('[Mock] Listing GitHub issues for:', projectPath);
|
logger.info('Mock Listing GitHub issues for:', projectPath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
openIssues: [],
|
openIssues: [],
|
||||||
@@ -2751,7 +2754,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
listPRs: async (projectPath: string) => {
|
listPRs: async (projectPath: string) => {
|
||||||
console.log('[Mock] Listing GitHub PRs for:', projectPath);
|
logger.info('Mock Listing GitHub PRs for:', projectPath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
openPRs: [],
|
openPRs: [],
|
||||||
@@ -2759,7 +2762,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
validateIssue: async (projectPath: string, issue: IssueValidationInput, model?: ModelAlias) => {
|
validateIssue: async (projectPath: string, issue: IssueValidationInput, model?: ModelAlias) => {
|
||||||
console.log('[Mock] Starting async validation:', { projectPath, issue, model });
|
logger.info('Mock Starting async validation:', { projectPath, issue, model });
|
||||||
|
|
||||||
// Simulate async validation in background
|
// Simulate async validation in background
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -2800,7 +2803,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
getValidationStatus: async (projectPath: string, issueNumber?: number) => {
|
getValidationStatus: async (projectPath: string, issueNumber?: number) => {
|
||||||
console.log('[Mock] Getting validation status:', { projectPath, issueNumber });
|
logger.info('Mock Getting validation status:', { projectPath, issueNumber });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
isRunning: false,
|
isRunning: false,
|
||||||
@@ -2808,21 +2811,21 @@ function createMockGitHubAPI(): GitHubAPI {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
stopValidation: async (projectPath: string, issueNumber: number) => {
|
stopValidation: async (projectPath: string, issueNumber: number) => {
|
||||||
console.log('[Mock] Stopping validation:', { projectPath, issueNumber });
|
logger.info('Mock Stopping validation:', { projectPath, issueNumber });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: `Validation for issue #${issueNumber} stopped`,
|
message: `Validation for issue #${issueNumber} stopped`,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getValidations: async (projectPath: string, issueNumber?: number) => {
|
getValidations: async (projectPath: string, issueNumber?: number) => {
|
||||||
console.log('[Mock] Getting validations:', { projectPath, issueNumber });
|
logger.info('Mock Getting validations:', { projectPath, issueNumber });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
validations: [],
|
validations: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
markValidationViewed: async (projectPath: string, issueNumber: number) => {
|
markValidationViewed: async (projectPath: string, issueNumber: number) => {
|
||||||
console.log('[Mock] Marking validation as viewed:', { projectPath, issueNumber });
|
logger.info('Mock Marking validation as viewed:', { projectPath, issueNumber });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
@@ -2834,7 +2837,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
getIssueComments: async (projectPath: string, issueNumber: number, cursor?: string) => {
|
getIssueComments: async (projectPath: string, issueNumber: number, cursor?: string) => {
|
||||||
console.log('[Mock] Getting issue comments:', { projectPath, issueNumber, cursor });
|
logger.info('Mock Getting issue comments:', { projectPath, issueNumber, cursor });
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
comments: [],
|
comments: [],
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
* user confirmation or server-side path resolution.
|
* user confirmation or server-side path resolution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('FilePicker');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directory picker result with structure information for server-side resolution
|
* Directory picker result with structure information for server-side resolution
|
||||||
*/
|
*/
|
||||||
@@ -65,18 +69,18 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
|||||||
focusTimeout = null;
|
focusTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[FilePicker] Change event fired');
|
logger.info('Change event fired');
|
||||||
const files = input.files;
|
const files = input.files;
|
||||||
console.log('[FilePicker] Files selected:', files?.length || 0);
|
logger.info('Files selected:', files?.length || 0);
|
||||||
|
|
||||||
if (!files || files.length === 0) {
|
if (!files || files.length === 0) {
|
||||||
console.log('[FilePicker] No files selected');
|
logger.info('No files selected');
|
||||||
safeResolve(null);
|
safeResolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstFile = files[0];
|
const firstFile = files[0];
|
||||||
console.log('[FilePicker] First file:', {
|
logger.info('First file:', {
|
||||||
name: firstFile.name,
|
name: firstFile.name,
|
||||||
webkitRelativePath: firstFile.webkitRelativePath,
|
webkitRelativePath: firstFile.webkitRelativePath,
|
||||||
// @ts-expect-error - path property is non-standard but available in some browsers
|
// @ts-expect-error - path property is non-standard but available in some browsers
|
||||||
@@ -92,12 +96,12 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
|||||||
if (firstFile.path) {
|
if (firstFile.path) {
|
||||||
// @ts-expect-error - path property is non-standard but available in some browsers
|
// @ts-expect-error - path property is non-standard but available in some browsers
|
||||||
const filePath = firstFile.path as string;
|
const filePath = firstFile.path as string;
|
||||||
console.log('[FilePicker] Found file.path:', filePath);
|
logger.info('Found file.path:', filePath);
|
||||||
// Extract directory path (remove filename)
|
// Extract directory path (remove filename)
|
||||||
const lastSeparator = Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/'));
|
const lastSeparator = Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/'));
|
||||||
if (lastSeparator > 0) {
|
if (lastSeparator > 0) {
|
||||||
const absolutePath = filePath.substring(0, lastSeparator);
|
const absolutePath = filePath.substring(0, lastSeparator);
|
||||||
console.log('[FilePicker] Found absolute path:', absolutePath);
|
logger.info('Found absolute path:', absolutePath);
|
||||||
// Return as directory name for now - server can validate it directly
|
// Return as directory name for now - server can validate it directly
|
||||||
directoryName = absolutePath;
|
directoryName = absolutePath;
|
||||||
}
|
}
|
||||||
@@ -106,11 +110,11 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
|||||||
// Method 2: Extract directory name from webkitRelativePath
|
// Method 2: Extract directory name from webkitRelativePath
|
||||||
if (directoryName === 'Selected Directory' && firstFile.webkitRelativePath) {
|
if (directoryName === 'Selected Directory' && firstFile.webkitRelativePath) {
|
||||||
const relativePath = firstFile.webkitRelativePath;
|
const relativePath = firstFile.webkitRelativePath;
|
||||||
console.log('[FilePicker] Using webkitRelativePath:', relativePath);
|
logger.info('Using webkitRelativePath:', relativePath);
|
||||||
const pathParts = relativePath.split('/');
|
const pathParts = relativePath.split('/');
|
||||||
if (pathParts.length > 0) {
|
if (pathParts.length > 0) {
|
||||||
directoryName = pathParts[0]; // Top-level directory name
|
directoryName = pathParts[0]; // Top-level directory name
|
||||||
console.log('[FilePicker] Extracted directory name:', directoryName);
|
logger.info('Extracted directory name:', directoryName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +131,7 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[FilePicker] Directory info:', {
|
logger.info('Directory info:', {
|
||||||
directoryName,
|
directoryName,
|
||||||
fileCount: files.length,
|
fileCount: files.length,
|
||||||
sampleFiles: sampleFiles.slice(0, 5), // Log first 5
|
sampleFiles: sampleFiles.slice(0, 5), // Log first 5
|
||||||
@@ -147,7 +151,7 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
|||||||
// Only resolve as canceled if change event hasn't fired after a delay
|
// Only resolve as canceled if change event hasn't fired after a delay
|
||||||
focusTimeout = setTimeout(() => {
|
focusTimeout = setTimeout(() => {
|
||||||
if (!resolved && !changeEventFired && (!input.files || input.files.length === 0)) {
|
if (!resolved && !changeEventFired && (!input.files || input.files.length === 0)) {
|
||||||
console.log('[FilePicker] Dialog canceled (no files after focus and no change event)');
|
logger.info('Dialog canceled (no files after focus and no change event)');
|
||||||
safeResolve(null);
|
safeResolve(null);
|
||||||
}
|
}
|
||||||
}, 2000); // Increased timeout for Windows - give it time
|
}, 2000); // Increased timeout for Windows - give it time
|
||||||
@@ -155,19 +159,19 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
|||||||
|
|
||||||
// Add to DOM temporarily
|
// Add to DOM temporarily
|
||||||
document.body.appendChild(input);
|
document.body.appendChild(input);
|
||||||
console.log('[FilePicker] Opening directory picker...');
|
logger.info('Opening directory picker...');
|
||||||
|
|
||||||
// Try to show picker programmatically
|
// Try to show picker programmatically
|
||||||
if ('showPicker' in HTMLInputElement.prototype) {
|
if ('showPicker' in HTMLInputElement.prototype) {
|
||||||
try {
|
try {
|
||||||
(input as any).showPicker();
|
(input as any).showPicker();
|
||||||
console.log('[FilePicker] Using showPicker()');
|
logger.info('Using showPicker()');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('[FilePicker] showPicker() failed, using click()', error);
|
logger.info('showPicker() failed, using click()', error);
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('[FilePicker] Using click()');
|
logger.info('Using click()');
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
* but communicates with the backend server via HTTP/WebSocket.
|
* but communicates with the backend server via HTTP/WebSocket.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import type {
|
import type {
|
||||||
ElectronAPI,
|
ElectronAPI,
|
||||||
FileResult,
|
FileResult,
|
||||||
@@ -32,6 +33,8 @@ import type { Feature, ClaudeUsageResponse } from '@/store/app-store';
|
|||||||
import type { WorktreeAPI, GitAPI, ModelDefinition, ProviderStatus } from '@/types/electron';
|
import type { WorktreeAPI, GitAPI, ModelDefinition, ProviderStatus } from '@/types/electron';
|
||||||
import { getGlobalFileBrowser } from '@/contexts/file-browser-context';
|
import { getGlobalFileBrowser } from '@/contexts/file-browser-context';
|
||||||
|
|
||||||
|
const logger = createLogger('HttpClient');
|
||||||
|
|
||||||
// Cached server URL (set during initialization in Electron mode)
|
// Cached server URL (set during initialization in Electron mode)
|
||||||
let cachedServerUrl: string | null = null;
|
let cachedServerUrl: string | null = null;
|
||||||
|
|
||||||
@@ -46,9 +49,9 @@ export const initServerUrl = async (): Promise<void> => {
|
|||||||
if (electron?.getServerUrl) {
|
if (electron?.getServerUrl) {
|
||||||
try {
|
try {
|
||||||
cachedServerUrl = await electron.getServerUrl();
|
cachedServerUrl = await electron.getServerUrl();
|
||||||
console.log('[HTTP Client] Server URL from Electron:', cachedServerUrl);
|
logger.info('Server URL from Electron:', cachedServerUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[HTTP Client] Failed to get server URL from Electron:', error);
|
logger.warn('Failed to get server URL from Electron:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -145,16 +148,16 @@ export const initApiKey = async (): Promise<void> => {
|
|||||||
try {
|
try {
|
||||||
cachedApiKey = await window.electronAPI.getApiKey();
|
cachedApiKey = await window.electronAPI.getApiKey();
|
||||||
if (cachedApiKey) {
|
if (cachedApiKey) {
|
||||||
console.log('[HTTP Client] Using API key from Electron');
|
logger.info('Using API key from Electron');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[HTTP Client] Failed to get API key from Electron:', error);
|
logger.warn('Failed to get API key from Electron:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In web mode, authentication is handled via HTTP-only cookies
|
// In web mode, authentication is handled via HTTP-only cookies
|
||||||
console.log('[HTTP Client] Web mode - using cookie-based authentication');
|
logger.info('Web mode - using cookie-based authentication');
|
||||||
} finally {
|
} finally {
|
||||||
// Mark as initialized after completion, regardless of success or failure
|
// Mark as initialized after completion, regardless of success or failure
|
||||||
apiKeyInitialized = true;
|
apiKeyInitialized = true;
|
||||||
@@ -182,7 +185,7 @@ export const checkAuthStatus = async (): Promise<{
|
|||||||
required: data.required ?? true,
|
required: data.required ?? true,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HTTP Client] Failed to check auth status:', error);
|
logger.error('Failed to check auth status:', error);
|
||||||
return { authenticated: false, required: true };
|
return { authenticated: false, required: true };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -207,23 +210,23 @@ export const login = async (
|
|||||||
// Store the session token if login succeeded
|
// Store the session token if login succeeded
|
||||||
if (data.success && data.token) {
|
if (data.success && data.token) {
|
||||||
setSessionToken(data.token);
|
setSessionToken(data.token);
|
||||||
console.log('[HTTP Client] Session token stored after login');
|
logger.info('Session token stored after login');
|
||||||
|
|
||||||
// Verify the session is actually working by making a request to an authenticated endpoint
|
// Verify the session is actually working by making a request to an authenticated endpoint
|
||||||
const verified = await verifySession();
|
const verified = await verifySession();
|
||||||
if (!verified) {
|
if (!verified) {
|
||||||
console.error('[HTTP Client] Login appeared successful but session verification failed');
|
logger.error('Login appeared successful but session verification failed');
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Session verification failed. Please try again.',
|
error: 'Session verification failed. Please try again.',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
console.log('[HTTP Client] Login verified successfully');
|
logger.info('Login verified successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HTTP Client] Login failed:', error);
|
logger.error('Login failed:', error);
|
||||||
return { success: false, error: 'Network error' };
|
return { success: false, error: 'Network error' };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -243,20 +246,20 @@ export const fetchSessionToken = async (): Promise<boolean> => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.log('[HTTP Client] Failed to check auth status');
|
logger.info('Failed to check auth status');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.success && data.authenticated) {
|
if (data.success && data.authenticated) {
|
||||||
console.log('[HTTP Client] Session cookie is valid');
|
logger.info('Session cookie is valid');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[HTTP Client] Session cookie is not authenticated');
|
logger.info('Session cookie is not authenticated');
|
||||||
return false;
|
return false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HTTP Client] Failed to check session:', error);
|
logger.error('Failed to check session:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -273,11 +276,11 @@ export const logout = async (): Promise<{ success: boolean }> => {
|
|||||||
|
|
||||||
// Clear the cached session token
|
// Clear the cached session token
|
||||||
clearSessionToken();
|
clearSessionToken();
|
||||||
console.log('[HTTP Client] Session token cleared on logout');
|
logger.info('Session token cleared on logout');
|
||||||
|
|
||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HTTP Client] Logout failed:', error);
|
logger.error('Logout failed:', error);
|
||||||
return { success: false };
|
return { success: false };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -310,7 +313,7 @@ export const verifySession = async (): Promise<boolean> => {
|
|||||||
|
|
||||||
// Check for authentication errors
|
// Check for authentication errors
|
||||||
if (response.status === 401 || response.status === 403) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
console.warn('[HTTP Client] Session verification failed - session expired or invalid');
|
logger.warn('Session verification failed - session expired or invalid');
|
||||||
// Clear the session since it's no longer valid
|
// Clear the session since it's no longer valid
|
||||||
clearSessionToken();
|
clearSessionToken();
|
||||||
// Try to clear the cookie via logout (fire and forget)
|
// Try to clear the cookie via logout (fire and forget)
|
||||||
@@ -324,14 +327,14 @@ export const verifySession = async (): Promise<boolean> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.warn('[HTTP Client] Session verification failed with status:', response.status);
|
logger.warn('Session verification failed with status:', response.status);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[HTTP Client] Session verified successfully');
|
logger.info('Session verified successfully');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HTTP Client] Session verification error:', error);
|
logger.error('Session verification error:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -350,14 +353,14 @@ export const checkSandboxEnvironment = async (): Promise<{
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.warn('[HTTP Client] Failed to check sandbox environment');
|
logger.warn('Failed to check sandbox environment');
|
||||||
return { isContainerized: false, error: 'Failed to check environment' };
|
return { isContainerized: false, error: 'Failed to check environment' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return { isContainerized: data.isContainerized ?? false };
|
return { isContainerized: data.isContainerized ?? false };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HTTP Client] Sandbox environment check failed:', error);
|
logger.error('Sandbox environment check failed:', error);
|
||||||
return { isContainerized: false, error: 'Network error' };
|
return { isContainerized: false, error: 'Network error' };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -399,7 +402,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
this.connectWebSocket();
|
this.connectWebSocket();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('[HttpApiClient] API key initialization failed:', error);
|
logger.error('API key initialization failed:', error);
|
||||||
// Still attempt WebSocket connection - it may work with cookie auth
|
// Still attempt WebSocket connection - it may work with cookie auth
|
||||||
this.connectWebSocket();
|
this.connectWebSocket();
|
||||||
});
|
});
|
||||||
@@ -428,7 +431,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.warn('[HttpApiClient] Failed to fetch wsToken:', response.status);
|
logger.warn('Failed to fetch wsToken:', response.status);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,7 +442,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HttpApiClient] Error fetching wsToken:', error);
|
logger.error('Error fetching wsToken:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -456,9 +459,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
if (isElectronMode()) {
|
if (isElectronMode()) {
|
||||||
const apiKey = getApiKey();
|
const apiKey = getApiKey();
|
||||||
if (!apiKey) {
|
if (!apiKey) {
|
||||||
console.warn(
|
logger.warn('Electron mode: API key not ready, delaying WebSocket connect');
|
||||||
'[HttpApiClient] Electron mode: API key not ready, delaying WebSocket connect'
|
|
||||||
);
|
|
||||||
this.isConnecting = false;
|
this.isConnecting = false;
|
||||||
if (!this.reconnectTimer) {
|
if (!this.reconnectTimer) {
|
||||||
this.reconnectTimer = setTimeout(() => {
|
this.reconnectTimer = setTimeout(() => {
|
||||||
@@ -482,12 +483,12 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
this.establishWebSocket(`${wsUrl}?wsToken=${encodeURIComponent(wsToken)}`);
|
this.establishWebSocket(`${wsUrl}?wsToken=${encodeURIComponent(wsToken)}`);
|
||||||
} else {
|
} else {
|
||||||
// Fallback: try connecting without token (will fail if not authenticated)
|
// Fallback: try connecting without token (will fail if not authenticated)
|
||||||
console.warn('[HttpApiClient] No wsToken available, attempting connection anyway');
|
logger.warn('No wsToken available, attempting connection anyway');
|
||||||
this.establishWebSocket(wsUrl);
|
this.establishWebSocket(wsUrl);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('[HttpApiClient] Failed to prepare WebSocket connection:', error);
|
logger.error('Failed to prepare WebSocket connection:', error);
|
||||||
this.isConnecting = false;
|
this.isConnecting = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -500,7 +501,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
this.ws = new WebSocket(wsUrl);
|
this.ws = new WebSocket(wsUrl);
|
||||||
|
|
||||||
this.ws.onopen = () => {
|
this.ws.onopen = () => {
|
||||||
console.log('[HttpApiClient] WebSocket connected');
|
logger.info('WebSocket connected');
|
||||||
this.isConnecting = false;
|
this.isConnecting = false;
|
||||||
if (this.reconnectTimer) {
|
if (this.reconnectTimer) {
|
||||||
clearTimeout(this.reconnectTimer);
|
clearTimeout(this.reconnectTimer);
|
||||||
@@ -511,8 +512,8 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
this.ws.onmessage = (event) => {
|
this.ws.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
console.log(
|
logger.info(
|
||||||
'[HttpApiClient] WebSocket message:',
|
'WebSocket message:',
|
||||||
data.type,
|
data.type,
|
||||||
'hasPayload:',
|
'hasPayload:',
|
||||||
!!data.payload,
|
!!data.payload,
|
||||||
@@ -521,16 +522,16 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
);
|
);
|
||||||
const callbacks = this.eventCallbacks.get(data.type);
|
const callbacks = this.eventCallbacks.get(data.type);
|
||||||
if (callbacks) {
|
if (callbacks) {
|
||||||
console.log('[HttpApiClient] Dispatching to', callbacks.size, 'callbacks');
|
logger.info('Dispatching to', callbacks.size, 'callbacks');
|
||||||
callbacks.forEach((cb) => cb(data.payload));
|
callbacks.forEach((cb) => cb(data.payload));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HttpApiClient] Failed to parse WebSocket message:', error);
|
logger.error('Failed to parse WebSocket message:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onclose = () => {
|
this.ws.onclose = () => {
|
||||||
console.log('[HttpApiClient] WebSocket disconnected');
|
logger.info('WebSocket disconnected');
|
||||||
this.isConnecting = false;
|
this.isConnecting = false;
|
||||||
this.ws = null;
|
this.ws = null;
|
||||||
// Attempt to reconnect after 5 seconds
|
// Attempt to reconnect after 5 seconds
|
||||||
@@ -543,11 +544,11 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onerror = (error) => {
|
this.ws.onerror = (error) => {
|
||||||
console.error('[HttpApiClient] WebSocket error:', error);
|
logger.error('WebSocket error:', error);
|
||||||
this.isConnecting = false;
|
this.isConnecting = false;
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[HttpApiClient] Failed to create WebSocket:', error);
|
logger.error('Failed to create WebSocket:', error);
|
||||||
this.isConnecting = false;
|
this.isConnecting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -747,7 +748,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
const fileBrowser = getGlobalFileBrowser();
|
const fileBrowser = getGlobalFileBrowser();
|
||||||
|
|
||||||
if (!fileBrowser) {
|
if (!fileBrowser) {
|
||||||
console.error('File browser not initialized');
|
logger.error('File browser not initialized');
|
||||||
return { canceled: true, filePaths: [] };
|
return { canceled: true, filePaths: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,7 +770,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
return { canceled: false, filePaths: [result.path] };
|
return { canceled: false, filePaths: [result.path] };
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error('Invalid directory:', result.error || 'Path not allowed');
|
logger.error('Invalid directory:', result.error || 'Path not allowed');
|
||||||
return { canceled: true, filePaths: [] };
|
return { canceled: true, filePaths: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,7 +778,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
const fileBrowser = getGlobalFileBrowser();
|
const fileBrowser = getGlobalFileBrowser();
|
||||||
|
|
||||||
if (!fileBrowser) {
|
if (!fileBrowser) {
|
||||||
console.error('File browser not initialized');
|
logger.error('File browser not initialized');
|
||||||
return { canceled: true, filePaths: [] };
|
return { canceled: true, filePaths: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,7 +797,7 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
return { canceled: false, filePaths: [path] };
|
return { canceled: false, filePaths: [path] };
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error('File not found');
|
logger.error('File not found');
|
||||||
return { canceled: true, filePaths: [] };
|
return { canceled: true, filePaths: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1910,5 +1911,5 @@ export function getHttpApiClient(): HttpApiClient {
|
|||||||
// This ensures the init promise is created early, even before React components mount
|
// This ensures the init promise is created early, even before React components mount
|
||||||
// The actual async work happens in the background and won't block module loading
|
// The actual async work happens in the background and won't block module loading
|
||||||
initApiKey().catch((error) => {
|
initApiKey().catch((error) => {
|
||||||
console.error('[HTTP Client] Failed to initialize API key:', error);
|
logger.error('Failed to initialize API key:', error);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
* new or existing projects.
|
* new or existing projects.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getElectronAPI } from './electron';
|
import { getElectronAPI } from './electron';
|
||||||
|
|
||||||
|
const logger = createLogger('ProjectInit');
|
||||||
|
|
||||||
export interface ProjectInitResult {
|
export interface ProjectInitResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
isNewProject: boolean;
|
isNewProject: boolean;
|
||||||
@@ -72,22 +75,22 @@ export async function initializeProject(projectPath: string): Promise<ProjectIni
|
|||||||
// Initialize git repository if it doesn't exist
|
// Initialize git repository if it doesn't exist
|
||||||
const gitDirExists = await api.exists(`${projectPath}/.git`);
|
const gitDirExists = await api.exists(`${projectPath}/.git`);
|
||||||
if (!gitDirExists) {
|
if (!gitDirExists) {
|
||||||
console.log('[project-init] Initializing git repository...');
|
logger.info('Initializing git repository...');
|
||||||
try {
|
try {
|
||||||
// Initialize git and create an initial empty commit via server route
|
// Initialize git and create an initial empty commit via server route
|
||||||
const result = await api.worktree?.initGit(projectPath);
|
const result = await api.worktree?.initGit(projectPath);
|
||||||
if (result?.success && result.result?.initialized) {
|
if (result?.success && result.result?.initialized) {
|
||||||
createdFiles.push('.git');
|
createdFiles.push('.git');
|
||||||
console.log('[project-init] Git repository initialized with initial commit');
|
logger.info('Git repository initialized with initial commit');
|
||||||
} else if (result?.success && !result.result?.initialized) {
|
} else if (result?.success && !result.result?.initialized) {
|
||||||
// Git already existed (shouldn't happen since we checked, but handle it)
|
// Git already existed (shouldn't happen since we checked, but handle it)
|
||||||
existingFiles.push('.git');
|
existingFiles.push('.git');
|
||||||
console.log('[project-init] Git repository already exists');
|
logger.info('Git repository already exists');
|
||||||
} else {
|
} else {
|
||||||
console.warn('[project-init] Failed to initialize git repository:', result?.error);
|
logger.warn('Failed to initialize git repository:', result?.error);
|
||||||
}
|
}
|
||||||
} catch (gitError) {
|
} catch (gitError) {
|
||||||
console.warn('[project-init] Failed to initialize git repository:', gitError);
|
logger.warn('Failed to initialize git repository:', gitError);
|
||||||
// Don't fail the whole initialization if git init fails
|
// Don't fail the whole initialization if git init fails
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -123,7 +126,7 @@ export async function initializeProject(projectPath: string): Promise<ProjectIni
|
|||||||
existingFiles,
|
existingFiles,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[project-init] Failed to initialize project:', error);
|
logger.error('Failed to initialize project:', error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
isNewProject: false,
|
isNewProject: false,
|
||||||
@@ -153,7 +156,7 @@ export async function isProjectInitialized(projectPath: string): Promise<boolean
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[project-init] Error checking project initialization:', error);
|
logger.error('Error checking project initialization:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +194,7 @@ export async function getProjectInitStatus(projectPath: string): Promise<{
|
|||||||
existingFiles,
|
existingFiles,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[project-init] Error getting project status:', error);
|
logger.error('Error getting project status:', error);
|
||||||
return {
|
return {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
missingFiles: REQUIRED_STRUCTURE.directories,
|
missingFiles: REQUIRED_STRUCTURE.directories,
|
||||||
@@ -212,7 +215,7 @@ export async function hasAppSpec(projectPath: string): Promise<boolean> {
|
|||||||
const fullPath = `${projectPath}/.automaker/app_spec.txt`;
|
const fullPath = `${projectPath}/.automaker/app_spec.txt`;
|
||||||
return await api.exists(fullPath);
|
return await api.exists(fullPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[project-init] Error checking app_spec.txt:', error);
|
logger.error('Error checking app_spec.txt:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,7 +232,7 @@ export async function hasAutomakerDir(projectPath: string): Promise<boolean> {
|
|||||||
const fullPath = `${projectPath}/.automaker`;
|
const fullPath = `${projectPath}/.automaker`;
|
||||||
return await api.exists(fullPath);
|
return await api.exists(fullPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[project-init] Error checking .automaker dir:', error);
|
logger.error('Error checking .automaker dir:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
* Centralizes the logic for determining where projects should be created/opened
|
* Centralizes the logic for determining where projects should be created/opened
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { getHttpApiClient } from './http-api-client';
|
import { getHttpApiClient } from './http-api-client';
|
||||||
import { getElectronAPI } from './electron';
|
import { getElectronAPI } from './electron';
|
||||||
import { getItem, setItem } from './storage';
|
import { getItem, setItem } from './storage';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
const logger = createLogger('WorkspaceConfig');
|
||||||
|
|
||||||
const LAST_PROJECT_DIR_KEY = 'automaker:lastProjectDir';
|
const LAST_PROJECT_DIR_KEY = 'automaker:lastProjectDir';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,9 +23,7 @@ async function getDefaultDocumentsPath(): Promise<string | null> {
|
|||||||
const documentsPath = await api.getPath('documents');
|
const documentsPath = await api.getPath('documents');
|
||||||
return path.join(documentsPath, 'Automaker');
|
return path.join(documentsPath, 'Automaker');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (typeof window !== 'undefined' && window.console) {
|
logger.error('Failed to get documents path:', error);
|
||||||
window.console.error('Failed to get documents path:', error);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,9 +82,7 @@ export async function getDefaultWorkspaceDirectory(): Promise<string | null> {
|
|||||||
const documentsPath = await getDefaultDocumentsPath();
|
const documentsPath = await getDefaultDocumentsPath();
|
||||||
return documentsPath;
|
return documentsPath;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (typeof window !== 'undefined' && window.console) {
|
logger.error('Failed to get default workspace directory:', error);
|
||||||
window.console.error('Failed to get default workspace directory:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// On error, try last used dir and Documents
|
// On error, try last used dir and Documents
|
||||||
const lastUsedDir = getItem(LAST_PROJECT_DIR_KEY);
|
const lastUsedDir = getItem(LAST_PROJECT_DIR_KEY);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import crypto from 'crypto';
|
|||||||
import http, { Server } from 'http';
|
import http, { Server } from 'http';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import { app, BrowserWindow, ipcMain, dialog, shell, screen } from 'electron';
|
import { app, BrowserWindow, ipcMain, dialog, shell, screen } from 'electron';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import {
|
import {
|
||||||
findNodeExecutable,
|
findNodeExecutable,
|
||||||
buildEnhancedPath,
|
buildEnhancedPath,
|
||||||
@@ -35,6 +36,9 @@ import {
|
|||||||
systemPathExists,
|
systemPathExists,
|
||||||
} from '@automaker/platform';
|
} from '@automaker/platform';
|
||||||
|
|
||||||
|
const logger = createLogger('Electron');
|
||||||
|
const serverLogger = createLogger('Server');
|
||||||
|
|
||||||
// Development environment
|
// Development environment
|
||||||
const isDev = !app.isPackaged;
|
const isDev = !app.isPackaged;
|
||||||
const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL;
|
const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL;
|
||||||
@@ -45,7 +49,7 @@ if (isDev) {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[Electron] dotenv not available:', (error as Error).message);
|
logger.warn('dotenv not available:', (error as Error).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,21 +148,21 @@ function ensureApiKey(): string {
|
|||||||
const key = electronUserDataReadFileSync(API_KEY_FILENAME).trim();
|
const key = electronUserDataReadFileSync(API_KEY_FILENAME).trim();
|
||||||
if (key) {
|
if (key) {
|
||||||
apiKey = key;
|
apiKey = key;
|
||||||
console.log('[Electron] Loaded existing API key');
|
logger.info('Loaded existing API key');
|
||||||
return apiKey;
|
return apiKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[Electron] Error reading API key:', error);
|
logger.warn('Error reading API key:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new key
|
// Generate new key
|
||||||
apiKey = crypto.randomUUID();
|
apiKey = crypto.randomUUID();
|
||||||
try {
|
try {
|
||||||
electronUserDataWriteFileSync(API_KEY_FILENAME, apiKey, { encoding: 'utf-8', mode: 0o600 });
|
electronUserDataWriteFileSync(API_KEY_FILENAME, apiKey, { encoding: 'utf-8', mode: 0o600 });
|
||||||
console.log('[Electron] Generated new API key');
|
logger.info('Generated new API key');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Electron] Failed to save API key:', error);
|
logger.error('Failed to save API key:', error);
|
||||||
}
|
}
|
||||||
return apiKey;
|
return apiKey;
|
||||||
}
|
}
|
||||||
@@ -183,11 +187,11 @@ function getIconPath(): string | null {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (!electronAppExists(iconPath)) {
|
if (!electronAppExists(iconPath)) {
|
||||||
console.warn(`[Electron] Icon not found at: ${iconPath}`);
|
logger.warn('Icon not found at:', iconPath);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`[Electron] Icon check failed: ${iconPath}`, error);
|
logger.warn('Icon check failed:', iconPath, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +223,7 @@ function loadWindowBounds(): WindowBounds | null {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[Electron] Failed to load window bounds:', (error as Error).message);
|
logger.warn('Failed to load window bounds:', (error as Error).message);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -231,9 +235,9 @@ function loadWindowBounds(): WindowBounds | null {
|
|||||||
function saveWindowBounds(bounds: WindowBounds): void {
|
function saveWindowBounds(bounds: WindowBounds): void {
|
||||||
try {
|
try {
|
||||||
electronUserDataWriteFileSync(WINDOW_BOUNDS_FILENAME, JSON.stringify(bounds, null, 2));
|
electronUserDataWriteFileSync(WINDOW_BOUNDS_FILENAME, JSON.stringify(bounds, null, 2));
|
||||||
console.log('[Electron] Window bounds saved');
|
logger.info('Window bounds saved');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[Electron] Failed to save window bounds:', (error as Error).message);
|
logger.warn('Failed to save window bounds:', (error as Error).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +375,7 @@ async function startStaticServer(): Promise<void> {
|
|||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
staticServer!.listen(staticPort, () => {
|
staticServer!.listen(staticPort, () => {
|
||||||
console.log(`[Electron] Static server running at http://localhost:${staticPort}`);
|
logger.info('Static server running at http://localhost:' + staticPort);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
staticServer!.on('error', reject);
|
staticServer!.on('error', reject);
|
||||||
@@ -386,7 +390,7 @@ async function startServer(): Promise<void> {
|
|||||||
// Find Node.js executable (handles desktop launcher scenarios)
|
// Find Node.js executable (handles desktop launcher scenarios)
|
||||||
const nodeResult = findNodeExecutable({
|
const nodeResult = findNodeExecutable({
|
||||||
skipSearch: isDev,
|
skipSearch: isDev,
|
||||||
logger: (msg: string) => console.log(`[Electron] ${msg}`),
|
logger: (msg: string) => logger.info(msg),
|
||||||
});
|
});
|
||||||
const command = nodeResult.nodePath;
|
const command = nodeResult.nodePath;
|
||||||
|
|
||||||
@@ -470,7 +474,7 @@ async function startServer(): Promise<void> {
|
|||||||
// Build enhanced PATH that includes Node.js directory (cross-platform)
|
// Build enhanced PATH that includes Node.js directory (cross-platform)
|
||||||
const enhancedPath = buildEnhancedPath(command, process.env.PATH || '');
|
const enhancedPath = buildEnhancedPath(command, process.env.PATH || '');
|
||||||
if (enhancedPath !== process.env.PATH) {
|
if (enhancedPath !== process.env.PATH) {
|
||||||
console.log(`[Electron] Enhanced PATH with Node directory: ${path.dirname(command)}`);
|
logger.info('Enhanced PATH with Node directory:', path.dirname(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
const env = {
|
const env = {
|
||||||
@@ -488,12 +492,12 @@ async function startServer(): Promise<void> {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(`[Electron] Server will use port ${serverPort}`);
|
logger.info('Server will use port', serverPort);
|
||||||
|
|
||||||
console.log('[Electron] Starting backend server...');
|
logger.info('Starting backend server...');
|
||||||
console.log('[Electron] Server path:', serverPath);
|
logger.info('Server path:', serverPath);
|
||||||
console.log('[Electron] Server root (cwd):', serverRoot);
|
logger.info('Server root (cwd):', serverRoot);
|
||||||
console.log('[Electron] NODE_PATH:', serverNodeModules);
|
logger.info('NODE_PATH:', serverNodeModules);
|
||||||
|
|
||||||
serverProcess = spawn(command, args, {
|
serverProcess = spawn(command, args, {
|
||||||
cwd: serverRoot,
|
cwd: serverRoot,
|
||||||
@@ -502,20 +506,20 @@ async function startServer(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.stdout?.on('data', (data) => {
|
serverProcess.stdout?.on('data', (data) => {
|
||||||
console.log(`[Server] ${data.toString().trim()}`);
|
serverLogger.info(data.toString().trim());
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.stderr?.on('data', (data) => {
|
serverProcess.stderr?.on('data', (data) => {
|
||||||
console.error(`[Server Error] ${data.toString().trim()}`);
|
serverLogger.error(data.toString().trim());
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.on('close', (code) => {
|
serverProcess.on('close', (code) => {
|
||||||
console.log(`[Server] Process exited with code ${code}`);
|
serverLogger.info('Process exited with code', code);
|
||||||
serverProcess = null;
|
serverProcess = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.on('error', (err) => {
|
serverProcess.on('error', (err) => {
|
||||||
console.error(`[Server] Failed to start server process:`, err);
|
serverLogger.error('Failed to start server process:', err);
|
||||||
serverProcess = null;
|
serverProcess = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -542,7 +546,7 @@ async function waitForServer(maxAttempts = 30): Promise<void> {
|
|||||||
reject(new Error('Timeout'));
|
reject(new Error('Timeout'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
console.log('[Electron] Server is ready');
|
logger.info('Server is ready');
|
||||||
return;
|
return;
|
||||||
} catch {
|
} catch {
|
||||||
await new Promise((r) => setTimeout(r, 500));
|
await new Promise((r) => setTimeout(r, 500));
|
||||||
@@ -645,10 +649,10 @@ app.whenReady().then(async () => {
|
|||||||
const desiredUserDataPath = path.join(app.getPath('appData'), 'Automaker');
|
const desiredUserDataPath = path.join(app.getPath('appData'), 'Automaker');
|
||||||
if (app.getPath('userData') !== desiredUserDataPath) {
|
if (app.getPath('userData') !== desiredUserDataPath) {
|
||||||
app.setPath('userData', desiredUserDataPath);
|
app.setPath('userData', desiredUserDataPath);
|
||||||
console.log('[Electron] userData path set to:', desiredUserDataPath);
|
logger.info('userData path set to:', desiredUserDataPath);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[Electron] Failed to set userData path:', (error as Error).message);
|
logger.warn('Failed to set userData path:', (error as Error).message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize centralized path helpers for Electron
|
// Initialize centralized path helpers for Electron
|
||||||
@@ -664,7 +668,7 @@ app.whenReady().then(async () => {
|
|||||||
} else {
|
} else {
|
||||||
setElectronAppPaths(__dirname, process.resourcesPath);
|
setElectronAppPaths(__dirname, process.resourcesPath);
|
||||||
}
|
}
|
||||||
console.log('[Electron] Initialized path security helpers');
|
logger.info('Initialized path security helpers');
|
||||||
|
|
||||||
// Initialize security settings for path validation
|
// Initialize security settings for path validation
|
||||||
// Set DATA_DIR before initializing so it's available for security checks
|
// Set DATA_DIR before initializing so it's available for security checks
|
||||||
@@ -679,7 +683,7 @@ app.whenReady().then(async () => {
|
|||||||
try {
|
try {
|
||||||
app.dock.setIcon(iconPath);
|
app.dock.setIcon(iconPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[Electron] Failed to set dock icon:', (error as Error).message);
|
logger.warn('Failed to set dock icon:', (error as Error).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -691,16 +695,12 @@ app.whenReady().then(async () => {
|
|||||||
// Find available ports (prevents conflicts with other apps using same ports)
|
// Find available ports (prevents conflicts with other apps using same ports)
|
||||||
serverPort = await findAvailablePort(DEFAULT_SERVER_PORT);
|
serverPort = await findAvailablePort(DEFAULT_SERVER_PORT);
|
||||||
if (serverPort !== DEFAULT_SERVER_PORT) {
|
if (serverPort !== DEFAULT_SERVER_PORT) {
|
||||||
console.log(
|
logger.info('Default server port', DEFAULT_SERVER_PORT, 'in use, using port', serverPort);
|
||||||
`[Electron] Default server port ${DEFAULT_SERVER_PORT} in use, using port ${serverPort}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
staticPort = await findAvailablePort(DEFAULT_STATIC_PORT);
|
staticPort = await findAvailablePort(DEFAULT_STATIC_PORT);
|
||||||
if (staticPort !== DEFAULT_STATIC_PORT) {
|
if (staticPort !== DEFAULT_STATIC_PORT) {
|
||||||
console.log(
|
logger.info('Default static port', DEFAULT_STATIC_PORT, 'in use, using port', staticPort);
|
||||||
`[Electron] Default static port ${DEFAULT_STATIC_PORT} in use, using port ${staticPort}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start static file server in production
|
// Start static file server in production
|
||||||
@@ -714,7 +714,7 @@ app.whenReady().then(async () => {
|
|||||||
// Create window
|
// Create window
|
||||||
createWindow();
|
createWindow();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Electron] Failed to start:', error);
|
logger.error('Failed to start:', error);
|
||||||
const errorMessage = (error as Error).message;
|
const errorMessage = (error as Error).message;
|
||||||
const isNodeError = errorMessage.includes('Node.js');
|
const isNodeError = errorMessage.includes('Node.js');
|
||||||
dialog.showErrorBox(
|
dialog.showErrorBox(
|
||||||
@@ -740,12 +740,12 @@ app.on('window-all-closed', () => {
|
|||||||
// (standard macOS behavior). On other platforms, stop servers and quit.
|
// (standard macOS behavior). On other platforms, stop servers and quit.
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
if (serverProcess && serverProcess.pid) {
|
if (serverProcess && serverProcess.pid) {
|
||||||
console.log('[Electron] All windows closed, stopping server...');
|
logger.info('All windows closed, stopping server...');
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
try {
|
try {
|
||||||
execSync(`taskkill /f /t /pid ${serverProcess.pid}`, { stdio: 'ignore' });
|
execSync(`taskkill /f /t /pid ${serverProcess.pid}`, { stdio: 'ignore' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Electron] Failed to kill server process:', (error as Error).message);
|
logger.error('Failed to kill server process:', (error as Error).message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
serverProcess.kill('SIGTERM');
|
serverProcess.kill('SIGTERM');
|
||||||
@@ -754,7 +754,7 @@ app.on('window-all-closed', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (staticServer) {
|
if (staticServer) {
|
||||||
console.log('[Electron] Stopping static server...');
|
logger.info('Stopping static server...');
|
||||||
staticServer.close();
|
staticServer.close();
|
||||||
staticServer = null;
|
staticServer = null;
|
||||||
}
|
}
|
||||||
@@ -765,7 +765,7 @@ app.on('window-all-closed', () => {
|
|||||||
|
|
||||||
app.on('before-quit', () => {
|
app.on('before-quit', () => {
|
||||||
if (serverProcess && serverProcess.pid) {
|
if (serverProcess && serverProcess.pid) {
|
||||||
console.log('[Electron] Stopping server...');
|
logger.info('Stopping server...');
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
try {
|
try {
|
||||||
// Windows: use taskkill with /t to kill entire process tree
|
// Windows: use taskkill with /t to kill entire process tree
|
||||||
@@ -773,7 +773,7 @@ app.on('before-quit', () => {
|
|||||||
// Using execSync to ensure process is killed before app exits
|
// Using execSync to ensure process is killed before app exits
|
||||||
execSync(`taskkill /f /t /pid ${serverProcess.pid}`, { stdio: 'ignore' });
|
execSync(`taskkill /f /t /pid ${serverProcess.pid}`, { stdio: 'ignore' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Electron] Failed to kill server process:', (error as Error).message);
|
logger.error('Failed to kill server process:', (error as Error).message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
serverProcess.kill('SIGTERM');
|
serverProcess.kill('SIGTERM');
|
||||||
@@ -782,7 +782,7 @@ app.on('before-quit', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (staticServer) {
|
if (staticServer) {
|
||||||
console.log('[Electron] Stopping static server...');
|
logger.info('Stopping static server...');
|
||||||
staticServer.close();
|
staticServer.close();
|
||||||
staticServer = null;
|
staticServer = null;
|
||||||
}
|
}
|
||||||
@@ -924,6 +924,6 @@ ipcMain.handle('window:updateMinWidth', (_, _sidebarExpanded: boolean) => {
|
|||||||
|
|
||||||
// Quit the application (used when user denies sandbox risk confirmation)
|
// Quit the application (used when user denies sandbox risk confirmation)
|
||||||
ipcMain.handle('app:quit', () => {
|
ipcMain.handle('app:quit', () => {
|
||||||
console.log('[Electron] Quitting application via IPC request');
|
logger.info('Quitting application via IPC request');
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { contextBridge, ipcRenderer, OpenDialogOptions, SaveDialogOptions } from 'electron';
|
import { contextBridge, ipcRenderer, OpenDialogOptions, SaveDialogOptions } from 'electron';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
|
|
||||||
|
const logger = createLogger('Preload');
|
||||||
|
|
||||||
// Expose minimal API for native features
|
// Expose minimal API for native features
|
||||||
contextBridge.exposeInMainWorld('electronAPI', {
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
@@ -55,4 +58,4 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
quit: (): Promise<void> => ipcRenderer.invoke('app:quit'),
|
quit: (): Promise<void> => ipcRenderer.invoke('app:quit'),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('[Preload] Electron API exposed (TypeScript)');
|
logger.info('Electron API exposed (TypeScript)');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createRootRoute, Outlet, useLocation, useNavigate } from '@tanstack/react-router';
|
import { createRootRoute, Outlet, useLocation, useNavigate } from '@tanstack/react-router';
|
||||||
import { useEffect, useState, useCallback, useDeferredValue, useRef } from 'react';
|
import { useEffect, useState, useCallback, useDeferredValue, useRef } from 'react';
|
||||||
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { Sidebar } from '@/components/layout/sidebar';
|
import { Sidebar } from '@/components/layout/sidebar';
|
||||||
import {
|
import {
|
||||||
FileBrowserProvider,
|
FileBrowserProvider,
|
||||||
@@ -23,6 +24,8 @@ import { ThemeOption, themeOptions } from '@/config/theme-options';
|
|||||||
import { SandboxRiskDialog } from '@/components/dialogs/sandbox-risk-dialog';
|
import { SandboxRiskDialog } from '@/components/dialogs/sandbox-risk-dialog';
|
||||||
import { SandboxRejectionScreen } from '@/components/dialogs/sandbox-rejection-screen';
|
import { SandboxRejectionScreen } from '@/components/dialogs/sandbox-rejection-screen';
|
||||||
|
|
||||||
|
const logger = createLogger('RootLayout');
|
||||||
|
|
||||||
function RootLayoutContent() {
|
function RootLayoutContent() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const {
|
const {
|
||||||
@@ -120,7 +123,7 @@ function RootLayoutContent() {
|
|||||||
setSandboxStatus('needs-confirmation');
|
setSandboxStatus('needs-confirmation');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Sandbox] Failed to check environment:', error);
|
logger.error('Failed to check environment:', error);
|
||||||
// On error, assume not containerized and show warning
|
// On error, assume not containerized and show warning
|
||||||
if (skipSandboxWarning) {
|
if (skipSandboxWarning) {
|
||||||
setSandboxStatus('confirmed');
|
setSandboxStatus('confirmed');
|
||||||
@@ -154,10 +157,10 @@ function RootLayoutContent() {
|
|||||||
if (electronAPI?.quit) {
|
if (electronAPI?.quit) {
|
||||||
await electronAPI.quit();
|
await electronAPI.quit();
|
||||||
} else {
|
} else {
|
||||||
console.error('[Sandbox] quit() not available on electronAPI');
|
logger.error('quit() not available on electronAPI');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Sandbox] Failed to quit app:', error);
|
logger.error('Failed to quit app:', error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// In web mode, show rejection screen
|
// In web mode, show rejection screen
|
||||||
@@ -202,7 +205,7 @@ function RootLayoutContent() {
|
|||||||
// Session is invalid or expired - treat as not authenticated
|
// Session is invalid or expired - treat as not authenticated
|
||||||
useAuthStore.getState().setAuthState({ isAuthenticated: false, authChecked: true });
|
useAuthStore.getState().setAuthState({ isAuthenticated: false, authChecked: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to initialize auth:', error);
|
logger.error('Failed to initialize auth:', error);
|
||||||
// On error, treat as not authenticated
|
// On error, treat as not authenticated
|
||||||
useAuthStore.getState().setAuthState({ isAuthenticated: false, authChecked: true });
|
useAuthStore.getState().setAuthState({ isAuthenticated: false, authChecked: true });
|
||||||
} finally {
|
} finally {
|
||||||
@@ -282,7 +285,7 @@ function RootLayoutContent() {
|
|||||||
});
|
});
|
||||||
setIpcConnected(response.ok);
|
setIpcConnected(response.ok);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('IPC connection failed:', error);
|
logger.error('IPC connection failed:', error);
|
||||||
setIpcConnected(false);
|
setIpcConnected(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ export default defineConfig(({ command }) => {
|
|||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['child_process', 'fs', 'path', 'crypto', 'http', 'net', 'os', 'util', 'stream', 'events', 'readline'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
exclude: ['@automaker/platform'],
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
__APP_VERSION__: JSON.stringify(appVersion),
|
__APP_VERSION__: JSON.stringify(appVersion),
|
||||||
|
|||||||
@@ -5,6 +5,16 @@
|
|||||||
"description": "Shared utility functions for AutoMaker",
|
"description": "Shared utility functions for AutoMaker",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./logger": {
|
||||||
|
"types": "./dist/logger.d.ts",
|
||||||
|
"default": "./dist/logger.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"watch": "tsc --watch",
|
"watch": "tsc --watch",
|
||||||
|
|||||||
@@ -40,7 +40,15 @@ export {
|
|||||||
} from './prompt-builder.js';
|
} from './prompt-builder.js';
|
||||||
|
|
||||||
// Logger
|
// Logger
|
||||||
export { createLogger, getLogLevel, setLogLevel, LogLevel } from './logger.js';
|
export {
|
||||||
|
createLogger,
|
||||||
|
getLogLevel,
|
||||||
|
setLogLevel,
|
||||||
|
setColorsEnabled,
|
||||||
|
setTimestampsEnabled,
|
||||||
|
LogLevel,
|
||||||
|
type Logger,
|
||||||
|
} from './logger.js';
|
||||||
|
|
||||||
// File system utilities
|
// File system utilities
|
||||||
export { mkdirSafe, existsSafe } from './fs-utils.js';
|
export { mkdirSafe, existsSafe } from './fs-utils.js';
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* Simple logger utility with log levels
|
* Enhanced logger with colors and timestamps
|
||||||
* Configure via LOG_LEVEL environment variable: error, warn, info, debug
|
*
|
||||||
* Defaults to 'info' if not set
|
* Environment Variables:
|
||||||
|
* - LOG_LEVEL: error, warn, info, debug (default: info)
|
||||||
|
* - LOG_COLORS: true/false (default: auto-detect TTY)
|
||||||
|
* - LOG_TIMESTAMPS: true/false (default: false)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum LogLevel {
|
export enum LogLevel {
|
||||||
@@ -18,42 +21,207 @@ const LOG_LEVEL_NAMES: Record<string, LogLevel> = {
|
|||||||
debug: LogLevel.DEBUG,
|
debug: LogLevel.DEBUG,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ANSI color codes for terminal output
|
||||||
|
const ANSI = {
|
||||||
|
reset: '\x1b[0m',
|
||||||
|
bold: '\x1b[1m',
|
||||||
|
dim: '\x1b[2m',
|
||||||
|
// Foreground colors
|
||||||
|
red: '\x1b[31m',
|
||||||
|
green: '\x1b[32m',
|
||||||
|
yellow: '\x1b[33m',
|
||||||
|
blue: '\x1b[34m',
|
||||||
|
magenta: '\x1b[35m',
|
||||||
|
cyan: '\x1b[36m',
|
||||||
|
white: '\x1b[37m',
|
||||||
|
gray: '\x1b[90m',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Browser CSS styles for console output
|
||||||
|
const BROWSER_STYLES = {
|
||||||
|
timestamp: 'color: #6b7280; font-size: 11px;',
|
||||||
|
context: 'color: #3b82f6; font-weight: 600;',
|
||||||
|
reset: 'color: inherit; font-weight: inherit;',
|
||||||
|
levels: {
|
||||||
|
ERROR:
|
||||||
|
'background: #ef4444; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
||||||
|
WARN: 'background: #f59e0b; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
||||||
|
INFO: 'background: #3b82f6; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
||||||
|
DEBUG:
|
||||||
|
'background: #8b5cf6; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Environment detection - use globalThis for cross-platform compatibility
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const isBrowser = typeof (globalThis as any).window !== 'undefined';
|
||||||
|
|
||||||
|
// Configuration state
|
||||||
let currentLogLevel: LogLevel = LogLevel.INFO;
|
let currentLogLevel: LogLevel = LogLevel.INFO;
|
||||||
|
|
||||||
|
// Detect if we're in a Node.js environment with TTY support
|
||||||
|
function isTTY(): boolean {
|
||||||
|
if (isBrowser) return false;
|
||||||
|
try {
|
||||||
|
return process?.stdout?.isTTY ?? false;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get environment variable safely (works in both Node.js and browser)
|
||||||
|
function getEnvVar(name: string): string | undefined {
|
||||||
|
if (isBrowser) return undefined;
|
||||||
|
try {
|
||||||
|
return process?.env?.[name];
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize configuration from environment variables
|
||||||
|
let colorsEnabled = isTTY() && getEnvVar('LOG_COLORS') !== 'false';
|
||||||
|
let timestampsEnabled = getEnvVar('LOG_TIMESTAMPS') === 'true';
|
||||||
|
|
||||||
// Initialize log level from environment variable
|
// Initialize log level from environment variable
|
||||||
const envLogLevel = process.env.LOG_LEVEL?.toLowerCase();
|
const envLogLevel = getEnvVar('LOG_LEVEL')?.toLowerCase();
|
||||||
if (envLogLevel && LOG_LEVEL_NAMES[envLogLevel] !== undefined) {
|
if (envLogLevel && LOG_LEVEL_NAMES[envLogLevel] !== undefined) {
|
||||||
currentLogLevel = LOG_LEVEL_NAMES[envLogLevel];
|
currentLogLevel = LOG_LEVEL_NAMES[envLogLevel];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format ISO timestamp
|
||||||
|
*/
|
||||||
|
function formatTimestamp(): string {
|
||||||
|
return new Date().toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format short time for browser (HH:mm:ss.SSS)
|
||||||
|
*/
|
||||||
|
function formatShortTime(): string {
|
||||||
|
return new Date().toISOString().split('T')[1].slice(0, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a log line for Node.js terminal output
|
||||||
|
*/
|
||||||
|
function formatNodeLog(level: string, context: string, levelColor: string): string {
|
||||||
|
const parts: string[] = [];
|
||||||
|
|
||||||
|
if (timestampsEnabled) {
|
||||||
|
parts.push(colorsEnabled ? `${ANSI.gray}${formatTimestamp()}${ANSI.reset}` : formatTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
const levelPadded = level.padEnd(5);
|
||||||
|
parts.push(colorsEnabled ? `${levelColor}${levelPadded}${ANSI.reset}` : levelPadded);
|
||||||
|
parts.push(colorsEnabled ? `${ANSI.blue}[${context}]${ANSI.reset}` : `[${context}]`);
|
||||||
|
|
||||||
|
return parts.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger interface returned by createLogger
|
||||||
|
*/
|
||||||
|
export interface Logger {
|
||||||
|
error: (...args: unknown[]) => void;
|
||||||
|
warn: (...args: unknown[]) => void;
|
||||||
|
info: (...args: unknown[]) => void;
|
||||||
|
debug: (...args: unknown[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a logger instance with a context prefix
|
* Create a logger instance with a context prefix
|
||||||
*/
|
*/
|
||||||
export function createLogger(context: string) {
|
export function createLogger(context: string): Logger {
|
||||||
const prefix = `[${context}]`;
|
if (isBrowser) {
|
||||||
|
// Browser implementation with CSS styling
|
||||||
|
return {
|
||||||
|
error: (...args: unknown[]): void => {
|
||||||
|
if (currentLogLevel >= LogLevel.ERROR) {
|
||||||
|
console.error(
|
||||||
|
`%cERROR%c %c${formatShortTime()}%c %c[${context}]%c`,
|
||||||
|
BROWSER_STYLES.levels.ERROR,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
BROWSER_STYLES.timestamp,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
BROWSER_STYLES.context,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
warn: (...args: unknown[]): void => {
|
||||||
|
if (currentLogLevel >= LogLevel.WARN) {
|
||||||
|
console.warn(
|
||||||
|
`%cWARN%c %c${formatShortTime()}%c %c[${context}]%c`,
|
||||||
|
BROWSER_STYLES.levels.WARN,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
BROWSER_STYLES.timestamp,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
BROWSER_STYLES.context,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
info: (...args: unknown[]): void => {
|
||||||
|
if (currentLogLevel >= LogLevel.INFO) {
|
||||||
|
console.log(
|
||||||
|
`%cINFO%c %c${formatShortTime()}%c %c[${context}]%c`,
|
||||||
|
BROWSER_STYLES.levels.INFO,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
BROWSER_STYLES.timestamp,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
BROWSER_STYLES.context,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
debug: (...args: unknown[]): void => {
|
||||||
|
if (currentLogLevel >= LogLevel.DEBUG) {
|
||||||
|
console.log(
|
||||||
|
`%cDEBUG%c %c${formatShortTime()}%c %c[${context}]%c`,
|
||||||
|
BROWSER_STYLES.levels.DEBUG,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
BROWSER_STYLES.timestamp,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
BROWSER_STYLES.context,
|
||||||
|
BROWSER_STYLES.reset,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node.js implementation with ANSI colors
|
||||||
return {
|
return {
|
||||||
error: (...args: unknown[]): void => {
|
error: (...args: unknown[]): void => {
|
||||||
if (currentLogLevel >= LogLevel.ERROR) {
|
if (currentLogLevel >= LogLevel.ERROR) {
|
||||||
console.error(prefix, ...args);
|
console.error(formatNodeLog('ERROR', context, ANSI.red), ...args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
warn: (...args: unknown[]): void => {
|
warn: (...args: unknown[]): void => {
|
||||||
if (currentLogLevel >= LogLevel.WARN) {
|
if (currentLogLevel >= LogLevel.WARN) {
|
||||||
console.warn(prefix, ...args);
|
console.warn(formatNodeLog('WARN', context, ANSI.yellow), ...args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
info: (...args: unknown[]): void => {
|
info: (...args: unknown[]): void => {
|
||||||
if (currentLogLevel >= LogLevel.INFO) {
|
if (currentLogLevel >= LogLevel.INFO) {
|
||||||
console.log(prefix, ...args);
|
console.log(formatNodeLog('INFO', context, ANSI.cyan), ...args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
debug: (...args: unknown[]): void => {
|
debug: (...args: unknown[]): void => {
|
||||||
if (currentLogLevel >= LogLevel.DEBUG) {
|
if (currentLogLevel >= LogLevel.DEBUG) {
|
||||||
console.log(prefix, '[DEBUG]', ...args);
|
console.log(formatNodeLog('DEBUG', context, ANSI.magenta), ...args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -72,3 +240,17 @@ export function getLogLevel(): LogLevel {
|
|||||||
export function setLogLevel(level: LogLevel): void {
|
export function setLogLevel(level: LogLevel): void {
|
||||||
currentLogLevel = level;
|
currentLogLevel = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable colored output
|
||||||
|
*/
|
||||||
|
export function setColorsEnabled(enabled: boolean): void {
|
||||||
|
colorsEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable timestamps in output
|
||||||
|
*/
|
||||||
|
export function setTimestampsEnabled(enabled: boolean): void {
|
||||||
|
timestampsEnabled = enabled;
|
||||||
|
}
|
||||||
|
|||||||
104
package-lock.json
generated
104
package-lock.json
generated
@@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"apps/server": {
|
"apps/server": {
|
||||||
"name": "@automaker/server",
|
"name": "@automaker/server",
|
||||||
"version": "0.7.1",
|
"version": "0.7.2",
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/claude-agent-sdk": "0.1.76",
|
"@anthropic-ai/claude-agent-sdk": "0.1.76",
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
},
|
},
|
||||||
"apps/ui": {
|
"apps/ui": {
|
||||||
"name": "@automaker/ui",
|
"name": "@automaker/ui",
|
||||||
"version": "0.7.1",
|
"version": "0.7.2",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -675,6 +675,7 @@
|
|||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -1258,6 +1259,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz",
|
||||||
"integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==",
|
"integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
"crelt": "^1.0.6",
|
"crelt": "^1.0.6",
|
||||||
@@ -1300,6 +1302,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/accessibility": "^3.1.1",
|
"@dnd-kit/accessibility": "^3.1.1",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
@@ -2120,7 +2123,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-dirname": "^0.1.0",
|
"cross-dirname": "^0.1.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
@@ -2142,7 +2144,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
@@ -2159,7 +2160,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -2174,7 +2174,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
@@ -2942,7 +2941,6 @@
|
|||||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
@@ -3067,7 +3065,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -3084,7 +3081,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -3101,7 +3097,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -3210,7 +3205,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -3233,7 +3227,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -3256,7 +3249,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -3342,7 +3334,6 @@
|
|||||||
],
|
],
|
||||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emnapi/runtime": "^1.7.0"
|
"@emnapi/runtime": "^1.7.0"
|
||||||
},
|
},
|
||||||
@@ -3365,7 +3356,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -3385,7 +3375,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -3785,8 +3774,7 @@
|
|||||||
"version": "16.0.10",
|
"version": "16.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz",
|
||||||
"integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==",
|
"integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-arm64": {
|
"node_modules/@next/swc-darwin-arm64": {
|
||||||
"version": "16.0.10",
|
"version": "16.0.10",
|
||||||
@@ -3800,7 +3788,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
@@ -3817,7 +3804,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
@@ -3834,7 +3820,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
@@ -3851,7 +3836,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
@@ -3868,7 +3852,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
@@ -3885,7 +3868,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
@@ -3902,7 +3884,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
@@ -3919,7 +3900,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
@@ -4010,6 +3990,7 @@
|
|||||||
"integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==",
|
"integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.57.0"
|
"playwright": "1.57.0"
|
||||||
},
|
},
|
||||||
@@ -5450,7 +5431,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||||
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.8.0"
|
"tslib": "^2.8.0"
|
||||||
}
|
}
|
||||||
@@ -5784,6 +5764,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.141.6.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.141.6.tgz",
|
||||||
"integrity": "sha512-qWFxi2D6eGc1L03RzUuhyEOplZ7Q6q62YOl7Of9Y0q4YjwQwxRm4zxwDVtvUIoy4RLVCpqp5UoE+Nxv2PY9trg==",
|
"integrity": "sha512-qWFxi2D6eGc1L03RzUuhyEOplZ7Q6q62YOl7Of9Y0q4YjwQwxRm4zxwDVtvUIoy4RLVCpqp5UoE+Nxv2PY9trg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/history": "1.141.0",
|
"@tanstack/history": "1.141.0",
|
||||||
"@tanstack/react-store": "^0.8.0",
|
"@tanstack/react-store": "^0.8.0",
|
||||||
@@ -6210,6 +6191,7 @@
|
|||||||
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
"@types/express-serve-static-core": "^5.0.0",
|
"@types/express-serve-static-core": "^5.0.0",
|
||||||
@@ -6352,6 +6334,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@@ -6362,6 +6345,7 @@
|
|||||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
@@ -6467,6 +6451,7 @@
|
|||||||
"integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==",
|
"integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.50.0",
|
"@typescript-eslint/scope-manager": "8.50.0",
|
||||||
"@typescript-eslint/types": "8.50.0",
|
"@typescript-eslint/types": "8.50.0",
|
||||||
@@ -6960,7 +6945,8 @@
|
|||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@xyflow/react": {
|
"node_modules/@xyflow/react": {
|
||||||
"version": "12.10.0",
|
"version": "12.10.0",
|
||||||
@@ -7058,6 +7044,7 @@
|
|||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -7118,6 +7105,7 @@
|
|||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@@ -7716,6 +7704,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -8247,8 +8236,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/cliui": {
|
"node_modules/cliui": {
|
||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
@@ -8553,8 +8541,7 @@
|
|||||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
@@ -8651,6 +8638,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -8952,6 +8940,7 @@
|
|||||||
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
|
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "26.0.12",
|
"app-builder-lib": "26.0.12",
|
||||||
"builder-util": "26.0.11",
|
"builder-util": "26.0.11",
|
||||||
@@ -9278,7 +9267,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/asar": "^3.2.1",
|
"@electron/asar": "^3.2.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
@@ -9299,7 +9287,6 @@
|
|||||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
@@ -9550,6 +9537,7 @@
|
|||||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -9864,6 +9852,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "^2.0.0",
|
"accepts": "^2.0.0",
|
||||||
"body-parser": "^2.2.1",
|
"body-parser": "^2.2.1",
|
||||||
@@ -11531,7 +11520,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11553,7 +11541,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11575,7 +11562,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11597,7 +11583,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11619,7 +11604,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11641,7 +11625,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11663,7 +11646,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11685,7 +11667,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11707,7 +11688,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11729,7 +11709,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -11751,7 +11730,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12.0.0"
|
"node": ">= 12.0.0"
|
||||||
},
|
},
|
||||||
@@ -14039,7 +14017,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.6",
|
"nanoid": "^3.3.6",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
@@ -14056,7 +14033,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^9.4.0"
|
"commander": "^9.4.0"
|
||||||
},
|
},
|
||||||
@@ -14074,7 +14050,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
@@ -14263,6 +14238,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -14272,6 +14248,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -14630,7 +14607,6 @@
|
|||||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
},
|
},
|
||||||
@@ -14819,6 +14795,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.4.0.tgz",
|
||||||
"integrity": "sha512-BdrNXdzlofomLTiRnwJTSEAaGKyHHZkbMXIywOh7zlzp4uZnXErEwl9XZ+N1hJSNpeTtNxWvVwN0wUzAIQ4Hpg==",
|
"integrity": "sha512-BdrNXdzlofomLTiRnwJTSEAaGKyHHZkbMXIywOh7zlzp4uZnXErEwl9XZ+N1hJSNpeTtNxWvVwN0wUzAIQ4Hpg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
@@ -14867,7 +14844,6 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@img/colour": "^1.0.0",
|
"@img/colour": "^1.0.0",
|
||||||
"detect-libc": "^2.1.2",
|
"detect-libc": "^2.1.2",
|
||||||
@@ -14918,7 +14894,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -14941,7 +14916,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -14964,7 +14938,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -14981,7 +14954,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -14998,7 +14970,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -15015,7 +14986,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -15032,7 +15002,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -15049,7 +15018,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -15066,7 +15034,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
@@ -15083,7 +15050,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -15106,7 +15072,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -15129,7 +15094,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -15152,7 +15116,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -15175,7 +15138,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -15198,7 +15160,6 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
@@ -15667,7 +15628,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
|
||||||
"integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
|
"integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"client-only": "0.0.1"
|
"client-only": "0.0.1"
|
||||||
},
|
},
|
||||||
@@ -15837,7 +15797,6 @@
|
|||||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"rimraf": "~2.6.2"
|
"rimraf": "~2.6.2"
|
||||||
@@ -15901,7 +15860,6 @@
|
|||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
},
|
},
|
||||||
@@ -15999,6 +15957,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -16203,6 +16162,7 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -16574,6 +16534,7 @@
|
|||||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -16663,7 +16624,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
|
"resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
|
||||||
"integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
|
"integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/vite/node_modules/fdir": {
|
"node_modules/vite/node_modules/fdir": {
|
||||||
"version": "6.5.0",
|
"version": "6.5.0",
|
||||||
@@ -16689,6 +16651,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -16731,6 +16694,7 @@
|
|||||||
"integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
|
"integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "4.0.16",
|
"@vitest/expect": "4.0.16",
|
||||||
"@vitest/mocker": "4.0.16",
|
"@vitest/mocker": "4.0.16",
|
||||||
@@ -16988,6 +16952,7 @@
|
|||||||
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"yaml": "bin.mjs"
|
"yaml": "bin.mjs"
|
||||||
},
|
},
|
||||||
@@ -17056,6 +17021,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
|
||||||
"integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
|
"integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user