Compare commits
2 Commits
fdcbe32dfa
...
af74841765
| Author | SHA1 | Date | |
|---|---|---|---|
| af74841765 | |||
| d7e4919d54 |
@@ -73,6 +73,40 @@
|
|||||||
- 📝 Recursive helper to write entity tree to Markdown with tier badges and enhanced details.
|
- 📝 Recursive helper to write entity tree to Markdown with tier badges and enhanced details.
|
||||||
- ƒ **to_dict** (`Function`) `[TRIVIAL]`
|
- ƒ **to_dict** (`Function`) `[TRIVIAL]`
|
||||||
- 📝 Auto-detected function (orphan)
|
- 📝 Auto-detected function (orphan)
|
||||||
|
- 📦 **PluginExampleShot** (`Module`)
|
||||||
|
- 📝 Reference implementation of a plugin following GRACE standards.
|
||||||
|
- 🔗 IMPLEMENTS -> `[DEF:Std:Plugin]`
|
||||||
|
- ƒ **get_schema** (`Function`)
|
||||||
|
- 📝 Defines input validation schema.
|
||||||
|
- ƒ **execute** (`Function`)
|
||||||
|
- 📝 Core plugin logic with structured logging and progress reporting.
|
||||||
|
- ƒ **id** (`Function`) `[TRIVIAL]`
|
||||||
|
- 📝 Auto-detected function (orphan)
|
||||||
|
- ƒ **name** (`Function`) `[TRIVIAL]`
|
||||||
|
- 📝 Auto-detected function (orphan)
|
||||||
|
- ƒ **description** (`Function`) `[TRIVIAL]`
|
||||||
|
- 📝 Auto-detected function (orphan)
|
||||||
|
- ƒ **version** (`Function`) `[TRIVIAL]`
|
||||||
|
- 📝 Auto-detected function (orphan)
|
||||||
|
- 📦 **BackendRouteShot** (`Module`)
|
||||||
|
- 📝 Reference implementation of a task-based route using GRACE-Poly.
|
||||||
|
- 🔗 IMPLEMENTS -> `[DEF:Std:API_FastAPI]`
|
||||||
|
- ƒ **create_task** (`Function`)
|
||||||
|
- 📝 Create and start a new task using TaskManager. Non-blocking.
|
||||||
|
- 🔗 CALLS -> `task_manager.create_task`
|
||||||
|
- 🧩 **FrontendComponentShot** (`Component`)
|
||||||
|
- 📝 Reference implementation of a task-spawning component using
|
||||||
|
- 🏗️ Layer: UI
|
||||||
|
- 📥 Props: plugin_id: any, params: any
|
||||||
|
- ⬅️ READS_FROM `lib`
|
||||||
|
- ⬅️ READS_FROM `t`
|
||||||
|
- 📦 **DashboardTypes** (`Module`) `[TRIVIAL]`
|
||||||
|
- 📝 TypeScript interfaces for Dashboard entities
|
||||||
|
- 🏗️ Layer: Domain
|
||||||
|
- 🧩 **Counter** (`Component`) `[TRIVIAL]`
|
||||||
|
- 📝 Simple counter demo component
|
||||||
|
- 🏗️ Layer: UI
|
||||||
|
- ➡️ WRITES_TO `state`
|
||||||
- 📦 **stores_module** (`Module`)
|
- 📦 **stores_module** (`Module`)
|
||||||
- 📝 Global state management using Svelte stores.
|
- 📝 Global state management using Svelte stores.
|
||||||
- 🏗️ Layer: UI-State
|
- 🏗️ Layer: UI-State
|
||||||
@@ -116,6 +150,11 @@
|
|||||||
- 📝 Generic request wrapper.
|
- 📝 Generic request wrapper.
|
||||||
- 📦 **api** (`Data`)
|
- 📦 **api** (`Data`)
|
||||||
- 📝 API client object with specific methods.
|
- 📝 API client object with specific methods.
|
||||||
|
- 📦 **Utils** (`Module`) `[TRIVIAL]`
|
||||||
|
- 📝 General utility functions (class merging)
|
||||||
|
- 🏗️ Layer: Infra
|
||||||
|
- ƒ **cn** (`Function`) `[TRIVIAL]`
|
||||||
|
- 📝 Auto-detected function (orphan)
|
||||||
- 🗄️ **authStore** (`Store`)
|
- 🗄️ **authStore** (`Store`)
|
||||||
- 📝 Manages the global authentication state on the frontend.
|
- 📝 Manages the global authentication state on the frontend.
|
||||||
- 🏗️ Layer: Feature
|
- 🏗️ Layer: Feature
|
||||||
@@ -131,9 +170,9 @@
|
|||||||
- 📝 Clears authentication state and storage.
|
- 📝 Clears authentication state and storage.
|
||||||
- ƒ **setLoading** (`Function`)
|
- ƒ **setLoading** (`Function`)
|
||||||
- 📝 Updates the loading state.
|
- 📝 Updates the loading state.
|
||||||
- 📦 **debounce** (`Module`) `[TRIVIAL]`
|
- 📦 **Debounce** (`Module`) `[TRIVIAL]`
|
||||||
- 📝 Auto-generated module for frontend/src/lib/utils/debounce.js
|
- 📝 Debounce utility for limiting function execution rate
|
||||||
- 🏗️ Layer: Unknown
|
- 🏗️ Layer: Infra
|
||||||
- ƒ **debounce** (`Function`) `[TRIVIAL]`
|
- ƒ **debounce** (`Function`) `[TRIVIAL]`
|
||||||
- 📝 Auto-detected function (orphan)
|
- 📝 Auto-detected function (orphan)
|
||||||
- 🗄️ **taskDrawer** (`Store`) `[CRITICAL]`
|
- 🗄️ **taskDrawer** (`Store`) `[CRITICAL]`
|
||||||
@@ -181,8 +220,9 @@
|
|||||||
- 📝 Unit tests for sidebar store
|
- 📝 Unit tests for sidebar store
|
||||||
- 🏗️ Layer: Domain (Tests)
|
- 🏗️ Layer: Domain (Tests)
|
||||||
- ƒ **test_sidebar_initial_state** (`Function`)
|
- ƒ **test_sidebar_initial_state** (`Function`)
|
||||||
- ƒ **test_toggleSidebar** (`Function`)
|
- ƒ **test_toggleSidebar** (`Function`)
|
||||||
- ƒ **test_setActiveItem** (`Function`)
|
- ƒ **test_setActiveItem** (`Function`)
|
||||||
|
- ƒ **test_mobile_functions** (`Function`)
|
||||||
- 📦 **frontend.src.lib.stores.__tests__.test_activity** (`Module`)
|
- 📦 **frontend.src.lib.stores.__tests__.test_activity** (`Module`)
|
||||||
- 📝 Unit tests for activity store
|
- 📝 Unit tests for activity store
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
@@ -202,7 +242,9 @@
|
|||||||
- 🧩 **Select** (`Component`) `[TRIVIAL]`
|
- 🧩 **Select** (`Component`) `[TRIVIAL]`
|
||||||
- 📝 Standardized dropdown selection component.
|
- 📝 Standardized dropdown selection component.
|
||||||
- 🏗️ Layer: Atom
|
- 🏗️ Layer: Atom
|
||||||
- 📥 Props: label: string , value: string | number , disabled: boolean
|
- ⬅️ READS_FROM `lib`
|
||||||
|
- ➡️ WRITES_TO `bindable`
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- 📦 **ui** (`Module`) `[TRIVIAL]`
|
- 📦 **ui** (`Module`) `[TRIVIAL]`
|
||||||
- 📝 Central export point for standardized UI components.
|
- 📝 Central export point for standardized UI components.
|
||||||
- 🏗️ Layer: Atom
|
- 🏗️ Layer: Atom
|
||||||
@@ -210,21 +252,26 @@
|
|||||||
- 🧩 **PageHeader** (`Component`) `[TRIVIAL]`
|
- 🧩 **PageHeader** (`Component`) `[TRIVIAL]`
|
||||||
- 📝 Standardized page header with title and action area.
|
- 📝 Standardized page header with title and action area.
|
||||||
- 🏗️ Layer: Atom
|
- 🏗️ Layer: Atom
|
||||||
- 📥 Props: title: string
|
- ⬅️ READS_FROM `lib`
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- 🧩 **Card** (`Component`) `[TRIVIAL]`
|
- 🧩 **Card** (`Component`) `[TRIVIAL]`
|
||||||
- 📝 Standardized container with padding and elevation.
|
- 📝 Standardized container with padding and elevation.
|
||||||
- 🏗️ Layer: Atom
|
- 🏗️ Layer: Atom
|
||||||
- 📥 Props: title: string
|
- ⬅️ READS_FROM `lib`
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- 🧩 **Button** (`Component`) `[TRIVIAL]`
|
- 🧩 **Button** (`Component`) `[TRIVIAL]`
|
||||||
- 📝 Define component interface and default values.
|
- 📝 Define component interface and default values (Svelte 5 Runes).
|
||||||
- 🏗️ Layer: Atom
|
- 🏗️ Layer: Atom
|
||||||
- 🔒 Invariant: Supports accessible labels and keyboard navigation.
|
- 🔒 Invariant: Supports accessible labels and keyboard navigation.
|
||||||
- 📥 Props: isLoading: boolean , disabled: boolean
|
- ⬅️ READS_FROM `lib`
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- 🧩 **Input** (`Component`) `[TRIVIAL]`
|
- 🧩 **Input** (`Component`) `[TRIVIAL]`
|
||||||
- 📝 Standardized text input component with label and error handling.
|
- 📝 Standardized text input component with label and error handling.
|
||||||
- 🏗️ Layer: Atom
|
- 🏗️ Layer: Atom
|
||||||
- 🔒 Invariant: Consistent spacing and focus states.
|
- 🔒 Invariant: Consistent spacing and focus states.
|
||||||
- 📥 Props: label: string , value: string , placeholder: string , error: string , disabled: boolean
|
- ⬅️ READS_FROM `lib`
|
||||||
|
- ➡️ WRITES_TO `bindable`
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- 🧩 **LanguageSwitcher** (`Component`) `[TRIVIAL]`
|
- 🧩 **LanguageSwitcher** (`Component`) `[TRIVIAL]`
|
||||||
- 📝 Dropdown component to switch between supported languages.
|
- 📝 Dropdown component to switch between supported languages.
|
||||||
- 🏗️ Layer: Atom
|
- 🏗️ Layer: Atom
|
||||||
@@ -293,10 +340,9 @@
|
|||||||
- 📝 Display page hierarchy navigation
|
- 📝 Display page hierarchy navigation
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 🔒 Invariant: Always shows current page path
|
- 🔒 Invariant: Always shows current page path
|
||||||
- 📥 Props: maxVisible: any
|
|
||||||
- ⬅️ READS_FROM `app`
|
- ⬅️ READS_FROM `app`
|
||||||
- ⬅️ READS_FROM `lib`
|
- ⬅️ READS_FROM `lib`
|
||||||
- ⬅️ READS_FROM `page`
|
- ➡️ WRITES_TO `props`
|
||||||
- 📦 **Breadcrumbs** (`Module`) `[TRIVIAL]`
|
- 📦 **Breadcrumbs** (`Module`) `[TRIVIAL]`
|
||||||
- 📝 Auto-generated module for frontend/src/lib/components/layout/Breadcrumbs.svelte
|
- 📝 Auto-generated module for frontend/src/lib/components/layout/Breadcrumbs.svelte
|
||||||
- 🏗️ Layer: Unknown
|
- 🏗️ Layer: Unknown
|
||||||
@@ -328,6 +374,12 @@
|
|||||||
- 📝 Auto-detected function (orphan)
|
- 📝 Auto-detected function (orphan)
|
||||||
- ƒ **disconnectWebSocket** (`Function`) `[TRIVIAL]`
|
- ƒ **disconnectWebSocket** (`Function`) `[TRIVIAL]`
|
||||||
- 📝 Auto-detected function (orphan)
|
- 📝 Auto-detected function (orphan)
|
||||||
|
- 📦 **ErrorPage** (`Page`)
|
||||||
|
- 📝 Global error page displaying HTTP status and messages
|
||||||
|
- 🏗️ Layer: UI
|
||||||
|
- 📦 **RootLayoutConfig** (`Module`) `[TRIVIAL]`
|
||||||
|
- 📝 Root layout configuration (SPA mode)
|
||||||
|
- 🏗️ Layer: Infra
|
||||||
- 📦 **HomePage** (`Page`) `[CRITICAL]`
|
- 📦 **HomePage** (`Page`) `[CRITICAL]`
|
||||||
- 📝 Redirect to Dashboard Hub as per UX requirements
|
- 📝 Redirect to Dashboard Hub as per UX requirements
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
@@ -692,8 +744,9 @@
|
|||||||
- 🧩 **PasswordPrompt** (`Component`)
|
- 🧩 **PasswordPrompt** (`Component`)
|
||||||
- 📝 A modal component to prompt the user for database passwords when a migration task is paused.
|
- 📝 A modal component to prompt the user for database passwords when a migration task is paused.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 📥 Props: show: any, databases: any, errorMessage: any
|
|
||||||
- ⚡ Events: cancel, resume
|
- ⚡ Events: cancel, resume
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
|
- ⬅️ READS_FROM `effect`
|
||||||
- ƒ **handleSubmit** (`Function`)
|
- ƒ **handleSubmit** (`Function`)
|
||||||
- 📝 Validates and dispatches the passwords to resume the task.
|
- 📝 Validates and dispatches the passwords to resume the task.
|
||||||
- ƒ **handleCancel** (`Function`)
|
- ƒ **handleCancel** (`Function`)
|
||||||
@@ -703,6 +756,7 @@
|
|||||||
- 🏗️ Layer: Feature
|
- 🏗️ Layer: Feature
|
||||||
- 🔒 Invariant: Each source database can be mapped to one target database.
|
- 🔒 Invariant: Each source database can be mapped to one target database.
|
||||||
- ⚡ Events: update
|
- ⚡ Events: update
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ƒ **updateMapping** (`Function`)
|
- ƒ **updateMapping** (`Function`)
|
||||||
- 📝 Updates a mapping for a specific source database.
|
- 📝 Updates a mapping for a specific source database.
|
||||||
- ƒ **getSuggestion** (`Function`)
|
- ƒ **getSuggestion** (`Function`)
|
||||||
@@ -711,13 +765,12 @@
|
|||||||
- 📝 Displays detailed logs for a specific task inline or in a modal using TaskLogPanel.
|
- 📝 Displays detailed logs for a specific task inline or in a modal using TaskLogPanel.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 🔒 Invariant: Real-time logs are always appended without duplicates.
|
- 🔒 Invariant: Real-time logs are always appended without duplicates.
|
||||||
- 📥 Props: show: any, inline: any, taskId: any, taskStatus: any, realTimeLogs: any
|
|
||||||
- ⚡ Events: close
|
- ⚡ Events: close
|
||||||
- ⬅️ READS_FROM `t`
|
- ➡️ WRITES_TO `bindable`
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
|
- ➡️ WRITES_TO `state`
|
||||||
- 📦 **handleRealTimeLogs** (`Action`)
|
- 📦 **handleRealTimeLogs** (`Action`)
|
||||||
- 📝 Append real-time logs as they arrive from WebSocket, preventing duplicates */
|
|
||||||
- ƒ **fetchLogs** (`Function`)
|
- ƒ **fetchLogs** (`Function`)
|
||||||
- 📝 Fetches logs for the current task from API (polling fallback).
|
|
||||||
- 📦 **TaskLogViewer** (`Module`) `[TRIVIAL]`
|
- 📦 **TaskLogViewer** (`Module`) `[TRIVIAL]`
|
||||||
- 📝 Auto-generated module for frontend/src/components/TaskLogViewer.svelte
|
- 📝 Auto-generated module for frontend/src/components/TaskLogViewer.svelte
|
||||||
- 🏗️ Layer: Unknown
|
- 🏗️ Layer: Unknown
|
||||||
@@ -732,8 +785,8 @@
|
|||||||
- 📝 Prompts the user to provide a database mapping when one is missing during migration.
|
- 📝 Prompts the user to provide a database mapping when one is missing during migration.
|
||||||
- 🏗️ Layer: Feature
|
- 🏗️ Layer: Feature
|
||||||
- 🔒 Invariant: Modal blocks migration progress until resolved or cancelled.
|
- 🔒 Invariant: Modal blocks migration progress until resolved or cancelled.
|
||||||
- 📥 Props: show: boolean , sourceDbName: string , sourceDbUuid: string
|
|
||||||
- ⚡ Events: cancel, resolve
|
- ⚡ Events: cancel, resolve
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ƒ **resolve** (`Function`)
|
- ƒ **resolve** (`Function`)
|
||||||
- 📝 Dispatches the resolution event with the selected mapping.
|
- 📝 Dispatches the resolution event with the selected mapping.
|
||||||
- ƒ **cancel** (`Function`)
|
- ƒ **cancel** (`Function`)
|
||||||
@@ -742,10 +795,10 @@
|
|||||||
- 📝 Displays a grid of dashboards with selection and pagination.
|
- 📝 Displays a grid of dashboards with selection and pagination.
|
||||||
- 🏗️ Layer: Component
|
- 🏗️ Layer: Component
|
||||||
- 🔒 Invariant: Selected IDs must be a subset of available dashboards.
|
- 🔒 Invariant: Selected IDs must be a subset of available dashboards.
|
||||||
- 📥 Props: dashboards: DashboardMetadata[] , selectedIds: number[] , environmentId: string
|
|
||||||
- ⚡ Events: selectionChanged
|
- ⚡ Events: selectionChanged
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
|
- ➡️ WRITES_TO `derived`
|
||||||
- ➡️ WRITES_TO `t`
|
- ➡️ WRITES_TO `t`
|
||||||
- ⬅️ READS_FROM `t`
|
|
||||||
- ƒ **handleValidate** (`Function`)
|
- ƒ **handleValidate** (`Function`)
|
||||||
- 📝 Triggers dashboard validation task.
|
- 📝 Triggers dashboard validation task.
|
||||||
- ƒ **handleSort** (`Function`)
|
- ƒ **handleSort** (`Function`)
|
||||||
@@ -816,8 +869,8 @@
|
|||||||
- 🧩 **TaskList** (`Component`)
|
- 🧩 **TaskList** (`Component`)
|
||||||
- 📝 Displays a list of tasks with their status and execution details.
|
- 📝 Displays a list of tasks with their status and execution details.
|
||||||
- 🏗️ Layer: Component
|
- 🏗️ Layer: Component
|
||||||
- 📥 Props: tasks: Array<any> , loading: boolean
|
|
||||||
- ⚡ Events: select
|
- ⚡ Events: select
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ➡️ WRITES_TO `t`
|
- ➡️ WRITES_TO `t`
|
||||||
- ⬅️ READS_FROM `t`
|
- ⬅️ READS_FROM `t`
|
||||||
- ƒ **getStatusColor** (`Function`)
|
- ƒ **getStatusColor** (`Function`)
|
||||||
@@ -829,8 +882,8 @@
|
|||||||
- 🧩 **DynamicForm** (`Component`)
|
- 🧩 **DynamicForm** (`Component`)
|
||||||
- 📝 Generates a form dynamically based on a JSON schema.
|
- 📝 Generates a form dynamically based on a JSON schema.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 📥 Props: schema: any
|
|
||||||
- ⚡ Events: submit
|
- ⚡ Events: submit
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ƒ **handleSubmit** (`Function`)
|
- ƒ **handleSubmit** (`Function`)
|
||||||
- 📝 Dispatches the submit event with the form data.
|
- 📝 Dispatches the submit event with the form data.
|
||||||
- ƒ **initializeForm** (`Function`)
|
- ƒ **initializeForm** (`Function`)
|
||||||
@@ -839,8 +892,8 @@
|
|||||||
- 📝 Provides a UI component for selecting source and target environments.
|
- 📝 Provides a UI component for selecting source and target environments.
|
||||||
- 🏗️ Layer: Feature
|
- 🏗️ Layer: Feature
|
||||||
- 🔒 Invariant: Source and target environments must be selectable from the list of configured environments.
|
- 🔒 Invariant: Source and target environments must be selectable from the list of configured environments.
|
||||||
- 📥 Props: label: string , selectedId: string
|
|
||||||
- ⚡ Events: change
|
- ⚡ Events: change
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ƒ **handleSelect** (`Function`)
|
- ƒ **handleSelect** (`Function`)
|
||||||
- 📝 Dispatches the selection change event.
|
- 📝 Dispatches the selection change event.
|
||||||
- 🧩 **ProtectedRoute** (`Component`) `[TRIVIAL]`
|
- 🧩 **ProtectedRoute** (`Component`) `[TRIVIAL]`
|
||||||
@@ -850,11 +903,13 @@
|
|||||||
- ⬅️ READS_FROM `app`
|
- ⬅️ READS_FROM `app`
|
||||||
- ⬅️ READS_FROM `auth`
|
- ⬅️ READS_FROM `auth`
|
||||||
- 🧩 **TaskLogPanel** (`Component`)
|
- 🧩 **TaskLogPanel** (`Component`)
|
||||||
- 📝 Component properties and state.
|
- 📝 Combines log filtering and display into a single cohesive dark-themed panel.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 🔒 Invariant: Must always display logs in chronological order and respect auto-scroll preference.
|
- 🔒 Invariant: Must always display logs in chronological order and respect auto-scroll preference.
|
||||||
- 📥 Props: logs: any, autoScroll: any
|
|
||||||
- ⚡ Events: filterChange
|
- ⚡ Events: filterChange
|
||||||
|
- ➡️ WRITES_TO `bindable`
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
|
- ➡️ WRITES_TO `state`
|
||||||
- 📦 **TaskLogPanel** (`Module`) `[TRIVIAL]`
|
- 📦 **TaskLogPanel** (`Module`) `[TRIVIAL]`
|
||||||
- 📝 Auto-generated module for frontend/src/components/tasks/TaskLogPanel.svelte
|
- 📝 Auto-generated module for frontend/src/components/tasks/TaskLogPanel.svelte
|
||||||
- 🏗️ Layer: Unknown
|
- 🏗️ Layer: Unknown
|
||||||
@@ -869,7 +924,9 @@
|
|||||||
- 🧩 **LogFilterBar** (`Component`)
|
- 🧩 **LogFilterBar** (`Component`)
|
||||||
- 📝 Compact filter toolbar for logs — level, source, and text search in a single dense row.
|
- 📝 Compact filter toolbar for logs — level, source, and text search in a single dense row.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 📥 Props: availableSources: any, selectedLevel: any, selectedSource: any, searchText: any
|
- ➡️ WRITES_TO `bindable`
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
|
- ➡️ WRITES_TO `derived`
|
||||||
- 📦 **LogFilterBar** (`Module`) `[TRIVIAL]`
|
- 📦 **LogFilterBar** (`Module`) `[TRIVIAL]`
|
||||||
- 📝 Auto-generated module for frontend/src/components/tasks/LogFilterBar.svelte
|
- 📝 Auto-generated module for frontend/src/components/tasks/LogFilterBar.svelte
|
||||||
- 🏗️ Layer: Unknown
|
- 🏗️ Layer: Unknown
|
||||||
@@ -884,21 +941,15 @@
|
|||||||
- 🧩 **LogEntryRow** (`Component`)
|
- 🧩 **LogEntryRow** (`Component`)
|
||||||
- 📝 Renders a single log entry with stacked layout optimized for narrow drawer panels.
|
- 📝 Renders a single log entry with stacked layout optimized for narrow drawer panels.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 📥 Props: log: any, showSource: any
|
- ➡️ WRITES_TO `props`
|
||||||
|
- ➡️ WRITES_TO `derived`
|
||||||
- ƒ **formatTime** (`Function`)
|
- ƒ **formatTime** (`Function`)
|
||||||
- 📝 Format ISO timestamp to HH:MM:SS */
|
- 📝 Format ISO timestamp to HH:MM:SS */
|
||||||
- 📦 **LogEntryRow** (`Module`) `[TRIVIAL]`
|
|
||||||
- 📝 Auto-generated module for frontend/src/components/tasks/LogEntryRow.svelte
|
|
||||||
- 🏗️ Layer: Unknown
|
|
||||||
- ƒ **getLevelClass** (`Function`) `[TRIVIAL]`
|
|
||||||
- 📝 Auto-detected function (orphan)
|
|
||||||
- ƒ **getSourceClass** (`Function`) `[TRIVIAL]`
|
|
||||||
- 📝 Auto-detected function (orphan)
|
|
||||||
- 🧩 **FileList** (`Component`)
|
- 🧩 **FileList** (`Component`)
|
||||||
- 📝 Displays a table of files with metadata and actions.
|
- 📝 Displays a table of files with metadata and actions.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 📥 Props: files: any
|
|
||||||
- ⚡ Events: delete, navigate
|
- ⚡ Events: delete, navigate
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ➡️ WRITES_TO `t`
|
- ➡️ WRITES_TO `t`
|
||||||
- ⬅️ READS_FROM `t`
|
- ⬅️ READS_FROM `t`
|
||||||
- ƒ **isDirectory** (`Function`)
|
- ƒ **isDirectory** (`Function`)
|
||||||
@@ -911,6 +962,7 @@
|
|||||||
- 📝 Provides a form for uploading files to a specific category.
|
- 📝 Provides a form for uploading files to a specific category.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- ⚡ Events: uploaded
|
- ⚡ Events: uploaded
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ⬅️ READS_FROM `t`
|
- ⬅️ READS_FROM `t`
|
||||||
- ➡️ WRITES_TO `t`
|
- ➡️ WRITES_TO `t`
|
||||||
- ƒ **handleUpload** (`Function`)
|
- ƒ **handleUpload** (`Function`)
|
||||||
@@ -964,7 +1016,7 @@
|
|||||||
- 🧩 **CommitHistory** (`Component`)
|
- 🧩 **CommitHistory** (`Component`)
|
||||||
- 📝 Displays the commit history for a specific dashboard.
|
- 📝 Displays the commit history for a specific dashboard.
|
||||||
- 🏗️ Layer: Component
|
- 🏗️ Layer: Component
|
||||||
- 📥 Props: dashboardId: any
|
- ➡️ WRITES_TO `props`
|
||||||
- ⬅️ READS_FROM `t`
|
- ⬅️ READS_FROM `t`
|
||||||
- ➡️ WRITES_TO `t`
|
- ➡️ WRITES_TO `t`
|
||||||
- ƒ **onMount** (`Function`)
|
- ƒ **onMount** (`Function`)
|
||||||
@@ -975,8 +1027,9 @@
|
|||||||
- 📝 Modal for deploying a dashboard to a target environment.
|
- 📝 Modal for deploying a dashboard to a target environment.
|
||||||
- 🏗️ Layer: Component
|
- 🏗️ Layer: Component
|
||||||
- 🔒 Invariant: Cannot deploy without a selected environment.
|
- 🔒 Invariant: Cannot deploy without a selected environment.
|
||||||
- 📥 Props: dashboardId: any, show: any
|
|
||||||
- ⚡ Events: deploy
|
- ⚡ Events: deploy
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
|
- ⬅️ READS_FROM `effect`
|
||||||
- 📦 **loadStatus** (`Watcher`)
|
- 📦 **loadStatus** (`Watcher`)
|
||||||
- ƒ **loadEnvironments** (`Function`)
|
- ƒ **loadEnvironments** (`Function`)
|
||||||
- 📝 Fetch available environments from API.
|
- 📝 Fetch available environments from API.
|
||||||
@@ -986,8 +1039,8 @@
|
|||||||
- 📝 UI for resolving merge conflicts (Keep Mine / Keep Theirs).
|
- 📝 UI for resolving merge conflicts (Keep Mine / Keep Theirs).
|
||||||
- 🏗️ Layer: Component
|
- 🏗️ Layer: Component
|
||||||
- 🔒 Invariant: User must resolve all conflicts before saving.
|
- 🔒 Invariant: User must resolve all conflicts before saving.
|
||||||
- 📥 Props: conflicts: any, show: any
|
|
||||||
- ⚡ Events: resolve
|
- ⚡ Events: resolve
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ƒ **resolve** (`Function`)
|
- ƒ **resolve** (`Function`)
|
||||||
- 📝 Set resolution strategy for a file.
|
- 📝 Set resolution strategy for a file.
|
||||||
- ƒ **handleSave** (`Function`)
|
- ƒ **handleSave** (`Function`)
|
||||||
@@ -995,8 +1048,9 @@
|
|||||||
- 🧩 **CommitModal** (`Component`)
|
- 🧩 **CommitModal** (`Component`)
|
||||||
- 📝 Модальное окно для создания коммита с просмотром изменений (diff).
|
- 📝 Модальное окно для создания коммита с просмотром изменений (diff).
|
||||||
- 🏗️ Layer: Component
|
- 🏗️ Layer: Component
|
||||||
- 📥 Props: dashboardId: any, show: any
|
|
||||||
- ⚡ Events: commit
|
- ⚡ Events: commit
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
|
- ⬅️ READS_FROM `effect`
|
||||||
- ƒ **handleGenerateMessage** (`Function`)
|
- ƒ **handleGenerateMessage** (`Function`)
|
||||||
- 📝 Generates a commit message using LLM.
|
- 📝 Generates a commit message using LLM.
|
||||||
- ƒ **loadStatus** (`Function`)
|
- ƒ **loadStatus** (`Function`)
|
||||||
@@ -1006,8 +1060,8 @@
|
|||||||
- 🧩 **BranchSelector** (`Component`)
|
- 🧩 **BranchSelector** (`Component`)
|
||||||
- 📝 UI для выбора и создания веток Git.
|
- 📝 UI для выбора и создания веток Git.
|
||||||
- 🏗️ Layer: Component
|
- 🏗️ Layer: Component
|
||||||
- 📥 Props: dashboardId: any, currentBranch: any
|
|
||||||
- ⚡ Events: change
|
- ⚡ Events: change
|
||||||
|
- ➡️ WRITES_TO `props`
|
||||||
- ⬅️ READS_FROM `t`
|
- ⬅️ READS_FROM `t`
|
||||||
- ƒ **onMount** (`Function`)
|
- ƒ **onMount** (`Function`)
|
||||||
- 📝 Load branches when component is mounted.
|
- 📝 Load branches when component is mounted.
|
||||||
@@ -1022,7 +1076,7 @@
|
|||||||
- 🧩 **GitManager** (`Component`)
|
- 🧩 **GitManager** (`Component`)
|
||||||
- 📝 Центральный компонент для управления Git-операциями конкретного дашборда.
|
- 📝 Центральный компонент для управления Git-операциями конкретного дашборда.
|
||||||
- 🏗️ Layer: Component
|
- 🏗️ Layer: Component
|
||||||
- 📥 Props: dashboardId: any, dashboardTitle: any, show: any
|
- ➡️ WRITES_TO `props`
|
||||||
- ➡️ WRITES_TO `t`
|
- ➡️ WRITES_TO `t`
|
||||||
- ⬅️ READS_FROM `t`
|
- ⬅️ READS_FROM `t`
|
||||||
- ƒ **checkStatus** (`Function`)
|
- ƒ **checkStatus** (`Function`)
|
||||||
@@ -1038,7 +1092,7 @@
|
|||||||
- 🧩 **DocPreview** (`Component`)
|
- 🧩 **DocPreview** (`Component`)
|
||||||
- 📝 UI component for previewing generated dataset documentation before saving.
|
- 📝 UI component for previewing generated dataset documentation before saving.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 📥 Props: documentation: any, onSave: any, onCancel: any
|
- ➡️ WRITES_TO `props`
|
||||||
- ➡️ WRITES_TO `t`
|
- ➡️ WRITES_TO `t`
|
||||||
- ⬅️ READS_FROM `t`
|
- ⬅️ READS_FROM `t`
|
||||||
- 📦 **DocPreview** (`Module`) `[TRIVIAL]`
|
- 📦 **DocPreview** (`Module`) `[TRIVIAL]`
|
||||||
@@ -1049,7 +1103,7 @@
|
|||||||
- 🧩 **ProviderConfig** (`Component`)
|
- 🧩 **ProviderConfig** (`Component`)
|
||||||
- 📝 UI form for managing LLM provider configurations.
|
- 📝 UI form for managing LLM provider configurations.
|
||||||
- 🏗️ Layer: UI
|
- 🏗️ Layer: UI
|
||||||
- 📥 Props: providers: any, onSave: any
|
- ➡️ WRITES_TO `props`
|
||||||
- ➡️ WRITES_TO `t`
|
- ➡️ WRITES_TO `t`
|
||||||
- ⬅️ READS_FROM `t`
|
- ⬅️ READS_FROM `t`
|
||||||
- 📦 **ProviderConfig** (`Module`) `[TRIVIAL]`
|
- 📦 **ProviderConfig** (`Module`) `[TRIVIAL]`
|
||||||
@@ -1099,14 +1153,14 @@
|
|||||||
- 📝 Provides a WebSocket endpoint for real-time log streaming of a task with server-side filtering.
|
- 📝 Provides a WebSocket endpoint for real-time log streaming of a task with server-side filtering.
|
||||||
- 📦 **StaticFiles** (`Mount`)
|
- 📦 **StaticFiles** (`Mount`)
|
||||||
- 📝 Mounts the frontend build directory to serve static assets.
|
- 📝 Mounts the frontend build directory to serve static assets.
|
||||||
|
- ƒ **serve_spa** (`Function`)
|
||||||
|
- 📝 Serves the SPA frontend for any path not matched by API routes.
|
||||||
- ƒ **read_root** (`Function`)
|
- ƒ **read_root** (`Function`)
|
||||||
- 📝 A simple root endpoint to confirm that the API is running when frontend is missing.
|
- 📝 A simple root endpoint to confirm that the API is running when frontend is missing.
|
||||||
- ƒ **network_error_handler** (`Function`) `[TRIVIAL]`
|
- ƒ **network_error_handler** (`Function`) `[TRIVIAL]`
|
||||||
- 📝 Auto-detected function (orphan)
|
- 📝 Auto-detected function (orphan)
|
||||||
- ƒ **matches_filters** (`Function`) `[TRIVIAL]`
|
- ƒ **matches_filters** (`Function`) `[TRIVIAL]`
|
||||||
- 📝 Auto-detected function (orphan)
|
- 📝 Auto-detected function (orphan)
|
||||||
- ƒ **serve_spa** (`Function`) `[TRIVIAL]`
|
|
||||||
- 📝 Auto-detected function (orphan)
|
|
||||||
- 📦 **Dependencies** (`Module`)
|
- 📦 **Dependencies** (`Module`)
|
||||||
- 📝 Manages creation and provision of shared application dependencies, such as PluginLoader and TaskManager, to avoid circular imports.
|
- 📝 Manages creation and provision of shared application dependencies, such as PluginLoader and TaskManager, to avoid circular imports.
|
||||||
- 🏗️ Layer: Core
|
- 🏗️ Layer: Core
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# [DEF:Project_Knowledge_Map:Root]
|
# [DEF:Project_Knowledge_Map:Root]
|
||||||
# @TIER: CRITICAL
|
# @TIER: CRITICAL
|
||||||
# @PURPOSE: Global navigation map for AI-Agent (GRACE Knowledge Graph).
|
# @PURPOSE: Global navigation map for AI-Agent (GRACE Knowledge Graph).
|
||||||
# @LAST_UPDATE: 2026-02-19
|
# @LAST_UPDATE: 2026-02-20
|
||||||
|
|
||||||
## 1. SYSTEM STANDARDS (Rules of the Game)
|
## 1. SYSTEM STANDARDS (Rules of the Game)
|
||||||
Strict policies and formatting rules.
|
Strict policies and formatting rules.
|
||||||
@@ -26,6 +26,8 @@ Use these for code generation (Style Transfer).
|
|||||||
* Ref: `.ai/shots/frontend_component.svelte` -> `[DEF:Shot:Svelte_Component]`
|
* Ref: `.ai/shots/frontend_component.svelte` -> `[DEF:Shot:Svelte_Component]`
|
||||||
* **Plugin Module:** Reference implementation of a task plugin.
|
* **Plugin Module:** Reference implementation of a task plugin.
|
||||||
* Ref: `.ai/shots/plugin_example.py` -> `[DEF:Shot:Plugin_Example]`
|
* Ref: `.ai/shots/plugin_example.py` -> `[DEF:Shot:Plugin_Example]`
|
||||||
|
* **Critical Module:** Core banking transaction processor with ACID guarantees.
|
||||||
|
* Ref: `.ai/shots/critical_module.py` -> `[DEF:Shot:Critical_Module]`
|
||||||
|
|
||||||
## 3. DOMAIN MAP (Modules)
|
## 3. DOMAIN MAP (Modules)
|
||||||
* **Project Map:** `.ai/PROJECT_MAP.md` -> `[DEF:Project_Map]`
|
* **Project Map:** `.ai/PROJECT_MAP.md` -> `[DEF:Project_Map]`
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
# [DEF:Shot:FastAPI_Route:Example]
|
# [DEF:BackendRouteShot:Module]
|
||||||
|
# @TIER: STANDARD
|
||||||
|
# @SEMANTICS: Route, Task, API, Async
|
||||||
# @PURPOSE: Reference implementation of a task-based route using GRACE-Poly.
|
# @PURPOSE: Reference implementation of a task-based route using GRACE-Poly.
|
||||||
|
# @LAYER: Interface (API)
|
||||||
# @RELATION: IMPLEMENTS -> [DEF:Std:API_FastAPI]
|
# @RELATION: IMPLEMENTS -> [DEF:Std:API_FastAPI]
|
||||||
|
# @INVARIANT: TaskManager must be available in dependency graph.
|
||||||
|
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import Dict, Any
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from ...core.logger import belief_scope
|
from ...core.logger import belief_scope
|
||||||
from ...core.task_manager import TaskManager, Task
|
from ...core.task_manager import TaskManager, Task
|
||||||
from ...core.config_manager import ConfigManager
|
from ...core.config_manager import ConfigManager
|
||||||
from ...dependencies import get_task_manager, get_config_manager, has_permission, get_current_user
|
from ...dependencies import get_task_manager, get_config_manager, get_current_user
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -21,37 +25,41 @@ class CreateTaskRequest(BaseModel):
|
|||||||
# @PURPOSE: Create and start a new task using TaskManager. Non-blocking.
|
# @PURPOSE: Create and start a new task using TaskManager. Non-blocking.
|
||||||
# @PARAM: request (CreateTaskRequest) - Plugin and params.
|
# @PARAM: request (CreateTaskRequest) - Plugin and params.
|
||||||
# @PARAM: task_manager (TaskManager) - Async task executor.
|
# @PARAM: task_manager (TaskManager) - Async task executor.
|
||||||
# @PARAM: config (ConfigManager) - Centralized configuration.
|
# @PRE: plugin_id must match a registered plugin.
|
||||||
# @PRE: plugin_id must exist; config must be initialized.
|
|
||||||
# @POST: A new task is spawned; Task ID returned immediately.
|
# @POST: A new task is spawned; Task ID returned immediately.
|
||||||
|
# @SIDE_EFFECT: Writes to DB, Trigger background worker.
|
||||||
async def create_task(
|
async def create_task(
|
||||||
request: CreateTaskRequest,
|
request: CreateTaskRequest,
|
||||||
task_manager: TaskManager = Depends(get_task_manager),
|
task_manager: TaskManager = Depends(get_task_manager),
|
||||||
config: ConfigManager = Depends(get_config_manager),
|
config: ConfigManager = Depends(get_config_manager),
|
||||||
current_user = Depends(get_current_user)
|
current_user = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
# RBAC: Dynamic permission check
|
# Context Logging
|
||||||
has_permission(f"plugin:{request.plugin_id}", "EXECUTE")(current_user)
|
|
||||||
|
|
||||||
with belief_scope("create_task"):
|
with belief_scope("create_task"):
|
||||||
try:
|
try:
|
||||||
# 1. Action: Resolve setting using ConfigManager (Example)
|
# 1. Action: Configuration Resolution
|
||||||
timeout = config.get("TASKS_DEFAULT_TIMEOUT", 3600)
|
timeout = config.get("TASKS_DEFAULT_TIMEOUT", 3600)
|
||||||
|
|
||||||
# 2. Action: Spawn async task via TaskManager
|
# 2. Action: Spawn async task
|
||||||
# @RELATION: CALLS -> task_manager.create_task
|
# @RELATION: CALLS -> task_manager.create_task
|
||||||
task = await task_manager.create_task(
|
task = await task_manager.create_task(
|
||||||
plugin_id=request.plugin_id,
|
plugin_id=request.plugin_id,
|
||||||
params={**request.params, "timeout": timeout}
|
params={**request.params, "timeout": timeout}
|
||||||
)
|
)
|
||||||
return task
|
return task
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
# 3. Recovery: Domain logic error mapping
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail=str(e)
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Evaluation: Proper error mapping and logging
|
# @UX_STATE: Error feedback -> 500 Internal Error
|
||||||
# @UX_STATE: Error feedback to frontend
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
detail=f"Task creation failed: {str(e)}"
|
detail="Internal Task Spawning Error"
|
||||||
)
|
)
|
||||||
# [/DEF:create_task:Function]
|
# [/DEF:create_task:Function]
|
||||||
|
|
||||||
# [/DEF:Shot:FastAPI_Route]
|
# [/DEF:BackendRouteShot:Module]
|
||||||
79
.ai/shots/critical_module.py
Normal file
79
.ai/shots/critical_module.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# [DEF:TransactionCore:Module]
|
||||||
|
# @TIER: CRITICAL
|
||||||
|
# @SEMANTICS: Finance, ACID, Transfer, Ledger
|
||||||
|
# @PURPOSE: Core banking transaction processor with ACID guarantees.
|
||||||
|
# @LAYER: Domain (Core)
|
||||||
|
# @RELATION: DEPENDS_ON -> [DEF:Infra:PostgresDB]
|
||||||
|
# @RELATION: DEPENDS_ON -> [DEF:Infra:AuditLog]
|
||||||
|
# @INVARIANT: Total system balance must remain constant (Double-Entry Bookkeeping).
|
||||||
|
# @INVARIANT: Negative transfers are strictly forbidden.
|
||||||
|
|
||||||
|
# @TEST_DATA: sufficient_funds -> {"from": "acc_A", "to": "acc_B", "amt": 100.00}
|
||||||
|
# @TEST_DATA: insufficient_funds -> {"from": "acc_empty", "to": "acc_B", "amt": 1000.00}
|
||||||
|
# @TEST_DATA: concurrency_lock -> {./fixtures/transactions.json#race_condition}
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from typing import NamedTuple
|
||||||
|
from ...core.logger import belief_scope
|
||||||
|
from ...core.db import atomic_transaction, get_balance, update_balance
|
||||||
|
from ...core.exceptions import BusinessRuleViolation
|
||||||
|
|
||||||
|
class TransferResult(NamedTuple):
|
||||||
|
tx_id: str
|
||||||
|
status: str
|
||||||
|
new_balance: Decimal
|
||||||
|
|
||||||
|
# [DEF:execute_transfer:Function]
|
||||||
|
# @PURPOSE: Atomically move funds between accounts with audit trails.
|
||||||
|
# @PARAM: sender_id (str) - Source account.
|
||||||
|
# @PARAM: receiver_id (str) - Destination account.
|
||||||
|
# @PARAM: amount (Decimal) - Positive amount to transfer.
|
||||||
|
# @PRE: amount > 0; sender != receiver; sender_balance >= amount.
|
||||||
|
# @POST: sender_balance -= amount; receiver_balance += amount; Audit Record Created.
|
||||||
|
# @SIDE_EFFECT: Database mutation (Rows locked), Audit IO.
|
||||||
|
#
|
||||||
|
# @UX_STATE: Success -> Returns 200 OK + Transaction Receipt.
|
||||||
|
# @UX_STATE: Error(LowBalance) -> 422 Unprocessable -> UI shows "Top-up needed" modal.
|
||||||
|
# @UX_STATE: Error(System) -> 500 Internal -> UI shows "Retry later" toast.
|
||||||
|
def execute_transfer(sender_id: str, receiver_id: str, amount: Decimal) -> TransferResult:
|
||||||
|
# Guard: Input Validation
|
||||||
|
if amount <= Decimal("0.00"):
|
||||||
|
raise BusinessRuleViolation("Transfer amount must be positive.")
|
||||||
|
if sender_id == receiver_id:
|
||||||
|
raise BusinessRuleViolation("Cannot transfer to self.")
|
||||||
|
|
||||||
|
with belief_scope("execute_transfer") as context:
|
||||||
|
context.logger.info("Initiating transfer", data={"from": sender_id, "to": receiver_id})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. Action: Atomic DB Transaction
|
||||||
|
# @RELATION: CALLS -> atomic_transaction
|
||||||
|
with atomic_transaction():
|
||||||
|
# Guard: State Validation (Strict)
|
||||||
|
current_balance = get_balance(sender_id, for_update=True)
|
||||||
|
|
||||||
|
if current_balance < amount:
|
||||||
|
# @UX_FEEDBACK: Triggers specific UI flow for insufficient funds
|
||||||
|
context.logger.warn("Insufficient funds", data={"balance": current_balance})
|
||||||
|
raise BusinessRuleViolation("INSUFFICIENT_FUNDS")
|
||||||
|
|
||||||
|
# 2. Action: Mutation
|
||||||
|
new_src_bal = update_balance(sender_id, -amount)
|
||||||
|
new_dst_bal = update_balance(receiver_id, +amount)
|
||||||
|
|
||||||
|
# 3. Action: Audit
|
||||||
|
tx_id = context.audit.log_transfer(sender_id, receiver_id, amount)
|
||||||
|
|
||||||
|
context.logger.info("Transfer committed", data={"tx_id": tx_id})
|
||||||
|
return TransferResult(tx_id, "COMPLETED", new_src_bal)
|
||||||
|
|
||||||
|
except BusinessRuleViolation as e:
|
||||||
|
# Logic: Explicit re-raise for UI mapping
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
# Logic: Catch-all safety net
|
||||||
|
context.logger.error("Critical Transfer Failure", error=e)
|
||||||
|
raise RuntimeError("TRANSACTION_ABORTED") from e
|
||||||
|
# [/DEF:execute_transfer:Function]
|
||||||
|
|
||||||
|
# [/DEF:TransactionCore:Module]
|
||||||
@@ -1,19 +1,23 @@
|
|||||||
<!-- [DEF:Shot:Svelte_Component:Example] -->
|
<!-- [DEF:FrontendComponentShot:Component] -->
|
||||||
# @PURPOSE: Reference implementation of a task-spawning component using Constitution rules.
|
|
||||||
# @RELATION: IMPLEMENTS -> [DEF:Std:UI_Svelte]
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/**
|
/**
|
||||||
* @TIER: STANDARD
|
* @TIER: CRITICAL
|
||||||
* @PURPOSE: Action button to spawn a new task.
|
* @SEMANTICS: Task, Button, Action, UX
|
||||||
* @LAYER: UI
|
* @PURPOSE: Action button to spawn a new task with full UX feedback cycle.
|
||||||
* @SEMANTICS: Task, Creation, Button
|
* @LAYER: UI (Presentation)
|
||||||
* @RELATION: CALLS -> postApi
|
* @RELATION: CALLS -> postApi
|
||||||
|
* @INVARIANT: Must prevent double-submission while loading.
|
||||||
*
|
*
|
||||||
* @UX_STATE: Idle -> Button enabled with primary color.
|
* @TEST_DATA: idle_state -> {"isLoading": false}
|
||||||
* @UX_STATE: Loading -> Button disabled with spinner while postApi resolves.
|
* @TEST_DATA: loading_state -> {"isLoading": true}
|
||||||
* @UX_FEEDBACK: toast.success on completion; toast.error on failure.
|
*
|
||||||
* @UX_TEST: Idle -> {click: spawnTask, expected: loading state then success}
|
* @UX_STATE: Idle -> Button enabled, primary color.
|
||||||
|
* @UX_STATE: Loading -> Button disabled, spinner visible.
|
||||||
|
* @UX_STATE: Error -> Toast notification triggers.
|
||||||
|
*
|
||||||
|
* @UX_FEEDBACK: Toast success/error.
|
||||||
|
* @UX_TEST: Idle -> {click: spawnTask, expected: isLoading=true}
|
||||||
|
* @UX_TEST: Success -> {api_resolve: 200, expected: toast.success called}
|
||||||
*/
|
*/
|
||||||
import { postApi } from "$lib/api.js";
|
import { postApi } from "$lib/api.js";
|
||||||
import { t } from "$lib/i18n";
|
import { t } from "$lib/i18n";
|
||||||
@@ -24,40 +28,43 @@
|
|||||||
|
|
||||||
let isLoading = false;
|
let isLoading = false;
|
||||||
|
|
||||||
async def spawnTask() {
|
// [DEF:spawnTask:Function]
|
||||||
|
async function spawnTask() {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
console.log("[FrontendComponentShot][Loading] Spawning task...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Action: Constitution Rule - MUST use postApi wrapper
|
// 1. Action: API Call
|
||||||
const response = await postApi("/api/tasks", {
|
const response = await postApi("/api/tasks", {
|
||||||
plugin_id,
|
plugin_id,
|
||||||
params
|
params
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Feedback: UX state management
|
// 2. Feedback: Success
|
||||||
if (response.task_id) {
|
if (response.task_id) {
|
||||||
|
console.log("[FrontendComponentShot][Success] Task created.");
|
||||||
toast.success($t.tasks.spawned_success);
|
toast.success($t.tasks.spawned_success);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 3. Recovery: Evaluation & UI reporting
|
// 3. Recovery: User notification
|
||||||
|
console.log("[FrontendComponentShot][Error] Failed:", error);
|
||||||
toast.error(`${$t.errors.task_failed}: ${error.message}`);
|
toast.error(`${$t.errors.task_failed}: ${error.message}`);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// [/DEF:spawnTask:Function]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
on:click={spawnTask}
|
on:click={spawnTask}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center gap-2"
|
class="btn-primary flex items-center gap-2"
|
||||||
|
aria-busy={isLoading}
|
||||||
>
|
>
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<span class="animate-spin text-sm">🌀</span>
|
<span class="animate-spin" aria-label="Loading">🌀</span>
|
||||||
{/if}
|
{/if}
|
||||||
<span>{$t.actions.start_task}</span>
|
<span>{$t.actions.start_task}</span>
|
||||||
</button>
|
</button>
|
||||||
|
<!-- [/DEF:FrontendComponentShot:Component] -->
|
||||||
<style>
|
|
||||||
/* Local styles minimized as per Constitution Rule III */
|
|
||||||
</style>
|
|
||||||
<!-- [/DEF:Shot:Svelte_Component] -->
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
# [DEF:Shot:Plugin_Example:Example]
|
# [DEF:PluginExampleShot:Module]
|
||||||
|
# @TIER: STANDARD
|
||||||
|
# @SEMANTICS: Plugin, Core, Extension
|
||||||
# @PURPOSE: Reference implementation of a plugin following GRACE standards.
|
# @PURPOSE: Reference implementation of a plugin following GRACE standards.
|
||||||
# @RELATION: IMPLEMENTS -> [DEF:Std:Plugin]
|
# @LAYER: Domain (Business Logic)
|
||||||
|
# @RELATION: INHERITS -> PluginBase
|
||||||
|
# @INVARIANT: get_schema must return valid JSON Schema.
|
||||||
|
|
||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
from ..core.plugin_base import PluginBase
|
from ..core.plugin_base import PluginBase
|
||||||
@@ -11,28 +15,15 @@ class ExamplePlugin(PluginBase):
|
|||||||
def id(self) -> str:
|
def id(self) -> str:
|
||||||
return "example-plugin"
|
return "example-plugin"
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "Example Plugin"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return "A simple plugin that demonstrates structured logging and progress tracking."
|
|
||||||
|
|
||||||
@property
|
|
||||||
def version(self) -> str:
|
|
||||||
return "1.0.0"
|
|
||||||
|
|
||||||
# [DEF:get_schema:Function]
|
# [DEF:get_schema:Function]
|
||||||
# @PURPOSE: Defines input validation schema.
|
# @PURPOSE: Defines input validation schema.
|
||||||
|
# @POST: Returns dict compliant with JSON Schema draft 7.
|
||||||
def get_schema(self) -> Dict[str, Any]:
|
def get_schema(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"message": {
|
"message": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Message",
|
|
||||||
"description": "A message to log.",
|
|
||||||
"default": "Hello, GRACE!",
|
"default": "Hello, GRACE!",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -41,27 +32,33 @@ class ExamplePlugin(PluginBase):
|
|||||||
# [/DEF:get_schema:Function]
|
# [/DEF:get_schema:Function]
|
||||||
|
|
||||||
# [DEF:execute:Function]
|
# [DEF:execute:Function]
|
||||||
# @PURPOSE: Core plugin logic with structured logging and progress reporting.
|
# @PURPOSE: Core plugin logic with structured logging and scope isolation.
|
||||||
# @PARAM: params (Dict) - Validated input parameters.
|
# @PARAM: params (Dict) - Validated input parameters.
|
||||||
# @PARAM: context (TaskContext) - Execution context with logging and progress tools.
|
# @PARAM: context (TaskContext) - Execution tools (log, progress).
|
||||||
async def execute(self, params: Dict[str, Any], context: Optional[TaskContext] = None):
|
# @SIDE_EFFECT: Emits logs to centralized system.
|
||||||
message = params["message"]
|
async def execute(self, params: Dict, context: Optional = None):
|
||||||
|
message = params
|
||||||
|
|
||||||
# 1. Action: Structured Logging with Source Attribution
|
# 1. Action: System-level tracing (Rule VI)
|
||||||
if context:
|
with belief_scope("example_plugin_exec") as b_scope:
|
||||||
log = context.logger.with_source("example_plugin")
|
if context:
|
||||||
log.info(f"Starting execution with message: {message}")
|
# Task Logs: Пишем в пользовательский контекст выполнения задачи
|
||||||
|
# @RELATION: BINDS_TO -> context.logger
|
||||||
|
log = context.logger.with_source("example_plugin")
|
||||||
|
|
||||||
# 2. Action: Progress Reporting
|
b_scope.logger.info("Using provided TaskContext") # System log
|
||||||
log.progress("Processing step 1...", percent=25)
|
log.info("Starting execution", data={"msg": message}) # Task log
|
||||||
# Simulating some async work...
|
|
||||||
# await some_async_op()
|
|
||||||
|
|
||||||
log.progress("Processing step 2...", percent=75)
|
# 2. Action: Progress Reporting
|
||||||
log.info("Execution completed successfully.")
|
log.progress("Processing...", percent=50)
|
||||||
else:
|
|
||||||
# Fallback for manual/standalone execution
|
# 3. Action: Finalize
|
||||||
print(f"Standalone execution: {message}")
|
log.info("Execution completed.")
|
||||||
|
else:
|
||||||
|
# Standalone Fallback: Замыкаемся на системный scope
|
||||||
|
b_scope.logger.warning("No TaskContext provided. Running standalone.")
|
||||||
|
b_scope.logger.info("Standalone execution", data={"msg": message})
|
||||||
|
print(f"Standalone: {message}")
|
||||||
# [/DEF:execute:Function]
|
# [/DEF:execute:Function]
|
||||||
|
|
||||||
# [/DEF:Shot:Plugin_Example]
|
# [/DEF:PluginExampleShot:Module]
|
||||||
Binary file not shown.
@@ -241,6 +241,10 @@ frontend_path = project_root / "frontend" / "build"
|
|||||||
if frontend_path.exists():
|
if frontend_path.exists():
|
||||||
app.mount("/_app", StaticFiles(directory=str(frontend_path / "_app")), name="static")
|
app.mount("/_app", StaticFiles(directory=str(frontend_path / "_app")), name="static")
|
||||||
|
|
||||||
|
# [DEF:serve_spa:Function]
|
||||||
|
# @PURPOSE: Serves the SPA frontend for any path not matched by API routes.
|
||||||
|
# @PRE: frontend_path exists.
|
||||||
|
# @POST: Returns the requested file or index.html.
|
||||||
@app.get("/{file_path:path}", include_in_schema=False)
|
@app.get("/{file_path:path}", include_in_schema=False)
|
||||||
async def serve_spa(file_path: str):
|
async def serve_spa(file_path: str):
|
||||||
# Only serve SPA for non-API paths
|
# Only serve SPA for non-API paths
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ def __getattr__(name):
|
|||||||
from .resource_service import ResourceService
|
from .resource_service import ResourceService
|
||||||
return ResourceService
|
return ResourceService
|
||||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||||
|
# [/DEF:backend.src.services:Module]
|
||||||
|
|||||||
BIN
backend/tasks.db
BIN
backend/tasks.db
Binary file not shown.
@@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
let passwords = {};
|
let passwords = $state({});
|
||||||
let submitting = false;
|
let submitting = $state(false);
|
||||||
|
|
||||||
// [DEF:handleSubmit:Function]
|
// [DEF:handleSubmit:Function]
|
||||||
// @PURPOSE: Validates and dispatches the passwords to resume the task.
|
// @PURPOSE: Validates and dispatches the passwords to resume the task.
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
<div
|
<div
|
||||||
class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
on:click={handleCancel}
|
onclick={handleCancel}
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<form
|
<form
|
||||||
on:submit|preventDefault={handleSubmit}
|
onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}
|
||||||
class="space-y-4"
|
class="space-y-4"
|
||||||
>
|
>
|
||||||
{#each databases as dbName}
|
{#each databases as dbName}
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50"
|
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50"
|
||||||
on:click={handleSubmit}
|
onclick={handleSubmit}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
>
|
>
|
||||||
{submitting ? "Resuming..." : "Resume Migration"}
|
{submitting ? "Resuming..." : "Resume Migration"}
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
|
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
|
||||||
on:click={handleCancel}
|
onclick={handleCancel}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
<span>{error}</span>
|
<span>{error}</span>
|
||||||
<button
|
<button
|
||||||
class="bg-terminal-surface text-terminal-text-subtle border border-terminal-border rounded-md px-3 py-1 text-xs cursor-pointer transition-all hover:bg-terminal-border hover:text-terminal-text-bright"
|
class="bg-terminal-surface text-terminal-text-subtle border border-terminal-border rounded-md px-3 py-1 text-xs cursor-pointer transition-all hover:bg-terminal-border hover:text-terminal-text-bright"
|
||||||
on:click={handleRefresh}>Retry</button
|
onclick={handleRefresh}>Retry</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -149,11 +149,11 @@
|
|||||||
<div
|
<div
|
||||||
class="fixed inset-0 bg-gray-500/75 transition-opacity"
|
class="fixed inset-0 bg-gray-500/75 transition-opacity"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
on:click={() => {
|
onclick={() => {
|
||||||
show = false;
|
show = false;
|
||||||
dispatch("close");
|
dispatch("close");
|
||||||
}}
|
}}
|
||||||
on:keydown={(e) => e.key === "Escape" && (show = false)}
|
onkeydown={(e) => e.key === "Escape" && (show = false)}
|
||||||
role="presentation"
|
role="presentation"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<button
|
<button
|
||||||
class="text-gray-500 hover:text-gray-300"
|
class="text-gray-500 hover:text-gray-300"
|
||||||
on:click={() => {
|
onclick={() => {
|
||||||
show = false;
|
show = false;
|
||||||
dispatch("close");
|
dispatch("close");
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -7,67 +7,74 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from "svelte";
|
||||||
import { t } from '../../lib/i18n';
|
import { t } from "../../lib/i18n";
|
||||||
import { requestApi } from '../../lib/api';
|
import { requestApi } from "../../lib/api";
|
||||||
|
|
||||||
/** @type {Array} */
|
/** @type {Array} */
|
||||||
let {
|
let { providers = [], onSave = () => {} } = $props();
|
||||||
provider,
|
|
||||||
config = {},
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
|
|
||||||
let editingProvider = null;
|
let editingProvider = null;
|
||||||
let showForm = false;
|
let showForm = false;
|
||||||
|
|
||||||
let formData = {
|
let formData = {
|
||||||
name: '',
|
name: "",
|
||||||
provider_type: 'openai',
|
provider_type: "openai",
|
||||||
base_url: 'https://api.openai.com/v1',
|
base_url: "https://api.openai.com/v1",
|
||||||
api_key: '',
|
api_key: "",
|
||||||
default_model: 'gpt-4o',
|
default_model: "gpt-4o",
|
||||||
is_active: true
|
is_active: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let testStatus = { type: '', message: '' };
|
let testStatus = { type: "", message: "" };
|
||||||
let isTesting = false;
|
let isTesting = false;
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
formData = {
|
formData = {
|
||||||
name: '',
|
name: "",
|
||||||
provider_type: 'openai',
|
provider_type: "openai",
|
||||||
base_url: 'https://api.openai.com/v1',
|
base_url: "https://api.openai.com/v1",
|
||||||
api_key: '',
|
api_key: "",
|
||||||
default_model: 'gpt-4o',
|
default_model: "gpt-4o",
|
||||||
is_active: true
|
is_active: true,
|
||||||
};
|
};
|
||||||
editingProvider = null;
|
editingProvider = null;
|
||||||
testStatus = { type: '', message: '' };
|
testStatus = { type: "", message: "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(provider) {
|
function handleEdit(provider) {
|
||||||
editingProvider = provider;
|
editingProvider = provider;
|
||||||
formData = { ...provider, api_key: '' }; // Don't populate key for security
|
formData = { ...provider, api_key: "" }; // Don't populate key for security
|
||||||
showForm = true;
|
showForm = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testConnection() {
|
async function testConnection() {
|
||||||
console.log("[ProviderConfig][Action] Testing connection", formData);
|
console.log("[ProviderConfig][Action] Testing connection", formData);
|
||||||
isTesting = true;
|
isTesting = true;
|
||||||
testStatus = { type: 'info', message: $t.llm.testing };
|
testStatus = { type: "info", message: $t.llm.testing };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const endpoint = editingProvider ? `/llm/providers/${editingProvider.id}/test` : '/llm/providers/test';
|
const endpoint = editingProvider
|
||||||
const result = await requestApi(endpoint, 'POST', formData);
|
? `/llm/providers/${editingProvider.id}/test`
|
||||||
|
: "/llm/providers/test";
|
||||||
|
const result = await requestApi(endpoint, "POST", formData);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
testStatus = { type: 'success', message: $t.llm.connection_success };
|
testStatus = { type: "success", message: $t.llm.connection_success };
|
||||||
} else {
|
} else {
|
||||||
testStatus = { type: 'error', message: $t.llm.connection_failed.replace('{error}', result.error || 'Unknown error') };
|
testStatus = {
|
||||||
|
type: "error",
|
||||||
|
message: $t.llm.connection_failed.replace(
|
||||||
|
"{error}",
|
||||||
|
result.error || "Unknown error",
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
testStatus = { type: 'error', message: $t.llm.connection_failed.replace('{error}', err.message) };
|
testStatus = {
|
||||||
|
type: "error",
|
||||||
|
message: $t.llm.connection_failed.replace("{error}", err.message),
|
||||||
|
};
|
||||||
} finally {
|
} finally {
|
||||||
isTesting = false;
|
isTesting = false;
|
||||||
}
|
}
|
||||||
@@ -75,8 +82,10 @@
|
|||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
console.log("[ProviderConfig][Action] Submitting provider config");
|
console.log("[ProviderConfig][Action] Submitting provider config");
|
||||||
const method = editingProvider ? 'PUT' : 'POST';
|
const method = editingProvider ? "PUT" : "POST";
|
||||||
const endpoint = editingProvider ? `/llm/providers/${editingProvider.id}` : '/llm/providers';
|
const endpoint = editingProvider
|
||||||
|
? `/llm/providers/${editingProvider.id}`
|
||||||
|
: "/llm/providers";
|
||||||
|
|
||||||
// When editing, only include api_key if user entered a new one
|
// When editing, only include api_key if user entered a new one
|
||||||
const submitData = { ...formData };
|
const submitData = { ...formData };
|
||||||
@@ -97,9 +106,9 @@
|
|||||||
|
|
||||||
async function toggleActive(provider) {
|
async function toggleActive(provider) {
|
||||||
try {
|
try {
|
||||||
await requestApi(`/llm/providers/${provider.id}`, 'PUT', {
|
await requestApi(`/llm/providers/${provider.id}`, "PUT", {
|
||||||
...provider,
|
...provider,
|
||||||
is_active: !provider.is_active
|
is_active: !provider.is_active,
|
||||||
});
|
});
|
||||||
onSave();
|
onSave();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -113,26 +122,51 @@
|
|||||||
<h2 class="text-xl font-bold">{$t.llm.providers_title}</h2>
|
<h2 class="text-xl font-bold">{$t.llm.providers_title}</h2>
|
||||||
<button
|
<button
|
||||||
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition"
|
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition"
|
||||||
on:click={() => { resetForm(); showForm = true; }}
|
on:click={() => {
|
||||||
|
resetForm();
|
||||||
|
showForm = true;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{$t.llm.add_provider}
|
{$t.llm.add_provider}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showForm}
|
{#if showForm}
|
||||||
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div
|
||||||
|
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
|
||||||
|
>
|
||||||
<div class="bg-white p-6 rounded-lg shadow-xl w-full max-w-md">
|
<div class="bg-white p-6 rounded-lg shadow-xl w-full max-w-md">
|
||||||
<h3 class="text-lg font-semibold mb-4">{editingProvider ? $t.llm.edit_provider : $t.llm.new_provider}</h3>
|
<h3 class="text-lg font-semibold mb-4">
|
||||||
|
{editingProvider ? $t.llm.edit_provider : $t.llm.new_provider}
|
||||||
|
</h3>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="provider-name" class="block text-sm font-medium text-gray-700">{$t.llm.name}</label>
|
<label
|
||||||
<input id="provider-name" type="text" bind:value={formData.name} class="mt-1 block w-full border rounded-md p-2" placeholder="e.g. My OpenAI" />
|
for="provider-name"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>{$t.llm.name}</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="provider-name"
|
||||||
|
type="text"
|
||||||
|
bind:value={formData.name}
|
||||||
|
class="mt-1 block w-full border rounded-md p-2"
|
||||||
|
placeholder="e.g. My OpenAI"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="provider-type" class="block text-sm font-medium text-gray-700">{$t.llm.type}</label>
|
<label
|
||||||
<select id="provider-type" bind:value={formData.provider_type} class="mt-1 block w-full border rounded-md p-2">
|
for="provider-type"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>{$t.llm.type}</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
id="provider-type"
|
||||||
|
bind:value={formData.provider_type}
|
||||||
|
class="mt-1 block w-full border rounded-md p-2"
|
||||||
|
>
|
||||||
<option value="openai">OpenAI</option>
|
<option value="openai">OpenAI</option>
|
||||||
<option value="openrouter">OpenRouter</option>
|
<option value="openrouter">OpenRouter</option>
|
||||||
<option value="kilo">Kilo</option>
|
<option value="kilo">Kilo</option>
|
||||||
@@ -140,28 +174,67 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="provider-base-url" class="block text-sm font-medium text-gray-700">{$t.llm.base_url}</label>
|
<label
|
||||||
<input id="provider-base-url" type="text" bind:value={formData.base_url} class="mt-1 block w-full border rounded-md p-2" />
|
for="provider-base-url"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>{$t.llm.base_url}</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="provider-base-url"
|
||||||
|
type="text"
|
||||||
|
bind:value={formData.base_url}
|
||||||
|
class="mt-1 block w-full border rounded-md p-2"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="provider-api-key" class="block text-sm font-medium text-gray-700">{$t.llm.api_key}</label>
|
<label
|
||||||
<input id="provider-api-key" type="password" bind:value={formData.api_key} class="mt-1 block w-full border rounded-md p-2" placeholder={editingProvider ? "••••••••" : "sk-..."} />
|
for="provider-api-key"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>{$t.llm.api_key}</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="provider-api-key"
|
||||||
|
type="password"
|
||||||
|
bind:value={formData.api_key}
|
||||||
|
class="mt-1 block w-full border rounded-md p-2"
|
||||||
|
placeholder={editingProvider ? "••••••••" : "sk-..."}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="provider-default-model" class="block text-sm font-medium text-gray-700">{$t.llm.default_model}</label>
|
<label
|
||||||
<input id="provider-default-model" type="text" bind:value={formData.default_model} class="mt-1 block w-full border rounded-md p-2" placeholder="gpt-4o" />
|
for="provider-default-model"
|
||||||
|
class="block text-sm font-medium text-gray-700"
|
||||||
|
>{$t.llm.default_model}</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="provider-default-model"
|
||||||
|
type="text"
|
||||||
|
bind:value={formData.default_model}
|
||||||
|
class="mt-1 block w-full border rounded-md p-2"
|
||||||
|
placeholder="gpt-4o"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input id="provider-active" type="checkbox" bind:checked={formData.is_active} class="mr-2" />
|
<input
|
||||||
<label for="provider-active" class="text-sm font-medium text-gray-700">{$t.llm.active}</label>
|
id="provider-active"
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={formData.is_active}
|
||||||
|
class="mr-2"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="provider-active"
|
||||||
|
class="text-sm font-medium text-gray-700">{$t.llm.active}</label
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if testStatus.message}
|
{#if testStatus.message}
|
||||||
<div class={`mt-4 p-2 rounded text-sm ${testStatus.type === 'success' ? 'bg-green-100 text-green-800' : testStatus.type === 'error' ? 'bg-red-100 text-red-800' : 'bg-blue-100 text-blue-800'}`}>
|
<div
|
||||||
|
class={`mt-4 p-2 rounded text-sm ${testStatus.type === "success" ? "bg-green-100 text-green-800" : testStatus.type === "error" ? "bg-red-100 text-red-800" : "bg-blue-100 text-blue-800"}`}
|
||||||
|
>
|
||||||
{testStatus.message}
|
{testStatus.message}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -169,7 +242,9 @@
|
|||||||
<div class="mt-6 flex justify-between gap-2">
|
<div class="mt-6 flex justify-between gap-2">
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 border rounded hover:bg-gray-50 flex-1"
|
class="px-4 py-2 border rounded hover:bg-gray-50 flex-1"
|
||||||
on:click={() => { showForm = false; }}
|
on:click={() => {
|
||||||
|
showForm = false;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{$t.llm.cancel}
|
{$t.llm.cancel}
|
||||||
</button>
|
</button>
|
||||||
@@ -193,15 +268,21 @@
|
|||||||
|
|
||||||
<div class="grid gap-4">
|
<div class="grid gap-4">
|
||||||
{#each providers as provider}
|
{#each providers as provider}
|
||||||
<div class="border rounded-lg p-4 flex justify-between items-center bg-white shadow-sm">
|
<div
|
||||||
|
class="border rounded-lg p-4 flex justify-between items-center bg-white shadow-sm"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<div class="font-bold flex items-center gap-2">
|
<div class="font-bold flex items-center gap-2">
|
||||||
{provider.name}
|
{provider.name}
|
||||||
<span class={`text-xs px-2 py-0.5 rounded-full ${provider.is_active ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`}>
|
<span
|
||||||
{provider.is_active ? $t.llm.active : 'Inactive'}
|
class={`text-xs px-2 py-0.5 rounded-full ${provider.is_active ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"}`}
|
||||||
|
>
|
||||||
|
{provider.is_active ? $t.llm.active : "Inactive"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-gray-500">{provider.provider_type} • {provider.default_model}</div>
|
<div class="text-sm text-gray-500">
|
||||||
|
{provider.provider_type} • {provider.default_model}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
@@ -211,15 +292,17 @@
|
|||||||
{$t.common.edit}
|
{$t.common.edit}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class={`text-sm ${provider.is_active ? 'text-orange-600' : 'text-green-600'} hover:underline`}
|
class={`text-sm ${provider.is_active ? "text-orange-600" : "text-green-600"} hover:underline`}
|
||||||
on:click={() => toggleActive(provider)}
|
on:click={() => toggleActive(provider)}
|
||||||
>
|
>
|
||||||
{provider.is_active ? 'Deactivate' : 'Activate'}
|
{provider.is_active ? "Deactivate" : "Activate"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="text-center py-8 text-gray-500 border-2 border-dashed rounded-lg">
|
<div
|
||||||
|
class="text-center py-8 text-gray-500 border-2 border-dashed rounded-lg"
|
||||||
|
>
|
||||||
{$t.llm.no_providers}
|
{$t.llm.no_providers}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
class="bg-terminal-surface text-terminal-text-subtle border border-terminal-border rounded px-2 py-[0.3125rem] text-xs cursor-pointer shrink-0 appearance-none pr-6 focus:outline-none focus:border-primary-ring"
|
class="bg-terminal-surface text-terminal-text-subtle border border-terminal-border rounded px-2 py-[0.3125rem] text-xs cursor-pointer shrink-0 appearance-none pr-6 focus:outline-none focus:border-primary-ring"
|
||||||
style="background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 0.375rem center;"
|
style="background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 0.375rem center;"
|
||||||
value={selectedLevel}
|
value={selectedLevel}
|
||||||
on:change={handleLevelChange}
|
onchange={handleLevelChange}
|
||||||
aria-label="Filter by level"
|
aria-label="Filter by level"
|
||||||
>
|
>
|
||||||
{#each levelOptions as option}
|
{#each levelOptions as option}
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
class="bg-terminal-surface text-terminal-text-subtle border border-terminal-border rounded px-2 py-[0.3125rem] text-xs cursor-pointer shrink-0 appearance-none pr-6 focus:outline-none focus:border-primary-ring"
|
class="bg-terminal-surface text-terminal-text-subtle border border-terminal-border rounded px-2 py-[0.3125rem] text-xs cursor-pointer shrink-0 appearance-none pr-6 focus:outline-none focus:border-primary-ring"
|
||||||
style="background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 0.375rem center;"
|
style="background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 0.375rem center;"
|
||||||
value={selectedSource}
|
value={selectedSource}
|
||||||
on:change={handleSourceChange}
|
onchange={handleSourceChange}
|
||||||
aria-label="Filter by source"
|
aria-label="Filter by source"
|
||||||
>
|
>
|
||||||
<option value="">All Sources</option>
|
<option value="">All Sources</option>
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
class="w-full bg-terminal-surface text-terminal-text-bright border border-terminal-border rounded py-[0.3125rem] px-2 pl-7 text-xs placeholder:text-terminal-text-muted focus:outline-none focus:border-primary-ring"
|
class="w-full bg-terminal-surface text-terminal-text-bright border border-terminal-border rounded py-[0.3125rem] px-2 pl-7 text-xs placeholder:text-terminal-text-muted focus:outline-none focus:border-primary-ring"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
value={searchText}
|
value={searchText}
|
||||||
on:input={handleSearchChange}
|
oninput={handleSearchChange}
|
||||||
aria-label="Search logs"
|
aria-label="Search logs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
{#if hasActiveFilters}
|
{#if hasActiveFilters}
|
||||||
<button
|
<button
|
||||||
class="flex items-center justify-center p-[0.3125rem] bg-transparent border border-terminal-border rounded text-terminal-text-subtle shrink-0 cursor-pointer transition-all hover:text-log-error hover:border-log-error hover:bg-log-error/10"
|
class="flex items-center justify-center p-[0.3125rem] bg-transparent border border-terminal-border rounded text-terminal-text-subtle shrink-0 cursor-pointer transition-all hover:text-log-error hover:border-log-error hover:bg-log-error/10"
|
||||||
on:click={clearFilters}
|
onclick={clearFilters}
|
||||||
aria-label="Clear filters"
|
aria-label="Clear filters"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -134,7 +134,7 @@
|
|||||||
<button
|
<button
|
||||||
class="flex items-center gap-1.5 bg-transparent border-none text-terminal-text-muted text-[0.6875rem] cursor-pointer py-px px-1.5 rounded transition-all hover:bg-terminal-surface hover:text-terminal-text-subtle
|
class="flex items-center gap-1.5 bg-transparent border-none text-terminal-text-muted text-[0.6875rem] cursor-pointer py-px px-1.5 rounded transition-all hover:bg-terminal-surface hover:text-terminal-text-subtle
|
||||||
{autoScroll ? 'text-terminal-accent' : ''}"
|
{autoScroll ? 'text-terminal-accent' : ''}"
|
||||||
on:click={toggleAutoScroll}
|
onclick={toggleAutoScroll}
|
||||||
aria-label="Toggle auto-scroll"
|
aria-label="Toggle auto-scroll"
|
||||||
>
|
>
|
||||||
{#if autoScroll}
|
{#if autoScroll}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
|
<!-- [DEF:Counter:Component] -->
|
||||||
|
<!--
|
||||||
|
@TIER: TRIVIAL
|
||||||
|
@PURPOSE: Simple counter demo component
|
||||||
|
@LAYER: UI
|
||||||
|
-->
|
||||||
<script>
|
<script>
|
||||||
let count = $state(0)
|
let count = $state(0);
|
||||||
const increment = () => {
|
const increment = () => {
|
||||||
count += 1
|
count += 1;
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button onclick={increment}>
|
<button onclick={increment}>
|
||||||
count is {count}
|
count is {count}
|
||||||
</button>
|
</button>
|
||||||
|
<!-- [/DEF:Counter:Component] -->
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ describe('SidebarStore', () => {
|
|||||||
expect(state.isMobileOpen).toBe(false);
|
expect(state.isMobileOpen).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// [/DEF:test_sidebar_initial_state:Function]
|
||||||
|
|
||||||
// [DEF:test_toggleSidebar:Function]
|
// [DEF:test_toggleSidebar:Function]
|
||||||
// @TEST: toggleSidebar toggles isExpanded state
|
// @TEST: toggleSidebar toggles isExpanded state
|
||||||
@@ -52,6 +53,7 @@ describe('SidebarStore', () => {
|
|||||||
expect(state.isExpanded).toBe(true);
|
expect(state.isExpanded).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// [/DEF:test_toggleSidebar:Function]
|
||||||
|
|
||||||
// [DEF:test_setActiveItem:Function]
|
// [DEF:test_setActiveItem:Function]
|
||||||
// @TEST: setActiveItem updates activeCategory and activeItem
|
// @TEST: setActiveItem updates activeCategory and activeItem
|
||||||
@@ -74,6 +76,7 @@ describe('SidebarStore', () => {
|
|||||||
expect(state.activeItem).toBe('/settings');
|
expect(state.activeItem).toBe('/settings');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// [/DEF:test_setActiveItem:Function]
|
||||||
|
|
||||||
// [DEF:test_mobile_functions:Function]
|
// [DEF:test_mobile_functions:Function]
|
||||||
// @TEST: Mobile functions correctly update isMobileOpen
|
// @TEST: Mobile functions correctly update isMobileOpen
|
||||||
@@ -110,6 +113,7 @@ describe('SidebarStore', () => {
|
|||||||
expect(state2.isMobileOpen).toBe(initialMobileOpen);
|
expect(state2.isMobileOpen).toBe(initialMobileOpen);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// [/DEF:test_mobile_functions:Function]
|
||||||
});
|
});
|
||||||
|
|
||||||
// [/DEF:frontend.src.lib.stores.__tests__.sidebar:Module]
|
// [/DEF:frontend.src.lib.stores.__tests__.sidebar:Module]
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
|
// [DEF:Utils:Module]
|
||||||
/**
|
/**
|
||||||
|
* @TIER: TRIVIAL
|
||||||
|
* @PURPOSE: General utility functions (class merging)
|
||||||
|
* @LAYER: Infra
|
||||||
|
*
|
||||||
* Merges class names into a single string.
|
* Merges class names into a single string.
|
||||||
* @param {...(string | undefined | null | false)} inputs
|
* @param {...(string | undefined | null | false)} inputs
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
@@ -6,3 +11,4 @@
|
|||||||
export function cn(...inputs) {
|
export function cn(...inputs) {
|
||||||
return inputs.filter(Boolean).join(" ");
|
return inputs.filter(Boolean).join(" ");
|
||||||
}
|
}
|
||||||
|
// [/DEF:Utils:Module]
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
|
// [DEF:Debounce:Module]
|
||||||
/**
|
/**
|
||||||
|
* @TIER: TRIVIAL
|
||||||
|
* @PURPOSE: Debounce utility for limiting function execution rate
|
||||||
|
* @LAYER: Infra
|
||||||
|
*
|
||||||
* Debounce utility function
|
* Debounce utility function
|
||||||
* Delays the execution of a function until a specified time has passed since the last call
|
* Delays the execution of a function until a specified time has passed since the last call
|
||||||
*
|
*
|
||||||
@@ -17,3 +22,4 @@ export function debounce(func, wait) {
|
|||||||
timeout = setTimeout(later, wait);
|
timeout = setTimeout(later, wait);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// [/DEF:Debounce:Module]
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
|
<!-- [DEF:ErrorPage:Page] -->
|
||||||
<script>
|
<script>
|
||||||
import { page } from '$app/stores';
|
/**
|
||||||
|
* @TIER: STANDARD
|
||||||
|
* @PURPOSE: Global error page displaying HTTP status and messages
|
||||||
|
* @LAYER: UI
|
||||||
|
* @UX_STATE: Error -> Displays error code and message with home link
|
||||||
|
*/
|
||||||
|
import { page } from "$app/stores";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container mx-auto p-4 text-center mt-20">
|
<div class="container mx-auto p-4 text-center mt-20">
|
||||||
<h1 class="text-6xl font-bold text-gray-800 mb-4">{$page.status}</h1>
|
<h1 class="text-6xl font-bold text-gray-800 mb-4">{$page.status}</h1>
|
||||||
<p class="text-2xl text-gray-600 mb-8">{$page.error?.message || 'Page not found'}</p>
|
<p class="text-2xl text-gray-600 mb-8">
|
||||||
<a href="/" class="bg-blue-500 text-white px-6 py-3 rounded-lg hover:bg-blue-600 transition-colors">
|
{$page.error?.message || "Page not found"}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
class="bg-blue-500 text-white px-6 py-3 rounded-lg hover:bg-blue-600 transition-colors"
|
||||||
|
>
|
||||||
Back to Dashboard
|
Back to Dashboard
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- [/DEF:ErrorPage:Page] -->
|
||||||
|
|||||||
@@ -1,2 +1,9 @@
|
|||||||
|
// [DEF:RootLayoutConfig:Module]
|
||||||
|
/**
|
||||||
|
* @TIER: TRIVIAL
|
||||||
|
* @PURPOSE: Root layout configuration (SPA mode)
|
||||||
|
* @LAYER: Infra
|
||||||
|
*/
|
||||||
export const ssr = false;
|
export const ssr = false;
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
// [/DEF:RootLayoutConfig:Module]
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
// [DEF:DashboardTypes:Module]
|
||||||
|
/**
|
||||||
|
* @TIER: TRIVIAL
|
||||||
|
* @PURPOSE: TypeScript interfaces for Dashboard entities
|
||||||
|
* @LAYER: Domain
|
||||||
|
*/
|
||||||
export interface DashboardMetadata {
|
export interface DashboardMetadata {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -11,3 +17,4 @@ export interface DashboardSelection {
|
|||||||
target_env_id: string;
|
target_env_id: string;
|
||||||
replace_db_config?: boolean;
|
replace_db_config?: boolean;
|
||||||
}
|
}
|
||||||
|
// [/DEF:DashboardTypes:Module]
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user