@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.
Files changed (193) hide show
  1. package/README.md +30 -0
  2. package/package.json +48 -0
  3. package/src/app.tsx +349 -0
  4. package/src/index.tsx +137 -0
  5. package/src/opentui-env.d.ts +61 -0
  6. package/src/panels/access/access-panel.tsx +597 -0
  7. package/src/panels/access/alert-list.tsx +77 -0
  8. package/src/panels/access/constraint-creator.tsx +128 -0
  9. package/src/panels/access/constraint-list.tsx +72 -0
  10. package/src/panels/access/credential-list.tsx +68 -0
  11. package/src/panels/access/delegation-chain-view.tsx +110 -0
  12. package/src/panels/access/delegation-completer.tsx +120 -0
  13. package/src/panels/access/delegation-creator.tsx +237 -0
  14. package/src/panels/access/delegation-list.tsx +74 -0
  15. package/src/panels/access/fraud-score-view.tsx +94 -0
  16. package/src/panels/access/manifest-creator.tsx +167 -0
  17. package/src/panels/access/manifest-list.tsx +105 -0
  18. package/src/panels/access/namespace-config-view.tsx +525 -0
  19. package/src/panels/access/permission-checker.tsx +231 -0
  20. package/src/panels/agents/agent-status-view.tsx +196 -0
  21. package/src/panels/agents/agents-panel.tsx +493 -0
  22. package/src/panels/agents/delegation-list.tsx +154 -0
  23. package/src/panels/agents/inbox-view.tsx +96 -0
  24. package/src/panels/agents/trajectories-tab.tsx +40 -0
  25. package/src/panels/api-console/api-console-panel.tsx +189 -0
  26. package/src/panels/api-console/codegen-viewer.tsx +36 -0
  27. package/src/panels/api-console/codegen.ts +112 -0
  28. package/src/panels/api-console/endpoint-list.tsx +57 -0
  29. package/src/panels/api-console/request-builder.tsx +69 -0
  30. package/src/panels/api-console/response-viewer.tsx +54 -0
  31. package/src/panels/connectors/available-tab.tsx +357 -0
  32. package/src/panels/connectors/connector-row.tsx +121 -0
  33. package/src/panels/connectors/connectors-panel.tsx +88 -0
  34. package/src/panels/connectors/error-parser.ts +116 -0
  35. package/src/panels/connectors/mounted-tab.tsx +179 -0
  36. package/src/panels/connectors/skills-tab.tsx +235 -0
  37. package/src/panels/connectors/template-generator.ts +211 -0
  38. package/src/panels/connectors/write-tab.tsx +514 -0
  39. package/src/panels/events/audit-tab.tsx +69 -0
  40. package/src/panels/events/audit-trail.tsx +75 -0
  41. package/src/panels/events/connector-detail.tsx +49 -0
  42. package/src/panels/events/connector-list.tsx +73 -0
  43. package/src/panels/events/connectors-tab.tsx +92 -0
  44. package/src/panels/events/event-replay.tsx +80 -0
  45. package/src/panels/events/events-panel.tsx +414 -0
  46. package/src/panels/events/events-tab.tsx +212 -0
  47. package/src/panels/events/lock-list.tsx +54 -0
  48. package/src/panels/events/locks-tab.tsx +103 -0
  49. package/src/panels/events/mcl-replay.tsx +77 -0
  50. package/src/panels/events/mcl-tab.tsx +83 -0
  51. package/src/panels/events/operations-tab-wrapper.tsx +62 -0
  52. package/src/panels/events/operations-tab.tsx +41 -0
  53. package/src/panels/events/replay-tab.tsx +76 -0
  54. package/src/panels/events/secrets-audit.tsx +64 -0
  55. package/src/panels/events/secrets-tab.tsx +75 -0
  56. package/src/panels/events/subscription-list.tsx +54 -0
  57. package/src/panels/events/subscriptions-tab.tsx +82 -0
  58. package/src/panels/files/file-aspects.tsx +93 -0
  59. package/src/panels/files/file-editor.tsx +160 -0
  60. package/src/panels/files/file-explorer-keybindings.ts +468 -0
  61. package/src/panels/files/file-explorer-panel.tsx +545 -0
  62. package/src/panels/files/file-lineage.tsx +163 -0
  63. package/src/panels/files/file-list-item.tsx +28 -0
  64. package/src/panels/files/file-metadata.tsx +62 -0
  65. package/src/panels/files/file-preview.tsx +108 -0
  66. package/src/panels/files/file-schema.tsx +89 -0
  67. package/src/panels/files/file-tree-node.tsx +44 -0
  68. package/src/panels/files/file-tree.tsx +169 -0
  69. package/src/panels/files/share-links-tab.tsx +33 -0
  70. package/src/panels/files/uploads-tab.tsx +45 -0
  71. package/src/panels/payments/approval-list.tsx +83 -0
  72. package/src/panels/payments/balance-card.tsx +43 -0
  73. package/src/panels/payments/budget-card.tsx +70 -0
  74. package/src/panels/payments/payments-panel.tsx +451 -0
  75. package/src/panels/payments/policy-list.tsx +64 -0
  76. package/src/panels/payments/reservation-list.tsx +78 -0
  77. package/src/panels/payments/transaction-list.tsx +103 -0
  78. package/src/panels/payments/transfer-form.tsx +109 -0
  79. package/src/panels/search/column-search.tsx +79 -0
  80. package/src/panels/search/knowledge-view.tsx +100 -0
  81. package/src/panels/search/memory-list.tsx +197 -0
  82. package/src/panels/search/playbook-list.tsx +77 -0
  83. package/src/panels/search/rlm-answer-view.tsx +105 -0
  84. package/src/panels/search/search-panel.tsx +405 -0
  85. package/src/panels/search/search-results.tsx +116 -0
  86. package/src/panels/stack/stack-panel.tsx +474 -0
  87. package/src/panels/versions/conflicts-tab.tsx +59 -0
  88. package/src/panels/versions/entry-detail.tsx +89 -0
  89. package/src/panels/versions/transaction-actions.tsx +34 -0
  90. package/src/panels/versions/transaction-list.tsx +90 -0
  91. package/src/panels/versions/versions-panel.tsx +276 -0
  92. package/src/panels/workflows/execution-list.tsx +102 -0
  93. package/src/panels/workflows/scheduler-view.tsx +135 -0
  94. package/src/panels/workflows/workflow-list.tsx +88 -0
  95. package/src/panels/workflows/workflows-panel.tsx +295 -0
  96. package/src/panels/zones/brick-detail.tsx +136 -0
  97. package/src/panels/zones/brick-list.tsx +56 -0
  98. package/src/panels/zones/cache-tab.tsx +118 -0
  99. package/src/panels/zones/drift-view.tsx +97 -0
  100. package/src/panels/zones/mcp-mounts-tab.tsx +38 -0
  101. package/src/panels/zones/memories-tab.tsx +37 -0
  102. package/src/panels/zones/reindex-status.tsx +84 -0
  103. package/src/panels/zones/workspaces-tab.tsx +37 -0
  104. package/src/panels/zones/zone-list.tsx +73 -0
  105. package/src/panels/zones/zones-panel.tsx +559 -0
  106. package/src/services/command-runner.ts +303 -0
  107. package/src/shared/accessibility-announcements.ts +44 -0
  108. package/src/shared/action-registry.ts +466 -0
  109. package/src/shared/brick-states.ts +91 -0
  110. package/src/shared/command-palette.ts +35 -0
  111. package/src/shared/components/announcement-bar.tsx +30 -0
  112. package/src/shared/components/app-confirm-dialog.tsx +29 -0
  113. package/src/shared/components/breadcrumb.tsx +21 -0
  114. package/src/shared/components/brick-gate.tsx +60 -0
  115. package/src/shared/components/command-output.tsx +95 -0
  116. package/src/shared/components/command-palette.tsx +97 -0
  117. package/src/shared/components/confirm-dialog.tsx +61 -0
  118. package/src/shared/components/diff-viewer.tsx +219 -0
  119. package/src/shared/components/empty-state.tsx +36 -0
  120. package/src/shared/components/error-bar.tsx +60 -0
  121. package/src/shared/components/error-boundary.tsx +53 -0
  122. package/src/shared/components/help-overlay.tsx +99 -0
  123. package/src/shared/components/identity-switcher.tsx +168 -0
  124. package/src/shared/components/loading-indicator.tsx +40 -0
  125. package/src/shared/components/pagination-bar.tsx +68 -0
  126. package/src/shared/components/pre-connection-screen.tsx +398 -0
  127. package/src/shared/components/scroll-indicator.tsx +46 -0
  128. package/src/shared/components/side-nav-utils.ts +68 -0
  129. package/src/shared/components/side-nav.tsx +287 -0
  130. package/src/shared/components/spinner.tsx +26 -0
  131. package/src/shared/components/status-bar.tsx +117 -0
  132. package/src/shared/components/styled-text.tsx +72 -0
  133. package/src/shared/components/sub-tab-bar-utils.ts +100 -0
  134. package/src/shared/components/sub-tab-bar.tsx +40 -0
  135. package/src/shared/components/tab-bar-utils.ts +36 -0
  136. package/src/shared/components/tab-bar.tsx +50 -0
  137. package/src/shared/components/text-input.tsx +73 -0
  138. package/src/shared/components/tooltip.tsx +53 -0
  139. package/src/shared/components/virtual-list.tsx +93 -0
  140. package/src/shared/components/welcome-screen.tsx +111 -0
  141. package/src/shared/hooks/use-api.ts +10 -0
  142. package/src/shared/hooks/use-brick-available.ts +42 -0
  143. package/src/shared/hooks/use-confirm.ts +66 -0
  144. package/src/shared/hooks/use-connection-state.ts +67 -0
  145. package/src/shared/hooks/use-copy.ts +31 -0
  146. package/src/shared/hooks/use-fresh-server.ts +62 -0
  147. package/src/shared/hooks/use-keyboard.ts +58 -0
  148. package/src/shared/hooks/use-list-navigation.ts +106 -0
  149. package/src/shared/hooks/use-swr.ts +117 -0
  150. package/src/shared/hooks/use-tab-fallback.ts +32 -0
  151. package/src/shared/hooks/use-text-input.ts +113 -0
  152. package/src/shared/hooks/use-visible-tabs.ts +61 -0
  153. package/src/shared/lib/circular-buffer.ts +82 -0
  154. package/src/shared/lib/clipboard.ts +14 -0
  155. package/src/shared/nav-items.ts +73 -0
  156. package/src/shared/navigation.ts +110 -0
  157. package/src/shared/status-breadcrumb.ts +74 -0
  158. package/src/shared/syntax-style.ts +3 -0
  159. package/src/shared/tab-visibility.ts +15 -0
  160. package/src/shared/text-style.ts +23 -0
  161. package/src/shared/theme.ts +179 -0
  162. package/src/shared/utils/format-size.ts +20 -0
  163. package/src/shared/utils/format-text.ts +10 -0
  164. package/src/shared/utils/format-time.ts +72 -0
  165. package/src/shared/utils/lru-cache.ts +75 -0
  166. package/src/stores/access-store-types.ts +154 -0
  167. package/src/stores/access-store.ts +674 -0
  168. package/src/stores/agents-store.ts +404 -0
  169. package/src/stores/announcement-store.ts +46 -0
  170. package/src/stores/api-console-store.ts +476 -0
  171. package/src/stores/connectors-store.ts +434 -0
  172. package/src/stores/create-api-action.ts +140 -0
  173. package/src/stores/delegation-store.ts +300 -0
  174. package/src/stores/error-store.ts +102 -0
  175. package/src/stores/events-store.ts +163 -0
  176. package/src/stores/files-store.ts +630 -0
  177. package/src/stores/first-run-store.ts +34 -0
  178. package/src/stores/global-store.ts +255 -0
  179. package/src/stores/infra-store.ts +461 -0
  180. package/src/stores/knowledge-store.ts +358 -0
  181. package/src/stores/lineage-store.ts +126 -0
  182. package/src/stores/mcp-store.ts +147 -0
  183. package/src/stores/payments-store.ts +545 -0
  184. package/src/stores/search-store-types.ts +155 -0
  185. package/src/stores/search-store.ts +656 -0
  186. package/src/stores/share-link-store.ts +151 -0
  187. package/src/stores/stack-store.ts +352 -0
  188. package/src/stores/ui-store.ts +161 -0
  189. package/src/stores/upload-store.ts +131 -0
  190. package/src/stores/versions-store.ts +355 -0
  191. package/src/stores/workflows-store.ts +402 -0
  192. package/src/stores/workspace-store.ts +185 -0
  193. package/src/stores/zones-store.ts +378 -0
