@nexus-ai-fs/tui 0.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/package.json +48 -0
- package/src/app.tsx +349 -0
- package/src/index.tsx +137 -0
- package/src/opentui-env.d.ts +61 -0
- package/src/panels/access/access-panel.tsx +597 -0
- package/src/panels/access/alert-list.tsx +77 -0
- package/src/panels/access/constraint-creator.tsx +128 -0
- package/src/panels/access/constraint-list.tsx +72 -0
- package/src/panels/access/credential-list.tsx +68 -0
- package/src/panels/access/delegation-chain-view.tsx +110 -0
- package/src/panels/access/delegation-completer.tsx +120 -0
- package/src/panels/access/delegation-creator.tsx +237 -0
- package/src/panels/access/delegation-list.tsx +74 -0
- package/src/panels/access/fraud-score-view.tsx +94 -0
- package/src/panels/access/manifest-creator.tsx +167 -0
- package/src/panels/access/manifest-list.tsx +105 -0
- package/src/panels/access/namespace-config-view.tsx +525 -0
- package/src/panels/access/permission-checker.tsx +231 -0
- package/src/panels/agents/agent-status-view.tsx +196 -0
- package/src/panels/agents/agents-panel.tsx +493 -0
- package/src/panels/agents/delegation-list.tsx +154 -0
- package/src/panels/agents/inbox-view.tsx +96 -0
- package/src/panels/agents/trajectories-tab.tsx +40 -0
- package/src/panels/api-console/api-console-panel.tsx +189 -0
- package/src/panels/api-console/codegen-viewer.tsx +36 -0
- package/src/panels/api-console/codegen.ts +112 -0
- package/src/panels/api-console/endpoint-list.tsx +57 -0
- package/src/panels/api-console/request-builder.tsx +69 -0
- package/src/panels/api-console/response-viewer.tsx +54 -0
- package/src/panels/connectors/available-tab.tsx +357 -0
- package/src/panels/connectors/connector-row.tsx +121 -0
- package/src/panels/connectors/connectors-panel.tsx +88 -0
- package/src/panels/connectors/error-parser.ts +116 -0
- package/src/panels/connectors/mounted-tab.tsx +179 -0
- package/src/panels/connectors/skills-tab.tsx +235 -0
- package/src/panels/connectors/template-generator.ts +211 -0
- package/src/panels/connectors/write-tab.tsx +514 -0
- package/src/panels/events/audit-tab.tsx +69 -0
- package/src/panels/events/audit-trail.tsx +75 -0
- package/src/panels/events/connector-detail.tsx +49 -0
- package/src/panels/events/connector-list.tsx +73 -0
- package/src/panels/events/connectors-tab.tsx +92 -0
- package/src/panels/events/event-replay.tsx +80 -0
- package/src/panels/events/events-panel.tsx +414 -0
- package/src/panels/events/events-tab.tsx +212 -0
- package/src/panels/events/lock-list.tsx +54 -0
- package/src/panels/events/locks-tab.tsx +103 -0
- package/src/panels/events/mcl-replay.tsx +77 -0
- package/src/panels/events/mcl-tab.tsx +83 -0
- package/src/panels/events/operations-tab-wrapper.tsx +62 -0
- package/src/panels/events/operations-tab.tsx +41 -0
- package/src/panels/events/replay-tab.tsx +76 -0
- package/src/panels/events/secrets-audit.tsx +64 -0
- package/src/panels/events/secrets-tab.tsx +75 -0
- package/src/panels/events/subscription-list.tsx +54 -0
- package/src/panels/events/subscriptions-tab.tsx +82 -0
- package/src/panels/files/file-aspects.tsx +93 -0
- package/src/panels/files/file-editor.tsx +160 -0
- package/src/panels/files/file-explorer-keybindings.ts +468 -0
- package/src/panels/files/file-explorer-panel.tsx +545 -0
- package/src/panels/files/file-lineage.tsx +163 -0
- package/src/panels/files/file-list-item.tsx +28 -0
- package/src/panels/files/file-metadata.tsx +62 -0
- package/src/panels/files/file-preview.tsx +108 -0
- package/src/panels/files/file-schema.tsx +89 -0
- package/src/panels/files/file-tree-node.tsx +44 -0
- package/src/panels/files/file-tree.tsx +169 -0
- package/src/panels/files/share-links-tab.tsx +33 -0
- package/src/panels/files/uploads-tab.tsx +45 -0
- package/src/panels/payments/approval-list.tsx +83 -0
- package/src/panels/payments/balance-card.tsx +43 -0
- package/src/panels/payments/budget-card.tsx +70 -0
- package/src/panels/payments/payments-panel.tsx +451 -0
- package/src/panels/payments/policy-list.tsx +64 -0
- package/src/panels/payments/reservation-list.tsx +78 -0
- package/src/panels/payments/transaction-list.tsx +103 -0
- package/src/panels/payments/transfer-form.tsx +109 -0
- package/src/panels/search/column-search.tsx +79 -0
- package/src/panels/search/knowledge-view.tsx +100 -0
- package/src/panels/search/memory-list.tsx +197 -0
- package/src/panels/search/playbook-list.tsx +77 -0
- package/src/panels/search/rlm-answer-view.tsx +105 -0
- package/src/panels/search/search-panel.tsx +405 -0
- package/src/panels/search/search-results.tsx +116 -0
- package/src/panels/stack/stack-panel.tsx +474 -0
- package/src/panels/versions/conflicts-tab.tsx +59 -0
- package/src/panels/versions/entry-detail.tsx +89 -0
- package/src/panels/versions/transaction-actions.tsx +34 -0
- package/src/panels/versions/transaction-list.tsx +90 -0
- package/src/panels/versions/versions-panel.tsx +276 -0
- package/src/panels/workflows/execution-list.tsx +102 -0
- package/src/panels/workflows/scheduler-view.tsx +135 -0
- package/src/panels/workflows/workflow-list.tsx +88 -0
- package/src/panels/workflows/workflows-panel.tsx +295 -0
- package/src/panels/zones/brick-detail.tsx +136 -0
- package/src/panels/zones/brick-list.tsx +56 -0
- package/src/panels/zones/cache-tab.tsx +118 -0
- package/src/panels/zones/drift-view.tsx +97 -0
- package/src/panels/zones/mcp-mounts-tab.tsx +38 -0
- package/src/panels/zones/memories-tab.tsx +37 -0
- package/src/panels/zones/reindex-status.tsx +84 -0
- package/src/panels/zones/workspaces-tab.tsx +37 -0
- package/src/panels/zones/zone-list.tsx +73 -0
- package/src/panels/zones/zones-panel.tsx +559 -0
- package/src/services/command-runner.ts +303 -0
- package/src/shared/accessibility-announcements.ts +44 -0
- package/src/shared/action-registry.ts +466 -0
- package/src/shared/brick-states.ts +91 -0
- package/src/shared/command-palette.ts +35 -0
- package/src/shared/components/announcement-bar.tsx +30 -0
- package/src/shared/components/app-confirm-dialog.tsx +29 -0
- package/src/shared/components/breadcrumb.tsx +21 -0
- package/src/shared/components/brick-gate.tsx +60 -0
- package/src/shared/components/command-output.tsx +95 -0
- package/src/shared/components/command-palette.tsx +97 -0
- package/src/shared/components/confirm-dialog.tsx +61 -0
- package/src/shared/components/diff-viewer.tsx +219 -0
- package/src/shared/components/empty-state.tsx +36 -0
- package/src/shared/components/error-bar.tsx +60 -0
- package/src/shared/components/error-boundary.tsx +53 -0
- package/src/shared/components/help-overlay.tsx +99 -0
- package/src/shared/components/identity-switcher.tsx +168 -0
- package/src/shared/components/loading-indicator.tsx +40 -0
- package/src/shared/components/pagination-bar.tsx +68 -0
- package/src/shared/components/pre-connection-screen.tsx +398 -0
- package/src/shared/components/scroll-indicator.tsx +46 -0
- package/src/shared/components/side-nav-utils.ts +68 -0
- package/src/shared/components/side-nav.tsx +287 -0
- package/src/shared/components/spinner.tsx +26 -0
- package/src/shared/components/status-bar.tsx +117 -0
- package/src/shared/components/styled-text.tsx +72 -0
- package/src/shared/components/sub-tab-bar-utils.ts +100 -0
- package/src/shared/components/sub-tab-bar.tsx +40 -0
- package/src/shared/components/tab-bar-utils.ts +36 -0
- package/src/shared/components/tab-bar.tsx +50 -0
- package/src/shared/components/text-input.tsx +73 -0
- package/src/shared/components/tooltip.tsx +53 -0
- package/src/shared/components/virtual-list.tsx +93 -0
- package/src/shared/components/welcome-screen.tsx +111 -0
- package/src/shared/hooks/use-api.ts +10 -0
- package/src/shared/hooks/use-brick-available.ts +42 -0
- package/src/shared/hooks/use-confirm.ts +66 -0
- package/src/shared/hooks/use-connection-state.ts +67 -0
- package/src/shared/hooks/use-copy.ts +31 -0
- package/src/shared/hooks/use-fresh-server.ts +62 -0
- package/src/shared/hooks/use-keyboard.ts +58 -0
- package/src/shared/hooks/use-list-navigation.ts +106 -0
- package/src/shared/hooks/use-swr.ts +117 -0
- package/src/shared/hooks/use-tab-fallback.ts +32 -0
- package/src/shared/hooks/use-text-input.ts +113 -0
- package/src/shared/hooks/use-visible-tabs.ts +61 -0
- package/src/shared/lib/circular-buffer.ts +82 -0
- package/src/shared/lib/clipboard.ts +14 -0
- package/src/shared/nav-items.ts +73 -0
- package/src/shared/navigation.ts +110 -0
- package/src/shared/status-breadcrumb.ts +74 -0
- package/src/shared/syntax-style.ts +3 -0
- package/src/shared/tab-visibility.ts +15 -0
- package/src/shared/text-style.ts +23 -0
- package/src/shared/theme.ts +179 -0
- package/src/shared/utils/format-size.ts +20 -0
- package/src/shared/utils/format-text.ts +10 -0
- package/src/shared/utils/format-time.ts +72 -0
- package/src/shared/utils/lru-cache.ts +75 -0
- package/src/stores/access-store-types.ts +154 -0
- package/src/stores/access-store.ts +674 -0
- package/src/stores/agents-store.ts +404 -0
- package/src/stores/announcement-store.ts +46 -0
- package/src/stores/api-console-store.ts +476 -0
- package/src/stores/connectors-store.ts +434 -0
- package/src/stores/create-api-action.ts +140 -0
- package/src/stores/delegation-store.ts +300 -0
- package/src/stores/error-store.ts +102 -0
- package/src/stores/events-store.ts +163 -0
- package/src/stores/files-store.ts +630 -0
- package/src/stores/first-run-store.ts +34 -0
- package/src/stores/global-store.ts +255 -0
- package/src/stores/infra-store.ts +461 -0
- package/src/stores/knowledge-store.ts +358 -0
- package/src/stores/lineage-store.ts +126 -0
- package/src/stores/mcp-store.ts +147 -0
- package/src/stores/payments-store.ts +545 -0
- package/src/stores/search-store-types.ts +155 -0
- package/src/stores/search-store.ts +656 -0
- package/src/stores/share-link-store.ts +151 -0
- package/src/stores/stack-store.ts +352 -0
- package/src/stores/ui-store.ts +161 -0
- package/src/stores/upload-store.ts +131 -0
- package/src/stores/versions-store.ts +355 -0
- package/src/stores/workflows-store.ts +402 -0
- package/src/stores/workspace-store.ts +185 -0
- package/src/stores/zones-store.ts +378 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand store for Workflows & Automation panel.
|
|
3
|
+
*
|
|
4
|
+
* Manages workflows, executions, and scheduler metrics
|
|
5
|
+
* across a tabbed interface.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { create } from "zustand";
|
|
9
|
+
import type { FetchClient } from "@nexus-ai-fs/api-client";
|
|
10
|
+
import { createApiAction, categorizeError } from "./create-api-action.js";
|
|
11
|
+
import { useErrorStore } from "./error-store.js";
|
|
12
|
+
import { useUiStore } from "./ui-store.js";
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Types (snake_case matching API wire format)
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
export interface WorkflowSummary {
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly version: string;
|
|
21
|
+
readonly description: string | null;
|
|
22
|
+
readonly enabled: boolean;
|
|
23
|
+
readonly triggers: number;
|
|
24
|
+
readonly actions: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface WorkflowDetail {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly version: string;
|
|
30
|
+
readonly description: string | null;
|
|
31
|
+
readonly triggers: readonly WorkflowTriggerModel[];
|
|
32
|
+
readonly actions: readonly WorkflowActionModel[];
|
|
33
|
+
readonly variables: Readonly<Record<string, unknown>>;
|
|
34
|
+
readonly enabled: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface WorkflowTriggerModel {
|
|
38
|
+
readonly [key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface WorkflowActionModel {
|
|
42
|
+
readonly [key: string]: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ExecutionSummary {
|
|
46
|
+
readonly execution_id: string;
|
|
47
|
+
readonly workflow_id: string;
|
|
48
|
+
readonly trigger_type: string;
|
|
49
|
+
readonly status: string;
|
|
50
|
+
readonly started_at: string | null;
|
|
51
|
+
readonly completed_at: string | null;
|
|
52
|
+
readonly actions_completed: number;
|
|
53
|
+
readonly actions_total: number;
|
|
54
|
+
readonly error_message: string | null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ExecutionResult {
|
|
58
|
+
readonly execution_id: string;
|
|
59
|
+
readonly status: string;
|
|
60
|
+
readonly actions_completed: number;
|
|
61
|
+
readonly actions_total: number;
|
|
62
|
+
readonly error_message: string | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ExecutionStep {
|
|
66
|
+
readonly step_index: number;
|
|
67
|
+
readonly action_name: string;
|
|
68
|
+
readonly status: string;
|
|
69
|
+
readonly started_at: string | null;
|
|
70
|
+
readonly completed_at: string | null;
|
|
71
|
+
readonly inputs: Readonly<Record<string, unknown>>;
|
|
72
|
+
readonly outputs: Readonly<Record<string, unknown>>;
|
|
73
|
+
readonly error_message: string | null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ExecutionDetail {
|
|
77
|
+
readonly execution_id: string;
|
|
78
|
+
readonly workflow_id: string;
|
|
79
|
+
readonly trigger_type: string;
|
|
80
|
+
readonly status: string;
|
|
81
|
+
readonly started_at: string | null;
|
|
82
|
+
readonly completed_at: string | null;
|
|
83
|
+
readonly actions_completed: number;
|
|
84
|
+
readonly actions_total: number;
|
|
85
|
+
readonly error_message: string | null;
|
|
86
|
+
readonly steps: readonly ExecutionStep[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface SchedulerMetrics {
|
|
90
|
+
readonly queued_tasks: number;
|
|
91
|
+
readonly running_tasks: number;
|
|
92
|
+
readonly completed_tasks: number;
|
|
93
|
+
readonly failed_tasks: number;
|
|
94
|
+
readonly avg_wait_ms: number;
|
|
95
|
+
readonly avg_duration_ms: number;
|
|
96
|
+
readonly throughput_per_minute: number;
|
|
97
|
+
// Raw Astraea API fields
|
|
98
|
+
readonly queue_by_class?: readonly { priority_class: string; count: number }[];
|
|
99
|
+
readonly fair_share?: Record<string, unknown>;
|
|
100
|
+
readonly use_hrrn?: boolean;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// Tab type
|
|
105
|
+
// =============================================================================
|
|
106
|
+
|
|
107
|
+
export type WorkflowTab = "workflows" | "executions" | "scheduler";
|
|
108
|
+
|
|
109
|
+
// =============================================================================
|
|
110
|
+
// Store
|
|
111
|
+
// =============================================================================
|
|
112
|
+
|
|
113
|
+
export interface WorkflowsState {
|
|
114
|
+
// Workflow list
|
|
115
|
+
readonly workflows: readonly WorkflowSummary[];
|
|
116
|
+
readonly selectedWorkflowIndex: number;
|
|
117
|
+
readonly workflowsLoading: boolean;
|
|
118
|
+
|
|
119
|
+
// Selected workflow detail
|
|
120
|
+
readonly selectedWorkflow: WorkflowDetail | null;
|
|
121
|
+
readonly detailLoading: boolean;
|
|
122
|
+
|
|
123
|
+
// Executions
|
|
124
|
+
readonly executions: readonly ExecutionSummary[];
|
|
125
|
+
readonly selectedExecutionIndex: number;
|
|
126
|
+
readonly executionsLoading: boolean;
|
|
127
|
+
|
|
128
|
+
// Execution detail (expanded view)
|
|
129
|
+
readonly selectedExecution: ExecutionDetail | null;
|
|
130
|
+
readonly executionDetailLoading: boolean;
|
|
131
|
+
|
|
132
|
+
// Scheduler metrics
|
|
133
|
+
readonly schedulerMetrics: SchedulerMetrics | null;
|
|
134
|
+
readonly schedulerLoading: boolean;
|
|
135
|
+
|
|
136
|
+
// Tab and error
|
|
137
|
+
readonly activeTab: WorkflowTab;
|
|
138
|
+
readonly error: string | null;
|
|
139
|
+
|
|
140
|
+
// Actions
|
|
141
|
+
readonly fetchWorkflows: (client: FetchClient) => Promise<void>;
|
|
142
|
+
readonly fetchWorkflowDetail: (name: string, client: FetchClient) => Promise<void>;
|
|
143
|
+
readonly executeWorkflow: (name: string, client: FetchClient) => Promise<void>;
|
|
144
|
+
readonly fetchExecutions: (workflowName: string, client: FetchClient) => Promise<void>;
|
|
145
|
+
readonly fetchSchedulerMetrics: (client: FetchClient) => Promise<void>;
|
|
146
|
+
readonly createWorkflow: (name: string, description: string, client: FetchClient) => Promise<void>;
|
|
147
|
+
readonly deleteWorkflow: (name: string, client: FetchClient) => Promise<void>;
|
|
148
|
+
readonly enableWorkflow: (name: string, client: FetchClient) => Promise<void>;
|
|
149
|
+
readonly disableWorkflow: (name: string, client: FetchClient) => Promise<void>;
|
|
150
|
+
readonly fetchExecutionDetail: (executionId: string, client: FetchClient) => Promise<void>;
|
|
151
|
+
readonly clearExecutionDetail: () => void;
|
|
152
|
+
readonly setActiveTab: (tab: WorkflowTab) => void;
|
|
153
|
+
readonly setSelectedWorkflowIndex: (index: number) => void;
|
|
154
|
+
readonly setSelectedExecutionIndex: (index: number) => void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const SOURCE = "workflows";
|
|
158
|
+
|
|
159
|
+
export const useWorkflowsStore = create<WorkflowsState>((set, get) => ({
|
|
160
|
+
workflows: [],
|
|
161
|
+
selectedWorkflowIndex: 0,
|
|
162
|
+
workflowsLoading: false,
|
|
163
|
+
|
|
164
|
+
selectedWorkflow: null,
|
|
165
|
+
detailLoading: false,
|
|
166
|
+
|
|
167
|
+
executions: [],
|
|
168
|
+
selectedExecutionIndex: 0,
|
|
169
|
+
executionsLoading: false,
|
|
170
|
+
|
|
171
|
+
selectedExecution: null,
|
|
172
|
+
executionDetailLoading: false,
|
|
173
|
+
|
|
174
|
+
schedulerMetrics: null,
|
|
175
|
+
schedulerLoading: false,
|
|
176
|
+
|
|
177
|
+
activeTab: "workflows",
|
|
178
|
+
error: null,
|
|
179
|
+
|
|
180
|
+
// =========================================================================
|
|
181
|
+
// Actions migrated to createApiAction (Decision 6A)
|
|
182
|
+
// =========================================================================
|
|
183
|
+
|
|
184
|
+
fetchWorkflows: createApiAction<WorkflowsState, [FetchClient]>(set, {
|
|
185
|
+
loadingKey: "workflowsLoading",
|
|
186
|
+
source: SOURCE,
|
|
187
|
+
errorMessage: "Failed to fetch workflows",
|
|
188
|
+
action: async (client) => {
|
|
189
|
+
const workflows = await client.get<readonly WorkflowSummary[]>(
|
|
190
|
+
"/api/v2/workflows",
|
|
191
|
+
);
|
|
192
|
+
return { workflows: workflows ?? [] };
|
|
193
|
+
},
|
|
194
|
+
}),
|
|
195
|
+
|
|
196
|
+
fetchWorkflowDetail: async (name, client) => {
|
|
197
|
+
set({ detailLoading: true, error: null });
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const workflow = await client.get<WorkflowDetail>(
|
|
201
|
+
`/api/v2/workflows/${encodeURIComponent(name)}`,
|
|
202
|
+
);
|
|
203
|
+
set({ selectedWorkflow: workflow, detailLoading: false });
|
|
204
|
+
useUiStore.getState().markDataUpdated("workflows");
|
|
205
|
+
} catch (err) {
|
|
206
|
+
const message = err instanceof Error ? err.message : "Failed to fetch workflow detail";
|
|
207
|
+
set({
|
|
208
|
+
selectedWorkflow: null,
|
|
209
|
+
detailLoading: false,
|
|
210
|
+
error: message,
|
|
211
|
+
});
|
|
212
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
// =========================================================================
|
|
217
|
+
// Actions without loading keys — inline with error store integration
|
|
218
|
+
// =========================================================================
|
|
219
|
+
|
|
220
|
+
executeWorkflow: async (name, client) => {
|
|
221
|
+
set({ error: null });
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
await client.post<ExecutionResult>(
|
|
225
|
+
`/api/v2/workflows/${encodeURIComponent(name)}/execute`,
|
|
226
|
+
{},
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// Refresh executions for this workflow after execution
|
|
230
|
+
const { fetchExecutions } = get();
|
|
231
|
+
await fetchExecutions(name, client);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
const message = err instanceof Error ? err.message : "Failed to execute workflow";
|
|
234
|
+
set({ error: message });
|
|
235
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
fetchExecutions: async (workflowName, client) => {
|
|
240
|
+
set({ executionsLoading: true, error: null });
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const executions = await client.get<readonly ExecutionSummary[]>(
|
|
244
|
+
`/api/v2/workflows/${encodeURIComponent(workflowName)}/executions?limit=10`,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
set({ executions: executions ?? [], executionsLoading: false });
|
|
248
|
+
useUiStore.getState().markDataUpdated("workflows");
|
|
249
|
+
} catch (err) {
|
|
250
|
+
const message = err instanceof Error ? err.message : "Failed to fetch executions";
|
|
251
|
+
set({
|
|
252
|
+
executions: [],
|
|
253
|
+
executionsLoading: false,
|
|
254
|
+
error: message,
|
|
255
|
+
});
|
|
256
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
fetchSchedulerMetrics: async (client) => {
|
|
261
|
+
set({ schedulerLoading: true, error: null });
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const raw = await client.get<Record<string, unknown>>(
|
|
265
|
+
"/api/v2/scheduler/metrics",
|
|
266
|
+
);
|
|
267
|
+
// Normalize Astraea response → SchedulerMetrics
|
|
268
|
+
const queueByClass = (raw.queue_by_class ?? raw.queueByClass ?? []) as readonly { count: number }[];
|
|
269
|
+
const totalQueued = queueByClass.reduce((sum, c) => sum + (c.count ?? 0), 0);
|
|
270
|
+
const metrics: SchedulerMetrics = {
|
|
271
|
+
queued_tasks: totalQueued,
|
|
272
|
+
running_tasks: (raw.running_tasks ?? raw.runningTasks ?? 0) as number,
|
|
273
|
+
completed_tasks: (raw.completed_tasks ?? raw.completedTasks ?? 0) as number,
|
|
274
|
+
failed_tasks: (raw.failed_tasks ?? raw.failedTasks ?? 0) as number,
|
|
275
|
+
avg_wait_ms: (raw.avg_wait_ms ?? raw.avgWaitMs ?? 0) as number,
|
|
276
|
+
avg_duration_ms: (raw.avg_duration_ms ?? raw.avgDurationMs ?? 0) as number,
|
|
277
|
+
throughput_per_minute: (raw.throughput_per_minute ?? raw.throughputPerMinute ?? 0) as number,
|
|
278
|
+
queue_by_class: queueByClass as SchedulerMetrics["queue_by_class"],
|
|
279
|
+
fair_share: (raw.fair_share ?? raw.fairShare ?? {}) as Record<string, unknown>,
|
|
280
|
+
use_hrrn: (raw.use_hrrn ?? raw.useHrrn ?? false) as boolean,
|
|
281
|
+
};
|
|
282
|
+
set({ schedulerMetrics: metrics, schedulerLoading: false });
|
|
283
|
+
useUiStore.getState().markDataUpdated("workflows");
|
|
284
|
+
} catch (err) {
|
|
285
|
+
const message = err instanceof Error ? err.message : "Failed to fetch scheduler metrics";
|
|
286
|
+
set({
|
|
287
|
+
schedulerMetrics: null,
|
|
288
|
+
schedulerLoading: false,
|
|
289
|
+
error: message,
|
|
290
|
+
});
|
|
291
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
createWorkflow: async (name, description, client) => {
|
|
296
|
+
set({ error: null });
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
await client.post("/api/v2/workflows", { name, description });
|
|
300
|
+
await get().fetchWorkflows(client);
|
|
301
|
+
} catch (err) {
|
|
302
|
+
const message = err instanceof Error ? err.message : "Failed to create workflow";
|
|
303
|
+
set({ error: message });
|
|
304
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
deleteWorkflow: async (name, client) => {
|
|
309
|
+
set({ error: null });
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
await client.delete(`/api/v2/workflows/${encodeURIComponent(name)}`);
|
|
313
|
+
set((state) => ({
|
|
314
|
+
workflows: state.workflows.filter((w) => w.name !== name),
|
|
315
|
+
selectedWorkflowIndex: Math.min(
|
|
316
|
+
state.selectedWorkflowIndex,
|
|
317
|
+
Math.max(state.workflows.length - 2, 0),
|
|
318
|
+
),
|
|
319
|
+
}));
|
|
320
|
+
} catch (err) {
|
|
321
|
+
const message = err instanceof Error ? err.message : "Failed to delete workflow";
|
|
322
|
+
set({ error: message });
|
|
323
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
enableWorkflow: async (name, client) => {
|
|
328
|
+
set({ error: null });
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
await client.post(
|
|
332
|
+
`/api/v2/workflows/${encodeURIComponent(name)}/enable`,
|
|
333
|
+
{},
|
|
334
|
+
);
|
|
335
|
+
set((state) => ({
|
|
336
|
+
workflows: state.workflows.map((w) =>
|
|
337
|
+
w.name === name ? { ...w, enabled: true } : w,
|
|
338
|
+
),
|
|
339
|
+
}));
|
|
340
|
+
} catch (err) {
|
|
341
|
+
const message = err instanceof Error ? err.message : "Failed to enable workflow";
|
|
342
|
+
set({ error: message });
|
|
343
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
disableWorkflow: async (name, client) => {
|
|
348
|
+
set({ error: null });
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
await client.post(
|
|
352
|
+
`/api/v2/workflows/${encodeURIComponent(name)}/disable`,
|
|
353
|
+
{},
|
|
354
|
+
);
|
|
355
|
+
set((state) => ({
|
|
356
|
+
workflows: state.workflows.map((w) =>
|
|
357
|
+
w.name === name ? { ...w, enabled: false } : w,
|
|
358
|
+
),
|
|
359
|
+
}));
|
|
360
|
+
} catch (err) {
|
|
361
|
+
const message = err instanceof Error ? err.message : "Failed to disable workflow";
|
|
362
|
+
set({ error: message });
|
|
363
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
fetchExecutionDetail: async (executionId, client) => {
|
|
368
|
+
set({ executionDetailLoading: true, error: null });
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
const detail = await client.get<ExecutionDetail>(
|
|
372
|
+
`/api/v2/workflows/executions/${encodeURIComponent(executionId)}`,
|
|
373
|
+
);
|
|
374
|
+
set({ selectedExecution: detail, executionDetailLoading: false });
|
|
375
|
+
useUiStore.getState().markDataUpdated("workflows");
|
|
376
|
+
} catch (err) {
|
|
377
|
+
const message = err instanceof Error ? err.message : "Failed to fetch execution detail";
|
|
378
|
+
set({
|
|
379
|
+
selectedExecution: null,
|
|
380
|
+
executionDetailLoading: false,
|
|
381
|
+
error: message,
|
|
382
|
+
});
|
|
383
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
clearExecutionDetail: () => {
|
|
388
|
+
set({ selectedExecution: null });
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
setActiveTab: (tab) => {
|
|
392
|
+
set({ activeTab: tab, error: null });
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
setSelectedWorkflowIndex: (index) => {
|
|
396
|
+
set({ selectedWorkflowIndex: index });
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
setSelectedExecutionIndex: (index) => {
|
|
400
|
+
set({ selectedExecutionIndex: index });
|
|
401
|
+
},
|
|
402
|
+
}));
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store for workspace and memory directory management.
|
|
3
|
+
*
|
|
4
|
+
* Workspaces use REST API at /api/v2/registry/workspaces (#2987, merged).
|
|
5
|
+
* Memory directories use JSON-RPC via /api/nfs/ (register_workspace with
|
|
6
|
+
* memory-specific params).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { create } from "zustand";
|
|
10
|
+
import type { FetchClient } from "@nexus-ai-fs/api-client";
|
|
11
|
+
import { createApiAction, categorizeError } from "./create-api-action.js";
|
|
12
|
+
import { useErrorStore } from "./error-store.js";
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Types (snake_case matching API wire format)
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
export interface WorkspaceInfo {
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly description: string | null;
|
|
22
|
+
readonly scope: "persistent" | "session";
|
|
23
|
+
readonly ttl_seconds: number | null;
|
|
24
|
+
readonly created_by: string | null;
|
|
25
|
+
readonly created_at: string | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface MemoryInfo {
|
|
29
|
+
readonly path: string;
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly description: string | null;
|
|
32
|
+
readonly scope: "persistent" | "session";
|
|
33
|
+
readonly created_by: string | null;
|
|
34
|
+
readonly created_at: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// Store
|
|
39
|
+
// =============================================================================
|
|
40
|
+
|
|
41
|
+
export interface WorkspaceState {
|
|
42
|
+
readonly workspaces: readonly WorkspaceInfo[];
|
|
43
|
+
readonly workspacesLoading: boolean;
|
|
44
|
+
readonly selectedWorkspaceIndex: number;
|
|
45
|
+
readonly memories: readonly MemoryInfo[];
|
|
46
|
+
readonly memoriesLoading: boolean;
|
|
47
|
+
readonly selectedMemoryIndex: number;
|
|
48
|
+
readonly error: string | null;
|
|
49
|
+
|
|
50
|
+
readonly fetchWorkspaces: (client: FetchClient) => Promise<void>;
|
|
51
|
+
readonly registerWorkspace: (
|
|
52
|
+
params: {
|
|
53
|
+
path: string;
|
|
54
|
+
name: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
scope?: string;
|
|
57
|
+
ttl_seconds?: number;
|
|
58
|
+
},
|
|
59
|
+
client: FetchClient,
|
|
60
|
+
) => Promise<void>;
|
|
61
|
+
readonly unregisterWorkspace: (path: string, client: FetchClient) => Promise<void>;
|
|
62
|
+
readonly fetchMemories: (client: FetchClient) => Promise<void>;
|
|
63
|
+
readonly registerMemory: (
|
|
64
|
+
params: { path: string; name: string; description?: string },
|
|
65
|
+
client: FetchClient,
|
|
66
|
+
) => Promise<void>;
|
|
67
|
+
readonly unregisterMemory: (path: string, client: FetchClient) => Promise<void>;
|
|
68
|
+
readonly setSelectedWorkspaceIndex: (index: number) => void;
|
|
69
|
+
readonly setSelectedMemoryIndex: (index: number) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const SOURCE = "workspaces";
|
|
73
|
+
|
|
74
|
+
export const useWorkspaceStore = create<WorkspaceState>((set, get) => ({
|
|
75
|
+
workspaces: [],
|
|
76
|
+
workspacesLoading: false,
|
|
77
|
+
selectedWorkspaceIndex: 0,
|
|
78
|
+
memories: [],
|
|
79
|
+
memoriesLoading: false,
|
|
80
|
+
selectedMemoryIndex: 0,
|
|
81
|
+
error: null,
|
|
82
|
+
|
|
83
|
+
// =========================================================================
|
|
84
|
+
// Actions with loading keys — createApiAction
|
|
85
|
+
// =========================================================================
|
|
86
|
+
|
|
87
|
+
fetchWorkspaces: createApiAction<WorkspaceState, [FetchClient]>(set, {
|
|
88
|
+
loadingKey: "workspacesLoading",
|
|
89
|
+
source: SOURCE,
|
|
90
|
+
errorMessage: "Failed to fetch workspaces",
|
|
91
|
+
action: async (client) => {
|
|
92
|
+
const response = await client.get<{
|
|
93
|
+
items: readonly WorkspaceInfo[];
|
|
94
|
+
}>("/api/v2/registry/workspaces");
|
|
95
|
+
return {
|
|
96
|
+
workspaces: response.items ?? [],
|
|
97
|
+
selectedWorkspaceIndex: 0,
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
|
|
102
|
+
fetchMemories: createApiAction<WorkspaceState, [FetchClient]>(set, {
|
|
103
|
+
loadingKey: "memoriesLoading",
|
|
104
|
+
source: SOURCE,
|
|
105
|
+
errorMessage: "Failed to fetch memories",
|
|
106
|
+
action: async (client) => {
|
|
107
|
+
const response = await client.post<{
|
|
108
|
+
result: { memories: readonly MemoryInfo[] };
|
|
109
|
+
}>("/api/nfs/list_workspaces", { params: { type: "memory" } });
|
|
110
|
+
return {
|
|
111
|
+
memories: response.result?.memories ?? [],
|
|
112
|
+
selectedMemoryIndex: 0,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
|
|
117
|
+
// =========================================================================
|
|
118
|
+
// Actions without loading keys — inline with error store integration
|
|
119
|
+
// =========================================================================
|
|
120
|
+
|
|
121
|
+
registerWorkspace: async (params, client) => {
|
|
122
|
+
set({ error: null });
|
|
123
|
+
try {
|
|
124
|
+
await client.post("/api/v2/registry/workspaces", params);
|
|
125
|
+
await get().fetchWorkspaces(client);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
const message = err instanceof Error ? err.message : "Failed to register workspace";
|
|
128
|
+
set({ error: message });
|
|
129
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
unregisterWorkspace: async (path, client) => {
|
|
134
|
+
set({ error: null });
|
|
135
|
+
try {
|
|
136
|
+
await client.delete(
|
|
137
|
+
`/api/v2/registry/workspaces/${encodeURIComponent(path)}`,
|
|
138
|
+
);
|
|
139
|
+
set((state) => ({
|
|
140
|
+
workspaces: state.workspaces.filter((w) => w.path !== path),
|
|
141
|
+
selectedWorkspaceIndex: Math.min(
|
|
142
|
+
state.selectedWorkspaceIndex,
|
|
143
|
+
Math.max(state.workspaces.length - 2, 0),
|
|
144
|
+
),
|
|
145
|
+
}));
|
|
146
|
+
} catch (err) {
|
|
147
|
+
const message = err instanceof Error ? err.message : "Failed to unregister workspace";
|
|
148
|
+
set({ error: message });
|
|
149
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
registerMemory: async (params, client) => {
|
|
154
|
+
set({ error: null });
|
|
155
|
+
try {
|
|
156
|
+
await client.post("/api/nfs/register_workspace", { params: { ...params, type: "memory" } });
|
|
157
|
+
await get().fetchMemories(client);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
const message = err instanceof Error ? err.message : "Failed to register memory";
|
|
160
|
+
set({ error: message });
|
|
161
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
unregisterMemory: async (path, client) => {
|
|
166
|
+
set({ error: null });
|
|
167
|
+
try {
|
|
168
|
+
await client.post("/api/nfs/unregister_workspace", { params: { path } });
|
|
169
|
+
set((state) => ({
|
|
170
|
+
memories: state.memories.filter((m) => m.path !== path),
|
|
171
|
+
selectedMemoryIndex: Math.min(
|
|
172
|
+
state.selectedMemoryIndex,
|
|
173
|
+
Math.max(state.memories.length - 2, 0),
|
|
174
|
+
),
|
|
175
|
+
}));
|
|
176
|
+
} catch (err) {
|
|
177
|
+
const message = err instanceof Error ? err.message : "Failed to unregister memory";
|
|
178
|
+
set({ error: message });
|
|
179
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
setSelectedWorkspaceIndex: (index) => set({ selectedWorkspaceIndex: index }),
|
|
184
|
+
setSelectedMemoryIndex: (index) => set({ selectedMemoryIndex: index }),
|
|
185
|
+
}));
|