@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,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global application state: connection, navigation, config.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { create } from "zustand";
|
|
6
|
+
import type { NexusClientOptions } from "@nexus-ai-fs/api-client";
|
|
7
|
+
import { FetchClient, resolveConfig } from "@nexus-ai-fs/api-client";
|
|
8
|
+
import { categorizeError } from "./create-api-action.js";
|
|
9
|
+
import { useErrorStore } from "./error-store.js";
|
|
10
|
+
|
|
11
|
+
export type ConnectionStatus = "disconnected" | "connecting" | "connected" | "error";
|
|
12
|
+
|
|
13
|
+
export type PanelId =
|
|
14
|
+
| "files"
|
|
15
|
+
| "versions"
|
|
16
|
+
| "agents"
|
|
17
|
+
| "zones"
|
|
18
|
+
| "access"
|
|
19
|
+
| "payments"
|
|
20
|
+
| "search"
|
|
21
|
+
| "workflows"
|
|
22
|
+
| "infrastructure"
|
|
23
|
+
| "console";
|
|
24
|
+
|
|
25
|
+
/** Response from GET /api/v2/features */
|
|
26
|
+
export interface FeaturesResponse {
|
|
27
|
+
readonly profile: string;
|
|
28
|
+
readonly mode: string;
|
|
29
|
+
readonly enabled_bricks: readonly string[];
|
|
30
|
+
readonly disabled_bricks: readonly string[];
|
|
31
|
+
readonly version: string | null;
|
|
32
|
+
readonly rate_limit_enabled: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Response from GET /auth/me */
|
|
36
|
+
export interface UserInfo {
|
|
37
|
+
readonly user_id: string;
|
|
38
|
+
readonly email: string;
|
|
39
|
+
readonly username: string | null;
|
|
40
|
+
readonly display_name: string | null;
|
|
41
|
+
readonly avatar_url: string | null;
|
|
42
|
+
readonly is_global_admin: boolean;
|
|
43
|
+
readonly primary_auth_method: string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface GlobalState {
|
|
47
|
+
// Connection
|
|
48
|
+
readonly connectionStatus: ConnectionStatus;
|
|
49
|
+
readonly connectionError: string | null;
|
|
50
|
+
readonly config: NexusClientOptions;
|
|
51
|
+
readonly client: FetchClient | null;
|
|
52
|
+
|
|
53
|
+
// Navigation
|
|
54
|
+
readonly activePanel: PanelId;
|
|
55
|
+
readonly panelHistory: readonly PanelId[];
|
|
56
|
+
|
|
57
|
+
// Identity display
|
|
58
|
+
readonly serverVersion: string | null;
|
|
59
|
+
readonly zoneId: string | null;
|
|
60
|
+
readonly uptime: number | null;
|
|
61
|
+
readonly userInfo: UserInfo | null;
|
|
62
|
+
|
|
63
|
+
// Features (from GET /api/v2/features)
|
|
64
|
+
readonly enabledBricks: readonly string[];
|
|
65
|
+
readonly profile: string | null;
|
|
66
|
+
readonly mode: string | null;
|
|
67
|
+
readonly featuresLoaded: boolean;
|
|
68
|
+
readonly featuresLastFetched: number;
|
|
69
|
+
|
|
70
|
+
// Actions
|
|
71
|
+
readonly initConfig: (overrides?: Partial<NexusClientOptions>) => void;
|
|
72
|
+
readonly testConnection: () => Promise<void>;
|
|
73
|
+
readonly setActivePanel: (panel: PanelId) => void;
|
|
74
|
+
readonly setConnectionStatus: (status: ConnectionStatus, error?: string) => void;
|
|
75
|
+
readonly setServerInfo: (info: { version?: string; zoneId?: string; uptime?: number }) => void;
|
|
76
|
+
readonly setIdentity: (identity: { agentId?: string; subject?: string; zoneId?: string }) => void;
|
|
77
|
+
readonly setFeatures: (features: FeaturesResponse) => void;
|
|
78
|
+
readonly refreshFeatures: () => Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const useGlobalStore = create<GlobalState>((set, get) => ({
|
|
82
|
+
// Initial state
|
|
83
|
+
connectionStatus: "disconnected",
|
|
84
|
+
connectionError: null,
|
|
85
|
+
config: resolveConfig(),
|
|
86
|
+
client: null,
|
|
87
|
+
activePanel: "files",
|
|
88
|
+
panelHistory: [],
|
|
89
|
+
serverVersion: null,
|
|
90
|
+
zoneId: null,
|
|
91
|
+
uptime: null,
|
|
92
|
+
userInfo: null,
|
|
93
|
+
enabledBricks: [],
|
|
94
|
+
profile: null,
|
|
95
|
+
mode: null,
|
|
96
|
+
featuresLoaded: false,
|
|
97
|
+
featuresLastFetched: 0,
|
|
98
|
+
|
|
99
|
+
initConfig: (overrides) => {
|
|
100
|
+
const config = resolveConfig({ transformKeys: false, ...overrides });
|
|
101
|
+
const client = new FetchClient(config);
|
|
102
|
+
set({ config, client, connectionStatus: client ? "connecting" : "disconnected" });
|
|
103
|
+
|
|
104
|
+
if (client) {
|
|
105
|
+
get().testConnection();
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
testConnection: async () => {
|
|
110
|
+
const client = get().client;
|
|
111
|
+
if (!client) {
|
|
112
|
+
set({ connectionStatus: "disconnected", connectionError: null, userInfo: null });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
set({ connectionStatus: "connecting", connectionError: null });
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
// Consolidated connection check (Decision 5A): health + features + auth in one flow
|
|
120
|
+
let [health, features, userInfo] = await Promise.all([
|
|
121
|
+
client.get<{ status?: string; uptime_seconds?: number }>(
|
|
122
|
+
"/healthz/ready",
|
|
123
|
+
).catch(() => null),
|
|
124
|
+
client.get<FeaturesResponse>("/api/v2/features").catch(() => null),
|
|
125
|
+
client.get<UserInfo>("/auth/me").catch(() => null),
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
// Auto-discovery: if health check fails, scan common ports to find the server.
|
|
129
|
+
// Only probe when running interactively (not in tests) — probing creates real
|
|
130
|
+
// HTTP connections that cause test timeouts.
|
|
131
|
+
if (!health && typeof process !== "undefined" && process.stdout?.isTTY) {
|
|
132
|
+
const configuredUrl = get().config.baseUrl ?? "";
|
|
133
|
+
const hostname = configuredUrl.replace(/:\d+$/, "").replace(/^https?:\/\//, "");
|
|
134
|
+
const protocol = configuredUrl.startsWith("https") ? "https" : "http";
|
|
135
|
+
const PROBE_PORTS = [2026, 2027, 2042, 2043, 8080, 2122];
|
|
136
|
+
|
|
137
|
+
for (const port of PROBE_PORTS) {
|
|
138
|
+
if (configuredUrl.includes(`:${port}`)) continue;
|
|
139
|
+
try {
|
|
140
|
+
const probeUrl = `${protocol}://${hostname || "localhost"}:${port}`;
|
|
141
|
+
const probeClient = new FetchClient({ ...get().config, baseUrl: probeUrl, timeout: 3000, maxRetries: 0 });
|
|
142
|
+
const probeHealth = await probeClient.get<{ status?: string; uptime_seconds?: number }>(
|
|
143
|
+
"/healthz/ready",
|
|
144
|
+
).catch(() => null);
|
|
145
|
+
if (probeHealth) {
|
|
146
|
+
const newConfig = resolveConfig({ transformKeys: false, baseUrl: probeUrl });
|
|
147
|
+
const newClient = new FetchClient(newConfig);
|
|
148
|
+
[health, features, userInfo] = await Promise.all([
|
|
149
|
+
Promise.resolve(probeHealth),
|
|
150
|
+
newClient.get<FeaturesResponse>("/api/v2/features").catch(() => null),
|
|
151
|
+
newClient.get<UserInfo>("/auth/me").catch(() => null),
|
|
152
|
+
]);
|
|
153
|
+
set({ config: newConfig, client: newClient });
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
// probe failed, try next port
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!health) {
|
|
163
|
+
throw new Error("Server health check failed");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Note: zoneId is not set here — no health/readiness endpoint provides it.
|
|
167
|
+
// The old /api/v2/bricks/health never returned zone_id either (its response
|
|
168
|
+
// was { total, active, failed, bricks }). zoneId is set via setIdentity()
|
|
169
|
+
// when the user explicitly configures a zone, or defaults to "root".
|
|
170
|
+
set({
|
|
171
|
+
serverVersion: features?.version ?? get().serverVersion,
|
|
172
|
+
uptime: health.uptime_seconds ?? get().uptime,
|
|
173
|
+
});
|
|
174
|
+
if (features) {
|
|
175
|
+
get().setFeatures(features);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
set({ connectionStatus: "connected", connectionError: null, userInfo });
|
|
179
|
+
} catch (err) {
|
|
180
|
+
const message = err instanceof Error ? err.message : "Connection test failed";
|
|
181
|
+
set({
|
|
182
|
+
connectionStatus: "error",
|
|
183
|
+
connectionError: message,
|
|
184
|
+
userInfo: null,
|
|
185
|
+
});
|
|
186
|
+
useErrorStore.getState().pushError({ message, category: categorizeError(message), source: "global" });
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
setActivePanel: (panel) => {
|
|
191
|
+
const current = get().activePanel;
|
|
192
|
+
if (current === panel) return;
|
|
193
|
+
set((state) => ({
|
|
194
|
+
activePanel: panel,
|
|
195
|
+
panelHistory: [...state.panelHistory.slice(-9), current],
|
|
196
|
+
}));
|
|
197
|
+
// Re-fetch features on panel switch (Decision 3A)
|
|
198
|
+
get().refreshFeatures();
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
setConnectionStatus: (status, error) => {
|
|
202
|
+
set({ connectionStatus: status, connectionError: error ?? null });
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
setServerInfo: (info) => {
|
|
206
|
+
set({
|
|
207
|
+
serverVersion: info.version ?? get().serverVersion,
|
|
208
|
+
zoneId: info.zoneId ?? get().zoneId,
|
|
209
|
+
uptime: info.uptime ?? get().uptime,
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
setIdentity: (identity) => {
|
|
214
|
+
const currentConfig = get().config;
|
|
215
|
+
// Use explicit values from identity, allowing empty string → undefined to clear fields
|
|
216
|
+
const config: NexusClientOptions = {
|
|
217
|
+
...currentConfig,
|
|
218
|
+
agentId: "agentId" in identity ? identity.agentId : currentConfig.agentId,
|
|
219
|
+
subject: "subject" in identity ? identity.subject : currentConfig.subject,
|
|
220
|
+
zoneId: "zoneId" in identity ? identity.zoneId : currentConfig.zoneId,
|
|
221
|
+
};
|
|
222
|
+
const client = new FetchClient(config);
|
|
223
|
+
set({ config, client });
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
setFeatures: (features) => {
|
|
227
|
+
set({
|
|
228
|
+
enabledBricks: features.enabled_bricks ?? [],
|
|
229
|
+
profile: features.profile ?? null,
|
|
230
|
+
mode: features.mode ?? null,
|
|
231
|
+
featuresLoaded: true,
|
|
232
|
+
featuresLastFetched: Date.now(),
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
refreshFeatures: async () => {
|
|
237
|
+
const { client, featuresLastFetched } = get();
|
|
238
|
+
if (!client) return;
|
|
239
|
+
// TTL: skip if fetched within the last 10 seconds (Decision 13A)
|
|
240
|
+
if (Date.now() - featuresLastFetched < 10_000) return;
|
|
241
|
+
try {
|
|
242
|
+
const features = await client.get<{
|
|
243
|
+
profile: string;
|
|
244
|
+
mode: string;
|
|
245
|
+
enabled_bricks: string[];
|
|
246
|
+
disabled_bricks: string[];
|
|
247
|
+
version: string | null;
|
|
248
|
+
rate_limit_enabled: boolean;
|
|
249
|
+
}>("/api/v2/features");
|
|
250
|
+
get().setFeatures(features);
|
|
251
|
+
} catch {
|
|
252
|
+
// Non-critical: use last known state
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
}));
|