@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,674 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand store for the Access Control panel:
|
|
3
|
+
* manifests (+ tuple entries), permission evaluation, governance alerts,
|
|
4
|
+
* credentials, fraud scores.
|
|
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
|
+
export type { DelegationItem } from "./delegation-store.js";
|
|
12
|
+
import type { DelegationItem } from "./delegation-store.js";
|
|
13
|
+
|
|
14
|
+
// Re-export all types for backward compatibility
|
|
15
|
+
export type {
|
|
16
|
+
ManifestEntry, AccessManifest, EvaluationTraceEntry,
|
|
17
|
+
EvaluationTraceResult, PermissionCheck, GovernanceAlert,
|
|
18
|
+
Credential, FraudScore, DelegationCreateResponse,
|
|
19
|
+
DelegationChainEntry, DelegationChain, NamespaceDetail,
|
|
20
|
+
GovernanceCheckResult, GovernanceConstraint, AccessTab,
|
|
21
|
+
} from "./access-store-types.js";
|
|
22
|
+
|
|
23
|
+
import type {
|
|
24
|
+
AccessManifest, EvaluationTraceResult, PermissionCheck,
|
|
25
|
+
GovernanceAlert, Credential, FraudScore,
|
|
26
|
+
DelegationCreateResponse, DelegationChain, NamespaceDetail,
|
|
27
|
+
GovernanceCheckResult, GovernanceConstraint, AccessTab,
|
|
28
|
+
} from "./access-store-types.js";
|
|
29
|
+
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// Store
|
|
32
|
+
// =============================================================================
|
|
33
|
+
|
|
34
|
+
export interface AccessState {
|
|
35
|
+
// Manifests
|
|
36
|
+
readonly manifests: readonly AccessManifest[];
|
|
37
|
+
readonly selectedManifestIndex: number;
|
|
38
|
+
readonly manifestsLoading: boolean;
|
|
39
|
+
|
|
40
|
+
// Permission check
|
|
41
|
+
readonly lastPermissionCheck: PermissionCheck | null;
|
|
42
|
+
readonly permissionCheckLoading: boolean;
|
|
43
|
+
|
|
44
|
+
// Governance alerts
|
|
45
|
+
readonly alerts: readonly GovernanceAlert[];
|
|
46
|
+
readonly alertsLoading: boolean;
|
|
47
|
+
readonly selectedAlertIndex: number;
|
|
48
|
+
|
|
49
|
+
// Credentials
|
|
50
|
+
readonly credentials: readonly Credential[];
|
|
51
|
+
readonly credentialsLoading: boolean;
|
|
52
|
+
|
|
53
|
+
// Fraud scores
|
|
54
|
+
readonly fraudScores: readonly FraudScore[];
|
|
55
|
+
readonly fraudScoresLoading: boolean;
|
|
56
|
+
readonly selectedFraudIndex: number;
|
|
57
|
+
|
|
58
|
+
// Delegations
|
|
59
|
+
readonly delegations: readonly DelegationItem[];
|
|
60
|
+
readonly delegationsLoading: boolean;
|
|
61
|
+
readonly selectedDelegationIndex: number;
|
|
62
|
+
readonly lastDelegationCreate: DelegationCreateResponse | null;
|
|
63
|
+
readonly delegationChain: DelegationChain | null;
|
|
64
|
+
readonly delegationChainLoading: boolean;
|
|
65
|
+
|
|
66
|
+
// Namespace detail
|
|
67
|
+
readonly namespaceDetail: NamespaceDetail | null;
|
|
68
|
+
readonly namespaceDetailLoading: boolean;
|
|
69
|
+
|
|
70
|
+
// Governance check
|
|
71
|
+
readonly governanceCheck: GovernanceCheckResult | null;
|
|
72
|
+
readonly governanceCheckLoading: boolean;
|
|
73
|
+
|
|
74
|
+
// Collusion detection
|
|
75
|
+
readonly collusionRings: readonly unknown[];
|
|
76
|
+
readonly collusionLoading: boolean;
|
|
77
|
+
|
|
78
|
+
// Governance constraints
|
|
79
|
+
readonly constraints: readonly GovernanceConstraint[];
|
|
80
|
+
readonly constraintsLoading: boolean;
|
|
81
|
+
readonly selectedConstraintIndex: number;
|
|
82
|
+
|
|
83
|
+
// UI state
|
|
84
|
+
readonly activeTab: AccessTab;
|
|
85
|
+
readonly error: string | null;
|
|
86
|
+
|
|
87
|
+
// Actions — manifests
|
|
88
|
+
readonly fetchManifests: (client: FetchClient) => Promise<void>;
|
|
89
|
+
readonly fetchManifestDetail: (manifestId: string, client: FetchClient) => Promise<void>;
|
|
90
|
+
readonly checkPermission: (
|
|
91
|
+
manifestId: string,
|
|
92
|
+
toolName: string,
|
|
93
|
+
client: FetchClient,
|
|
94
|
+
) => Promise<void>;
|
|
95
|
+
|
|
96
|
+
// Actions — alerts
|
|
97
|
+
readonly fetchAlerts: (zoneId: string | undefined, client: FetchClient) => Promise<void>;
|
|
98
|
+
readonly resolveAlert: (alertId: string, resolvedBy: string, zoneId: string | undefined, client: FetchClient) => Promise<void>;
|
|
99
|
+
|
|
100
|
+
// Actions — credentials
|
|
101
|
+
readonly fetchCredentials: (agentId: string, client: FetchClient) => Promise<void>;
|
|
102
|
+
readonly issueCredential: (agentId: string, claims: Record<string, unknown>, client: FetchClient) => Promise<void>;
|
|
103
|
+
readonly revokeCredential: (credentialId: string, agentId: string, client: FetchClient) => Promise<void>;
|
|
104
|
+
|
|
105
|
+
// Actions — fraud scores
|
|
106
|
+
readonly fetchFraudScores: (zoneId: string | undefined, client: FetchClient) => Promise<void>;
|
|
107
|
+
readonly computeFraudScores: (zoneId: string | undefined, client: FetchClient) => Promise<void>;
|
|
108
|
+
|
|
109
|
+
// Actions — delegations
|
|
110
|
+
readonly fetchDelegations: (client: FetchClient, status?: string | null) => Promise<void>;
|
|
111
|
+
readonly createDelegation: (
|
|
112
|
+
request: {
|
|
113
|
+
readonly worker_id: string;
|
|
114
|
+
readonly worker_name: string;
|
|
115
|
+
readonly namespace_mode: string;
|
|
116
|
+
readonly scope_prefix?: string;
|
|
117
|
+
readonly intent: string;
|
|
118
|
+
readonly can_sub_delegate: boolean;
|
|
119
|
+
readonly ttl_seconds?: number;
|
|
120
|
+
},
|
|
121
|
+
client: FetchClient,
|
|
122
|
+
) => Promise<void>;
|
|
123
|
+
readonly revokeDelegation: (delegationId: string, client: FetchClient) => Promise<void>;
|
|
124
|
+
readonly completeDelegation: (
|
|
125
|
+
delegationId: string,
|
|
126
|
+
outcome: string,
|
|
127
|
+
qualityScore: number | null,
|
|
128
|
+
client: FetchClient,
|
|
129
|
+
) => Promise<void>;
|
|
130
|
+
readonly fetchDelegationChain: (delegationId: string, client: FetchClient) => Promise<void>;
|
|
131
|
+
readonly fetchNamespaceDetail: (delegationId: string, client: FetchClient) => Promise<void>;
|
|
132
|
+
readonly updateNamespaceConfig: (
|
|
133
|
+
delegationId: string,
|
|
134
|
+
update: {
|
|
135
|
+
readonly scope_prefix?: string;
|
|
136
|
+
readonly remove_grants?: readonly string[];
|
|
137
|
+
readonly add_grants?: readonly string[];
|
|
138
|
+
readonly readonly_paths?: readonly string[];
|
|
139
|
+
},
|
|
140
|
+
client: FetchClient,
|
|
141
|
+
) => Promise<void>;
|
|
142
|
+
|
|
143
|
+
// Actions — governance check
|
|
144
|
+
readonly checkGovernanceEdge: (
|
|
145
|
+
fromAgentId: string,
|
|
146
|
+
toAgentId: string,
|
|
147
|
+
zoneId: string | undefined,
|
|
148
|
+
client: FetchClient,
|
|
149
|
+
) => Promise<void>;
|
|
150
|
+
|
|
151
|
+
// Actions — governance constraints
|
|
152
|
+
readonly fetchConstraints: (zoneId: string, client: FetchClient) => Promise<void>;
|
|
153
|
+
readonly createConstraint: (constraint: { from_agent_id: string; to_agent_id: string; constraint_type: string; zone_id: string }, client: FetchClient) => Promise<void>;
|
|
154
|
+
readonly deleteConstraint: (constraintId: string, client: FetchClient) => Promise<void>;
|
|
155
|
+
readonly setSelectedConstraintIndex: (index: number) => void;
|
|
156
|
+
|
|
157
|
+
// Actions — governance deep features
|
|
158
|
+
readonly fetchCollusionRings: (zoneId: string | undefined, client: FetchClient) => Promise<void>;
|
|
159
|
+
readonly suspendAgent: (agentId: string, reason: string, zoneId: string | undefined, client: FetchClient) => Promise<void>;
|
|
160
|
+
|
|
161
|
+
// Actions — manifests (create/revoke)
|
|
162
|
+
readonly createManifest: (
|
|
163
|
+
payload: {
|
|
164
|
+
readonly agent_id: string;
|
|
165
|
+
readonly name: string;
|
|
166
|
+
readonly entries: readonly { readonly tool_pattern: string; readonly permission: string; readonly max_calls_per_minute?: number }[];
|
|
167
|
+
readonly valid_from?: string;
|
|
168
|
+
readonly valid_until?: string;
|
|
169
|
+
},
|
|
170
|
+
client: FetchClient,
|
|
171
|
+
) => Promise<void>;
|
|
172
|
+
readonly revokeManifest: (manifestId: string, client: FetchClient) => Promise<void>;
|
|
173
|
+
|
|
174
|
+
// Actions — UI
|
|
175
|
+
readonly setActiveTab: (tab: AccessTab) => void;
|
|
176
|
+
readonly setSelectedManifestIndex: (index: number) => void;
|
|
177
|
+
readonly setSelectedAlertIndex: (index: number) => void;
|
|
178
|
+
readonly setSelectedFraudIndex: (index: number) => void;
|
|
179
|
+
readonly setSelectedDelegationIndex: (index: number) => void;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const SOURCE = "access";
|
|
183
|
+
|
|
184
|
+
export const useAccessStore = create<AccessState>((set, get) => ({
|
|
185
|
+
manifests: [],
|
|
186
|
+
selectedManifestIndex: 0,
|
|
187
|
+
manifestsLoading: false,
|
|
188
|
+
lastPermissionCheck: null,
|
|
189
|
+
permissionCheckLoading: false,
|
|
190
|
+
alerts: [],
|
|
191
|
+
alertsLoading: false,
|
|
192
|
+
selectedAlertIndex: 0,
|
|
193
|
+
credentials: [],
|
|
194
|
+
credentialsLoading: false,
|
|
195
|
+
fraudScores: [],
|
|
196
|
+
fraudScoresLoading: false,
|
|
197
|
+
selectedFraudIndex: 0,
|
|
198
|
+
delegations: [],
|
|
199
|
+
delegationsLoading: false,
|
|
200
|
+
selectedDelegationIndex: 0,
|
|
201
|
+
lastDelegationCreate: null,
|
|
202
|
+
delegationChain: null,
|
|
203
|
+
delegationChainLoading: false,
|
|
204
|
+
namespaceDetail: null,
|
|
205
|
+
namespaceDetailLoading: false,
|
|
206
|
+
governanceCheck: null,
|
|
207
|
+
governanceCheckLoading: false,
|
|
208
|
+
collusionRings: [],
|
|
209
|
+
collusionLoading: false,
|
|
210
|
+
constraints: [],
|
|
211
|
+
constraintsLoading: false,
|
|
212
|
+
selectedConstraintIndex: 0,
|
|
213
|
+
activeTab: "manifests",
|
|
214
|
+
error: null,
|
|
215
|
+
|
|
216
|
+
// =========================================================================
|
|
217
|
+
// Actions migrated to createApiAction (Decision 6A)
|
|
218
|
+
// =========================================================================
|
|
219
|
+
|
|
220
|
+
// ── Manifests ──────────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
fetchManifests: createApiAction<AccessState, [FetchClient]>(set, {
|
|
223
|
+
loadingKey: "manifestsLoading",
|
|
224
|
+
source: SOURCE,
|
|
225
|
+
action: async (client) => {
|
|
226
|
+
const response = await client.get<{
|
|
227
|
+
readonly manifests: readonly AccessManifest[];
|
|
228
|
+
readonly offset: number;
|
|
229
|
+
readonly limit: number;
|
|
230
|
+
readonly count: number;
|
|
231
|
+
}>("/api/v2/access-manifests");
|
|
232
|
+
return {
|
|
233
|
+
manifests: response.manifests,
|
|
234
|
+
selectedManifestIndex: 0,
|
|
235
|
+
};
|
|
236
|
+
},
|
|
237
|
+
}),
|
|
238
|
+
|
|
239
|
+
fetchManifestDetail: async (manifestId, client) => {
|
|
240
|
+
try {
|
|
241
|
+
const manifest = await client.get<AccessManifest>(
|
|
242
|
+
`/api/v2/access-manifests/${encodeURIComponent(manifestId)}`,
|
|
243
|
+
);
|
|
244
|
+
set((state) => {
|
|
245
|
+
const idx = state.manifests.findIndex((m) => m.manifest_id === manifestId);
|
|
246
|
+
if (idx >= 0) {
|
|
247
|
+
return {
|
|
248
|
+
manifests: state.manifests.map((m, i) => (i === idx ? manifest : m)),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return {};
|
|
252
|
+
});
|
|
253
|
+
} catch {
|
|
254
|
+
// Non-critical: entries just won't be available for the checker trace
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
checkPermission: createApiAction<AccessState, [string, string, FetchClient]>(set, {
|
|
259
|
+
loadingKey: "permissionCheckLoading",
|
|
260
|
+
source: SOURCE,
|
|
261
|
+
action: async (manifestId, toolName, client) => {
|
|
262
|
+
const response = await client.post<{
|
|
263
|
+
readonly tool_name: string;
|
|
264
|
+
readonly permission: string;
|
|
265
|
+
readonly agent_id: string;
|
|
266
|
+
readonly manifest_id: string;
|
|
267
|
+
readonly trace?: EvaluationTraceResult;
|
|
268
|
+
}>(`/api/v2/access-manifests/${encodeURIComponent(manifestId)}/evaluate`, {
|
|
269
|
+
tool_name: toolName,
|
|
270
|
+
});
|
|
271
|
+
return {
|
|
272
|
+
lastPermissionCheck: {
|
|
273
|
+
tool_name: response.tool_name,
|
|
274
|
+
permission: response.permission,
|
|
275
|
+
agent_id: response.agent_id,
|
|
276
|
+
manifest_id: response.manifest_id,
|
|
277
|
+
trace: response.trace ?? null,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
}),
|
|
282
|
+
|
|
283
|
+
// ── Alerts ─────────────────────────────────────────────────────────────
|
|
284
|
+
|
|
285
|
+
fetchAlerts: createApiAction<AccessState, [string | undefined, FetchClient]>(set, {
|
|
286
|
+
loadingKey: "alertsLoading",
|
|
287
|
+
source: SOURCE,
|
|
288
|
+
action: async (zoneId, client) => {
|
|
289
|
+
const params = zoneId ? `?zone_id=${encodeURIComponent(zoneId)}` : "";
|
|
290
|
+
const response = await client.get<{
|
|
291
|
+
readonly alerts: readonly GovernanceAlert[];
|
|
292
|
+
}>(`/api/v2/governance/alerts${params}`);
|
|
293
|
+
return {
|
|
294
|
+
alerts: response.alerts,
|
|
295
|
+
selectedAlertIndex: 0,
|
|
296
|
+
};
|
|
297
|
+
},
|
|
298
|
+
}),
|
|
299
|
+
|
|
300
|
+
resolveAlert: async (alertId, resolvedBy, zoneId, client) => {
|
|
301
|
+
set({ alertsLoading: true, error: null });
|
|
302
|
+
try {
|
|
303
|
+
const params = zoneId ? `?zone_id=${encodeURIComponent(zoneId)}` : "";
|
|
304
|
+
await client.post<{
|
|
305
|
+
readonly alert_id: string;
|
|
306
|
+
readonly resolved: boolean;
|
|
307
|
+
readonly resolved_by: string;
|
|
308
|
+
}>(`/api/v2/governance/alerts/${encodeURIComponent(alertId)}/resolve${params}`, {
|
|
309
|
+
resolved_by: resolvedBy,
|
|
310
|
+
});
|
|
311
|
+
set((state) => ({
|
|
312
|
+
alerts: state.alerts.map((a) =>
|
|
313
|
+
a.alert_id === alertId ? { ...a, resolved: true } : a,
|
|
314
|
+
),
|
|
315
|
+
alertsLoading: false,
|
|
316
|
+
}));
|
|
317
|
+
} catch (err) {
|
|
318
|
+
const message = err instanceof Error ? err.message : "Failed to resolve alert";
|
|
319
|
+
set({ alertsLoading: false, error: message });
|
|
320
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
// ── Credentials ────────────────────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
fetchCredentials: createApiAction<AccessState, [string, FetchClient]>(set, {
|
|
327
|
+
loadingKey: "credentialsLoading",
|
|
328
|
+
source: SOURCE,
|
|
329
|
+
action: async (agentId, client) => {
|
|
330
|
+
const response = await client.get<{
|
|
331
|
+
readonly agent_id: string;
|
|
332
|
+
readonly count: number;
|
|
333
|
+
readonly credentials: readonly Credential[];
|
|
334
|
+
}>(`/api/v2/agents/${encodeURIComponent(agentId)}/credentials`);
|
|
335
|
+
return { credentials: response.credentials };
|
|
336
|
+
},
|
|
337
|
+
}),
|
|
338
|
+
|
|
339
|
+
issueCredential: async (agentId, claims, client) => {
|
|
340
|
+
set({ credentialsLoading: true, error: null });
|
|
341
|
+
try {
|
|
342
|
+
await client.post(`/api/v2/agents/${encodeURIComponent(agentId)}/credentials`, { claims });
|
|
343
|
+
await get().fetchCredentials(agentId, client);
|
|
344
|
+
} catch (err) {
|
|
345
|
+
const message = err instanceof Error ? err.message : "Failed to issue credential";
|
|
346
|
+
set({ credentialsLoading: false, error: message });
|
|
347
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
revokeCredential: async (credentialId, agentId, client) => {
|
|
352
|
+
set({ credentialsLoading: true, error: null });
|
|
353
|
+
try {
|
|
354
|
+
await client.post(`/api/v2/agents/${encodeURIComponent(agentId)}/credentials/${encodeURIComponent(credentialId)}/revoke`, {});
|
|
355
|
+
set((state) => ({
|
|
356
|
+
credentials: state.credentials.map((c) =>
|
|
357
|
+
c.credential_id === credentialId ? { ...c, is_active: false, revoked_at: new Date().toISOString() } : c,
|
|
358
|
+
),
|
|
359
|
+
credentialsLoading: false,
|
|
360
|
+
}));
|
|
361
|
+
} catch (err) {
|
|
362
|
+
const message = err instanceof Error ? err.message : "Failed to revoke credential";
|
|
363
|
+
set({ credentialsLoading: false, error: message });
|
|
364
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
// ── Fraud scores ───────────────────────────────────────────────────────
|
|
369
|
+
|
|
370
|
+
fetchFraudScores: createApiAction<AccessState, [string | undefined, FetchClient]>(set, {
|
|
371
|
+
loadingKey: "fraudScoresLoading",
|
|
372
|
+
source: SOURCE,
|
|
373
|
+
action: async (zoneId, client) => {
|
|
374
|
+
const params = zoneId ? `?zone_id=${encodeURIComponent(zoneId)}` : "";
|
|
375
|
+
const response = await client.get<{
|
|
376
|
+
readonly scores: readonly FraudScore[];
|
|
377
|
+
readonly count: number;
|
|
378
|
+
}>(`/api/v2/governance/fraud-scores${params}`);
|
|
379
|
+
return {
|
|
380
|
+
fraudScores: response.scores,
|
|
381
|
+
selectedFraudIndex: 0,
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
}),
|
|
385
|
+
|
|
386
|
+
computeFraudScores: createApiAction<AccessState, [string | undefined, FetchClient]>(set, {
|
|
387
|
+
loadingKey: "fraudScoresLoading",
|
|
388
|
+
source: SOURCE,
|
|
389
|
+
action: async (zoneId, client) => {
|
|
390
|
+
const params = zoneId ? `?zone_id=${encodeURIComponent(zoneId)}` : "";
|
|
391
|
+
const response = await client.post<{
|
|
392
|
+
readonly scores: readonly FraudScore[];
|
|
393
|
+
readonly count: number;
|
|
394
|
+
}>(`/api/v2/governance/fraud-scores/compute${params}`, {});
|
|
395
|
+
return {
|
|
396
|
+
fraudScores: response.scores,
|
|
397
|
+
selectedFraudIndex: 0,
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
}),
|
|
401
|
+
|
|
402
|
+
// ── Manifests (create/revoke) ─────────────────────────────────────────
|
|
403
|
+
|
|
404
|
+
createManifest: async (payload, client) => {
|
|
405
|
+
set({ manifestsLoading: true, error: null });
|
|
406
|
+
try {
|
|
407
|
+
await client.post<AccessManifest>("/api/v2/access-manifests", payload);
|
|
408
|
+
// Re-fetch manifest list
|
|
409
|
+
const response = await client.get<{ manifests: readonly AccessManifest[]; }>("/api/v2/access-manifests");
|
|
410
|
+
set({ manifests: response.manifests, manifestsLoading: false, selectedManifestIndex: 0 });
|
|
411
|
+
} catch (err) {
|
|
412
|
+
const message = err instanceof Error ? err.message : "Failed to create manifest";
|
|
413
|
+
set({ manifestsLoading: false, error: message });
|
|
414
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
|
|
418
|
+
revokeManifest: async (manifestId, client) => {
|
|
419
|
+
set({ manifestsLoading: true, error: null });
|
|
420
|
+
try {
|
|
421
|
+
await client.post(`/api/v2/access-manifests/${encodeURIComponent(manifestId)}/revoke`, {});
|
|
422
|
+
set((state) => ({
|
|
423
|
+
manifests: state.manifests.map((m) =>
|
|
424
|
+
m.manifest_id === manifestId ? { ...m, status: "revoked" } : m,
|
|
425
|
+
),
|
|
426
|
+
manifestsLoading: false,
|
|
427
|
+
}));
|
|
428
|
+
} catch (err) {
|
|
429
|
+
const message = err instanceof Error ? err.message : "Failed to revoke manifest";
|
|
430
|
+
set({ manifestsLoading: false, error: message });
|
|
431
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
// ── UI ─────────────────────────────────────────────────────────────────
|
|
436
|
+
|
|
437
|
+
setActiveTab: (tab) => {
|
|
438
|
+
set({ activeTab: tab, error: null });
|
|
439
|
+
},
|
|
440
|
+
|
|
441
|
+
setSelectedManifestIndex: (index) => {
|
|
442
|
+
set({ selectedManifestIndex: index });
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
setSelectedAlertIndex: (index) => {
|
|
446
|
+
set({ selectedAlertIndex: index });
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
setSelectedFraudIndex: (index) => {
|
|
450
|
+
set({ selectedFraudIndex: index });
|
|
451
|
+
},
|
|
452
|
+
|
|
453
|
+
// ── Delegations ─────────────────────────────────────────────────────────
|
|
454
|
+
|
|
455
|
+
fetchDelegations: async (client, status) => {
|
|
456
|
+
set({ delegationsLoading: true, error: null });
|
|
457
|
+
try {
|
|
458
|
+
let url = "/api/v2/agents/delegate";
|
|
459
|
+
if (status) url += `?status=${encodeURIComponent(status)}`;
|
|
460
|
+
const response = await client.get<{
|
|
461
|
+
readonly delegations: readonly DelegationItem[];
|
|
462
|
+
readonly count: number;
|
|
463
|
+
}>(url);
|
|
464
|
+
set({
|
|
465
|
+
delegations: response.delegations,
|
|
466
|
+
delegationsLoading: false,
|
|
467
|
+
selectedDelegationIndex: 0,
|
|
468
|
+
});
|
|
469
|
+
} catch (err) {
|
|
470
|
+
const message = err instanceof Error ? err.message : "Failed to fetch delegations";
|
|
471
|
+
set({ delegationsLoading: false, error: message });
|
|
472
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
createDelegation: async (request, client) => {
|
|
477
|
+
set({ delegationsLoading: true, error: null });
|
|
478
|
+
try {
|
|
479
|
+
const response = await client.post<DelegationCreateResponse>(
|
|
480
|
+
"/api/v2/agents/delegate",
|
|
481
|
+
request,
|
|
482
|
+
);
|
|
483
|
+
set({ lastDelegationCreate: response, delegationsLoading: false });
|
|
484
|
+
} catch (err) {
|
|
485
|
+
const message = err instanceof Error ? err.message : "Failed to create delegation";
|
|
486
|
+
set({ delegationsLoading: false, error: message });
|
|
487
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
// Re-fetch list separately — a GET failure here must not mask the successful POST
|
|
491
|
+
try {
|
|
492
|
+
const listResponse = await client.get<{
|
|
493
|
+
readonly delegations: readonly DelegationItem[];
|
|
494
|
+
readonly count: number;
|
|
495
|
+
}>("/api/v2/agents/delegate");
|
|
496
|
+
set({
|
|
497
|
+
delegations: listResponse.delegations,
|
|
498
|
+
selectedDelegationIndex: 0,
|
|
499
|
+
});
|
|
500
|
+
} catch {
|
|
501
|
+
// Non-critical: list will refresh on next tab visit
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
revokeDelegation: async (delegationId, client) => {
|
|
506
|
+
set({ delegationsLoading: true, error: null });
|
|
507
|
+
try {
|
|
508
|
+
await client.delete<{
|
|
509
|
+
readonly status: string;
|
|
510
|
+
readonly delegation_id: string;
|
|
511
|
+
}>(`/api/v2/agents/delegate/${encodeURIComponent(delegationId)}`);
|
|
512
|
+
set((state) => ({
|
|
513
|
+
delegations: state.delegations.map((d) =>
|
|
514
|
+
d.delegation_id === delegationId ? { ...d, status: "revoked" } : d,
|
|
515
|
+
),
|
|
516
|
+
delegationsLoading: false,
|
|
517
|
+
}));
|
|
518
|
+
} catch (err) {
|
|
519
|
+
const message = err instanceof Error ? err.message : "Failed to revoke delegation";
|
|
520
|
+
set({ delegationsLoading: false, error: message });
|
|
521
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
|
|
525
|
+
completeDelegation: async (delegationId, outcome, qualityScore, client) => {
|
|
526
|
+
set({ delegationsLoading: true, error: null });
|
|
527
|
+
try {
|
|
528
|
+
const body: { outcome: string; quality_score?: number } = { outcome };
|
|
529
|
+
if (qualityScore !== null) {
|
|
530
|
+
body.quality_score = qualityScore;
|
|
531
|
+
}
|
|
532
|
+
await client.post<{
|
|
533
|
+
readonly status: string;
|
|
534
|
+
readonly delegation_id: string;
|
|
535
|
+
readonly outcome: string;
|
|
536
|
+
}>(`/api/v2/agents/delegate/${encodeURIComponent(delegationId)}/complete`, body);
|
|
537
|
+
set((state) => ({
|
|
538
|
+
delegations: state.delegations.map((d) =>
|
|
539
|
+
d.delegation_id === delegationId ? { ...d, status: "completed" } : d,
|
|
540
|
+
),
|
|
541
|
+
delegationsLoading: false,
|
|
542
|
+
}));
|
|
543
|
+
} catch (err) {
|
|
544
|
+
const message = err instanceof Error ? err.message : "Failed to complete delegation";
|
|
545
|
+
set({ delegationsLoading: false, error: message });
|
|
546
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
|
|
550
|
+
fetchDelegationChain: createApiAction<AccessState, [string, FetchClient]>(set, {
|
|
551
|
+
loadingKey: "delegationChainLoading",
|
|
552
|
+
source: SOURCE,
|
|
553
|
+
action: async (delegationId, client) => {
|
|
554
|
+
const response = await client.get<DelegationChain>(
|
|
555
|
+
`/api/v2/agents/delegate/${encodeURIComponent(delegationId)}/chain`,
|
|
556
|
+
);
|
|
557
|
+
return { delegationChain: response };
|
|
558
|
+
},
|
|
559
|
+
}),
|
|
560
|
+
|
|
561
|
+
fetchNamespaceDetail: createApiAction<AccessState, [string, FetchClient]>(set, {
|
|
562
|
+
loadingKey: "namespaceDetailLoading",
|
|
563
|
+
source: SOURCE,
|
|
564
|
+
action: async (delegationId, client) => {
|
|
565
|
+
const response = await client.get<NamespaceDetail>(
|
|
566
|
+
`/api/v2/agents/delegate/${encodeURIComponent(delegationId)}/namespace`,
|
|
567
|
+
);
|
|
568
|
+
return { namespaceDetail: response };
|
|
569
|
+
},
|
|
570
|
+
}),
|
|
571
|
+
|
|
572
|
+
updateNamespaceConfig: createApiAction<AccessState, [string, { readonly scope_prefix?: string; readonly remove_grants?: readonly string[]; readonly add_grants?: readonly string[]; readonly readonly_paths?: readonly string[] }, FetchClient]>(set, {
|
|
573
|
+
loadingKey: "namespaceDetailLoading",
|
|
574
|
+
source: SOURCE,
|
|
575
|
+
action: async (delegationId, update, client) => {
|
|
576
|
+
const response = await client.patch<NamespaceDetail>(
|
|
577
|
+
`/api/v2/agents/delegate/${encodeURIComponent(delegationId)}/namespace`,
|
|
578
|
+
update,
|
|
579
|
+
);
|
|
580
|
+
return { namespaceDetail: response };
|
|
581
|
+
},
|
|
582
|
+
}),
|
|
583
|
+
|
|
584
|
+
setSelectedDelegationIndex: (index) => {
|
|
585
|
+
set({ selectedDelegationIndex: index });
|
|
586
|
+
},
|
|
587
|
+
|
|
588
|
+
// ── Governance check ────────────────────────────────────────────────────
|
|
589
|
+
|
|
590
|
+
checkGovernanceEdge: createApiAction<AccessState, [string, string, string | undefined, FetchClient]>(set, {
|
|
591
|
+
loadingKey: "governanceCheckLoading",
|
|
592
|
+
source: SOURCE,
|
|
593
|
+
action: async (fromAgentId, toAgentId, zoneId, client) => {
|
|
594
|
+
const params = zoneId ? `?zone_id=${encodeURIComponent(zoneId)}` : "";
|
|
595
|
+
const response = await client.get<GovernanceCheckResult>(
|
|
596
|
+
`/api/v2/governance/check/${encodeURIComponent(fromAgentId)}/${encodeURIComponent(toAgentId)}${params}`,
|
|
597
|
+
);
|
|
598
|
+
return { governanceCheck: response };
|
|
599
|
+
},
|
|
600
|
+
}),
|
|
601
|
+
|
|
602
|
+
// ── Governance constraints ─────────────────────────────────────────────
|
|
603
|
+
|
|
604
|
+
fetchConstraints: createApiAction<AccessState, [string, FetchClient]>(set, {
|
|
605
|
+
loadingKey: "constraintsLoading",
|
|
606
|
+
source: SOURCE,
|
|
607
|
+
action: async (zoneId, client) => {
|
|
608
|
+
const response = await client.get<{
|
|
609
|
+
readonly constraints: readonly GovernanceConstraint[];
|
|
610
|
+
}>(`/api/v2/governance/constraints?zone_id=${encodeURIComponent(zoneId)}`);
|
|
611
|
+
return {
|
|
612
|
+
constraints: response.constraints,
|
|
613
|
+
selectedConstraintIndex: 0,
|
|
614
|
+
};
|
|
615
|
+
},
|
|
616
|
+
}),
|
|
617
|
+
|
|
618
|
+
createConstraint: async (constraint, client) => {
|
|
619
|
+
set({ constraintsLoading: true, error: null });
|
|
620
|
+
try {
|
|
621
|
+
await client.post("/api/v2/governance/constraints", constraint);
|
|
622
|
+
await get().fetchConstraints(constraint.zone_id, client);
|
|
623
|
+
} catch (err) {
|
|
624
|
+
const message = err instanceof Error ? err.message : "Failed to create constraint";
|
|
625
|
+
set({ constraintsLoading: false, error: message });
|
|
626
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
|
|
630
|
+
deleteConstraint: async (constraintId, client) => {
|
|
631
|
+
set({ constraintsLoading: true, error: null });
|
|
632
|
+
try {
|
|
633
|
+
await client.delete(`/api/v2/governance/constraints/${encodeURIComponent(constraintId)}`);
|
|
634
|
+
set((state) => ({
|
|
635
|
+
constraints: state.constraints.filter((c) => c.id !== constraintId),
|
|
636
|
+
constraintsLoading: false,
|
|
637
|
+
}));
|
|
638
|
+
} catch (err) {
|
|
639
|
+
const message = err instanceof Error ? err.message : "Failed to delete constraint";
|
|
640
|
+
set({ constraintsLoading: false, error: message });
|
|
641
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
|
|
645
|
+
setSelectedConstraintIndex: (index) => {
|
|
646
|
+
set({ selectedConstraintIndex: index });
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
// ── Governance deep features ───────────────────────────────────────────
|
|
650
|
+
|
|
651
|
+
fetchCollusionRings: createApiAction<AccessState, [string | undefined, FetchClient]>(set, {
|
|
652
|
+
loadingKey: "collusionLoading",
|
|
653
|
+
source: SOURCE,
|
|
654
|
+
action: async (zoneId, client) => {
|
|
655
|
+
const params = zoneId ? `?zone_id=${encodeURIComponent(zoneId)}` : "";
|
|
656
|
+
const response = await client.get<{ rings: readonly unknown[] }>(
|
|
657
|
+
`/api/v2/governance/collusion-rings${params}`,
|
|
658
|
+
);
|
|
659
|
+
return { collusionRings: response.rings ?? [] };
|
|
660
|
+
},
|
|
661
|
+
}),
|
|
662
|
+
|
|
663
|
+
suspendAgent: async (agentId, reason, zoneId, client) => {
|
|
664
|
+
set({ error: null });
|
|
665
|
+
try {
|
|
666
|
+
const params = zoneId ? `?zone_id=${encodeURIComponent(zoneId)}` : "";
|
|
667
|
+
await client.post(`/api/v2/governance/suspend/${encodeURIComponent(agentId)}${params}`, { reason });
|
|
668
|
+
} catch (err) {
|
|
669
|
+
const message = err instanceof Error ? err.message : "Failed to suspend agent";
|
|
670
|
+
set({ error: message });
|
|
671
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
}));
|