241 lines
5.9 KiB
TypeScript
241 lines
5.9 KiB
TypeScript
export type Fragment = {
|
|
id: string;
|
|
content: string;
|
|
created_at: string;
|
|
};
|
|
|
|
export type User = {
|
|
id: string;
|
|
label: string;
|
|
role: "admin" | "member";
|
|
debug_sharing: boolean;
|
|
};
|
|
|
|
export type Snapshot = {
|
|
title: string | null;
|
|
summary: string | null;
|
|
maturity_ai: number;
|
|
maturity_override: number | null;
|
|
confidence: number | null;
|
|
motion: string;
|
|
position: string;
|
|
tension: string;
|
|
trajectory: string;
|
|
possible_moves: string[];
|
|
change_kind: "analysis" | "manual_calibration" | "legacy_partial";
|
|
created_at: string;
|
|
};
|
|
|
|
export type Idea = {
|
|
id: string;
|
|
title: string;
|
|
summary: string;
|
|
maturity_ai: number;
|
|
maturity_override: number | null;
|
|
maturity: number;
|
|
is_overridden: boolean;
|
|
confidence: number;
|
|
motion: string;
|
|
position: string;
|
|
tension: string;
|
|
trajectory: string;
|
|
possible_moves: string[];
|
|
fragment_count: number;
|
|
latest_fragment_at: string | null;
|
|
updated_at: string;
|
|
fragments?: Fragment[];
|
|
snapshots?: Snapshot[];
|
|
};
|
|
|
|
export type AdminOverview = {
|
|
totals: {
|
|
users: number;
|
|
fragments: number;
|
|
ideas: number;
|
|
pending: number;
|
|
audit_events: number;
|
|
};
|
|
last_24h: {
|
|
runs: number;
|
|
successes: number;
|
|
errors: number;
|
|
success_rate: number;
|
|
avg_duration_ms: number;
|
|
input_tokens: number;
|
|
output_tokens: number;
|
|
reasoning_tokens: number;
|
|
cached_tokens: number;
|
|
model_rounds: number;
|
|
tool_calls: number;
|
|
};
|
|
runtime: {
|
|
uptime_seconds: number;
|
|
curator_configured: boolean;
|
|
queue_depth: number;
|
|
model: string;
|
|
prompt_version: string;
|
|
};
|
|
daily: Array<{
|
|
day: string;
|
|
runs: number;
|
|
successes: number;
|
|
errors: number;
|
|
reasoning_tokens: number;
|
|
}>;
|
|
recent_errors: Array<{
|
|
id: string;
|
|
started_at: string;
|
|
error_type: string;
|
|
error_message: string;
|
|
user_label: string;
|
|
}>;
|
|
audit_24h: {
|
|
events: number;
|
|
failed_logins: number;
|
|
};
|
|
narrative: string;
|
|
};
|
|
|
|
export type AgentRun = {
|
|
id: string;
|
|
user_id: string;
|
|
fragment_id: string;
|
|
status: "running" | "success" | "error";
|
|
prompt_version: string;
|
|
model: string;
|
|
thinking_effort: string;
|
|
started_at: string;
|
|
finished_at: string | null;
|
|
duration_ms: number | null;
|
|
attempt_count: number;
|
|
model_rounds: number;
|
|
tool_calls: number;
|
|
search_calls: number;
|
|
inspection_calls: number;
|
|
input_tokens: number;
|
|
output_tokens: number;
|
|
reasoning_tokens: number;
|
|
cached_tokens: number;
|
|
error_type: string | null;
|
|
error_message: string | null;
|
|
user_label: string;
|
|
user_role: string;
|
|
debug_sharing: boolean;
|
|
content_length: number;
|
|
};
|
|
|
|
export type AgentEvent = {
|
|
id: string;
|
|
event_type: string;
|
|
event_at: string;
|
|
duration_ms: number | null;
|
|
payload: Record<string, unknown>;
|
|
};
|
|
|
|
export type AgentRunDetail = {
|
|
run: AgentRun & { content_sha256: string };
|
|
fragment:
|
|
| { content_visible: true; content: string }
|
|
| { content_visible: false; content_length: number; content_sha256: string };
|
|
events: AgentEvent[];
|
|
privacy: {
|
|
content_visible: boolean;
|
|
reasoning_content_stored: boolean;
|
|
reason: string;
|
|
};
|
|
};
|
|
|
|
export type AdminUser = User & {
|
|
created_at: string;
|
|
fragment_count: number;
|
|
idea_count: number;
|
|
};
|
|
|
|
export type AuditEvent = {
|
|
id: string;
|
|
event_type: string;
|
|
created_at: string;
|
|
user_label: string | null;
|
|
payload: Record<string, unknown>;
|
|
};
|
|
|
|
type ApiOptions = RequestInit & { body?: string };
|
|
|
|
export class ApiError extends Error {
|
|
status: number;
|
|
|
|
constructor(status: number, message: string) {
|
|
super(message);
|
|
this.status = status;
|
|
}
|
|
}
|
|
|
|
async function request<T>(path: string, options: ApiOptions = {}): Promise<T> {
|
|
const response = await fetch(path, {
|
|
...options,
|
|
credentials: "same-origin",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-Note-Client": "xuxiang-web",
|
|
...(options.headers ?? {}),
|
|
},
|
|
});
|
|
if (!response.ok) {
|
|
let message = "请求没有完成";
|
|
try {
|
|
const body = await response.json();
|
|
if (typeof body.detail === "string") message = body.detail;
|
|
} catch {
|
|
// Keep the calm fallback message.
|
|
}
|
|
throw new ApiError(response.status, message);
|
|
}
|
|
return response.json() as Promise<T>;
|
|
}
|
|
|
|
export const api = {
|
|
session: () =>
|
|
request<{ authenticated: boolean; user: User }>("/api/session"),
|
|
login: (accessKey: string) =>
|
|
request<{ authenticated: boolean; user: User }>("/api/login", {
|
|
method: "POST",
|
|
body: JSON.stringify({ access_key: accessKey }),
|
|
}),
|
|
logout: () =>
|
|
request<{ authenticated: boolean }>("/api/logout", { method: "POST" }),
|
|
fragments: () => request<{ items: Fragment[] }>("/api/fragments"),
|
|
createFragment: (content: string) =>
|
|
request<Fragment>("/api/fragments", {
|
|
method: "POST",
|
|
body: JSON.stringify({ content }),
|
|
}),
|
|
ideas: () => request<{ items: Idea[] }>("/api/ideas"),
|
|
idea: (id: string) => request<Idea>(`/api/ideas/${id}`),
|
|
updateMaturity: (id: string, maturity: number | null) =>
|
|
request<Idea>(`/api/ideas/${id}/position`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify({ maturity }),
|
|
}),
|
|
status: () =>
|
|
request<{ pending: number; curator_configured: boolean }>(
|
|
"/api/system/status",
|
|
),
|
|
updateDebugSharing: (enabled: boolean) =>
|
|
request<User>("/api/account/debug-sharing", {
|
|
method: "PATCH",
|
|
body: JSON.stringify({ enabled }),
|
|
}),
|
|
adminOverview: () => request<AdminOverview>("/api/admin/overview"),
|
|
adminUsers: () => request<{ items: AdminUser[] }>("/api/admin/users"),
|
|
adminRuns: (limit = 80) =>
|
|
request<{ items: AgentRun[] }>(`/api/admin/runs?limit=${limit}`),
|
|
adminRun: (id: string) =>
|
|
request<AgentRunDetail>(`/api/admin/runs/${id}`),
|
|
retryAdminRun: (id: string) =>
|
|
request<{ queued: boolean }>(`/api/admin/runs/${id}/retry`, {
|
|
method: "POST",
|
|
}),
|
|
adminAudit: (limit = 80) =>
|
|
request<{ items: AuditEvent[] }>(`/api/admin/audit?limit=${limit}`),
|
|
};
|