@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,545 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand store for Payments & Credits panel.
|
|
3
|
+
*
|
|
4
|
+
* Manages balance queries, credit transfers, reservations (hold/commit/release),
|
|
5
|
+
* transaction audit feed, and spending policies / budget.
|
|
6
|
+
*
|
|
7
|
+
* Reservations are tracked locally (from createReservation responses) because
|
|
8
|
+
* the backend has no reservation list endpoint.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { create } from "zustand";
|
|
12
|
+
import type { FetchClient } from "@nexus-ai-fs/api-client";
|
|
13
|
+
import { createApiAction, categorizeError } from "./create-api-action.js";
|
|
14
|
+
import { useErrorStore } from "./error-store.js";
|
|
15
|
+
import { useUiStore } from "./ui-store.js";
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Types (snake_case matching API wire format)
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
export interface BalanceInfo {
|
|
22
|
+
readonly available: string;
|
|
23
|
+
readonly reserved: string;
|
|
24
|
+
readonly total: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Matches backend ReceiptResponse from pay.py. */
|
|
28
|
+
export interface TransferReceipt {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly method: string;
|
|
31
|
+
readonly amount: string;
|
|
32
|
+
readonly from_agent: string;
|
|
33
|
+
readonly to_agent: string;
|
|
34
|
+
readonly memo: string | null;
|
|
35
|
+
readonly timestamp: string | null;
|
|
36
|
+
readonly tx_hash: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Matches backend ReservationResponse from pay.py. */
|
|
40
|
+
export interface Reservation {
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly amount: string;
|
|
43
|
+
readonly purpose: string;
|
|
44
|
+
readonly expires_at: string | null;
|
|
45
|
+
readonly status: "pending" | "committed" | "released";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Matches backend audit transaction record. */
|
|
49
|
+
export interface TransactionRecord {
|
|
50
|
+
readonly id: string;
|
|
51
|
+
readonly record_hash: string;
|
|
52
|
+
readonly created_at: string;
|
|
53
|
+
readonly protocol: string;
|
|
54
|
+
readonly buyer_agent_id: string;
|
|
55
|
+
readonly seller_agent_id: string;
|
|
56
|
+
readonly amount: string;
|
|
57
|
+
readonly currency: string;
|
|
58
|
+
readonly status: string;
|
|
59
|
+
readonly zone_id: string;
|
|
60
|
+
readonly trace_id: string | null;
|
|
61
|
+
readonly metadata_hash: string | null;
|
|
62
|
+
readonly transfer_id: string | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Matches backend PolicyResponse from pay.py:541. All limit fields are nullable. */
|
|
66
|
+
export interface PolicyRecord {
|
|
67
|
+
readonly policy_id: string;
|
|
68
|
+
readonly zone_id: string;
|
|
69
|
+
readonly agent_id: string | null;
|
|
70
|
+
readonly daily_limit: string | null;
|
|
71
|
+
readonly weekly_limit: string | null;
|
|
72
|
+
readonly monthly_limit: string | null;
|
|
73
|
+
readonly per_tx_limit: string | null;
|
|
74
|
+
readonly auto_approve_threshold: string | null;
|
|
75
|
+
readonly max_tx_per_hour: number | null;
|
|
76
|
+
readonly max_tx_per_day: number | null;
|
|
77
|
+
readonly rules: readonly unknown[] | null;
|
|
78
|
+
readonly priority: number;
|
|
79
|
+
readonly enabled: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Matches backend budget summary. */
|
|
83
|
+
export interface BudgetSummary {
|
|
84
|
+
readonly has_policy: boolean;
|
|
85
|
+
readonly policy_id: string | null;
|
|
86
|
+
readonly limits: {
|
|
87
|
+
readonly daily: string;
|
|
88
|
+
readonly weekly: string;
|
|
89
|
+
readonly monthly: string;
|
|
90
|
+
};
|
|
91
|
+
readonly spent: {
|
|
92
|
+
readonly daily: string;
|
|
93
|
+
readonly weekly: string;
|
|
94
|
+
readonly monthly: string;
|
|
95
|
+
};
|
|
96
|
+
readonly remaining: {
|
|
97
|
+
readonly daily: string;
|
|
98
|
+
readonly weekly: string;
|
|
99
|
+
readonly monthly: string;
|
|
100
|
+
};
|
|
101
|
+
readonly rate_limits: unknown;
|
|
102
|
+
readonly has_rules: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Integrity verification result. */
|
|
106
|
+
export interface IntegrityResult {
|
|
107
|
+
readonly record_id: string;
|
|
108
|
+
readonly is_valid: boolean;
|
|
109
|
+
readonly record_hash: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Matches backend spending approval request. */
|
|
113
|
+
export interface ApprovalRequest {
|
|
114
|
+
readonly id: string;
|
|
115
|
+
readonly requester_id: string;
|
|
116
|
+
readonly amount: number;
|
|
117
|
+
readonly purpose: string;
|
|
118
|
+
readonly status: "pending" | "approved" | "rejected";
|
|
119
|
+
readonly created_at: string;
|
|
120
|
+
readonly decided_at: string | null;
|
|
121
|
+
readonly decided_by: string | null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type PaymentsTab = "balance" | "reservations" | "transactions" | "policies" | "approvals";
|
|
125
|
+
|
|
126
|
+
// =============================================================================
|
|
127
|
+
// Wire response shapes
|
|
128
|
+
// =============================================================================
|
|
129
|
+
|
|
130
|
+
interface TransactionsResponse {
|
|
131
|
+
readonly transactions: readonly TransactionRecord[];
|
|
132
|
+
readonly limit: number;
|
|
133
|
+
readonly has_more: boolean;
|
|
134
|
+
readonly total: number;
|
|
135
|
+
readonly next_cursor: string | null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Note: GET /api/v2/pay/policies returns a bare list[PolicyResponse] (pay.py:641),
|
|
139
|
+
// not a wrapper object. We type the response as PolicyRecord[] directly.
|
|
140
|
+
|
|
141
|
+
// =============================================================================
|
|
142
|
+
// Store
|
|
143
|
+
// =============================================================================
|
|
144
|
+
|
|
145
|
+
export interface PaymentsState {
|
|
146
|
+
// Balance
|
|
147
|
+
readonly balance: BalanceInfo | null;
|
|
148
|
+
readonly balanceLoading: boolean;
|
|
149
|
+
|
|
150
|
+
// Reservations (tracked locally from create responses)
|
|
151
|
+
readonly reservations: readonly Reservation[];
|
|
152
|
+
readonly selectedReservationIndex: number;
|
|
153
|
+
readonly reservationsLoading: boolean;
|
|
154
|
+
|
|
155
|
+
// Transactions (audit feed with cursor-based pagination)
|
|
156
|
+
readonly transactions: readonly TransactionRecord[];
|
|
157
|
+
readonly transactionsLoading: boolean;
|
|
158
|
+
readonly selectedTransactionIndex: number;
|
|
159
|
+
readonly transactionsHasMore: boolean;
|
|
160
|
+
readonly transactionsNextCursor: string | null;
|
|
161
|
+
readonly transactionsCursorStack: readonly string[];
|
|
162
|
+
readonly transactionsTotal: number | null;
|
|
163
|
+
readonly integrityResult: IntegrityResult | null;
|
|
164
|
+
|
|
165
|
+
// Policies & budget
|
|
166
|
+
readonly policies: readonly PolicyRecord[];
|
|
167
|
+
readonly policiesLoading: boolean;
|
|
168
|
+
readonly budget: BudgetSummary | null;
|
|
169
|
+
readonly budgetLoading: boolean;
|
|
170
|
+
|
|
171
|
+
// Affordability check
|
|
172
|
+
readonly affordResult: { can_afford: boolean; balance: string; requested: string } | null;
|
|
173
|
+
|
|
174
|
+
// Approvals
|
|
175
|
+
readonly approvals: readonly ApprovalRequest[];
|
|
176
|
+
readonly approvalsLoading: boolean;
|
|
177
|
+
readonly selectedApprovalIndex: number;
|
|
178
|
+
|
|
179
|
+
// UI state
|
|
180
|
+
readonly activeTab: PaymentsTab;
|
|
181
|
+
readonly error: string | null;
|
|
182
|
+
|
|
183
|
+
// Actions
|
|
184
|
+
readonly fetchBalance: (client: FetchClient) => Promise<void>;
|
|
185
|
+
readonly transfer: (
|
|
186
|
+
to: string,
|
|
187
|
+
amount: string,
|
|
188
|
+
memo: string,
|
|
189
|
+
client: FetchClient,
|
|
190
|
+
) => Promise<void>;
|
|
191
|
+
readonly createReservation: (
|
|
192
|
+
amount: string,
|
|
193
|
+
purpose: string,
|
|
194
|
+
timeout: number,
|
|
195
|
+
client: FetchClient,
|
|
196
|
+
) => Promise<void>;
|
|
197
|
+
readonly commitReservation: (id: string, client: FetchClient) => Promise<void>;
|
|
198
|
+
readonly releaseReservation: (id: string, client: FetchClient) => Promise<void>;
|
|
199
|
+
readonly fetchTransactions: (client: FetchClient, cursor?: string) => Promise<void>;
|
|
200
|
+
readonly fetchNextTransactions: (client: FetchClient) => Promise<void>;
|
|
201
|
+
readonly fetchPrevTransactions: (client: FetchClient) => Promise<void>;
|
|
202
|
+
readonly fetchPolicies: (client: FetchClient) => Promise<void>;
|
|
203
|
+
readonly fetchBudget: (client: FetchClient) => Promise<void>;
|
|
204
|
+
readonly deletePolicy: (policyId: string, client: FetchClient) => Promise<void>;
|
|
205
|
+
readonly verifyIntegrity: (recordId: string, client: FetchClient) => Promise<IntegrityResult | null>;
|
|
206
|
+
readonly createPolicy: (name: string, rules: Record<string, unknown>, client: FetchClient) => Promise<void>;
|
|
207
|
+
readonly checkAfford: (amount: string, client: FetchClient) => Promise<void>;
|
|
208
|
+
readonly fetchApprovals: (client: FetchClient) => Promise<void>;
|
|
209
|
+
readonly requestApproval: (amount: number, purpose: string, client: FetchClient) => Promise<void>;
|
|
210
|
+
readonly approveRequest: (approvalId: string, client: FetchClient) => Promise<void>;
|
|
211
|
+
readonly rejectRequest: (approvalId: string, client: FetchClient) => Promise<void>;
|
|
212
|
+
readonly setSelectedApprovalIndex: (index: number) => void;
|
|
213
|
+
readonly setActiveTab: (tab: PaymentsTab) => void;
|
|
214
|
+
readonly setSelectedReservationIndex: (index: number) => void;
|
|
215
|
+
readonly setSelectedTransactionIndex: (index: number) => void;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const SOURCE = "payments";
|
|
219
|
+
|
|
220
|
+
export const usePaymentsStore = create<PaymentsState>((set, get) => ({
|
|
221
|
+
balance: null,
|
|
222
|
+
balanceLoading: false,
|
|
223
|
+
reservations: [],
|
|
224
|
+
selectedReservationIndex: 0,
|
|
225
|
+
reservationsLoading: false,
|
|
226
|
+
transactions: [],
|
|
227
|
+
transactionsLoading: false,
|
|
228
|
+
selectedTransactionIndex: 0,
|
|
229
|
+
transactionsHasMore: false,
|
|
230
|
+
transactionsNextCursor: null,
|
|
231
|
+
transactionsCursorStack: [],
|
|
232
|
+
transactionsTotal: null,
|
|
233
|
+
integrityResult: null,
|
|
234
|
+
policies: [],
|
|
235
|
+
policiesLoading: false,
|
|
236
|
+
budget: null,
|
|
237
|
+
budgetLoading: false,
|
|
238
|
+
affordResult: null,
|
|
239
|
+
approvals: [],
|
|
240
|
+
approvalsLoading: false,
|
|
241
|
+
selectedApprovalIndex: 0,
|
|
242
|
+
activeTab: "balance",
|
|
243
|
+
error: null,
|
|
244
|
+
|
|
245
|
+
// =========================================================================
|
|
246
|
+
// Actions migrated to createApiAction
|
|
247
|
+
// =========================================================================
|
|
248
|
+
|
|
249
|
+
fetchBalance: createApiAction<PaymentsState, [FetchClient]>(set, {
|
|
250
|
+
loadingKey: "balanceLoading",
|
|
251
|
+
source: SOURCE,
|
|
252
|
+
errorMessage: "Failed to fetch balance",
|
|
253
|
+
action: async (client) => {
|
|
254
|
+
const balance = await client.get<BalanceInfo>("/api/v2/pay/balance");
|
|
255
|
+
return { balance: balance ?? null };
|
|
256
|
+
},
|
|
257
|
+
}),
|
|
258
|
+
|
|
259
|
+
fetchPolicies: createApiAction<PaymentsState, [FetchClient]>(set, {
|
|
260
|
+
loadingKey: "policiesLoading",
|
|
261
|
+
source: SOURCE,
|
|
262
|
+
errorMessage: "Failed to fetch policies",
|
|
263
|
+
action: async (client) => {
|
|
264
|
+
// Backend returns bare list[PolicyResponse], not a wrapper object
|
|
265
|
+
const policies = await client.get<readonly PolicyRecord[]>("/api/v2/pay/policies");
|
|
266
|
+
return { policies };
|
|
267
|
+
},
|
|
268
|
+
}),
|
|
269
|
+
|
|
270
|
+
fetchBudget: createApiAction<PaymentsState, [FetchClient]>(set, {
|
|
271
|
+
loadingKey: "budgetLoading",
|
|
272
|
+
source: SOURCE,
|
|
273
|
+
errorMessage: "Failed to fetch budget",
|
|
274
|
+
action: async (client) => {
|
|
275
|
+
const budget = await client.get<BudgetSummary>("/api/v2/pay/budget");
|
|
276
|
+
return { budget: budget ?? null };
|
|
277
|
+
},
|
|
278
|
+
}),
|
|
279
|
+
|
|
280
|
+
// =========================================================================
|
|
281
|
+
// Actions without loading keys or with special patterns — inline with error store
|
|
282
|
+
// =========================================================================
|
|
283
|
+
|
|
284
|
+
transfer: async (to, amount, memo, client) => {
|
|
285
|
+
set({ error: null });
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
await client.post<TransferReceipt>("/api/v2/pay/transfer", {
|
|
289
|
+
to,
|
|
290
|
+
amount,
|
|
291
|
+
memo,
|
|
292
|
+
});
|
|
293
|
+
await get().fetchBalance(client);
|
|
294
|
+
} catch (err) {
|
|
295
|
+
const message = err instanceof Error ? err.message : "Failed to transfer credits";
|
|
296
|
+
set({ error: message });
|
|
297
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
createReservation: async (amount, purpose, timeout, client) => {
|
|
302
|
+
set({ error: null });
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
const reservation = await client.post<Reservation>("/api/v2/pay/reserve", {
|
|
306
|
+
amount,
|
|
307
|
+
purpose,
|
|
308
|
+
timeout,
|
|
309
|
+
});
|
|
310
|
+
set((state) => ({
|
|
311
|
+
reservations: [...state.reservations, reservation],
|
|
312
|
+
}));
|
|
313
|
+
} catch (err) {
|
|
314
|
+
const message = err instanceof Error ? err.message : "Failed to create reservation";
|
|
315
|
+
set({ error: message });
|
|
316
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
commitReservation: async (id, client) => {
|
|
321
|
+
set({ error: null });
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
await client.postNoContent(
|
|
325
|
+
`/api/v2/pay/reserve/${encodeURIComponent(id)}/commit`,
|
|
326
|
+
);
|
|
327
|
+
set((state) => ({
|
|
328
|
+
reservations: state.reservations.map((r) =>
|
|
329
|
+
r.id === id ? { ...r, status: "committed" as const } : r,
|
|
330
|
+
),
|
|
331
|
+
}));
|
|
332
|
+
} catch (err) {
|
|
333
|
+
const message = err instanceof Error ? err.message : "Failed to commit reservation";
|
|
334
|
+
set({ error: message });
|
|
335
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
releaseReservation: async (id, client) => {
|
|
340
|
+
set({ error: null });
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
await client.postNoContent(
|
|
344
|
+
`/api/v2/pay/reserve/${encodeURIComponent(id)}/release`,
|
|
345
|
+
);
|
|
346
|
+
set((state) => ({
|
|
347
|
+
reservations: state.reservations.map((r) =>
|
|
348
|
+
r.id === id ? { ...r, status: "released" as const } : r,
|
|
349
|
+
),
|
|
350
|
+
}));
|
|
351
|
+
} catch (err) {
|
|
352
|
+
const message = err instanceof Error ? err.message : "Failed to release reservation";
|
|
353
|
+
set({ error: message });
|
|
354
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
fetchTransactions: async (client, cursor) => {
|
|
359
|
+
// Reset pagination state when fetching first page (no cursor = fresh load)
|
|
360
|
+
const resetPagination = !cursor ? { transactionsCursorStack: [] as readonly string[] } : {};
|
|
361
|
+
set({ transactionsLoading: true, error: null, ...resetPagination });
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
const params = new URLSearchParams({ limit: "50", include_total: "true" });
|
|
365
|
+
if (cursor) {
|
|
366
|
+
params.set("cursor", cursor);
|
|
367
|
+
}
|
|
368
|
+
const data = await client.get<TransactionsResponse>(
|
|
369
|
+
`/api/v2/audit/transactions?${params.toString()}`,
|
|
370
|
+
);
|
|
371
|
+
set({
|
|
372
|
+
transactions: data.transactions,
|
|
373
|
+
transactionsHasMore: data.has_more,
|
|
374
|
+
transactionsNextCursor: data.next_cursor ?? null,
|
|
375
|
+
transactionsTotal: data.total ?? null,
|
|
376
|
+
selectedTransactionIndex: 0,
|
|
377
|
+
transactionsLoading: false,
|
|
378
|
+
integrityResult: null,
|
|
379
|
+
});
|
|
380
|
+
useUiStore.getState().markDataUpdated("payments");
|
|
381
|
+
} catch (err) {
|
|
382
|
+
const message = err instanceof Error ? err.message : "Failed to fetch transactions";
|
|
383
|
+
set({
|
|
384
|
+
transactionsLoading: false,
|
|
385
|
+
error: message,
|
|
386
|
+
});
|
|
387
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
fetchNextTransactions: async (client) => {
|
|
392
|
+
const { transactionsNextCursor, transactionsHasMore } = get();
|
|
393
|
+
if (!transactionsHasMore || !transactionsNextCursor) return;
|
|
394
|
+
|
|
395
|
+
// Push current page's next_cursor onto stack before navigating forward
|
|
396
|
+
set((state) => ({
|
|
397
|
+
transactionsCursorStack: [...state.transactionsCursorStack, transactionsNextCursor],
|
|
398
|
+
}));
|
|
399
|
+
|
|
400
|
+
await get().fetchTransactions(client, transactionsNextCursor);
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
fetchPrevTransactions: async (client) => {
|
|
404
|
+
const { transactionsCursorStack } = get();
|
|
405
|
+
if (transactionsCursorStack.length === 0) return;
|
|
406
|
+
|
|
407
|
+
// Pop the current cursor (it brought us to this page)
|
|
408
|
+
const stack = [...transactionsCursorStack];
|
|
409
|
+
stack.pop();
|
|
410
|
+
|
|
411
|
+
// The previous cursor (or undefined for first page)
|
|
412
|
+
const prevCursor = stack.length > 0 ? stack[stack.length - 1] : undefined;
|
|
413
|
+
set({ transactionsCursorStack: stack });
|
|
414
|
+
|
|
415
|
+
await get().fetchTransactions(client, prevCursor);
|
|
416
|
+
},
|
|
417
|
+
|
|
418
|
+
deletePolicy: async (policyId, client) => {
|
|
419
|
+
set({ error: null });
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
await client.deleteNoContent(
|
|
423
|
+
`/api/v2/pay/policies/${encodeURIComponent(policyId)}`,
|
|
424
|
+
);
|
|
425
|
+
set((state) => ({
|
|
426
|
+
policies: state.policies.filter((p) => p.policy_id !== policyId),
|
|
427
|
+
}));
|
|
428
|
+
} catch (err) {
|
|
429
|
+
const message = err instanceof Error ? err.message : "Failed to delete policy";
|
|
430
|
+
set({ error: message });
|
|
431
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
verifyIntegrity: async (recordId, client) => {
|
|
436
|
+
set({ error: null, integrityResult: null });
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
const result = await client.get<IntegrityResult>(
|
|
440
|
+
`/api/v2/audit/integrity/${encodeURIComponent(recordId)}`,
|
|
441
|
+
);
|
|
442
|
+
set({ integrityResult: result });
|
|
443
|
+
useUiStore.getState().markDataUpdated("payments");
|
|
444
|
+
return result;
|
|
445
|
+
} catch (err) {
|
|
446
|
+
const message = err instanceof Error ? err.message : "Failed to verify integrity";
|
|
447
|
+
set({ error: message });
|
|
448
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
|
|
453
|
+
createPolicy: async (name, rules, client) => {
|
|
454
|
+
set({ policiesLoading: true, error: null });
|
|
455
|
+
try {
|
|
456
|
+
await client.post("/api/v2/pay/policies", { name, rules });
|
|
457
|
+
await get().fetchPolicies(client);
|
|
458
|
+
} catch (err) {
|
|
459
|
+
const message = err instanceof Error ? err.message : "Failed to create policy";
|
|
460
|
+
set({ policiesLoading: false, error: message });
|
|
461
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
checkAfford: async (amount, client) => {
|
|
466
|
+
set({ error: null });
|
|
467
|
+
try {
|
|
468
|
+
const result = await client.get<{ can_afford: boolean; balance: string; requested: string }>(
|
|
469
|
+
`/api/v2/pay/can-afford?amount=${encodeURIComponent(amount)}`,
|
|
470
|
+
);
|
|
471
|
+
set({ affordResult: result });
|
|
472
|
+
useUiStore.getState().markDataUpdated("payments");
|
|
473
|
+
} catch (err) {
|
|
474
|
+
const message = err instanceof Error ? err.message : "Failed to check affordability";
|
|
475
|
+
set({ error: message });
|
|
476
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
// =========================================================================
|
|
481
|
+
// Approvals
|
|
482
|
+
// =========================================================================
|
|
483
|
+
|
|
484
|
+
fetchApprovals: createApiAction<PaymentsState, [FetchClient]>(set, {
|
|
485
|
+
loadingKey: "approvalsLoading",
|
|
486
|
+
source: SOURCE,
|
|
487
|
+
errorMessage: "Failed to fetch approvals",
|
|
488
|
+
action: async (client) => {
|
|
489
|
+
const approvals = await client.get<readonly ApprovalRequest[]>("/api/v2/pay/approvals");
|
|
490
|
+
return { approvals, selectedApprovalIndex: 0 };
|
|
491
|
+
},
|
|
492
|
+
}),
|
|
493
|
+
|
|
494
|
+
requestApproval: async (amount, purpose, client) => {
|
|
495
|
+
set({ approvalsLoading: true, error: null });
|
|
496
|
+
try {
|
|
497
|
+
await client.post("/api/v2/pay/approvals/request", { amount, purpose });
|
|
498
|
+
await get().fetchApprovals(client);
|
|
499
|
+
} catch (err) {
|
|
500
|
+
const message = err instanceof Error ? err.message : "Failed to request approval";
|
|
501
|
+
set({ approvalsLoading: false, error: message });
|
|
502
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
approveRequest: async (approvalId, client) => {
|
|
507
|
+
set({ approvalsLoading: true, error: null });
|
|
508
|
+
try {
|
|
509
|
+
await client.post(`/api/v2/pay/approvals/${encodeURIComponent(approvalId)}/approve`, {});
|
|
510
|
+
await get().fetchApprovals(client);
|
|
511
|
+
} catch (err) {
|
|
512
|
+
const message = err instanceof Error ? err.message : "Failed to approve request";
|
|
513
|
+
set({ approvalsLoading: false, error: message });
|
|
514
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
rejectRequest: async (approvalId, client) => {
|
|
519
|
+
set({ approvalsLoading: true, error: null });
|
|
520
|
+
try {
|
|
521
|
+
await client.post(`/api/v2/pay/approvals/${encodeURIComponent(approvalId)}/reject`, {});
|
|
522
|
+
await get().fetchApprovals(client);
|
|
523
|
+
} catch (err) {
|
|
524
|
+
const message = err instanceof Error ? err.message : "Failed to reject request";
|
|
525
|
+
set({ approvalsLoading: false, error: message });
|
|
526
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
setSelectedApprovalIndex: (index) => {
|
|
531
|
+
set({ selectedApprovalIndex: index });
|
|
532
|
+
},
|
|
533
|
+
|
|
534
|
+
setActiveTab: (tab) => {
|
|
535
|
+
set({ activeTab: tab, error: null });
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
setSelectedReservationIndex: (index) => {
|
|
539
|
+
set({ selectedReservationIndex: index });
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
setSelectedTransactionIndex: (index) => {
|
|
543
|
+
set({ selectedTransactionIndex: index });
|
|
544
|
+
},
|
|
545
|
+
}));
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the Search & Knowledge panel store.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from search-store.ts to keep the store file focused on
|
|
5
|
+
* state management and actions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Public types (snake_case matching API wire format)
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
export interface SearchResult {
|
|
13
|
+
readonly path: string;
|
|
14
|
+
readonly chunk_text: string;
|
|
15
|
+
readonly score: number;
|
|
16
|
+
readonly chunk_index: number;
|
|
17
|
+
readonly line_start: number | null;
|
|
18
|
+
readonly line_end: number | null;
|
|
19
|
+
readonly keyword_score: number | null;
|
|
20
|
+
readonly vector_score: number | null;
|
|
21
|
+
readonly splade_score?: number | null;
|
|
22
|
+
readonly reranker_score?: number | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface KnowledgeEntity {
|
|
26
|
+
readonly [key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface NeighborEntry {
|
|
30
|
+
readonly entity: KnowledgeEntity;
|
|
31
|
+
readonly depth: number;
|
|
32
|
+
readonly path: readonly string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Memory {
|
|
36
|
+
readonly [key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Matches backend PlaybookResponse from playbook.py. */
|
|
40
|
+
export interface PlaybookRecord {
|
|
41
|
+
readonly playbook_id: string;
|
|
42
|
+
readonly name: string;
|
|
43
|
+
readonly description: string | null;
|
|
44
|
+
readonly version: number;
|
|
45
|
+
readonly scope: string;
|
|
46
|
+
readonly visibility: string;
|
|
47
|
+
readonly usage_count: number;
|
|
48
|
+
readonly success_rate: number | null;
|
|
49
|
+
readonly strategies: readonly unknown[] | null;
|
|
50
|
+
readonly created_at: string | null;
|
|
51
|
+
readonly updated_at: string | null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface MemoryVersion {
|
|
55
|
+
readonly version: number;
|
|
56
|
+
readonly created_at: string;
|
|
57
|
+
readonly status: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface MemoryHistory {
|
|
61
|
+
readonly memory_id: string;
|
|
62
|
+
readonly current_version: number;
|
|
63
|
+
readonly versions: readonly MemoryVersion[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface MemoryDiff {
|
|
67
|
+
readonly diff: string;
|
|
68
|
+
readonly mode: string;
|
|
69
|
+
readonly v1: number;
|
|
70
|
+
readonly v2: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** A single RLM inference iteration (rlm.iteration SSE event). */
|
|
74
|
+
export interface RlmStep {
|
|
75
|
+
readonly step: number;
|
|
76
|
+
readonly code_executed: string;
|
|
77
|
+
readonly output_summary: string;
|
|
78
|
+
readonly tokens_used: number;
|
|
79
|
+
readonly duration_seconds: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Progressive state of an RLM streaming inference. */
|
|
83
|
+
export interface RlmAnswer {
|
|
84
|
+
readonly status: "streaming" | "completed" | "budget_exceeded" | "error";
|
|
85
|
+
readonly answer: string | null;
|
|
86
|
+
readonly total_tokens: number;
|
|
87
|
+
readonly total_duration_seconds: number;
|
|
88
|
+
readonly iterations: number;
|
|
89
|
+
readonly error_message: string | null;
|
|
90
|
+
readonly steps: readonly RlmStep[];
|
|
91
|
+
readonly model: string | null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type SearchTab = "search" | "knowledge" | "memories" | "playbooks" | "ask" | "columns";
|
|
95
|
+
export type SearchMode = "keyword" | "semantic" | "hybrid";
|
|
96
|
+
|
|
97
|
+
// =============================================================================
|
|
98
|
+
// Internal response types (used only by the store implementation)
|
|
99
|
+
// =============================================================================
|
|
100
|
+
|
|
101
|
+
export interface SearchQueryResponse {
|
|
102
|
+
readonly query: string;
|
|
103
|
+
readonly search_type: string;
|
|
104
|
+
readonly graph_mode: string;
|
|
105
|
+
readonly results: readonly SearchResult[];
|
|
106
|
+
readonly total: number;
|
|
107
|
+
readonly latency_ms: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface EntityResponse {
|
|
111
|
+
readonly entity: KnowledgeEntity | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface NeighborsResponse {
|
|
115
|
+
readonly neighbors: readonly NeighborEntry[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface KnowledgeSearchResponse {
|
|
119
|
+
readonly entity: KnowledgeEntity | null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface MemorySearchResponse {
|
|
123
|
+
readonly memories: readonly Memory[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface MemoryDetailResponse {
|
|
127
|
+
readonly memory: Memory;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface PlaybooksListResponse {
|
|
131
|
+
readonly playbooks: readonly PlaybookRecord[];
|
|
132
|
+
readonly total: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface MemoryHistoryResponse {
|
|
136
|
+
readonly memory_id: string;
|
|
137
|
+
readonly current_version: number;
|
|
138
|
+
readonly versions: readonly MemoryVersion[];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface MemoryDiffResponse {
|
|
142
|
+
readonly diff: string;
|
|
143
|
+
readonly mode: string;
|
|
144
|
+
readonly v1: number;
|
|
145
|
+
readonly v2: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Matches backend rollback response from memories.py:480. */
|
|
149
|
+
export interface MemoryRollbackResponse {
|
|
150
|
+
readonly rolled_back: boolean;
|
|
151
|
+
readonly memory_id: string;
|
|
152
|
+
readonly rolled_back_to_version: number;
|
|
153
|
+
readonly current_version: number | null;
|
|
154
|
+
readonly content: unknown;
|
|
155
|
+
}
|