@@ -0,0 +1,461 @@
1
+ /**
2
+ * Zustand store for Infrastructure panel: connectors, subscriptions, locks, secrets audit.
3
+ *
4
+ * Complements the events-store (SSE streaming) with REST-based infra management.
5
+ */
6
+
7
+ import { create } from "zustand";
8
+ import type { FetchClient } from "@nexus-ai-fs/api-client";
9
+ import { createApiAction, categorizeError } from "./create-api-action.js";
10
+ import { useErrorStore } from "./error-store.js";
11
+ import { useUiStore } from "./ui-store.js";
12
+
13
+ // =============================================================================
14
+ // Types (snake_case matching API wire format)
15
+ // =============================================================================
16
+
17
+ export interface Connector {
18
+ readonly connector_id: string;
19
+ readonly name: string;
20
+ readonly type: string;
21
+ readonly status: "active" | "inactive" | "error";
22
+ readonly capabilities: readonly string[];
23
+ readonly config: Record<string, unknown>;
24
+ readonly created_at: string;
25
+ readonly last_seen: string | null;
26
+ }
27
+
28
+ export interface Subscription {
29
+ readonly subscription_id: string;
30
+ readonly event_type: string;
31
+ readonly endpoint: string;
32
+ readonly status: "active" | "paused" | "failed";
33
+ readonly filter: string | null;
34
+ readonly created_at: string;
35
+ readonly last_triggered: string | null;
36
+ readonly trigger_count: number;
37
+ }
38
+
39
+ /** Lock shape matching backend LockInfoMutex / LockInfoSemaphore. */
40
+ export interface Lock {
41
+ readonly lock_id: string;
42
+ readonly mode: "mutex" | "semaphore";
43
+ readonly max_holders: number;
44
+ readonly holder_info: string;
45
+ readonly acquired_at: number;
46
+ readonly expires_at: number;
47
+ readonly fence_token: number;
48
+ /** The resource path this lock is held on (derived from the list key). */
49
+ readonly resource: string;
50
+ }
51
+
52
+ /** Matches backend SecretAuditEventResponse. */
53
+ export interface SecretAuditEntry {
54
+ readonly id: string;
55
+ readonly record_hash: string;
56
+ readonly created_at: string;
57
+ readonly event_type: string;
58
+ readonly actor_id: string;
59
+ readonly provider: string | null;
60
+ readonly credential_id: string | null;
61
+ readonly token_family_id: string | null;
62
+ readonly zone_id: string;
63
+ readonly ip_address: string | null;
64
+ readonly details: string | null;
65
+ readonly metadata_hash: string | null;
66
+ }
67
+
68
+ export interface OperationItem {
69
+ readonly operation_id: string;
70
+ readonly agent_id: string | null;
71
+ readonly type: string;
72
+ readonly status: string;
73
+ readonly started_at: string | null;
74
+ readonly completed_at: string | null;
75
+ }
76
+
77
+ /** Audit transaction from /api/v2/audit/transactions. */
78
+ export interface AuditTransaction {
79
+ readonly transaction_id: string;
80
+ readonly actor_id: string;
81
+ readonly action: string;
82
+ readonly resource: string;
83
+ readonly timestamp: string;
84
+ readonly status: string;
85
+ readonly details: string | null;
86
+ }
87
+
88
+ export type InfraTab = "connectors" | "subscriptions" | "locks" | "secrets";
89
+ export type EventsPanelTab = "events" | "mcl" | "replay" | "operations" | "audit" | InfraTab;
90
+
91
+ // =============================================================================
92
+ // Store
93
+ // =============================================================================
94
+
95
+ export interface InfraState {
96
+ // Connectors
97
+ readonly connectors: readonly Connector[];
98
+ readonly selectedConnectorIndex: number;
99
+ readonly connectorsLoading: boolean;
100
+
101
+ // Subscriptions
102
+ readonly subscriptions: readonly Subscription[];
103
+ readonly selectedSubscriptionIndex: number;
104
+ readonly subscriptionsLoading: boolean;
105
+
106
+ // Locks
107
+ readonly locks: readonly Lock[];
108
+ readonly selectedLockIndex: number;
109
+ readonly locksLoading: boolean;
110
+
111
+ // Secrets audit
112
+ readonly secretAuditEntries: readonly SecretAuditEntry[];
113
+ readonly secretsLoading: boolean;
114
+
115
+ // Operations
116
+ readonly operations: readonly OperationItem[];
117
+ readonly operationsLoading: boolean;
118
+ readonly selectedOperationIndex: number;
119
+
120
+ // Connector capabilities
121
+ readonly connectorCapabilities: unknown | null;
122
+ readonly capabilitiesLoading: boolean;
123
+
124
+ // Audit transactions
125
+ readonly auditTransactions: readonly AuditTransaction[];
126
+ readonly auditLoading: boolean;
127
+ readonly auditHasMore: boolean;
128
+ readonly auditNextCursor: string | null;
129
+
130
+ // Navigation
131
+ readonly activeTab: InfraTab;
132
+ readonly activePanelTab: EventsPanelTab;
133
+
134
+ // Error
135
+ readonly error: string | null;
136
+
137
+ // Actions
138
+ readonly fetchConnectors: (client: FetchClient) => Promise<void>;
139
+ readonly fetchSubscriptions: (client: FetchClient) => Promise<void>;
140
+ readonly createSubscription: (
141
+ eventType: string,
142
+ endpoint: string,
143
+ client: FetchClient,
144
+ ) => Promise<void>;
145
+ readonly deleteSubscription: (id: string, client: FetchClient) => Promise<void>;
146
+ readonly testSubscription: (id: string, client: FetchClient) => Promise<void>;
147
+ readonly fetchLocks: (client: FetchClient) => Promise<void>;
148
+ readonly acquireLock: (path: string, mode: "mutex" | "semaphore", ttlSeconds: number, client: FetchClient) => Promise<void>;
149
+ readonly releaseLock: (path: string, lockId: string, client: FetchClient) => Promise<void>;
150
+ readonly extendLock: (path: string, lockId: string, ttlSeconds: number, client: FetchClient) => Promise<void>;
151
+ readonly fetchSecretAudit: (client: FetchClient) => Promise<void>;
152
+ readonly fetchOperations: (client: FetchClient) => Promise<void>;
153
+ readonly fetchConnectorCapabilities: (connectorName: string, client: FetchClient) => Promise<void>;
154
+ readonly fetchAuditTransactions: (filters: { cursor?: string; limit?: number }, client: FetchClient) => Promise<void>;
155
+ readonly setActiveTab: (tab: InfraTab) => void;
156
+ readonly setActivePanelTab: (tab: EventsPanelTab) => void;
157
+ readonly setSelectedOperationIndex: (index: number) => void;
158
+ readonly setSelectedConnectorIndex: (index: number) => void;
159
+ readonly setSelectedSubscriptionIndex: (index: number) => void;
160
+ readonly setSelectedLockIndex: (index: number) => void;
161
+ }
162
+
163
+ const SOURCE = "infrastructure";
164
+
165
+ export const useInfraStore = create<InfraState>((set, get) => ({
166
+ connectors: [],
167
+ selectedConnectorIndex: 0,
168
+ connectorsLoading: false,
169
+ subscriptions: [],
170
+ selectedSubscriptionIndex: 0,
171
+ subscriptionsLoading: false,
172
+ locks: [],
173
+ selectedLockIndex: 0,
174
+ locksLoading: false,
175
+ secretAuditEntries: [],
176
+ secretsLoading: false,
177
+ operations: [],
178
+ operationsLoading: false,
179
+ selectedOperationIndex: 0,
180
+ connectorCapabilities: null,
181
+ capabilitiesLoading: false,
182
+ auditTransactions: [],
183
+ auditLoading: false,
184
+ auditHasMore: false,
185
+ auditNextCursor: null,
186
+ activeTab: "connectors",
187
+ activePanelTab: "events",
188
+ error: null,
189
+
190
+ // =========================================================================
191
+ // Actions with loading keys — createApiAction
192
+ // =========================================================================
193
+
194
+ fetchConnectors: createApiAction<InfraState, [FetchClient]>(set, {
195
+ loadingKey: "connectorsLoading",
196
+ source: SOURCE,
197
+ errorMessage: "Failed to fetch connectors",
198
+ action: async (client) => {
199
+ const response = await client.get<{
200
+ readonly connectors: readonly any[];
201
+ }>("/api/v2/connectors");
202
+ // Normalize: API returns category/name, store expects type/connector_id/status
203
+ const connectors: Connector[] = (response.connectors ?? []).map((c: any) => ({
204
+ connector_id: c.connector_id ?? c.name ?? "",
205
+ name: c.name ?? "",
206
+ type: c.type ?? c.category ?? "",
207
+ status: c.status ?? "active",
208
+ capabilities: c.capabilities ?? [],
209
+ config: c.config ?? {},
210
+ created_at: c.created_at ?? "",
211
+ last_seen: c.last_seen ?? null,
212
+ }));
213
+ return { connectors };
214
+ },
215
+ }),
216
+
217
+ fetchSubscriptions: createApiAction<InfraState, [FetchClient]>(set, {
218
+ loadingKey: "subscriptionsLoading",
219
+ source: SOURCE,
220
+ errorMessage: "Failed to fetch subscriptions",
221
+ action: async (client) => {
222
+ const response = await client.get<{
223
+ readonly subscriptions: readonly Subscription[];
224
+ }>("/api/v2/subscriptions");
225
+ return {
226
+ subscriptions: response.subscriptions ?? [],
227
+ selectedSubscriptionIndex: 0,
228
+ };
229
+ },
230
+ }),
231
+
232
+ fetchLocks: createApiAction<InfraState, [FetchClient]>(set, {
233
+ loadingKey: "locksLoading",
234
+ source: SOURCE,
235
+ errorMessage: "Failed to fetch locks",
236
+ action: async (client) => {
237
+ const response = await client.get<{
238
+ readonly locks: readonly Lock[];
239
+ readonly count: number;
240
+ }>("/api/v2/locks");
241
+ return { locks: response.locks ?? [], selectedLockIndex: 0 };
242
+ },
243
+ }),
244
+
245
+ fetchSecretAudit: createApiAction<InfraState, [FetchClient]>(set, {
246
+ loadingKey: "secretsLoading",
247
+ source: SOURCE,
248
+ errorMessage: "Failed to fetch secrets audit",
249
+ action: async (client) => {
250
+ const response = await client.get<{
251
+ readonly events: readonly SecretAuditEntry[];
252
+ readonly limit: number;
253
+ readonly has_more: boolean;
254
+ readonly total: number | null;
255
+ readonly next_cursor: string | null;
256
+ }>("/api/v2/secrets-audit/events");
257
+ return { secretAuditEntries: response.events ?? [] };
258
+ },
259
+ }),
260
+
261
+ fetchOperations: createApiAction<InfraState, [FetchClient]>(set, {
262
+ loadingKey: "operationsLoading",
263
+ source: SOURCE,
264
+ errorMessage: "Failed to fetch operations",
265
+ action: async (client) => {
266
+ const response = await client.get<{
267
+ readonly operations: readonly any[];
268
+ }>("/api/v2/operations?limit=20");
269
+ // Normalize API fields (id → operation_id, operation_type → type)
270
+ const ops: OperationItem[] = (response.operations ?? []).map((op: any) => ({
271
+ operation_id: op.operation_id ?? op.id ?? "",
272
+ agent_id: op.agent_id ?? null,
273
+ type: op.type ?? op.operation_type ?? "",
274
+ status: op.status ?? "",
275
+ started_at: op.started_at ?? op.timestamp ?? null,
276
+ completed_at: op.completed_at ?? null,
277
+ }));
278
+ return {
279
+ operations: ops,
280
+ selectedOperationIndex: 0,
281
+ };
282
+ },
283
+ }),
284
+
285
+ fetchConnectorCapabilities: createApiAction<InfraState, [string, FetchClient]>(set, {
286
+ loadingKey: "capabilitiesLoading",
287
+ source: SOURCE,
288
+ errorMessage: "Failed to fetch connector capabilities",
289
+ action: async (connectorName, client) => {
290
+ const response = await client.get<{
291
+ readonly capabilities: unknown;
292
+ }>(`/api/v2/connectors/${encodeURIComponent(connectorName)}/capabilities`);
293
+ return { connectorCapabilities: response.capabilities ?? null };
294
+ },
295
+ }),
296
+
297
+ fetchAuditTransactions: async (filters, client) => {
298
+ set({ auditLoading: true, error: null });
299
+ try {
300
+ const params = new URLSearchParams();
301
+ if (filters.cursor) params.set("cursor", filters.cursor);
302
+ params.set("limit", String(filters.limit ?? 50));
303
+ const qs = params.toString();
304
+ const url = `/api/v2/audit/transactions${qs ? `?${qs}` : ""}`;
305
+ const response = await client.get<{
306
+ readonly transactions: readonly AuditTransaction[];
307
+ readonly has_more: boolean;
308
+ readonly next_cursor: string | null;
309
+ }>(url);
310
+ const incoming = (response.transactions ?? []).map((t) => ({
311
+ transaction_id: t.transaction_id ?? "",
312
+ actor_id: t.actor_id ?? "",
313
+ action: t.action ?? "",
314
+ resource: t.resource ?? "",
315
+ timestamp: t.timestamp ?? "",
316
+ status: t.status ?? "",
317
+ details: t.details ?? null,
318
+ }));
319
+ set((state) => {
320
+ const combined = filters.cursor
321
+ ? [...state.auditTransactions, ...incoming]
322
+ : incoming;
323
+ return {
324
+ auditTransactions: combined.slice(-1000), // keep latest 1000
325
+ auditLoading: false,
326
+ auditHasMore: response.has_more ?? false,
327
+ auditNextCursor: response.next_cursor ?? null,
328
+ };
329
+ });
330
+ useUiStore.getState().markDataUpdated("infrastructure");
331
+ } catch (err) {
332
+ const message = err instanceof Error ? err.message : "Failed to fetch audit transactions";
333
+ set({ auditLoading: false, error: message });
334
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
335
+ }
336
+ },
337
+
338
+ // =========================================================================
339
+ // Actions without loading keys — inline with error store integration
340
+ // =========================================================================
341
+
342
+ createSubscription: async (eventType, endpoint, client) => {
343
+ set({ error: null });
344
+ try {
345
+ await client.post<Subscription>("/api/v2/subscriptions", {
346
+ event_type: eventType,
347
+ endpoint,
348
+ });
349
+ await get().fetchSubscriptions(client);
350
+ } catch (err) {
351
+ const message = err instanceof Error ? err.message : "Failed to create subscription";
352
+ set({ error: message });
353
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
354
+ }
355
+ },
356
+
357
+ deleteSubscription: async (id, client) => {
358
+ set({ error: null });
359
+ try {
360
+ await client.delete(`/api/v2/subscriptions/${encodeURIComponent(id)}`);
361
+ set((state) => ({
362
+ subscriptions: state.subscriptions.filter((s) => s.subscription_id !== id),
363
+ }));
364
+ } catch (err) {
365
+ const message = err instanceof Error ? err.message : "Failed to delete subscription";
366
+ set({ error: message });
367
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
368
+ }
369
+ },
370
+
371
+ testSubscription: async (id, client) => {
372
+ set({ error: null });
373
+ try {
374
+ await client.post(
375
+ `/api/v2/subscriptions/${encodeURIComponent(id)}/test`,
376
+ {},
377
+ );
378
+ } catch (err) {
379
+ const message = err instanceof Error ? err.message : "Failed to test subscription";
380
+ set({ error: message });
381
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
382
+ }
383
+ },
384
+
385
+ acquireLock: async (path, mode, ttlSeconds, client) => {
386
+ set({ error: null });
387
+ try {
388
+ await client.post(`/api/v2/locks/${encodeURIComponent(path)}/acquire`, {
389
+ mode,
390
+ ttl_seconds: ttlSeconds,
391
+ });
392
+ // Refresh lock list after acquisition
393
+ await get().fetchLocks(client);
394
+ } catch (err) {
395
+ const message = err instanceof Error ? err.message : "Failed to acquire lock";
396
+ set({ error: message });
397
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
398
+ }
399
+ },
400
+
401
+ releaseLock: async (path, lockId, client) => {
402
+ set({ error: null });
403
+ try {
404
+ await client.deleteNoContent(
405
+ `/api/v2/locks/${encodeURIComponent(path)}?lock_id=${encodeURIComponent(lockId)}`,
406
+ );
407
+ set((state) => ({
408
+ locks: state.locks.filter((l) => l.lock_id !== lockId),
409
+ }));
410
+ } catch (err) {
411
+ const message = err instanceof Error ? err.message : "Failed to release lock";
412
+ set({ error: message });
413
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
414
+ }
415
+ },
416
+
417
+ extendLock: async (path, lockId, ttlSeconds, client) => {
418
+ set({ error: null });
419
+ try {
420
+ await client.patch(`/api/v2/locks/${encodeURIComponent(path)}`, {
421
+ lock_id: lockId,
422
+ ttl: ttlSeconds,
423
+ });
424
+ await get().fetchLocks(client);
425
+ } catch (err) {
426
+ const message = err instanceof Error ? err.message : "Failed to extend lock";
427
+ set({ error: message });
428
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
429
+ }
430
+ },
431
+
432
+ setActiveTab: (tab) => {
433
+ set({ activeTab: tab, activePanelTab: tab, error: null });
434
+ },
435
+
436
+ setActivePanelTab: (tab) => {
437
+ set((state) => ({
438
+ activePanelTab: tab,
439
+ activeTab: tab === "events" || tab === "mcl" || tab === "replay" || tab === "operations" || tab === "audit"
440
+ ? state.activeTab
441
+ : tab,
442
+ error: null,
443
+ }));
444
+ },
445
+
446
+ setSelectedOperationIndex: (index) => {
447
+ set({ selectedOperationIndex: index });
448
+ },
449
+
450
+ setSelectedConnectorIndex: (index) => {
451
+ set({ selectedConnectorIndex: index });
452
+ },
453
+
454
+ setSelectedSubscriptionIndex: (index) => {
455
+ set({ selectedSubscriptionIndex: index });
456
+ },
457
+
458
+ setSelectedLockIndex: (index) => {
459
+ set({ selectedLockIndex: index });
460
+ },
461
+ }));