feat(phase2): complete P1 tasks - Channels, Triggers, Skills CRUD and UI enhancements

Phase 2 P1 Tasks Completed:

API Layer (gateway-client.ts, gatewayStore.ts):
- Add Channels CRUD: getChannel, createChannel, updateChannel, deleteChannel
- Add Triggers CRUD: getTrigger, createTrigger, updateTrigger, deleteTrigger
- Add Skills CRUD: getSkill, createSkill, updateSkill, deleteSkill
- Add Scheduled Tasks API: createScheduledTask, deleteScheduledTask, toggleScheduledTask
- Add loadModels action for dynamic model list

UI Components:
- ModelsAPI.tsx: Dynamic model loading from API with loading/error states
- SchedulerPanel.tsx: Full CreateJobModal with cron/interval/once scheduling
- SecurityStatus.tsx: Loading states, error handling, retry functionality
- WorkflowEditor.tsx: New workflow creation/editing modal (new file)
- WorkflowHistory.tsx: Workflow execution history viewer (new file)
- WorkflowList.tsx: Integrated editor and history access

Configuration:
- Add 4 Hands TOML configs: clip, collector, predictor, twitter

Documentation (SYSTEM_ANALYSIS.md):
- Update API coverage: 65% → 89% (53/62 endpoints)
- Update UI completion: 85% → 92%
- Mark Phase 2 P1 tasks as completed
- Update technical debt cleanup status

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-15 01:38:34 +08:00
parent 1f9b6553fc
commit 5599c1a4db
15 changed files with 3216 additions and 296 deletions

View File

@@ -99,6 +99,12 @@ const mockClient = {
{ id: 'wf_2', name: 'Report Generator', steps: 5 },
],
})),
listWorkflowRuns: vi.fn(async (workflowId: string, opts?: { limit?: number; offset?: number }) => ({
runs: [
{ runId: 'run_wf1_001', status: 'completed', startedAt: '2026-03-14T10:00:00Z', completedAt: '2026-03-14T10:05:00Z' },
{ runId: 'run_wf1_002', status: 'running', startedAt: '2026-03-14T11:00:00Z' },
],
})),
executeWorkflow: vi.fn(async (id: string, input?: Record<string, unknown>) => ({
runId: `wfrun_${id}_${Date.now()}`,
status: 'running',
@@ -231,6 +237,7 @@ function resetClientMocks() {
mockClient.listHands.mockReset();
mockClient.triggerHand.mockReset();
mockClient.listWorkflows.mockReset();
mockClient.listWorkflowRuns.mockReset();
mockClient.executeWorkflow.mockReset();
mockClient.listTriggers.mockReset();
mockClient.getAuditLogs.mockReset();
@@ -470,8 +477,28 @@ describe('OpenFang actions', () => {
expect(mockClient.listHands).toHaveBeenCalledTimes(1);
expect(useGatewayStore.getState().hands).toEqual([
{ name: 'echo', description: 'Echo handler', status: 'active' },
{ name: 'notify', description: 'Notification handler', status: 'active' },
{
id: 'echo',
name: 'echo',
description: 'Echo handler',
status: 'active',
requirements_met: undefined,
category: undefined,
icon: undefined,
toolCount: undefined,
metricCount: undefined,
},
{
id: 'notify',
name: 'notify',
description: 'Notification handler',
status: 'active',
requirements_met: undefined,
category: undefined,
icon: undefined,
toolCount: undefined,
metricCount: undefined,
},
]);
});