Merge branch 'fix/lint-issues'

This commit is contained in:
Timothy
2026-01-15 15:19:07 -08:00
25 changed files with 233 additions and 91 deletions
+24
View File
@@ -0,0 +1,24 @@
module.exports = {
root: true,
env: { node: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
// Allow unused vars that start with underscore
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
}],
// Allow any types (common in API/external data handling)
'@typescript-eslint/no-explicit-any': 'off',
},
}
+1 -1
View File
@@ -118,7 +118,7 @@ function validateConfig(): void {
required.push(['USER_DB_PG_URL or TSDB_PG_URL', config.userDb.url]);
}
const missing = required.filter(([name, value]) => !value);
const missing = required.filter(([, value]) => !value);
if (missing.length > 0) {
const names = missing.map(([name]) => name).join(', ');
+4 -4
View File
@@ -82,8 +82,8 @@ router.get('/get-current-team', async (req: Request, res: Response) => {
teamId: team.id,
teamName: team.name,
});
} catch (err: any) {
console.error('[IAMController] /get-current-team error:', err.message);
} catch (err) {
console.error('[IAMController] /get-current-team error:', err instanceof Error ? err.message : err);
res.status(500).json({
success: false,
msg: 'Failed to get current team',
@@ -142,8 +142,8 @@ router.get('/team/get-team-role-by-id/:teamId', async (req: Request, res: Respon
const roleId = membership ? (roleMap[membership.role] || 2) : 2;
res.json({ roleId });
} catch (err: any) {
console.error('[IAMController] /team/get-team-role-by-id error:', err.message);
} catch (err) {
console.error('[IAMController] /team/get-team-role-by-id error:', err instanceof Error ? err.message : err);
res.status(500).json({
success: false,
msg: 'Failed to get team role',
+1 -10
View File
@@ -17,15 +17,6 @@ const router = express.Router();
const AUTH_MIDDLEWARE = passport.authenticate("jwt", { session: false });
const isRecord = (v: unknown): v is Record<string, unknown> => {
return typeof v === "object" && v !== null && !Array.isArray(v);
};
const utcDateKey = (d: Date): string => {
const x = new Date(d);
x.setUTCHours(0, 0, 0, 0);
return x.toISOString().slice(0, 10);
};
interface TokenContext {
team_id: string;
@@ -318,7 +309,7 @@ router.get("/logs", AUTH_MIDDLEWARE, async (req: Request, res: Response) => {
// Convert to array and sort by cost desc
return Array.from(merged.values())
.map(({ latency_sum, ...rest }) => ({ ...rest, latency_sum: 0 }))
.map(({ latency_sum: _latency_sum, ...rest }) => ({ ...rest, latency_sum: 0 }))
.sort((a, b) => b.total_cost - a.total_cost);
};
+32 -25
View File
@@ -35,9 +35,10 @@ const EMAIL_REGEX =
*/
router.post(
"/login-v2",
async (req: Request, res: Response, next: NextFunction) => {
async (req: Request, res: Response, _next: NextFunction) => {
try {
let { email, password } = req.body;
let { email } = req.body;
const { password } = req.body;
// Validate required fields
if (
@@ -102,32 +103,29 @@ router.post(
current_team_id: result.current_team_id,
create_time: result.created_at,
});
} catch (err: any) {
console.error("[UserController] login-v2 error:", err.message);
} catch (err) {
const error = err as { message?: string; code?: string };
console.error("[UserController] login-v2 error:", error.message);
// Handle specific error codes
if (err.code === "USER_NOT_FOUND") {
return res.status(401).json({
success: false,
msg: "User not found. Please sign up for an account.",
});
}
if (err.code === "INVALID_CREDENTIALS") {
if (
error.code === "USER_NOT_FOUND" ||
error.code === "INVALID_CREDENTIALS"
) {
return res.status(401).json({
success: false,
msg: "Invalid email or password",
});
}
if (err.code === "OAUTH_REQUIRED") {
if (error.code === "OAUTH_REQUIRED") {
return res.status(400).json({
success: false,
msg: err.message,
msg: error.message,
});
}
if (err.code === "ACCOUNT_DISABLED") {
if (error.code === "ACCOUNT_DISABLED") {
return res.status(403).json({
success: false,
msg: "Your account has been disabled",
@@ -151,7 +149,8 @@ router.post(
*/
router.post("/register", async (req: Request, res: Response) => {
try {
let { email, password, name, firstname, lastname } = req.body;
let { email } = req.body;
const { password, name, firstname, lastname } = req.body;
// Validate required fields
if (
@@ -479,7 +478,7 @@ router.post("/generate-dev-token", async (req: Request, res: Response) => {
*/
const DEFAULT_UI_SETTINGS = {
sidebarCollapsed: false,
performanceDashboardTimeRange: 'today',
performanceDashboardTimeRange: "today",
};
/**
@@ -511,8 +510,11 @@ router.get("/settings", async (req: Request, res: Response) => {
// Extract UI settings from preferences, merge with defaults
const preferences = user.preferences || {};
const uiSettings = {
sidebarCollapsed: preferences.sidebarCollapsed ?? DEFAULT_UI_SETTINGS.sidebarCollapsed,
performanceDashboardTimeRange: preferences.performanceDashboardTimeRange ?? DEFAULT_UI_SETTINGS.performanceDashboardTimeRange,
sidebarCollapsed:
preferences.sidebarCollapsed ?? DEFAULT_UI_SETTINGS.sidebarCollapsed,
performanceDashboardTimeRange:
preferences.performanceDashboardTimeRange ??
DEFAULT_UI_SETTINGS.performanceDashboardTimeRange,
};
res.json({
@@ -558,7 +560,7 @@ router.put("/settings", async (req: Request, res: Response) => {
// Build update object with only provided fields
const updates: Record<string, any> = {};
if (typeof sidebarCollapsed === 'boolean') {
if (typeof sidebarCollapsed === "boolean") {
updates.sidebarCollapsed = sidebarCollapsed;
}
if (performanceDashboardTimeRange !== undefined) {
@@ -576,23 +578,28 @@ router.put("/settings", async (req: Request, res: Response) => {
if (pgPool) {
// PostgreSQL - use JSONB
await pgPool.query(
'UPDATE users SET preferences = $1, updated_at = NOW() WHERE id = $2',
"UPDATE users SET preferences = $1, updated_at = NOW() WHERE id = $2",
[JSON.stringify(newPreferences), user.id]
);
} else if (mysqlPool) {
// MySQL - use JSON column
await mysqlPool.query(
'UPDATE user SET preferences = ?, updated_at = NOW() WHERE id = ?',
"UPDATE user SET preferences = ?, updated_at = NOW() WHERE id = ?",
[JSON.stringify(newPreferences), user.id]
);
} else {
console.warn("[UserController] PUT /settings: No database pool available, settings not persisted");
console.warn(
"[UserController] PUT /settings: No database pool available, settings not persisted"
);
}
// Return updated settings
const uiSettings = {
sidebarCollapsed: newPreferences.sidebarCollapsed ?? DEFAULT_UI_SETTINGS.sidebarCollapsed,
performanceDashboardTimeRange: newPreferences.performanceDashboardTimeRange ?? DEFAULT_UI_SETTINGS.performanceDashboardTimeRange,
sidebarCollapsed:
newPreferences.sidebarCollapsed ?? DEFAULT_UI_SETTINGS.sidebarCollapsed,
performanceDashboardTimeRange:
newPreferences.performanceDashboardTimeRange ??
DEFAULT_UI_SETTINGS.performanceDashboardTimeRange,
};
res.json({
+4
View File
@@ -15,9 +15,13 @@ import { initializeSockets, setUserDbService } from "./sockets/control.socket";
const PORT = process.env.PORT || 4000;
// Declare globals for MongoDB (used by services)
// eslint-disable-next-line no-var
declare global {
// eslint-disable-next-line no-var
var _ACHO_MG_DB: MongoClient;
// eslint-disable-next-line no-var
var _ACHO_MDB_CONFIG: { ERP_DBNAME: string; DBNAME: string };
// eslint-disable-next-line no-var
var _ACHO_MDB_COLLECTIONS: {
ADEN_CONTROL_POLICIES: string;
ADEN_CONTROL_CONTENT: string;
-1
View File
@@ -15,7 +15,6 @@ import {
createSuccessResponse,
handleToolError,
} from "../utils/response-helpers";
import { idSchema, paginationSchema } from "../utils/schema-helpers";
export function registerPolicyTools(server: McpServer, api: ApiClient) {
// ==================== hive_policies_list ====================
+2 -2
View File
@@ -5,7 +5,7 @@
* - GET /mcp - SSE stream for server-to-client messages
* - POST /mcp/message - Client-to-server messages
*/
import express, { Request, Response, NextFunction, Router } from "express";
import express, { Request, Response, Router } from "express";
import passport from "passport";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { createHiveMcpServer, type HiveMcpServerOptions } from "../server";
@@ -155,7 +155,7 @@ export function createMcpRouter(
// Only show sessions for the requesting team
const teamSessions = Array.from(sessions.entries())
.filter(([_, session]) => session.teamId === teamId)
.filter(([, session]) => session.teamId === teamId)
.map(([id, session]) => ({
session_id: id,
team_id: session.teamId,
@@ -18,7 +18,8 @@ interface HttpError extends Error {
* @param {Object} res - Express response
* @param {Function} next - Next middleware
*/
function errorHandler(err: HttpError, req: Request, res: Response, next: NextFunction): void {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function errorHandler(err: HttpError, req: Request, res: Response, _next: NextFunction): void {
// Log error
console.error('[Error]', {
message: err.message,
+10 -8
View File
@@ -253,7 +253,7 @@ async function calculateBudgetAnalyticsFromTsdb(teamId: string | number, budget:
const conditions = [`team_id = $1`, `"timestamp" >= $2`, `"timestamp" <= $3`];
const values: unknown[] = [String(teamId), baseTableStart, now];
let paramIndex = 4;
const paramIndex = 4;
// Apply budget-specific filter based on budget type
const budgetFilter = getBudgetFilter(budget, paramIndex);
@@ -663,11 +663,12 @@ function matchesBudgetType(budget: Budget, metricData: MetricData): boolean {
// Feature budgets apply when feature name matches
return !!metadata.feature && budget.name === metadata.feature;
case "tag":
case "tag": {
// Tag budgets apply when the tagCategory value matches budget name
if (!budget.tagCategory || !metadata.tags) return false;
const tagValue = (metadata.tags as Record<string, string>)[budget.tagCategory];
return !!tagValue && budget.name === tagValue;
}
default:
return false;
@@ -726,18 +727,21 @@ async function sendBudgetNotifications(budget: Budget, alertData: Record<string,
: "#eff6ff";
// Build notification content
let subject: string, title: string, description: string;
let title: string, description: string;
if (isLimitAction) {
subject = `[Aden] Budget "${budget.name}" - ${(action || "").toUpperCase()}`;
title = "Budget Limit Triggered";
description = `The budget <strong>${budget.name}</strong> has exceeded its limit and triggered a control action.`;
} else {
subject = `[Aden] Budget "${budget.name}" at ${spentPercentage}%`;
title = "Budget Threshold Alert";
description = `The budget <strong>${budget.name}</strong> has reached ${threshold}% of its limit.`;
}
const htmlContent = `
// Email subject and content prepared for future email notification implementation
const _subject = isLimitAction
? `[Aden] Budget "${budget.name}" - ${(action || "").toUpperCase()}`
: `[Aden] Budget "${budget.name}" at ${spentPercentage}%`;
const _htmlContent = `
<!DOCTYPE html>
<html>
<head>
@@ -1743,8 +1747,6 @@ async function getBudgetDetails(teamId: string | number, policyId: string | null
// Get real-time tracker status
const tracker = budgetTracker.get(budgetId);
const spent = tracker?.spent ?? budget.spent ?? 0;
const remaining = Math.max(0, budget.limit - spent);
const usagePercent = budget.limit > 0 ? (spent / budget.limit) * 100 : 0;
return {
...budget,
+2 -1
View File
@@ -552,7 +552,7 @@ function initAdenControlSockets(io: Server, rootEmitter: RedisEmitter): ControlE
}
break;
case "get_policy":
case "get_policy": {
// Request for current policy
const policy = await controlService.getPolicy(teamId!, policyId || null);
socket.emit("message", {
@@ -560,6 +560,7 @@ function initAdenControlSockets(io: Server, rootEmitter: RedisEmitter): ControlE
policy,
});
break;
}
default:
console.warn(
+4 -4
View File
@@ -13,8 +13,8 @@
*/
// In-memory cache for pricing data
let pricingCache = new Map<string, PricingEntry>();
let aliasCacheMap = new Map<string, string>(); // model alias -> canonical model
const pricingCache = new Map<string, PricingEntry>();
const aliasCacheMap = new Map<string, string>(); // model alias -> canonical model
let cacheLoadedAt: number | null = null;
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
@@ -389,7 +389,7 @@ interface CostResult {
* @param {Object} params - Request parameters
* @returns {Object} Cost breakdown { total, input_cost, output_cost, cached_cost, pricing }
*/
function calculateCostSync({ model, provider, input_tokens = 0, output_tokens = 0, cached_tokens = 0 }: CostCalculationParams): CostResult {
function calculateCostSync({ model, provider: _provider, input_tokens = 0, output_tokens = 0, cached_tokens = 0 }: CostCalculationParams): CostResult {
const resolved = resolveAlias(model);
let pricing: { input: number; output: number; cached_input: number; model: string; source: string };
@@ -577,7 +577,7 @@ async function getAllPricing(): Promise<AllPricingResult> {
await loadPricingFromDb();
const result: AllPricingResult = {};
for (const [key, pricing] of pricingCache.entries()) {
for (const [, pricing] of pricingCache.entries()) {
result[pricing.model] = {
provider: pricing.provider,
input: pricing.input,
+18
View File
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
@@ -24,6 +24,21 @@ import {
} from './charts/specs'
import type { RawJsonData, KPIValues } from '@/types/agentControl'
// Shape of analytics API response for type safety
interface AnalyticsResponse extends RawJsonData {
analytics?: {
summary?: {
total_cost?: number
total_requests?: number
total_tokens?: number
avg_latency_ms?: number
cache_savings?: number
}
}
kpis?: Record<string, unknown>
summary?: Record<string, unknown>
}
const timeRangeOptions: { value: TimeRange; label: string }[] = [
{ value: 'all', label: 'All Time' },
{ value: 'month', label: 'Last Month' },
@@ -47,7 +62,7 @@ function extractKpis(data: RawJsonData | undefined): KPIValues {
if (!data) return defaults
// Handle new analytics response shape
const analyticsData = data as any
const analyticsData = data as AnalyticsResponse
if (analyticsData?.analytics?.summary) {
const summary = analyticsData.analytics.summary
return {
@@ -63,8 +63,8 @@ export function WorkersPanel() {
const realtimeAgents = useMemo(() => deriveWorkersFromEvents(eventsBuffer), [eventsBuffer])
// Merge API agents with real-time updates (real-time overrides API data)
const apiAgents = agentsData?.agents || []
const workers = useMemo(() => {
const apiAgents = agentsData?.agents || []
const agentMap = new Map<string, AgentInfo>()
// Add API agents first
for (const agent of apiAgents) {
@@ -75,7 +75,7 @@ export function WorkersPanel() {
agentMap.set(agent.agent, agent)
}
return Array.from(agentMap.values())
}, [apiAgents, realtimeAgents])
}, [agentsData?.agents, realtimeAgents])
// Compute summary stats
const onlineCount = workers.filter((w: AgentInfo) => w.status === 'connected').length
@@ -66,10 +66,10 @@ export function CostByModelChart({
dataKey="cost"
nameKey="name"
>
{data.map((entry, index) => (
{data.map((item, index) => (
<Cell
key={`cell-${index}`}
fill={entry.color || COLORS[index % COLORS.length]}
fill={item.color || COLORS[index % COLORS.length]}
/>
))}
</Pie>
@@ -89,7 +89,7 @@ export function CostByModelChart({
maxWidth: '45%',
overflow: 'hidden',
}}
formatter={(value, entry) => {
formatter={(value) => {
const item = data.find((d) => d.name === value)
return (
<span className="text-sm block truncate max-w-[120px]" title={String(value)}>
@@ -35,7 +35,6 @@ export function TopAgentsChart({
// Sort by spend descending and take top 10
const sortedData = [...data].sort((a, b) => b.spend - a.spend).slice(0, 10)
const maxSpend = Math.max(...sortedData.map((d) => d.spend))
return (
<Card className={className}>
@@ -6,6 +6,82 @@
// Color palette for models (matches launchpad)
export const MODEL_COLORS = ['#263A99', '#22c55e', '#6b21a8', '#f59e0b', '#c1392b', '#06b6d4']
// =============================================================================
// API Response Types (for type safety with unknown API data)
// =============================================================================
interface TimelineCostItem {
bucket: string
cost_total?: number
}
interface TimelineRequestItem {
bucket: string
requests?: number
}
interface TimelineTokenItem {
bucket: string
input_tokens?: number
output_tokens?: number
}
interface TimelineLatencyItem {
bucket: string
p50_ms?: number
p95_ms?: number
p99_ms?: number
}
interface TimelineData {
cost?: TimelineCostItem[]
requests?: TimelineRequestItem[]
tokens?: TimelineTokenItem[]
latency_percentiles?: TimelineLatencyItem[]
}
interface CostByModelItem {
model?: string
cost_total?: number
share?: number
}
interface CostByModelResponse {
models?: CostByModelItem[]
}
interface LatencyBucketItem {
bucket: string
count?: number
}
interface LatencyDistributionResponse {
buckets?: LatencyBucketItem[]
}
interface CostByAgentItem {
agent?: string
cost_total?: number
requests?: number
}
interface CostByAgentResponse {
agents?: CostByAgentItem[]
}
interface AnalyticsData {
analytics?: {
timeline?: {
resolution?: string
hourly?: TimelineData
daily?: TimelineData
}
cost_by_model?: CostByModelResponse
latency_distribution?: LatencyDistributionResponse
cost_by_agent?: CostByAgentResponse
}
}
// =============================================================================
// Types for transformed chart data
// =============================================================================
@@ -72,7 +148,7 @@ export function formatBucketLabel(bucket: string, resolution: 'day' | 'hour'): s
/**
* Transform analytics API response to chart-ready data
*/
export function transformAnalyticsData(data: any) {
export function transformAnalyticsData(data: AnalyticsData | undefined) {
if (!data?.analytics) {
return {
costTrends: [],
@@ -85,7 +161,7 @@ export function transformAnalyticsData(data: any) {
}
const analytics = data.analytics
const resolution = analytics.timeline?.resolution || 'day'
const resolution: 'day' | 'hour' = analytics.timeline?.resolution === 'hour' ? 'hour' : 'day'
const timeline = resolution === 'hour' ? analytics.timeline?.hourly : analytics.timeline?.daily
return {
@@ -102,7 +178,7 @@ export function transformAnalyticsData(data: any) {
* Transform cost timeline to cost trend data
*/
function transformCostTrends(
timeline: any,
timeline: TimelineData | undefined,
resolution: 'day' | 'hour'
): CostTrendData[] {
if (!timeline?.cost || !Array.isArray(timeline.cost)) {
@@ -111,10 +187,10 @@ function transformCostTrends(
// Create requests lookup map
const requestsMap = new Map<string, number>(
(timeline.requests || []).map((r: any) => [r.bucket, r.requests])
(timeline.requests || []).map((r: TimelineRequestItem) => [r.bucket, r.requests ?? 0])
)
return timeline.cost.map((d: any) => ({
return timeline.cost.map((d: TimelineCostItem) => ({
date: formatBucketLabel(d.bucket, resolution),
cost: d.cost_total || 0,
requests: requestsMap.get(d.bucket) || 0,
@@ -126,14 +202,14 @@ function transformCostTrends(
* Transform token timeline to stacked bar chart data (flattened)
*/
function transformTokenUsage(
timeline: any,
timeline: TimelineData | undefined,
resolution: 'day' | 'hour'
): TokenUsageData[] {
if (!timeline?.tokens || !Array.isArray(timeline.tokens)) {
return []
}
return timeline.tokens.flatMap((d: any) => [
return timeline.tokens.flatMap((d: TimelineTokenItem) => [
{
date: formatBucketLabel(d.bucket, resolution),
type: 'Input' as const,
@@ -150,12 +226,12 @@ function transformTokenUsage(
/**
* Transform cost by model to pie/donut chart data
*/
function transformCostByModel(costByModel: any): CostByModelData[] {
function transformCostByModel(costByModel: CostByModelResponse | undefined): CostByModelData[] {
if (!costByModel?.models || !Array.isArray(costByModel.models)) {
return []
}
return costByModel.models.map((m: any, i: number) => ({
return costByModel.models.map((m: CostByModelItem, i: number) => ({
name: m.model?.split('/').pop() || m.model || 'Unknown',
cost: m.cost_total || 0,
value: Math.round((m.share || 0) * 100),
@@ -169,7 +245,7 @@ function transformCostByModel(costByModel: any): CostByModelData[] {
* UI: 0-2s, 2-5s, 5-10s, 10-20s, 20s+
*/
function transformLatencyDistribution(
latencyDistribution: any
latencyDistribution: LatencyDistributionResponse | undefined
): LatencyDistributionData[] {
if (!latencyDistribution?.buckets || !Array.isArray(latencyDistribution.buckets)) {
return []
@@ -183,7 +259,7 @@ function transformLatencyDistribution(
'20s+': 0,
}
latencyDistribution.buckets.forEach((b: any) => {
latencyDistribution.buckets.forEach((b: LatencyBucketItem) => {
switch (b.bucket) {
case '0-1s':
case '1-2s':
@@ -217,14 +293,14 @@ function transformLatencyDistribution(
* Transform latency percentiles to multi-line chart data (flattened)
*/
function transformLatencyPercentiles(
timeline: any,
timeline: TimelineData | undefined,
resolution: 'day' | 'hour'
): LatencyPercentilesData[] {
if (!timeline?.latency_percentiles || !Array.isArray(timeline.latency_percentiles)) {
return []
}
return timeline.latency_percentiles.flatMap((d: any) => [
return timeline.latency_percentiles.flatMap((d: TimelineLatencyItem) => [
{
date: formatBucketLabel(d.bucket, resolution),
percentile: 'P50' as const,
@@ -246,15 +322,18 @@ function transformLatencyPercentiles(
/**
* Transform cost by agent to top agents list
*/
function transformTopAgents(costByAgent: any): TopAgentData[] {
function transformTopAgents(costByAgent: CostByAgentResponse | undefined): TopAgentData[] {
if (!costByAgent?.agents || !Array.isArray(costByAgent.agents)) {
return []
}
return costByAgent.agents.map((a: any) => ({
name: a.agent || 'Unknown',
spend: a.cost_total || 0,
requests: a.requests || 0,
avgCost: a.requests > 0 ? (a.cost_total || 0) / a.requests : 0,
}))
return costByAgent.agents.map((a: CostByAgentItem) => {
const requests = a.requests || 0
return {
name: a.agent || 'Unknown',
spend: a.cost_total || 0,
requests,
avgCost: requests > 0 ? (a.cost_total || 0) / requests : 0,
}
})
}
+1
View File
@@ -33,4 +33,5 @@ function Badge({ className, variant, ...props }: BadgeProps) {
)
}
// eslint-disable-next-line react-refresh/only-export-components
export { Badge, badgeVariants }
+1
View File
@@ -53,4 +53,5 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
)
Button.displayName = "Button"
// eslint-disable-next-line react-refresh/only-export-components
export { Button, buttonVariants }
@@ -5,7 +5,6 @@ import {
getMetricsSummary,
} from '@/services/agentControlApi'
import { useSettingsStore } from '@/stores/settingsStore'
import type { RawJsonData } from '@/types/agentControl'
// =============================================================================
// Analytics Hook
@@ -10,7 +10,6 @@ import type {
BudgetConfig,
BudgetAlert,
BudgetNotifications,
RawJsonData,
} from '@/types/agentControl'
// =============================================================================
-1
View File
@@ -1,6 +1,5 @@
import { useQuery, useInfiniteQuery } from '@tanstack/react-query'
import { getLogs, getLogsAggregated } from '@/services/agentControlApi'
import type { RawJsonData } from '@/types/agentControl'
// =============================================================================
// Types
+6 -2
View File
@@ -93,9 +93,13 @@ export function useAgentStatus(
let buffer = ''
// Read SSE stream
while (true) {
let streamActive = true
while (streamActive) {
const { done, value } = await reader.read()
if (done) break
if (done) {
streamActive = false
continue
}
buffer += decoder.decode(value, { stream: true })
+1 -2
View File
@@ -1,4 +1,4 @@
import { useParams, useSearchParams } from 'react-router-dom'
import { useParams } from 'react-router-dom'
import { useEffect, useState } from 'react'
import { RegisterForm } from '@/components/auth/RegisterForm'
import AdenLogo from '@/assets/aden-logo.svg'
@@ -6,7 +6,6 @@ import { getOrgInfoByPath } from '@/services/authApi'
export function RegisterPage() {
const { org } = useParams<{ org?: string }>()
const [searchParams] = useSearchParams()
const [orgName, setOrgName] = useState<string | undefined>()
const [isLoadingOrg, setIsLoadingOrg] = useState(!!org)